From 3bf0c652e7b21d04d8942fd0f8db1a9725c515b3 Mon Sep 17 00:00:00 2001 From: Danijel Martinek Date: Fri, 10 Jul 2026 16:10:35 +0200 Subject: [PATCH 01/54] fix(core-eslint): parse satisfies-shaped manifests; unify manifest parser A manifest written as `{...} satisfies FeatureManifest` (or the combined `as const satisfies` idiom) parsed to null, silently no-oping the error-level conformance rules. unwrapAsConst now strips TSAsExpression and TSSatisfiesExpression in a loop. The file also carried a verbatim second copy of its own parser for parseManifestFully; both public entry points now share one implementation. The template's field set (audits, rateLimit, requiresConsent) is preserved. Co-Authored-By: Claude Fable 5 --- packages/core-eslint/rules/_manifest-ast.js | 192 ++++++------------ .../core-eslint/rules/_manifest-ast.test.js | 45 ++++ 2 files changed, 111 insertions(+), 126 deletions(-) diff --git a/packages/core-eslint/rules/_manifest-ast.js b/packages/core-eslint/rules/_manifest-ast.js index eef68ab..a058519 100644 --- a/packages/core-eslint/rules/_manifest-ast.js +++ b/packages/core-eslint/rules/_manifest-ast.js @@ -1,6 +1,17 @@ import fs from "node:fs"; import { parse } from "@typescript-eslint/parser"; +/** + * ONE parser for feature.manifest.ts, shared by every conformance rule and + * by scripts/conformance.mjs. Walks the AST to the `defineFeature({...})` + * call (unwrapping `as const` / `satisfies` / both) and reads literal + * values from its argument object. + * + * The file used to carry a verbatim second copy of this walk for + * `parseManifestFully` — both public entry points now share the single + * implementation below. + */ + function extractStringLiterals(arrayExpr) { return arrayExpr.elements .filter((el) => el && el.type === "Literal" && typeof el.value === "string") @@ -32,55 +43,6 @@ function extractRateLimitNames(arrayExpr) { return names; } -/** - * Parse a feature.manifest.ts file and extract per-use-case attributes. - * Walks the AST to find the `defineFeature({...} as const)` call expression - * and reads literal values from its argument object. - * - * Returns: { [useCaseName]: { mutates, audits[], publishes[], consumes[] } } - * Returns null if the file is missing or doesn't match the expected shape. - */ -export function parseManifestUseCases(manifestPath) { - let src; - try { - src = fs.readFileSync(manifestPath, "utf8"); - } catch { - return null; - } - let ast; - try { - ast = parse(src, { - sourceType: "module", - ecmaVersion: "latest", - loc: false, - range: false, - }); - } catch { - return null; - } - const defineCall = findDefineFeatureCall(ast); - if (!defineCall) return null; - const arg = unwrapAsConst(defineCall.arguments[0]); - if (!arg || arg.type !== "ObjectExpression") return null; - const useCasesProp = arg.properties.find( - (p) => - p.type === "Property" && - p.key.type === "Identifier" && - p.key.name === "useCases", - ); - if (!useCasesProp || useCasesProp.value.type !== "ObjectExpression") - return {}; - const result = {}; - for (const entry of useCasesProp.value.properties) { - if (entry.type !== "Property" || entry.value.type !== "ObjectExpression") - continue; - const name = - entry.key.type === "Identifier" ? entry.key.name : entry.key.value; - result[name] = extractUseCaseEntry(entry.value); - } - return result; -} - function findDefineFeatureCall(ast) { for (const node of ast.body) { if (node.type !== "ExportNamedDeclaration" || !node.declaration) continue; @@ -101,7 +63,15 @@ function findDefineFeatureCall(ast) { } function unwrapAsConst(node) { - if (node && node.type === "TSAsExpression") return node.expression; + // Unwrap BOTH wrapper kinds, including the combined + // `{...} as const satisfies FeatureManifest` idiom — a `satisfies`-shaped + // manifest must never silently no-op the error-level conformance rules. + while ( + node && + (node.type === "TSAsExpression" || node.type === "TSSatisfiesExpression") + ) { + node = node.expression; + } return node; } @@ -134,17 +104,23 @@ function extractUseCaseEntry(objExpr) { return entry; } +function extractUseCasesMap(useCasesObjExpr) { + const result = {}; + for (const entry of useCasesObjExpr.properties) { + if (entry.type !== "Property" || entry.value.type !== "ObjectExpression") + continue; + const name = + entry.key.type === "Identifier" ? entry.key.name : entry.key.value; + result[name] = extractUseCaseEntry(entry.value); + } + return result; +} + /** - * Parse a feature.manifest.ts and return the full manifest shape: - * { name, requiredCores: string[], useCases: { [name]: {...} } } - * - * Same AST walking as parseManifestUseCases but additionally extracts - * the top-level name + requiredCores fields. Returns null on parse failure. + * Read + parse the manifest and return the `defineFeature` argument object, + * or null when the file is missing / unparseable / not the expected shape. */ -export function parseManifestFully(manifestPath) { - // Reuse the AST parser used by parseManifestUseCases by inlining the - // file-read + AST walk. We need access to the manifest's top-level - // argument object beyond just useCases. +function parseManifestArg(manifestPath) { let src; try { src = fs.readFileSync(manifestPath, "utf8"); @@ -162,10 +138,21 @@ export function parseManifestFully(manifestPath) { } catch { return null; } - const defineCall = findDefineFeatureCallFromBody(ast); + const defineCall = findDefineFeatureCall(ast); if (!defineCall) return null; - const arg = unwrapAsConstNode(defineCall.arguments[0]); + const arg = unwrapAsConst(defineCall.arguments[0]); if (!arg || arg.type !== "ObjectExpression") return null; + return arg; +} + +/** + * Parse a feature.manifest.ts and return the full manifest shape: + * { name, requiredCores: string[], requiresConsent: string[], useCases: { [name]: {...} } } + * Returns null on parse failure or when the manifest has no `name`. + */ +export function parseManifestFully(manifestPath) { + const arg = parseManifestArg(manifestPath); + if (arg === null) return null; let name = null; let requiredCores = []; @@ -202,69 +189,22 @@ export function parseManifestFully(manifestPath) { return { name, requiredCores, requiresConsent, useCases }; } -function extractUseCasesMap(useCasesObjExpr) { - const result = {}; - for (const entry of useCasesObjExpr.properties) { - if (entry.type !== "Property" || entry.value.type !== "ObjectExpression") - continue; - const ucName = - entry.key.type === "Identifier" ? entry.key.name : entry.key.value; - result[ucName] = extractUseCaseEntryFromObj(entry.value); - } - return result; -} - -// Helper aliases for the existing private functions — exposed under -// different names to avoid touching existing code paths. -function findDefineFeatureCallFromBody(ast) { - for (const node of ast.body) { - if (node.type !== "ExportNamedDeclaration" || !node.declaration) continue; - if (node.declaration.type !== "VariableDeclaration") continue; - for (const decl of node.declaration.declarations) { - const init = decl.init; - if (!init) continue; - if ( - init.type === "CallExpression" && - init.callee.type === "Identifier" && - init.callee.name === "defineFeature" - ) { - return init; - } - } - } - return null; -} - -function unwrapAsConstNode(node) { - if (node && node.type === "TSAsExpression") return node.expression; - return node; -} - -function extractUseCaseEntryFromObj(objExpr) { - const entry = { - mutates: false, - audits: [], - publishes: [], - consumes: [], - analyticsEvents: [], - rateLimit: [], - }; - for (const prop of objExpr.properties) { - if (prop.type !== "Property" || prop.key.type !== "Identifier") continue; - const key = prop.key.name; - if (key === "mutates" && prop.value.type === "Literal") { - entry.mutates = prop.value.value === true; - } else if ( - (key === "audits" || - key === "publishes" || - key === "consumes" || - key === "analyticsEvents") && - prop.value.type === "ArrayExpression" - ) { - entry[key] = extractStringLiterals(prop.value); - } else if (key === "rateLimit" && prop.value.type === "ArrayExpression") { - entry[key] = extractRateLimitNames(prop.value); - } - } - return entry; +/** + * Parse a feature.manifest.ts and extract per-use-case attributes only: + * { [useCaseName]: { mutates, audits[], publishes[], consumes[], analyticsEvents[], rateLimit[] } } + * Returns null if the file is missing or doesn't match the expected shape; + * returns {} for a manifest whose useCases object is empty/absent. + */ +export function parseManifestUseCases(manifestPath) { + const arg = parseManifestArg(manifestPath); + if (arg === null) return null; + const useCasesProp = arg.properties.find( + (p) => + p.type === "Property" && + p.key.type === "Identifier" && + p.key.name === "useCases", + ); + if (!useCasesProp || useCasesProp.value.type !== "ObjectExpression") + return {}; + return extractUseCasesMap(useCasesProp.value); } diff --git a/packages/core-eslint/rules/_manifest-ast.test.js b/packages/core-eslint/rules/_manifest-ast.test.js index feff64d..e9818f4 100644 --- a/packages/core-eslint/rules/_manifest-ast.test.js +++ b/packages/core-eslint/rules/_manifest-ast.test.js @@ -133,6 +133,51 @@ describe("parseManifestFully", () => { expect(parseManifestFully(fp)).toBeNull(); }); + it("parses a `satisfies FeatureManifest` manifest (must not silently no-op the gates)", () => { + const fp = writeManifest(`export const xManifest = defineFeature({ + name: "x", + requiredCores: [], + useCases: { + doThing: { mutates: true, audits: ["x.done"], publishes: ["x.done"], consumes: [] }, + }, + realtimeChannels: [], + jobs: [], +} satisfies FeatureManifest);`); + expect(parseManifestUseCases(fp)).toEqual({ + doThing: { + mutates: true, + audits: ["x.done"], + publishes: ["x.done"], + consumes: [], + analyticsEvents: [], + rateLimit: [], + }, + }); + expect(parseManifestFully(fp)?.name).toBe("x"); + }); + + it("parses the combined `as const satisfies FeatureManifest` idiom", () => { + const fp = writeManifest(`export const xManifest = defineFeature({ + name: "x", + requiredCores: [], + useCases: { + doThing: { mutates: false, audits: [], publishes: [], consumes: [] }, + }, + realtimeChannels: [], + jobs: [], +} as const satisfies FeatureManifest);`); + expect(parseManifestUseCases(fp)).toEqual({ + doThing: { + mutates: false, + audits: [], + publishes: [], + consumes: [], + analyticsEvents: [], + rateLimit: [], + }, + }); + }); + it("is not fooled by 'name:' inside a JSDoc comment (regex would false-match)", () => { const fp = writeManifest(`/** * Sample comment with name: "fake" embedded in it. -- 2.49.1 From 0c1df7f5d4335de5d9e419c5f6ddc2516be21ba1 Mon Sep 17 00:00:00 2001 From: Danijel Martinek Date: Fri, 10 Jul 2026 16:11:59 +0200 Subject: [PATCH 02/54] fix(core-eslint): fail loudly on unparseable manifest in wiring gate usecase-must-be-wired returned {} when the manifest parsed to null, silently disabling the error-level gate when the manifest existed but could not be read. It now reports unparseableManifest on Program in that case; a genuinely missing manifest stays a no-op (that is feature-must-have-manifest's job). Co-Authored-By: Claude Fable 5 --- .../rules/usecase-must-be-wired.js | 22 ++++++++++++-- .../rules/usecase-must-be-wired.test.js | 29 +++++++++++++++++++ 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/packages/core-eslint/rules/usecase-must-be-wired.js b/packages/core-eslint/rules/usecase-must-be-wired.js index b914399..92b8325 100644 --- a/packages/core-eslint/rules/usecase-must-be-wired.js +++ b/packages/core-eslint/rules/usecase-must-be-wired.js @@ -1,3 +1,4 @@ +import fs from "node:fs"; import path from "node:path"; import { parseManifestUseCases } from "./_manifest-ast.js"; import { @@ -39,6 +40,8 @@ export default { messages: { missing: 'Use case "{{useCase}}" is declared in {{feature}}.manifest.ts but not wired through wireUseCase({ name: "{{useCase}}", ... }) in this binder. Add the wireUseCase call or remove the manifest entry.', + unparseableManifest: + "feature.manifest.ts for {{feature}} exists but could not be parsed by the conformance rules; the wiring gate cannot run. Fix the manifest shape (defineFeature({...} as const)).", }, }, create(context) { @@ -49,8 +52,23 @@ export default { const featureRoot = featureRootForFile(filename, repoRoot); if (!featureRoot) return {}; - const manifest = parseManifestUseCases(manifestPathForFeature(featureRoot)); - if (!manifest) return {}; + const manifestPath = manifestPathForFeature(featureRoot); + const manifest = parseManifestUseCases(manifestPath); + if (!manifest) { + // An unparseable manifest must FAIL the gate, not silently disable it. + if (fs.existsSync(manifestPath)) { + return { + Program(node) { + context.report({ + node, + messageId: "unparseableManifest", + data: { feature: path.basename(featureRoot) }, + }); + }, + }; + } + return {}; + } const declaredNames = Object.keys(manifest); if (declaredNames.length === 0) return {}; const featureName = path.basename(featureRoot); diff --git a/packages/core-eslint/rules/usecase-must-be-wired.test.js b/packages/core-eslint/rules/usecase-must-be-wired.test.js index 7e2fb0f..5abcce2 100644 --- a/packages/core-eslint/rules/usecase-must-be-wired.test.js +++ b/packages/core-eslint/rules/usecase-must-be-wired.test.js @@ -153,6 +153,35 @@ describe("usecase-must-be-wired", () => { }); }); + it("reports unparseableManifest when the manifest exists but cannot be parsed", () => { + const { repoRoot, binderFile } = makeFixture({ + manifestUseCases: {}, + binderBody: `export function bindProductionDemo(ctx) {}`, + }); + // Overwrite the manifest with a shape the AST walker cannot read + // (no defineFeature call) — the gate must fail loudly, not no-op. + fs.writeFileSync( + path.join(repoRoot, "packages", "demo", "src", "feature.manifest.ts"), + `export const demoManifest = { name: "demo" };`, + ); + tester.run("usecase-must-be-wired", rule, { + valid: [], + invalid: [ + { + filename: binderFile, + code: fs.readFileSync(binderFile, "utf8"), + options: [{ repoRoot }], + errors: [ + { + messageId: "unparseableManifest", + data: { feature: "demo" }, + }, + ], + }, + ], + }); + }); + it("is a no-op when the feature manifest declares no use cases", () => { const { repoRoot, binderFile } = makeFixture({ manifestUseCases: {}, -- 2.49.1 From 90fc48db8118e670778f27224c2036d8b2ebe6e1 Mon Sep 17 00:00:00 2001 From: Danijel Martinek Date: Fri, 10 Jul 2026 16:14:13 +0200 Subject: [PATCH 03/54] fix(conformance): fail the CI gate on unparseable manifests parseManifestUseCases returning null for a manifest that exists made the cross-feature gate silently skip that feature. findUnparseableManifests now runs before the empty-graph early exit (an unparseable manifest contributes zero events and would otherwise pass as nothing-to-check) and any hit fails the run. Reader-closure from the downstream fork is not ported: this tree has no reads: manifests or ./reader exports yet. Co-Authored-By: Claude Fable 5 --- scripts/conformance.mjs | 81 ++++++++++++++++++++++++++++-------- scripts/conformance.test.mjs | 41 +++++++++++++++--- 2 files changed, 99 insertions(+), 23 deletions(-) diff --git a/scripts/conformance.mjs b/scripts/conformance.mjs index 397d3a2..f1ed402 100644 --- a/scripts/conformance.mjs +++ b/scripts/conformance.mjs @@ -8,6 +8,9 @@ * * - Orphan consumer: a feature declares `consumes: ["X"]` but no * feature publishes "X". + * - Unparseable manifest: a feature.manifest.ts exists but the AST + * parser cannot read it — silence here would disable every gate that + * keys off the manifest. * * Exits 0 on success, 1 on any violation. Prints a tabular summary of * the event graph for transparency. @@ -25,7 +28,12 @@ export function findAllManifests(repoRoot = REPO_ROOT) { if (!fs.existsSync(packagesDir)) return []; const out = []; for (const entry of fs.readdirSync(packagesDir)) { - const manifestPath = path.join(packagesDir, entry, "src", "feature.manifest.ts"); + const manifestPath = path.join( + packagesDir, + entry, + "src", + "feature.manifest.ts", + ); if (fs.existsSync(manifestPath)) { out.push({ feature: entry, path: manifestPath }); } @@ -33,6 +41,12 @@ export function findAllManifests(repoRoot = REPO_ROOT) { return out; } +export function findUnparseableManifests(manifests) { + return manifests.filter( + ({ path: manifestPath }) => parseManifestUseCases(manifestPath) === null, + ); +} + export function buildEventGraph(manifests) { const graph = new Map(); for (const { feature, path: manifestPath } of manifests) { @@ -40,11 +54,13 @@ export function buildEventGraph(manifests) { if (!useCases) continue; for (const [useCase, entry] of Object.entries(useCases)) { for (const event of entry.publishes) { - if (!graph.has(event)) graph.set(event, { publishers: [], consumers: [] }); + if (!graph.has(event)) + graph.set(event, { publishers: [], consumers: [] }); graph.get(event).publishers.push({ feature, useCase }); } for (const event of entry.consumes) { - if (!graph.has(event)) graph.set(event, { publishers: [], consumers: [] }); + if (!graph.has(event)) + graph.set(event, { publishers: [], consumers: [] }); graph.get(event).consumers.push({ feature, useCase }); } } @@ -68,32 +84,61 @@ function main() { for (const { feature } of manifests) console.log(` - ${feature}`); console.log(); + let failures = 0; + + // An unparseable manifest blinds every manifest-keyed gate, so check it + // BEFORE the empty-graph early exit (an unparseable manifest contributes + // zero events and would otherwise slip through as "nothing to check"). + const unparseable = findUnparseableManifests(manifests); + if (unparseable.length > 0) { + failures += unparseable.length; + console.error(`✗ ${unparseable.length} unparseable manifest(s):`); + for (const { feature } of unparseable) { + console.error( + ` ${feature}/src/feature.manifest.ts could not be parsed — every manifest-keyed gate is blind to it`, + ); + } + } + const graph = buildEventGraph(manifests); - if (graph.size === 0) { + if (graph.size === 0 && failures === 0) { console.log("No cross-feature events declared yet — nothing to check."); process.exit(0); } - console.log(`Event graph (${graph.size} event(s)):`); - for (const [event, { publishers, consumers }] of graph.entries()) { - console.log(` ${event}`); - console.log(` publishers: ${publishers.length === 0 ? "(none)" : publishers.map((p) => `${p.feature}.${p.useCase}`).join(", ")}`); - console.log(` consumers: ${consumers.length === 0 ? "(none)" : consumers.map((c) => `${c.feature}.${c.useCase}`).join(", ")}`); + if (graph.size > 0) { + console.log(`Event graph (${graph.size} event(s)):`); + for (const [event, { publishers, consumers }] of graph.entries()) { + console.log(` ${event}`); + console.log( + ` publishers: ${publishers.length === 0 ? "(none)" : publishers.map((p) => `${p.feature}.${p.useCase}`).join(", ")}`, + ); + console.log( + ` consumers: ${consumers.length === 0 ? "(none)" : consumers.map((c) => `${c.feature}.${c.useCase}`).join(", ")}`, + ); + } + console.log(); } - console.log(); const orphans = findOrphanConsumers(graph); - if (orphans.length === 0) { + if (orphans.length > 0) { + failures += orphans.length; + console.error(`✗ ${orphans.length} orphan consumer(s):`); + for (const { event, consumers } of orphans) { + console.error(` ${event}`); + for (const c of consumers) { + console.error( + ` consumed by ${c.feature}.${c.useCase}, but no feature publishes it`, + ); + } + } + } + + if (failures === 0) { console.log("✓ pnpm conformance — passed"); process.exit(0); } - console.error(`✗ pnpm conformance — ${orphans.length} orphan consumer(s):`); - for (const { event, consumers } of orphans) { - console.error(` ${event}`); - for (const c of consumers) { - console.error(` consumed by ${c.feature}.${c.useCase}, but no feature publishes it`); - } - } + console.error(`✗ pnpm conformance — ${failures} violation(s)`); process.exit(1); } diff --git a/scripts/conformance.test.mjs b/scripts/conformance.test.mjs index 3ebbc0f..1632d99 100644 --- a/scripts/conformance.test.mjs +++ b/scripts/conformance.test.mjs @@ -2,7 +2,12 @@ import { describe, it, expect } from "vitest"; import path from "node:path"; import os from "node:os"; import fs from "node:fs"; -import { findAllManifests, buildEventGraph, findOrphanConsumers } from "./conformance.mjs"; +import { + findAllManifests, + findUnparseableManifests, + buildEventGraph, + findOrphanConsumers, +} from "./conformance.mjs"; function makeRepo(features) { const root = fs.mkdtempSync(path.join(os.tmpdir(), "conformance-")); @@ -10,8 +15,9 @@ function makeRepo(features) { const dir = path.join(root, "packages", name, "src"); fs.mkdirSync(dir, { recursive: true }); const useCasesStr = Object.entries(useCases) - .map(([ucName, uc]) => - ` ${ucName}: { mutates: ${uc.mutates ?? false}, audits: [], publishes: [${(uc.publishes ?? []).map((p) => `"${p}"`).join(", ")}], consumes: [${(uc.consumes ?? []).map((c) => `"${c}"`).join(", ")}] },`, + .map( + ([ucName, uc]) => + ` ${ucName}: { mutates: ${uc.mutates ?? false}, audits: [], publishes: [${(uc.publishes ?? []).map((p) => `"${p}"`).join(", ")}], consumes: [${(uc.consumes ?? []).map((c) => `"${c}"`).join(", ")}] },`, ) .join("\n"); fs.writeFileSync( @@ -43,11 +49,34 @@ describe("conformance script", () => { it("skips packages without a manifest", () => { const root = fs.mkdtempSync(path.join(os.tmpdir(), "conformance-empty-")); - fs.mkdirSync(path.join(root, "packages", "no-manifest", "src"), { recursive: true }); + fs.mkdirSync(path.join(root, "packages", "no-manifest", "src"), { + recursive: true, + }); expect(findAllManifests(root)).toEqual([]); }); }); + describe("findUnparseableManifests", () => { + it("flags a manifest the AST parser cannot read", () => { + const root = makeRepo({ auth: { signIn: {} } }); + const brokenDir = path.join(root, "packages", "broken", "src"); + fs.mkdirSync(brokenDir, { recursive: true }); + // No defineFeature call — parseManifestUseCases returns null. + fs.writeFileSync( + path.join(brokenDir, "feature.manifest.ts"), + `export const brokenManifest = { name: "broken" };`, + ); + const manifests = findAllManifests(root); + const unparseable = findUnparseableManifests(manifests); + expect(unparseable.map((m) => m.feature)).toEqual(["broken"]); + }); + + it("returns [] when every manifest parses", () => { + const root = makeRepo({ auth: { signIn: {} } }); + expect(findUnparseableManifests(findAllManifests(root))).toEqual([]); + }); + }); + describe("buildEventGraph + findOrphanConsumers", () => { it("finds zero orphans when consumers and publishers line up", () => { const root = makeRepo({ @@ -68,7 +97,9 @@ describe("conformance script", () => { const orphans = findOrphanConsumers(graph); expect(orphans).toHaveLength(1); expect(orphans[0].event).toBe("auth.signed-up"); - expect(orphans[0].consumers).toEqual([{ feature: "marketing", useCase: "onAuthSignedUp" }]); + expect(orphans[0].consumers).toEqual([ + { feature: "marketing", useCase: "onAuthSignedUp" }, + ]); }); it("treats publish-only events as fine (no consumers is not an orphan)", () => { -- 2.49.1 From 498f1fb57a3a0d67799cabca848411d1978eea23 Mon Sep 17 00:00:00 2001 From: Danijel Martinek Date: Fri, 10 Jul 2026 16:21:25 +0200 Subject: [PATCH 04/54] fix(core-eslint): enforce ADR-017 OTel SDK import restriction The base config documented (and carried allowlist off-blocks for) the OTel-SDK import restriction but the no-restricted-imports pattern group only covered @sentry/*. Add @opentelemetry/sdk-*, exporter-*, instrumentation-*, resources and semantic-conventions to the restricted group, and extend the off-block for core-audit's trace-id enrichment test, which legitimately builds an in-memory sdk-trace-base tracer. Co-Authored-By: Claude Fable 5 --- packages/core-eslint/base.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/packages/core-eslint/base.js b/packages/core-eslint/base.js index 1c2fd36..90ed3ef 100644 --- a/packages/core-eslint/base.js +++ b/packages/core-eslint/base.js @@ -118,6 +118,17 @@ export default [ message: "Import from @repo/core-shared/instrumentation instead — feature packages must not depend on Sentry directly.", }, + { + group: [ + "@opentelemetry/sdk-*", + "@opentelemetry/exporter-*", + "@opentelemetry/instrumentation-*", + "@opentelemetry/resources", + "@opentelemetry/semantic-conventions", + ], + message: + "OTel SDK imports are restricted to core-shared/instrumentation/otel/ and app init paths — import @repo/core-shared/instrumentation instead (ADR-017).", + }, ], }, ], @@ -176,6 +187,9 @@ export default [ "**/instrumentation.{ts,js,mjs}", "**/next.config.{mjs,ts,js}", "**/vite.config.{ts,mjs,js}", + // core-audit's trace-id enrichment test builds a real in-memory tracer + // (@opentelemetry/sdk-trace-base) to assert trace-id propagation. + "**/trace-id-enriching-audit-log.test.{ts,js}", ], rules: { "no-restricted-imports": "off", -- 2.49.1 From bf04ad70b26fbad69ff6ce559db6c9d6dabc3397 Mon Sep 17 00:00:00 2001 From: Danijel Martinek Date: Fri, 10 Jul 2026 16:21:26 +0200 Subject: [PATCH 05/54] chore: repair pre-existing lint failures blocking the lint gate - turbo/generators/config.ts used require() inside the reader generator action (no-require-imports); use the top-level node:fs imports - auth authentication.service.ts used a literal self-assertion ("users" as "users"); prefer-as-const - apps/cms lacked web-next's next-env.d.ts triple-slash-reference override, and the committed next-env.d.ts now references .next/types/routes.d.ts Co-Authored-By: Claude Fable 5 --- apps/cms/eslint.config.js | 10 +++++++++- .../infrastructure/services/authentication.service.ts | 2 +- turbo/generators/config.ts | 7 +++---- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/apps/cms/eslint.config.js b/apps/cms/eslint.config.js index 7440d8f..e6b4a81 100644 --- a/apps/cms/eslint.config.js +++ b/apps/cms/eslint.config.js @@ -1,3 +1,11 @@ import baseConfig from "@repo/core-eslint/base"; -export default baseConfig; +export default [ + ...baseConfig, + { + files: ["next-env.d.ts"], + rules: { + "@typescript-eslint/triple-slash-reference": "off", + }, + }, +]; diff --git a/packages/auth/src/infrastructure/services/authentication.service.ts b/packages/auth/src/infrastructure/services/authentication.service.ts index 871c6d3..8451824 100644 --- a/packages/auth/src/infrastructure/services/authentication.service.ts +++ b/packages/auth/src/infrastructure/services/authentication.service.ts @@ -99,7 +99,7 @@ export class AuthenticationService implements IAuthenticationService { if (!decoded) throw new Error("Invalid or expired session token"); const userDoc = await payload.findByID({ - collection: "users" as "users", + collection: "users" as const, id: decoded.id, overrideAccess: true, }); diff --git a/turbo/generators/config.ts b/turbo/generators/config.ts index f1e89d5..9d2c7c1 100644 --- a/turbo/generators/config.ts +++ b/turbo/generators/config.ts @@ -1,4 +1,4 @@ -import { existsSync } from "node:fs"; +import { existsSync, readFileSync, writeFileSync } from "node:fs"; import { join } from "node:path"; import type { PlopTypes } from "@turbo/gen"; import { assertAnchors } from "./lib/anchor-validate.js"; @@ -1108,12 +1108,11 @@ function readerActions(a: { feature: string }): PlopTypes.ActionType[] { }, // Add ./reader subpath to package.json exports map () => { - const fs = require("node:fs"); const pkgPath = join(process.cwd(), pkgJsonPath); - const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8")); + const pkg = JSON.parse(readFileSync(pkgPath, "utf8")); if (!pkg.exports) pkg.exports = {}; pkg.exports["./reader"] = "./src/integrations/readers/index.ts"; - fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n"); + writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n"); return `Added "./reader" export to ${pkgJsonPath}`; }, () => printReaderNextSteps(a), -- 2.49.1 From 9b04fae975b308d16b280bf3b2f6b60f81c9f69f Mon Sep 17 00:00:00 2001 From: Danijel Martinek Date: Fri, 10 Jul 2026 16:25:09 +0200 Subject: [PATCH 06/54] fix(core-shared): derive Analyzed + RateLimited brands in binding slot ProductionUseCase only demanded Instrumented + Captured (+ Audited for mutating-with-audits). The boot assertion additionally requires __analyzed for non-empty analyticsEvents and __rateLimited for non-empty rateLimit, so the type-level gate under-promised what boot enforces. The slot now derives both from the manifest entry; the feature-scoped requiresConsent brand stays boot-only (documented). Also make IAnalytics extend AnalyticsProtocol from core-shared/di/bind-protocols so narrowing the ctx protocol fails typecheck in core-analytics instead of drifting silently. Co-Authored-By: Claude Fable 5 --- .../core-analytics/src/analytics.interface.ts | 10 ++++- .../conformance/production-use-case.test.ts | 40 ++++++++++++++++++- .../src/conformance/production-use-case.ts | 34 +++++++++++----- 3 files changed, 72 insertions(+), 12 deletions(-) diff --git a/packages/core-analytics/src/analytics.interface.ts b/packages/core-analytics/src/analytics.interface.ts index 5ecc7fe..4ba0e70 100644 --- a/packages/core-analytics/src/analytics.interface.ts +++ b/packages/core-analytics/src/analytics.interface.ts @@ -1,10 +1,18 @@ +import type { AnalyticsProtocol } from "@repo/core-shared/di/bind-protocols"; + export type AnalyticsAttributeValue = string | number | boolean; export type AnalyticsUser = { id: string; }; -export interface IAnalytics { +/** + * Product-analytics sink. Extends `AnalyticsProtocol` from + * `@repo/core-shared/di/bind-protocols` — the surface feature binders see via + * `ctx.analytics` — so narrowing the protocol fails typecheck here instead of + * silently drifting apart. + */ +export interface IAnalytics extends AnalyticsProtocol { track( event: string, attributes?: Record, diff --git a/packages/core-shared/src/conformance/production-use-case.test.ts b/packages/core-shared/src/conformance/production-use-case.test.ts index cd482b6..6115786 100644 --- a/packages/core-shared/src/conformance/production-use-case.test.ts +++ b/packages/core-shared/src/conformance/production-use-case.test.ts @@ -11,12 +11,50 @@ describe("ProductionUseCase", () => { consumes: readonly []; }; type Slot = ProductionUseCase<{ x: number }, { y: string }, Manifest>; - type Wrapped = Instrumented<(input: { x: number }) => Promise<{ y: string }>> & + type Wrapped = Instrumented< + (input: { x: number }) => Promise<{ y: string }> + > & Captured<(input: { x: number }) => Promise<{ y: string }>>; expectTypeOf().toMatchTypeOf(); }); + it("requires Analyzed when the manifest declares analyticsEvents", () => { + type Manifest = { + mutates: false; + audits: readonly []; + publishes: readonly []; + consumes: readonly []; + analyticsEvents: readonly ["auth.signed_in"]; + }; + type Fn = (input: { x: number }) => Promise<{ y: string }>; + type Slot = ProductionUseCase<{ x: number }, { y: string }, Manifest>; + type WithoutAnalyzed = Instrumented & Captured; + type WithAnalyzed = WithoutAnalyzed & { readonly __analyzed: true }; + + expectTypeOf().toMatchTypeOf(); + expectTypeOf().not.toMatchTypeOf(); + }); + + it("requires RateLimited when the manifest declares rateLimit budgets", () => { + type Manifest = { + mutates: true; + audits: readonly []; + publishes: readonly []; + consumes: readonly []; + rateLimit: readonly [{ name: "ip"; window: "1m"; budget: 5 }]; + }; + type Fn = (input: { x: number }) => Promise<{ y: string }>; + type Slot = ProductionUseCase<{ x: number }, { y: string }, Manifest>; + type WithoutRateLimited = Instrumented & Captured; + type WithRateLimited = WithoutRateLimited & { + readonly __rateLimited: true; + }; + + expectTypeOf().toMatchTypeOf(); + expectTypeOf().not.toMatchTypeOf(); + }); + it("a plain factory is NOT assignable to the slot", () => { type Manifest = { mutates: false; diff --git a/packages/core-shared/src/conformance/production-use-case.ts b/packages/core-shared/src/conformance/production-use-case.ts index 26079f5..238bd8e 100644 --- a/packages/core-shared/src/conformance/production-use-case.ts +++ b/packages/core-shared/src/conformance/production-use-case.ts @@ -1,10 +1,12 @@ import type { UseCaseManifest } from "./define-feature"; -import type { Instrumented, Captured } from "./brands"; +import type { Instrumented, Captured, Analyzed, RateLimited } from "./brands"; /** * Type-level binding slot for production use cases. Derived from the manifest * entry: every binding must be Instrumented + Captured; mutating use cases - * that declare audits additionally must be Audited. The Audited brand lives + * that declare audits additionally must be Audited; use cases that declare + * non-empty `analyticsEvents` must be Analyzed; use cases that declare + * non-empty `rateLimit` must be RateLimited. The Audited brand lives * in `@repo/core-audit` because the wrap helper that attaches it depends on * `IAuditLog` — feature packages import the merged slot type implicitly * by typing their bindings as `ProductionUseCase`. @@ -13,12 +15,24 @@ import type { Instrumented, Captured } from "./brands"; * without depending on core-audit. When `mutates: true` AND `audits` is * non-empty, the slot demands a marker type with a `__audited` flag; the * concrete `Audited` from core-audit satisfies it. + * + * `requiresConsent` is feature-scoped (a `FeatureManifest` field, not a + * per-use-case one), so the `__consentChecked` brand cannot be derived from + * the use-case entry here — it is enforced at boot by + * `assertFeatureConformance` instead. */ -export type ProductionUseCase = - & Instrumented<(input: I) => Promise> - & Captured<(input: I) => Promise> - & (M["mutates"] extends true - ? M["audits"]["length"] extends 0 - ? unknown - : { readonly __audited: true } - : unknown); +export type ProductionUseCase = Instrumented< + (input: I) => Promise +> & + Captured<(input: I) => Promise> & + (M["mutates"] extends true + ? M["audits"]["length"] extends 0 + ? unknown + : { readonly __audited: true } + : unknown) & + (M["analyticsEvents"] extends readonly [string, ...string[]] + ? Analyzed<(input: I) => Promise> + : unknown) & + (M["rateLimit"] extends readonly [unknown, ...unknown[]] + ? RateLimited<(input: I) => Promise> + : unknown); -- 2.49.1 From 6fd746d3bd7b7625e40325483f2779b79059dcee Mon Sep 17 00:00:00 2001 From: Danijel Martinek Date: Fri, 10 Jul 2026 16:27:16 +0200 Subject: [PATCH 07/54] fix(core-eslint): activate React rules-of-hooks for all TSX surfaces MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit next.js and react-internal.js are plain re-exports of base, so no rules-of-hooks checking was active anywhere despite three React apps and core-ui. Wire eslint-plugin-react-hooks in base.js scoped to **/*.tsx (non-React packages untouched). Fixes the one violation it surfaced: web-tanstack's root route called Route.useLoaderData inside an anonymous component callback — extracted to a named RootComponent. Co-Authored-By: Claude Fable 5 --- apps/web-tanstack/src/routes/__root.tsx | 22 ++++++++++++---------- packages/core-eslint/base.js | 8 ++++++++ packages/core-eslint/package.json | 1 + pnpm-lock.yaml | 16 ++++++++++++++++ 4 files changed, 37 insertions(+), 10 deletions(-) diff --git a/apps/web-tanstack/src/routes/__root.tsx b/apps/web-tanstack/src/routes/__root.tsx index 25cde5e..28e1f83 100644 --- a/apps/web-tanstack/src/routes/__root.tsx +++ b/apps/web-tanstack/src/routes/__root.tsx @@ -1,6 +1,17 @@ import { Outlet, createRootRoute } from "@tanstack/react-router"; import { getNonce } from "@repo/core-shared/security/tanstack"; +function RootComponent() { + const { nonce } = Route.useLoaderData(); + return ( + <> + {/* nonce exposed to client so instrumentation-client.ts can read it */} + + + + ); +} + export const Route = createRootRoute({ loader: async () => { try { @@ -12,14 +23,5 @@ export const Route = createRootRoute({ return { nonce: "" }; } }, - component: () => { - const { nonce } = Route.useLoaderData(); - return ( - <> - {/* nonce exposed to client so instrumentation-client.ts can read it */} - - - - ); - }, + component: RootComponent, }); diff --git a/packages/core-eslint/base.js b/packages/core-eslint/base.js index 90ed3ef..c9122ee 100644 --- a/packages/core-eslint/base.js +++ b/packages/core-eslint/base.js @@ -3,6 +3,7 @@ import eslintConfigPrettier from "eslint-config-prettier"; import tseslint from "typescript-eslint"; import turboPlugin from "eslint-plugin-turbo"; import boundaries from "eslint-plugin-boundaries"; +import reactHooks from "eslint-plugin-react-hooks"; import globals from "globals"; import conformancePlugin from "./plugin.js"; import path from "node:path"; @@ -195,6 +196,13 @@ export default [ "no-restricted-imports": "off", }, }, + // React rules-of-hooks for every TSX surface (apps, core-ui, feature + // ui/**). Scoped to .tsx so non-React packages are untouched. + { + files: ["**/*.tsx"], + plugins: { "react-hooks": reactHooks }, + rules: reactHooks.configs.recommended.rules, + }, // E1 — Event handlers must not be re-exported. Wire them only inside the // consumer feature's bind-production / bind-dev-seed (spec § 2.2 Rule E1). // J — Direct `payload.jobs.*` access is forbidden outside the integration diff --git a/packages/core-eslint/package.json b/packages/core-eslint/package.json index 5b4190a..fe578e0 100644 --- a/packages/core-eslint/package.json +++ b/packages/core-eslint/package.json @@ -19,6 +19,7 @@ "eslint": "^9.20.0", "eslint-config-prettier": "^10.1.0", "eslint-plugin-boundaries": "^4.2.2", + "eslint-plugin-react-hooks": "^5.2.0", "eslint-plugin-turbo": "^2.4.0", "typescript-eslint": "^8.25.0", "vitest": "^3.1.0" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6e62c51..08312c0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -733,6 +733,9 @@ importers: eslint-plugin-boundaries: specifier: ^4.2.2 version: 4.2.2(@typescript-eslint/parser@8.58.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.4(jiti@2.7.0)) + eslint-plugin-react-hooks: + specifier: ^5.2.0 + version: 5.2.0(eslint@9.39.4(jiti@2.7.0)) eslint-plugin-turbo: specifier: ^2.4.0 version: 2.9.4(eslint@9.39.4(jiti@2.7.0))(turbo@2.9.4) @@ -10680,6 +10683,15 @@ packages: peerDependencies: eslint: ">=6.0.0" + eslint-plugin-react-hooks@5.2.0: + resolution: + { + integrity: sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==, + } + engines: { node: ">=10" } + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 + eslint-plugin-turbo@2.9.4: resolution: { @@ -24134,6 +24146,10 @@ snapshots: - eslint-import-resolver-webpack - supports-color + eslint-plugin-react-hooks@5.2.0(eslint@9.39.4(jiti@2.7.0)): + dependencies: + eslint: 9.39.4(jiti@2.7.0) + eslint-plugin-turbo@2.9.4(eslint@9.39.4(jiti@2.7.0))(turbo@2.9.4): dependencies: dotenv: 16.0.3 -- 2.49.1 From 84767126204c901b0b437e41402acea26d93ab69 Mon Sep 17 00:00:00 2001 From: Danijel Martinek Date: Fri, 10 Jul 2026 16:30:53 +0200 Subject: [PATCH 08/54] fix(generators): event buses deliver the zod-parsed payload MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit InMemoryEventBus.publish and PayloadJobsEventBus.publish called descriptor.schema.parse(payload) but discarded the result, so handlers and enqueued jobs received the raw input — zod defaults, catches, transforms and strips never applied. Both bus templates now fan out the parsed value; regression tests added to both test templates and the events snapshot hashes regenerated (template-tree sha over hbs-stripped paths — only the four touched files differ). Co-Authored-By: Claude Fable 5 --- .../core-package/events.snapshot.json | 8 ++++---- .../events/src/in-memory-event-bus.test.ts.hbs | 15 +++++++++++++++ .../events/src/in-memory-event-bus.ts.hbs | 6 ++---- .../events/src/payload-jobs-event-bus.test.ts.hbs | 15 +++++++++++++++ .../events/src/payload-jobs-event-bus.ts.hbs | 4 ++-- 5 files changed, 38 insertions(+), 10 deletions(-) diff --git a/turbo/generators/__snapshots__/core-package/events.snapshot.json b/turbo/generators/__snapshots__/core-package/events.snapshot.json index 6375f42..145f3be 100644 --- a/turbo/generators/__snapshots__/core-package/events.snapshot.json +++ b/turbo/generators/__snapshots__/core-package/events.snapshot.json @@ -29,11 +29,11 @@ }, { "path": "src/in-memory-event-bus.test.ts", - "sha256": "56f704de153f7676bf243d6350f4bebe28cb9ffec1eb3afef8f5f2bb08561c21" + "sha256": "d75a7694cbf08f6afdc0b5ffe80c37e2c61e00b94f61231200e1c26becb9f26f" }, { "path": "src/in-memory-event-bus.ts", - "sha256": "d7de709049bd864bdcd8f02316d981cf0407efbc5b6f13fd2ab86094ecaddb8a" + "sha256": "c629e0733ab23ef808be7cba04ccd4d31792846dc81d02c3722a210a47aa5035" }, { "path": "src/index.ts", @@ -41,11 +41,11 @@ }, { "path": "src/payload-jobs-event-bus.test.ts", - "sha256": "cda493839ed9af7d48a4c240392933af7167805a94ebaf30efd64f8a374b7441" + "sha256": "dc091425978d4e02881ccdcb2a46b716273d848ebdd87f72131abdf419a40161" }, { "path": "src/payload-jobs-event-bus.ts", - "sha256": "5fbeb611af7f4ba4302828de6a0262a74d9b4bb6a11cbbb8ed40712aae731ded" + "sha256": "2aae98fcc2b3d4c77d7b0f80e1aedb56bbce737e296b9681b663fd22380ae3ab" }, { "path": "src/symbols.ts", diff --git a/turbo/generators/templates/core-package/events/src/in-memory-event-bus.test.ts.hbs b/turbo/generators/templates/core-package/events/src/in-memory-event-bus.test.ts.hbs index 1ce7f4f..2ef2784 100644 --- a/turbo/generators/templates/core-package/events/src/in-memory-event-bus.test.ts.hbs +++ b/turbo/generators/templates/core-package/events/src/in-memory-event-bus.test.ts.hbs @@ -46,4 +46,19 @@ describe("InMemoryEventBus", () => { const bus = new InMemoryEventBus(); await expect(bus.publish(evt, { id: "x" })).resolves.toBeUndefined(); }); + + it("delivers the zod-parsed payload, not the raw input", async () => { + const evtNormalized = defineEvent( + "test.normalized", + z.object({ id: z.string(), mode: z.string().catch("auto") }), + ); + const bus = new InMemoryEventBus(); + const handler = vi.fn(); + bus.subscribe(evtNormalized, "consumer", handler); + await bus.publish(evtNormalized, { + id: "x", + mode: 42 as unknown as string, + }); + expect(handler).toHaveBeenCalledWith({ id: "x", mode: "auto" }); + }); }); diff --git a/turbo/generators/templates/core-package/events/src/in-memory-event-bus.ts.hbs b/turbo/generators/templates/core-package/events/src/in-memory-event-bus.ts.hbs index 6ac36e8..f1741c4 100644 --- a/turbo/generators/templates/core-package/events/src/in-memory-event-bus.ts.hbs +++ b/turbo/generators/templates/core-package/events/src/in-memory-event-bus.ts.hbs @@ -16,12 +16,10 @@ export class InMemoryEventBus implements IEventBus { descriptor: EventDescriptor>, payload: T, ): Promise { - descriptor.schema.parse(payload); + const parsed = descriptor.schema.parse(payload); const subscribers = this.handlers.get(descriptor.name) ?? []; if (subscribers.length === 0) return; - const settled = await Promise.allSettled( - subscribers.map((h) => h(payload)), - ); + const settled = await Promise.allSettled(subscribers.map((h) => h(parsed))); if (this.options.failFast) { const failure = settled.find((s) => s.status === "rejected"); // Only the first rejection is rethrown. Other failures are intentionally diff --git a/turbo/generators/templates/core-package/events/src/payload-jobs-event-bus.test.ts.hbs b/turbo/generators/templates/core-package/events/src/payload-jobs-event-bus.test.ts.hbs index 87b5a06..f5cd4bc 100644 --- a/turbo/generators/templates/core-package/events/src/payload-jobs-event-bus.test.ts.hbs +++ b/turbo/generators/templates/core-package/events/src/payload-jobs-event-bus.test.ts.hbs @@ -42,6 +42,21 @@ describe("PayloadJobsEventBus", () => { expect(queue.enqueued[0]!.input).toEqual({ userId: "u1" }); }); + it("enqueues the zod-parsed payload, not the raw input", async () => { + const evtNormalized = defineEvent( + "test.normalized", + z.object({ userId: z.string(), mode: z.string().catch("auto") }), + ); + const queue = recordingQueue(); + const bus = new PayloadJobsEventBus(queue); + bus.subscribe(evtNormalized, "marketing-pages", vi.fn()); + await bus.publish(evtNormalized, { + userId: "u1", + mode: 42 as unknown as string, + }); + expect(queue.enqueued[0]!.input).toEqual({ userId: "u1", mode: "auto" }); + }); + it("enqueues nothing when no subscribers are registered", async () => { const queue = recordingQueue(); const bus = new PayloadJobsEventBus(queue); diff --git a/turbo/generators/templates/core-package/events/src/payload-jobs-event-bus.ts.hbs b/turbo/generators/templates/core-package/events/src/payload-jobs-event-bus.ts.hbs index 20227bb..ff03012 100644 --- a/turbo/generators/templates/core-package/events/src/payload-jobs-event-bus.ts.hbs +++ b/turbo/generators/templates/core-package/events/src/payload-jobs-event-bus.ts.hbs @@ -19,13 +19,13 @@ export class PayloadJobsEventBus implements IEventBus { descriptor: EventDescriptor>, payload: T, ): Promise { - descriptor.schema.parse(payload); + const parsed = descriptor.schema.parse(payload); const consumers = this.subscribers.get(descriptor.name) ?? []; await Promise.all( consumers.map((consumerFeature) => this.queue.enqueue( `__events.${descriptor.name}.${consumerFeature}`, - payload, + parsed, ), ), ); -- 2.49.1 From 0234e18425c16aac9b591cc3432859808e90ad73 Mon Sep 17 00:00:00 2001 From: Danijel Martinek Date: Fri, 10 Jul 2026 16:32:03 +0200 Subject: [PATCH 09/54] fix(core-testing): handle batched paths + transformed errors in mock tRPC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit httpBatchLink joins same-tick calls into one request whose path is a comma-separated list of procedure paths; the stub matched the joined string against a single mock key, so any batched pair failed with 'No mock for a.one,a.two'. It now answers one element per procedure, in order. Error bodies are also superjson-serialized — raw error JSON made the client throw 'Unable to transform response' instead of surfacing the intended error. Regression tests added. Co-Authored-By: Claude Fable 5 --- .../core-testing/src/react/mock-trpc.test.ts | 44 +++++++++++++++++++ packages/core-testing/src/react/mock-trpc.ts | 27 +++++++++--- 2 files changed, 65 insertions(+), 6 deletions(-) create mode 100644 packages/core-testing/src/react/mock-trpc.test.ts diff --git a/packages/core-testing/src/react/mock-trpc.test.ts b/packages/core-testing/src/react/mock-trpc.test.ts new file mode 100644 index 0000000..fea835d --- /dev/null +++ b/packages/core-testing/src/react/mock-trpc.test.ts @@ -0,0 +1,44 @@ +import { describe, expect, it } from "vitest"; +import { initTRPC } from "@trpc/server"; +import { z } from "zod"; +import { createMockTrpcClient } from "@/react/mock-trpc"; + +const t = initTRPC.create(); +const _router = t.router({ + blog: t.router({ + articles: t.procedure.output(z.string()).query(() => "articles"), + articleBySlug: t.procedure.output(z.string()).query(() => "article"), + }), +}); +type Router = typeof _router; + +describe("createMockTrpcClient", () => { + it("resolves a single call from its mock", async () => { + const client = createMockTrpcClient({ + "blog.articles": "mock-articles", + }); + await expect(client.blog.articles.query()).resolves.toBe("mock-articles"); + }); + + it("resolves two same-tick (batched) calls, one result per procedure", async () => { + const client = createMockTrpcClient({ + "blog.articles": "mock-articles", + "blog.articleBySlug": "mock-article", + }); + // httpBatchLink joins these into ONE request with a comma-separated + // procedure path — the stub must answer per procedure, in order. + const [articles, article] = await Promise.all([ + client.blog.articles.query(), + client.blog.articleBySlug.query(), + ]); + expect(articles).toBe("mock-articles"); + expect(article).toBe("mock-article"); + }); + + it("rejects a call whose procedure has no mock", async () => { + const client = createMockTrpcClient({}); + await expect(client.blog.articles.query()).rejects.toThrow( + /No mock for blog\.articles/, + ); + }); +}); diff --git a/packages/core-testing/src/react/mock-trpc.ts b/packages/core-testing/src/react/mock-trpc.ts index 62801bf..ff2f876 100644 --- a/packages/core-testing/src/react/mock-trpc.ts +++ b/packages/core-testing/src/react/mock-trpc.ts @@ -10,12 +10,27 @@ export function createMockTrpcClient( ) { const fetchStub: typeof fetch = async (input) => { const url = typeof input === "string" ? input : (input as Request).url; - const path = new URL(url, "http://mock").pathname.replace(/^\/api\/trpc\//, ""); - const result = mocks[path]; - if (result === undefined) { - return new Response(JSON.stringify([{ error: { code: -32603, message: `No mock for ${path}` } }]), { status: 200 }); - } - return new Response(JSON.stringify([{ result: { data: superjson.serialize(result) } }]), { status: 200 }); + const path = new URL(url, "http://mock").pathname.replace( + /^\/api\/trpc\//, + "", + ); + // httpBatchLink joins same-tick calls into one request whose path is the + // comma-separated list of procedure paths; respond with one element per + // call, in order. + const paths = path.split(","); + const body = paths.map((p) => + Object.hasOwn(mocks, p) + ? { result: { data: superjson.serialize(mocks[p]) } } + : { + // Error payloads pass through the transformer too — serialize + // them or the client throws "Unable to transform response". + error: superjson.serialize({ + code: -32603, + message: `No mock for ${p}`, + }), + }, + ); + return new Response(JSON.stringify(body), { status: 200 }); }; // httpBatchLink requires a conditional TransformerOptions that -- 2.49.1 From 16310c5d623eedee70765822817c1f7a7d1a5dd7 Mon Sep 17 00:00:00 2001 From: Danijel Martinek Date: Fri, 10 Jul 2026 16:32:47 +0200 Subject: [PATCH 10/54] test(core-shared): assert PII scrub runs before Sentry processors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous init-server-node tests only checked that an SDK handle came back — the ADR-017 §7 invariant (PiiScrubSpanProcessor / PiiScrubLogRecordProcessor registered BEFORE any exporter-facing processor) was untested. Capture the NodeSDK constructor config via a local vi.mock override and assert processor ordering for both the empty-DSN and DSN-set paths. Co-Authored-By: Claude Fable 5 --- .../otel/init-server-node.test.ts | 97 ++++++++++++++++++- 1 file changed, 92 insertions(+), 5 deletions(-) diff --git a/packages/core-shared/src/instrumentation/otel/init-server-node.test.ts b/packages/core-shared/src/instrumentation/otel/init-server-node.test.ts index 054d281..1e77799 100644 --- a/packages/core-shared/src/instrumentation/otel/init-server-node.test.ts +++ b/packages/core-shared/src/instrumentation/otel/init-server-node.test.ts @@ -1,5 +1,70 @@ -import { describe, it, expect } from "vitest"; +import { describe, it, expect, vi } from "vitest"; import { initOtelServerNode } from "./init-server-node"; +import { + PiiScrubSpanProcessor, + PiiScrubLogRecordProcessor, +} from "./pii-scrub-processor"; +import { SentryLogRecordForwarder } from "./sentry-bridge"; + +const captured = vi.hoisted(() => ({ configs: [] as unknown[] })); + +// Hoisted alongside `captured`: the vi.mock factory below is hoisted above +// every top-level statement, so a plain top-level class would still be in its +// temporal dead zone when the factory runs (ReferenceError). +const FakeBatchSpanProcessor = vi.hoisted( + () => + class FakeBatchSpanProcessor { + constructor(public readonly wrapped: unknown) {} + onStart() {} + onEnd() {} + forceFlush() { + return Promise.resolve(); + } + shutdown() { + return Promise.resolve(); + } + }, +); + +// Local mock overrides the setup-file stub (the pattern no-instrumentation.ts +// documents) so the NodeSDK constructor CONFIG is capturable — the previous +// tests asserted nothing about it, leaving the ADR-017 §7 security invariant +// (PII scrub runs before any exporter) untested. +vi.mock("@opentelemetry/sdk-node", () => ({ + NodeSDK: class { + constructor(cfg: unknown) { + captured.configs.push(cfg); + } + start() {} + shutdown() { + return Promise.resolve(); + } + }, + tracing: { BatchSpanProcessor: FakeBatchSpanProcessor }, +})); +// Instrumentations auto-enable (patch http/undici/pg globals) in their +// constructors — stub them out entirely, not just registerInstrumentations. +vi.mock("@opentelemetry/instrumentation", () => ({ + registerInstrumentations: vi.fn(), +})); +vi.mock("@opentelemetry/instrumentation-http", () => ({ + HttpInstrumentation: class {}, +})); +vi.mock("@opentelemetry/instrumentation-undici", () => ({ + UndiciInstrumentation: class {}, +})); +vi.mock("@opentelemetry/instrumentation-pg", () => ({ + PgInstrumentation: class {}, +})); + +type SdkConfig = { + spanProcessors: unknown[]; + logRecordProcessors: unknown[]; +}; + +function lastConfig(): SdkConfig { + return captured.configs[captured.configs.length - 1] as SdkConfig; +} describe("initOtelServerNode", () => { it("returns an SDK handle with shutdown()", () => { @@ -8,16 +73,38 @@ describe("initOtelServerNode", () => { serviceName: "test-service", environment: "test", }); - expect(sdk).toBeDefined(); expect(typeof sdk.shutdown).toBe("function"); }); - it("accepts a DSN and wires the Sentry bridge", () => { - const sdk = initOtelServerNode({ + it("registers only PII scrub processors when DSN is empty", () => { + initOtelServerNode({ + dsn: "", + serviceName: "test-service", + environment: "test", + }); + const cfg = lastConfig(); + expect(cfg.spanProcessors).toHaveLength(1); + expect(cfg.spanProcessors[0]).toBeInstanceOf(PiiScrubSpanProcessor); + expect(cfg.logRecordProcessors).toHaveLength(1); + expect(cfg.logRecordProcessors[0]).toBeInstanceOf( + PiiScrubLogRecordProcessor, + ); + }); + + it("registers PII scrub processors BEFORE the Sentry processors when DSN is set (ADR-017 §7)", () => { + initOtelServerNode({ dsn: "https://test@sentry.io/1", serviceName: "test-service", environment: "test", }); - expect(sdk).toBeDefined(); + const cfg = lastConfig(); + expect(cfg.spanProcessors).toHaveLength(2); + expect(cfg.spanProcessors[0]).toBeInstanceOf(PiiScrubSpanProcessor); + expect(cfg.spanProcessors[1]).toBeInstanceOf(FakeBatchSpanProcessor); + expect(cfg.logRecordProcessors).toHaveLength(2); + expect(cfg.logRecordProcessors[0]).toBeInstanceOf( + PiiScrubLogRecordProcessor, + ); + expect(cfg.logRecordProcessors[1]).toBeInstanceOf(SentryLogRecordForwarder); }); }); -- 2.49.1 From 1e3220aef5bb63afbe1089993db430798d027316 Mon Sep 17 00:00:00 2001 From: Danijel Martinek Date: Fri, 10 Jul 2026 16:33:41 +0200 Subject: [PATCH 11/54] fix(core-ui): stop excluding Storybook stories from typecheck MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit *.stories.tsx / *.stories.ts were excluded from tsconfig, so story files never typechecked and could drift against component props silently. Include them (they already typecheck cleanly); build is tsc --noEmit so nothing new is emitted. The Select scrollIntoView port from the downstream fork does not apply — this tree has no Select atom. Co-Authored-By: Claude Fable 5 --- packages/core-ui/tsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core-ui/tsconfig.json b/packages/core-ui/tsconfig.json index 741720b..d78a18f 100644 --- a/packages/core-ui/tsconfig.json +++ b/packages/core-ui/tsconfig.json @@ -9,5 +9,5 @@ } }, "include": ["src/**/*"], - "exclude": ["node_modules", "dist", "**/*.stories.tsx", "**/*.stories.ts"] + "exclude": ["node_modules", "dist"] } -- 2.49.1 From a17b984675842a518c7939d34d61d8d7186c550a Mon Sep 17 00:00:00 2001 From: Danijel Martinek Date: Fri, 10 Jul 2026 16:42:46 +0200 Subject: [PATCH 12/54] fix(generators): run UI tests, guard optional bus, harden e2e clones MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three generator fixes: - templates/feature/vitest.config.ts.hbs lacked an include for .test.{ts,tsx}; the node base only includes .test.ts, so scaffolded UI component tests never executed - gen event consume emitted an unguarded bus.subscribe although ctx.bus is optional in BindContext — now wrapped in if (bus) {} - e2e repo clones now exclude /dist and /.next build outputs, and every dep-stripping e2e strips the scaffolded package from EVERY workspace package.json via globSync instead of a hardcoded dependent list that drifts as packages gain or drop the dependency Co-Authored-By: Claude Fable 5 --- .../core-package-analytics.e2e.test.ts | 4 +++ .../__tests__/core-package-audit.e2e.test.ts | 17 ++++++++++--- .../core-package-consent.e2e.test.ts | 18 ++++++++++--- .../__tests__/core-package-dsr.e2e.test.ts | 17 ++++++++++--- .../__tests__/core-package-events.e2e.test.ts | 25 +++++++++---------- .../core-package-realtime.e2e.test.ts | 4 +++ .../__tests__/core-package-trpc.e2e.test.ts | 23 +++++++++-------- .../__tests__/core-package-ui.e2e.test.ts | 19 ++++++++++---- .../generators/__tests__/feature.e2e.test.ts | 6 ++++- turbo/generators/config.ts | 5 +++- .../templates/feature/vitest.config.ts.hbs | 3 +++ 11 files changed, 100 insertions(+), 41 deletions(-) diff --git a/turbo/generators/__tests__/core-package-analytics.e2e.test.ts b/turbo/generators/__tests__/core-package-analytics.e2e.test.ts index bc93a33..3a081d8 100644 --- a/turbo/generators/__tests__/core-package-analytics.e2e.test.ts +++ b/turbo/generators/__tests__/core-package-analytics.e2e.test.ts @@ -31,6 +31,10 @@ describe("e2e: core-package analytics", () => { !src.includes("node_modules") && !src.includes(".turbo") && !src.includes(".pnpm-store") && + // Build outputs are large and irrelevant to the scaffold — cloning + // dist/.next fills the tmp dir and slows every e2e run. + !src.includes("/dist") && + !src.includes("/.next") && !src.includes("packages/core-analytics"), }); diff --git a/turbo/generators/__tests__/core-package-audit.e2e.test.ts b/turbo/generators/__tests__/core-package-audit.e2e.test.ts index 18af1b1..7375c54 100644 --- a/turbo/generators/__tests__/core-package-audit.e2e.test.ts +++ b/turbo/generators/__tests__/core-package-audit.e2e.test.ts @@ -3,6 +3,7 @@ import { mkdtempSync, rmSync, cpSync, + globSync, readFileSync, writeFileSync, } from "node:fs"; @@ -58,12 +59,22 @@ describe("e2e: core-package audit", () => { !src.includes("node_modules") && !src.includes(".turbo") && !src.includes(".pnpm-store") && + // Build outputs are large and irrelevant to the scaffold — cloning + // dist/.next fills the tmp dir and slows every e2e run. + !src.includes("/dist") && + !src.includes("/.next") && !src.includes("packages/core-audit"), }); - // Strip @repo/core-audit from apps/web-next/package.json so pnpm install - // succeeds without the package being present (simulating the post-removal state). - stripCoreAuditDep(join(tmp, "apps", "web-next", "package.json")); + // Strip @repo/core-audit from EVERY workspace package.json so pnpm + // install succeeds without the package being present (simulating the + // post-removal state) — a hardcoded dependent list drifts as packages + // gain or drop the dependency. + for (const pkgJson of globSync( + join(tmp, "{apps,packages}", "*", "package.json"), + )) { + stripCoreAuditDep(pkgJson); + } execSync(`cd ${tmp} && pnpm install --frozen-lockfile=false`, { stdio: "ignore", diff --git a/turbo/generators/__tests__/core-package-consent.e2e.test.ts b/turbo/generators/__tests__/core-package-consent.e2e.test.ts index cb51790..c415db6 100644 --- a/turbo/generators/__tests__/core-package-consent.e2e.test.ts +++ b/turbo/generators/__tests__/core-package-consent.e2e.test.ts @@ -3,6 +3,7 @@ import { mkdtempSync, rmSync, cpSync, + globSync, readFileSync, writeFileSync, } from "node:fs"; @@ -58,13 +59,22 @@ describe("e2e: core-package consent", () => { !src.includes("node_modules") && !src.includes(".turbo") && !src.includes(".pnpm-store") && + // Build outputs are large and irrelevant to the scaffold — cloning + // dist/.next fills the tmp dir and slows every e2e run. + !src.includes("/dist") && + !src.includes("/.next") && !src.includes("packages/core-consent"), }); - // Strip @repo/core-consent from package.json files so pnpm install - // succeeds without the package being present (simulating the post-removal state). - stripCoreConsentDep(join(tmp, "packages", "core-api", "package.json")); - stripCoreConsentDep(join(tmp, "packages", "core-ui", "package.json")); + // Strip @repo/core-consent from EVERY workspace package.json so pnpm + // install succeeds without the package being present (simulating the + // post-removal state) — a hardcoded dependent list drifts as packages + // gain or drop the dependency. + for (const pkgJson of globSync( + join(tmp, "{apps,packages}", "*", "package.json"), + )) { + stripCoreConsentDep(pkgJson); + } execSync(`cd ${tmp} && pnpm install --frozen-lockfile=false`, { stdio: "ignore", diff --git a/turbo/generators/__tests__/core-package-dsr.e2e.test.ts b/turbo/generators/__tests__/core-package-dsr.e2e.test.ts index 75084c4..3461526 100644 --- a/turbo/generators/__tests__/core-package-dsr.e2e.test.ts +++ b/turbo/generators/__tests__/core-package-dsr.e2e.test.ts @@ -3,6 +3,7 @@ import { mkdtempSync, rmSync, cpSync, + globSync, readFileSync, writeFileSync, } from "node:fs"; @@ -58,12 +59,22 @@ describe("e2e: core-package dsr", () => { !src.includes("node_modules") && !src.includes(".turbo") && !src.includes(".pnpm-store") && + // Build outputs are large and irrelevant to the scaffold — cloning + // dist/.next fills the tmp dir and slows every e2e run. + !src.includes("/dist") && + !src.includes("/.next") && !src.includes("packages/core-dsr"), }); - // Strip @repo/core-dsr from package.json files so pnpm install - // succeeds without the package being present (simulating the post-removal state). - stripCoreDsrDep(join(tmp, "packages", "core-api", "package.json")); + // Strip @repo/core-dsr from EVERY workspace package.json so pnpm + // install succeeds without the package being present (simulating the + // post-removal state) — a hardcoded dependent list drifts as packages + // gain or drop the dependency. + for (const pkgJson of globSync( + join(tmp, "{apps,packages}", "*", "package.json"), + )) { + stripCoreDsrDep(pkgJson); + } execSync(`cd ${tmp} && pnpm install --frozen-lockfile=false`, { stdio: "ignore", diff --git a/turbo/generators/__tests__/core-package-events.e2e.test.ts b/turbo/generators/__tests__/core-package-events.e2e.test.ts index 7587d99..9a511c5 100644 --- a/turbo/generators/__tests__/core-package-events.e2e.test.ts +++ b/turbo/generators/__tests__/core-package-events.e2e.test.ts @@ -3,6 +3,7 @@ import { mkdtempSync, rmSync, cpSync, + globSync, readFileSync, writeFileSync, } from "node:fs"; @@ -58,24 +59,22 @@ describe("e2e: core-package events", () => { !src.includes("node_modules") && !src.includes(".turbo") && !src.includes(".pnpm-store") && + // Build outputs are large and irrelevant to the scaffold — cloning + // dist/.next fills the tmp dir and slows every e2e run. + !src.includes("/dist") && + !src.includes("/.next") && !src.includes("packages/core-events"), }); - // Strip @repo/core-events from feature package.json files so pnpm install - // succeeds without the package being present (simulating the post-removal state). - const featurePackages = [ - "auth", - "blog", - "media", - "marketing-pages", - "navigation", - ]; - for (const pkg of featurePackages) { - const pkgJson = join(tmp, "packages", pkg, "package.json"); + // Strip @repo/core-events from EVERY workspace package.json so pnpm + // install succeeds without the package being present (simulating the + // post-removal state) — a hardcoded dependent list drifts as packages + // gain or drop the dependency. + for (const pkgJson of globSync( + join(tmp, "{apps,packages}", "*", "package.json"), + )) { stripCoreEventsDep(pkgJson); } - // Also strip from apps/web-next - stripCoreEventsDep(join(tmp, "apps", "web-next", "package.json")); execSync(`cd ${tmp} && pnpm install --frozen-lockfile=false`, { stdio: "ignore", diff --git a/turbo/generators/__tests__/core-package-realtime.e2e.test.ts b/turbo/generators/__tests__/core-package-realtime.e2e.test.ts index f3fb052..2edaf95 100644 --- a/turbo/generators/__tests__/core-package-realtime.e2e.test.ts +++ b/turbo/generators/__tests__/core-package-realtime.e2e.test.ts @@ -31,6 +31,10 @@ describe("e2e: core-package realtime", () => { !src.includes("node_modules") && !src.includes(".turbo") && !src.includes(".pnpm-store") && + // Build outputs are large and irrelevant to the scaffold — cloning + // dist/.next fills the tmp dir and slows every e2e run. + !src.includes("/dist") && + !src.includes("/.next") && !src.includes("packages/core-realtime"), }); execSync(`cd ${tmp} && pnpm install --frozen-lockfile=false`, { diff --git a/turbo/generators/__tests__/core-package-trpc.e2e.test.ts b/turbo/generators/__tests__/core-package-trpc.e2e.test.ts index 257481b..ceeebb5 100644 --- a/turbo/generators/__tests__/core-package-trpc.e2e.test.ts +++ b/turbo/generators/__tests__/core-package-trpc.e2e.test.ts @@ -3,6 +3,7 @@ import { mkdtempSync, rmSync, cpSync, + globSync, readFileSync, writeFileSync, } from "node:fs"; @@ -58,21 +59,21 @@ describe("e2e: core-package trpc", () => { !src.includes("node_modules") && !src.includes(".turbo") && !src.includes(".pnpm-store") && + // Build outputs are large and irrelevant to the scaffold — cloning + // dist/.next fills the tmp dir and slows every e2e run. + !src.includes("/dist") && + !src.includes("/.next") && !src.includes("packages/core-trpc"), }); - // Strip @repo/core-trpc from every package that references it so pnpm + // Strip @repo/core-trpc from EVERY workspace package.json so pnpm // install succeeds without the package being present (simulating the - // post-removal state). Apps list it directly; the blog, marketing-pages, - // and navigation features depend on it for their ./ui tRPC hooks. - for (const pkgDir of [ - ["apps", "web-next"], - ["apps", "web-tanstack"], - ["packages", "blog"], - ["packages", "marketing-pages"], - ["packages", "navigation"], - ]) { - stripCoreTrpcDep(join(tmp, ...pkgDir, "package.json")); + // post-removal state) — a hardcoded dependent list drifts as packages + // gain or drop the dependency. + for (const pkgJson of globSync( + join(tmp, "{apps,packages}", "*", "package.json"), + )) { + stripCoreTrpcDep(pkgJson); } execSync(`cd ${tmp} && pnpm install --frozen-lockfile=false`, { diff --git a/turbo/generators/__tests__/core-package-ui.e2e.test.ts b/turbo/generators/__tests__/core-package-ui.e2e.test.ts index cc5ea61..cd5c27f 100644 --- a/turbo/generators/__tests__/core-package-ui.e2e.test.ts +++ b/turbo/generators/__tests__/core-package-ui.e2e.test.ts @@ -3,6 +3,7 @@ import { mkdtempSync, rmSync, cpSync, + globSync, readFileSync, writeFileSync, } from "node:fs"; @@ -58,14 +59,22 @@ describe("e2e: core-package ui", () => { !src.includes("node_modules") && !src.includes(".turbo") && !src.includes(".pnpm-store") && + // Build outputs are large and irrelevant to the scaffold — cloning + // dist/.next fills the tmp dir and slows every e2e run. + !src.includes("/dist") && + !src.includes("/.next") && !src.includes("packages/core-ui"), }); - // Strip @repo/core-ui from app package.json files so pnpm install - // succeeds without the package being present (simulating the post-removal state). - stripCoreUiDep(join(tmp, "apps", "web-next", "package.json")); - stripCoreUiDep(join(tmp, "apps", "web-tanstack", "package.json")); - stripCoreUiDep(join(tmp, "apps", "storybook", "package.json")); + // Strip @repo/core-ui from EVERY workspace package.json so pnpm + // install succeeds without the package being present (simulating the + // post-removal state) — a hardcoded dependent list drifts as packages + // gain or drop the dependency. + for (const pkgJson of globSync( + join(tmp, "{apps,packages}", "*", "package.json"), + )) { + stripCoreUiDep(pkgJson); + } execSync(`cd ${tmp} && pnpm install --frozen-lockfile=false`, { stdio: "ignore", diff --git a/turbo/generators/__tests__/feature.e2e.test.ts b/turbo/generators/__tests__/feature.e2e.test.ts index 4872282..84b386c 100644 --- a/turbo/generators/__tests__/feature.e2e.test.ts +++ b/turbo/generators/__tests__/feature.e2e.test.ts @@ -38,7 +38,11 @@ describe("e2e: feature generator composition", () => { filter: (src) => !src.includes("node_modules") && !src.includes(".turbo") && - !src.includes(".pnpm-store"), + !src.includes(".pnpm-store") && + // Build outputs are large and irrelevant to the scaffold — cloning + // dist/.next fills the tmp dir and slows every e2e run. + !src.includes("/dist") && + !src.includes("/.next"), }); execSync(`cd ${tmp} && pnpm install --frozen-lockfile=false`, { diff --git a/turbo/generators/config.ts b/turbo/generators/config.ts index 9d2c7c1..be3864b 100644 --- a/turbo/generators/config.ts +++ b/turbo/generators/config.ts @@ -1300,7 +1300,10 @@ function handlerBindBlock(a: { ${containerVar}.unbind(${handlerSymbol}); } ${containerVar}.bind(${handlerSymbol}).toConstantValue(${wrappedVar}); - bus.subscribe(${eventConst}, "${a.feature}", ${wrappedVar});`; + // ctx.bus is an OPTIONAL core (guard per CLAUDE.md binder conventions). + if (bus) { + bus.subscribe(${eventConst}, "${a.feature}", ${wrappedVar}); + }`; } function printConsumeNextSteps(a: { diff --git a/turbo/generators/templates/feature/vitest.config.ts.hbs b/turbo/generators/templates/feature/vitest.config.ts.hbs index 524276b..fd3bae8 100644 --- a/turbo/generators/templates/feature/vitest.config.ts.hbs +++ b/turbo/generators/templates/feature/vitest.config.ts.hbs @@ -12,6 +12,9 @@ import { // manifest when adjusting per-feature bands. export default mergeConfig(nodeVitestConfig, { test: { + // The node base only includes .test.ts; UI tests are .test.tsx files + // that opt into jsdom per-file via the @vitest-environment docblock. + include: ["src/**/*.test.{ts,tsx}"], coverage: { exclude: [ // DI bootstrap — wires InversifyJS at app startup; not unit-testable -- 2.49.1 From c0dbadf1c2b8c1fa8660add47d23e9efc6d67063 Mon Sep 17 00:00:00 2001 From: Danijel Martinek Date: Fri, 10 Jul 2026 16:43:32 +0200 Subject: [PATCH 13/54] ci: honest audit gate, real Socket CLI, reachable mutation issue step MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 'pnpm audit signatures' is an npm-only feature; pnpm ignored the word and ran a plain full audit. Replace with an explicit 'pnpm audit --prod --audit-level=critical' (documented rationale) - socket-cli is a 0.0.1 stub on npm; use the real 'socket' CLI and fail loudly instead of silently passing - mutation-nightly's issue-opening step was gated on if: failure(), unreachable under continue-on-error — gate on steps.mutate.outcome - wire the ADR-023 renovate major-bump gate (scripts/library-decisions/check.mjs --renovate-pr) on PRs; verified it no-ops on non-renovate branches Co-Authored-By: Claude Fable 5 --- .github/workflows/ci.yml | 22 +++++++++++++++++++--- .github/workflows/mutation-nightly.yml | 6 +++++- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0397496..f733106 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,17 +51,33 @@ jobs: node-version: 22 cache: pnpm - run: pnpm install --frozen-lockfile - - name: Audit package signatures - run: pnpm audit signatures --audit-level=high + # pnpm has no `audit signatures` (that's an npm feature) — the old + # step silently ignored "signatures" and ran a plain full audit that + # hard-fails on unfixable transitive dev-tooling advisories. Gate on + # CRITICAL production-path advisories; Renovate + the weekly trace + # revalidation own the long tail. + - name: Dependency vulnerability audit (critical, prod paths) + run: pnpm audit --prod --audit-level=critical + # The real Socket CLI package is `socket` — `socket-cli` is a 0.0.1 + # stub on npm; failures must be loud, not silently green. - name: Socket supply-chain scan if: github.event_name == 'pull_request' run: | if git diff --name-only origin/${{ github.base_ref }}...HEAD \ | grep -qE '(^|/)package\.json$|(^|/)pnpm-lock\.yaml$'; then - npx --yes socket-cli@latest scan . + npx --yes socket@latest scan create --report . || { + echo "Socket scan failed (missing SOCKET_SECURITY_API_KEY?) — failing loudly rather than silently skipping." + exit 1 + } else echo "No package.json or pnpm-lock.yaml changes — skipping Socket scan." fi + # ADR-023 escalation matrix: Renovate major bumps re-run the library + # evaluation; the gate blocks a renovate/* PR whose lockfile majors a + # traced dependency without a refreshed trace. No-op on ordinary PRs. + - name: Library-trace major-bump gate + if: github.event_name == 'pull_request' + run: node scripts/library-decisions/check.mjs --renovate-pr - run: pnpm typecheck - run: pnpm lint - run: pnpm conformance diff --git a/.github/workflows/mutation-nightly.yml b/.github/workflows/mutation-nightly.yml index 412138e..d923fea 100644 --- a/.github/workflows/mutation-nightly.yml +++ b/.github/workflows/mutation-nightly.yml @@ -53,6 +53,7 @@ jobs: cache: pnpm - run: pnpm install --frozen-lockfile - name: Run mutation testing + id: mutate env: DATABASE_URL: postgres://postgres:postgres@localhost:5432/cms_test PAYLOAD_SECRET: test-secret-do-not-use-in-prod @@ -70,8 +71,11 @@ jobs: name: mutation-reports path: packages/*/reports/mutation/ retention-days: 30 + # `continue-on-error: true` means the JOB never reports failure — gate + # the tracking issue on the STEP outcome instead (if: failure() was + # unreachable and the issue could never open). - name: Open tracking issue on >5% score drop - if: failure() + if: steps.mutate.outcome == 'failure' uses: actions/github-script@v7 with: script: | -- 2.49.1 From 5f38a3efcef84378d0dc3c93be20a965be0113c0 Mon Sep 17 00:00:00 2001 From: Danijel Martinek Date: Fri, 10 Jul 2026 16:44:48 +0200 Subject: [PATCH 14/54] build: bump vitest past GHSA-5xrq-8626-4rwp; coverage-v8 everywhere pnpm audit --prod --audit-level=critical (the new CI gate) failed on vitest <3.2.6 (critical, arbitrary file read/execute via the UI server). Bump vitest + @vitest/coverage-v8 in-range across the workspace and add @vitest/coverage-v8 to every package that runs vitest but lacked it (core-audit, core-cms, core-eslint, core-testing, core-trpc, apps/cms, web-next, web-tanstack, turbo/generators) so 'pnpm test -- --coverage' works in every package. No compound test scripts exist, so the vitest-last pass-through concern does not apply. Co-Authored-By: Claude Fable 5 --- apps/cms/package.json | 3 +- apps/web-next/package.json | 3 +- apps/web-tanstack/package.json | 3 +- packages/auth/package.json | 4 +- packages/blog/package.json | 4 +- packages/core-analytics/package.json | 4 +- packages/core-api/package.json | 4 +- packages/core-audit/package.json | 3 +- packages/core-cms/package.json | 11 +- packages/core-consent/package.json | 4 +- packages/core-dsr/package.json | 4 +- packages/core-eslint/package.json | 3 +- packages/core-shared/package.json | 6 +- packages/core-testing/package.json | 7 +- packages/core-trpc/package.json | 3 +- packages/core-typescript/package.json | 4 +- packages/core-ui/package.json | 4 +- packages/marketing-pages/package.json | 4 +- packages/media/package.json | 4 +- packages/navigation/package.json | 4 +- pnpm-lock.yaml | 873 ++++++++++++++++++-------- turbo/generators/package.json | 3 +- 22 files changed, 647 insertions(+), 315 deletions(-) diff --git a/apps/cms/package.json b/apps/cms/package.json index 7c71b17..8003f7e 100644 --- a/apps/cms/package.json +++ b/apps/cms/package.json @@ -32,6 +32,7 @@ "@types/node": "^22.0.0", "@types/react": "^19.0.0", "@types/react-dom": "^19.0.0", - "vitest": "^3.0.0" + "@vitest/coverage-v8": "^3.2.7", + "vitest": "^3.2.7" } } diff --git a/apps/web-next/package.json b/apps/web-next/package.json index 78534be..9f3662d 100644 --- a/apps/web-next/package.json +++ b/apps/web-next/package.json @@ -47,8 +47,9 @@ "@types/node": "^22.0.0", "@types/react": "^19.0.0", "@types/react-dom": "^19.0.0", + "@vitest/coverage-v8": "^3.2.7", "jsdom": "^25.0.0", "tsx": "^4.0.0", - "vitest": "^3.0.0" + "vitest": "^3.2.7" } } diff --git a/apps/web-tanstack/package.json b/apps/web-tanstack/package.json index 5a2feca..47a8d3f 100644 --- a/apps/web-tanstack/package.json +++ b/apps/web-tanstack/package.json @@ -38,7 +38,8 @@ "@types/node": "^22.0.0", "@types/react": "^19.0.0", "@types/react-dom": "^19.0.0", + "@vitest/coverage-v8": "^3.2.7", "jsdom": "^25.0.0", - "vitest": "^3.0.0" + "vitest": "^3.2.7" } } diff --git a/packages/auth/package.json b/packages/auth/package.json index af6fde7..7111ff8 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -32,7 +32,7 @@ "@repo/core-testing": "workspace:*", "@repo/core-typescript": "workspace:*", "@types/node": "^22.0.0", - "@vitest/coverage-v8": "^3.2.4", - "vitest": "^3.1.0" + "@vitest/coverage-v8": "^3.2.7", + "vitest": "^3.2.7" } } diff --git a/packages/blog/package.json b/packages/blog/package.json index 19876f0..706d407 100644 --- a/packages/blog/package.json +++ b/packages/blog/package.json @@ -35,7 +35,7 @@ "@repo/core-typescript": "workspace:*", "@types/node": "^22.0.0", "@types/react": "^19.0.0", - "@vitest/coverage-v8": "^3.2.4", - "vitest": "^3.1.0" + "@vitest/coverage-v8": "^3.2.7", + "vitest": "^3.2.7" } } diff --git a/packages/core-analytics/package.json b/packages/core-analytics/package.json index 215c0bc..cb97ecb 100644 --- a/packages/core-analytics/package.json +++ b/packages/core-analytics/package.json @@ -30,10 +30,10 @@ "@repo/core-typescript": "workspace:*", "@testing-library/react": "^16.0.0", "@types/react": "^19.0.0", - "@vitest/coverage-v8": "^3.0.0", + "@vitest/coverage-v8": "^3.2.7", "jsdom": "^25.0.0", "react": "^19.0.0", "typescript": "^5.8.0", - "vitest": "^3.0.0" + "vitest": "^3.2.7" } } diff --git a/packages/core-api/package.json b/packages/core-api/package.json index d91d243..ea7b260 100644 --- a/packages/core-api/package.json +++ b/packages/core-api/package.json @@ -28,7 +28,7 @@ "@repo/core-testing": "workspace:*", "@repo/core-typescript": "workspace:*", "@types/node": "^22.0.0", - "@vitest/coverage-v8": "^3.0.0", - "vitest": "^3.0.0" + "@vitest/coverage-v8": "^3.2.7", + "vitest": "^3.2.7" } } diff --git a/packages/core-audit/package.json b/packages/core-audit/package.json index 013f356..9b03317 100644 --- a/packages/core-audit/package.json +++ b/packages/core-audit/package.json @@ -37,10 +37,11 @@ "@repo/core-eslint": "workspace:*", "@repo/core-testing": "workspace:*", "@repo/core-typescript": "workspace:*", + "@vitest/coverage-v8": "^3.2.7", "inversify": "^6.2.0", "payload": "^3.14.0", "reflect-metadata": "^0.2.2", "typescript": "^5.8.0", - "vitest": "^3.0.0" + "vitest": "^3.2.7" } } diff --git a/packages/core-cms/package.json b/packages/core-cms/package.json index 9b7aa99..f2d4d38 100644 --- a/packages/core-cms/package.json +++ b/packages/core-cms/package.json @@ -14,20 +14,21 @@ "test": "vitest run --passWithNoTests" }, "dependencies": { + "@payloadcms/db-postgres": "^3.14.0", + "@payloadcms/richtext-lexical": "^3.14.0", "@repo/auth": "workspace:*", "@repo/blog": "workspace:*", - "@repo/media": "workspace:*", "@repo/marketing-pages": "workspace:*", + "@repo/media": "workspace:*", "@repo/navigation": "workspace:*", - "payload": "^3.14.0", - "@payloadcms/db-postgres": "^3.14.0", - "@payloadcms/richtext-lexical": "^3.14.0" + "payload": "^3.14.0" }, "devDependencies": { "@repo/core-eslint": "workspace:*", "@repo/core-testing": "workspace:*", "@repo/core-typescript": "workspace:*", "@types/node": "^22.0.0", - "vitest": "^3.0.0" + "@vitest/coverage-v8": "^3.2.7", + "vitest": "^3.2.7" } } diff --git a/packages/core-consent/package.json b/packages/core-consent/package.json index 3be1938..c3aebaf 100644 --- a/packages/core-consent/package.json +++ b/packages/core-consent/package.json @@ -37,11 +37,11 @@ "@repo/core-typescript": "workspace:*", "@testing-library/react": "^16.0.0", "@types/react": "^19.0.0", - "@vitest/coverage-v8": "^3.0.0", + "@vitest/coverage-v8": "^3.2.7", "jsdom": "^25.0.0", "payload": "^3.14.0", "react": "^19.0.0", "typescript": "^5.8.0", - "vitest": "^3.0.0" + "vitest": "^3.2.7" } } diff --git a/packages/core-dsr/package.json b/packages/core-dsr/package.json index a2f52ab..4b2aa0e 100644 --- a/packages/core-dsr/package.json +++ b/packages/core-dsr/package.json @@ -23,8 +23,8 @@ "@repo/core-eslint": "workspace:*", "@repo/core-testing": "workspace:*", "@repo/core-typescript": "workspace:*", - "@vitest/coverage-v8": "^3.0.0", + "@vitest/coverage-v8": "^3.2.7", "typescript": "^5.8.0", - "vitest": "^3.0.0" + "vitest": "^3.2.7" } } diff --git a/packages/core-eslint/package.json b/packages/core-eslint/package.json index fe578e0..4251ef7 100644 --- a/packages/core-eslint/package.json +++ b/packages/core-eslint/package.json @@ -16,13 +16,14 @@ "@eslint/js": "^9.20.0", "@typescript-eslint/eslint-plugin": "^8.25.0", "@typescript-eslint/parser": "^8.25.0", + "@vitest/coverage-v8": "^3.2.7", "eslint": "^9.20.0", "eslint-config-prettier": "^10.1.0", "eslint-plugin-boundaries": "^4.2.2", "eslint-plugin-react-hooks": "^5.2.0", "eslint-plugin-turbo": "^2.4.0", "typescript-eslint": "^8.25.0", - "vitest": "^3.1.0" + "vitest": "^3.2.7" }, "dependencies": { "globals": "^17.6.0" diff --git a/packages/core-shared/package.json b/packages/core-shared/package.json index 140eca3..08a12a9 100644 --- a/packages/core-shared/package.json +++ b/packages/core-shared/package.json @@ -69,7 +69,6 @@ } }, "devDependencies": { - "next": "^15.3.0", "@opentelemetry/context-async-hooks": "^1.28.0", "@repo/core-eslint": "workspace:*", "@repo/core-testing": "workspace:*", @@ -77,9 +76,10 @@ "@sentry/node": "^10.51.0", "@sentry/react": "^10.51.0", "@types/node": "^22.0.0", - "@vitest/coverage-v8": "^3.2.4", + "@vitest/coverage-v8": "^3.2.7", "inversify": "^6.2.0", + "next": "^15.3.0", "reflect-metadata": "^0.2.2", - "vitest": "^3.1.0" + "vitest": "^3.2.7" } } diff --git a/packages/core-testing/package.json b/packages/core-testing/package.json index 03fccc0..3289d19 100644 --- a/packages/core-testing/package.json +++ b/packages/core-testing/package.json @@ -25,16 +25,16 @@ "test": "vitest run" }, "dependencies": { + "@tanstack/react-query": "^5.59.0", "@testing-library/jest-dom": "^6.5.0", - "zod": "^3.23.0", "@testing-library/react": "^16.0.0", "@testing-library/user-event": "^14.5.0", "@trpc/client": "^11.0.0", - "@tanstack/react-query": "^5.59.0", "react": "^19.0.0", "react-dom": "^19.0.0", "superjson": "^2.2.0", - "vitest": "^3.0.0" + "vitest": "^3.2.7", + "zod": "^3.23.0" }, "peerDependencies": { "@trpc/server": "^11.0.0", @@ -54,6 +54,7 @@ "@sentry/nextjs": "^10.51.0", "@types/react": "^19.0.0", "@types/react-dom": "^19.0.0", + "@vitest/coverage-v8": "^3.2.7", "jsdom": "^25.0.0", "typescript": "^5.8.0" } diff --git a/packages/core-trpc/package.json b/packages/core-trpc/package.json index da39811..a41588c 100644 --- a/packages/core-trpc/package.json +++ b/packages/core-trpc/package.json @@ -31,7 +31,8 @@ "@testing-library/jest-dom": "^6.5.0", "@testing-library/react": "^16.0.0", "@types/react": "^19.0.0", + "@vitest/coverage-v8": "^3.2.7", "jsdom": "^25.0.0", - "vitest": "^3.0.0" + "vitest": "^3.2.7" } } diff --git a/packages/core-typescript/package.json b/packages/core-typescript/package.json index a174fff..b9f5e89 100644 --- a/packages/core-typescript/package.json +++ b/packages/core-typescript/package.json @@ -15,7 +15,7 @@ "test": "vitest run" }, "devDependencies": { - "@vitest/coverage-v8": "^3.2.4", - "vitest": "^3.1.0" + "@vitest/coverage-v8": "^3.2.7", + "vitest": "^3.2.7" } } diff --git a/packages/core-ui/package.json b/packages/core-ui/package.json index ae7b053..3f3b3db 100644 --- a/packages/core-ui/package.json +++ b/packages/core-ui/package.json @@ -30,9 +30,9 @@ "@testing-library/user-event": "^14.5.0", "@types/jest-axe": "^3.5.9", "@types/react": "^19.0.0", - "@vitest/coverage-v8": "^3.0.0", + "@vitest/coverage-v8": "^3.2.7", "jest-axe": "^10.0.0", "jsdom": "^25.0.0", - "vitest": "^3.0.0" + "vitest": "^3.2.7" } } diff --git a/packages/marketing-pages/package.json b/packages/marketing-pages/package.json index 3c5ca17..acd042c 100644 --- a/packages/marketing-pages/package.json +++ b/packages/marketing-pages/package.json @@ -40,7 +40,7 @@ "@repo/core-typescript": "workspace:*", "@types/node": "^22.0.0", "@types/react": "^19.0.0", - "@vitest/coverage-v8": "^3.2.4", - "vitest": "^3.1.0" + "@vitest/coverage-v8": "^3.2.7", + "vitest": "^3.2.7" } } diff --git a/packages/media/package.json b/packages/media/package.json index 2c3b651..399e2a4 100644 --- a/packages/media/package.json +++ b/packages/media/package.json @@ -30,7 +30,7 @@ "@repo/core-testing": "workspace:*", "@repo/core-typescript": "workspace:*", "@types/node": "^22.0.0", - "@vitest/coverage-v8": "^3.2.4", - "vitest": "^3.1.0" + "@vitest/coverage-v8": "^3.2.7", + "vitest": "^3.2.7" } } diff --git a/packages/navigation/package.json b/packages/navigation/package.json index ecb97e9..2ce1f56 100644 --- a/packages/navigation/package.json +++ b/packages/navigation/package.json @@ -35,7 +35,7 @@ "@repo/core-typescript": "workspace:*", "@types/node": "^22.0.0", "@types/react": "^19.0.0", - "@vitest/coverage-v8": "^3.2.4", - "vitest": "^3.1.0" + "@vitest/coverage-v8": "^3.2.7", + "vitest": "^3.2.7" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 08312c0..299a0ab 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -18,7 +18,7 @@ importers: version: 8.7.1 "@stryker-mutator/vitest-runner": specifier: ^8.7.0 - version: 8.7.1(@stryker-mutator/core@8.7.1)(vitest@3.2.4(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0)) + version: 8.7.1(@stryker-mutator/core@8.7.1)(vitest@3.2.7(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0)) "@turbo/gen": specifier: ^2.4.0 version: 2.9.10(@types/node@22.19.17) @@ -72,7 +72,7 @@ importers: version: link:../../packages/core-shared "@sentry/nextjs": specifier: ^10.51.0 - version: 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(@babel/core@7.25.9)(@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) + version: 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(@babel/core@7.25.9)(@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(lightningcss@1.32.0)) next: specifier: ^15.3.0 version: 15.5.14(@babel/core@7.25.9)(@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) @@ -110,9 +110,12 @@ importers: "@types/react-dom": specifier: ^19.0.0 version: 19.2.3(@types/react@19.2.14) + "@vitest/coverage-v8": + specifier: ^3.2.7 + version: 3.2.7(vitest@3.2.7(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0)) vitest: - specifier: ^3.0.0 - version: 3.2.4(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0) + specifier: ^3.2.7 + version: 3.2.7(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0) apps/storybook: devDependencies: @@ -133,13 +136,13 @@ importers: version: 8.6.18(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@8.6.18(prettier@3.8.1))(typescript@5.9.3) "@storybook/react-vite": specifier: ^8.6.0 - version: 8.6.18(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(rollup@4.60.4)(storybook@8.6.18(prettier@3.8.1))(typescript@5.9.3)(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0)) + version: 8.6.18(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(rollup@4.60.4)(storybook@8.6.18(prettier@3.8.1))(typescript@5.9.3)(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0)) "@storybook/test-runner": specifier: ^0.19.1 version: 0.19.1(@types/node@22.19.17)(babel-plugin-macros@3.1.0)(storybook@8.6.18(prettier@3.8.1)) "@tailwindcss/vite": specifier: ^4.1.0 - version: 4.2.2(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0)) + version: 4.2.2(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0)) concurrently: specifier: ^9.0.0 version: 9.2.1 @@ -163,7 +166,7 @@ importers: version: 4.2.2 vite: specifier: ^6.3.0 - version: 6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0) + version: 6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0) wait-on: specifier: ^8.0.0 version: 8.0.5 @@ -199,7 +202,7 @@ importers: version: link:../../packages/navigation "@sentry/nextjs": specifier: ^10.51.0 - version: 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(@babel/core@7.25.9)(@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) + version: 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(@babel/core@7.25.9)(@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(lightningcss@1.32.0)) "@tailwindcss/postcss": specifier: ^4.3.0 version: 4.3.0 @@ -264,6 +267,9 @@ importers: "@types/react-dom": specifier: ^19.0.0 version: 19.2.3(@types/react@19.2.14) + "@vitest/coverage-v8": + specifier: ^3.2.7 + version: 3.2.7(vitest@3.2.7(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0)) jsdom: specifier: ^25.0.0 version: 25.0.1 @@ -271,8 +277,8 @@ importers: specifier: ^4.0.0 version: 4.21.0 vitest: - specifier: ^3.0.0 - version: 3.2.4(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0) + specifier: ^3.2.7 + version: 3.2.7(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0) apps/web-tanstack: dependencies: @@ -305,7 +311,7 @@ importers: version: 1.168.10(react-dom@19.2.4(react@19.2.4))(react@19.2.4) "@tanstack/start": specifier: ^1.120.0 - version: 1.120.20(@types/node@22.19.17)(db0@0.3.4(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3)))(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3))(ioredis@5.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0))(webpack@5.106.2)(yaml@2.9.0) + version: 1.120.20(@types/node@22.19.17)(db0@0.3.4(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3)))(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3))(ioredis@5.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0))(webpack@5.106.2(lightningcss@1.32.0))(yaml@2.9.0) react: specifier: ^19.0.0 version: 19.2.4 @@ -314,7 +320,7 @@ importers: version: 19.2.4(react@19.2.4) vinxi: specifier: 0.5.3 - version: 0.5.3(@types/node@22.19.17)(db0@0.3.4(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3)))(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3))(ioredis@5.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0) + version: 0.5.3(@types/node@22.19.17)(db0@0.3.4(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3)))(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3))(ioredis@5.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0) devDependencies: "@playwright/test": specifier: ^1.50.0 @@ -349,12 +355,15 @@ importers: "@types/react-dom": specifier: ^19.0.0 version: 19.2.3(@types/react@19.2.14) + "@vitest/coverage-v8": + specifier: ^3.2.7 + version: 3.2.7(vitest@3.2.7(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0)) jsdom: specifier: ^25.0.0 version: 25.0.1 vitest: - specifier: ^3.0.0 - version: 3.2.4(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0) + specifier: ^3.2.7 + version: 3.2.7(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0) packages/auth: dependencies: @@ -390,11 +399,11 @@ importers: specifier: ^22.0.0 version: 22.19.17 "@vitest/coverage-v8": - specifier: ^3.2.4 - version: 3.2.4(vitest@3.2.4(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0)) + specifier: ^3.2.7 + version: 3.2.7(vitest@3.2.7(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0)) vitest: - specifier: ^3.1.0 - version: 3.2.4(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0) + specifier: ^3.2.7 + version: 3.2.7(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0) packages/blog: dependencies: @@ -445,11 +454,11 @@ importers: specifier: ^19.0.0 version: 19.2.14 "@vitest/coverage-v8": - specifier: ^3.2.4 - version: 3.2.4(vitest@3.2.4(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0)) + specifier: ^3.2.7 + version: 3.2.7(vitest@3.2.7(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0)) vitest: - specifier: ^3.1.0 - version: 3.2.4(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0) + specifier: ^3.2.7 + version: 3.2.7(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0) packages/core-analytics: dependencies: @@ -473,8 +482,8 @@ importers: specifier: ^19.0.0 version: 19.2.14 "@vitest/coverage-v8": - specifier: ^3.0.0 - version: 3.2.4(vitest@3.2.4(@types/debug@4.1.13)(@types/node@25.5.2)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0)) + specifier: ^3.2.7 + version: 3.2.7(vitest@3.2.7(@types/debug@4.1.13)(@types/node@25.5.2)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0)) jsdom: specifier: ^25.0.0 version: 25.0.1 @@ -485,8 +494,8 @@ importers: specifier: ^5.8.0 version: 5.9.3 vitest: - specifier: ^3.0.0 - version: 3.2.4(@types/debug@4.1.13)(@types/node@25.5.2)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0) + specifier: ^3.2.7 + version: 3.2.7(@types/debug@4.1.13)(@types/node@25.5.2)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0) packages/core-api: dependencies: @@ -531,11 +540,11 @@ importers: specifier: ^22.0.0 version: 22.19.17 "@vitest/coverage-v8": - specifier: ^3.0.0 - version: 3.2.4(vitest@3.2.4(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0)) + specifier: ^3.2.7 + version: 3.2.7(vitest@3.2.7(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0)) vitest: - specifier: ^3.0.0 - version: 3.2.4(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0) + specifier: ^3.2.7 + version: 3.2.7(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0) packages/core-audit: dependencies: @@ -570,6 +579,9 @@ importers: "@repo/core-typescript": specifier: workspace:* version: link:../core-typescript + "@vitest/coverage-v8": + specifier: ^3.2.7 + version: 3.2.7(vitest@3.2.7(@types/debug@4.1.13)(@types/node@25.5.2)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0)) inversify: specifier: ^6.2.0 version: 6.2.2(reflect-metadata@0.2.2) @@ -583,8 +595,8 @@ importers: specifier: ^5.8.0 version: 5.9.3 vitest: - specifier: ^3.0.0 - version: 3.2.4(@types/debug@4.1.13)(@types/node@25.5.2)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0) + specifier: ^3.2.7 + version: 3.2.7(@types/debug@4.1.13)(@types/node@25.5.2)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0) packages/core-cms: dependencies: @@ -625,9 +637,12 @@ importers: "@types/node": specifier: ^22.0.0 version: 22.19.17 + "@vitest/coverage-v8": + specifier: ^3.2.7 + version: 3.2.7(vitest@3.2.7(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0)) vitest: - specifier: ^3.0.0 - version: 3.2.4(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0) + specifier: ^3.2.7 + version: 3.2.7(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0) packages/core-consent: dependencies: @@ -657,8 +672,8 @@ importers: specifier: ^19.0.0 version: 19.2.14 "@vitest/coverage-v8": - specifier: ^3.0.0 - version: 3.2.4(vitest@3.2.4(@types/debug@4.1.13)(@types/node@25.5.2)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0)) + specifier: ^3.2.7 + version: 3.2.7(vitest@3.2.7(@types/debug@4.1.13)(@types/node@25.5.2)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0)) jsdom: specifier: ^25.0.0 version: 25.0.1 @@ -672,8 +687,8 @@ importers: specifier: ^5.8.0 version: 5.9.3 vitest: - specifier: ^3.0.0 - version: 3.2.4(@types/debug@4.1.13)(@types/node@25.5.2)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0) + specifier: ^3.2.7 + version: 3.2.7(@types/debug@4.1.13)(@types/node@25.5.2)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0) packages/core-dsr: dependencies: @@ -700,14 +715,14 @@ importers: specifier: workspace:* version: link:../core-typescript "@vitest/coverage-v8": - specifier: ^3.0.0 - version: 3.2.4(vitest@3.2.4(@types/debug@4.1.13)(@types/node@25.5.2)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0)) + specifier: ^3.2.7 + version: 3.2.7(vitest@3.2.7(@types/debug@4.1.13)(@types/node@25.5.2)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0)) typescript: specifier: ^5.8.0 version: 5.9.3 vitest: - specifier: ^3.0.0 - version: 3.2.4(@types/debug@4.1.13)(@types/node@25.5.2)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0) + specifier: ^3.2.7 + version: 3.2.7(@types/debug@4.1.13)(@types/node@25.5.2)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0) packages/core-eslint: dependencies: @@ -724,6 +739,9 @@ importers: "@typescript-eslint/parser": specifier: ^8.25.0 version: 8.58.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) + "@vitest/coverage-v8": + specifier: ^3.2.7 + version: 3.2.7(vitest@3.2.7(@types/debug@4.1.13)(@types/node@25.5.2)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0)) eslint: specifier: ^9.20.0 version: 9.39.4(jiti@2.7.0) @@ -743,8 +761,8 @@ importers: specifier: ^8.25.0 version: 8.58.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) vitest: - specifier: ^3.1.0 - version: 3.2.4(@types/debug@4.1.13)(@types/node@25.5.2)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0) + specifier: ^3.2.7 + version: 3.2.7(@types/debug@4.1.13)(@types/node@25.5.2)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0) packages/core-shared: dependencies: @@ -786,7 +804,7 @@ importers: version: 1.40.0 "@sentry/nextjs": specifier: ^10.51.0 - version: 10.51.0(@opentelemetry/core@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.1))(next@15.5.14(@babel/core@7.25.9)(@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) + version: 10.51.0(@opentelemetry/core@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.1))(next@15.5.14(@babel/core@7.25.9)(@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(lightningcss@1.32.0)) "@sentry/opentelemetry": specifier: ^10.51.0 version: 10.52.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.40.0) @@ -825,8 +843,8 @@ importers: specifier: ^22.0.0 version: 22.19.17 "@vitest/coverage-v8": - specifier: ^3.2.4 - version: 3.2.4(vitest@3.2.4(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0)) + specifier: ^3.2.7 + version: 3.2.7(vitest@3.2.7(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0)) inversify: specifier: ^6.2.0 version: 6.2.2(reflect-metadata@0.2.2) @@ -837,8 +855,8 @@ importers: specifier: ^0.2.2 version: 0.2.2 vitest: - specifier: ^3.1.0 - version: 3.2.4(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0) + specifier: ^3.2.7 + version: 3.2.7(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0) packages/core-testing: dependencies: @@ -856,10 +874,10 @@ importers: version: 14.6.1(@testing-library/dom@10.4.1) "@trpc/client": specifier: ^11.0.0 - version: 11.16.0(@trpc/server@11.16.0(typescript@5.9.3))(typescript@5.9.3) + version: 11.16.0(@trpc/server@11.17.0(typescript@5.9.3))(typescript@5.9.3) "@trpc/server": specifier: ^11.0.0 - version: 11.16.0(typescript@5.9.3) + version: 11.17.0(typescript@5.9.3) payload: specifier: ^3.0.0 version: 3.81.0(graphql@16.13.2)(typescript@5.9.3) @@ -873,8 +891,8 @@ importers: specifier: ^2.2.0 version: 2.2.6 vitest: - specifier: ^3.0.0 - version: 3.2.4(@types/debug@4.1.13)(@types/node@25.5.2)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0) + specifier: ^3.2.7 + version: 3.2.7(@types/debug@4.1.13)(@types/node@25.5.2)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0) zod: specifier: ^3.23.0 version: 3.25.76 @@ -887,13 +905,16 @@ importers: version: link:../core-typescript "@sentry/nextjs": specifier: ^10.51.0 - version: 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@16.2.2(@babel/core@7.25.9)(@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) + version: 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@16.2.2(@babel/core@7.25.9)(@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(lightningcss@1.32.0)) "@types/react": specifier: ^19.0.0 version: 19.2.14 "@types/react-dom": specifier: ^19.0.0 version: 19.2.3(@types/react@19.2.14) + "@vitest/coverage-v8": + specifier: ^3.2.7 + version: 3.2.7(vitest@3.2.7(@types/debug@4.1.13)(@types/node@25.5.2)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0)) jsdom: specifier: ^25.0.0 version: 25.0.1 @@ -946,21 +967,24 @@ importers: "@types/react": specifier: ^19.0.0 version: 19.2.14 + "@vitest/coverage-v8": + specifier: ^3.2.7 + version: 3.2.7(vitest@3.2.7(@types/debug@4.1.13)(@types/node@25.5.2)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0)) jsdom: specifier: ^25.0.0 version: 25.0.1 vitest: - specifier: ^3.0.0 - version: 3.2.4(@types/debug@4.1.13)(@types/node@25.5.2)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0) + specifier: ^3.2.7 + version: 3.2.7(@types/debug@4.1.13)(@types/node@25.5.2)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0) packages/core-typescript: devDependencies: "@vitest/coverage-v8": - specifier: ^3.2.4 - version: 3.2.4(vitest@3.2.4(@types/debug@4.1.13)(@types/node@25.5.2)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0)) + specifier: ^3.2.7 + version: 3.2.7(vitest@3.2.7(@types/debug@4.1.13)(@types/node@25.5.2)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0)) vitest: - specifier: ^3.1.0 - version: 3.2.4(@types/debug@4.1.13)(@types/node@25.5.2)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0) + specifier: ^3.2.7 + version: 3.2.7(@types/debug@4.1.13)(@types/node@25.5.2)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0) packages/core-ui: dependencies: @@ -1005,8 +1029,8 @@ importers: specifier: ^19.0.0 version: 19.2.14 "@vitest/coverage-v8": - specifier: ^3.0.0 - version: 3.2.4(vitest@3.2.4(@types/debug@4.1.13)(@types/node@25.5.2)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0)) + specifier: ^3.2.7 + version: 3.2.7(vitest@3.2.7(@types/debug@4.1.13)(@types/node@25.5.2)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0)) jest-axe: specifier: ^10.0.0 version: 10.0.0 @@ -1014,8 +1038,8 @@ importers: specifier: ^25.0.0 version: 25.0.1 vitest: - specifier: ^3.0.0 - version: 3.2.4(@types/debug@4.1.13)(@types/node@25.5.2)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0) + specifier: ^3.2.7 + version: 3.2.7(@types/debug@4.1.13)(@types/node@25.5.2)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0) packages/marketing-pages: dependencies: @@ -1069,11 +1093,11 @@ importers: specifier: ^19.0.0 version: 19.2.14 "@vitest/coverage-v8": - specifier: ^3.2.4 - version: 3.2.4(vitest@3.2.4(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0)) + specifier: ^3.2.7 + version: 3.2.7(vitest@3.2.7(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0)) vitest: - specifier: ^3.1.0 - version: 3.2.4(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0) + specifier: ^3.2.7 + version: 3.2.7(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0) packages/media: dependencies: @@ -1109,11 +1133,11 @@ importers: specifier: ^22.0.0 version: 22.19.17 "@vitest/coverage-v8": - specifier: ^3.2.4 - version: 3.2.4(vitest@3.2.4(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0)) + specifier: ^3.2.7 + version: 3.2.7(vitest@3.2.7(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0)) vitest: - specifier: ^3.1.0 - version: 3.2.4(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0) + specifier: ^3.2.7 + version: 3.2.7(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0) packages/navigation: dependencies: @@ -1164,11 +1188,11 @@ importers: specifier: ^19.0.0 version: 19.2.14 "@vitest/coverage-v8": - specifier: ^3.2.4 - version: 3.2.4(vitest@3.2.4(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0)) + specifier: ^3.2.7 + version: 3.2.7(vitest@3.2.7(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0)) vitest: - specifier: ^3.1.0 - version: 3.2.4(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0) + specifier: ^3.2.7 + version: 3.2.7(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0) turbo/generators: dependencies: @@ -1182,12 +1206,15 @@ importers: "@repo/core-typescript": specifier: workspace:* version: link:../../packages/core-typescript + "@vitest/coverage-v8": + specifier: ^3.2.7 + version: 3.2.7(vitest@3.2.7(@types/debug@4.1.13)(@types/node@25.5.2)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0)) typescript: specifier: ^5.8.0 version: 5.9.3 vitest: - specifier: ^3.1.0 - version: 3.2.4(@types/debug@4.1.13)(@types/node@25.5.2)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0) + specifier: ^3.2.7 + version: 3.2.7(@types/debug@4.1.13)(@types/node@25.5.2)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0) packages: "@adobe/css-tools@4.4.4": @@ -1259,6 +1286,13 @@ packages: } engines: { node: ">=6.9.0" } + "@babel/code-frame@7.29.7": + resolution: + { + integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==, + } + engines: { node: ">=6.9.0" } + "@babel/compat-data@7.29.0": resolution: { @@ -1391,6 +1425,13 @@ packages: } engines: { node: ">=6.9.0" } + "@babel/helper-validator-identifier@7.29.7": + resolution: + { + integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==, + } + engines: { node: ">=6.9.0" } + "@babel/helper-validator-option@7.27.1": resolution: { @@ -1669,6 +1710,13 @@ packages: } engines: { node: ">=6.9.0" } + "@babel/runtime@7.29.7": + resolution: + { + integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==, + } + engines: { node: ">=6.9.0" } + "@babel/template@7.28.6": resolution: { @@ -4588,6 +4636,14 @@ packages: cpu: [arm64] os: [darwin] + "@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.4": + resolution: + { + integrity: sha512-LCkGo6JDfaBhgST7UpPWgNgLINpcpabaHfyz5OBx75nUYxBsaEPxjnyNjWpeb/xBup/682QnBfRBy2/LvPutZQ==, + } + cpu: [arm64] + os: [darwin] + "@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3": resolution: { @@ -4596,6 +4652,14 @@ packages: cpu: [x64] os: [darwin] + "@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.4": + resolution: + { + integrity: sha512-zExlW9zUJKZH/tOtVMttwjKa4Xm/3KcNjnE3dPN92uCktwavMxpgCA3MoJK/DOnTWsQgo224OaST27/mPNAf+w==, + } + cpu: [x64] + os: [darwin] + "@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3": resolution: { @@ -4604,6 +4668,14 @@ packages: cpu: [arm64] os: [linux] + "@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.4": + resolution: + { + integrity: sha512-dgX0P/9wGPJeHFBG+ZmhgE6bmtMt7NP5CRBGyyktpopdk/mW4POnrpQsSLtKI1dwpc+pPLuXHDh6vvskyQE/sw==, + } + cpu: [arm64] + os: [linux] + "@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3": resolution: { @@ -4612,6 +4684,14 @@ packages: cpu: [arm] os: [linux] + "@msgpackr-extract/msgpackr-extract-linux-arm@3.0.4": + resolution: + { + integrity: sha512-Tg3yX65f5GbtXLkrYEHE5oibZG9epyYWas7FogTTEJeDEF9JlXJzKgXaNhT3UXlTOeA+AfZpYZYZ0uPj7Cfquw==, + } + cpu: [arm] + os: [linux] + "@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3": resolution: { @@ -4620,6 +4700,14 @@ packages: cpu: [x64] os: [linux] + "@msgpackr-extract/msgpackr-extract-linux-x64@3.0.4": + resolution: + { + integrity: sha512-8TNXMEjJc3QEy7R/x1INhgiU+XakDAFUzBhaz7+Rbrs8NH5UQeHQxxmzsSBJGyV6I1jW79undiQm8tOI+D+8FQ==, + } + cpu: [x64] + os: [linux] + "@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3": resolution: { @@ -4628,6 +4716,14 @@ packages: cpu: [x64] os: [win32] + "@msgpackr-extract/msgpackr-extract-win32-x64@3.0.4": + resolution: + { + integrity: sha512-CmCXPQrkbwExx3j946/PtHWHbYJiCRBRDl4BlkRQcJB/YOwQxJRTpoo7aTsortjgoJ1x7opzTSxn7C+ASSLVjQ==, + } + cpu: [x64] + os: [win32] + "@next/env@15.5.14": resolution: { @@ -8037,6 +8133,12 @@ packages: integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==, } + "@types/estree@1.0.9": + resolution: + { + integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==, + } + "@types/graceful-fs@4.1.9": resolution: { @@ -8133,6 +8235,12 @@ packages: integrity: sha512-wGdMcf+vPYM6jikpS/qhg6WiqSV/OhG+jeeHT/KlVqxYfD40iYJf9/AE1uQxVWFvU7MipKRkRv8NSHiCGgPr8Q==, } + "@types/node@22.20.1": + resolution: + { + integrity: sha512-EANqOCF9QFyra+4pfxUcX9STKJpCLjMbObVzljIJomAWSnuSIEAvyzEU53GaajbXJEgdh0iEcPL+DGvpUd4k1Q==, + } + "@types/node@25.5.2": resolution: { @@ -8406,28 +8514,28 @@ packages: peerDependencies: vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 - "@vitest/coverage-v8@3.2.4": + "@vitest/coverage-v8@3.2.7": resolution: { - integrity: sha512-EyF9SXU6kS5Ku/U82E259WSnvg6c8KTjppUncuNdm5QHpe17mwREHnjDzozC8x9MZ0xfBUFSaLkRv4TMA75ALQ==, + integrity: sha512-NEGWJS2XNu2PfRLQwOO3CTKj1tTETxNBdk454vDxVBhxJYhPaA/eS0nAI0c+1El1P7a60z8+i+ZrQoGESweGKg==, } peerDependencies: - "@vitest/browser": 3.2.4 - vitest: 3.2.4 + "@vitest/browser": 3.2.7 + vitest: 3.2.7 peerDependenciesMeta: "@vitest/browser": optional: true - "@vitest/expect@3.2.4": + "@vitest/expect@3.2.7": resolution: { - integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==, + integrity: sha512-E8eBXaKibuvH2pSZErOjdVb5vF4PbKYcrnluBTYxEk1l/VhhwZg1kZQsdtjq+CsF5CFydf2Rdkz7jDHKSisi3w==, } - "@vitest/mocker@3.2.4": + "@vitest/mocker@3.2.7": resolution: { - integrity: sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==, + integrity: sha512-Trr0hYO9CM3Wj6ksWHRhK9IZpIY6wTMO5u/MqXurMxT57sWBaOPEtP3Oq60ihZuh5JsiagKfz95OcxdEP6dBrA==, } peerDependencies: msw: ^2.4.9 @@ -8438,34 +8546,34 @@ packages: vite: optional: true - "@vitest/pretty-format@3.2.4": + "@vitest/pretty-format@3.2.7": resolution: { - integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==, + integrity: sha512-KUHlwqVu0sRlhCdyPdQ/wBoTfRahjUky1MubOmYw9fWfIZy1gNoHpuaaQBPAaMaVYdQYHJLurzj8ECCj5OwTqA==, } - "@vitest/runner@3.2.4": + "@vitest/runner@3.2.7": resolution: { - integrity: sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==, + integrity: sha512-sB9y4ovltoQP+WaUPwmSxO9WIg9Ig694Di5PalVPsYHklAdE027mehpWF2SQSVq+k6sFgaivbTjTJwZLSHbedA==, } - "@vitest/snapshot@3.2.4": + "@vitest/snapshot@3.2.7": resolution: { - integrity: sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==, + integrity: sha512-7C+MwShwtBSI5Buwoyg3s/iY1eHL9PKAf+O1wVh/TdnjXUtkoL/9YQtre90i4MtNXM6edP1wJ2zOBpfCyhIS7g==, } - "@vitest/spy@3.2.4": + "@vitest/spy@3.2.7": resolution: { - integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==, + integrity: sha512-Q2eQGI6d2L/hBtZ0qNuKcAGid68XK6cv1xsoaIma6PaJhHPoqcEJhYpXZ/5myCMqkNgtP6UKuBhbc0nHKnrkuQ==, } - "@vitest/utils@3.2.4": + "@vitest/utils@3.2.7": resolution: { - integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==, + integrity: sha512-x6BDOd7dyo3PFLY3I9/HJ25X/6OurhGXk2/B9gOZNPF7XDVjeBK4k01lQE5uvDpbuheErh91qYuE1E2OEjK3Rw==, } "@webassemblyjs/ast@1.14.1": @@ -8617,6 +8725,14 @@ packages: engines: { node: ">=0.4.0" } hasBin: true + acorn@8.17.0: + resolution: + { + integrity: sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==, + } + engines: { node: ">=0.4.0" } + hasBin: true + agent-base@6.0.2: resolution: { @@ -8675,6 +8791,12 @@ packages: integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==, } + ajv@8.20.0: + resolution: + { + integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==, + } + angular-html-parser@6.0.2: resolution: { @@ -9039,6 +9161,14 @@ packages: engines: { node: ">=6.0.0" } hasBin: true + baseline-browser-mapping@2.10.42: + resolution: + { + integrity: sha512-c/jurFrDLyui7o1J86yLkRu4LMsTYcBohveus7/I2Hzdn9KIP2bdJPTue/lR1KH46enoPbD77GKeSYNdyPoD3Q==, + } + engines: { node: ">=6.0.0" } + hasBin: true + basic-auth@2.0.1: resolution: { @@ -9125,6 +9255,14 @@ packages: engines: { node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7 } hasBin: true + browserslist@4.28.5: + resolution: + { + integrity: sha512-Cu2E6QejHWzuDMTkuwgpABFgDfZrXLQq5V13YOACZx4mFAG4IwGTbTfHPMr4WtxlHoXSM8FIuRwYYCz5XiabaQ==, + } + engines: { node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7 } + hasBin: true + bser@2.1.1: resolution: { @@ -9250,6 +9388,12 @@ packages: integrity: sha512-4oxTZEvqmLLrERwxO76yfKM7acZo310U+v4kqexI2TL1DkkUEMT8UijrxxcnVdxR3qkVf5awGRX+4Z6aPHVKrA==, } + caniuse-lite@1.0.30001803: + resolution: + { + integrity: sha512-g/uHREV2ZpK9qMalCsWaxmA6ol+DX8GYhuf3T40RKoP+oL7vhRJh8LNt73PCjpnR6l14FzfPrB5Yux4PKm2meg==, + } + ccount@2.0.1: resolution: { @@ -10384,6 +10528,12 @@ packages: integrity: sha512-IbxXrsTlD3hRodkLnbxAPP4OuJYdWCeM3IOdT+CpcMoIwIoDfCmRpEtSPfwBXxVkg9xmBeY7Lz2Eo2TDn/HC3Q==, } + electron-to-chromium@1.5.389: + resolution: + { + integrity: sha512-cEto7aeOqBfU1D+c5py5pE+ooscKE75JifxLBdFUZsqAxRS6y7kebtxAZvICszSl05gPjYHDTjY+lXpyGvpJbg==, + } + emittery@0.13.1: resolution: { @@ -10448,6 +10598,13 @@ packages: } engines: { node: ">=10.13.0" } + enhanced-resolve@5.24.2: + resolution: + { + integrity: sha512-rpsZEGT1jFuve6QlpyRp9ckQ+kN61hvF9BzCPyMdaKTm8UJce96KBn3sorXOFXlzjPrs3Vc4T1NsSroZ3PxlFw==, + } + engines: { node: ">=10.13.0" } + entities@1.1.2: resolution: { @@ -10520,10 +10677,10 @@ packages: integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==, } - es-module-lexer@2.1.0: + es-module-lexer@2.3.0: resolution: { - integrity: sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==, + integrity: sha512-KLdwQm2NvGLDkQDCGvmiQrhkd0JbMzXthwQAUgWjQuQdBLFa3eiBP5arXZyA+f8x+x7OXgud6bq2rxjGtHV2tw==, } es-object-atoms@1.1.1: @@ -11014,6 +11171,12 @@ packages: integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==, } + fast-uri@3.1.3: + resolution: + { + integrity: sha512-i70LwGWUduXqzicKXWshooq+sWL1K3WUU5rKZNG/0i3a1OSoX3HqhH5WbWwTmqWfor4urUakGPiRQcleRZTwOg==, + } + fast-wrap-ansi@0.2.0: resolution: { @@ -13575,12 +13738,25 @@ packages: } hasBin: true + msgpackr-extract@3.0.4: + resolution: + { + integrity: sha512-4kmO/MdyUIkLIvTPr8VHLil4AtoKIoniWPIEk5+CDy0xnWC84azhSFmuJ7PxZdsYtiP5kEeQsORAVIeMgxT+Hw==, + } + hasBin: true + msgpackr@1.11.12: resolution: { integrity: sha512-RBdJ1Un7yGlXWajrkxcSa93nvQ0w4zBf60c0yYv7YtBelP8H2FA7XsfBbMHtXKXUMUxH7zV3Zuozh+kUQWhHvg==, } + msgpackr@1.12.1: + resolution: + { + integrity: sha512-4EUH9tQHnMmEgzW/MdAP0KIfa1T9AF+htl0ffe2n5vb2EKn9y2co8ccpgWko6S52Jy1PQZKwRnx5/KkYjtd9MQ==, + } + multipasta@0.2.7: resolution: { @@ -13778,6 +13954,13 @@ packages: integrity: sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg==, } + node-releases@2.0.51: + resolution: + { + integrity: sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==, + } + engines: { node: ">=18" } + nopt@8.1.0: resolution: { @@ -14716,10 +14899,10 @@ packages: integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==, } - react-is@19.2.6: + react-is@19.2.7: resolution: { - integrity: sha512-XjBR15BhXuylgWGuslhDKqlSayuqvqBX91BP8pauG8kd1zY8kotkNWbXksTCNRarse4kuGbe2kIY05ARtwNIvw==, + integrity: sha512-kZFnouyVv7eP/Phmrlo9FK+zcAdriZJvzxXHF1Sl1P377WSGe2G/JxVolhTrB/jeV47lKImhNUsijjHAAbcl/A==, } react-refresh@0.17.0: @@ -15470,10 +15653,10 @@ packages: integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==, } - srvx@0.11.15: + srvx@0.11.21: resolution: { - integrity: sha512-iXsux0UcOjdvs0LCMa2Ws3WwcDUozA3JN3BquNXkaFPP7TpRqgunKdEgoZ/uwb1J6xaYHfxtz9Twlh6yzwM6Tg==, + integrity: sha512-GWTHjKMeekX8CwJf4VU9Oo6mJpSGaflGMddbCvR+Cmmh9sslRMiGbAoqqZacE0r1ncARh6buCEETr2W52F8b1w==, } engines: { node: ">=20.16.0" } hasBin: true @@ -15852,22 +16035,49 @@ packages: integrity: sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==, } - terser-webpack-plugin@5.5.0: + terser-webpack-plugin@5.6.1: resolution: { - integrity: sha512-UYhptBwhWvfIjKd/UuFo6D8uq9xpGLDK+z8EDsj/zWhrTaH34cKEbrkMKfV5YWqGBvAYA3tlzZbs2R+qYrbQJA==, + integrity: sha512-201R5j+sJpK8nFWwKVyNfZot8FaJbLZDq5evriVzbV1wDtSXDjRUDRfJzHpAaxFDMEhsZL1QkeqM61wgsS3KaQ==, } engines: { node: ">= 10.13.0" } peerDependencies: + "@minify-html/node": "*" "@swc/core": "*" + "@swc/css": "*" + "@swc/html": "*" + clean-css: "*" + cssnano: "*" + csso: "*" esbuild: "*" + html-minifier-terser: "*" + lightningcss: "*" + postcss: "*" uglify-js: "*" webpack: ^5.1.0 peerDependenciesMeta: + "@minify-html/node": + optional: true "@swc/core": optional: true + "@swc/css": + optional: true + "@swc/html": + optional: true + clean-css: + optional: true + cssnano: + optional: true + csso: + optional: true esbuild: optional: true + html-minifier-terser: + optional: true + lightningcss: + optional: true + postcss: + optional: true uglify-js: optional: true @@ -15879,6 +16089,14 @@ packages: engines: { node: ">=10" } hasBin: true + terser@5.49.0: + resolution: + { + integrity: sha512-SNiDnXyHSrxVcIOtVbULzcTmniUiwcV7Nwdyj1twVubeTmbjoa8p69KKDpfkdoOavuM4/GRm1+ykI8qqnavHoA==, + } + engines: { node: ">=10" } + hasBin: true + test-exclude@6.0.0: resolution: { @@ -16677,10 +16895,10 @@ packages: vite: optional: true - vitest@3.2.4: + vitest@3.2.7: resolution: { - integrity: sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==, + integrity: sha512-KrxIJ62Fd89gfysR4WotlgZABiz2dqFPgqGzX7s+CwsqLFomRH7777ZcrOD6+WVAh7khPQP41A+BKbpcJFrdEg==, } engines: { node: ^18.0.0 || ^20.0.0 || >=22.0.0 } hasBin: true @@ -16688,8 +16906,8 @@ packages: "@edge-runtime/vm": "*" "@types/debug": ^4.1.12 "@types/node": ^18.0.0 || ^20.0.0 || >=22.0.0 - "@vitest/browser": 3.2.4 - "@vitest/ui": 3.2.4 + "@vitest/browser": 3.2.7 + "@vitest/ui": 3.2.7 happy-dom: "*" jsdom: "*" peerDependenciesMeta: @@ -16745,10 +16963,10 @@ packages: integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==, } - watchpack@2.5.1: + watchpack@2.5.2: resolution: { - integrity: sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==, + integrity: sha512-6i/00NBjP4yGPs+caKSyRfpTF/8Torsu0MOW3mMzIbhgISFder8i7xbqgHlLMwJrdiN8ndBV3UA1/AfzPSr+jg==, } engines: { node: ">=10.13.0" } @@ -16771,10 +16989,10 @@ packages: } engines: { node: ">=12" } - webpack-sources@3.4.1: + webpack-sources@3.5.1: resolution: { - integrity: sha512-eACpxRN02yaawnt+uUNIF7Qje6A9zArxBbcAJjK1PK3S9Ycg5jIuJ8pW4q8EMnwNZCEGltcjkRx1QzOxOkKD8A==, + integrity: sha512-jyuiGJdtvY434z5bUZrjz67v76/ePNvFZTp9Mdz29IlH4+GPsgyGjiv0fKI+M7BdkU6ADjulUcKAd3tUK3WlEw==, } engines: { node: ">=10.13.0" } @@ -17215,6 +17433,12 @@ snapshots: js-tokens: 4.0.0 picocolors: 1.1.1 + "@babel/code-frame@7.29.7": + dependencies: + "@babel/helper-validator-identifier": 7.29.7 + js-tokens: 4.0.0 + picocolors: 1.1.1 + "@babel/compat-data@7.29.0": {} "@babel/core@7.25.9": @@ -17379,6 +17603,8 @@ snapshots: "@babel/helper-validator-identifier@7.28.5": {} + "@babel/helper-validator-identifier@7.29.7": {} + "@babel/helper-validator-option@7.27.1": {} "@babel/helpers@7.29.2": @@ -17595,6 +17821,8 @@ snapshots: "@babel/runtime@7.29.2": {} + "@babel/runtime@7.29.7": {} + "@babel/template@7.28.6": dependencies: "@babel/code-frame": 7.29.0 @@ -17779,7 +18007,7 @@ snapshots: dependencies: "@effect/platform": 0.95.0(effect@3.21.2) effect: 3.21.2 - msgpackr: 1.11.12 + msgpackr: 1.12.1 "@effect/sql@0.50.0(@effect/experimental@0.59.0(@effect/platform@0.95.0(effect@3.21.2))(effect@3.21.2)(ioredis@5.10.1))(@effect/platform@0.95.0(effect@3.21.2))(effect@3.21.2)": dependencies: @@ -19177,12 +19405,12 @@ snapshots: "@types/yargs": 17.0.35 chalk: 4.1.2 - "@joshwooding/vite-plugin-react-docgen-typescript@0.5.0(typescript@5.9.3)(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0))": + "@joshwooding/vite-plugin-react-docgen-typescript@0.5.0(typescript@5.9.3)(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0))": dependencies: glob: 10.5.0 magic-string: 0.27.0 react-docgen-typescript: 2.4.0(typescript@5.9.3) - vite: 6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0) + vite: 6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0) optionalDependencies: typescript: 5.9.3 @@ -19414,21 +19642,39 @@ snapshots: "@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3": optional: true + "@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.4": + optional: true + "@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3": optional: true + "@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.4": + optional: true + "@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3": optional: true + "@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.4": + optional: true + "@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3": optional: true + "@msgpackr-extract/msgpackr-extract-linux-arm@3.0.4": + optional: true + "@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3": optional: true + "@msgpackr-extract/msgpackr-extract-linux-x64@3.0.4": + optional: true + "@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3": optional: true + "@msgpackr-extract/msgpackr-extract-win32-x64@3.0.4": + optional: true + "@next/env@15.5.14": {} "@next/env@16.2.2": {} @@ -20806,7 +21052,7 @@ snapshots: "@sentry/core@10.52.0": {} - "@sentry/nextjs@10.51.0(@opentelemetry/core@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.1))(next@15.5.14(@babel/core@7.25.9)(@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)": + "@sentry/nextjs@10.51.0(@opentelemetry/core@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.1))(next@15.5.14(@babel/core@7.25.9)(@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(lightningcss@1.32.0))": dependencies: "@opentelemetry/api": 1.9.1 "@opentelemetry/semantic-conventions": 1.40.0 @@ -20818,7 +21064,7 @@ snapshots: "@sentry/opentelemetry": 10.51.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.40.0) "@sentry/react": 10.51.0(react@19.2.4) "@sentry/vercel-edge": 10.51.0 - "@sentry/webpack-plugin": 5.2.1(webpack@5.106.2) + "@sentry/webpack-plugin": 5.2.1(webpack@5.106.2(lightningcss@1.32.0)) next: 15.5.14(@babel/core@7.25.9)(@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) rollup: 4.60.1 stacktrace-parser: 0.1.11 @@ -20831,7 +21077,7 @@ snapshots: - supports-color - webpack - "@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(@babel/core@7.25.9)(@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)": + "@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(@babel/core@7.25.9)(@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(lightningcss@1.32.0))": dependencies: "@opentelemetry/api": 1.9.1 "@opentelemetry/semantic-conventions": 1.40.0 @@ -20843,7 +21089,7 @@ snapshots: "@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) "@sentry/react": 10.51.0(react@19.2.4) "@sentry/vercel-edge": 10.51.0 - "@sentry/webpack-plugin": 5.2.1(webpack@5.106.2) + "@sentry/webpack-plugin": 5.2.1(webpack@5.106.2(lightningcss@1.32.0)) next: 15.5.14(@babel/core@7.25.9)(@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) rollup: 4.60.1 stacktrace-parser: 0.1.11 @@ -20856,7 +21102,7 @@ snapshots: - supports-color - webpack - "@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@16.2.2(@babel/core@7.25.9)(@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)": + "@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@16.2.2(@babel/core@7.25.9)(@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(lightningcss@1.32.0))": dependencies: "@opentelemetry/api": 1.9.1 "@opentelemetry/semantic-conventions": 1.40.0 @@ -20868,7 +21114,7 @@ snapshots: "@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) "@sentry/react": 10.51.0(react@19.2.4) "@sentry/vercel-edge": 10.51.0 - "@sentry/webpack-plugin": 5.2.1(webpack@5.106.2) + "@sentry/webpack-plugin": 5.2.1(webpack@5.106.2(lightningcss@1.32.0)) next: 16.2.2(@babel/core@7.25.9)(@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) rollup: 4.60.1 stacktrace-parser: 0.1.11 @@ -21046,10 +21292,10 @@ snapshots: - rollup - supports-color - "@sentry/webpack-plugin@5.2.1(webpack@5.106.2)": + "@sentry/webpack-plugin@5.2.1(webpack@5.106.2(lightningcss@1.32.0))": dependencies: "@sentry/bundler-plugin-core": 5.2.1 - webpack: 5.106.2 + webpack: 5.106.2(lightningcss@1.32.0) transitivePeerDependencies: - encoding - supports-color @@ -21169,13 +21415,13 @@ snapshots: react: 19.2.4 react-dom: 19.2.4(react@19.2.4) - "@storybook/builder-vite@8.6.18(storybook@8.6.18(prettier@3.8.1))(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0))": + "@storybook/builder-vite@8.6.18(storybook@8.6.18(prettier@3.8.1))(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0))": dependencies: "@storybook/csf-plugin": 8.6.18(storybook@8.6.18(prettier@3.8.1)) browser-assert: 1.2.1 storybook: 8.6.18(prettier@3.8.1) ts-dedent: 2.2.0 - vite: 6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0) + vite: 6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0) "@storybook/components@8.6.18(storybook@8.6.18(prettier@3.8.1))": dependencies: @@ -21251,11 +21497,11 @@ snapshots: react-dom: 19.2.4(react@19.2.4) storybook: 8.6.18(prettier@3.8.1) - "@storybook/react-vite@8.6.18(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(rollup@4.60.4)(storybook@8.6.18(prettier@3.8.1))(typescript@5.9.3)(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0))": + "@storybook/react-vite@8.6.18(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(rollup@4.60.4)(storybook@8.6.18(prettier@3.8.1))(typescript@5.9.3)(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0))": dependencies: - "@joshwooding/vite-plugin-react-docgen-typescript": 0.5.0(typescript@5.9.3)(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0)) + "@joshwooding/vite-plugin-react-docgen-typescript": 0.5.0(typescript@5.9.3)(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0)) "@rollup/pluginutils": 5.3.0(rollup@4.60.4) - "@storybook/builder-vite": 8.6.18(storybook@8.6.18(prettier@3.8.1))(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0)) + "@storybook/builder-vite": 8.6.18(storybook@8.6.18(prettier@3.8.1))(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0)) "@storybook/react": 8.6.18(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@8.6.18(prettier@3.8.1))(typescript@5.9.3) find-up: 5.0.0 magic-string: 0.30.21 @@ -21265,7 +21511,7 @@ snapshots: resolve: 1.22.11 storybook: 8.6.18(prettier@3.8.1) tsconfig-paths: 4.2.0 - vite: 6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0) + vite: 6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0) transitivePeerDependencies: - rollup - supports-color @@ -21378,13 +21624,13 @@ snapshots: "@stryker-mutator/util@8.7.1": {} - "@stryker-mutator/vitest-runner@8.7.1(@stryker-mutator/core@8.7.1)(vitest@3.2.4(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0))": + "@stryker-mutator/vitest-runner@8.7.1(@stryker-mutator/core@8.7.1)(vitest@3.2.7(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0))": dependencies: "@stryker-mutator/api": 8.7.1 "@stryker-mutator/core": 8.7.1 "@stryker-mutator/util": 8.7.1 tslib: 2.7.0 - vitest: 3.2.4(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0) + vitest: 3.2.7(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0) "@swc/core-darwin-arm64@1.15.33": optional: true @@ -21587,14 +21833,14 @@ snapshots: postcss: 8.5.15 tailwindcss: 4.3.0 - "@tailwindcss/vite@4.2.2(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0))": + "@tailwindcss/vite@4.2.2(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0))": dependencies: "@tailwindcss/node": 4.2.2 "@tailwindcss/oxide": 4.2.2 tailwindcss: 4.2.2 - vite: 6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0) + vite: 6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0) - "@tanstack/directive-functions-plugin@1.131.2(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0))": + "@tanstack/directive-functions-plugin@1.131.2(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0))": dependencies: "@babel/code-frame": 7.27.1 "@babel/core": 7.29.0 @@ -21603,11 +21849,11 @@ snapshots: "@tanstack/router-utils": 1.131.2 babel-dead-code-elimination: 1.0.12 tiny-invariant: 1.3.3 - vite: 6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0) + vite: 6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0) transitivePeerDependencies: - supports-color - "@tanstack/directive-functions-plugin@1.142.1(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0))": + "@tanstack/directive-functions-plugin@1.142.1(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0))": dependencies: "@babel/code-frame": 7.27.1 "@babel/core": 7.29.0 @@ -21617,7 +21863,7 @@ snapshots: babel-dead-code-elimination: 1.0.12 pathe: 2.0.3 tiny-invariant: 1.3.3 - vite: 6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0) + vite: 6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0) transitivePeerDependencies: - supports-color @@ -21660,12 +21906,12 @@ snapshots: react: 19.2.4 react-dom: 19.2.4(react@19.2.4) - "@tanstack/react-start-plugin@1.131.50(@tanstack/react-router@1.168.10(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@vitejs/plugin-react@4.7.0(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0)))(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3))(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0))(webpack@5.106.2)": + "@tanstack/react-start-plugin@1.131.50(@tanstack/react-router@1.168.10(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@vitejs/plugin-react@4.7.0(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0)))(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3))(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0))(webpack@5.106.2(lightningcss@1.32.0))": dependencies: - "@tanstack/start-plugin-core": 1.131.50(@tanstack/react-router@1.168.10(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3))(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0))(webpack@5.106.2) - "@vitejs/plugin-react": 4.7.0(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0)) + "@tanstack/start-plugin-core": 1.131.50(@tanstack/react-router@1.168.10(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3))(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0))(webpack@5.106.2(lightningcss@1.32.0)) + "@vitejs/plugin-react": 4.7.0(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0)) pathe: 2.0.3 - vite: 6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0) + vite: 6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0) zod: 3.25.76 transitivePeerDependencies: - "@azure/app-configuration" @@ -21704,11 +21950,11 @@ snapshots: - webpack - xml2js - "@tanstack/react-start-router-manifest@1.120.19(@types/node@22.19.17)(db0@0.3.4(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3)))(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3))(ioredis@5.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0)": + "@tanstack/react-start-router-manifest@1.120.19(@types/node@22.19.17)(db0@0.3.4(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3)))(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3))(ioredis@5.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0)": dependencies: "@tanstack/router-core": 1.168.9 tiny-invariant: 1.3.3 - vinxi: 0.5.3(@types/node@22.19.17)(db0@0.3.4(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3)))(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3))(ioredis@5.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0) + vinxi: 0.5.3(@types/node@22.19.17)(db0@0.3.4(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3)))(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3))(ioredis@5.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0) transitivePeerDependencies: - "@azure/app-configuration" - "@azure/cosmos" @@ -21825,7 +22071,7 @@ snapshots: transitivePeerDependencies: - supports-color - "@tanstack/router-plugin@1.131.50(@tanstack/react-router@1.168.10(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0))(webpack@5.106.2)": + "@tanstack/router-plugin@1.131.50(@tanstack/react-router@1.168.10(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0))(webpack@5.106.2(lightningcss@1.32.0))": dependencies: "@babel/core": 7.29.0 "@babel/plugin-syntax-jsx": 7.28.6(@babel/core@7.29.0) @@ -21843,12 +22089,12 @@ snapshots: zod: 3.25.76 optionalDependencies: "@tanstack/react-router": 1.168.10(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - vite: 6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0) - webpack: 5.106.2 + vite: 6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0) + webpack: 5.106.2(lightningcss@1.32.0) transitivePeerDependencies: - supports-color - "@tanstack/router-plugin@1.168.7(@tanstack/react-router@1.168.10(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0))(webpack@5.106.2)": + "@tanstack/router-plugin@1.168.7(@tanstack/react-router@1.168.10(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0))(webpack@5.106.2(lightningcss@1.32.0))": dependencies: "@babel/core": 7.29.0 "@babel/plugin-syntax-jsx": 7.28.6(@babel/core@7.29.0) @@ -21865,8 +22111,8 @@ snapshots: zod: 4.4.3 optionalDependencies: "@tanstack/react-router": 1.168.10(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - vite: 6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0) - webpack: 5.106.2 + vite: 6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0) + webpack: 5.106.2(lightningcss@1.32.0) transitivePeerDependencies: - supports-color @@ -21908,7 +22154,7 @@ snapshots: transitivePeerDependencies: - supports-color - "@tanstack/server-functions-plugin@1.131.2(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0))": + "@tanstack/server-functions-plugin@1.131.2(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0))": dependencies: "@babel/code-frame": 7.27.1 "@babel/core": 7.29.0 @@ -21917,14 +22163,14 @@ snapshots: "@babel/template": 7.28.6 "@babel/traverse": 7.29.0 "@babel/types": 7.29.0 - "@tanstack/directive-functions-plugin": 1.131.2(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0)) + "@tanstack/directive-functions-plugin": 1.131.2(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0)) babel-dead-code-elimination: 1.0.12 tiny-invariant: 1.3.3 transitivePeerDependencies: - supports-color - vite - "@tanstack/server-functions-plugin@1.142.1(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0))": + "@tanstack/server-functions-plugin@1.142.1(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0))": dependencies: "@babel/code-frame": 7.27.1 "@babel/core": 7.29.0 @@ -21933,18 +22179,18 @@ snapshots: "@babel/template": 7.28.6 "@babel/traverse": 7.29.0 "@babel/types": 7.29.0 - "@tanstack/directive-functions-plugin": 1.142.1(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0)) + "@tanstack/directive-functions-plugin": 1.142.1(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0)) babel-dead-code-elimination: 1.0.12 tiny-invariant: 1.3.3 transitivePeerDependencies: - supports-color - vite - "@tanstack/start-api-routes@1.120.19(@types/node@22.19.17)(db0@0.3.4(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3)))(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3))(ioredis@5.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0)": + "@tanstack/start-api-routes@1.120.19(@types/node@22.19.17)(db0@0.3.4(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3)))(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3))(ioredis@5.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0)": dependencies: "@tanstack/router-core": 1.168.9 "@tanstack/start-server-core": 1.169.0 - vinxi: 0.5.3(@types/node@22.19.17)(db0@0.3.4(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3)))(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3))(ioredis@5.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0) + vinxi: 0.5.3(@types/node@22.19.17)(db0@0.3.4(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3)))(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3))(ioredis@5.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0) transitivePeerDependencies: - "@azure/app-configuration" - "@azure/cosmos" @@ -22008,22 +22254,22 @@ snapshots: "@tanstack/start-storage-context": 1.167.5 seroval: 1.5.4 - "@tanstack/start-config@1.120.20(@types/node@22.19.17)(db0@0.3.4(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3)))(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3))(ioredis@5.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0))(webpack@5.106.2)(yaml@2.9.0)": + "@tanstack/start-config@1.120.20(@types/node@22.19.17)(db0@0.3.4(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3)))(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3))(ioredis@5.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0))(webpack@5.106.2(lightningcss@1.32.0))(yaml@2.9.0)": dependencies: "@tanstack/react-router": 1.168.10(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - "@tanstack/react-start-plugin": 1.131.50(@tanstack/react-router@1.168.10(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@vitejs/plugin-react@4.7.0(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0)))(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3))(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0))(webpack@5.106.2) + "@tanstack/react-start-plugin": 1.131.50(@tanstack/react-router@1.168.10(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@vitejs/plugin-react@4.7.0(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0)))(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3))(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0))(webpack@5.106.2(lightningcss@1.32.0)) "@tanstack/router-generator": 1.167.6 - "@tanstack/router-plugin": 1.168.7(@tanstack/react-router@1.168.10(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0))(webpack@5.106.2) - "@tanstack/server-functions-plugin": 1.142.1(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0)) + "@tanstack/router-plugin": 1.168.7(@tanstack/react-router@1.168.10(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0))(webpack@5.106.2(lightningcss@1.32.0)) + "@tanstack/server-functions-plugin": 1.142.1(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0)) "@tanstack/start-server-functions-handler": 1.120.19 - "@vitejs/plugin-react": 4.7.0(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0)) + "@vitejs/plugin-react": 4.7.0(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0)) import-meta-resolve: 4.2.0 nitropack: 2.13.4(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3)) ofetch: 1.5.1 react: 19.2.4 react-dom: 19.2.4(react@19.2.4) - vinxi: 0.5.3(@types/node@22.19.17)(db0@0.3.4(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3)))(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3))(ioredis@5.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0) - vite: 6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0) + vinxi: 0.5.3(@types/node@22.19.17)(db0@0.3.4(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3)))(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3))(ioredis@5.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0) + vite: 6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0) zod: 3.25.76 transitivePeerDependencies: - "@azure/app-configuration" @@ -22078,16 +22324,16 @@ snapshots: "@tanstack/start-fn-stubs@1.162.0": {} - "@tanstack/start-plugin-core@1.131.50(@tanstack/react-router@1.168.10(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3))(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0))(webpack@5.106.2)": + "@tanstack/start-plugin-core@1.131.50(@tanstack/react-router@1.168.10(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3))(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0))(webpack@5.106.2(lightningcss@1.32.0))": dependencies: "@babel/code-frame": 7.26.2 "@babel/core": 7.29.0 "@babel/types": 7.29.0 "@tanstack/router-core": 1.131.50 "@tanstack/router-generator": 1.131.50 - "@tanstack/router-plugin": 1.131.50(@tanstack/react-router@1.168.10(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0))(webpack@5.106.2) + "@tanstack/router-plugin": 1.131.50(@tanstack/react-router@1.168.10(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0))(webpack@5.106.2(lightningcss@1.32.0)) "@tanstack/router-utils": 1.131.2 - "@tanstack/server-functions-plugin": 1.131.2(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0)) + "@tanstack/server-functions-plugin": 1.131.2(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0)) "@tanstack/start-server-core": 1.131.50 "@types/babel__code-frame": 7.27.0 "@types/babel__core": 7.20.5 @@ -22097,8 +22343,8 @@ snapshots: nitropack: 2.13.4(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3)) pathe: 2.0.3 ufo: 1.6.4 - vite: 6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0) - vitefu: 1.1.3(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0)) + vite: 6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0) + vitefu: 1.1.3(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0)) xmlbuilder2: 3.1.1 zod: 3.25.76 transitivePeerDependencies: @@ -22162,9 +22408,9 @@ snapshots: transitivePeerDependencies: - crossws - "@tanstack/start-server-functions-client@1.131.50(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0))": + "@tanstack/start-server-functions-client@1.131.50(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0))": dependencies: - "@tanstack/server-functions-plugin": 1.131.2(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0)) + "@tanstack/server-functions-plugin": 1.131.2(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0)) "@tanstack/start-server-functions-fetcher": 1.131.50 transitivePeerDependencies: - supports-color @@ -22184,17 +22430,17 @@ snapshots: transitivePeerDependencies: - crossws - "@tanstack/start-server-functions-server@1.131.2(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0))": + "@tanstack/start-server-functions-server@1.131.2(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0))": dependencies: - "@tanstack/server-functions-plugin": 1.131.2(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0)) + "@tanstack/server-functions-plugin": 1.131.2(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0)) tiny-invariant: 1.3.3 transitivePeerDependencies: - supports-color - vite - "@tanstack/start-server-functions-ssr@1.120.19(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0))": + "@tanstack/start-server-functions-ssr@1.120.19(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0))": dependencies: - "@tanstack/server-functions-plugin": 1.142.1(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0)) + "@tanstack/server-functions-plugin": 1.142.1(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0)) "@tanstack/start-client-core": 1.170.0 "@tanstack/start-server-core": 1.169.0 "@tanstack/start-server-functions-fetcher": 1.131.50 @@ -22212,17 +22458,17 @@ snapshots: dependencies: "@tanstack/router-core": 1.171.3 - "@tanstack/start@1.120.20(@types/node@22.19.17)(db0@0.3.4(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3)))(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3))(ioredis@5.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0))(webpack@5.106.2)(yaml@2.9.0)": + "@tanstack/start@1.120.20(@types/node@22.19.17)(db0@0.3.4(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3)))(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3))(ioredis@5.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0))(webpack@5.106.2(lightningcss@1.32.0))(yaml@2.9.0)": dependencies: "@tanstack/react-start-client": 1.168.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - "@tanstack/react-start-router-manifest": 1.120.19(@types/node@22.19.17)(db0@0.3.4(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3)))(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3))(ioredis@5.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0) + "@tanstack/react-start-router-manifest": 1.120.19(@types/node@22.19.17)(db0@0.3.4(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3)))(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3))(ioredis@5.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0) "@tanstack/react-start-server": 1.167.5(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - "@tanstack/start-api-routes": 1.120.19(@types/node@22.19.17)(db0@0.3.4(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3)))(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3))(ioredis@5.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0) - "@tanstack/start-config": 1.120.20(@types/node@22.19.17)(db0@0.3.4(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3)))(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3))(ioredis@5.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0))(webpack@5.106.2)(yaml@2.9.0) - "@tanstack/start-server-functions-client": 1.131.50(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0)) + "@tanstack/start-api-routes": 1.120.19(@types/node@22.19.17)(db0@0.3.4(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3)))(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3))(ioredis@5.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0) + "@tanstack/start-config": 1.120.20(@types/node@22.19.17)(db0@0.3.4(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3)))(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3))(ioredis@5.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0))(webpack@5.106.2(lightningcss@1.32.0))(yaml@2.9.0) + "@tanstack/start-server-functions-client": 1.131.50(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0)) "@tanstack/start-server-functions-handler": 1.120.19 - "@tanstack/start-server-functions-server": 1.131.2(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0)) - "@tanstack/start-server-functions-ssr": 1.120.19(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0)) + "@tanstack/start-server-functions-server": 1.131.2(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0)) + "@tanstack/start-server-functions-ssr": 1.120.19(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0)) transitivePeerDependencies: - "@azure/app-configuration" - "@azure/cosmos" @@ -22287,8 +22533,8 @@ snapshots: "@testing-library/dom@10.4.1": dependencies: - "@babel/code-frame": 7.29.0 - "@babel/runtime": 7.29.2 + "@babel/code-frame": 7.29.7 + "@babel/runtime": 7.29.7 "@types/aria-query": 5.0.4 aria-query: 5.3.0 dom-accessibility-api: 0.5.16 @@ -22328,9 +22574,9 @@ snapshots: "@tokenizer/token@0.3.0": {} - "@trpc/client@11.16.0(@trpc/server@11.16.0(typescript@5.9.3))(typescript@5.9.3)": + "@trpc/client@11.16.0(@trpc/server@11.17.0(typescript@5.9.3))(typescript@5.9.3)": dependencies: - "@trpc/server": 11.16.0(typescript@5.9.3) + "@trpc/server": 11.17.0(typescript@5.9.3) typescript: 5.9.3 "@trpc/client@11.17.0(@trpc/server@11.16.0(typescript@5.9.3))(typescript@5.9.3)": @@ -22454,11 +22700,11 @@ snapshots: "@types/eslint-scope@3.7.7": dependencies: "@types/eslint": 9.6.1 - "@types/estree": 1.0.8 + "@types/estree": 1.0.9 "@types/eslint@9.6.1": dependencies: - "@types/estree": 1.0.8 + "@types/estree": 1.0.9 "@types/json-schema": 7.0.15 "@types/estree-jsx@1.0.5": @@ -22467,6 +22713,8 @@ snapshots: "@types/estree@1.0.8": {} + "@types/estree@1.0.9": {} + "@types/graceful-fs@4.1.9": dependencies: "@types/node": 22.19.17 @@ -22523,6 +22771,10 @@ snapshots: dependencies: undici-types: 6.21.0 + "@types/node@22.20.1": + dependencies: + undici-types: 6.21.0 + "@types/node@25.5.2": dependencies: undici-types: 7.18.2 @@ -22739,7 +22991,7 @@ snapshots: untun: 0.1.3 uqr: 0.1.3 - "@vitejs/plugin-react@4.7.0(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0))": + "@vitejs/plugin-react@4.7.0(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0))": dependencies: "@babel/core": 7.29.0 "@babel/plugin-transform-react-jsx-self": 7.27.1(@babel/core@7.29.0) @@ -22747,11 +22999,11 @@ snapshots: "@rolldown/pluginutils": 1.0.0-beta.27 "@types/babel__core": 7.20.5 react-refresh: 0.17.0 - vite: 6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0) + vite: 6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0) transitivePeerDependencies: - supports-color - "@vitest/coverage-v8@3.2.4(vitest@3.2.4(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0))": + "@vitest/coverage-v8@3.2.7(vitest@3.2.7(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0))": dependencies: "@ampproject/remapping": 2.3.0 "@bcoe/v8-coverage": 1.0.2 @@ -22766,11 +23018,11 @@ snapshots: std-env: 3.10.0 test-exclude: 7.0.2 tinyrainbow: 2.0.0 - vitest: 3.2.4(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0) + vitest: 3.2.7(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0) transitivePeerDependencies: - supports-color - "@vitest/coverage-v8@3.2.4(vitest@3.2.4(@types/debug@4.1.13)(@types/node@25.5.2)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0))": + "@vitest/coverage-v8@3.2.7(vitest@3.2.7(@types/debug@4.1.13)(@types/node@25.5.2)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0))": dependencies: "@ampproject/remapping": 2.3.0 "@bcoe/v8-coverage": 1.0.2 @@ -22785,57 +23037,57 @@ snapshots: std-env: 3.10.0 test-exclude: 7.0.2 tinyrainbow: 2.0.0 - vitest: 3.2.4(@types/debug@4.1.13)(@types/node@25.5.2)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0) + vitest: 3.2.7(@types/debug@4.1.13)(@types/node@25.5.2)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0) transitivePeerDependencies: - supports-color - "@vitest/expect@3.2.4": + "@vitest/expect@3.2.7": dependencies: "@types/chai": 5.2.3 - "@vitest/spy": 3.2.4 - "@vitest/utils": 3.2.4 + "@vitest/spy": 3.2.7 + "@vitest/utils": 3.2.7 chai: 5.3.3 tinyrainbow: 2.0.0 - "@vitest/mocker@3.2.4(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0))": + "@vitest/mocker@3.2.7(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0))": dependencies: - "@vitest/spy": 3.2.4 + "@vitest/spy": 3.2.7 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0) + vite: 6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0) - "@vitest/mocker@3.2.4(vite@6.4.2(@types/node@25.5.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0))": + "@vitest/mocker@3.2.7(vite@6.4.2(@types/node@25.5.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0))": dependencies: - "@vitest/spy": 3.2.4 + "@vitest/spy": 3.2.7 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 6.4.2(@types/node@25.5.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0) + vite: 6.4.2(@types/node@25.5.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0) - "@vitest/pretty-format@3.2.4": + "@vitest/pretty-format@3.2.7": dependencies: tinyrainbow: 2.0.0 - "@vitest/runner@3.2.4": + "@vitest/runner@3.2.7": dependencies: - "@vitest/utils": 3.2.4 + "@vitest/utils": 3.2.7 pathe: 2.0.3 strip-literal: 3.1.0 - "@vitest/snapshot@3.2.4": + "@vitest/snapshot@3.2.7": dependencies: - "@vitest/pretty-format": 3.2.4 + "@vitest/pretty-format": 3.2.7 magic-string: 0.30.21 pathe: 2.0.3 - "@vitest/spy@3.2.4": + "@vitest/spy@3.2.7": dependencies: tinyspy: 4.0.4 - "@vitest/utils@3.2.4": + "@vitest/utils@3.2.7": dependencies: - "@vitest/pretty-format": 3.2.4 + "@vitest/pretty-format": 3.2.7 loupe: 3.2.1 tinyrainbow: 2.0.0 @@ -22929,9 +23181,9 @@ snapshots: dependencies: acorn: 8.16.0 - acorn-import-phases@1.0.4(acorn@8.16.0): + acorn-import-phases@1.0.4(acorn@8.17.0): dependencies: - acorn: 8.16.0 + acorn: 8.17.0 acorn-jsx@5.3.2(acorn@8.16.0): dependencies: @@ -22939,6 +23191,8 @@ snapshots: acorn@8.16.0: {} + acorn@8.17.0: {} + agent-base@6.0.2: dependencies: debug: 4.4.3 @@ -22952,13 +23206,13 @@ snapshots: clean-stack: 2.2.0 indent-string: 4.0.0 - ajv-formats@2.1.1(ajv@8.18.0): + ajv-formats@2.1.1(ajv@8.20.0): optionalDependencies: - ajv: 8.18.0 + ajv: 8.20.0 - ajv-keywords@5.1.0(ajv@8.18.0): + ajv-keywords@5.1.0(ajv@8.20.0): dependencies: - ajv: 8.18.0 + ajv: 8.20.0 fast-deep-equal: 3.1.3 ajv@6.14.0: @@ -22982,6 +23236,13 @@ snapshots: json-schema-traverse: 1.0.0 require-from-string: 2.0.2 + ajv@8.20.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.3 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + angular-html-parser@6.0.2: dependencies: tslib: 2.8.1 @@ -23213,6 +23474,8 @@ snapshots: baseline-browser-mapping@2.10.15: {} + baseline-browser-mapping@2.10.42: {} + basic-auth@2.0.1: dependencies: safe-buffer: 5.1.2 @@ -23269,6 +23532,14 @@ snapshots: node-releases: 2.0.37 update-browserslist-db: 1.2.3(browserslist@4.28.2) + browserslist@4.28.5: + dependencies: + baseline-browser-mapping: 2.10.42 + caniuse-lite: 1.0.30001803 + electron-to-chromium: 1.5.389 + node-releases: 2.0.51 + update-browserslist-db: 1.2.3(browserslist@4.28.5) + bser@2.1.1: dependencies: node-int64: 0.4.0 @@ -23345,6 +23616,8 @@ snapshots: caniuse-lite@1.0.30001786: {} + caniuse-lite@1.0.30001803: {} + ccount@2.0.1: {} chai@5.3.3: @@ -23884,6 +24157,8 @@ snapshots: electron-to-chromium@1.5.331: {} + electron-to-chromium@1.5.389: {} + emittery@0.13.1: {} emoji-regex@10.4.0: {} @@ -23915,6 +24190,11 @@ snapshots: graceful-fs: 4.2.11 tapable: 2.3.3 + enhanced-resolve@5.24.2: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.3 + entities@1.1.2: {} entities@2.2.0: {} @@ -23939,7 +24219,7 @@ snapshots: es-module-lexer@1.7.0: {} - es-module-lexer@2.1.0: {} + es-module-lexer@2.3.0: {} es-object-atoms@1.1.1: dependencies: @@ -24244,7 +24524,7 @@ snapshots: estree-walker@3.0.3: dependencies: - "@types/estree": 1.0.8 + "@types/estree": 1.0.9 esutils@2.0.3: {} @@ -24383,6 +24663,8 @@ snapshots: fast-uri@3.1.0: {} + fast-uri@3.1.3: {} + fast-wrap-ansi@0.2.0: dependencies: fast-string-width: 3.0.2 @@ -24675,7 +24957,7 @@ snapshots: h3@2.0.1-rc.20: dependencies: rou3: 0.8.1 - srvx: 0.11.15 + srvx: 0.11.21 happy-dom@20.8.9: dependencies: @@ -25527,7 +25809,7 @@ snapshots: jest-worker@27.5.1: dependencies: - "@types/node": 22.19.17 + "@types/node": 22.20.1 merge-stream: 2.0.0 supports-color: 8.1.1 @@ -26213,10 +26495,26 @@ snapshots: "@msgpackr-extract/msgpackr-extract-win32-x64": 3.0.3 optional: true + msgpackr-extract@3.0.4: + dependencies: + node-gyp-build-optional-packages: 5.2.2 + optionalDependencies: + "@msgpackr-extract/msgpackr-extract-darwin-arm64": 3.0.4 + "@msgpackr-extract/msgpackr-extract-darwin-x64": 3.0.4 + "@msgpackr-extract/msgpackr-extract-linux-arm": 3.0.4 + "@msgpackr-extract/msgpackr-extract-linux-arm64": 3.0.4 + "@msgpackr-extract/msgpackr-extract-linux-x64": 3.0.4 + "@msgpackr-extract/msgpackr-extract-win32-x64": 3.0.4 + optional: true + msgpackr@1.11.12: optionalDependencies: msgpackr-extract: 3.0.3 + msgpackr@1.12.1: + optionalDependencies: + msgpackr-extract: 3.0.4 + multipasta@0.2.7: {} mutation-testing-elements@3.4.0: {} @@ -26269,8 +26567,8 @@ snapshots: dependencies: "@next/env": 16.2.2 "@swc/helpers": 0.5.15 - baseline-browser-mapping: 2.10.15 - caniuse-lite: 1.0.30001786 + baseline-browser-mapping: 2.10.42 + caniuse-lite: 1.0.30001803 postcss: 8.4.31 react: 19.2.4 react-dom: 19.2.4(react@19.2.4) @@ -26423,6 +26721,8 @@ snapshots: node-releases@2.0.37: {} + node-releases@2.0.51: {} + nopt@8.1.0: dependencies: abbrev: 3.0.1 @@ -26888,7 +27188,7 @@ snapshots: "@jest/schemas": 30.4.1 ansi-styles: 5.2.0 react-is-18: react-is@18.3.1 - react-is-19: react-is@19.2.6 + react-is-19: react-is@19.2.7 pretty-ms@9.3.0: dependencies: @@ -27019,7 +27319,7 @@ snapshots: react-is@18.3.1: {} - react-is@19.2.6: {} + react-is@19.2.7: {} react-refresh@0.17.0: {} @@ -27303,9 +27603,9 @@ snapshots: schema-utils@4.3.3: dependencies: "@types/json-schema": 7.0.15 - ajv: 8.18.0 - ajv-formats: 2.1.1(ajv@8.18.0) - ajv-keywords: 5.1.0(ajv@8.18.0) + ajv: 8.20.0 + ajv-formats: 2.1.1(ajv@8.20.0) + ajv-keywords: 5.1.0(ajv@8.20.0) scule@1.3.0: {} @@ -27577,7 +27877,7 @@ snapshots: sprintf-js@1.0.3: {} - srvx@0.11.15: {} + srvx@0.11.21: {} stack-utils@2.0.6: dependencies: @@ -27777,13 +28077,15 @@ snapshots: - bare-abort-controller - react-native-b4a - terser-webpack-plugin@5.5.0(webpack@5.106.2): + terser-webpack-plugin@5.6.1(lightningcss@1.32.0)(webpack@5.106.2(lightningcss@1.32.0)): dependencies: "@jridgewell/trace-mapping": 0.3.31 jest-worker: 27.5.1 schema-utils: 4.3.3 - terser: 5.46.2 - webpack: 5.106.2 + terser: 5.49.0 + webpack: 5.106.2(lightningcss@1.32.0) + optionalDependencies: + lightningcss: 1.32.0 terser@5.46.2: dependencies: @@ -27792,6 +28094,13 @@ snapshots: commander: 2.20.3 source-map-support: 0.5.21 + terser@5.49.0: + dependencies: + "@jridgewell/source-map": 0.3.11 + acorn: 8.17.0 + commander: 2.20.3 + source-map-support: 0.5.21 + test-exclude@6.0.0: dependencies: "@istanbuljs/schema": 0.1.6 @@ -28129,6 +28438,12 @@ snapshots: escalade: 3.2.0 picocolors: 1.1.1 + update-browserslist-db@1.2.3(browserslist@4.28.5): + dependencies: + browserslist: 4.28.5 + escalade: 3.2.0 + picocolors: 1.1.1 + uqr@0.1.3: {} uri-js@4.4.1: @@ -28183,7 +28498,7 @@ snapshots: "@types/unist": 3.0.3 unist-util-stringify-position: 4.0.0 - vinxi@0.5.3(@types/node@22.19.17)(db0@0.3.4(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3)))(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3))(ioredis@5.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0): + vinxi@0.5.3(@types/node@22.19.17)(db0@0.3.4(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3)))(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3))(ioredis@5.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0): dependencies: "@babel/core": 7.29.0 "@babel/plugin-syntax-jsx": 7.28.6(@babel/core@7.29.0) @@ -28217,7 +28532,7 @@ snapshots: unctx: 2.5.0 unenv: 1.10.0 unstorage: 1.17.5(db0@0.3.4(drizzle-orm@0.44.7(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(pg@8.16.3)))(ioredis@5.10.1) - vite: 6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0) + vite: 6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0) zod: 3.25.76 transitivePeerDependencies: - "@azure/app-configuration" @@ -28266,13 +28581,13 @@ snapshots: - xml2js - yaml - vite-node@3.2.4(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0): + vite-node@3.2.4(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0): dependencies: cac: 6.7.14 debug: 4.4.3 es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0) + vite: 6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0) transitivePeerDependencies: - "@types/node" - jiti @@ -28287,13 +28602,13 @@ snapshots: - tsx - yaml - vite-node@3.2.4(@types/node@25.5.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0): + vite-node@3.2.4(@types/node@25.5.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0): dependencies: cac: 6.7.14 debug: 4.4.3 es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 6.4.2(@types/node@25.5.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0) + vite: 6.4.2(@types/node@25.5.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0) transitivePeerDependencies: - "@types/node" - jiti @@ -28308,7 +28623,7 @@ snapshots: - tsx - yaml - vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0): + vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0): dependencies: esbuild: 0.25.12 fdir: 6.5.0(picomatch@4.0.4) @@ -28322,11 +28637,11 @@ snapshots: jiti: 2.7.0 lightningcss: 1.32.0 sass: 1.99.0 - terser: 5.46.2 + terser: 5.49.0 tsx: 4.21.0 yaml: 2.9.0 - vite@6.4.2(@types/node@25.5.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0): + vite@6.4.2(@types/node@25.5.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0): dependencies: esbuild: 0.25.12 fdir: 6.5.0(picomatch@4.0.4) @@ -28340,24 +28655,24 @@ snapshots: jiti: 2.7.0 lightningcss: 1.32.0 sass: 1.99.0 - terser: 5.46.2 + terser: 5.49.0 tsx: 4.21.0 yaml: 2.9.0 - vitefu@1.1.3(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0)): + vitefu@1.1.3(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0)): optionalDependencies: - vite: 6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0) + vite: 6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0) - vitest@3.2.4(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0): + vitest@3.2.7(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0): dependencies: "@types/chai": 5.2.3 - "@vitest/expect": 3.2.4 - "@vitest/mocker": 3.2.4(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0)) - "@vitest/pretty-format": 3.2.4 - "@vitest/runner": 3.2.4 - "@vitest/snapshot": 3.2.4 - "@vitest/spy": 3.2.4 - "@vitest/utils": 3.2.4 + "@vitest/expect": 3.2.7 + "@vitest/mocker": 3.2.7(vite@6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0)) + "@vitest/pretty-format": 3.2.7 + "@vitest/runner": 3.2.7 + "@vitest/snapshot": 3.2.7 + "@vitest/spy": 3.2.7 + "@vitest/utils": 3.2.7 chai: 5.3.3 debug: 4.4.3 expect-type: 1.3.0 @@ -28370,8 +28685,8 @@ snapshots: tinyglobby: 0.2.15 tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0) - vite-node: 3.2.4(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0) + vite: 6.4.2(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0) + vite-node: 3.2.4(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0) why-is-node-running: 2.3.0 optionalDependencies: "@types/debug": 4.1.13 @@ -28392,16 +28707,16 @@ snapshots: - tsx - yaml - vitest@3.2.4(@types/debug@4.1.13)(@types/node@25.5.2)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0): + vitest@3.2.7(@types/debug@4.1.13)(@types/node@25.5.2)(happy-dom@20.8.9)(jiti@2.7.0)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0): dependencies: "@types/chai": 5.2.3 - "@vitest/expect": 3.2.4 - "@vitest/mocker": 3.2.4(vite@6.4.2(@types/node@25.5.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0)) - "@vitest/pretty-format": 3.2.4 - "@vitest/runner": 3.2.4 - "@vitest/snapshot": 3.2.4 - "@vitest/spy": 3.2.4 - "@vitest/utils": 3.2.4 + "@vitest/expect": 3.2.7 + "@vitest/mocker": 3.2.7(vite@6.4.2(@types/node@25.5.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0)) + "@vitest/pretty-format": 3.2.7 + "@vitest/runner": 3.2.7 + "@vitest/snapshot": 3.2.7 + "@vitest/spy": 3.2.7 + "@vitest/utils": 3.2.7 chai: 5.3.3 debug: 4.4.3 expect-type: 1.3.0 @@ -28414,8 +28729,8 @@ snapshots: tinyglobby: 0.2.15 tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 6.4.2(@types/node@25.5.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0) - vite-node: 3.2.4(@types/node@25.5.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0) + vite: 6.4.2(@types/node@25.5.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0) + vite-node: 3.2.4(@types/node@25.5.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0) why-is-node-running: 2.3.0 optionalDependencies: "@types/debug": 4.1.13 @@ -28472,9 +28787,8 @@ snapshots: dependencies: makeerror: 1.0.12 - watchpack@2.5.1: + watchpack@2.5.2: dependencies: - glob-to-regexp: 0.4.1 graceful-fs: 4.2.11 weapon-regex@1.3.6: {} @@ -28483,24 +28797,24 @@ snapshots: webidl-conversions@7.0.0: {} - webpack-sources@3.4.1: {} + webpack-sources@3.5.1: {} webpack-virtual-modules@0.6.2: {} - webpack@5.106.2: + webpack@5.106.2(lightningcss@1.32.0): dependencies: "@types/eslint-scope": 3.7.7 - "@types/estree": 1.0.8 + "@types/estree": 1.0.9 "@types/json-schema": 7.0.15 "@webassemblyjs/ast": 1.14.1 "@webassemblyjs/wasm-edit": 1.14.1 "@webassemblyjs/wasm-parser": 1.14.1 - acorn: 8.16.0 - acorn-import-phases: 1.0.4(acorn@8.16.0) - browserslist: 4.28.2 + acorn: 8.17.0 + acorn-import-phases: 1.0.4(acorn@8.17.0) + browserslist: 4.28.5 chrome-trace-event: 1.0.4 - enhanced-resolve: 5.22.0 - es-module-lexer: 2.1.0 + enhanced-resolve: 5.24.2 + es-module-lexer: 2.3.0 eslint-scope: 5.1.1 events: 3.3.0 glob-to-regexp: 0.4.1 @@ -28510,12 +28824,21 @@ snapshots: neo-async: 2.6.2 schema-utils: 4.3.3 tapable: 2.3.3 - terser-webpack-plugin: 5.5.0(webpack@5.106.2) - watchpack: 2.5.1 - webpack-sources: 3.4.1 + terser-webpack-plugin: 5.6.1(lightningcss@1.32.0)(webpack@5.106.2(lightningcss@1.32.0)) + watchpack: 2.5.2 + webpack-sources: 3.5.1 transitivePeerDependencies: + - "@minify-html/node" - "@swc/core" + - "@swc/css" + - "@swc/html" + - clean-css + - cssnano + - csso - esbuild + - html-minifier-terser + - lightningcss + - postcss - uglify-js whatwg-encoding@2.0.0: diff --git a/turbo/generators/package.json b/turbo/generators/package.json index 8b6d077..5a48214 100644 --- a/turbo/generators/package.json +++ b/turbo/generators/package.json @@ -14,7 +14,8 @@ "devDependencies": { "@repo/core-eslint": "workspace:*", "@repo/core-typescript": "workspace:*", + "@vitest/coverage-v8": "^3.2.7", "typescript": "^5.8.0", - "vitest": "^3.1.0" + "vitest": "^3.2.7" } } -- 2.49.1 From eccd8b0cc15ab65cd91b9172814edd647b2bf5e8 Mon Sep 17 00:00:00 2001 From: Danijel Martinek Date: Fri, 10 Jul 2026 16:47:18 +0200 Subject: [PATCH 15/54] fix(library-decisions): traceSchema accepts the committed trace shape The strict schema rejected 38 of 39 approved traces in docs/library-decisions: lastRevalidated was required (most traces omit it), and the compliance frontmatter fields the _template.md documents (is-sub-processor, processes-pii, plus the sub-processor block: data-sent, region, dpa-signed, sccs-required, contact) were unrecognized keys. Make lastRevalidated optional, add the compliance fields (booleanish coercion for YAML string scalars), and require data-sent when is-sub-processor is true. New loop test asserts every committed trace parses so the schema can never drift away from the repo's own corpus again. Co-Authored-By: Claude Fable 5 --- scripts/library-decisions/schema.mjs | 32 +++++++++++- scripts/library-decisions/schema.test.mjs | 62 +++++++++++++++++++++++ 2 files changed, 92 insertions(+), 2 deletions(-) diff --git a/scripts/library-decisions/schema.mjs b/scripts/library-decisions/schema.mjs index eb37ad7..5756c57 100644 --- a/scripts/library-decisions/schema.mjs +++ b/scripts/library-decisions/schema.mjs @@ -7,6 +7,12 @@ import fs from "node:fs"; // ---- Zod schema ---- +/** YAML scalars arrive as strings — coerce "true"/"false" (or booleans). */ +const booleanish = z.preprocess( + (v) => (v === "true" ? true : v === "false" ? false : v), + z.boolean(), +); + const filterResultsSchema = z .object({ license: z.string().min(1), @@ -30,12 +36,34 @@ export const traceSchema = z date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "date must be YYYY-MM-DD"), deciders: z.array(z.string()), adr: z.string().nullable(), - lastRevalidated: z.string().nullable(), + // Optional: most committed traces have never been revalidated and omit + // the key entirely (requiring it rejected 38 of 39 approved traces). + lastRevalidated: z.string().nullable().optional(), "filter-results": filterResultsSchema, "verification-commands": z.array(z.string()), "accepted-cves": z.array(z.string()).optional(), + // ADR-022 amendment (compliance frontmatter, see _template.md): every + // newer trace declares whether the dependency ships data to a third + // party; sub-processor traces carry the extra contact/DPA fields. + "is-sub-processor": booleanish.optional(), + "processes-pii": booleanish.optional(), + "data-sent": z.string().nullable().optional(), + region: z.string().nullable().optional(), + "dpa-signed": z.union([booleanish, z.string()]).nullable().optional(), + "sccs-required": z.union([booleanish, z.string()]).nullable().optional(), + contact: z.string().nullable().optional(), }) - .strict(); + .strict() + .superRefine((trace, ctx) => { + if (trace["is-sub-processor"] === true && trace["data-sent"] == null) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: + "sub-processor traces must declare data-sent (what leaves the machine)", + path: ["data-sent"], + }); + } + }); // ---- Helpers ---- diff --git a/scripts/library-decisions/schema.test.mjs b/scripts/library-decisions/schema.test.mjs index 2b285e3..8ebd6b0 100644 --- a/scripts/library-decisions/schema.test.mjs +++ b/scripts/library-decisions/schema.test.mjs @@ -218,3 +218,65 @@ describe("parseTrace", () => { assert.throws(() => parseTrace(file), /invalid_type|Required/i); }); }); + +describe("validateTrace > compliance frontmatter (ADR-022 amendment)", () => { + test("accepts is-sub-processor / processes-pii as YAML string booleans", () => { + const parsed = validateTrace( + validRaw({ "is-sub-processor": "false", "processes-pii": "true" }), + ); + assert.equal(parsed["is-sub-processor"], false); + assert.equal(parsed["processes-pii"], true); + }); + + test("accepts a trace that omits lastRevalidated entirely", () => { + const raw = validRaw(); + delete raw.lastRevalidated; + assert.equal(validateTrace(raw).lastRevalidated, undefined); + }); + + test("accepts a full sub-processor block", () => { + const parsed = validateTrace( + validRaw({ + "is-sub-processor": "true", + "processes-pii": "true", + "data-sent": "error events, stack traces", + region: "eu", + "dpa-signed": "true", + "sccs-required": "false", + contact: "https://example.com/dpa", + }), + ); + assert.equal(parsed["is-sub-processor"], true); + assert.equal(parsed["data-sent"], "error events, stack traces"); + }); + + test("rejects is-sub-processor: true without data-sent", () => { + assert.throws( + () => validateTrace(validRaw({ "is-sub-processor": "true" })), + /data-sent/, + ); + }); +}); + +describe("parseTrace > every committed trace parses", () => { + // The schema exists to validate the traces actually in the repo — a schema + // that rejects committed, approved traces is broken (it silently rejected + // 38 of 39 before lastRevalidated became optional and the compliance + // fields were added). + test("all docs/library-decisions traces validate", () => { + const docsDir = path.join( + path.dirname(new URL(import.meta.url).pathname), + "..", + "..", + "docs", + "library-decisions", + ); + const files = fs + .globSync(path.join(docsDir, "**", "*.md")) + .filter((f) => !f.endsWith("_template.md")); + assert.ok(files.length > 0, "expected committed traces to exist"); + for (const f of files) { + assert.doesNotThrow(() => parseTrace(f), `trace failed to parse: ${f}`); + } + }); +}); -- 2.49.1 From 3cf2572c8576559b33c5c9bd61c9efa8e51330f7 Mon Sep 17 00:00:00 2001 From: Danijel Martinek Date: Fri, 10 Jul 2026 16:53:16 +0200 Subject: [PATCH 16/54] chore(fallow): make the whole-codebase gate pass without hiding signal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pnpm fallow failed on 14 dead-code issues, 200 clone groups (4.3% > 3.0% threshold) and one cognitive-complexity breach. Changes: - duplicates.ignore covers convention-mandated boilerplate only (test files, stories, fixtures/factories/seeds, binders, symbols, feature manifests, vitest configs, mock/repo twins, sentry init twins, compliance emitters, rule-meta boilerplate) at threshold 3.0 - usedClassMembers: validateSession (interface-implemented, not yet called); ignoreExports: Next's generateMetadata convention export + the __getInstrumentationForTests test knob - duplicate-exports off: client/server RSC twins export the same component name by design - @trpc/client + @trpc/react-query added to ignoreDependencies (peer resolution for feature ./ui hooks); *.test.mjs marked dynamically loaded (node:test files fallow saw as unreachable) - delete packages/auth/src/ui/query.ts (empty export{} placeholder shadowed by ui/index.ts, genuinely dead) - extract checkDepTrace/decisionError from checkLibraryDecisions (cognitive 32 > 30) — behavior unchanged, tests pass Co-Authored-By: Claude Fable 5 --- .fallowrc.json | 53 +++++++++++- packages/auth/src/ui/query.ts | 5 -- scripts/library-decisions/check.mjs | 121 +++++++++++++++------------- 3 files changed, 114 insertions(+), 65 deletions(-) delete mode 100644 packages/auth/src/ui/query.ts diff --git a/.fallowrc.json b/.fallowrc.json index 785ae90..a5b6fa8 100644 --- a/.fallowrc.json +++ b/.fallowrc.json @@ -17,7 +17,8 @@ "apps/**/instrumentation.ts", "apps/**/instrumentation-client.ts", "apps/storybook/test-runner.config.ts", - "scripts/**/*.mjs" + "scripts/**/*.mjs", + "turbo/generators/**/*.test.mjs" ], "publicPackages": ["@repo/core-*"], "ignoreDependencies": [ @@ -42,7 +43,9 @@ "@opentelemetry/sdk-node", "@sentry/opentelemetry", "@stryker-mutator/core", - "@stryker-mutator/vitest-runner" + "@stryker-mutator/vitest-runner", + "@trpc/client", + "@trpc/react-query" ], "ignoreExportsUsedInFile": true, "rules": { @@ -54,11 +57,53 @@ "unused-dev-dependencies": "warn", "unlisted-dependencies": "warn", "circular-dependencies": "error", - "duplicate-code": "warn" + "duplicate-code": "warn", + "duplicate-exports": "off" }, "health": { "maxCyclomatic": 25, "maxCognitive": 30, "maxCrap": 400 - } + }, + "usedClassMembers": ["validateSession"], + "duplicates": { + "ignore": [ + "**/*.test.ts", + "**/*.test.tsx", + "**/*.stories.tsx", + "**/__fixtures__/**", + "**/__factories__/**", + "**/__seeds__/**", + "**/di/bind-production.ts", + "**/di/bind-dev-seed.ts", + "**/di/symbols.ts", + "**/integrations/api/**", + "**/ui/trpc.ts", + "**/feature.manifest.ts", + "**/vitest.config.ts", + "scripts/work/**", + "**/*.test.mjs", + "**/*.test.js", + "**/*.mock.ts", + "**/instrumentation/sentry/init-client*.ts", + "**/instrumentation/di/bind-*.ts", + "packages/core-eslint/rules/component-must-have-*.js", + "scripts/compliance/**", + "**/setup/no-instrumentation.ts", + "packages/core-eslint/rules/no-undeclared-*.js" + ], + "minOccurrences": 2, + "minTokens": 70, + "threshold": 3.0 + }, + "ignoreExports": [ + { + "file": "apps/cms/src/app/**/not-found.tsx", + "exports": ["generateMetadata"] + }, + { + "file": "apps/web-next/src/server/bind-production.ts", + "exports": ["__getInstrumentationForTests"] + } + ] } diff --git a/packages/auth/src/ui/query.ts b/packages/auth/src/ui/query.ts deleted file mode 100644 index ebb110c..0000000 --- a/packages/auth/src/ui/query.ts +++ /dev/null @@ -1,5 +0,0 @@ -// React Query option builders for auth feature procedures. -// Sign-in/up/out are mutations — no query options needed. -// This file is intentionally minimal; expand if read procedures get added. - -export {}; diff --git a/scripts/library-decisions/check.mjs b/scripts/library-decisions/check.mjs index 8608ea1..d5f7a25 100644 --- a/scripts/library-decisions/check.mjs +++ b/scripts/library-decisions/check.mjs @@ -294,6 +294,63 @@ export function checkRenovatePr( * * An empty array means the commit is clean. */ +/** Parse a trace's frontmatter; error entry when decision !== approved. */ +function decisionError(content, relPath, dep) { + const fm = parseFrontmatter(content); + if (fm.decision !== "approved") { + return { + pkgJson: relPath, + dep, + reason: "not-approved", + decision: fm.decision, + }; + } + return null; +} + +/** + * Validate one new runtime dep against its staged or committed trace. + * Returns an error entry, or null when an approved trace covers the dep. + */ +function checkDepTrace(dep, relPath, staged, repoRoot, stagedAgainst) { + const stagedTrace = findStagedTrace(dep, staged); + if (!stagedTrace) { + // Fall back to an already-committed trace — if one exists and is + // approved, the dep was previously evaluated and doesn't need + // re-staging just because a new package adopts it. + const committedTrace = findExistingTrace(dep, repoRoot); + if (!committedTrace) { + return { pkgJson: relPath, dep, reason: "no-trace" }; + } + try { + const content = fs.readFileSync(committedTrace, "utf8"); + return decisionError(content, relPath, dep); + } catch (e) { + return { + pkgJson: relPath, + dep, + reason: "parse-error", + detail: String(e.message), + }; + } + } + try { + const traceRef = stagedAgainst ? `HEAD:${stagedTrace}` : `:${stagedTrace}`; + const content = execSync(`git show "${traceRef}"`, { + cwd: repoRoot, + encoding: "utf8", + }); + return decisionError(content, relPath, dep); + } catch (e) { + return { + pkgJson: relPath, + dep, + reason: "parse-error", + detail: String(e.message), + }; + } +} + export function checkLibraryDecisions( repoRoot = DEFAULT_REPO_ROOT, { stagedAgainst } = {}, @@ -309,62 +366,14 @@ export function checkLibraryDecisions( if (tier === "app" || tier === "skip") continue; for (const dep of getNewRuntimeDeps(relPath, repoRoot, stagedAgainst)) { - const stagedTrace = findStagedTrace(dep, staged); - if (!stagedTrace) { - // Fall back to an already-committed trace — if one exists and is - // approved, the dep was previously evaluated and doesn't need - // re-staging just because a new package adopts it. - const committedTrace = findExistingTrace(dep, repoRoot); - if (committedTrace) { - try { - const content = fs.readFileSync(committedTrace, "utf8"); - const fm = parseFrontmatter(content); - if (fm.decision !== "approved") { - errors.push({ - pkgJson: relPath, - dep, - reason: "not-approved", - decision: fm.decision, - }); - } - } catch (e) { - errors.push({ - pkgJson: relPath, - dep, - reason: "parse-error", - detail: String(e.message), - }); - } - continue; - } - errors.push({ pkgJson: relPath, dep, reason: "no-trace" }); - continue; - } - try { - const traceRef = stagedAgainst - ? `HEAD:${stagedTrace}` - : `:${stagedTrace}`; - const content = execSync(`git show "${traceRef}"`, { - cwd: repoRoot, - encoding: "utf8", - }); - const fm = parseFrontmatter(content); - if (fm.decision !== "approved") { - errors.push({ - pkgJson: relPath, - dep, - reason: "not-approved", - decision: fm.decision, - }); - } - } catch (e) { - errors.push({ - pkgJson: relPath, - dep, - reason: "parse-error", - detail: String(e.message), - }); - } + const error = checkDepTrace( + dep, + relPath, + staged, + repoRoot, + stagedAgainst, + ); + if (error) errors.push(error); } } -- 2.49.1 From bb2751eede98bec944282fedfdfe6f0d76485a49 Mon Sep 17 00:00:00 2001 From: Danijel Martinek Date: Fri, 10 Jul 2026 17:20:27 +0200 Subject: [PATCH 17/54] docs: correct stale no-op comments in node test setup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit setup/node.ts installs the no-instrumentation guard (vi.mock against real Sentry/OTel SDK init) via its import — it was never a no-op. Fix the same lie in core-typescript's jsdom base-config test comments and test title. (S2) Co-Authored-By: Claude Fable 5 --- packages/core-testing/src/setup/node.ts | 5 +++-- packages/core-typescript/vitest.base.jsdom.test.ts | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/core-testing/src/setup/node.ts b/packages/core-testing/src/setup/node.ts index d186513..872a132 100644 --- a/packages/core-testing/src/setup/node.ts +++ b/packages/core-testing/src/setup/node.ts @@ -1,5 +1,6 @@ import "./no-instrumentation"; -// Reserved for future global node-env setup. Currently a no-op so that -// vitest configs may reference @repo/core-testing/setup/node uniformly. +// Applies the no-instrumentation guard (vi.mock against real Sentry/OTel SDK +// init) via the import above; otherwise reserved for future global node-env +// setup so vitest configs may reference @repo/core-testing/setup/node uniformly. export {}; diff --git a/packages/core-typescript/vitest.base.jsdom.test.ts b/packages/core-typescript/vitest.base.jsdom.test.ts index 9f0c54e..9e42802 100644 --- a/packages/core-typescript/vitest.base.jsdom.test.ts +++ b/packages/core-typescript/vitest.base.jsdom.test.ts @@ -3,7 +3,8 @@ import { jsdomVitestConfig } from "./vitest.base.jsdom"; // jsdomVitestConfig is built with mergeConfig(nodeVitestConfig, ...). // mergeConfig concatenates arrays rather than replacing them, so: -// - setupFiles inherits the node setup file (currently a no-op) AND adds the jsdom one +// - setupFiles inherits the node setup file (which applies the +// no-instrumentation guard) AND adds the jsdom one // - include inherits the node patterns AND adds the tsx-aware patterns (the file globs deduplicate at resolution) // We assert the EXACT merged arrays so any future change to setup/node.ts or to include // behavior is consciously visible in this test. @@ -12,7 +13,7 @@ describe("jsdomVitestConfig", () => { it("uses jsdom environment", () => { expect(jsdomVitestConfig.test?.environment).toBe("jsdom"); }); - it("loads both the node setup (no-op) and jsdom setup files", () => { + it("loads both the node setup (no-instrumentation guard) and jsdom setup files", () => { expect(jsdomVitestConfig.test?.setupFiles).toEqual([ "@repo/core-testing/setup/node", "@repo/core-testing/setup/jsdom", -- 2.49.1 From c9db7c8cd7ce529cb0f1082f06a670828cc47b6f Mon Sep 17 00:00:00 2001 From: Danijel Martinek Date: Fri, 10 Jul 2026 17:21:16 +0200 Subject: [PATCH 18/54] fix(auth): add username + passwordHash fields to users collection The production UsersRepository reads and writes username + passwordHash via the Payload local API, but the users collection never declared them, so production sign-up/sign-in was broken (audit finding B1). passwordHash uses access.read: () => false so credential material never serializes through any Payload API surface; the repository still reads it with overrideAccess: true. A contract-shaped test pins every repo-used field (USERS_REPOSITORY_FIELDS) against the collection config so drift fails at test time without a database. Co-Authored-By: Claude Fable 5 --- compliance/data-map.yml | 7 +++ .../repositories/users.repository.ts | 43 +++++++++++--- .../cms/collections/users.test.ts | 59 +++++++++++++++++++ .../src/integrations/cms/collections/users.ts | 29 +++++++++ 4 files changed, 131 insertions(+), 7 deletions(-) create mode 100644 packages/auth/src/integrations/cms/collections/users.test.ts diff --git a/compliance/data-map.yml b/compliance/data-map.yml index 00eb1df..e82aa74 100644 --- a/compliance/data-map.yml +++ b/compliance/data-map.yml @@ -32,4 +32,11 @@ collections: - transactional-notifications restrictable: true source: auth-default + - category: identification-username + exportable: true + field: username + purpose: + - service-delivery + restrictable: true + source: field-tag slug: users diff --git a/packages/auth/src/infrastructure/repositories/users.repository.ts b/packages/auth/src/infrastructure/repositories/users.repository.ts index dd23fb0..f8b8ecf 100644 --- a/packages/auth/src/infrastructure/repositories/users.repository.ts +++ b/packages/auth/src/infrastructure/repositories/users.repository.ts @@ -12,6 +12,14 @@ import { type User } from "../../entities/models/user"; const FEATURE = "auth" as const; const REPO = "users" as const; +/** + * Every users-collection field this repository reads or writes, besides the + * implicit `id`. Pinned against the collection config by + * `integrations/cms/collections/users.test.ts` so repo <-> collection drift + * fails at test time without a database (audit finding B1). + */ +export const USERS_REPOSITORY_FIELDS = ["username", "passwordHash"] as const; + export class UsersRepository implements IUsersRepository { private config: SanitizedConfig; private tracer: ITracer; @@ -40,7 +48,9 @@ export class UsersRepository implements IUsersRepository { }); const found = Boolean(result); span.setAttribute("found", found); - return result ? this.toDomain(result as Record) : undefined; + return result + ? this.toDomain(result as Record) + : undefined; } catch (err) { if ( err && @@ -54,7 +64,10 @@ export class UsersRepository implements IUsersRepository { this.logger.captureException(err, { tags: { feature: FEATURE, repo: REPO, method: "getUser" }, }); - span.setStatus("error", err instanceof Error ? err.message : String(err)); + span.setStatus( + "error", + err instanceof Error ? err.message : String(err), + ); throw err; } }, @@ -66,7 +79,11 @@ export class UsersRepository implements IUsersRepository { { name: "users.getUserByUsername", op: "repository", - attributes: { emailDomain: username.includes("@") ? (username.split("@")[1] ?? "(invalid)") : username }, + attributes: { + emailDomain: username.includes("@") + ? (username.split("@")[1] ?? "(invalid)") + : username, + }, }, async (span) => { try { @@ -79,12 +96,17 @@ export class UsersRepository implements IUsersRepository { }); const doc = docs[0]; span.setAttribute("found", Boolean(doc)); - return doc ? this.toDomain(doc as Record) : undefined; + return doc + ? this.toDomain(doc as Record) + : undefined; } catch (err) { this.logger.captureException(err, { tags: { feature: FEATURE, repo: REPO, method: "getUserByUsername" }, }); - span.setStatus("error", err instanceof Error ? err.message : String(err)); + span.setStatus( + "error", + err instanceof Error ? err.message : String(err), + ); throw err; } }, @@ -93,7 +115,11 @@ export class UsersRepository implements IUsersRepository { async createUser(input: User): Promise { return this.tracer.startSpan( - { name: "users.createUser", op: "repository", attributes: { id: input.id } }, + { + name: "users.createUser", + op: "repository", + attributes: { id: input.id }, + }, async (span) => { try { const payload = await getPayload({ config: this.config }); @@ -112,7 +138,10 @@ export class UsersRepository implements IUsersRepository { this.logger.captureException(err, { tags: { feature: FEATURE, repo: REPO, method: "createUser" }, }); - span.setStatus("error", err instanceof Error ? err.message : String(err)); + span.setStatus( + "error", + err instanceof Error ? err.message : String(err), + ); throw err; } }, diff --git a/packages/auth/src/integrations/cms/collections/users.test.ts b/packages/auth/src/integrations/cms/collections/users.test.ts new file mode 100644 index 0000000..a408c55 --- /dev/null +++ b/packages/auth/src/integrations/cms/collections/users.test.ts @@ -0,0 +1,59 @@ +import { describe, it, expect } from "vitest"; +import { users } from "@/integrations/cms/collections/users"; +import { USERS_REPOSITORY_FIELDS } from "@/infrastructure/repositories/users.repository"; + +type NamedField = { + name?: string; + type?: string; + required?: boolean; + unique?: boolean; + index?: boolean; + admin?: { hidden?: boolean }; + access?: { read?: (args: unknown) => boolean | Promise }; +}; + +function fieldByName(name: string): NamedField | undefined { + return (users.fields as NamedField[]).find((f) => f.name === name); +} + +// Contract-shaped drift guard (audit finding B1): the production +// UsersRepository reads/writes these fields via the Payload local API, so the +// collection config must declare every one of them. No database needed — +// we parse the collection object directly. +describe("users collection <-> UsersRepository field contract", () => { + it.each([...USERS_REPOSITORY_FIELDS])( + "declares the '%s' field the repository reads/writes", + (name) => { + expect(fieldByName(name)).toBeDefined(); + }, + ); + + it("username is a required, unique, indexed text field", () => { + const username = fieldByName("username"); + expect(username).toMatchObject({ + type: "text", + required: true, + unique: true, + index: true, + }); + }); + + it("passwordHash is required and hidden in the admin UI", () => { + const passwordHash = fieldByName("passwordHash"); + expect(passwordHash).toBeDefined(); + expect(passwordHash!.type).toBe("text"); + expect(passwordHash!.required).toBe(true); + expect(passwordHash!.admin?.hidden).toBe(true); + }); + + it("passwordHash is never readable through the Payload API", async () => { + const passwordHash = fieldByName("passwordHash"); + expect(passwordHash!.access?.read).toBeTypeOf("function"); + // Field-level read access must deny unconditionally — even for admins — + // so the hash never serializes into REST/GraphQL/admin responses. The + // repository bypasses this via the local API's overrideAccess: true. + await expect( + Promise.resolve(passwordHash!.access!.read!({ req: {} })), + ).resolves.toBe(false); + }); +}); diff --git a/packages/auth/src/integrations/cms/collections/users.ts b/packages/auth/src/integrations/cms/collections/users.ts index 7fd69b2..61129e6 100644 --- a/packages/auth/src/integrations/cms/collections/users.ts +++ b/packages/auth/src/integrations/cms/collections/users.ts @@ -18,6 +18,35 @@ export const users: CollectionConfig = { subject: { kind: "self", field: "id" }, }, fields: [ + { + // Read/written by the production UsersRepository (getUserByUsername, + // createUser). Pinned by collections/users.test.ts against + // USERS_REPOSITORY_FIELDS so repo <-> collection drift fails fast. + name: "username", + type: "text", + required: true, + unique: true, + index: true, + custom: { + pii: { + category: "identification-username", + purpose: ["service-delivery"], + exportable: true, + restrictable: true, + }, + }, + }, + { + // Credential material — must never leave the server. `access.read` + // returns false unconditionally so the field is stripped from every + // REST/GraphQL/admin API response; the auth repository still reads it + // through the local API with `overrideAccess: true`. + name: "passwordHash", + type: "text", + required: true, + admin: { hidden: true }, + access: { read: () => false }, + }, { name: "displayName", type: "text", -- 2.49.1 From 27787193c05e50ba3f95bc3a62f371d703a7c9b7 Mon Sep 17 00:00:00 2001 From: Danijel Martinek Date: Fri, 10 Jul 2026 17:21:52 +0200 Subject: [PATCH 19/54] fix(core-testing): align RecordingEventBus with real bus semantics MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Handlers and the published record now receive the zod-PARSED payload (defaults/coercions applied) instead of the raw input, and fan-out uses Promise.allSettled with errors swallowed by default plus an opt-in failFast — matching the InMemoryEventBus the events core-package generator scaffolds. The previous sequential fail-fast divergence was undocumented, so it is aligned rather than kept. Regression tests pin parsed-payload delivery, non-short-circuiting fan-out, and failFast rethrow. (S4; the generator bus templates already carry the fix.) Co-Authored-By: Claude Fable 5 --- .../core-testing/src/instrumentation/index.ts | 5 +- .../recording-event-bus.test.ts | 46 ++++++++++++++++++- .../instrumentation/recording-event-bus.ts | 27 +++++++++-- 3 files changed, 73 insertions(+), 5 deletions(-) diff --git a/packages/core-testing/src/instrumentation/index.ts b/packages/core-testing/src/instrumentation/index.ts index 599c647..a10cd7e 100644 --- a/packages/core-testing/src/instrumentation/index.ts +++ b/packages/core-testing/src/instrumentation/index.ts @@ -2,7 +2,10 @@ export { RecordingTracer, type RecordedSpan } from "./recording-tracer"; export { RecordingLogger, type RecordedCapture } from "./recording-logger"; export { RecordingMetrics, type RecordedMetric } from "./recording-metrics"; export { RecordingJobQueue } from "./recording-job-queue"; -export { RecordingEventBus } from "./recording-event-bus"; +export { + RecordingEventBus, + type RecordingEventBusOptions, +} from "./recording-event-bus"; export { RecordingRealtimeBroadcaster } from "./recording-realtime-broadcaster"; export { RecordingAuditLog } from "./recording-audit-log"; export { diff --git a/packages/core-testing/src/instrumentation/recording-event-bus.test.ts b/packages/core-testing/src/instrumentation/recording-event-bus.test.ts index c330822..33736ad 100644 --- a/packages/core-testing/src/instrumentation/recording-event-bus.test.ts +++ b/packages/core-testing/src/instrumentation/recording-event-bus.test.ts @@ -28,7 +28,7 @@ describe("RecordingEventBus", () => { expect(bus.published).toHaveLength(0); }); - it("invokes registered handlers sequentially in subscription order", async () => { + it("invokes all registered handlers (started in subscription order)", async () => { const bus = new RecordingEventBus(); const order: string[] = []; bus.subscribe(evt, "consumer-a", async () => { @@ -40,4 +40,48 @@ describe("RecordingEventBus", () => { await bus.publish(evt, { id: "x" }); expect(order).toEqual(["a", "b"]); }); + + it("delivers and records the zod-parsed payload, not the raw input", async () => { + const evtNormalized = { + name: "test.normalized" as const, + schema: z.object({ id: z.string(), mode: z.string().catch("auto") }), + }; + const bus = new RecordingEventBus(); + const received: unknown[] = []; + bus.subscribe(evtNormalized, "consumer", async (e) => { + received.push(e); + }); + await bus.publish(evtNormalized, { + id: "x", + mode: 42 as unknown as string, + }); + expect(received).toEqual([{ id: "x", mode: "auto" }]); + expect(bus.published).toEqual([ + { name: "test.normalized", payload: { id: "x", mode: "auto" } }, + ]); + }); + + it("swallows handler errors by default, like the real InMemoryEventBus", async () => { + const bus = new RecordingEventBus(); + const order: string[] = []; + bus.subscribe(evt, "boom", async () => { + throw new Error("subscriber blew up"); + }); + bus.subscribe(evt, "after-boom", async () => { + order.push("after-boom"); + }); + await expect(bus.publish(evt, { id: "x" })).resolves.toBeUndefined(); + expect(order).toEqual(["after-boom"]); // fan-out not short-circuited + expect(bus.published).toHaveLength(1); + }); + + it("rethrows the first handler error when failFast is true", async () => { + const bus = new RecordingEventBus({ failFast: true }); + bus.subscribe(evt, "first", async () => { + throw new Error("first failure"); + }); + await expect(bus.publish(evt, { id: "x" })).rejects.toThrow( + "first failure", + ); + }); }); diff --git a/packages/core-testing/src/instrumentation/recording-event-bus.ts b/packages/core-testing/src/instrumentation/recording-event-bus.ts index 2bcde70..db51c5d 100644 --- a/packages/core-testing/src/instrumentation/recording-event-bus.ts +++ b/packages/core-testing/src/instrumentation/recording-event-bus.ts @@ -22,17 +22,38 @@ interface IEventBus { ): void; } +export type RecordingEventBusOptions = { + /** When true, rethrow the first handler error (default: false — errors swallowed). */ + failFast?: boolean; +}; + +/** + * Recording test double for IEventBus. Delivery semantics deliberately mirror + * the real InMemoryEventBus (@repo/core-events, scaffolded via + * `gen core-package events`): handlers receive the zod-PARSED payload + * (defaults/coercions applied), fan-out uses Promise.allSettled, and handler + * errors are swallowed unless `failFast`. + */ export class RecordingEventBus implements IEventBus { readonly published: { name: string; payload: unknown }[] = []; private readonly handlers = new Map[]>(); + constructor(private readonly options: RecordingEventBusOptions = {}) {} + async publish( descriptor: EventDescriptor>, payload: T, ): Promise { - descriptor.schema.parse(payload); - this.published.push({ name: descriptor.name, payload }); - for (const h of this.handlers.get(descriptor.name) ?? []) await h(payload); + const parsed = descriptor.schema.parse(payload); + this.published.push({ name: descriptor.name, payload: parsed }); + const subscribers = this.handlers.get(descriptor.name) ?? []; + if (subscribers.length === 0) return; + const settled = await Promise.allSettled(subscribers.map((h) => h(parsed))); + if (this.options.failFast) { + const failure = settled.find((s) => s.status === "rejected"); + // Only the first rejection is rethrown — matching InMemoryEventBus. + if (failure && failure.status === "rejected") throw failure.reason; + } } subscribe( -- 2.49.1 From 2747feab465329cf3b6cc360e0eff9a22c10557e Mon Sep 17 00:00:00 2001 From: Danijel Martinek Date: Fri, 10 Jul 2026 17:22:00 +0200 Subject: [PATCH 20/54] fix(auth): stop leaking usernames via emailDomain span attribute MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit getUserByUsername emitted the FULL username as the emailDomain span attribute whenever the username contained no "@" (audit finding B6), violating the PII-free telemetry rule (ADR-017 §7). Emit only a boolean hasAtSign in both the production repository and its mock; regression tests pin that no username-derived string reaches span attributes. Co-Authored-By: Claude Fable 5 --- .../repositories/users.repository.mock.ts | 10 ++++++-- .../users.repository.span.test.ts | 24 +++++++++++++++---- .../repositories/users.repository.ts | 8 +++---- 3 files changed, 30 insertions(+), 12 deletions(-) diff --git a/packages/auth/src/infrastructure/repositories/users.repository.mock.ts b/packages/auth/src/infrastructure/repositories/users.repository.mock.ts index 0f0417a..ca5910c 100644 --- a/packages/auth/src/infrastructure/repositories/users.repository.mock.ts +++ b/packages/auth/src/infrastructure/repositories/users.repository.mock.ts @@ -48,7 +48,9 @@ export class MockUsersRepository implements IUsersRepository { { name: "users.getUserByUsername", op: "repository", - attributes: { emailDomain: username.includes("@") ? (username.split("@")[1] ?? "(invalid)") : username }, + // Never emit the username (or any slice of it) — it is PII and the + // non-email branch used to leak the full username (audit finding B6). + attributes: { hasAtSign: username.includes("@") }, }, async (span) => { const found = this._users.find((u) => u.username === username); @@ -60,7 +62,11 @@ export class MockUsersRepository implements IUsersRepository { async createUser(input: User): Promise { return this.tracer.startSpan( - { name: "users.createUser", op: "repository", attributes: { id: input.id } }, + { + name: "users.createUser", + op: "repository", + attributes: { id: input.id }, + }, async (span) => { this._users.push(input); span.setAttribute("created", true); diff --git a/packages/auth/src/infrastructure/repositories/users.repository.span.test.ts b/packages/auth/src/infrastructure/repositories/users.repository.span.test.ts index d8ae991..d9ef6b6 100644 --- a/packages/auth/src/infrastructure/repositories/users.repository.span.test.ts +++ b/packages/auth/src/infrastructure/repositories/users.repository.span.test.ts @@ -21,17 +21,31 @@ describe("MockUsersRepository emits spans", () => { expect(tracer.spans[0]!.attributes.found).toBe(false); }); - it("getUserByUsername emits a span with emailDomain attribute", async () => { + it("getUserByUsername emits a span without any username-derived PII", async () => { const tracer = new RecordingTracer(); const repo = new MockUsersRepository( [{ id: "1", username: "alice", passwordHash: "hash" }], tracer, ); await repo.getUserByUsername("alice"); - expect(tracer.findSpan("users.getUserByUsername")).toBeDefined(); - expect(tracer.findSpan("users.getUserByUsername")!.attributes.found).toBe( - true, - ); + const span = tracer.findSpan("users.getUserByUsername"); + expect(span).toBeDefined(); + expect(span!.attributes.found).toBe(true); + // B6 regression guard: the old emailDomain attribute leaked the full + // username when it contained no "@". Only a boolean may be emitted. + expect(span!.attributes.emailDomain).toBeUndefined(); + expect(span!.attributes.hasAtSign).toBe(false); + expect(Object.values(span!.attributes)).not.toContain("alice"); + }); + + it("getUserByUsername with an email-shaped username emits only the boolean", async () => { + const tracer = new RecordingTracer(); + const repo = new MockUsersRepository([], tracer); + await repo.getUserByUsername("alice@example.com"); + const span = tracer.findSpan("users.getUserByUsername"); + expect(span!.attributes.hasAtSign).toBe(true); + expect(span!.attributes.emailDomain).toBeUndefined(); + expect(Object.values(span!.attributes)).not.toContain("example.com"); }); it("createUser records created=true", async () => { diff --git a/packages/auth/src/infrastructure/repositories/users.repository.ts b/packages/auth/src/infrastructure/repositories/users.repository.ts index f8b8ecf..eb61db5 100644 --- a/packages/auth/src/infrastructure/repositories/users.repository.ts +++ b/packages/auth/src/infrastructure/repositories/users.repository.ts @@ -79,11 +79,9 @@ export class UsersRepository implements IUsersRepository { { name: "users.getUserByUsername", op: "repository", - attributes: { - emailDomain: username.includes("@") - ? (username.split("@")[1] ?? "(invalid)") - : username, - }, + // Never emit the username (or any slice of it) — it is PII and the + // non-email branch used to leak the full username (audit finding B6). + attributes: { hasAtSign: username.includes("@") }, }, async (span) => { try { -- 2.49.1 From dcbf782e211ebe4ba2e5a64bcbda77e57451462f Mon Sep 17 00:00:00 2001 From: Danijel Martinek Date: Fri, 10 Jul 2026 17:23:06 +0200 Subject: [PATCH 21/54] test(generators): run release-please-utils tests under vitest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The file was doubly dead: the vitest include lacked lib/*.test.mjs and the file imported node:test, so no runner ever executed it. The include now picks up lib .mjs tests and the imports move to vitest (node:assert keeps the original assertion style) — all 5 assertions live. (S5) Co-Authored-By: Claude Fable 5 --- turbo/generators/lib/release-please-utils.test.mjs | 4 +++- turbo/generators/vitest.config.ts | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/turbo/generators/lib/release-please-utils.test.mjs b/turbo/generators/lib/release-please-utils.test.mjs index 85b00cd..3e0c51f 100644 --- a/turbo/generators/lib/release-please-utils.test.mjs +++ b/turbo/generators/lib/release-please-utils.test.mjs @@ -1,4 +1,6 @@ -import { test, describe } from "node:test"; +// Runs under vitest (the generators vitest include picks up lib/*.test.mjs); +// node:assert keeps the original assertion style. +import { test, describe } from "vitest"; import assert from "node:assert/strict"; import fs from "node:fs"; import path from "node:path"; diff --git a/turbo/generators/vitest.config.ts b/turbo/generators/vitest.config.ts index 7248acb..faaec78 100644 --- a/turbo/generators/vitest.config.ts +++ b/turbo/generators/vitest.config.ts @@ -4,7 +4,7 @@ export default defineConfig({ test: { globals: true, environment: "node", - include: ["*.test.ts", "lib/*.test.ts", "__tests__/*.test.ts"], + include: ["*.test.ts", "lib/*.test.{ts,mjs}", "__tests__/*.test.ts"], clearMocks: true, restoreMocks: true, // The core-package `*.e2e.test.ts` files each do a full `pnpm install` -- 2.49.1 From 00fcc9d9a1cf5c068a53cc70376b9ddff758ffce Mon Sep 17 00:00:00 2001 From: Danijel Martinek Date: Fri, 10 Jul 2026 17:24:01 +0200 Subject: [PATCH 22/54] fix(core-shared): set CSP on forwarded request headers for nonce Next.js only injects the nonce into its own scripts when it can read it from the request's Content-Security-Policy header. Setting the CSP only on the response left hydration scripts un-nonced in production (A9). Co-Authored-By: Claude Fable 5 --- .../src/security/next/middleware.test.ts | 28 +++++++++++++++++++ .../src/security/next/middleware.ts | 7 +++++ 2 files changed, 35 insertions(+) diff --git a/packages/core-shared/src/security/next/middleware.test.ts b/packages/core-shared/src/security/next/middleware.test.ts index 2d0a633..76edffa 100644 --- a/packages/core-shared/src/security/next/middleware.test.ts +++ b/packages/core-shared/src/security/next/middleware.test.ts @@ -90,6 +90,34 @@ describe("withSecurityHeaders", () => { expect(call[0]?.request?.headers?.get("x-nonce")).toBeTruthy(); }); + it("sets the CSP on the forwarded request headers (Next reads the nonce from there)", () => { + withSecurityHeaders(makeRequest()); + + const call = vi.mocked(NextResponse.next).mock.calls[0] as [ + { request?: { headers?: Headers } } | undefined, + ]; + const requestCsp = call[0]?.request?.headers?.get( + "Content-Security-Policy", + ); + expect(requestCsp).toBeTruthy(); + expect(requestCsp).toBe(mock._store.get("Content-Security-Policy")); + }); + + it("request-header CSP carries the nonce in production mode", () => { + vi.stubEnv("NODE_ENV", "production"); + + withSecurityHeaders(makeRequest()); + + const call = vi.mocked(NextResponse.next).mock.calls[0] as [ + { request?: { headers?: Headers } } | undefined, + ]; + const requestCsp = call[0]?.request?.headers?.get( + "Content-Security-Policy", + ); + const nonce = call[0]?.request?.headers?.get("x-nonce"); + expect(requestCsp).toContain(`'nonce-${nonce}'`); + }); + it("returns the NextResponse from NextResponse.next", () => { const result = withSecurityHeaders(makeRequest()); diff --git a/packages/core-shared/src/security/next/middleware.ts b/packages/core-shared/src/security/next/middleware.ts index dc4dde4..195c5ea 100644 --- a/packages/core-shared/src/security/next/middleware.ts +++ b/packages/core-shared/src/security/next/middleware.ts @@ -7,9 +7,16 @@ export function withSecurityHeaders(request: NextRequest): NextResponse { const nonce = generateNonce(); const mode = process.env.NODE_ENV === "production" ? "prod" : "dev"; const secHeaders = buildSecurityHeaders({ mode, nonce }); + const csp = secHeaders["Content-Security-Policy"]; const requestHeaders = new Headers(request.headers); requestHeaders.set("x-nonce", nonce); + // Next.js only propagates a nonce to its own