feat(web-tanstack): Sentry instrumentation via @sentry/node + @sentry/react + R38 PII test

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) <noreply@anthropic.com>
This commit is contained in:
2026-05-07 20:25:14 +02:00
parent d348cb9179
commit e1b6ecf578
13 changed files with 548 additions and 0 deletions

View File

@@ -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",

View File

@@ -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<typeof beforeSend>[0];
const result = beforeSend(event, {}) as {
extra: Record<string, string>;
request: { headers: Record<string, string> };
};
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<typeof beforeSendTransaction>[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]");
});
});

View File

@@ -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"],
});

View File

@@ -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"],
});

11
apps/web-tanstack/src/vite-env.d.ts vendored Normal file
View File

@@ -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 `/// <reference types="vite/client" />`.
interface ImportMetaEnv {
readonly VITE_WEB_TANSTACK_SENTRY_DSN?: string;
readonly VITE_GIT_COMMIT_SHA?: string;
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}