Initial commit

This commit is contained in:
fraqtal
2026-07-12 08:15:46 +00:00
commit ee0fec0691
1397 changed files with 127242 additions and 0 deletions

View File

@@ -0,0 +1,92 @@
// packages/core-shared/src/instrumentation/sentry/init-client-react.test.ts
import { describe, it, expect, vi, beforeEach } from "vitest";
const { replayIntegration, feedbackIntegration } = vi.hoisted(() => {
const replayIntegration = vi.fn((opts: unknown) => ({
name: "Replay",
_opts: opts,
}));
const feedbackIntegration = vi.fn((opts: unknown) => ({
name: "Feedback",
_opts: opts,
}));
return { replayIntegration, feedbackIntegration };
});
vi.mock("@sentry/react", () => ({
init: vi.fn(),
replayIntegration,
feedbackIntegration,
}));
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", () => {
initSentryClientReact({ dsn: "https://x@y/1", app: "web-tanstack" });
const call = (SentryReact.init as ReturnType<typeof vi.fn>).mock
.calls[0]![0] as Record<string, unknown>;
expect(call["sendDefaultPii"]).toBe(false);
});
it("attaches replay integration with mask flags", () => {
initSentryClientReact({ dsn: "https://x@y/1", app: "web-tanstack" });
expect(replayIntegration).toHaveBeenCalledTimes(1);
const replayOpts = (replayIntegration as ReturnType<typeof vi.fn>).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", () => {
initSentryClientReact({ dsn: "https://x@y/1", app: "web-tanstack" });
const call = (SentryReact.init as ReturnType<typeof vi.fn>).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<typeof vi.fn>).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();
});
it("attaches feedbackIntegration when SentryReact.feedbackIntegration is available", () => {
initSentryClientReact({ dsn: "https://x@y/1", app: "web-tanstack" });
expect(feedbackIntegration).toHaveBeenCalledTimes(1);
});
it("passes styleNonce and scriptNonce to feedbackIntegration when nonce provided", () => {
initSentryClientReact({
dsn: "https://x@y/1",
app: "web-tanstack",
nonce: "abc123",
});
const feedbackOpts = (feedbackIntegration as ReturnType<typeof vi.fn>).mock
.calls[0]![0] as Record<string, unknown>;
expect(feedbackOpts["styleNonce"]).toBe("abc123");
expect(feedbackOpts["scriptNonce"]).toBe("abc123");
});
it("omits nonce props from feedbackIntegration when nonce not provided", () => {
initSentryClientReact({ dsn: "https://x@y/1", app: "web-tanstack" });
const feedbackOpts = (feedbackIntegration as ReturnType<typeof vi.fn>).mock
.calls[0]![0] as Record<string, unknown>;
expect(feedbackOpts["styleNonce"]).toBeUndefined();
expect(feedbackOpts["scriptNonce"]).toBeUndefined();
});
});

View File

@@ -0,0 +1,141 @@
// packages/core-shared/src/instrumentation/sentry/init-client-react.ts
// Browser-side Sentry init for Vite/React runtimes (TanStack Start). PII scrubbing is
// applied via beforeSend/beforeSendTransaction because browser does NOT use the OTel pipeline.
// PII field lists imported from otel/pii-fields.ts (vendor-neutral).
import * as SentryReact from "@sentry/react";
import type { InitClientOpts } from "./init-client";
import {
PII_KEY_SUBSTRINGS,
PII_QUERY_PARAM_SUBSTRINGS,
REDACTED_VALUE,
REDACTED_IP,
IPV4_REGEX,
IPV6_REGEX,
} from "../otel/pii-fields";
// Inline scrub helpers for browser-side Sentry (server uses OTel processors instead).
function keyContainsPii(key: string): boolean {
const lower = key.toLowerCase();
return PII_KEY_SUBSTRINGS.some((s) => lower.includes(s));
}
function queryParamContainsPii(key: string): boolean {
const lower = key.toLowerCase();
return PII_QUERY_PARAM_SUBSTRINGS.some((s) => lower.includes(s));
}
function redactString(s: string): string {
const ipv4 = new RegExp(IPV4_REGEX.source, "g");
const ipv6 = new RegExp(IPV6_REGEX.source, "g");
return s.replace(ipv4, REDACTED_IP).replace(ipv6, REDACTED_IP);
}
function deepScrub(value: unknown, parentKey = ""): unknown {
if (value === null || value === undefined) return value;
if (typeof value === "string") {
return parentKey && keyContainsPii(parentKey)
? REDACTED_VALUE
: redactString(value);
}
if (typeof value === "number" || typeof value === "boolean") {
return parentKey && keyContainsPii(parentKey) ? REDACTED_VALUE : value;
}
if (Array.isArray(value)) return value.map((v) => deepScrub(v, parentKey));
if (typeof value === "object") {
const out: Record<string, unknown> = {};
for (const [k, v] of Object.entries(value as Record<string, unknown>)) {
out[k] = keyContainsPii(k) ? REDACTED_VALUE : deepScrub(v, k);
}
return out;
}
return value;
}
function scrubUrl(url: string): string {
try {
const u = new URL(url, "http://placeholder.local");
for (const [k] of Array.from(u.searchParams.entries())) {
if (queryParamContainsPii(k)) u.searchParams.set(k, REDACTED_VALUE);
}
return url.startsWith("/") ? `${u.pathname}${u.search}` : u.toString();
} catch {
return url;
}
}
/**
* Client-side init for non-Next.js (Vite/React) runtimes (TanStack Start).
* Mirrors init-client.ts but uses @sentry/react directly. Same PII,
* replay, and scrubbing requirements apply.
*/
export function initSentryClientReact(opts: InitClientOpts): void {
if (!opts.dsn) return;
const isProd = process.env["NODE_ENV"] === "production";
const { nonce } = opts;
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<typeof SentryReact.init>[0];
type SentryEvent = {
extra?: Record<string, unknown> | null;
contexts?: Record<string, Record<string, unknown> | undefined>;
request?: {
url?: string;
headers?: Record<string, string | undefined>;
[key: string]: unknown;
};
transaction?: string;
[key: string]: unknown;
};
SentryReact.init({
dsn: opts.dsn,
environment,
release,
tracesSampleRate,
sendDefaultPii: false,
beforeSend: ((event: SentryEvent) =>
deepScrub(event)) as unknown as NonNullable<InitOpts>["beforeSend"],
beforeSendTransaction: ((event: SentryEvent) => {
const out = { ...event };
if (out.request?.url)
out.request = { ...out.request, url: scrubUrl(out.request.url) };
if (
out.transaction &&
(out.transaction.includes("?") || out.transaction.includes("="))
) {
out.transaction = scrubUrl(out.transaction);
}
return out;
}) as unknown as NonNullable<InitOpts>["beforeSendTransaction"],
replaysSessionSampleRate: 0.0,
replaysOnErrorSampleRate: 1.0,
integrations: [
// mandatory mask flags; allowlist starts empty
SentryReact.replayIntegration({
maskAllText: true,
maskAllInputs: true,
blockAllMedia: true,
}),
...(SentryReact.feedbackIntegration
? [
SentryReact.feedbackIntegration({
...(nonce ? { styleNonce: nonce, scriptNonce: nonce } : {}),
}),
]
: []),
],
initialScope: { tags: { app: opts.app } },
});
}

View File

@@ -0,0 +1,98 @@
// packages/core-shared/src/instrumentation/sentry/init-client.test.ts
import { describe, it, expect, vi, beforeEach } from "vitest";
const { replayIntegration, feedbackIntegration } = vi.hoisted(() => {
const replayIntegration = vi.fn((opts: unknown) => ({
name: "Replay",
_opts: opts,
}));
const feedbackIntegration = vi.fn((opts: unknown) => ({
name: "Feedback",
_opts: opts,
}));
return { replayIntegration, feedbackIntegration };
});
vi.mock("@sentry/nextjs", () => ({
init: vi.fn(),
replayIntegration,
feedbackIntegration,
}));
import * as Sentry from "@sentry/nextjs";
import { initSentryClient } from "@/instrumentation/sentry/init-client";
describe("initSentryClient", () => {
beforeEach(() => {
vi.clearAllMocks();
});
it("calls Sentry.init with sendDefaultPii: false", () => {
initSentryClient({ dsn: "https://x@y/1", app: "web-next" });
const call = (Sentry.init as ReturnType<typeof vi.fn>).mock
.calls[0]![0] as Record<string, unknown>;
expect(call["sendDefaultPii"]).toBe(false);
});
it("attaches replay integration with maskAllText/maskAllInputs/blockAllMedia: true", () => {
initSentryClient({ dsn: "https://x@y/1", app: "web-next" });
expect(replayIntegration).toHaveBeenCalledTimes(1);
const replayOpts = (replayIntegration as ReturnType<typeof vi.fn>).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 replaysSessionSampleRate to 0.0", () => {
initSentryClient({ dsn: "https://x@y/1", app: "web-next" });
const call = (Sentry.init as ReturnType<typeof vi.fn>).mock
.calls[0]![0] as Record<string, unknown>;
expect(call["replaysSessionSampleRate"]).toBe(0.0);
});
it("defaults replaysOnErrorSampleRate to 1.0", () => {
initSentryClient({ dsn: "https://x@y/1", app: "web-next" });
const call = (Sentry.init as ReturnType<typeof vi.fn>).mock
.calls[0]![0] as Record<string, unknown>;
expect(call["replaysOnErrorSampleRate"]).toBe(1.0);
});
it("attaches beforeSend + beforeSendTransaction", () => {
initSentryClient({ dsn: "https://x@y/1", app: "web-next" });
const call = (Sentry.init as ReturnType<typeof vi.fn>).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 empty", () => {
initSentryClient({ dsn: "", app: "web-next" });
expect(Sentry.init).not.toHaveBeenCalled();
});
it("attaches feedbackIntegration when Sentry.feedbackIntegration is available", () => {
initSentryClient({ dsn: "https://x@y/1", app: "web-next" });
expect(feedbackIntegration).toHaveBeenCalledTimes(1);
});
it("passes styleNonce and scriptNonce to feedbackIntegration when nonce provided", () => {
initSentryClient({
dsn: "https://x@y/1",
app: "web-next",
nonce: "abc123",
});
const feedbackOpts = (feedbackIntegration as ReturnType<typeof vi.fn>).mock
.calls[0]![0] as Record<string, unknown>;
expect(feedbackOpts["styleNonce"]).toBe("abc123");
expect(feedbackOpts["scriptNonce"]).toBe("abc123");
});
it("omits nonce props from feedbackIntegration when nonce not provided", () => {
initSentryClient({ dsn: "https://x@y/1", app: "web-next" });
const feedbackOpts = (feedbackIntegration as ReturnType<typeof vi.fn>).mock
.calls[0]![0] as Record<string, unknown>;
expect(feedbackOpts["styleNonce"]).toBeUndefined();
expect(feedbackOpts["scriptNonce"]).toBeUndefined();
});
});

View File

@@ -0,0 +1,142 @@
// packages/core-shared/src/instrumentation/sentry/init-client.ts
// Browser-side Sentry init. PII scrubbing is applied via beforeSend/beforeSendTransaction
// hooks because browser does NOT use the OTel pipeline (server-only migration). The
// PII field lists come from otel/pii-fields.ts (vendor-neutral location).
import * as Sentry from "@sentry/nextjs";
import {
PII_KEY_SUBSTRINGS,
PII_QUERY_PARAM_SUBSTRINGS,
REDACTED_VALUE,
REDACTED_IP,
IPV4_REGEX,
IPV6_REGEX,
} from "../otel/pii-fields";
export type InitClientOpts = {
dsn: string | undefined;
app: "web-next" | "cms" | "web-tanstack";
release?: string;
nonce?: string;
};
// Inline scrub helpers for browser-side Sentry (server uses OTel processors instead).
function keyContainsPii(key: string): boolean {
const lower = key.toLowerCase();
return PII_KEY_SUBSTRINGS.some((s) => lower.includes(s));
}
function queryParamContainsPii(key: string): boolean {
const lower = key.toLowerCase();
return PII_QUERY_PARAM_SUBSTRINGS.some((s) => lower.includes(s));
}
function redactString(s: string): string {
const ipv4 = new RegExp(IPV4_REGEX.source, "g");
const ipv6 = new RegExp(IPV6_REGEX.source, "g");
return s.replace(ipv4, REDACTED_IP).replace(ipv6, REDACTED_IP);
}
function deepScrub(value: unknown, parentKey = ""): unknown {
if (value === null || value === undefined) return value;
if (typeof value === "string") {
return parentKey && keyContainsPii(parentKey)
? REDACTED_VALUE
: redactString(value);
}
if (typeof value === "number" || typeof value === "boolean") {
return parentKey && keyContainsPii(parentKey) ? REDACTED_VALUE : value;
}
if (Array.isArray(value)) return value.map((v) => deepScrub(v, parentKey));
if (typeof value === "object") {
const out: Record<string, unknown> = {};
for (const [k, v] of Object.entries(value as Record<string, unknown>)) {
out[k] = keyContainsPii(k) ? REDACTED_VALUE : deepScrub(v, k);
}
return out;
}
return value;
}
function scrubUrl(url: string): string {
try {
const u = new URL(url, "http://placeholder.local");
for (const [k] of Array.from(u.searchParams.entries())) {
if (queryParamContainsPii(k)) u.searchParams.set(k, REDACTED_VALUE);
}
return url.startsWith("/") ? `${u.pathname}${u.search}` : u.toString();
} catch {
return url;
}
}
export function initSentryClient(opts: InitClientOpts): void {
if (!opts.dsn) return;
const { nonce } = opts;
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<typeof Sentry.init>[0];
type SentryEvent = {
extra?: Record<string, unknown> | null;
contexts?: Record<string, Record<string, unknown> | undefined>;
request?: {
url?: string;
headers?: Record<string, string | undefined>;
[key: string]: unknown;
};
transaction?: string;
[key: string]: unknown;
};
Sentry.init({
dsn: opts.dsn,
environment,
release,
tracesSampleRate,
sendDefaultPii: false,
beforeSend: ((event: SentryEvent) =>
deepScrub(event)) as unknown as InitOpts["beforeSend"],
beforeSendTransaction: ((event: SentryEvent) => {
const out = { ...event };
if (out.request?.url)
out.request = { ...out.request, url: scrubUrl(out.request.url) };
if (
out.transaction &&
(out.transaction.includes("?") || out.transaction.includes("="))
) {
out.transaction = scrubUrl(out.transaction);
}
return out;
}) as unknown as InitOpts["beforeSendTransaction"],
replaysSessionSampleRate: 0.0, // privacy default
replaysOnErrorSampleRate: 1.0,
integrations: [
// mandatory mask flags; allowlist starts empty
Sentry.replayIntegration({
maskAllText: true,
maskAllInputs: true,
blockAllMedia: true,
}),
...(Sentry.feedbackIntegration
? [
Sentry.feedbackIntegration({
...(nonce ? { styleNonce: nonce, scriptNonce: nonce } : {}),
}),
]
: []),
],
initialScope: { tags: { app: opts.app } },
});
}