mirror of
https://github.com/jakejarvis/jarv.is.git
synced 2025-04-26 08:05:23 -04:00
zod ➡️ valibot (plus more server action tracing)
This commit is contained in:
parent
87a24a98f0
commit
444f91b6dc
@ -1,28 +1,27 @@
|
|||||||
"use server";
|
"use server";
|
||||||
|
|
||||||
import { headers } from "next/headers";
|
import { headers } from "next/headers";
|
||||||
import { z } from "zod";
|
import * as v from "valibot";
|
||||||
import { Resend } from "resend";
|
import { Resend } from "resend";
|
||||||
import * as Sentry from "@sentry/nextjs";
|
import * as Sentry from "@sentry/nextjs";
|
||||||
import * as config from "../../lib/config";
|
import * as config from "../../lib/config";
|
||||||
|
|
||||||
const schema = z.object({
|
const ContactSchema = v.object({
|
||||||
name: z.string().min(1, { message: "Name is required" }),
|
name: v.pipe(v.string(), v.nonEmpty("Your name is required.")),
|
||||||
email: z.string().email({ message: "Invalid email address" }),
|
email: v.pipe(v.string(), v.nonEmpty("Your email address is required."), v.email("Invalid email address.")),
|
||||||
message: z.string().min(1, { message: "Message is required" }),
|
message: v.pipe(v.string(), v.nonEmpty("A message is required.")),
|
||||||
["cf-turnstile-response"]: z.string().min(1, { message: "CAPTCHA not completed" }),
|
"cf-turnstile-response": v.pipe(v.string(), v.nonEmpty("Just do the stinkin CAPTCHA! 🤖")),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const sendMessage = async (
|
export type ContactInput = v.InferInput<typeof ContactSchema>;
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
prevState: any,
|
export type ContactState = {
|
||||||
formData: FormData
|
|
||||||
): Promise<{
|
|
||||||
success: boolean;
|
success: boolean;
|
||||||
message: string;
|
message: string;
|
||||||
errors?: z.inferFormattedError<typeof schema>;
|
errors?: v.FlatErrors<typeof ContactSchema>["nested"];
|
||||||
payload?: FormData;
|
};
|
||||||
}> => {
|
|
||||||
|
export const sendMessage = async (prevState: ContactState, formData: FormData): Promise<ContactState> => {
|
||||||
return await Sentry.withServerActionInstrumentation(
|
return await Sentry.withServerActionInstrumentation(
|
||||||
"sendMessage",
|
"sendMessage",
|
||||||
{
|
{
|
||||||
@ -32,17 +31,16 @@ export const sendMessage = async (
|
|||||||
},
|
},
|
||||||
async () => {
|
async () => {
|
||||||
try {
|
try {
|
||||||
const validatedFields = schema.safeParse(Object.fromEntries(formData));
|
const data = v.safeParse(ContactSchema, Object.fromEntries(formData));
|
||||||
|
|
||||||
// backup to client-side validations just in case someone squeezes through without them
|
// send raw valibot result to Sentry for debugging
|
||||||
if (!validatedFields.success) {
|
Sentry.captureMessage(JSON.stringify(data), "debug");
|
||||||
console.debug("[contact form] validation error:", validatedFields.error.flatten());
|
|
||||||
|
|
||||||
|
if (!data.success) {
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
message: "Please make sure that all fields are properly filled in.",
|
message: "Please make sure all fields are filled in.",
|
||||||
errors: validatedFields.error.format(),
|
errors: v.flatten(data.issues).nested,
|
||||||
payload: formData,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,14 +50,14 @@ export const sendMessage = async (
|
|||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
secret: process.env.TURNSTILE_SECRET_KEY || "1x0000000000000000000000000000000AA",
|
secret: process.env.TURNSTILE_SECRET_KEY || "1x0000000000000000000000000000000AA",
|
||||||
response: validatedFields.data["cf-turnstile-response"],
|
response: data.output["cf-turnstile-response"],
|
||||||
remoteip: (await headers()).get("x-forwarded-for") || "",
|
remoteip: (await headers()).get("x-forwarded-for") || "",
|
||||||
}),
|
}),
|
||||||
cache: "no-store",
|
cache: "no-store",
|
||||||
signal: AbortSignal.timeout(5000), // 5 second timeout
|
signal: AbortSignal.timeout(5000), // 5 second timeout
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!turnstileResponse.ok) {
|
if (!turnstileResponse || !turnstileResponse.ok) {
|
||||||
throw new Error(`[contact form] turnstile validation failed: ${turnstileResponse.status}`);
|
throw new Error(`[contact form] turnstile validation failed: ${turnstileResponse.status}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,7 +67,6 @@ export const sendMessage = async (
|
|||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
message: "Did you complete the CAPTCHA? (If you're human, that is...)",
|
message: "Did you complete the CAPTCHA? (If you're human, that is...)",
|
||||||
payload: formData,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,22 +77,20 @@ export const sendMessage = async (
|
|||||||
// send email
|
// send email
|
||||||
const resend = new Resend(process.env.RESEND_API_KEY);
|
const resend = new Resend(process.env.RESEND_API_KEY);
|
||||||
await resend.emails.send({
|
await resend.emails.send({
|
||||||
from: `${validatedFields.data.name} <${process.env.RESEND_FROM_EMAIL ?? "onboarding@resend.dev"}>`,
|
from: `${data.output.name} <${process.env.RESEND_FROM_EMAIL ?? "onboarding@resend.dev"}>`,
|
||||||
replyTo: `${validatedFields.data.name} <${validatedFields.data.email}>`,
|
replyTo: `${data.output.name} <${data.output.email}>`,
|
||||||
to: [config.authorEmail],
|
to: [config.authorEmail],
|
||||||
subject: `[${config.siteName}] Contact Form Submission`,
|
subject: `[${config.siteName}] Contact Form Submission`,
|
||||||
text: validatedFields.data.message,
|
text: data.output.message,
|
||||||
});
|
});
|
||||||
|
|
||||||
return { success: true, message: "Thanks! You should hear from me soon.", payload: formData };
|
return { success: true, message: "Thanks! You should hear from me soon." };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Sentry.captureException(error);
|
Sentry.captureException(error);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
message: "Internal server error... Try again later or shoot me an old-fashioned email?",
|
message: "Internal server error. Please try again later or shoot me an email.",
|
||||||
errors: error instanceof z.ZodError ? error.format() : undefined,
|
|
||||||
payload: formData,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,9 +24,15 @@
|
|||||||
border-color: var(--colors-error);
|
border-color: var(--colors-error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fieldError {
|
||||||
|
font-size: 0.9em;
|
||||||
|
color: var(--colors-error);
|
||||||
|
}
|
||||||
|
|
||||||
.actionRow {
|
.actionRow {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
margin-top: 0.6em;
|
||||||
min-height: 3.75em;
|
min-height: 3.75em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,20 +1,27 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useActionState } from "react";
|
import { useActionState, useState } from "react";
|
||||||
import TextareaAutosize from "react-textarea-autosize";
|
import TextareaAutosize from "react-textarea-autosize";
|
||||||
import Turnstile from "react-turnstile";
|
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 } from "./actions";
|
import { sendMessage, type ContactInput, type ContactState } from "./actions";
|
||||||
|
|
||||||
import styles from "./form.module.css";
|
import styles from "./form.module.css";
|
||||||
|
|
||||||
const ContactForm = () => {
|
const ContactForm = () => {
|
||||||
const [formState, formAction, pending] = useActionState<Awaited<ReturnType<typeof sendMessage>>, FormData>(
|
const [formState, formAction, pending] = useActionState<ContactState, FormData>(sendMessage, {
|
||||||
sendMessage,
|
success: false,
|
||||||
{ success: false, message: "" }
|
message: "",
|
||||||
);
|
});
|
||||||
|
|
||||||
|
const [formFields, setFormFields] = useState<ContactInput>({
|
||||||
|
name: "",
|
||||||
|
email: "",
|
||||||
|
message: "",
|
||||||
|
"cf-turnstile-response": "",
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form action={formAction}>
|
<form action={formAction}>
|
||||||
@ -22,32 +29,44 @@ const ContactForm = () => {
|
|||||||
type="text"
|
type="text"
|
||||||
name="name"
|
name="name"
|
||||||
placeholder="Name"
|
placeholder="Name"
|
||||||
required
|
// required
|
||||||
className={clsx(styles.input, formState?.errors?.name && styles.invalid)}
|
value={formFields.name}
|
||||||
defaultValue={(formState?.payload?.get("name") || "") as string}
|
onChange={(e) => {
|
||||||
disabled={formState?.success}
|
setFormFields({ ...formFields, name: e.target.value });
|
||||||
|
}}
|
||||||
|
disabled={pending || formState.success}
|
||||||
|
className={clsx(styles.input, formState.errors?.name && styles.invalid)}
|
||||||
/>
|
/>
|
||||||
|
{formState.errors?.name && <span className={styles.fieldError}>{formState.errors.name[0]}</span>}
|
||||||
|
|
||||||
<input
|
<input
|
||||||
type="email"
|
type="email"
|
||||||
name="email"
|
name="email"
|
||||||
placeholder="Email"
|
placeholder="Email"
|
||||||
required
|
// required
|
||||||
inputMode="email"
|
inputMode="email"
|
||||||
className={clsx(styles.input, formState?.errors?.email && styles.invalid)}
|
value={formFields.email}
|
||||||
defaultValue={(formState?.payload?.get("email") || "") as string}
|
onChange={(e) => {
|
||||||
disabled={formState?.success}
|
setFormFields({ ...formFields, email: e.target.value });
|
||||||
|
}}
|
||||||
|
disabled={pending || formState.success}
|
||||||
|
className={clsx(styles.input, formState.errors?.email && styles.invalid)}
|
||||||
/>
|
/>
|
||||||
|
{formState.errors?.email && <span className={styles.fieldError}>{formState.errors.email[0]}</span>}
|
||||||
|
|
||||||
<TextareaAutosize
|
<TextareaAutosize
|
||||||
name="message"
|
name="message"
|
||||||
placeholder="Write something..."
|
placeholder="Write something..."
|
||||||
minRows={5}
|
minRows={5}
|
||||||
required
|
// required
|
||||||
className={clsx(styles.input, styles.textarea, formState?.errors?.message && styles.invalid)}
|
value={formFields.message}
|
||||||
defaultValue={(formState?.payload?.get("message") || "") as string}
|
onChange={(e) => {
|
||||||
disabled={formState?.success}
|
setFormFields({ ...formFields, message: e.target.value });
|
||||||
|
}}
|
||||||
|
disabled={pending || formState.success}
|
||||||
|
className={clsx(styles.input, styles.textarea, formState.errors?.message && styles.invalid)}
|
||||||
/>
|
/>
|
||||||
|
{formState.errors?.message && <span className={styles.fieldError}>{formState.errors.message[0]}</span>}
|
||||||
|
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
@ -78,7 +97,7 @@ const ContactForm = () => {
|
|||||||
Markdown syntax
|
Markdown syntax
|
||||||
</Link>{" "}
|
</Link>{" "}
|
||||||
is allowed here, e.g.: <strong>**bold**</strong>, <em>_italics_</em>, [
|
is allowed here, e.g.: <strong>**bold**</strong>, <em>_italics_</em>, [
|
||||||
<Link href="https://jarv.is" plain>
|
<Link href="https://jarv.is" plain openInNewTab>
|
||||||
links
|
links
|
||||||
</Link>
|
</Link>
|
||||||
](https://jarv.is), and <code>`code`</code>.
|
](https://jarv.is), and <code>`code`</code>.
|
||||||
@ -87,6 +106,9 @@ const ContactForm = () => {
|
|||||||
<div style={{ margin: "1em 0" }}>
|
<div style={{ margin: "1em 0" }}>
|
||||||
<Turnstile sitekey={process.env.NEXT_PUBLIC_TURNSTILE_SITE_KEY || "1x00000000000000000000AA"} fixedSize />
|
<Turnstile sitekey={process.env.NEXT_PUBLIC_TURNSTILE_SITE_KEY || "1x00000000000000000000AA"} fixedSize />
|
||||||
</div>
|
</div>
|
||||||
|
{formState.errors?.["cf-turnstile-response"] && (
|
||||||
|
<span className={styles.fieldError}>{formState.errors["cf-turnstile-response"][0]}</span>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className={styles.actionRow}>
|
<div className={styles.actionRow}>
|
||||||
{!formState?.success && (
|
{!formState?.success && (
|
||||||
@ -109,14 +131,14 @@ const ContactForm = () => {
|
|||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
{formState?.message && (
|
{formState.message && (
|
||||||
<div className={clsx(styles.result, formState?.success ? styles.success : styles.error)}>
|
<div className={clsx(styles.result, formState.success ? styles.success : styles.error)}>
|
||||||
{formState?.success ? (
|
{formState.success ? (
|
||||||
<CheckIcon size="1.3em" className={styles.resultIcon} />
|
<CheckIcon size="1.3em" className={styles.resultIcon} />
|
||||||
) : (
|
) : (
|
||||||
<XIcon size="1.3em" className={styles.resultIcon} />
|
<XIcon size="1.3em" className={styles.resultIcon} />
|
||||||
)}{" "}
|
)}{" "}
|
||||||
{formState?.message}
|
{formState.message}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,29 +1,8 @@
|
|||||||
// This file configures the initialization of Sentry on the client.
|
|
||||||
// The added config here will be used whenever a users loads a page in their browser.
|
|
||||||
// https://docs.sentry.io/platforms/javascript/guides/nextjs/
|
|
||||||
|
|
||||||
import * as Sentry from "@sentry/nextjs";
|
import * as Sentry from "@sentry/nextjs";
|
||||||
|
|
||||||
Sentry.init({
|
Sentry.init({
|
||||||
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN || process.env.SENTRY_DSN,
|
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
|
||||||
integrations: [
|
environment: process.env.NEXT_PUBLIC_VERCEL_ENV,
|
||||||
Sentry.replayIntegration({
|
integrations: [Sentry.browserTracingIntegration(), Sentry.httpClientIntegration()],
|
||||||
networkDetailAllowUrls: [process.env.NEXT_PUBLIC_VERCEL_PROJECT_PRODUCTION_URL!],
|
|
||||||
networkRequestHeaders: ["referer", "origin", "user-agent", "x-upstream-proxy"],
|
|
||||||
networkResponseHeaders: [
|
|
||||||
"location",
|
|
||||||
"x-matched-path",
|
|
||||||
"x-nextjs-prerender",
|
|
||||||
"x-vercel-cache",
|
|
||||||
"x-vercel-id",
|
|
||||||
"x-vercel-error",
|
|
||||||
"x-rewrite-url",
|
|
||||||
"x-got-milk",
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
tracesSampleRate: 1.0,
|
tracesSampleRate: 1.0,
|
||||||
replaysSessionSampleRate: 0,
|
|
||||||
replaysOnErrorSampleRate: 1.0,
|
|
||||||
debug: false,
|
|
||||||
});
|
});
|
||||||
|
@ -2,12 +2,23 @@ import * as Sentry from "@sentry/nextjs";
|
|||||||
|
|
||||||
export const onRequestError = Sentry.captureRequestError;
|
export const onRequestError = Sentry.captureRequestError;
|
||||||
|
|
||||||
export const register = async () => {
|
export const register = () => {
|
||||||
if (process.env.NEXT_RUNTIME === "nodejs") {
|
Sentry.init({
|
||||||
await import("./sentry.server.config");
|
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN || process.env.SENTRY_DSN,
|
||||||
}
|
environment: process.env.NEXT_PUBLIC_VERCEL_ENV,
|
||||||
|
integrations: [Sentry.captureConsoleIntegration()],
|
||||||
|
tracesSampleRate: 1.0,
|
||||||
|
// https://docs.sentry.io/platforms/javascript/guides/nextjs/configuration/options/#normalizeDepth
|
||||||
|
normalizeDepth: 5,
|
||||||
|
});
|
||||||
|
|
||||||
if (process.env.NEXT_RUNTIME === "edge") {
|
if (process.env.NEXT_RUNTIME === "nodejs") {
|
||||||
await import("./sentry.edge.config");
|
// filesystem is only available in nodejs runtime
|
||||||
|
// https://docs.sentry.io/platforms/javascript/guides/nextjs/configuration/integrations/fs/
|
||||||
|
Sentry.addIntegration(
|
||||||
|
Sentry.fsIntegration({
|
||||||
|
recordFilePaths: true,
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -212,6 +212,9 @@ const nextPlugins: Array<NextPlugin | [NextPlugin, any]> = [
|
|||||||
widenClientFileUpload: true,
|
widenClientFileUpload: true,
|
||||||
disableLogger: true,
|
disableLogger: true,
|
||||||
telemetry: false,
|
telemetry: false,
|
||||||
|
autoInstrumentAppDirectory: true,
|
||||||
|
autoInstrumentServerFunctions: true,
|
||||||
|
autoInstrumentMiddleware: false,
|
||||||
bundleSizeOptimizations: {
|
bundleSizeOptimizations: {
|
||||||
excludeDebugStatements: true,
|
excludeDebugStatements: true,
|
||||||
},
|
},
|
||||||
|
@ -69,12 +69,13 @@
|
|||||||
"to-vfile": "^8.0.0",
|
"to-vfile": "^8.0.0",
|
||||||
"unified": "^11.0.5",
|
"unified": "^11.0.5",
|
||||||
"unist-util-visit": "^5.0.0",
|
"unist-util-visit": "^5.0.0",
|
||||||
"zod": "^3.24.2"
|
"valibot": "^1.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/eslintrc": "^3.3.1",
|
"@eslint/eslintrc": "^3.3.1",
|
||||||
"@eslint/js": "^9.23.0",
|
"@eslint/js": "^9.23.0",
|
||||||
"@jakejarvis/eslint-config": "^4.0.7",
|
"@jakejarvis/eslint-config": "^4.0.7",
|
||||||
|
"@sentry/cli": "^2.43.0",
|
||||||
"@types/comma-number": "^2.1.2",
|
"@types/comma-number": "^2.1.2",
|
||||||
"@types/mdx": "^2.0.13",
|
"@types/mdx": "^2.0.13",
|
||||||
"@types/node": "^22.13.14",
|
"@types/node": "^22.13.14",
|
||||||
|
117
pnpm-lock.yaml
generated
117
pnpm-lock.yaml
generated
@ -164,9 +164,9 @@ importers:
|
|||||||
unist-util-visit:
|
unist-util-visit:
|
||||||
specifier: ^5.0.0
|
specifier: ^5.0.0
|
||||||
version: 5.0.0
|
version: 5.0.0
|
||||||
zod:
|
valibot:
|
||||||
specifier: ^3.24.2
|
specifier: ^1.0.0
|
||||||
version: 3.24.2
|
version: 1.0.0(typescript@5.8.2)
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@eslint/eslintrc':
|
'@eslint/eslintrc':
|
||||||
specifier: ^3.3.1
|
specifier: ^3.3.1
|
||||||
@ -177,6 +177,9 @@ importers:
|
|||||||
'@jakejarvis/eslint-config':
|
'@jakejarvis/eslint-config':
|
||||||
specifier: ^4.0.7
|
specifier: ^4.0.7
|
||||||
version: 4.0.7(eslint@9.23.0)
|
version: 4.0.7(eslint@9.23.0)
|
||||||
|
'@sentry/cli':
|
||||||
|
specifier: ^2.43.0
|
||||||
|
version: 2.43.0
|
||||||
'@types/comma-number':
|
'@types/comma-number':
|
||||||
specifier: ^2.1.2
|
specifier: ^2.1.2
|
||||||
version: 2.1.2
|
version: 2.1.2
|
||||||
@ -1168,47 +1171,99 @@ packages:
|
|||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
os: [darwin]
|
os: [darwin]
|
||||||
|
|
||||||
|
'@sentry/cli-darwin@2.43.0':
|
||||||
|
resolution: {integrity: sha512-0MYvRHJowXOMNY5W6XF4p9GQNH3LuQ+IHAQwVbZOsfwnEv8e20rf9BiPPzmJ9sIjZSWYR4yIqm6dBp6ABJFbGQ==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
os: [darwin]
|
||||||
|
|
||||||
'@sentry/cli-linux-arm64@2.42.2':
|
'@sentry/cli-linux-arm64@2.42.2':
|
||||||
resolution: {integrity: sha512-BOxzI7sgEU5Dhq3o4SblFXdE9zScpz6EXc5Zwr1UDZvzgXZGosUtKVc7d1LmkrHP8Q2o18HcDWtF3WvJRb5Zpw==}
|
resolution: {integrity: sha512-BOxzI7sgEU5Dhq3o4SblFXdE9zScpz6EXc5Zwr1UDZvzgXZGosUtKVc7d1LmkrHP8Q2o18HcDWtF3WvJRb5Zpw==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux, freebsd]
|
os: [linux, freebsd]
|
||||||
|
|
||||||
|
'@sentry/cli-linux-arm64@2.43.0':
|
||||||
|
resolution: {integrity: sha512-7URSaNjbEJQZyYJ33XK3pVKl6PU2oO9ETF6R/4Cz2FmU3fecACLKVldv7+OuNl9aspLZ62mnPMDvT732/Fp2Ug==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [linux, freebsd]
|
||||||
|
|
||||||
'@sentry/cli-linux-arm@2.42.2':
|
'@sentry/cli-linux-arm@2.42.2':
|
||||||
resolution: {integrity: sha512-7udCw+YL9lwq+9eL3WLspvnuG+k5Icg92YE7zsteTzWLwgPVzaxeZD2f8hwhsu+wmL+jNqbpCRmktPteh3i2mg==}
|
resolution: {integrity: sha512-7udCw+YL9lwq+9eL3WLspvnuG+k5Icg92YE7zsteTzWLwgPVzaxeZD2f8hwhsu+wmL+jNqbpCRmktPteh3i2mg==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
cpu: [arm]
|
cpu: [arm]
|
||||||
os: [linux, freebsd]
|
os: [linux, freebsd]
|
||||||
|
|
||||||
|
'@sentry/cli-linux-arm@2.43.0':
|
||||||
|
resolution: {integrity: sha512-c2Fwb6HrFL1nbaGV4uRhHC1wEJPR+wfpKN5y06PgSNNbd10YrECAB3tqBHXC8CEmhuDyFR+ORGZ7VbswfCWEEQ==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
cpu: [arm]
|
||||||
|
os: [linux, freebsd]
|
||||||
|
|
||||||
'@sentry/cli-linux-i686@2.42.2':
|
'@sentry/cli-linux-i686@2.42.2':
|
||||||
resolution: {integrity: sha512-Sw/dQp5ZPvKnq3/y7wIJyxTUJYPGoTX/YeMbDs8BzDlu9to2LWV3K3r7hE7W1Lpbaw4tSquUHiQjP5QHCOS7aQ==}
|
resolution: {integrity: sha512-Sw/dQp5ZPvKnq3/y7wIJyxTUJYPGoTX/YeMbDs8BzDlu9to2LWV3K3r7hE7W1Lpbaw4tSquUHiQjP5QHCOS7aQ==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
cpu: [x86, ia32]
|
cpu: [x86, ia32]
|
||||||
os: [linux, freebsd]
|
os: [linux, freebsd]
|
||||||
|
|
||||||
|
'@sentry/cli-linux-i686@2.43.0':
|
||||||
|
resolution: {integrity: sha512-bFo/tpMZeMJ275HPGmAENREchnBxhALOOpZAphSyalUu3pGZ+EETEtlSLrKyVNJo26Dye5W7GlrYUV9+rkyCtg==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
cpu: [x86, ia32]
|
||||||
|
os: [linux, freebsd]
|
||||||
|
|
||||||
'@sentry/cli-linux-x64@2.42.2':
|
'@sentry/cli-linux-x64@2.42.2':
|
||||||
resolution: {integrity: sha512-mU4zUspAal6TIwlNLBV5oq6yYqiENnCWSxtSQVzWs0Jyq97wtqGNG9U+QrnwjJZ+ta/hvye9fvL2X25D/RxHQw==}
|
resolution: {integrity: sha512-mU4zUspAal6TIwlNLBV5oq6yYqiENnCWSxtSQVzWs0Jyq97wtqGNG9U+QrnwjJZ+ta/hvye9fvL2X25D/RxHQw==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux, freebsd]
|
os: [linux, freebsd]
|
||||||
|
|
||||||
|
'@sentry/cli-linux-x64@2.43.0':
|
||||||
|
resolution: {integrity: sha512-EbAmKXUNU/Ii4pNGVRCepU6ks1M43wStMKx3pibrUTllrrCwqYKyPxRRdoFYySHkduwCxnoKZcLEg9vWZ3qS6A==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [linux, freebsd]
|
||||||
|
|
||||||
|
'@sentry/cli-win32-arm64@2.43.0':
|
||||||
|
resolution: {integrity: sha512-KmJRCdQQGLSErJvrcGcN+yWo68m+5OdluhyJHsVYMOQknwu8YMOWLm12EIa+4t4GclDvwg5xcxLccCuiWMJUZw==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [win32]
|
||||||
|
|
||||||
'@sentry/cli-win32-i686@2.42.2':
|
'@sentry/cli-win32-i686@2.42.2':
|
||||||
resolution: {integrity: sha512-iHvFHPGqgJMNqXJoQpqttfsv2GI3cGodeTq4aoVLU/BT3+hXzbV0x1VpvvEhncJkDgDicJpFLM8sEPHb3b8abw==}
|
resolution: {integrity: sha512-iHvFHPGqgJMNqXJoQpqttfsv2GI3cGodeTq4aoVLU/BT3+hXzbV0x1VpvvEhncJkDgDicJpFLM8sEPHb3b8abw==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
cpu: [x86, ia32]
|
cpu: [x86, ia32]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
|
|
||||||
|
'@sentry/cli-win32-i686@2.43.0':
|
||||||
|
resolution: {integrity: sha512-ZWxZdOyZX7NJ/CTskzg+dJ2xTpobFLXVNMOMq0HiwdhqXP2zYYJzKnIt3mHNJYA40zYFODGSgxIamodjpB8BuA==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
cpu: [x86, ia32]
|
||||||
|
os: [win32]
|
||||||
|
|
||||||
'@sentry/cli-win32-x64@2.42.2':
|
'@sentry/cli-win32-x64@2.42.2':
|
||||||
resolution: {integrity: sha512-vPPGHjYoaGmfrU7xhfFxG7qlTBacroz5NdT+0FmDn6692D8IvpNXl1K+eV3Kag44ipJBBeR8g1HRJyx/F/9ACw==}
|
resolution: {integrity: sha512-vPPGHjYoaGmfrU7xhfFxG7qlTBacroz5NdT+0FmDn6692D8IvpNXl1K+eV3Kag44ipJBBeR8g1HRJyx/F/9ACw==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
|
|
||||||
|
'@sentry/cli-win32-x64@2.43.0':
|
||||||
|
resolution: {integrity: sha512-S/IRQYAziEnjpyROhnqzTqShDq3m8jcevXx+q5f49uQnFbfYcTgS1sdrEPqqao/K2boOWbffxYtTkvBiB/piQQ==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [win32]
|
||||||
|
|
||||||
'@sentry/cli@2.42.2':
|
'@sentry/cli@2.42.2':
|
||||||
resolution: {integrity: sha512-spb7S/RUumCGyiSTg8DlrCX4bivCNmU/A1hcfkwuciTFGu8l5CDc2I6jJWWZw8/0enDGxuj5XujgXvU5tr4bxg==}
|
resolution: {integrity: sha512-spb7S/RUumCGyiSTg8DlrCX4bivCNmU/A1hcfkwuciTFGu8l5CDc2I6jJWWZw8/0enDGxuj5XujgXvU5tr4bxg==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
'@sentry/cli@2.43.0':
|
||||||
|
resolution: {integrity: sha512-gBE3bkx+PBJxopTrzIJLX4xHe5S0w87q5frIveWKDZ5ulVIU6YWnVumay0y07RIEweUEj3IYva1qH6HG2abfiA==}
|
||||||
|
engines: {node: '>= 10'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
'@sentry/core@9.10.1':
|
'@sentry/core@9.10.1':
|
||||||
resolution: {integrity: sha512-TE2zZV3Od4131mZNgFo2Mv4aKU8FXxL0s96yqRvmV+8AU57mJoycMXBnmNSYfWuDICbPJTVAp+3bYMXwX7N5YA==}
|
resolution: {integrity: sha512-TE2zZV3Od4131mZNgFo2Mv4aKU8FXxL0s96yqRvmV+8AU57mJoycMXBnmNSYfWuDICbPJTVAp+3bYMXwX7N5YA==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
@ -4377,6 +4432,14 @@ packages:
|
|||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
valibot@1.0.0:
|
||||||
|
resolution: {integrity: sha512-1Hc0ihzWxBar6NGeZv7fPLY0QuxFMyxwYR2sF1Blu7Wq7EnremwY2W02tit2ij2VJT8HcSkHAQqmFfl77f73Yw==}
|
||||||
|
peerDependencies:
|
||||||
|
typescript: '>=5'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
typescript:
|
||||||
|
optional: true
|
||||||
|
|
||||||
validate-npm-package-license@3.0.4:
|
validate-npm-package-license@3.0.4:
|
||||||
resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==}
|
resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==}
|
||||||
|
|
||||||
@ -5528,24 +5591,48 @@ snapshots:
|
|||||||
'@sentry/cli-darwin@2.42.2':
|
'@sentry/cli-darwin@2.42.2':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@sentry/cli-darwin@2.43.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@sentry/cli-linux-arm64@2.42.2':
|
'@sentry/cli-linux-arm64@2.42.2':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@sentry/cli-linux-arm64@2.43.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@sentry/cli-linux-arm@2.42.2':
|
'@sentry/cli-linux-arm@2.42.2':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@sentry/cli-linux-arm@2.43.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@sentry/cli-linux-i686@2.42.2':
|
'@sentry/cli-linux-i686@2.42.2':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@sentry/cli-linux-i686@2.43.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@sentry/cli-linux-x64@2.42.2':
|
'@sentry/cli-linux-x64@2.42.2':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@sentry/cli-linux-x64@2.43.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@sentry/cli-win32-arm64@2.43.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@sentry/cli-win32-i686@2.42.2':
|
'@sentry/cli-win32-i686@2.42.2':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@sentry/cli-win32-i686@2.43.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@sentry/cli-win32-x64@2.42.2':
|
'@sentry/cli-win32-x64@2.42.2':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@sentry/cli-win32-x64@2.43.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@sentry/cli@2.42.2':
|
'@sentry/cli@2.42.2':
|
||||||
dependencies:
|
dependencies:
|
||||||
https-proxy-agent: 5.0.1
|
https-proxy-agent: 5.0.1
|
||||||
@ -5565,6 +5652,26 @@ snapshots:
|
|||||||
- encoding
|
- encoding
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
|
'@sentry/cli@2.43.0':
|
||||||
|
dependencies:
|
||||||
|
https-proxy-agent: 5.0.1
|
||||||
|
node-fetch: 2.7.0
|
||||||
|
progress: 2.0.3
|
||||||
|
proxy-from-env: 1.1.0
|
||||||
|
which: 2.0.2
|
||||||
|
optionalDependencies:
|
||||||
|
'@sentry/cli-darwin': 2.43.0
|
||||||
|
'@sentry/cli-linux-arm': 2.43.0
|
||||||
|
'@sentry/cli-linux-arm64': 2.43.0
|
||||||
|
'@sentry/cli-linux-i686': 2.43.0
|
||||||
|
'@sentry/cli-linux-x64': 2.43.0
|
||||||
|
'@sentry/cli-win32-arm64': 2.43.0
|
||||||
|
'@sentry/cli-win32-i686': 2.43.0
|
||||||
|
'@sentry/cli-win32-x64': 2.43.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- encoding
|
||||||
|
- supports-color
|
||||||
|
|
||||||
'@sentry/core@9.10.1': {}
|
'@sentry/core@9.10.1': {}
|
||||||
|
|
||||||
'@sentry/nextjs@9.10.1(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(next@15.3.0-canary.25(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@19.0.0-beta-aeaed83-20250323)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)(webpack@5.98.0)':
|
'@sentry/nextjs@9.10.1(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(next@15.3.0-canary.25(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@19.0.0-beta-aeaed83-20250323)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)(webpack@5.98.0)':
|
||||||
@ -9672,6 +9779,10 @@ snapshots:
|
|||||||
kleur: 4.1.5
|
kleur: 4.1.5
|
||||||
sade: 1.8.1
|
sade: 1.8.1
|
||||||
|
|
||||||
|
valibot@1.0.0(typescript@5.8.2):
|
||||||
|
optionalDependencies:
|
||||||
|
typescript: 5.8.2
|
||||||
|
|
||||||
validate-npm-package-license@3.0.4:
|
validate-npm-package-license@3.0.4:
|
||||||
dependencies:
|
dependencies:
|
||||||
spdx-correct: 3.2.0
|
spdx-correct: 3.2.0
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
// This file configures the initialization of Sentry for edge features (middleware, edge routes, and so on).
|
|
||||||
// The config you add here will be used whenever one of the edge features is loaded.
|
|
||||||
// Note that this config is unrelated to the Vercel Edge Runtime and is also required when running locally.
|
|
||||||
// https://docs.sentry.io/platforms/javascript/guides/nextjs/
|
|
||||||
|
|
||||||
import * as Sentry from "@sentry/nextjs";
|
|
||||||
|
|
||||||
Sentry.init({
|
|
||||||
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN || process.env.SENTRY_DSN,
|
|
||||||
tracesSampleRate: 1.0,
|
|
||||||
debug: false,
|
|
||||||
});
|
|
@ -1,11 +0,0 @@
|
|||||||
// This file configures the initialization of Sentry on the server.
|
|
||||||
// The config you add here will be used whenever the server handles a request.
|
|
||||||
// https://docs.sentry.io/platforms/javascript/guides/nextjs/
|
|
||||||
|
|
||||||
import * as Sentry from "@sentry/nextjs";
|
|
||||||
|
|
||||||
Sentry.init({
|
|
||||||
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN || process.env.SENTRY_DSN,
|
|
||||||
tracesSampleRate: 1.0,
|
|
||||||
debug: false,
|
|
||||||
});
|
|
Loading…
x
Reference in New Issue
Block a user