mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
Removes Sentry tracing entirely (BullMQ idle polling burned 4.8M transactions in 2 days at the baked 0.1 rate), decouples PostHog sampling, and replaces the type-only error scrub with a vetted-field sanitizer plus SafeError/ToolInputError contracts. One classified capture path with per-signature throttles and a per-process ceiling makes storms impossible (NODE-1E was 4,541 events from one 30s loop). Browser errors move to a dedicated web Sentry project with their own source maps. Adds the SNAPOTTER_TELEMETRY runtime kill switch and silences test fleets. Crash fixes: remote 204/304 SSRF process kill (NODE-20), conversion-preset boot crash loop (NODE-21), Redis version preflight + unhandled subscribe rejection (NODE-1T), Sign PDF on plain-http origins (NODE-1K/1M), wavesurfer/pdf.js teardown rejections (NODE-1P/1N), bundle-import ZlibError to 400 (NODE-1Z), chart-maker input errors declassified (NODE-1H/1J), asset requests skip the session DB lookup (NODE-1D).
41 lines
1.7 KiB
TypeScript
41 lines
1.7 KiB
TypeScript
import { readdirSync, readFileSync } from "node:fs";
|
|
import { dirname, join } from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
import { describe, expect, it } from "vitest";
|
|
|
|
// The web Sentry scrubber treats native error classes (TypeError, RangeError,
|
|
// and friends) as browser/runtime faults: their messages pass through with
|
|
// redaction instead of being replaced wholesale. That is only safe while our
|
|
// own code never throws those classes, so own-code throws must stay plain
|
|
// Error (or an app-specific subclass). This turns that grep into an invariant.
|
|
const ROOT = join(dirname(fileURLToPath(import.meta.url)), "../../..");
|
|
const TREES = ["apps/web/src", "packages/shared/src"];
|
|
const NATIVE_THROW =
|
|
/throw\s+new\s+(TypeError|RangeError|SyntaxError|ReferenceError|DOMException)\s*\(/;
|
|
|
|
function sourceFiles(tree: string): string[] {
|
|
return readdirSync(join(ROOT, tree), { recursive: true })
|
|
.map(String)
|
|
.filter((p) => (p.endsWith(".ts") || p.endsWith(".tsx")) && !p.includes(".test."))
|
|
.map((p) => join(tree, p));
|
|
}
|
|
|
|
describe("no native error-class throws in web/shared source", () => {
|
|
const files = TREES.flatMap(sourceFiles);
|
|
|
|
it("finds source files", () => {
|
|
expect(files.length).toBeGreaterThan(100);
|
|
});
|
|
|
|
it("own code throws plain Error, never native error classes", () => {
|
|
const violations: string[] = [];
|
|
for (const file of files) {
|
|
const lines = readFileSync(join(ROOT, file), "utf-8").split("\n");
|
|
lines.forEach((line, i) => {
|
|
if (NATIVE_THROW.test(line)) violations.push(`${file}:${i + 1}`);
|
|
});
|
|
}
|
|
expect(violations, "use plain Error so the Sentry scrubber fully redacts").toEqual([]);
|
|
});
|
|
});
|