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";
|
||||
|
||||
import { headers } from "next/headers";
|
||||
import { z } from "zod";
|
||||
import * as v from "valibot";
|
||||
import { Resend } from "resend";
|
||||
import * as Sentry from "@sentry/nextjs";
|
||||
import * as config from "../../lib/config";
|
||||
|
||||
const schema = z.object({
|
||||
name: z.string().min(1, { message: "Name is required" }),
|
||||
email: z.string().email({ message: "Invalid email address" }),
|
||||
message: z.string().min(1, { message: "Message is required" }),
|
||||
["cf-turnstile-response"]: z.string().min(1, { message: "CAPTCHA not completed" }),
|
||||
const ContactSchema = v.object({
|
||||
name: v.pipe(v.string(), v.nonEmpty("Your name is required.")),
|
||||
email: v.pipe(v.string(), v.nonEmpty("Your email address is required."), v.email("Invalid email address.")),
|
||||
message: v.pipe(v.string(), v.nonEmpty("A message is required.")),
|
||||
"cf-turnstile-response": v.pipe(v.string(), v.nonEmpty("Just do the stinkin CAPTCHA! 🤖")),
|
||||
});
|
||||
|
||||
export const sendMessage = async (
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
prevState: any,
|
||||
formData: FormData
|
||||
): Promise<{
|
||||
export type ContactInput = v.InferInput<typeof ContactSchema>;
|
||||
|
||||
export type ContactState = {
|
||||
success: boolean;
|
||||
message: string;
|
||||
errors?: z.inferFormattedError<typeof schema>;
|
||||
payload?: FormData;
|
||||
}> => {
|
||||
errors?: v.FlatErrors<typeof ContactSchema>["nested"];
|
||||
};
|
||||
|
||||
export const sendMessage = async (prevState: ContactState, formData: FormData): Promise<ContactState> => {
|
||||
return await Sentry.withServerActionInstrumentation(
|
||||
"sendMessage",
|
||||
{
|
||||
@ -32,17 +31,16 @@ export const sendMessage = async (
|
||||
},
|
||||
async () => {
|
||||
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
|
||||
if (!validatedFields.success) {
|
||||
console.debug("[contact form] validation error:", validatedFields.error.flatten());
|
||||
// send raw valibot result to Sentry for debugging
|
||||
Sentry.captureMessage(JSON.stringify(data), "debug");
|
||||
|
||||
if (!data.success) {
|
||||
return {
|
||||
success: false,
|
||||
message: "Please make sure that all fields are properly filled in.",
|
||||
errors: validatedFields.error.format(),
|
||||
payload: formData,
|
||||
message: "Please make sure all fields are filled in.",
|
||||
errors: v.flatten(data.issues).nested,
|
||||
};
|
||||
}
|
||||
|
||||
@ -52,14 +50,14 @@ export const sendMessage = async (
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
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") || "",
|
||||
}),
|
||||
cache: "no-store",
|
||||
signal: AbortSignal.timeout(5000), // 5 second timeout
|
||||
});
|
||||
|
||||
if (!turnstileResponse.ok) {
|
||||
if (!turnstileResponse || !turnstileResponse.ok) {
|
||||
throw new Error(`[contact form] turnstile validation failed: ${turnstileResponse.status}`);
|
||||
}
|
||||
|
||||
@ -69,7 +67,6 @@ export const sendMessage = async (
|
||||
return {
|
||||
success: false,
|
||||
message: "Did you complete the CAPTCHA? (If you're human, that is...)",
|
||||
payload: formData,
|
||||
};
|
||||
}
|
||||
|
||||
@ -80,22 +77,20 @@ export const sendMessage = async (
|
||||
// send email
|
||||
const resend = new Resend(process.env.RESEND_API_KEY);
|
||||
await resend.emails.send({
|
||||
from: `${validatedFields.data.name} <${process.env.RESEND_FROM_EMAIL ?? "onboarding@resend.dev"}>`,
|
||||
replyTo: `${validatedFields.data.name} <${validatedFields.data.email}>`,
|
||||
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: 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) {
|
||||
Sentry.captureException(error);
|
||||
|
||||
return {
|
||||
success: false,
|
||||
message: "Internal server error... Try again later or shoot me an old-fashioned email?",
|
||||
errors: error instanceof z.ZodError ? error.format() : undefined,
|
||||
payload: formData,
|
||||
message: "Internal server error. Please try again later or shoot me an email.",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -24,9 +24,15 @@
|
||||
border-color: var(--colors-error);
|
||||
}
|
||||
|
||||
.fieldError {
|
||||
font-size: 0.9em;
|
||||
color: var(--colors-error);
|
||||
}
|
||||
|
||||
.actionRow {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 0.6em;
|
||||
min-height: 3.75em;
|
||||
}
|
||||
|
||||
|
@ -1,20 +1,27 @@
|
||||
"use client";
|
||||
|
||||
import { useActionState } from "react";
|
||||
import { useActionState, useState } from "react";
|
||||
import TextareaAutosize from "react-textarea-autosize";
|
||||
import Turnstile from "react-turnstile";
|
||||
import clsx from "clsx";
|
||||
import { CheckIcon, XIcon } from "lucide-react";
|
||||
import Link from "../../components/Link";
|
||||
import { sendMessage } from "./actions";
|
||||
import { sendMessage, type ContactInput, type ContactState } from "./actions";
|
||||
|
||||
import styles from "./form.module.css";
|
||||
|
||||
const ContactForm = () => {
|
||||
const [formState, formAction, pending] = useActionState<Awaited<ReturnType<typeof sendMessage>>, FormData>(
|
||||
sendMessage,
|
||||
{ success: false, message: "" }
|
||||
);
|
||||
const [formState, formAction, pending] = useActionState<ContactState, FormData>(sendMessage, {
|
||||
success: false,
|
||||
message: "",
|
||||
});
|
||||
|
||||
const [formFields, setFormFields] = useState<ContactInput>({
|
||||
name: "",
|
||||
email: "",
|
||||
message: "",
|
||||
"cf-turnstile-response": "",
|
||||
});
|
||||
|
||||
return (
|
||||
<form action={formAction}>
|
||||
@ -22,32 +29,44 @@ const ContactForm = () => {
|
||||
type="text"
|
||||
name="name"
|
||||
placeholder="Name"
|
||||
required
|
||||
className={clsx(styles.input, formState?.errors?.name && styles.invalid)}
|
||||
defaultValue={(formState?.payload?.get("name") || "") as string}
|
||||
disabled={formState?.success}
|
||||
// required
|
||||
value={formFields.name}
|
||||
onChange={(e) => {
|
||||
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
|
||||
type="email"
|
||||
name="email"
|
||||
placeholder="Email"
|
||||
required
|
||||
// required
|
||||
inputMode="email"
|
||||
className={clsx(styles.input, formState?.errors?.email && styles.invalid)}
|
||||
defaultValue={(formState?.payload?.get("email") || "") as string}
|
||||
disabled={formState?.success}
|
||||
value={formFields.email}
|
||||
onChange={(e) => {
|
||||
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
|
||||
name="message"
|
||||
placeholder="Write something..."
|
||||
minRows={5}
|
||||
required
|
||||
className={clsx(styles.input, styles.textarea, formState?.errors?.message && styles.invalid)}
|
||||
defaultValue={(formState?.payload?.get("message") || "") as string}
|
||||
disabled={formState?.success}
|
||||
// required
|
||||
value={formFields.message}
|
||||
onChange={(e) => {
|
||||
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
|
||||
style={{
|
||||
@ -78,7 +97,7 @@ const ContactForm = () => {
|
||||
Markdown syntax
|
||||
</Link>{" "}
|
||||
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
|
||||
</Link>
|
||||
](https://jarv.is), and <code>`code`</code>.
|
||||
@ -87,6 +106,9 @@ const ContactForm = () => {
|
||||
<div style={{ margin: "1em 0" }}>
|
||||
<Turnstile sitekey={process.env.NEXT_PUBLIC_TURNSTILE_SITE_KEY || "1x00000000000000000000AA"} fixedSize />
|
||||
</div>
|
||||
{formState.errors?.["cf-turnstile-response"] && (
|
||||
<span className={styles.fieldError}>{formState.errors["cf-turnstile-response"][0]}</span>
|
||||
)}
|
||||
|
||||
<div className={styles.actionRow}>
|
||||
{!formState?.success && (
|
||||
@ -109,14 +131,14 @@ const ContactForm = () => {
|
||||
)}
|
||||
</button>
|
||||
)}
|
||||
{formState?.message && (
|
||||
<div className={clsx(styles.result, formState?.success ? styles.success : styles.error)}>
|
||||
{formState?.success ? (
|
||||
{formState.message && (
|
||||
<div className={clsx(styles.result, formState.success ? styles.success : styles.error)}>
|
||||
{formState.success ? (
|
||||
<CheckIcon size="1.3em" className={styles.resultIcon} />
|
||||
) : (
|
||||
<XIcon size="1.3em" className={styles.resultIcon} />
|
||||
)}{" "}
|
||||
{formState?.message}
|
||||
{formState.message}
|
||||
</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";
|
||||
|
||||
Sentry.init({
|
||||
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN || process.env.SENTRY_DSN,
|
||||
integrations: [
|
||||
Sentry.replayIntegration({
|
||||
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",
|
||||
],
|
||||
}),
|
||||
],
|
||||
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
|
||||
environment: process.env.NEXT_PUBLIC_VERCEL_ENV,
|
||||
integrations: [Sentry.browserTracingIntegration(), Sentry.httpClientIntegration()],
|
||||
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 register = async () => {
|
||||
if (process.env.NEXT_RUNTIME === "nodejs") {
|
||||
await import("./sentry.server.config");
|
||||
}
|
||||
export const register = () => {
|
||||
Sentry.init({
|
||||
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") {
|
||||
await import("./sentry.edge.config");
|
||||
if (process.env.NEXT_RUNTIME === "nodejs") {
|
||||
// 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,
|
||||
disableLogger: true,
|
||||
telemetry: false,
|
||||
autoInstrumentAppDirectory: true,
|
||||
autoInstrumentServerFunctions: true,
|
||||
autoInstrumentMiddleware: false,
|
||||
bundleSizeOptimizations: {
|
||||
excludeDebugStatements: true,
|
||||
},
|
||||
|
@ -69,12 +69,13 @@
|
||||
"to-vfile": "^8.0.0",
|
||||
"unified": "^11.0.5",
|
||||
"unist-util-visit": "^5.0.0",
|
||||
"zod": "^3.24.2"
|
||||
"valibot": "^1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/eslintrc": "^3.3.1",
|
||||
"@eslint/js": "^9.23.0",
|
||||
"@jakejarvis/eslint-config": "^4.0.7",
|
||||
"@sentry/cli": "^2.43.0",
|
||||
"@types/comma-number": "^2.1.2",
|
||||
"@types/mdx": "^2.0.13",
|
||||
"@types/node": "^22.13.14",
|
||||
|
117
pnpm-lock.yaml
generated
117
pnpm-lock.yaml
generated
@ -164,9 +164,9 @@ importers:
|
||||
unist-util-visit:
|
||||
specifier: ^5.0.0
|
||||
version: 5.0.0
|
||||
zod:
|
||||
specifier: ^3.24.2
|
||||
version: 3.24.2
|
||||
valibot:
|
||||
specifier: ^1.0.0
|
||||
version: 1.0.0(typescript@5.8.2)
|
||||
devDependencies:
|
||||
'@eslint/eslintrc':
|
||||
specifier: ^3.3.1
|
||||
@ -177,6 +177,9 @@ importers:
|
||||
'@jakejarvis/eslint-config':
|
||||
specifier: ^4.0.7
|
||||
version: 4.0.7(eslint@9.23.0)
|
||||
'@sentry/cli':
|
||||
specifier: ^2.43.0
|
||||
version: 2.43.0
|
||||
'@types/comma-number':
|
||||
specifier: ^2.1.2
|
||||
version: 2.1.2
|
||||
@ -1168,47 +1171,99 @@ packages:
|
||||
engines: {node: '>=10'}
|
||||
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':
|
||||
resolution: {integrity: sha512-BOxzI7sgEU5Dhq3o4SblFXdE9zScpz6EXc5Zwr1UDZvzgXZGosUtKVc7d1LmkrHP8Q2o18HcDWtF3WvJRb5Zpw==}
|
||||
engines: {node: '>=10'}
|
||||
cpu: [arm64]
|
||||
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':
|
||||
resolution: {integrity: sha512-7udCw+YL9lwq+9eL3WLspvnuG+k5Icg92YE7zsteTzWLwgPVzaxeZD2f8hwhsu+wmL+jNqbpCRmktPteh3i2mg==}
|
||||
engines: {node: '>=10'}
|
||||
cpu: [arm]
|
||||
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':
|
||||
resolution: {integrity: sha512-Sw/dQp5ZPvKnq3/y7wIJyxTUJYPGoTX/YeMbDs8BzDlu9to2LWV3K3r7hE7W1Lpbaw4tSquUHiQjP5QHCOS7aQ==}
|
||||
engines: {node: '>=10'}
|
||||
cpu: [x86, ia32]
|
||||
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':
|
||||
resolution: {integrity: sha512-mU4zUspAal6TIwlNLBV5oq6yYqiENnCWSxtSQVzWs0Jyq97wtqGNG9U+QrnwjJZ+ta/hvye9fvL2X25D/RxHQw==}
|
||||
engines: {node: '>=10'}
|
||||
cpu: [x64]
|
||||
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':
|
||||
resolution: {integrity: sha512-iHvFHPGqgJMNqXJoQpqttfsv2GI3cGodeTq4aoVLU/BT3+hXzbV0x1VpvvEhncJkDgDicJpFLM8sEPHb3b8abw==}
|
||||
engines: {node: '>=10'}
|
||||
cpu: [x86, ia32]
|
||||
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':
|
||||
resolution: {integrity: sha512-vPPGHjYoaGmfrU7xhfFxG7qlTBacroz5NdT+0FmDn6692D8IvpNXl1K+eV3Kag44ipJBBeR8g1HRJyx/F/9ACw==}
|
||||
engines: {node: '>=10'}
|
||||
cpu: [x64]
|
||||
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':
|
||||
resolution: {integrity: sha512-spb7S/RUumCGyiSTg8DlrCX4bivCNmU/A1hcfkwuciTFGu8l5CDc2I6jJWWZw8/0enDGxuj5XujgXvU5tr4bxg==}
|
||||
engines: {node: '>= 10'}
|
||||
hasBin: true
|
||||
|
||||
'@sentry/cli@2.43.0':
|
||||
resolution: {integrity: sha512-gBE3bkx+PBJxopTrzIJLX4xHe5S0w87q5frIveWKDZ5ulVIU6YWnVumay0y07RIEweUEj3IYva1qH6HG2abfiA==}
|
||||
engines: {node: '>= 10'}
|
||||
hasBin: true
|
||||
|
||||
'@sentry/core@9.10.1':
|
||||
resolution: {integrity: sha512-TE2zZV3Od4131mZNgFo2Mv4aKU8FXxL0s96yqRvmV+8AU57mJoycMXBnmNSYfWuDICbPJTVAp+3bYMXwX7N5YA==}
|
||||
engines: {node: '>=18'}
|
||||
@ -4377,6 +4432,14 @@ packages:
|
||||
engines: {node: '>=8'}
|
||||
hasBin: true
|
||||
|
||||
valibot@1.0.0:
|
||||
resolution: {integrity: sha512-1Hc0ihzWxBar6NGeZv7fPLY0QuxFMyxwYR2sF1Blu7Wq7EnremwY2W02tit2ij2VJT8HcSkHAQqmFfl77f73Yw==}
|
||||
peerDependencies:
|
||||
typescript: '>=5'
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
|
||||
validate-npm-package-license@3.0.4:
|
||||
resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==}
|
||||
|
||||
@ -5528,24 +5591,48 @@ snapshots:
|
||||
'@sentry/cli-darwin@2.42.2':
|
||||
optional: true
|
||||
|
||||
'@sentry/cli-darwin@2.43.0':
|
||||
optional: true
|
||||
|
||||
'@sentry/cli-linux-arm64@2.42.2':
|
||||
optional: true
|
||||
|
||||
'@sentry/cli-linux-arm64@2.43.0':
|
||||
optional: true
|
||||
|
||||
'@sentry/cli-linux-arm@2.42.2':
|
||||
optional: true
|
||||
|
||||
'@sentry/cli-linux-arm@2.43.0':
|
||||
optional: true
|
||||
|
||||
'@sentry/cli-linux-i686@2.42.2':
|
||||
optional: true
|
||||
|
||||
'@sentry/cli-linux-i686@2.43.0':
|
||||
optional: true
|
||||
|
||||
'@sentry/cli-linux-x64@2.42.2':
|
||||
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':
|
||||
optional: true
|
||||
|
||||
'@sentry/cli-win32-i686@2.43.0':
|
||||
optional: true
|
||||
|
||||
'@sentry/cli-win32-x64@2.42.2':
|
||||
optional: true
|
||||
|
||||
'@sentry/cli-win32-x64@2.43.0':
|
||||
optional: true
|
||||
|
||||
'@sentry/cli@2.42.2':
|
||||
dependencies:
|
||||
https-proxy-agent: 5.0.1
|
||||
@ -5565,6 +5652,26 @@ snapshots:
|
||||
- encoding
|
||||
- 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/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
|
||||
sade: 1.8.1
|
||||
|
||||
valibot@1.0.0(typescript@5.8.2):
|
||||
optionalDependencies:
|
||||
typescript: 5.8.2
|
||||
|
||||
validate-npm-package-license@3.0.4:
|
||||
dependencies:
|
||||
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