mirror of
https://github.com/jakejarvis/jarv.is.git
synced 2025-04-28 04:50:28 -04:00
refactor contact form server action
This commit is contained in:
parent
3043f4df8c
commit
64f5cd2c39
@ -1,112 +0,0 @@
|
|||||||
"use server";
|
|
||||||
|
|
||||||
import { headers } from "next/headers";
|
|
||||||
import * as v from "valibot";
|
|
||||||
import { Resend } from "resend";
|
|
||||||
import * as config from "../../lib/config";
|
|
||||||
|
|
||||||
const ContactSchema = v.object({
|
|
||||||
// TODO: replace duplicate error messages with v.message() when released. see:
|
|
||||||
// https://valibot.dev/api/message/
|
|
||||||
// https://github.com/fabian-hiller/valibot/blob/main/library/src/methods/message/message.ts
|
|
||||||
name: v.pipe(v.string("Your name is required."), v.trim(), v.nonEmpty("Your name is required.")),
|
|
||||||
email: v.pipe(
|
|
||||||
v.string("Your email address is required."),
|
|
||||||
v.trim(),
|
|
||||||
v.nonEmpty("Your email address is required."),
|
|
||||||
v.email("Invalid email address.")
|
|
||||||
),
|
|
||||||
message: v.pipe(
|
|
||||||
v.string("A message is required."),
|
|
||||||
v.trim(),
|
|
||||||
v.nonEmpty("A message is required."),
|
|
||||||
v.minLength(10, "Your message must be at least 10 characters.")
|
|
||||||
),
|
|
||||||
"cf-turnstile-response": v.pipe(
|
|
||||||
// token wasn't submitted at _all_, most likely a direct POST request by a spam bot
|
|
||||||
v.string("Shoo, bot."),
|
|
||||||
// form submitted properly but token was missing, might be a forgetful human
|
|
||||||
v.nonEmpty("Just do the stinkin CAPTCHA, human! 🤖"),
|
|
||||||
// very rudimentary length check based on Cloudflare's docs
|
|
||||||
// https://developers.cloudflare.com/turnstile/troubleshooting/testing/
|
|
||||||
v.minLength("XXXX.DUMMY.TOKEN.XXXX".length),
|
|
||||||
// "A Turnstile token can have up to 2048 characters."
|
|
||||||
// https://developers.cloudflare.com/turnstile/get-started/server-side-validation/
|
|
||||||
v.maxLength(2048),
|
|
||||||
v.readonly()
|
|
||||||
),
|
|
||||||
});
|
|
||||||
|
|
||||||
export type ContactInput = v.InferInput<typeof ContactSchema>;
|
|
||||||
|
|
||||||
export type ContactState = {
|
|
||||||
success: boolean;
|
|
||||||
message: string;
|
|
||||||
errors?: v.FlatErrors<typeof ContactSchema>["nested"];
|
|
||||||
};
|
|
||||||
|
|
||||||
export const sendMessage = async (prevState: ContactState, formData: FormData): Promise<ContactState> => {
|
|
||||||
try {
|
|
||||||
// TODO: remove after debugging why automated spam entries are causing 500 errors
|
|
||||||
console.debug("[contact form] received data:", formData);
|
|
||||||
|
|
||||||
const data = v.safeParse(ContactSchema, Object.fromEntries(formData));
|
|
||||||
|
|
||||||
if (!data.success) {
|
|
||||||
return {
|
|
||||||
success: false,
|
|
||||||
message: "Please make sure all fields are filled in.",
|
|
||||||
errors: v.flatten(data.issues).nested,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// validate captcha
|
|
||||||
const turnstileResponse = await fetch("https://challenges.cloudflare.com/turnstile/v0/siteverify", {
|
|
||||||
method: "POST",
|
|
||||||
headers: { "Content-Type": "application/json" },
|
|
||||||
body: JSON.stringify({
|
|
||||||
secret: process.env.TURNSTILE_SECRET_KEY || "1x0000000000000000000000000000000AA",
|
|
||||||
response: data.output["cf-turnstile-response"],
|
|
||||||
remoteip: (await headers()).get("x-forwarded-for") || "",
|
|
||||||
}),
|
|
||||||
cache: "no-store",
|
|
||||||
signal: AbortSignal.timeout(5000), // 5 second timeout
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!turnstileResponse || !turnstileResponse.ok) {
|
|
||||||
throw new Error(`[contact form] turnstile validation failed: ${turnstileResponse.status}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const turnstileData = (await turnstileResponse.json()) as { success: boolean };
|
|
||||||
|
|
||||||
if (!turnstileData.success) {
|
|
||||||
return {
|
|
||||||
success: false,
|
|
||||||
message: "Did you complete the CAPTCHA? (If you're human, that is...)",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!process.env.RESEND_FROM_EMAIL) {
|
|
||||||
console.warn("[contact form] RESEND_FROM_EMAIL not set, falling back to onboarding@resend.dev.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// send email
|
|
||||||
const resend = new Resend(process.env.RESEND_API_KEY);
|
|
||||||
await resend.emails.send({
|
|
||||||
from: `${data.output.name} <${process.env.RESEND_FROM_EMAIL ?? "onboarding@resend.dev"}>`,
|
|
||||||
replyTo: `${data.output.name} <${data.output.email}>`,
|
|
||||||
to: [config.authorEmail],
|
|
||||||
subject: `[${config.siteName}] Contact Form Submission`,
|
|
||||||
text: data.output.message,
|
|
||||||
});
|
|
||||||
|
|
||||||
return { success: true, message: "Thanks! You should hear from me soon." };
|
|
||||||
} catch (error) {
|
|
||||||
console.error("[contact form] fatal error:", error);
|
|
||||||
|
|
||||||
return {
|
|
||||||
success: false,
|
|
||||||
message: "Internal server error. Please try again later or shoot me an email.",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
@ -24,7 +24,7 @@
|
|||||||
border-color: var(--colors-error);
|
border-color: var(--colors-error);
|
||||||
}
|
}
|
||||||
|
|
||||||
.fieldError {
|
.errorMessage {
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
color: var(--colors-error);
|
color: var(--colors-error);
|
||||||
}
|
}
|
||||||
|
@ -6,12 +6,16 @@ import Turnstile from "react-turnstile";
|
|||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import { CheckIcon, XIcon } from "lucide-react";
|
import { CheckIcon, XIcon } from "lucide-react";
|
||||||
import Link from "../../components/Link";
|
import Link from "../../components/Link";
|
||||||
import { sendMessage, type ContactInput, type ContactState } from "./actions";
|
import type { ContactInput, ContactState } from "./schema";
|
||||||
|
|
||||||
import styles from "./form.module.css";
|
import styles from "./form.module.css";
|
||||||
|
|
||||||
const ContactForm = () => {
|
const ContactForm = ({
|
||||||
const [formState, formAction, pending] = useActionState<ContactState, FormData>(sendMessage, {
|
serverAction,
|
||||||
|
}: {
|
||||||
|
serverAction: (state: ContactState, payload: FormData) => Promise<ContactState>;
|
||||||
|
}) => {
|
||||||
|
const [formState, formAction, pending] = useActionState<ContactState, FormData>(serverAction, {
|
||||||
success: false,
|
success: false,
|
||||||
message: "",
|
message: "",
|
||||||
});
|
});
|
||||||
@ -36,7 +40,7 @@ const ContactForm = () => {
|
|||||||
disabled={pending || formState.success}
|
disabled={pending || formState.success}
|
||||||
className={clsx(styles.input, !pending && formState.errors?.name && styles.invalid)}
|
className={clsx(styles.input, !pending && formState.errors?.name && styles.invalid)}
|
||||||
/>
|
/>
|
||||||
{!pending && formState.errors?.name && <span className={styles.fieldError}>{formState.errors.name[0]}</span>}
|
{!pending && formState.errors?.name && <span className={styles.errorMessage}>{formState.errors.name[0]}</span>}
|
||||||
|
|
||||||
<input
|
<input
|
||||||
type="email"
|
type="email"
|
||||||
@ -50,7 +54,7 @@ const ContactForm = () => {
|
|||||||
disabled={pending || formState.success}
|
disabled={pending || formState.success}
|
||||||
className={clsx(styles.input, !pending && formState.errors?.email && styles.invalid)}
|
className={clsx(styles.input, !pending && formState.errors?.email && styles.invalid)}
|
||||||
/>
|
/>
|
||||||
{!pending && formState.errors?.email && <span className={styles.fieldError}>{formState.errors.email[0]}</span>}
|
{!pending && formState.errors?.email && <span className={styles.errorMessage}>{formState.errors.email[0]}</span>}
|
||||||
|
|
||||||
<TextareaAutosize
|
<TextareaAutosize
|
||||||
name="message"
|
name="message"
|
||||||
@ -64,7 +68,7 @@ const ContactForm = () => {
|
|||||||
className={clsx(styles.input, styles.textarea, !pending && formState.errors?.message && styles.invalid)}
|
className={clsx(styles.input, styles.textarea, !pending && formState.errors?.message && styles.invalid)}
|
||||||
/>
|
/>
|
||||||
{!pending && formState.errors?.message && (
|
{!pending && formState.errors?.message && (
|
||||||
<span className={styles.fieldError}>{formState.errors.message[0]}</span>
|
<span className={styles.errorMessage}>{formState.errors.message[0]}</span>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div
|
<div
|
||||||
@ -106,7 +110,7 @@ const ContactForm = () => {
|
|||||||
<Turnstile sitekey={process.env.NEXT_PUBLIC_TURNSTILE_SITE_KEY || "1x00000000000000000000AA"} fixedSize />
|
<Turnstile sitekey={process.env.NEXT_PUBLIC_TURNSTILE_SITE_KEY || "1x00000000000000000000AA"} fixedSize />
|
||||||
</div>
|
</div>
|
||||||
{!pending && formState.errors?.["cf-turnstile-response"] && (
|
{!pending && formState.errors?.["cf-turnstile-response"] && (
|
||||||
<span className={styles.fieldError}>{formState.errors["cf-turnstile-response"][0]}</span>
|
<span className={styles.errorMessage}>{formState.errors["cf-turnstile-response"][0]}</span>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className={styles.actionRow}>
|
<div className={styles.actionRow}>
|
||||||
|
@ -1,7 +1,13 @@
|
|||||||
|
import { headers } from "next/headers";
|
||||||
|
import * as v from "valibot";
|
||||||
|
import { Resend } from "resend";
|
||||||
import PageTitle from "../../components/PageTitle";
|
import PageTitle from "../../components/PageTitle";
|
||||||
import Link from "../../components/Link";
|
import Link from "../../components/Link";
|
||||||
import ContactForm from "./form";
|
|
||||||
import { addMetadata } from "../../lib/helpers/metadata";
|
import { addMetadata } from "../../lib/helpers/metadata";
|
||||||
|
import * as config from "../../lib/config";
|
||||||
|
|
||||||
|
import ContactForm from "./form";
|
||||||
|
import ContactSchema, { type ContactState } from "./schema";
|
||||||
|
|
||||||
export const metadata = addMetadata({
|
export const metadata = addMetadata({
|
||||||
title: "Contact Me",
|
title: "Contact Me",
|
||||||
@ -11,6 +17,84 @@ export const metadata = addMetadata({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const send = async (prevState: ContactState, formData: FormData): Promise<ContactState> => {
|
||||||
|
"use server";
|
||||||
|
|
||||||
|
// TODO: remove after debugging why automated spam entries are causing 500 errors
|
||||||
|
console.debug("[contact form] received data:", formData);
|
||||||
|
|
||||||
|
if (!process.env.RESEND_API_KEY) {
|
||||||
|
throw new Error("[contact form] 'RESEND_API_KEY' is not set.");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const data = v.safeParse(ContactSchema, Object.fromEntries(formData));
|
||||||
|
|
||||||
|
if (!data.success) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: "Please make sure all fields are filled in.",
|
||||||
|
errors: v.flatten(data.issues).nested,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to get the client IP (for turnstile accuracy, not logging!) but no biggie if we can't
|
||||||
|
let remoteip;
|
||||||
|
try {
|
||||||
|
remoteip = (await headers()).get("x-forwarded-for");
|
||||||
|
} catch {} // eslint-disable-line no-empty
|
||||||
|
|
||||||
|
// validate captcha
|
||||||
|
const turnstileResponse = await fetch("https://challenges.cloudflare.com/turnstile/v0/siteverify", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({
|
||||||
|
secret: process.env.TURNSTILE_SECRET_KEY || "1x0000000000000000000000000000000AA",
|
||||||
|
response: data.output["cf-turnstile-response"],
|
||||||
|
remoteip,
|
||||||
|
}),
|
||||||
|
cache: "no-store",
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!turnstileResponse || !turnstileResponse.ok) {
|
||||||
|
throw new Error(`[contact form] turnstile validation failed: ${turnstileResponse.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const turnstileData = (await turnstileResponse.json()) as { success: boolean };
|
||||||
|
|
||||||
|
if (!turnstileData.success) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: "Did you complete the CAPTCHA? (If you're human, that is...)",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!process.env.RESEND_FROM_EMAIL) {
|
||||||
|
// https://resend.com/docs/api-reference/emails/send-email
|
||||||
|
console.warn("[contact form] 'RESEND_FROM_EMAIL' is not set, falling back to onboarding@resend.dev.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// send email
|
||||||
|
const resend = new Resend(process.env.RESEND_API_KEY!);
|
||||||
|
await resend.emails.send({
|
||||||
|
from: `${data.output.name} <${process.env.RESEND_FROM_EMAIL || "onboarding@resend.dev"}>`,
|
||||||
|
replyTo: `${data.output.name} <${data.output.email}>`,
|
||||||
|
to: [config.authorEmail],
|
||||||
|
subject: `[${config.siteName}] Contact Form Submission`,
|
||||||
|
text: data.output.message,
|
||||||
|
});
|
||||||
|
|
||||||
|
return { success: true, message: "Thanks! You should hear from me soon." };
|
||||||
|
} catch (error) {
|
||||||
|
console.error("[contact form] fatal error:", error);
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: "Internal server error. Please try again later or shoot me an email.",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const Page = () => {
|
const Page = () => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@ -43,7 +127,7 @@ const Page = () => {
|
|||||||
.
|
.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<ContactForm />
|
<ContactForm serverAction={send} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
43
app/contact/schema.ts
Normal file
43
app/contact/schema.ts
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import * as v from "valibot";
|
||||||
|
|
||||||
|
export const ContactSchema = v.object({
|
||||||
|
// TODO: replace duplicate error messages with v.message() when released. see:
|
||||||
|
// https://valibot.dev/api/message/
|
||||||
|
// https://github.com/fabian-hiller/valibot/blob/main/library/src/methods/message/message.ts
|
||||||
|
name: v.pipe(v.string("Your name is required."), v.trim(), v.nonEmpty("Your name is required.")),
|
||||||
|
email: v.pipe(
|
||||||
|
v.string("Your email address is required."),
|
||||||
|
v.trim(),
|
||||||
|
v.nonEmpty("Your email address is required."),
|
||||||
|
v.email("Invalid email address.")
|
||||||
|
),
|
||||||
|
message: v.pipe(
|
||||||
|
v.string("A message is required."),
|
||||||
|
v.trim(),
|
||||||
|
v.nonEmpty("A message is required."),
|
||||||
|
v.minLength(10, "Your message must be at least 10 characters.")
|
||||||
|
),
|
||||||
|
"cf-turnstile-response": v.pipe(
|
||||||
|
// token wasn't submitted at _all_, most likely a direct POST request by a spam bot
|
||||||
|
v.string("Shoo, bot."),
|
||||||
|
// form submitted properly but token was missing, might be a forgetful human
|
||||||
|
v.nonEmpty("Just do the stinkin CAPTCHA, human! 🤖"),
|
||||||
|
// very rudimentary length check based on Cloudflare's docs
|
||||||
|
// https://developers.cloudflare.com/turnstile/troubleshooting/testing/
|
||||||
|
v.minLength("XXXX.DUMMY.TOKEN.XXXX".length),
|
||||||
|
// "A Turnstile token can have up to 2048 characters."
|
||||||
|
// https://developers.cloudflare.com/turnstile/get-started/server-side-validation/
|
||||||
|
v.maxLength(2048),
|
||||||
|
v.readonly()
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type ContactInput = v.InferInput<typeof ContactSchema>;
|
||||||
|
|
||||||
|
export type ContactState = {
|
||||||
|
success: boolean;
|
||||||
|
message: string;
|
||||||
|
errors?: v.FlatErrors<typeof ContactSchema>["nested"];
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ContactSchema;
|
@ -23,9 +23,7 @@ const useLocalStorage = <T = string>(
|
|||||||
window.localStorage.setItem(key, serializer(initialValue));
|
window.localStorage.setItem(key, serializer(initialValue));
|
||||||
return initialValue;
|
return initialValue;
|
||||||
}
|
}
|
||||||
} catch (
|
} catch {
|
||||||
error // eslint-disable-line @typescript-eslint/no-unused-vars
|
|
||||||
) {
|
|
||||||
return initialValue;
|
return initialValue;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -51,7 +49,7 @@ const useLocalStorage = <T = string>(
|
|||||||
try {
|
try {
|
||||||
window.localStorage.removeItem(key);
|
window.localStorage.removeItem(key);
|
||||||
setState(undefined);
|
setState(undefined);
|
||||||
} catch (error) {} // eslint-disable-line no-empty, @typescript-eslint/no-unused-vars
|
} catch {} // eslint-disable-line no-empty
|
||||||
}, [key]);
|
}, [key]);
|
||||||
|
|
||||||
return [state, set, remove];
|
return [state, set, remove];
|
||||||
|
@ -23,8 +23,8 @@
|
|||||||
"@giscus/react": "^3.1.0",
|
"@giscus/react": "^3.1.0",
|
||||||
"@mdx-js/loader": "^3.1.0",
|
"@mdx-js/loader": "^3.1.0",
|
||||||
"@mdx-js/react": "^3.1.0",
|
"@mdx-js/react": "^3.1.0",
|
||||||
"@next/bundle-analyzer": "15.3.0-canary.29",
|
"@next/bundle-analyzer": "15.3.0-canary.33",
|
||||||
"@next/mdx": "15.3.0-canary.29",
|
"@next/mdx": "15.3.0-canary.33",
|
||||||
"@octokit/graphql": "^8.2.1",
|
"@octokit/graphql": "^8.2.1",
|
||||||
"@octokit/graphql-schema": "^15.26.0",
|
"@octokit/graphql-schema": "^15.26.0",
|
||||||
"@upstash/redis": "^1.34.6",
|
"@upstash/redis": "^1.34.6",
|
||||||
@ -38,7 +38,7 @@
|
|||||||
"html-entities": "^2.6.0",
|
"html-entities": "^2.6.0",
|
||||||
"lucide-react": "0.487.0",
|
"lucide-react": "0.487.0",
|
||||||
"modern-normalize": "^3.0.1",
|
"modern-normalize": "^3.0.1",
|
||||||
"next": "15.3.0-canary.29",
|
"next": "15.3.0-canary.33",
|
||||||
"polished": "^4.3.1",
|
"polished": "^4.3.1",
|
||||||
"prop-types": "^15.8.1",
|
"prop-types": "^15.8.1",
|
||||||
"react": "19.1.0",
|
"react": "19.1.0",
|
||||||
@ -83,7 +83,7 @@
|
|||||||
"babel-plugin-react-compiler": "19.0.0-beta-e993439-20250328",
|
"babel-plugin-react-compiler": "19.0.0-beta-e993439-20250328",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"eslint": "^9.23.0",
|
"eslint": "^9.23.0",
|
||||||
"eslint-config-next": "15.3.0-canary.29",
|
"eslint-config-next": "15.3.0-canary.33",
|
||||||
"eslint-config-prettier": "^10.1.1",
|
"eslint-config-prettier": "^10.1.1",
|
||||||
"eslint-plugin-css-modules": "^2.12.0",
|
"eslint-plugin-css-modules": "^2.12.0",
|
||||||
"eslint-plugin-import": "^2.31.0",
|
"eslint-plugin-import": "^2.31.0",
|
||||||
|
126
pnpm-lock.yaml
generated
126
pnpm-lock.yaml
generated
@ -27,11 +27,11 @@ importers:
|
|||||||
specifier: ^3.1.0
|
specifier: ^3.1.0
|
||||||
version: 3.1.0(@types/react@19.1.0)(react@19.1.0)
|
version: 3.1.0(@types/react@19.1.0)(react@19.1.0)
|
||||||
'@next/bundle-analyzer':
|
'@next/bundle-analyzer':
|
||||||
specifier: 15.3.0-canary.29
|
specifier: 15.3.0-canary.33
|
||||||
version: 15.3.0-canary.29
|
version: 15.3.0-canary.33
|
||||||
'@next/mdx':
|
'@next/mdx':
|
||||||
specifier: 15.3.0-canary.29
|
specifier: 15.3.0-canary.33
|
||||||
version: 15.3.0-canary.29(@mdx-js/loader@3.1.0(acorn@8.14.1))(@mdx-js/react@3.1.0(@types/react@19.1.0)(react@19.1.0))
|
version: 15.3.0-canary.33(@mdx-js/loader@3.1.0(acorn@8.14.1))(@mdx-js/react@3.1.0(@types/react@19.1.0)(react@19.1.0))
|
||||||
'@octokit/graphql':
|
'@octokit/graphql':
|
||||||
specifier: ^8.2.1
|
specifier: ^8.2.1
|
||||||
version: 8.2.1
|
version: 8.2.1
|
||||||
@ -61,7 +61,7 @@ importers:
|
|||||||
version: 4.2.2
|
version: 4.2.2
|
||||||
geist:
|
geist:
|
||||||
specifier: ^1.3.1
|
specifier: ^1.3.1
|
||||||
version: 1.3.1(next@15.3.0-canary.29(@babel/core@7.26.10)(babel-plugin-react-compiler@19.0.0-beta-e993439-20250328)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))
|
version: 1.3.1(next@15.3.0-canary.33(@babel/core@7.26.10)(babel-plugin-react-compiler@19.0.0-beta-e993439-20250328)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))
|
||||||
html-entities:
|
html-entities:
|
||||||
specifier: ^2.6.0
|
specifier: ^2.6.0
|
||||||
version: 2.6.0
|
version: 2.6.0
|
||||||
@ -72,8 +72,8 @@ importers:
|
|||||||
specifier: ^3.0.1
|
specifier: ^3.0.1
|
||||||
version: 3.0.1
|
version: 3.0.1
|
||||||
next:
|
next:
|
||||||
specifier: 15.3.0-canary.29
|
specifier: 15.3.0-canary.33
|
||||||
version: 15.3.0-canary.29(@babel/core@7.26.10)(babel-plugin-react-compiler@19.0.0-beta-e993439-20250328)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
version: 15.3.0-canary.33(@babel/core@7.26.10)(babel-plugin-react-compiler@19.0.0-beta-e993439-20250328)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||||
polished:
|
polished:
|
||||||
specifier: ^4.3.1
|
specifier: ^4.3.1
|
||||||
version: 4.3.1
|
version: 4.3.1
|
||||||
@ -202,8 +202,8 @@ importers:
|
|||||||
specifier: ^9.23.0
|
specifier: ^9.23.0
|
||||||
version: 9.23.0
|
version: 9.23.0
|
||||||
eslint-config-next:
|
eslint-config-next:
|
||||||
specifier: 15.3.0-canary.29
|
specifier: 15.3.0-canary.33
|
||||||
version: 15.3.0-canary.29(eslint@9.23.0)(typescript@5.8.2)
|
version: 15.3.0-canary.33(eslint@9.23.0)(typescript@5.8.2)
|
||||||
eslint-config-prettier:
|
eslint-config-prettier:
|
||||||
specifier: ^10.1.1
|
specifier: ^10.1.1
|
||||||
version: 10.1.1(eslint@9.23.0)
|
version: 10.1.1(eslint@9.23.0)
|
||||||
@ -652,17 +652,17 @@ packages:
|
|||||||
'@napi-rs/wasm-runtime@0.2.8':
|
'@napi-rs/wasm-runtime@0.2.8':
|
||||||
resolution: {integrity: sha512-OBlgKdX7gin7OIq4fadsjpg+cp2ZphvAIKucHsNfTdJiqdOmOEwQd/bHi0VwNrcw5xpBJyUw6cK/QilCqy1BSg==}
|
resolution: {integrity: sha512-OBlgKdX7gin7OIq4fadsjpg+cp2ZphvAIKucHsNfTdJiqdOmOEwQd/bHi0VwNrcw5xpBJyUw6cK/QilCqy1BSg==}
|
||||||
|
|
||||||
'@next/bundle-analyzer@15.3.0-canary.29':
|
'@next/bundle-analyzer@15.3.0-canary.33':
|
||||||
resolution: {integrity: sha512-siOmaq4mf5a0ZIVmRLLvxDXPB9u0jgHhatqd7quDIBQ95yuTOYcJiCkHmtPnw8xx0cCdMCEH22HqAfIEB2MTZg==}
|
resolution: {integrity: sha512-n27QmTA0/AaoFD90hoC924r/OVMN6pSeUw8mGZkwQhSTh1Ns3thcHkoSP4UbZV2x5clxg4XBuH3qUwk2sMogtQ==}
|
||||||
|
|
||||||
'@next/env@15.3.0-canary.29':
|
'@next/env@15.3.0-canary.33':
|
||||||
resolution: {integrity: sha512-9n0tgLzP18NxWsJwJsRz7txV8XcGSYkfwmScxJnOCcbLjjowku74cQ4TVMAZ/vC0kkZdgkVysb0dp77iJoMJMQ==}
|
resolution: {integrity: sha512-yPuNYRSFLHtulgy5Mge7tEy2GT/SmIcC7ZM9mJRFVtCsjjR6hqqOkLdbu/6cl7qT0x5ADRfpokT5Rf5bTzUumA==}
|
||||||
|
|
||||||
'@next/eslint-plugin-next@15.3.0-canary.29':
|
'@next/eslint-plugin-next@15.3.0-canary.33':
|
||||||
resolution: {integrity: sha512-qn73vyDNsqfgodEd1/UM76LpPTkFKui0d8jQ3AvS3eybS+RCfY5j3WEhdqq5UFBIAitFClscr/sr5U6bRHvpQw==}
|
resolution: {integrity: sha512-Wc9n4UkDUuayQnehXy5AT50YoqpLqmeuM9QGZvQajtjwaQ2u002X0wCrJuSOHemHx0NdiZ72CHpfjbaRPCVZnw==}
|
||||||
|
|
||||||
'@next/mdx@15.3.0-canary.29':
|
'@next/mdx@15.3.0-canary.33':
|
||||||
resolution: {integrity: sha512-ZO8GXzt4IRdUVLPGY22ciLBzbXgDVkM1sM6wt5DnQ2Oi2RGxLmxPSW21z/rABVbStZ0u14csRZO+uN3csWbGug==}
|
resolution: {integrity: sha512-Zjc/0IUcAC3XQZj7NUAnR4XHZ2zH723DCrzDwzuIuIe4cYse8Sdy/a/6bGmFJCUdKzbNewGktIKDdNI8/uaS1g==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@mdx-js/loader': '>=0.15.0'
|
'@mdx-js/loader': '>=0.15.0'
|
||||||
'@mdx-js/react': '>=0.15.0'
|
'@mdx-js/react': '>=0.15.0'
|
||||||
@ -672,50 +672,50 @@ packages:
|
|||||||
'@mdx-js/react':
|
'@mdx-js/react':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@next/swc-darwin-arm64@15.3.0-canary.29':
|
'@next/swc-darwin-arm64@15.3.0-canary.33':
|
||||||
resolution: {integrity: sha512-EqZVuXjBIJ2RTcMhY6fWUndaPli/F5qLkJ2ubDz1Mgjq0voHLvjXr94Qdb6H0pNYgID/TEU2N5GX8LBH7kUafQ==}
|
resolution: {integrity: sha512-oWIA9x2llzFxrvnz/6ZNmiMb5yrfR6WfRKa28mo+6c4e9r7M0gKFpNd5RbDZF+fF2RTetrv57ze+Cm9KkTUrSg==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [darwin]
|
os: [darwin]
|
||||||
|
|
||||||
'@next/swc-darwin-x64@15.3.0-canary.29':
|
'@next/swc-darwin-x64@15.3.0-canary.33':
|
||||||
resolution: {integrity: sha512-hOneW+Yo+jmV+Y+EVWwZTTV4npJhHF8vEv2L8ETN5vc7HgEV+SUd4kah7n80Mzn2gxChLzY2/OqAVRBMvQ8irQ==}
|
resolution: {integrity: sha512-Yj1S17ww6ga6VCCNbWrA0URQsuT3Xb5dE5hToo7OGf2NOFN9zEVMYbDp6CO4/ugOB4BslR8tzSVyfKyytibdCQ==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [darwin]
|
os: [darwin]
|
||||||
|
|
||||||
'@next/swc-linux-arm64-gnu@15.3.0-canary.29':
|
'@next/swc-linux-arm64-gnu@15.3.0-canary.33':
|
||||||
resolution: {integrity: sha512-hTkjTojPdtI86DIc75QR2spGJ45pe0Pr8PzxvcgUDC4X5nOifQBho+5e7T4OZWYjRfON2Jv7Hrk9EMWi/E6iOw==}
|
resolution: {integrity: sha512-nkV4cw4w23qzpkgG9ITeLnXvLhV/i4ixy6NGJGCxOsYSoGXA5O6KD+ZEoYSSxRuM7rVVUfu6dO7A+W9s74U63Q==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
'@next/swc-linux-arm64-musl@15.3.0-canary.29':
|
'@next/swc-linux-arm64-musl@15.3.0-canary.33':
|
||||||
resolution: {integrity: sha512-AxY/VxNrbMIGLiuG2lhHAoi++k/VBvcugj5Oo5Ovk2gN+tKEJRryoffawELBpdm1eAiAOjn2fwQNb7xq4YZIvw==}
|
resolution: {integrity: sha512-Y/se0HzGrooecLp1JWWgODK89te2+akTr88NkYKGW63rGVrJvevXDZ8jJQiSMCwUq+OAWBRoSyrswLI+/9VMOQ==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
'@next/swc-linux-x64-gnu@15.3.0-canary.29':
|
'@next/swc-linux-x64-gnu@15.3.0-canary.33':
|
||||||
resolution: {integrity: sha512-KY1rz+M2XQhTOjgbJsXJfuK30m6mI1dMXdwRmTmWPwy+/9q0mtusp3oTi5uK/i40dQCUs3SPCWUFTsf0iqowUg==}
|
resolution: {integrity: sha512-ENGvmcSyu51THeSG9i5vQmRVa6EriDmDVgnUGYm5o/X4lrU/xWNNMfpg9Sp0vjsyX3TCw6DSa5x+oZw8w4lPfw==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
'@next/swc-linux-x64-musl@15.3.0-canary.29':
|
'@next/swc-linux-x64-musl@15.3.0-canary.33':
|
||||||
resolution: {integrity: sha512-bS92i+EPGq3yj9uME/h8j0rd1h1SyuPGnchS4NyV+GOW34JA78lrCL8NttyFcVhBU180vxn++gP0CroadXRIFg==}
|
resolution: {integrity: sha512-3UEL+tjNYKL/OG1hlR69C6S5voe9CXNX60O9Irl7TAZAxozdN+J3PrsfZ07QRduQIze0VORWpAh90LNnqXp1FQ==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
'@next/swc-win32-arm64-msvc@15.3.0-canary.29':
|
'@next/swc-win32-arm64-msvc@15.3.0-canary.33':
|
||||||
resolution: {integrity: sha512-DI0kO4fsJduROSCNQgwzJmgXh2mjGItHDj3Q3SMyVhFcfTWGtbdD4ZXPkCp/6+T3Rso4WkIUefx+vgWbcP8TLg==}
|
resolution: {integrity: sha512-FQHFbBi340K4uAR/M7FbzHMnucXsPyuns2PKMy9PLaZKgXiBDkVAurI4FRlIKspG35vddnqybn4K/1BMqW4mWg==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
|
|
||||||
'@next/swc-win32-x64-msvc@15.3.0-canary.29':
|
'@next/swc-win32-x64-msvc@15.3.0-canary.33':
|
||||||
resolution: {integrity: sha512-Xudgxsm4iNyNynZ2ZuMeCxMoPnkkRPgdXg24WwrkYM+kzlZi28sGX2BV7Nc2QNopDh7NRPIgpMgyeabJMJRqbQ==}
|
resolution: {integrity: sha512-sctiGfzVmVMw+7995NzH5UM+kQr7bz0AJN07pVaubiT9ByRhcsp7WLnyWEVs2NYQWzVpHEfNwHs5PbZ4w0C3FQ==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
@ -1534,8 +1534,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==}
|
resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
eslint-config-next@15.3.0-canary.29:
|
eslint-config-next@15.3.0-canary.33:
|
||||||
resolution: {integrity: sha512-s0NPH9ZaNrugNbLGkDaBNokDKMSM8723Wzkj2WyQ4TZD0YSbqbEVqivmTN5GolGyrmDO4oxf7baZOGvhnJvRbw==}
|
resolution: {integrity: sha512-V3/yanFbPrLekEv93yp5WhJhMcE3Y5Ah633N9GZ3ueWAyT/VFd7BO5VRnHZlC9E2eIUSkD0At2OCXqVWr3mfzw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
eslint: ^7.23.0 || ^8.0.0 || ^9.0.0
|
eslint: ^7.23.0 || ^8.0.0 || ^9.0.0
|
||||||
typescript: '>=3.3.1'
|
typescript: '>=3.3.1'
|
||||||
@ -2624,8 +2624,8 @@ packages:
|
|||||||
natural-compare@1.4.0:
|
natural-compare@1.4.0:
|
||||||
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
|
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
|
||||||
|
|
||||||
next@15.3.0-canary.29:
|
next@15.3.0-canary.33:
|
||||||
resolution: {integrity: sha512-TuZ5SWBVd7Imwvq1d/4YI6suKLFwVHAEZbqRJwI+qC0g4nQlRVQ8v4ZVPpwSYQmDahaJqZvWo6mqQCVcRYeRaA==}
|
resolution: {integrity: sha512-5Yc/W1hqOgibDoxnLiOvKHGId76/F+SrvlbZSw0LHhsmWYat6qAEaxv28vlHxj9OiRBqtrp0Ydsb+6RmYjv6IA==}
|
||||||
engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0}
|
engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -4156,48 +4156,48 @@ snapshots:
|
|||||||
'@tybys/wasm-util': 0.9.0
|
'@tybys/wasm-util': 0.9.0
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@next/bundle-analyzer@15.3.0-canary.29':
|
'@next/bundle-analyzer@15.3.0-canary.33':
|
||||||
dependencies:
|
dependencies:
|
||||||
webpack-bundle-analyzer: 4.10.1
|
webpack-bundle-analyzer: 4.10.1
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- bufferutil
|
- bufferutil
|
||||||
- utf-8-validate
|
- utf-8-validate
|
||||||
|
|
||||||
'@next/env@15.3.0-canary.29': {}
|
'@next/env@15.3.0-canary.33': {}
|
||||||
|
|
||||||
'@next/eslint-plugin-next@15.3.0-canary.29':
|
'@next/eslint-plugin-next@15.3.0-canary.33':
|
||||||
dependencies:
|
dependencies:
|
||||||
fast-glob: 3.3.1
|
fast-glob: 3.3.1
|
||||||
|
|
||||||
'@next/mdx@15.3.0-canary.29(@mdx-js/loader@3.1.0(acorn@8.14.1))(@mdx-js/react@3.1.0(@types/react@19.1.0)(react@19.1.0))':
|
'@next/mdx@15.3.0-canary.33(@mdx-js/loader@3.1.0(acorn@8.14.1))(@mdx-js/react@3.1.0(@types/react@19.1.0)(react@19.1.0))':
|
||||||
dependencies:
|
dependencies:
|
||||||
source-map: 0.7.4
|
source-map: 0.7.4
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@mdx-js/loader': 3.1.0(acorn@8.14.1)
|
'@mdx-js/loader': 3.1.0(acorn@8.14.1)
|
||||||
'@mdx-js/react': 3.1.0(@types/react@19.1.0)(react@19.1.0)
|
'@mdx-js/react': 3.1.0(@types/react@19.1.0)(react@19.1.0)
|
||||||
|
|
||||||
'@next/swc-darwin-arm64@15.3.0-canary.29':
|
'@next/swc-darwin-arm64@15.3.0-canary.33':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@next/swc-darwin-x64@15.3.0-canary.29':
|
'@next/swc-darwin-x64@15.3.0-canary.33':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@next/swc-linux-arm64-gnu@15.3.0-canary.29':
|
'@next/swc-linux-arm64-gnu@15.3.0-canary.33':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@next/swc-linux-arm64-musl@15.3.0-canary.29':
|
'@next/swc-linux-arm64-musl@15.3.0-canary.33':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@next/swc-linux-x64-gnu@15.3.0-canary.29':
|
'@next/swc-linux-x64-gnu@15.3.0-canary.33':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@next/swc-linux-x64-musl@15.3.0-canary.29':
|
'@next/swc-linux-x64-musl@15.3.0-canary.33':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@next/swc-win32-arm64-msvc@15.3.0-canary.29':
|
'@next/swc-win32-arm64-msvc@15.3.0-canary.33':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@next/swc-win32-x64-msvc@15.3.0-canary.29':
|
'@next/swc-win32-x64-msvc@15.3.0-canary.33':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@nodelib/fs.scandir@2.1.5':
|
'@nodelib/fs.scandir@2.1.5':
|
||||||
@ -5112,9 +5112,9 @@ snapshots:
|
|||||||
|
|
||||||
escape-string-regexp@5.0.0: {}
|
escape-string-regexp@5.0.0: {}
|
||||||
|
|
||||||
eslint-config-next@15.3.0-canary.29(eslint@9.23.0)(typescript@5.8.2):
|
eslint-config-next@15.3.0-canary.33(eslint@9.23.0)(typescript@5.8.2):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@next/eslint-plugin-next': 15.3.0-canary.29
|
'@next/eslint-plugin-next': 15.3.0-canary.33
|
||||||
'@rushstack/eslint-patch': 1.11.0
|
'@rushstack/eslint-patch': 1.11.0
|
||||||
'@typescript-eslint/eslint-plugin': 8.29.0(@typescript-eslint/parser@8.29.0(eslint@9.23.0)(typescript@5.8.2))(eslint@9.23.0)(typescript@5.8.2)
|
'@typescript-eslint/eslint-plugin': 8.29.0(@typescript-eslint/parser@8.29.0(eslint@9.23.0)(typescript@5.8.2))(eslint@9.23.0)(typescript@5.8.2)
|
||||||
'@typescript-eslint/parser': 8.29.0(eslint@9.23.0)(typescript@5.8.2)
|
'@typescript-eslint/parser': 8.29.0(eslint@9.23.0)(typescript@5.8.2)
|
||||||
@ -5531,9 +5531,9 @@ snapshots:
|
|||||||
|
|
||||||
functions-have-names@1.2.3: {}
|
functions-have-names@1.2.3: {}
|
||||||
|
|
||||||
geist@1.3.1(next@15.3.0-canary.29(@babel/core@7.26.10)(babel-plugin-react-compiler@19.0.0-beta-e993439-20250328)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)):
|
geist@1.3.1(next@15.3.0-canary.33(@babel/core@7.26.10)(babel-plugin-react-compiler@19.0.0-beta-e993439-20250328)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)):
|
||||||
dependencies:
|
dependencies:
|
||||||
next: 15.3.0-canary.29(@babel/core@7.26.10)(babel-plugin-react-compiler@19.0.0-beta-e993439-20250328)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
next: 15.3.0-canary.33(@babel/core@7.26.10)(babel-plugin-react-compiler@19.0.0-beta-e993439-20250328)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||||
|
|
||||||
gensync@1.0.0-beta.2: {}
|
gensync@1.0.0-beta.2: {}
|
||||||
|
|
||||||
@ -6664,9 +6664,9 @@ snapshots:
|
|||||||
|
|
||||||
natural-compare@1.4.0: {}
|
natural-compare@1.4.0: {}
|
||||||
|
|
||||||
next@15.3.0-canary.29(@babel/core@7.26.10)(babel-plugin-react-compiler@19.0.0-beta-e993439-20250328)(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
|
next@15.3.0-canary.33(@babel/core@7.26.10)(babel-plugin-react-compiler@19.0.0-beta-e993439-20250328)(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@next/env': 15.3.0-canary.29
|
'@next/env': 15.3.0-canary.33
|
||||||
'@swc/counter': 0.1.3
|
'@swc/counter': 0.1.3
|
||||||
'@swc/helpers': 0.5.15
|
'@swc/helpers': 0.5.15
|
||||||
busboy: 1.6.0
|
busboy: 1.6.0
|
||||||
@ -6676,14 +6676,14 @@ snapshots:
|
|||||||
react-dom: 19.1.0(react@19.1.0)
|
react-dom: 19.1.0(react@19.1.0)
|
||||||
styled-jsx: 5.1.6(@babel/core@7.26.10)(react@19.1.0)
|
styled-jsx: 5.1.6(@babel/core@7.26.10)(react@19.1.0)
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@next/swc-darwin-arm64': 15.3.0-canary.29
|
'@next/swc-darwin-arm64': 15.3.0-canary.33
|
||||||
'@next/swc-darwin-x64': 15.3.0-canary.29
|
'@next/swc-darwin-x64': 15.3.0-canary.33
|
||||||
'@next/swc-linux-arm64-gnu': 15.3.0-canary.29
|
'@next/swc-linux-arm64-gnu': 15.3.0-canary.33
|
||||||
'@next/swc-linux-arm64-musl': 15.3.0-canary.29
|
'@next/swc-linux-arm64-musl': 15.3.0-canary.33
|
||||||
'@next/swc-linux-x64-gnu': 15.3.0-canary.29
|
'@next/swc-linux-x64-gnu': 15.3.0-canary.33
|
||||||
'@next/swc-linux-x64-musl': 15.3.0-canary.29
|
'@next/swc-linux-x64-musl': 15.3.0-canary.33
|
||||||
'@next/swc-win32-arm64-msvc': 15.3.0-canary.29
|
'@next/swc-win32-arm64-msvc': 15.3.0-canary.33
|
||||||
'@next/swc-win32-x64-msvc': 15.3.0-canary.29
|
'@next/swc-win32-x64-msvc': 15.3.0-canary.33
|
||||||
babel-plugin-react-compiler: 19.0.0-beta-e993439-20250328
|
babel-plugin-react-compiler: 19.0.0-beta-e993439-20250328
|
||||||
sharp: 0.33.5
|
sharp: 0.33.5
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user