From e1b6ecf578514c9373c2e032da7e141dae5284fe Mon Sep 17 00:00:00 2001 From: Danijel Martinek Date: Thu, 7 May 2026 20:25:14 +0200 Subject: [PATCH] feat(web-tanstack): Sentry instrumentation via @sentry/node + @sentry/react + R38 PII test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds initSentryServerNode + initSentryClientReact to core-shared (Vite/non-Next variants of the existing init helpers — same R31/R32/R33 posture, R34/R35/R37 replay defaults). Extends no-sentry.ts to mock @sentry/node + @sentry/react. Wires the web-tanstack server/client instrumentation entry hooks and adds the R38 PII test. Spec deviation: web-tanstack has no vite.config.ts yet (placeholder app per its package.json). The @sentry/vite-plugin dep is added but unused until the TanStack Start build is wired in a later plan. A minimal src/vite-env.d.ts shims ImportMetaEnv for the client entry until the full Vite types land. @sentry/node and @sentry/react are added to core-shared as optional peerDependencies so feature packages don't transitively pull them in; they're also devDependencies of core-shared for typecheck/test runs. Co-Authored-By: Claude Opus 4.7 (1M context) --- apps/web-tanstack/package.json | 4 + .../src/__tests__/sentry-pii-scrubber.test.ts | 56 +++++ .../src/instrumentation-client.ts | 9 + apps/web-tanstack/src/instrumentation.ts | 9 + apps/web-tanstack/src/vite-env.d.ts | 11 + packages/core-shared/package.json | 12 + .../core-shared/src/instrumentation/index.ts | 2 + .../sentry/init-client-react.test.ts | 67 ++++++ .../sentry/init-client-react.ts | 48 ++++ .../sentry/init-server-node.test.ts | 51 +++++ .../sentry/init-server-node.ts | 40 ++++ packages/core-testing/src/setup/no-sentry.ts | 33 +++ pnpm-lock.yaml | 206 ++++++++++++++++++ 13 files changed, 548 insertions(+) create mode 100644 apps/web-tanstack/src/__tests__/sentry-pii-scrubber.test.ts create mode 100644 apps/web-tanstack/src/instrumentation-client.ts create mode 100644 apps/web-tanstack/src/instrumentation.ts create mode 100644 apps/web-tanstack/src/vite-env.d.ts create mode 100644 packages/core-shared/src/instrumentation/sentry/init-client-react.test.ts create mode 100644 packages/core-shared/src/instrumentation/sentry/init-client-react.ts create mode 100644 packages/core-shared/src/instrumentation/sentry/init-server-node.test.ts create mode 100644 packages/core-shared/src/instrumentation/sentry/init-server-node.ts diff --git a/apps/web-tanstack/package.json b/apps/web-tanstack/package.json index 7e2866c..d628f4d 100644 --- a/apps/web-tanstack/package.json +++ b/apps/web-tanstack/package.json @@ -14,10 +14,13 @@ "dependencies": { "@repo/blog": "workspace:*", "@repo/core-api": "workspace:*", + "@repo/core-shared": "workspace:*", "@repo/core-trpc": "workspace:*", "@repo/core-ui": "workspace:*", "@repo/marketing-pages": "workspace:*", "@repo/navigation": "workspace:*", + "@sentry/node": "^10.52.0", + "@sentry/react": "^10.52.0", "@tanstack/react-query": "^5.66.0", "@tanstack/react-router": "^1.120.0", "react": "^19.0.0", @@ -28,6 +31,7 @@ "@repo/core-eslint": "workspace:*", "@repo/core-testing": "workspace:*", "@repo/core-typescript": "workspace:*", + "@sentry/vite-plugin": "^5.2.1", "@testing-library/jest-dom": "^6.5.0", "@testing-library/react": "^16.0.0", "@testing-library/user-event": "^14.5.0", diff --git a/apps/web-tanstack/src/__tests__/sentry-pii-scrubber.test.ts b/apps/web-tanstack/src/__tests__/sentry-pii-scrubber.test.ts new file mode 100644 index 0000000..11eddd5 --- /dev/null +++ b/apps/web-tanstack/src/__tests__/sentry-pii-scrubber.test.ts @@ -0,0 +1,56 @@ +import { describe, it, expect } from "vitest"; +import { + beforeSend, + beforeSendTransaction, +} from "@repo/core-shared/instrumentation/sentry/scrub"; + +describe("R38 — apps/web-tanstack PII scrubber", () => { + it("strips email/password/cookie/auth/IP from event payload", () => { + const event = { + extra: { + userEmail: "alice@example.com", + password: "p4$$w0rd", + ipAddress: "192.168.1.10", + note: "request from 10.0.0.1", + }, + request: { + headers: { + Authorization: "Bearer secret", + "Set-Cookie": "session=abc", + "User-Agent": "Mozilla", + }, + }, + } as Parameters[0]; + const result = beforeSend(event, {}) as { + extra: Record; + request: { headers: Record }; + }; + expect(result.extra["userEmail"]).toBe("[redacted]"); + expect(result.extra["password"]).toBe("[redacted]"); + expect(result.extra["ipAddress"]).toBe("[redacted]"); + expect(result.extra["note"]).toContain("[redacted-ip]"); + expect(result.request.headers["Authorization"]).toBe("[redacted]"); + expect(result.request.headers["Set-Cookie"]).toBe("[redacted]"); + expect(result.request.headers["User-Agent"]).toBe("Mozilla"); + }); + + it("strips ?token / ?email / ?password / ?secret / ?signature from URLs", () => { + const event = { + request: { + url: "https://app/api/x?token=abc&email=a@b.c&password=p&secret=z&signature=s&safe=1", + }, + transaction: "/foo?accessToken=t", + } as Parameters[0]; + const result = beforeSendTransaction(event, {}) as { + request: { url: string }; + transaction: string; + }; + const url = decodeURIComponent(result.request.url); + const txn = decodeURIComponent(result.transaction); + for (const key of ["token", "email", "password", "secret", "signature"]) { + expect(url).toContain(`${key}=[redacted]`); + } + expect(url).toContain("safe=1"); + expect(txn).toContain("accessToken=[redacted]"); + }); +}); diff --git a/apps/web-tanstack/src/instrumentation-client.ts b/apps/web-tanstack/src/instrumentation-client.ts new file mode 100644 index 0000000..5432c37 --- /dev/null +++ b/apps/web-tanstack/src/instrumentation-client.ts @@ -0,0 +1,9 @@ +// apps/web-tanstack/src/instrumentation-client.ts +// Browser-entry hook. Imported at the top of the client entry file. +import { initSentryClientReact } from "@repo/core-shared/instrumentation/sentry/init-client-react"; + +initSentryClientReact({ + dsn: import.meta.env["VITE_WEB_TANSTACK_SENTRY_DSN"], + app: "web-tanstack", + release: import.meta.env["VITE_GIT_COMMIT_SHA"], +}); diff --git a/apps/web-tanstack/src/instrumentation.ts b/apps/web-tanstack/src/instrumentation.ts new file mode 100644 index 0000000..5b13c9a --- /dev/null +++ b/apps/web-tanstack/src/instrumentation.ts @@ -0,0 +1,9 @@ +// apps/web-tanstack/src/instrumentation.ts +// Server-entry hook. Imported at the top of the server entry file. +import { initSentryServerNode } from "@repo/core-shared/instrumentation/sentry/init-server-node"; + +initSentryServerNode({ + dsn: process.env["WEB_TANSTACK_SENTRY_DSN"], + app: "web-tanstack", + release: process.env["VITE_GIT_COMMIT_SHA"], +}); diff --git a/apps/web-tanstack/src/vite-env.d.ts b/apps/web-tanstack/src/vite-env.d.ts new file mode 100644 index 0000000..980176c --- /dev/null +++ b/apps/web-tanstack/src/vite-env.d.ts @@ -0,0 +1,11 @@ +// Minimal Vite-style env typing for the instrumentation-client entry. +// When the full TanStack Start / Vite build is wired in a later plan, +// replace this with `/// `. +interface ImportMetaEnv { + readonly VITE_WEB_TANSTACK_SENTRY_DSN?: string; + readonly VITE_GIT_COMMIT_SHA?: string; +} + +interface ImportMeta { + readonly env: ImportMetaEnv; +} diff --git a/packages/core-shared/package.json b/packages/core-shared/package.json index f34e6e4..fe33e6d 100644 --- a/packages/core-shared/package.json +++ b/packages/core-shared/package.json @@ -12,6 +12,8 @@ "./instrumentation": "./src/instrumentation/index.ts", "./instrumentation/sentry/init-server": "./src/instrumentation/sentry/init-server.ts", "./instrumentation/sentry/init-client": "./src/instrumentation/sentry/init-client.ts", + "./instrumentation/sentry/init-server-node": "./src/instrumentation/sentry/init-server-node.ts", + "./instrumentation/sentry/init-client-react": "./src/instrumentation/sentry/init-client-react.ts", "./instrumentation/sentry/scrub": "./src/instrumentation/sentry/scrub.ts" }, "scripts": { @@ -27,10 +29,20 @@ "superjson": "^2.2.1", "zod": "^3.24.0" }, + "peerDependencies": { + "@sentry/node": "^10.51.0", + "@sentry/react": "^10.51.0" + }, + "peerDependenciesMeta": { + "@sentry/node": { "optional": true }, + "@sentry/react": { "optional": true } + }, "devDependencies": { "@repo/core-eslint": "workspace:*", "@repo/core-testing": "workspace:*", "@repo/core-typescript": "workspace:*", + "@sentry/node": "^10.51.0", + "@sentry/react": "^10.51.0", "@types/node": "^22.0.0", "inversify": "^6.2.0", "reflect-metadata": "^0.2.2", diff --git a/packages/core-shared/src/instrumentation/index.ts b/packages/core-shared/src/instrumentation/index.ts index 583ba8d..54e0b6c 100644 --- a/packages/core-shared/src/instrumentation/index.ts +++ b/packages/core-shared/src/instrumentation/index.ts @@ -18,3 +18,5 @@ export { bindSentryInstrumentation, type BindSentryOpts, } from "./di/bind-sentry-instrumentation"; +export { initSentryServerNode } from "./sentry/init-server-node"; +export { initSentryClientReact } from "./sentry/init-client-react"; diff --git a/packages/core-shared/src/instrumentation/sentry/init-client-react.test.ts b/packages/core-shared/src/instrumentation/sentry/init-client-react.test.ts new file mode 100644 index 0000000..6f3a56c --- /dev/null +++ b/packages/core-shared/src/instrumentation/sentry/init-client-react.test.ts @@ -0,0 +1,67 @@ +// packages/core-shared/src/instrumentation/sentry/init-client-react.test.ts +import { describe, it, expect, vi, beforeEach } from "vitest"; + +const { replayIntegration } = vi.hoisted(() => { + const replayIntegration = vi.fn((opts: unknown) => ({ name: "Replay", _opts: opts })); + return { replayIntegration }; +}); + +vi.mock("@sentry/react", () => ({ + init: vi.fn(), + replayIntegration, +})); + +import * as SentryReact from "@sentry/react"; +import { initSentryClientReact } from "@/instrumentation/sentry/init-client-react"; + +describe("initSentryClientReact", () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it("calls SentryReact.init with sendDefaultPii: false (R31)", () => { + initSentryClientReact({ dsn: "https://x@y/1", app: "web-tanstack" }); + const call = (SentryReact.init as ReturnType).mock.calls[0]![0] as Record< + string, + unknown + >; + expect(call["sendDefaultPii"]).toBe(false); + }); + + it("attaches replay integration with mask flags (R34, R35)", () => { + initSentryClientReact({ dsn: "https://x@y/1", app: "web-tanstack" }); + expect(replayIntegration).toHaveBeenCalledTimes(1); + const replayOpts = (replayIntegration as ReturnType).mock.calls[0]![0] as Record< + string, + unknown + >; + expect(replayOpts["maskAllText"]).toBe(true); + expect(replayOpts["maskAllInputs"]).toBe(true); + expect(replayOpts["blockAllMedia"]).toBe(true); + }); + + it("defaults replay sample rates per R37", () => { + initSentryClientReact({ dsn: "https://x@y/1", app: "web-tanstack" }); + const call = (SentryReact.init as ReturnType).mock.calls[0]![0] as Record< + string, + unknown + >; + expect(call["replaysSessionSampleRate"]).toBe(0.0); + expect(call["replaysOnErrorSampleRate"]).toBe(1.0); + }); + + it("attaches beforeSend + beforeSendTransaction scrubbers", () => { + initSentryClientReact({ dsn: "https://x@y/1", app: "web-tanstack" }); + const call = (SentryReact.init as ReturnType).mock.calls[0]![0] as Record< + string, + unknown + >; + expect(typeof call["beforeSend"]).toBe("function"); + expect(typeof call["beforeSendTransaction"]).toBe("function"); + }); + + it("is a no-op when dsn is missing", () => { + initSentryClientReact({ dsn: "", app: "web-tanstack" }); + expect(SentryReact.init).not.toHaveBeenCalled(); + }); +}); diff --git a/packages/core-shared/src/instrumentation/sentry/init-client-react.ts b/packages/core-shared/src/instrumentation/sentry/init-client-react.ts new file mode 100644 index 0000000..e18e6c8 --- /dev/null +++ b/packages/core-shared/src/instrumentation/sentry/init-client-react.ts @@ -0,0 +1,48 @@ +// packages/core-shared/src/instrumentation/sentry/init-client-react.ts +import * as SentryReact from "@sentry/react"; +import { beforeSend, beforeSendTransaction } from "./scrub"; +import type { InitClientOpts } from "./init-client"; + +/** + * Client-side init for non-Next.js (Vite/React) runtimes (TanStack Start). + * Mirrors init-client.ts but uses @sentry/react directly. R31, R32, R33, + * R34, R35, R37 still apply. + */ +export function initSentryClientReact(opts: InitClientOpts): void { + if (!opts.dsn) return; + + const isProd = process.env["NODE_ENV"] === "production"; + const tracesSampleRate = + process.env["SENTRY_TRACES_SAMPLE_RATE"] !== undefined + ? Number(process.env["SENTRY_TRACES_SAMPLE_RATE"]) + : isProd + ? 0.1 + : 1.0; + + const environment = + process.env["SENTRY_ENVIRONMENT"] ?? process.env["NODE_ENV"] ?? "development"; + const release = opts.release ?? "unknown"; + + type InitOpts = Parameters[0]; + SentryReact.init({ + dsn: opts.dsn, + environment, + release, + tracesSampleRate, + sendDefaultPii: false, // R31 + beforeSend: beforeSend as unknown as NonNullable["beforeSend"], // R32 + beforeSendTransaction: + beforeSendTransaction as unknown as NonNullable["beforeSendTransaction"], // R33 + replaysSessionSampleRate: 0.0, // R37 + replaysOnErrorSampleRate: 1.0, // R37 + integrations: [ + // R34, R35 — mandatory mask flags; allowlist starts empty + SentryReact.replayIntegration({ + maskAllText: true, + maskAllInputs: true, + blockAllMedia: true, + }), + ], + initialScope: { tags: { app: opts.app } }, + }); +} diff --git a/packages/core-shared/src/instrumentation/sentry/init-server-node.test.ts b/packages/core-shared/src/instrumentation/sentry/init-server-node.test.ts new file mode 100644 index 0000000..354238a --- /dev/null +++ b/packages/core-shared/src/instrumentation/sentry/init-server-node.test.ts @@ -0,0 +1,51 @@ +// packages/core-shared/src/instrumentation/sentry/init-server-node.test.ts +import { describe, it, expect, vi, beforeEach } from "vitest"; + +vi.mock("@sentry/node", () => ({ + init: vi.fn(), +})); + +import * as SentryNode from "@sentry/node"; +import { initSentryServerNode } from "@/instrumentation/sentry/init-server-node"; + +describe("initSentryServerNode", () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it("calls SentryNode.init with sendDefaultPii: false (R31)", () => { + initSentryServerNode({ dsn: "https://x@y/1", app: "web-tanstack" }); + const call = (SentryNode.init as ReturnType).mock.calls[0]![0] as Record< + string, + unknown + >; + expect(call["sendDefaultPii"]).toBe(false); + }); + + it("attaches beforeSend + beforeSendTransaction scrubbers", () => { + initSentryServerNode({ dsn: "https://x@y/1", app: "web-tanstack" }); + const call = (SentryNode.init as ReturnType).mock.calls[0]![0] as Record< + string, + unknown + >; + expect(typeof call["beforeSend"]).toBe("function"); + expect(typeof call["beforeSendTransaction"]).toBe("function"); + }); + + it("tags events with the app name", () => { + initSentryServerNode({ dsn: "https://x@y/1", app: "web-tanstack" }); + const call = (SentryNode.init as ReturnType).mock.calls[0]![0] as Record< + string, + unknown + >; + const initialScope = call["initialScope"] as { tags?: Record }; + expect(initialScope?.tags?.["app"]).toBe("web-tanstack"); + }); + + it("is a no-op when dsn is missing", () => { + initSentryServerNode({ dsn: "", app: "web-tanstack" }); + expect(SentryNode.init).not.toHaveBeenCalled(); + initSentryServerNode({ dsn: undefined as unknown as string, app: "web-tanstack" }); + expect(SentryNode.init).not.toHaveBeenCalled(); + }); +}); diff --git a/packages/core-shared/src/instrumentation/sentry/init-server-node.ts b/packages/core-shared/src/instrumentation/sentry/init-server-node.ts new file mode 100644 index 0000000..dc3ffa9 --- /dev/null +++ b/packages/core-shared/src/instrumentation/sentry/init-server-node.ts @@ -0,0 +1,40 @@ +// packages/core-shared/src/instrumentation/sentry/init-server-node.ts +import * as SentryNode from "@sentry/node"; +import { beforeSend, beforeSendTransaction } from "./scrub"; +import type { InitServerOpts } from "./init-server"; + +/** + * Server-side init for non-Next.js runtimes (TanStack Start). Mirrors + * init-server.ts but uses @sentry/node directly. R31, R32, R33 still apply. + */ +export function initSentryServerNode(opts: InitServerOpts): void { + if (!opts.dsn) return; + + const isProd = process.env["NODE_ENV"] === "production"; + const tracesSampleRate = + process.env["SENTRY_TRACES_SAMPLE_RATE"] !== undefined + ? Number(process.env["SENTRY_TRACES_SAMPLE_RATE"]) + : isProd + ? 0.1 + : 1.0; + + const environment = + process.env["SENTRY_ENVIRONMENT"] ?? + process.env["VERCEL_ENV"] ?? + process.env["NODE_ENV"] ?? + "development"; + const release = opts.release ?? process.env["VITE_GIT_COMMIT_SHA"] ?? "unknown"; + + type InitOpts = Parameters[0]; + SentryNode.init({ + dsn: opts.dsn, + environment, + release, + tracesSampleRate, + sendDefaultPii: false, // R31 + beforeSend: beforeSend as unknown as NonNullable["beforeSend"], // R32 + beforeSendTransaction: + beforeSendTransaction as unknown as NonNullable["beforeSendTransaction"], // R33 + initialScope: { tags: { app: opts.app } }, + }); +} diff --git a/packages/core-testing/src/setup/no-sentry.ts b/packages/core-testing/src/setup/no-sentry.ts index 83c3c99..e9df5b1 100644 --- a/packages/core-testing/src/setup/no-sentry.ts +++ b/packages/core-testing/src/setup/no-sentry.ts @@ -27,3 +27,36 @@ vi.mock("@sentry/nextjs", () => ({ getActiveSpan: vi.fn(() => undefined), getCurrentHub: vi.fn(() => ({ getClient: () => undefined })), })); + +vi.mock("@sentry/node", () => ({ + init: vi.fn(), + startSpan: vi.fn((_opts: unknown, fn: (span: unknown) => unknown) => + fn({ setAttribute: vi.fn(), setStatus: vi.fn() }), + ), + captureException: vi.fn(), + captureMessage: vi.fn(), + addBreadcrumb: vi.fn(), + setUser: vi.fn(), + setContext: vi.fn(), + setTag: vi.fn(), + setExtra: vi.fn(), + withScope: vi.fn((fn: (scope: unknown) => unknown) => + fn({ setTag: vi.fn(), setExtra: vi.fn() }), + ), + getActiveSpan: vi.fn(() => undefined), +})); + +vi.mock("@sentry/react", () => ({ + init: vi.fn(), + captureException: vi.fn(), + captureMessage: vi.fn(), + addBreadcrumb: vi.fn(), + setUser: vi.fn(), + setContext: vi.fn(), + setTag: vi.fn(), + setExtra: vi.fn(), + withScope: vi.fn((fn: (scope: unknown) => unknown) => + fn({ setTag: vi.fn(), setExtra: vi.fn() }), + ), + replayIntegration: vi.fn(() => ({ name: "Replay" })), +})); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b13090f..cadcdd0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -242,6 +242,9 @@ importers: '@repo/core-api': specifier: workspace:* version: link:../../packages/core-api + '@repo/core-shared': + specifier: workspace:* + version: link:../../packages/core-shared '@repo/core-trpc': specifier: workspace:* version: link:../../packages/core-trpc @@ -254,6 +257,12 @@ importers: '@repo/navigation': specifier: workspace:* version: link:../../packages/navigation + '@sentry/node': + specifier: ^10.52.0 + version: 10.52.0 + '@sentry/react': + specifier: ^10.52.0 + version: 10.52.0(react@19.2.4) '@tanstack/react-query': specifier: ^5.66.0 version: 5.96.2(react@19.2.4) @@ -279,6 +288,9 @@ importers: '@repo/core-typescript': specifier: workspace:* version: link:../../packages/core-typescript + '@sentry/vite-plugin': + specifier: ^5.2.1 + version: 5.2.1(rollup@4.60.1) '@testing-library/jest-dom': specifier: ^6.5.0 version: 6.9.1 @@ -521,6 +533,12 @@ importers: '@repo/core-typescript': specifier: workspace:* version: link:../core-typescript + '@sentry/node': + specifier: ^10.51.0 + version: 10.52.0 + '@sentry/react': + specifier: ^10.51.0 + version: 10.52.0(react@19.2.4) '@types/node': specifier: ^22.0.0 version: 22.19.17 @@ -2749,18 +2767,34 @@ packages: resolution: {integrity: sha512-lNKBS4P7RUvf1niojXQWe9bU3gnBUCbST4Dj0pSiyat1N96cXVyHkeE+uGxowD0RrVWhs+kGHiVX3FcmRWF6sA==} engines: {node: '>=18'} + '@sentry-internal/browser-utils@10.52.0': + resolution: {integrity: sha512-x/yEPZdpH6NGQeoeQnV9tj8reAH8twNttiltGZl2o8Rk7sQeUfe7E8yuYP2XbJ2RqyZK5qRS3COrNyMPzf6KFA==} + engines: {node: '>=18'} + '@sentry-internal/feedback@10.51.0': resolution: {integrity: sha512-bCM95bcpphx28e6aU0bwRLxOgwosYsdNzezM1sM0pVOkb0TB3hDFRamramVDK+/Hp1o8qmRxS4c5w/A7YBZGkA==} engines: {node: '>=18'} + '@sentry-internal/feedback@10.52.0': + resolution: {integrity: sha512-5kAn1W8ZvCuHtEHXpq6iRkUMdNCilwww+YxaN2yofVrCivAbB3Ha5JJUMqmWOPW0pC27zGYmoJMIDvG+PczUxA==} + engines: {node: '>=18'} + '@sentry-internal/replay-canvas@10.51.0': resolution: {integrity: sha512-8PW1Pp+Yl3lPwYqhBCr5SgkuhDanu9ZLzUqD2bPKL/ElqbM2eDVIWxq4z4ZzePrmZa6IcCjTv6sVQJ7Z4dLyLA==} engines: {node: '>=18'} + '@sentry-internal/replay-canvas@10.52.0': + resolution: {integrity: sha512-BI5ie4dxPuUJ344CXVSnAxY1xZCbghglPSCIlTOYODpR9so9yo5IZh+Mwspt0oWsUMaxWJiQSNYlbPWi7WDavg==} + engines: {node: '>=18'} + '@sentry-internal/replay@10.51.0': resolution: {integrity: sha512-jCpI5HXSwK6ZT2HX70+mDRciAocHzSiDk4DTgvzV69Wvd+Ei5WLgE+d39eaEPsm8lUC0Ydntb5sJIB6uG9D4bw==} engines: {node: '>=18'} + '@sentry-internal/replay@10.52.0': + resolution: {integrity: sha512-diywyuc/H7VTUR+W5ryVmLF+0X4UP1OskMqb6V8RSAvJHcj2JmIm7uP+Fc6ACTno+b6AUShwT/L4xVXzO6X9Cw==} + engines: {node: '>=18'} + '@sentry/babel-plugin-component-annotate@5.2.1': resolution: {integrity: sha512-QQ9AL5EXIbSK26ObLVtiU6l3tCUdpGSJ/6VwDkPhC3qvtoksSlcoU9Yzm7XC0NBcvu1N2abL5R7gckKGZ4JewQ==} engines: {node: '>= 18'} @@ -2769,6 +2803,10 @@ packages: resolution: {integrity: sha512-Zdc0sKfenxUtW/OGhtJ7xHFN44bXR7YqxJ1zBDzlZfW0nTbeTTUZBq9z5NUw6qdS0Vs/i3V4qzAKTbRKWfqSEA==} engines: {node: '>=18'} + '@sentry/browser@10.52.0': + resolution: {integrity: sha512-ijL9jN86oXwXQWbwhPlEb70ODJSEmjxQEQdnZkC4gDWbjswcwvRsVJPYk+1xl2ir2iZixRIHipVxDcLwian35g==} + engines: {node: '>=18'} + '@sentry/bundler-plugin-core@5.2.1': resolution: {integrity: sha512-uXb+TOZKXxm2STsP3iR70Jh/yYHwlHOvql7w/bUVYgDyiB/1Mv0D6oNGS0kelsgBsBwCq3ngyJYlyNy3oM1pPw==} engines: {node: '>= 18'} @@ -2829,6 +2867,10 @@ packages: resolution: {integrity: sha512-Y45V/YXvVLEXmOdkbD1oG1gkRWFi9guCEGg3PlIlIpRjAbZUrvLGgjRJIc1E7XpSzmOnWbs5BbUxMv4PDaPj2w==} engines: {node: '>=18'} + '@sentry/core@10.52.0': + resolution: {integrity: sha512-VA/kAqLhkMnRWY2RXdBLyTemR9D4m7MVRy/gyapoq9yvllVPx9WXbvKgnMP2LQp7mFgT/oLFvw58aQKaYTGn3A==} + engines: {node: '>=18'} + '@sentry/nextjs@10.51.0': resolution: {integrity: sha512-Bh3DeieTnbOOfhFEWbT57vgxlCdoTU7+x5or8QLa8VGCmZEekIdt0rGd8exbG1msI4g6SusCiJzbF/8bucmY/A==} engines: {node: '>=18'} @@ -2859,10 +2901,38 @@ packages: '@opentelemetry/semantic-conventions': optional: true + '@sentry/node-core@10.52.0': + resolution: {integrity: sha512-IG7MBtLRPQ2LuU+kbD14AFZroZgAeUmJQTP1FI/F8n56O31+p+9R703LuBTpvZr6sm+eRYDMWcGYYkfLHRVjwg==} + engines: {node: '>=18'} + peerDependencies: + '@opentelemetry/api': ^1.9.0 + '@opentelemetry/core': ^1.30.1 || ^2.1.0 + '@opentelemetry/exporter-trace-otlp-http': '>=0.57.0 <1' + '@opentelemetry/instrumentation': '>=0.57.1 <1' + '@opentelemetry/sdk-trace-base': ^1.30.1 || ^2.1.0 + '@opentelemetry/semantic-conventions': ^1.39.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + '@opentelemetry/core': + optional: true + '@opentelemetry/exporter-trace-otlp-http': + optional: true + '@opentelemetry/instrumentation': + optional: true + '@opentelemetry/sdk-trace-base': + optional: true + '@opentelemetry/semantic-conventions': + optional: true + '@sentry/node@10.51.0': resolution: {integrity: sha512-2yZLRZwS1dKG8/4eOTpGSo/gO/EgmT9aPj6lAzUkRa7bZCTTdW4BraaHU0leX5T94909Qfhbr3W5AVTfDOCKiQ==} engines: {node: '>=18'} + '@sentry/node@10.52.0': + resolution: {integrity: sha512-9+p3KJUk3rHO1HOEZuSknP2RgKCJZONDm4HWgkVDtVBtocb66KLtVlMjc59d2/bWP7tM3wc877tpG30quFfU9g==} + engines: {node: '>=18'} + '@sentry/opentelemetry@10.51.0': resolution: {integrity: sha512-Qc7AlCE4uhB+SvHLqah4RgR1WdY7wmmr/hx9g/prDP9R1ocshmUEMrZK9qjuwaklW7/fmkFCXI8ETxo5L1bHIA==} engines: {node: '>=18'} @@ -2872,16 +2942,44 @@ packages: '@opentelemetry/sdk-trace-base': ^1.30.1 || ^2.1.0 '@opentelemetry/semantic-conventions': ^1.39.0 + '@sentry/opentelemetry@10.52.0': + resolution: {integrity: sha512-Sc7StsvC0bwhMcgDfTRWUIexO5cNzzKUurvUwtpgQUnxO7AzexU3lkY3yHYDsCbWYAEQMXAgQYQtbcqoh+Ie7g==} + engines: {node: '>=18'} + peerDependencies: + '@opentelemetry/api': ^1.9.0 + '@opentelemetry/core': ^1.30.1 || ^2.1.0 + '@opentelemetry/sdk-trace-base': ^1.30.1 || ^2.1.0 + '@opentelemetry/semantic-conventions': ^1.39.0 + '@sentry/react@10.51.0': resolution: {integrity: sha512-RRHHqjNvjji6ebIqdlAr453AkST8Vm4cxdu1vWm772IgbzTO7Jx46Cj6Bt2/GjMyH0YLE5euDaAOQhFMmpvAOw==} engines: {node: '>=18'} peerDependencies: react: ^16.14.0 || 17.x || 18.x || 19.x + '@sentry/react@10.52.0': + resolution: {integrity: sha512-2m72QCsja2cJJHD0ALxRnVt0qMEC2FV4LSi6AAiEdEG4lTb6mgcxavx5pJrW90jE+6dMGPbUz4q8c9vi4jh1qQ==} + engines: {node: '>=18'} + peerDependencies: + react: ^16.14.0 || 17.x || 18.x || 19.x + + '@sentry/rollup-plugin@5.2.1': + resolution: {integrity: sha512-LKJyL4fzcHnHExipVN0/QinhBNoGZt+UXg8xJaqc6MwOolOhxHW0ii2hu1OZsiOhX0+r9MK7T+a7Sx0F0bzdMQ==} + engines: {node: '>= 18'} + peerDependencies: + rollup: '>=3.2.0' + peerDependenciesMeta: + rollup: + optional: true + '@sentry/vercel-edge@10.51.0': resolution: {integrity: sha512-tADUhv+S3gtAj/hSAih6FcYTRZQma+brI4dY6bue2RwgWQvaQoP5CF/PsTb4RhK82etPhqphsdJivY09/L7vWA==} engines: {node: '>=18'} + '@sentry/vite-plugin@5.2.1': + resolution: {integrity: sha512-sSQzOhN8xvo/R70vqgyonnC/fwXpJ1kbkJ0g81Xy/OR+N89+v5tPN4VlKTAq3T2c5yAPE39XCbdgeEnI4kbWGg==} + engines: {node: '>= 18'} + '@sentry/webpack-plugin@5.2.1': resolution: {integrity: sha512-ZGxwCkszFHdk9N11XIbAyTTsJsGUKHMYEXMRLUwPLi+iKi+b+YuXLBg7rlxe6Nd3M0i7xWy3gz6jcW7jeqEfIw==} engines: {node: '>= 18'} @@ -9364,20 +9462,38 @@ snapshots: dependencies: '@sentry/core': 10.51.0 + '@sentry-internal/browser-utils@10.52.0': + dependencies: + '@sentry/core': 10.52.0 + '@sentry-internal/feedback@10.51.0': dependencies: '@sentry/core': 10.51.0 + '@sentry-internal/feedback@10.52.0': + dependencies: + '@sentry/core': 10.52.0 + '@sentry-internal/replay-canvas@10.51.0': dependencies: '@sentry-internal/replay': 10.51.0 '@sentry/core': 10.51.0 + '@sentry-internal/replay-canvas@10.52.0': + dependencies: + '@sentry-internal/replay': 10.52.0 + '@sentry/core': 10.52.0 + '@sentry-internal/replay@10.51.0': dependencies: '@sentry-internal/browser-utils': 10.51.0 '@sentry/core': 10.51.0 + '@sentry-internal/replay@10.52.0': + dependencies: + '@sentry-internal/browser-utils': 10.52.0 + '@sentry/core': 10.52.0 + '@sentry/babel-plugin-component-annotate@5.2.1': {} '@sentry/browser@10.51.0': @@ -9388,6 +9504,14 @@ snapshots: '@sentry-internal/replay-canvas': 10.51.0 '@sentry/core': 10.51.0 + '@sentry/browser@10.52.0': + dependencies: + '@sentry-internal/browser-utils': 10.52.0 + '@sentry-internal/feedback': 10.52.0 + '@sentry-internal/replay': 10.52.0 + '@sentry-internal/replay-canvas': 10.52.0 + '@sentry/core': 10.52.0 + '@sentry/bundler-plugin-core@5.2.1': dependencies: '@babel/core': 7.29.0 @@ -9447,6 +9571,8 @@ snapshots: '@sentry/core@10.51.0': {} + '@sentry/core@10.52.0': {} + '@sentry/nextjs@10.51.0(@opentelemetry/core@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.7.1(@opentelemetry/api@1.9.1))(next@15.5.14(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.99.0))(react@19.2.4)(webpack@5.106.2)': dependencies: '@opentelemetry/api': 1.9.1 @@ -9509,6 +9635,18 @@ snapshots: '@opentelemetry/sdk-trace-base': 2.7.1(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.40.0 + '@sentry/node-core@10.52.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.214.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.40.0)': + dependencies: + '@sentry/core': 10.52.0 + '@sentry/opentelemetry': 10.52.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.40.0) + import-in-the-middle: 3.0.1 + optionalDependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation': 0.214.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace-base': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/semantic-conventions': 1.40.0 + '@sentry/node@10.51.0': dependencies: '@fastify/otel': 0.18.0(@opentelemetry/api@1.9.1) @@ -9546,6 +9684,41 @@ snapshots: - '@opentelemetry/exporter-trace-otlp-http' - supports-color + '@sentry/node@10.52.0': + dependencies: + '@fastify/otel': 0.18.0(@opentelemetry/api@1.9.1) + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation': 0.214.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation-amqplib': 0.61.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation-connect': 0.57.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation-dataloader': 0.31.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation-fs': 0.33.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation-generic-pool': 0.57.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation-graphql': 0.62.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation-hapi': 0.60.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation-http': 0.214.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation-kafkajs': 0.23.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation-knex': 0.58.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation-koa': 0.62.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation-lru-memoizer': 0.58.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation-mongodb': 0.67.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation-mongoose': 0.60.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation-mysql': 0.60.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation-mysql2': 0.60.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation-pg': 0.66.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation-tedious': 0.33.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace-base': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/semantic-conventions': 1.40.0 + '@prisma/instrumentation': 7.6.0(@opentelemetry/api@1.9.1) + '@sentry/core': 10.52.0 + '@sentry/node-core': 10.52.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.214.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.40.0) + '@sentry/opentelemetry': 10.52.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.40.0) + import-in-the-middle: 3.0.1 + transitivePeerDependencies: + - '@opentelemetry/exporter-trace-otlp-http' + - supports-color + '@sentry/opentelemetry@10.51.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.40.0)': dependencies: '@opentelemetry/api': 1.9.1 @@ -9554,18 +9727,51 @@ snapshots: '@opentelemetry/semantic-conventions': 1.40.0 '@sentry/core': 10.51.0 + '@sentry/opentelemetry@10.52.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.40.0)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace-base': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/semantic-conventions': 1.40.0 + '@sentry/core': 10.52.0 + '@sentry/react@10.51.0(react@19.2.4)': dependencies: '@sentry/browser': 10.51.0 '@sentry/core': 10.51.0 react: 19.2.4 + '@sentry/react@10.52.0(react@19.2.4)': + dependencies: + '@sentry/browser': 10.52.0 + '@sentry/core': 10.52.0 + react: 19.2.4 + + '@sentry/rollup-plugin@5.2.1(rollup@4.60.1)': + dependencies: + '@sentry/bundler-plugin-core': 5.2.1 + magic-string: 0.30.21 + optionalDependencies: + rollup: 4.60.1 + transitivePeerDependencies: + - encoding + - supports-color + '@sentry/vercel-edge@10.51.0': dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/resources': 2.7.1(@opentelemetry/api@1.9.1) '@sentry/core': 10.51.0 + '@sentry/vite-plugin@5.2.1(rollup@4.60.1)': + dependencies: + '@sentry/bundler-plugin-core': 5.2.1 + '@sentry/rollup-plugin': 5.2.1(rollup@4.60.1) + transitivePeerDependencies: + - encoding + - rollup + - supports-color + '@sentry/webpack-plugin@5.2.1(webpack@5.106.2)': dependencies: '@sentry/bundler-plugin-core': 5.2.1