@ttt-productions/repo-guards 0.1.21 → 0.1.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/dist/modules/core-definitions/text-field-declarations.d.ts +4 -1
  2. package/dist/modules/core-definitions/text-field-declarations.d.ts.map +1 -1
  3. package/dist/modules/core-definitions/text-field-declarations.js.map +1 -1
  4. package/dist/modules/import-paths/guard.d.ts.map +1 -1
  5. package/dist/modules/import-paths/guard.js +6 -5
  6. package/dist/modules/import-paths/guard.js.map +1 -1
  7. package/dist/modules/repo/blanket-casts.d.ts +9 -0
  8. package/dist/modules/repo/blanket-casts.d.ts.map +1 -0
  9. package/dist/modules/repo/blanket-casts.js +67 -0
  10. package/dist/modules/repo/blanket-casts.js.map +1 -0
  11. package/dist/modules/repo/e2e-actions.d.ts +17 -0
  12. package/dist/modules/repo/e2e-actions.d.ts.map +1 -0
  13. package/dist/modules/repo/e2e-actions.js +329 -0
  14. package/dist/modules/repo/e2e-actions.js.map +1 -0
  15. package/dist/modules/repo/guards.d.ts +5 -0
  16. package/dist/modules/repo/guards.d.ts.map +1 -1
  17. package/dist/modules/repo/guards.js +5 -0
  18. package/dist/modules/repo/guards.js.map +1 -1
  19. package/dist/node/suite/config.d.ts +4 -0
  20. package/dist/node/suite/config.d.ts.map +1 -1
  21. package/dist/node/suite/config.js +6 -2
  22. package/dist/node/suite/config.js.map +1 -1
  23. package/dist/node/tooling/vitest-report.d.ts +1 -0
  24. package/dist/node/tooling/vitest-report.d.ts.map +1 -1
  25. package/dist/node/tooling/vitest-report.js +14 -1
  26. package/dist/node/tooling/vitest-report.js.map +1 -1
  27. package/dist/support/anchored-review.d.ts +18 -0
  28. package/dist/support/anchored-review.d.ts.map +1 -0
  29. package/dist/support/anchored-review.js +50 -0
  30. package/dist/support/anchored-review.js.map +1 -0
  31. package/package.json +1 -1
@@ -13,6 +13,9 @@ type FieldSchema = {
13
13
  data?: unknown;
14
14
  };
15
15
  };
16
+ interface FieldSchemaOf {
17
+ schemaOf(declaration: unknown): FieldSchema;
18
+ }
16
19
  export interface TextFieldDeclarationFacts {
17
20
  wireFolders: readonly string[];
18
21
  declarationsFile: string;
@@ -24,7 +27,7 @@ export interface TextFieldDeclarationFacts {
24
27
  why: string;
25
28
  }>;
26
29
  declarations: Loader<Readonly<Record<string, unknown>>>;
27
- fieldSchema: Loader<(declaration: never) => FieldSchema>;
30
+ fieldSchema: Loader<FieldSchemaOf['schemaOf']>;
28
31
  }
29
32
  interface Site {
30
33
  key: string;
@@ -1 +1 @@
1
- {"version":3,"file":"text-field-declarations.d.ts","sourceRoot":"","sources":["../../../src/modules/core-definitions/text-field-declarations.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AACzE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAErD,OAAO,EAIL,KAAK,MAAM,EAEZ,MAAM,wBAAwB,CAAC;AAIhC,KAAK,cAAc,GAAG,aAAa,CAAC;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb,CAAC,CAAC;AACH,KAAK,WAAW,GAAG;IAAE,SAAS,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAA;KAAE,CAAA;CAAE,CAAC;AAE3F,MAAM,WAAW,yBAAyB;IACxC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B,gBAAgB,EAAE,MAAM,CAAC;IACzB,qBAAqB,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC,WAAW,EAAE,cAAc,CAAC;IAC5B,kBAAkB,EAAE,cAAc,CAAC;IACnC,WAAW,EAAE,aAAa,CAAC;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACjE,YAAY,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IACxD,WAAW,EAAE,MAAM,CAAC,CAAC,WAAW,EAAE,KAAK,KAAK,WAAW,CAAC,CAAC;CAC1D;AA+BD,UAAU,IAAI;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI,EAAE,CAyBxD;AAED,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,QAAQ,EACd,WAAW,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,GAC5C,IAAI,EAAE,CAiCR;AAsDD,eAAO,MAAM,0BAA0B,EAAE,sBAAsB,CAAC,yBAAyB,CAmKxF,CAAC"}
1
+ {"version":3,"file":"text-field-declarations.d.ts","sourceRoot":"","sources":["../../../src/modules/core-definitions/text-field-declarations.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AACzE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAErD,OAAO,EAIL,KAAK,MAAM,EAEZ,MAAM,wBAAwB,CAAC;AAIhC,KAAK,cAAc,GAAG,aAAa,CAAC;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb,CAAC,CAAC;AACH,KAAK,WAAW,GAAG;IAAE,SAAS,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAA;KAAE,CAAA;CAAE,CAAC;AAI3F,UAAU,aAAa;IACrB,QAAQ,CAAC,WAAW,EAAE,OAAO,GAAG,WAAW,CAAC;CAC7C;AAED,MAAM,WAAW,yBAAyB;IACxC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B,gBAAgB,EAAE,MAAM,CAAC;IACzB,qBAAqB,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC,WAAW,EAAE,cAAc,CAAC;IAC5B,kBAAkB,EAAE,cAAc,CAAC;IACnC,WAAW,EAAE,aAAa,CAAC;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACjE,YAAY,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IACxD,WAAW,EAAE,MAAM,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC,CAAC;CAChD;AA+BD,UAAU,IAAI;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI,EAAE,CAyBxD;AAED,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,QAAQ,EACd,WAAW,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,GAC5C,IAAI,EAAE,CAiCR;AAsDD,eAAO,MAAM,0BAA0B,EAAE,sBAAsB,CAAC,yBAAyB,CAmKxF,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"text-field-declarations.js","sourceRoot":"","sources":["../../../src/modules/core-definitions/text-field-declarations.ts"],"names":[],"mappings":"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","sourcesContent":["import ts from 'typescript';\nimport type { Finding, GuardContext } from '../../guard.js';\nimport type { GenericGuardDefinition } from '../../node/suite/config.js';\nimport type { FileView } from '../../reader/view.js';\nimport { lineOfNode } from '../../support/ast.js';\nimport {\n namesProblem,\n reviewedProblems,\n loaderProblem,\n type Loader,\n textProblem,\n} from '../../support/facts.js';\nimport { CORE_CODE, coreBuild, filesIn, isCount, topLevelDeclaration } from './shared.js';\nimport { REPOSITORY_GUARD_TIMEOUT_MS } from '../../support/timeouts.js';\n\ntype ReviewedCounts = ReadonlyArray<{\n file: string;\n declaration: string;\n count: number;\n why: string;\n}>;\ntype FieldSchema = { safeParse: (value: unknown) => { success: boolean; data?: unknown } };\n\nexport interface TextFieldDeclarationFacts {\n wireFolders: readonly string[];\n declarationsFile: string;\n wireInputNameSuffixes: readonly string[];\n selfBounded: ReviewedCounts;\n undeclaredFreeText: ReviewedCounts;\n keepAsTyped: ReadonlyArray<{ declaration: string; why: string }>;\n declarations: Loader<Readonly<Record<string, unknown>>>;\n fieldSchema: Loader<(declaration: never) => FieldSchema>;\n}\n\nconst isZString = (node: ts.Node): node is ts.CallExpression =>\n ts.isCallExpression(node) &&\n ts.isPropertyAccessExpression(node.expression) &&\n node.expression.name.text === 'string' &&\n ts.isIdentifier(node.expression.expression) &&\n node.expression.expression.text === 'z';\n\nconst TEXT_LENGTH_CONSTANT = /^(MAX|MIN)_(?![A-Z0-9_]*_ID_LENGTH$)[A-Z0-9_]*LENGTH$/;\nconst FREE_TEXT_PROPERTY =\n /reason|message|text|note|comment|title|description|name|subject|caption|summary|statement|facts|letter|label|heading|body|query|content|suggestion|region|country|value|intro|points|strings|term/i;\nconst BUILT_IN_FORMAT_METHODS = new Set(['regex', 'url', 'email', 'uuid']);\n\nfunction chainedCalls(root: ts.CallExpression): ts.CallExpression[] {\n const calls: ts.CallExpression[] = [];\n let node: ts.Node = root;\n while (\n ts.isPropertyAccessExpression(node.parent) &&\n node.parent.expression === node &&\n ts.isCallExpression(node.parent.parent)\n ) {\n calls.push(node.parent.parent);\n node = node.parent.parent;\n }\n return calls;\n}\n\nconst methodOf = (call: ts.CallExpression) =>\n (call.expression as ts.PropertyAccessExpression).name.text;\n\ninterface Site {\n key: string;\n line: number;\n}\n\nexport function selfBoundedChains(file: FileView): Site[] {\n const sites: Site[] = [];\n const visit = (node: ts.Node): void => {\n if (isZString(node)) {\n const bounded = chainedCalls(node).some((call) => {\n const method = methodOf(call);\n if (method === 'trim') return true;\n if (method !== 'min' && method !== 'max') return false;\n const [argument] = call.arguments;\n return (\n argument !== undefined &&\n ts.isIdentifier(argument) &&\n TEXT_LENGTH_CONSTANT.test(argument.text)\n );\n });\n if (bounded)\n sites.push({\n key: `${file.path}#${topLevelDeclaration(node)}`,\n line: lineOfNode(file, node),\n });\n }\n ts.forEachChild(node, visit);\n };\n visit(file.sourceFile);\n return sites;\n}\n\nexport function undeclaredFreeText(\n file: FileView,\n isWireInput: (declaration: string) => boolean,\n): Site[] {\n const sites: Site[] = [];\n const visit = (node: ts.Node): void => {\n if (isZString(node)) {\n const declaration = topLevelDeclaration(node);\n const calls = chainedCalls(node);\n const outer: ts.Node = calls.length ? calls[calls.length - 1] : node;\n const parent = outer.parent;\n let property: string | null = null;\n if (parent && ts.isPropertyAssignment(parent))\n property = parent.name.getText(file.sourceFile);\n else if (\n parent &&\n ts.isCallExpression(parent) &&\n parent.parent &&\n ts.isPropertyAssignment(parent.parent)\n ) {\n property = parent.parent.name.getText(file.sourceFile);\n }\n const builtIn = calls.some((call) => BUILT_IN_FORMAT_METHODS.has(methodOf(call)));\n if (\n property !== null &&\n isWireInput(declaration) &&\n FREE_TEXT_PROPERTY.test(property) &&\n !builtIn\n ) {\n sites.push({ key: `${file.path}#${declaration}`, line: lineOfNode(file, node) });\n }\n }\n ts.forEachChild(node, visit);\n };\n visit(file.sourceFile);\n return sites;\n}\n\nfunction compareReviewed(\n sites: readonly Site[],\n reviewed: ReviewedCounts,\n what: string,\n): Finding[] {\n const findings: Finding[] = [];\n const byKey = new Map<string, Site[]>();\n for (const site of sites) byKey.set(site.key, [...(byKey.get(site.key) ?? []), site]);\n const allowed = new Map(reviewed.map((e) => [`${e.file}#${e.declaration}`, e.count]));\n for (const [key, found] of byKey) {\n const count = allowed.get(key) ?? 0;\n const file = key.slice(0, key.indexOf('#'));\n for (const site of found.slice(count))\n findings.push({\n file,\n line: site.line,\n detail: `${what} in ${key.slice(key.indexOf('#') + 1)}${count ? ` beyond the ${count} reviewed` : ''}`,\n });\n }\n for (const [key, count] of allowed) {\n const found = byKey.get(key)?.length ?? 0;\n if (found < count)\n findings.push({\n file: key.slice(0, key.indexOf('#')),\n detail: `stale reviewed count for ${key}: reviewed ${count}, found ${found}`,\n });\n }\n return findings;\n}\n\ntype Declaration = { format?: unknown; min?: number; keepAsTyped?: unknown };\n\nconst FREE_TEXT_FORMAT = 'none';\nconst textDeclarations = (exports: Readonly<Record<string, unknown>>) =>\n Object.entries(exports).filter(\n (e): e is [string, Declaration] =>\n typeof e[1] === 'object' && e[1] !== null && 'format' in e[1],\n );\n\nconst reviewedCountProblems = (value: unknown, what: string, problems: string[]) =>\n reviewedProblems(value, what, problems, {\n key: 'file',\n id: (e) =>\n typeof e.declaration === 'string' && e.declaration.trim()\n ? `${e.file}#${e.declaration}`\n : undefined,\n idNames: 'declaration',\n extra(entry, label) {\n if (!isCount(entry.count)) problems.push(`${label}'s count must be a positive whole number`);\n },\n });\n\nexport const textFieldDeclarationsGuard: GenericGuardDefinition<TextFieldDeclarationFacts> = {\n id: 'textFieldDeclarations',\n absentWhen: \"the app's core declares no text field through input-format-core\",\n factProblems(facts) {\n const problems: string[] = [];\n namesProblem(facts?.wireFolders, 'wireFolders', problems);\n textProblem(facts?.declarationsFile, 'declarationsFile', problems);\n namesProblem(facts?.wireInputNameSuffixes, 'wireInputNameSuffixes', problems);\n reviewedCountProblems(facts?.selfBounded, 'selfBounded', problems);\n reviewedCountProblems(facts?.undeclaredFreeText, 'undeclaredFreeText', problems);\n reviewedProblems(facts?.keepAsTyped, 'keepAsTyped', problems, { key: 'declaration' });\n loaderProblem(facts?.declarations, 'declarations', problems);\n loaderProblem(facts?.fieldSchema, 'fieldSchema', problems);\n return problems;\n },\n build(facts) {\n const isWireInput = (name: string) => facts.wireInputNameSuffixes.some((s) => name.endsWith(s));\n const wireFiles = (context: GuardContext, findings: Finding[]) =>\n filesIn(context.files(CORE_CODE), facts.wireFolders, findings);\n return [\n {\n title: 'every free-text property of a core wire input is a declared text field',\n description:\n 'A wire input property that names free text (reason, message, title, …) reads its declared text field through ' +\n \"the core's field-schema builder, or carries a built-in format (.regex / .url / .email / .uuid). Wire text no \" +\n 'person types is a reviewed module#declaration count with why; a count that no longer matches fails.',\n timeoutMs: REPOSITORY_GUARD_TIMEOUT_MS,\n run(context) {\n const findings: Finding[] = [];\n const sites = wireFiles(context, findings).flatMap((f) =>\n undeclaredFreeText(f, isWireInput),\n );\n return [\n ...findings,\n ...compareReviewed(\n sites,\n facts.undeclaredFreeText,\n 'an undeclared free-text z.string() property',\n ),\n ];\n },\n },\n {\n title: 'no core wire schema trims or bounds a text field itself',\n description:\n 'A wire z.string() chain never trims itself or bounds itself by a text-length constant: that is a second ' +\n \"declaration of the field. Read the field's declaration through the core's field-schema builder. Wire text no \" +\n 'person types (stored values, fixed strings, ids, addresses) is a reviewed module#declaration count with why.',\n timeoutMs: REPOSITORY_GUARD_TIMEOUT_MS,\n run(context) {\n const findings: Finding[] = [];\n const sites = wireFiles(context, findings).flatMap(selfBoundedChains);\n return [\n ...findings,\n ...compareReviewed(sites, facts.selfBounded, 'a self-bounded z.string() chain'),\n ];\n },\n },\n {\n title: 'text fields are declared only in the core declarations module',\n description:\n 'defineInputFormat is called, and keepAsTyped is set, only in the one module that declares every text field, ' +\n 'so each field has one declaration. Move the declaration there and import it.',\n timeoutMs: REPOSITORY_GUARD_TIMEOUT_MS,\n run(context) {\n const findings: Finding[] = [];\n const files = context.files(CORE_CODE);\n if (!files.some((f) => f.path === facts.declarationsFile)) {\n findings.push({\n file: facts.declarationsFile,\n detail: 'the declarations module is not in the core build',\n });\n }\n for (const file of files) {\n if (file.path === facts.declarationsFile) continue;\n for (const m of file.code.matchAll(/\\bdefineInputFormat\\s*\\(|\\bkeepAsTyped\\b/g)) {\n findings.push({\n file: file.path,\n line: file.lineOf(m.index ?? 0),\n detail: `${m[0].replace(/\\s*\\($/, '')} outside the declarations module`,\n });\n }\n }\n return findings;\n },\n },\n {\n title: 'only the reviewed text fields keep their text as typed',\n description:\n 'Every declared text field is trimmed unless it is a reviewed keep-as-typed declaration with why (a legal or ' +\n 'statutory text whose exact characters matter). A declaration that newly keeps its text fails until reviewed, ' +\n 'and a reviewed one that no longer does is stale.',\n async run(context) {\n const findings: Finding[] = [];\n coreBuild(context, findings);\n const kept = new Set(\n textDeclarations(await facts.declarations())\n .filter(([, d]) => 'keepAsTyped' in d)\n .map(([n]) => n),\n );\n const reviewed = new Set(facts.keepAsTyped.map((e) => e.declaration));\n for (const name of [...kept].sort())\n if (!reviewed.has(name))\n findings.push({\n file: `(core) ${name}`,\n detail: 'keeps its text as typed without review',\n });\n for (const name of reviewed)\n if (!kept.has(name))\n findings.push({\n file: `(core) ${name}`,\n detail:\n 'stale keep-as-typed entry: the declaration no longer keeps its text, or is gone',\n });\n return findings;\n },\n },\n {\n title:\n 'every declared text field trims, one that holds words keeps one space for each run of spaces, and a required one refuses blank text',\n description:\n \"Each text field's wire schema, built from its declaration, refuses whitespace-only text when the field requires \" +\n 'text (min of 1 or more), and trims the text of every field that does not keep it as typed; a field whose format ' +\n \"holds words (every format but free text, 'none') keeps one space for a run of them — so the form, the wire, and the writer agree, and a \" +\n 'name spaced twice is never stored, searched, or reserved apart from the same name spaced once.',\n async run(context) {\n const findings: Finding[] = [];\n coreBuild(context, findings);\n const declarations = textDeclarations(await facts.declarations());\n const schemaOf = await facts.fieldSchema();\n if (!declarations.length)\n findings.push({\n file: facts.declarationsFile,\n detail: 'no text-field declaration was loaded',\n });\n for (const [name, declaration] of declarations) {\n const schema = schemaOf(declaration as never);\n const min = typeof declaration.min === 'number' ? declaration.min : 0;\n if (min >= 1 && schema.safeParse(' \\n\\t ').success)\n findings.push({\n file: `(core) ${name}`,\n detail: 'accepts whitespace-only text although the field requires text',\n });\n if ('keepAsTyped' in declaration) continue;\n const sample = 'a'.repeat(Math.max(min, 1));\n const parsed = schema.safeParse(` ${sample} `);\n if (!parsed.success || parsed.data !== sample)\n findings.push({ file: `(core) ${name}`, detail: 'does not trim its text' });\n if (declaration.format === FREE_TEXT_FORMAT) continue;\n const word = 'a'.repeat(Math.max(min - 2, 1));\n if (!schema.safeParse(`${word} a`).success) continue;\n const run = schema.safeParse(`${word} a`);\n if (!run.success || run.data !== `${word} a`)\n findings.push({\n file: `(core) ${name}`,\n detail: 'keeps a run of spaces instead of one space',\n });\n }\n return findings;\n },\n },\n ];\n },\n};\n"]}
1
+ {"version":3,"file":"text-field-declarations.js","sourceRoot":"","sources":["../../../src/modules/core-definitions/text-field-declarations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAI5B,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,EACL,YAAY,EACZ,gBAAgB,EAChB,aAAa,EAEb,WAAW,GACZ,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAC1F,OAAO,EAAE,2BAA2B,EAAE,MAAM,2BAA2B,CAAC;AA2BxE,MAAM,SAAS,GAAG,CAAC,IAAa,EAA6B,EAAE,CAC7D,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC;IACzB,EAAE,CAAC,0BAA0B,CAAC,IAAI,CAAC,UAAU,CAAC;IAC9C,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,KAAK,QAAQ;IACtC,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC;IAC3C,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,IAAI,KAAK,GAAG,CAAC;AAE1C,MAAM,oBAAoB,GAAG,uDAAuD,CAAC;AACrF,MAAM,kBAAkB,GACtB,oMAAoM,CAAC;AACvM,MAAM,uBAAuB,GAAG,IAAI,GAAG,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;AAE3E,SAAS,YAAY,CAAC,IAAuB;IAC3C,MAAM,KAAK,GAAwB,EAAE,CAAC;IACtC,IAAI,IAAI,GAAY,IAAI,CAAC;IACzB,OACE,EAAE,CAAC,0BAA0B,CAAC,IAAI,CAAC,MAAM,CAAC;QAC1C,IAAI,CAAC,MAAM,CAAC,UAAU,KAAK,IAAI;QAC/B,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,EACvC,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC/B,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC;IAC5B,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,QAAQ,GAAG,CAAC,IAAuB,EAAE,EAAE,CAC1C,IAAI,CAAC,UAA0C,CAAC,IAAI,CAAC,IAAI,CAAC;AAO7D,MAAM,UAAU,iBAAiB,CAAC,IAAc;IAC9C,MAAM,KAAK,GAAW,EAAE,CAAC;IACzB,MAAM,KAAK,GAAG,CAAC,IAAa,EAAQ,EAAE;QACpC,IAAI,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;YACpB,MAAM,OAAO,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE;gBAC/C,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;gBAC9B,IAAI,MAAM,KAAK,MAAM;oBAAE,OAAO,IAAI,CAAC;gBACnC,IAAI,MAAM,KAAK,KAAK,IAAI,MAAM,KAAK,KAAK;oBAAE,OAAO,KAAK,CAAC;gBACvD,MAAM,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC;gBAClC,OAAO,CACL,QAAQ,KAAK,SAAS;oBACtB,EAAE,CAAC,YAAY,CAAC,QAAQ,CAAC;oBACzB,oBAAoB,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CACzC,CAAC;YACJ,CAAC,CAAC,CAAC;YACH,IAAI,OAAO;gBACT,KAAK,CAAC,IAAI,CAAC;oBACT,GAAG,EAAE,GAAG,IAAI,CAAC,IAAI,IAAI,mBAAmB,CAAC,IAAI,CAAC,EAAE;oBAChD,IAAI,EAAE,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC;iBAC7B,CAAC,CAAC;QACP,CAAC;QACD,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC/B,CAAC,CAAC;IACF,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACvB,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,kBAAkB,CAChC,IAAc,EACd,WAA6C;IAE7C,MAAM,KAAK,GAAW,EAAE,CAAC;IACzB,MAAM,KAAK,GAAG,CAAC,IAAa,EAAQ,EAAE;QACpC,IAAI,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;YACpB,MAAM,WAAW,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAC;YAC9C,MAAM,KAAK,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;YACjC,MAAM,KAAK,GAAY,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;YACrE,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;YAC5B,IAAI,QAAQ,GAAkB,IAAI,CAAC;YACnC,IAAI,MAAM,IAAI,EAAE,CAAC,oBAAoB,CAAC,MAAM,CAAC;gBAC3C,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;iBAC7C,IACH,MAAM;gBACN,EAAE,CAAC,gBAAgB,CAAC,MAAM,CAAC;gBAC3B,MAAM,CAAC,MAAM;gBACb,EAAE,CAAC,oBAAoB,CAAC,MAAM,CAAC,MAAM,CAAC,EACtC,CAAC;gBACD,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YACzD,CAAC;YACD,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,uBAAuB,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClF,IACE,QAAQ,KAAK,IAAI;gBACjB,WAAW,CAAC,WAAW,CAAC;gBACxB,kBAAkB,CAAC,IAAI,CAAC,QAAQ,CAAC;gBACjC,CAAC,OAAO,EACR,CAAC;gBACD,KAAK,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,IAAI,IAAI,WAAW,EAAE,EAAE,IAAI,EAAE,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;YACnF,CAAC;QACH,CAAC;QACD,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC/B,CAAC,CAAC;IACF,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACvB,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,eAAe,CACtB,KAAsB,EACtB,QAAwB,EACxB,IAAY;IAEZ,MAAM,QAAQ,GAAc,EAAE,CAAC;IAC/B,MAAM,KAAK,GAAG,IAAI,GAAG,EAAkB,CAAC;IACxC,KAAK,MAAM,IAAI,IAAI,KAAK;QAAE,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;IACtF,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACtF,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,KAAK,EAAE,CAAC;QACjC,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACpC,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;QAC5C,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC;YACnC,QAAQ,CAAC,IAAI,CAAC;gBACZ,IAAI;gBACJ,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,MAAM,EAAE,GAAG,IAAI,OAAO,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,eAAe,KAAK,WAAW,CAAC,CAAC,CAAC,EAAE,EAAE;aACvG,CAAC,CAAC;IACP,CAAC;IACD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,OAAO,EAAE,CAAC;QACnC,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC,CAAC;QAC1C,IAAI,KAAK,GAAG,KAAK;YACf,QAAQ,CAAC,IAAI,CAAC;gBACZ,IAAI,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;gBACpC,MAAM,EAAE,4BAA4B,GAAG,cAAc,KAAK,WAAW,KAAK,EAAE;aAC7E,CAAC,CAAC;IACP,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAID,MAAM,gBAAgB,GAAG,MAAM,CAAC;AAChC,MAAM,gBAAgB,GAAG,CAAC,OAA0C,EAAE,EAAE,CACtE,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,MAAM,CAC5B,CAAC,CAAC,EAA8B,EAAE,CAChC,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI,QAAQ,IAAI,CAAC,CAAC,CAAC,CAAC,CAChE,CAAC;AAEJ,MAAM,qBAAqB,GAAG,CAAC,KAAc,EAAE,IAAY,EAAE,QAAkB,EAAE,EAAE,CACjF,gBAAgB,CAAC,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE;IACtC,GAAG,EAAE,MAAM;IACX,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,CACR,OAAO,CAAC,CAAC,WAAW,KAAK,QAAQ,IAAI,CAAC,CAAC,WAAW,CAAC,IAAI,EAAE;QACvD,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,WAAW,EAAE;QAC9B,CAAC,CAAC,SAAS;IACf,OAAO,EAAE,aAAa;IACtB,KAAK,CAAC,KAAK,EAAE,KAAK;QAChB,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC;YAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,KAAK,0CAA0C,CAAC,CAAC;IAC/F,CAAC;CACF,CAAC,CAAC;AAEL,MAAM,CAAC,MAAM,0BAA0B,GAAsD;IAC3F,EAAE,EAAE,uBAAuB;IAC3B,UAAU,EAAE,iEAAiE;IAC7E,YAAY,CAAC,KAAK;QAChB,MAAM,QAAQ,GAAa,EAAE,CAAC;QAC9B,YAAY,CAAC,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,QAAQ,CAAC,CAAC;QAC1D,WAAW,CAAC,KAAK,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,QAAQ,CAAC,CAAC;QACnE,YAAY,CAAC,KAAK,EAAE,qBAAqB,EAAE,uBAAuB,EAAE,QAAQ,CAAC,CAAC;QAC9E,qBAAqB,CAAC,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,QAAQ,CAAC,CAAC;QACnE,qBAAqB,CAAC,KAAK,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,QAAQ,CAAC,CAAC;QACjF,gBAAgB,CAAC,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,QAAQ,EAAE,EAAE,GAAG,EAAE,aAAa,EAAE,CAAC,CAAC;QACtF,aAAa,CAAC,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,QAAQ,CAAC,CAAC;QAC7D,aAAa,CAAC,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,QAAQ,CAAC,CAAC;QAC3D,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,KAAK,CAAC,KAAK;QACT,MAAM,WAAW,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,KAAK,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;QAChG,MAAM,SAAS,GAAG,CAAC,OAAqB,EAAE,QAAmB,EAAE,EAAE,CAC/D,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,KAAK,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;QACjE,OAAO;YACL;gBACE,KAAK,EAAE,wEAAwE;gBAC/E,WAAW,EACT,+GAA+G;oBAC/G,+GAA+G;oBAC/G,qGAAqG;gBACvG,SAAS,EAAE,2BAA2B;gBACtC,GAAG,CAAC,OAAO;oBACT,MAAM,QAAQ,GAAc,EAAE,CAAC;oBAC/B,MAAM,KAAK,GAAG,SAAS,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CACvD,kBAAkB,CAAC,CAAC,EAAE,WAAW,CAAC,CACnC,CAAC;oBACF,OAAO;wBACL,GAAG,QAAQ;wBACX,GAAG,eAAe,CAChB,KAAK,EACL,KAAK,CAAC,kBAAkB,EACxB,6CAA6C,CAC9C;qBACF,CAAC;gBACJ,CAAC;aACF;YACD;gBACE,KAAK,EAAE,yDAAyD;gBAChE,WAAW,EACT,0GAA0G;oBAC1G,+GAA+G;oBAC/G,8GAA8G;gBAChH,SAAS,EAAE,2BAA2B;gBACtC,GAAG,CAAC,OAAO;oBACT,MAAM,QAAQ,GAAc,EAAE,CAAC;oBAC/B,MAAM,KAAK,GAAG,SAAS,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC;oBACtE,OAAO;wBACL,GAAG,QAAQ;wBACX,GAAG,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC,WAAW,EAAE,iCAAiC,CAAC;qBAChF,CAAC;gBACJ,CAAC;aACF;YACD;gBACE,KAAK,EAAE,+DAA+D;gBACtE,WAAW,EACT,8GAA8G;oBAC9G,8EAA8E;gBAChF,SAAS,EAAE,2BAA2B;gBACtC,GAAG,CAAC,OAAO;oBACT,MAAM,QAAQ,GAAc,EAAE,CAAC;oBAC/B,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;oBACvC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,KAAK,CAAC,gBAAgB,CAAC,EAAE,CAAC;wBAC1D,QAAQ,CAAC,IAAI,CAAC;4BACZ,IAAI,EAAE,KAAK,CAAC,gBAAgB;4BAC5B,MAAM,EAAE,kDAAkD;yBAC3D,CAAC,CAAC;oBACL,CAAC;oBACD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;wBACzB,IAAI,IAAI,CAAC,IAAI,KAAK,KAAK,CAAC,gBAAgB;4BAAE,SAAS;wBACnD,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,2CAA2C,CAAC,EAAE,CAAC;4BAChF,QAAQ,CAAC,IAAI,CAAC;gCACZ,IAAI,EAAE,IAAI,CAAC,IAAI;gCACf,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC;gCAC/B,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,kCAAkC;6BACxE,CAAC,CAAC;wBACL,CAAC;oBACH,CAAC;oBACD,OAAO,QAAQ,CAAC;gBAClB,CAAC;aACF;YACD;gBACE,KAAK,EAAE,wDAAwD;gBAC/D,WAAW,EACT,8GAA8G;oBAC9G,+GAA+G;oBAC/G,kDAAkD;gBACpD,KAAK,CAAC,GAAG,CAAC,OAAO;oBACf,MAAM,QAAQ,GAAc,EAAE,CAAC;oBAC/B,SAAS,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;oBAC7B,MAAM,IAAI,GAAG,IAAI,GAAG,CAClB,gBAAgB,CAAC,MAAM,KAAK,CAAC,YAAY,EAAE,CAAC;yBACzC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,aAAa,IAAI,CAAC,CAAC;yBACrC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CACnB,CAAC;oBACF,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC;oBACtE,KAAK,MAAM,IAAI,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC,IAAI,EAAE;wBACjC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC;4BACrB,QAAQ,CAAC,IAAI,CAAC;gCACZ,IAAI,EAAE,UAAU,IAAI,EAAE;gCACtB,MAAM,EAAE,wCAAwC;6BACjD,CAAC,CAAC;oBACP,KAAK,MAAM,IAAI,IAAI,QAAQ;wBACzB,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC;4BACjB,QAAQ,CAAC,IAAI,CAAC;gCACZ,IAAI,EAAE,UAAU,IAAI,EAAE;gCACtB,MAAM,EACJ,iFAAiF;6BACpF,CAAC,CAAC;oBACP,OAAO,QAAQ,CAAC;gBAClB,CAAC;aACF;YACD;gBACE,KAAK,EACH,qIAAqI;gBACvI,WAAW,EACT,kHAAkH;oBAClH,kHAAkH;oBAClH,0IAA0I;oBAC1I,gGAAgG;gBAClG,KAAK,CAAC,GAAG,CAAC,OAAO;oBACf,MAAM,QAAQ,GAAc,EAAE,CAAC;oBAC/B,SAAS,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;oBAC7B,MAAM,YAAY,GAAG,gBAAgB,CAAC,MAAM,KAAK,CAAC,YAAY,EAAE,CAAC,CAAC;oBAClE,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,WAAW,EAAE,CAAC;oBAC3C,IAAI,CAAC,YAAY,CAAC,MAAM;wBACtB,QAAQ,CAAC,IAAI,CAAC;4BACZ,IAAI,EAAE,KAAK,CAAC,gBAAgB;4BAC5B,MAAM,EAAE,sCAAsC;yBAC/C,CAAC,CAAC;oBACL,KAAK,MAAM,CAAC,IAAI,EAAE,WAAW,CAAC,IAAI,YAAY,EAAE,CAAC;wBAC/C,MAAM,MAAM,GAAG,QAAQ,CAAC,WAAW,CAAC,CAAC;wBACrC,MAAM,GAAG,GAAG,OAAO,WAAW,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;wBACtE,IAAI,GAAG,IAAI,CAAC,IAAI,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,OAAO;4BAChD,QAAQ,CAAC,IAAI,CAAC;gCACZ,IAAI,EAAE,UAAU,IAAI,EAAE;gCACtB,MAAM,EAAE,+DAA+D;6BACxE,CAAC,CAAC;wBACL,IAAI,aAAa,IAAI,WAAW;4BAAE,SAAS;wBAC3C,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC;wBAC5C,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,KAAK,MAAM,IAAI,CAAC,CAAC;wBACjD,IAAI,CAAC,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM;4BAC3C,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,UAAU,IAAI,EAAE,EAAE,MAAM,EAAE,wBAAwB,EAAE,CAAC,CAAC;wBAC9E,IAAI,WAAW,CAAC,MAAM,KAAK,gBAAgB;4BAAE,SAAS;wBACtD,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;wBAC9C,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,GAAG,IAAI,IAAI,CAAC,CAAC,OAAO;4BAAE,SAAS;wBACrD,MAAM,GAAG,GAAG,MAAM,CAAC,SAAS,CAAC,GAAG,IAAI,KAAK,CAAC,CAAC;wBAC3C,IAAI,CAAC,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,IAAI,KAAK,GAAG,IAAI,IAAI;4BAC1C,QAAQ,CAAC,IAAI,CAAC;gCACZ,IAAI,EAAE,UAAU,IAAI,EAAE;gCACtB,MAAM,EAAE,4CAA4C;6BACrD,CAAC,CAAC;oBACP,CAAC;oBACD,OAAO,QAAQ,CAAC;gBAClB,CAAC;aACF;SACF,CAAC;IACJ,CAAC;CACF,CAAC","sourcesContent":["import ts from 'typescript';\nimport type { Finding, GuardContext } from '../../guard.js';\nimport type { GenericGuardDefinition } from '../../node/suite/config.js';\nimport type { FileView } from '../../reader/view.js';\nimport { lineOfNode } from '../../support/ast.js';\nimport {\n namesProblem,\n reviewedProblems,\n loaderProblem,\n type Loader,\n textProblem,\n} from '../../support/facts.js';\nimport { CORE_CODE, coreBuild, filesIn, isCount, topLevelDeclaration } from './shared.js';\nimport { REPOSITORY_GUARD_TIMEOUT_MS } from '../../support/timeouts.js';\n\ntype ReviewedCounts = ReadonlyArray<{\n file: string;\n declaration: string;\n count: number;\n why: string;\n}>;\ntype FieldSchema = { safeParse: (value: unknown) => { success: boolean; data?: unknown } };\n\n// A method, so the app's own reader — typed on its declaration type — fits it as declared, and the\n// guard hands it the declarations it loaded from that same module.\ninterface FieldSchemaOf {\n schemaOf(declaration: unknown): FieldSchema;\n}\n\nexport interface TextFieldDeclarationFacts {\n wireFolders: readonly string[];\n declarationsFile: string;\n wireInputNameSuffixes: readonly string[];\n selfBounded: ReviewedCounts;\n undeclaredFreeText: ReviewedCounts;\n keepAsTyped: ReadonlyArray<{ declaration: string; why: string }>;\n declarations: Loader<Readonly<Record<string, unknown>>>;\n fieldSchema: Loader<FieldSchemaOf['schemaOf']>;\n}\n\nconst isZString = (node: ts.Node): node is ts.CallExpression =>\n ts.isCallExpression(node) &&\n ts.isPropertyAccessExpression(node.expression) &&\n node.expression.name.text === 'string' &&\n ts.isIdentifier(node.expression.expression) &&\n node.expression.expression.text === 'z';\n\nconst TEXT_LENGTH_CONSTANT = /^(MAX|MIN)_(?![A-Z0-9_]*_ID_LENGTH$)[A-Z0-9_]*LENGTH$/;\nconst FREE_TEXT_PROPERTY =\n /reason|message|text|note|comment|title|description|name|subject|caption|summary|statement|facts|letter|label|heading|body|query|content|suggestion|region|country|value|intro|points|strings|term/i;\nconst BUILT_IN_FORMAT_METHODS = new Set(['regex', 'url', 'email', 'uuid']);\n\nfunction chainedCalls(root: ts.CallExpression): ts.CallExpression[] {\n const calls: ts.CallExpression[] = [];\n let node: ts.Node = root;\n while (\n ts.isPropertyAccessExpression(node.parent) &&\n node.parent.expression === node &&\n ts.isCallExpression(node.parent.parent)\n ) {\n calls.push(node.parent.parent);\n node = node.parent.parent;\n }\n return calls;\n}\n\nconst methodOf = (call: ts.CallExpression) =>\n (call.expression as ts.PropertyAccessExpression).name.text;\n\ninterface Site {\n key: string;\n line: number;\n}\n\nexport function selfBoundedChains(file: FileView): Site[] {\n const sites: Site[] = [];\n const visit = (node: ts.Node): void => {\n if (isZString(node)) {\n const bounded = chainedCalls(node).some((call) => {\n const method = methodOf(call);\n if (method === 'trim') return true;\n if (method !== 'min' && method !== 'max') return false;\n const [argument] = call.arguments;\n return (\n argument !== undefined &&\n ts.isIdentifier(argument) &&\n TEXT_LENGTH_CONSTANT.test(argument.text)\n );\n });\n if (bounded)\n sites.push({\n key: `${file.path}#${topLevelDeclaration(node)}`,\n line: lineOfNode(file, node),\n });\n }\n ts.forEachChild(node, visit);\n };\n visit(file.sourceFile);\n return sites;\n}\n\nexport function undeclaredFreeText(\n file: FileView,\n isWireInput: (declaration: string) => boolean,\n): Site[] {\n const sites: Site[] = [];\n const visit = (node: ts.Node): void => {\n if (isZString(node)) {\n const declaration = topLevelDeclaration(node);\n const calls = chainedCalls(node);\n const outer: ts.Node = calls.length ? calls[calls.length - 1] : node;\n const parent = outer.parent;\n let property: string | null = null;\n if (parent && ts.isPropertyAssignment(parent))\n property = parent.name.getText(file.sourceFile);\n else if (\n parent &&\n ts.isCallExpression(parent) &&\n parent.parent &&\n ts.isPropertyAssignment(parent.parent)\n ) {\n property = parent.parent.name.getText(file.sourceFile);\n }\n const builtIn = calls.some((call) => BUILT_IN_FORMAT_METHODS.has(methodOf(call)));\n if (\n property !== null &&\n isWireInput(declaration) &&\n FREE_TEXT_PROPERTY.test(property) &&\n !builtIn\n ) {\n sites.push({ key: `${file.path}#${declaration}`, line: lineOfNode(file, node) });\n }\n }\n ts.forEachChild(node, visit);\n };\n visit(file.sourceFile);\n return sites;\n}\n\nfunction compareReviewed(\n sites: readonly Site[],\n reviewed: ReviewedCounts,\n what: string,\n): Finding[] {\n const findings: Finding[] = [];\n const byKey = new Map<string, Site[]>();\n for (const site of sites) byKey.set(site.key, [...(byKey.get(site.key) ?? []), site]);\n const allowed = new Map(reviewed.map((e) => [`${e.file}#${e.declaration}`, e.count]));\n for (const [key, found] of byKey) {\n const count = allowed.get(key) ?? 0;\n const file = key.slice(0, key.indexOf('#'));\n for (const site of found.slice(count))\n findings.push({\n file,\n line: site.line,\n detail: `${what} in ${key.slice(key.indexOf('#') + 1)}${count ? ` beyond the ${count} reviewed` : ''}`,\n });\n }\n for (const [key, count] of allowed) {\n const found = byKey.get(key)?.length ?? 0;\n if (found < count)\n findings.push({\n file: key.slice(0, key.indexOf('#')),\n detail: `stale reviewed count for ${key}: reviewed ${count}, found ${found}`,\n });\n }\n return findings;\n}\n\ntype Declaration = { format?: unknown; min?: number; keepAsTyped?: unknown };\n\nconst FREE_TEXT_FORMAT = 'none';\nconst textDeclarations = (exports: Readonly<Record<string, unknown>>) =>\n Object.entries(exports).filter(\n (e): e is [string, Declaration] =>\n typeof e[1] === 'object' && e[1] !== null && 'format' in e[1],\n );\n\nconst reviewedCountProblems = (value: unknown, what: string, problems: string[]) =>\n reviewedProblems(value, what, problems, {\n key: 'file',\n id: (e) =>\n typeof e.declaration === 'string' && e.declaration.trim()\n ? `${e.file}#${e.declaration}`\n : undefined,\n idNames: 'declaration',\n extra(entry, label) {\n if (!isCount(entry.count)) problems.push(`${label}'s count must be a positive whole number`);\n },\n });\n\nexport const textFieldDeclarationsGuard: GenericGuardDefinition<TextFieldDeclarationFacts> = {\n id: 'textFieldDeclarations',\n absentWhen: \"the app's core declares no text field through input-format-core\",\n factProblems(facts) {\n const problems: string[] = [];\n namesProblem(facts?.wireFolders, 'wireFolders', problems);\n textProblem(facts?.declarationsFile, 'declarationsFile', problems);\n namesProblem(facts?.wireInputNameSuffixes, 'wireInputNameSuffixes', problems);\n reviewedCountProblems(facts?.selfBounded, 'selfBounded', problems);\n reviewedCountProblems(facts?.undeclaredFreeText, 'undeclaredFreeText', problems);\n reviewedProblems(facts?.keepAsTyped, 'keepAsTyped', problems, { key: 'declaration' });\n loaderProblem(facts?.declarations, 'declarations', problems);\n loaderProblem(facts?.fieldSchema, 'fieldSchema', problems);\n return problems;\n },\n build(facts) {\n const isWireInput = (name: string) => facts.wireInputNameSuffixes.some((s) => name.endsWith(s));\n const wireFiles = (context: GuardContext, findings: Finding[]) =>\n filesIn(context.files(CORE_CODE), facts.wireFolders, findings);\n return [\n {\n title: 'every free-text property of a core wire input is a declared text field',\n description:\n 'A wire input property that names free text (reason, message, title, …) reads its declared text field through ' +\n \"the core's field-schema builder, or carries a built-in format (.regex / .url / .email / .uuid). Wire text no \" +\n 'person types is a reviewed module#declaration count with why; a count that no longer matches fails.',\n timeoutMs: REPOSITORY_GUARD_TIMEOUT_MS,\n run(context) {\n const findings: Finding[] = [];\n const sites = wireFiles(context, findings).flatMap((f) =>\n undeclaredFreeText(f, isWireInput),\n );\n return [\n ...findings,\n ...compareReviewed(\n sites,\n facts.undeclaredFreeText,\n 'an undeclared free-text z.string() property',\n ),\n ];\n },\n },\n {\n title: 'no core wire schema trims or bounds a text field itself',\n description:\n 'A wire z.string() chain never trims itself or bounds itself by a text-length constant: that is a second ' +\n \"declaration of the field. Read the field's declaration through the core's field-schema builder. Wire text no \" +\n 'person types (stored values, fixed strings, ids, addresses) is a reviewed module#declaration count with why.',\n timeoutMs: REPOSITORY_GUARD_TIMEOUT_MS,\n run(context) {\n const findings: Finding[] = [];\n const sites = wireFiles(context, findings).flatMap(selfBoundedChains);\n return [\n ...findings,\n ...compareReviewed(sites, facts.selfBounded, 'a self-bounded z.string() chain'),\n ];\n },\n },\n {\n title: 'text fields are declared only in the core declarations module',\n description:\n 'defineInputFormat is called, and keepAsTyped is set, only in the one module that declares every text field, ' +\n 'so each field has one declaration. Move the declaration there and import it.',\n timeoutMs: REPOSITORY_GUARD_TIMEOUT_MS,\n run(context) {\n const findings: Finding[] = [];\n const files = context.files(CORE_CODE);\n if (!files.some((f) => f.path === facts.declarationsFile)) {\n findings.push({\n file: facts.declarationsFile,\n detail: 'the declarations module is not in the core build',\n });\n }\n for (const file of files) {\n if (file.path === facts.declarationsFile) continue;\n for (const m of file.code.matchAll(/\\bdefineInputFormat\\s*\\(|\\bkeepAsTyped\\b/g)) {\n findings.push({\n file: file.path,\n line: file.lineOf(m.index ?? 0),\n detail: `${m[0].replace(/\\s*\\($/, '')} outside the declarations module`,\n });\n }\n }\n return findings;\n },\n },\n {\n title: 'only the reviewed text fields keep their text as typed',\n description:\n 'Every declared text field is trimmed unless it is a reviewed keep-as-typed declaration with why (a legal or ' +\n 'statutory text whose exact characters matter). A declaration that newly keeps its text fails until reviewed, ' +\n 'and a reviewed one that no longer does is stale.',\n async run(context) {\n const findings: Finding[] = [];\n coreBuild(context, findings);\n const kept = new Set(\n textDeclarations(await facts.declarations())\n .filter(([, d]) => 'keepAsTyped' in d)\n .map(([n]) => n),\n );\n const reviewed = new Set(facts.keepAsTyped.map((e) => e.declaration));\n for (const name of [...kept].sort())\n if (!reviewed.has(name))\n findings.push({\n file: `(core) ${name}`,\n detail: 'keeps its text as typed without review',\n });\n for (const name of reviewed)\n if (!kept.has(name))\n findings.push({\n file: `(core) ${name}`,\n detail:\n 'stale keep-as-typed entry: the declaration no longer keeps its text, or is gone',\n });\n return findings;\n },\n },\n {\n title:\n 'every declared text field trims, one that holds words keeps one space for each run of spaces, and a required one refuses blank text',\n description:\n \"Each text field's wire schema, built from its declaration, refuses whitespace-only text when the field requires \" +\n 'text (min of 1 or more), and trims the text of every field that does not keep it as typed; a field whose format ' +\n \"holds words (every format but free text, 'none') keeps one space for a run of them — so the form, the wire, and the writer agree, and a \" +\n 'name spaced twice is never stored, searched, or reserved apart from the same name spaced once.',\n async run(context) {\n const findings: Finding[] = [];\n coreBuild(context, findings);\n const declarations = textDeclarations(await facts.declarations());\n const schemaOf = await facts.fieldSchema();\n if (!declarations.length)\n findings.push({\n file: facts.declarationsFile,\n detail: 'no text-field declaration was loaded',\n });\n for (const [name, declaration] of declarations) {\n const schema = schemaOf(declaration);\n const min = typeof declaration.min === 'number' ? declaration.min : 0;\n if (min >= 1 && schema.safeParse(' \\n\\t ').success)\n findings.push({\n file: `(core) ${name}`,\n detail: 'accepts whitespace-only text although the field requires text',\n });\n if ('keepAsTyped' in declaration) continue;\n const sample = 'a'.repeat(Math.max(min, 1));\n const parsed = schema.safeParse(` ${sample} `);\n if (!parsed.success || parsed.data !== sample)\n findings.push({ file: `(core) ${name}`, detail: 'does not trim its text' });\n if (declaration.format === FREE_TEXT_FORMAT) continue;\n const word = 'a'.repeat(Math.max(min - 2, 1));\n if (!schema.safeParse(`${word} a`).success) continue;\n const run = schema.safeParse(`${word} a`);\n if (!run.success || run.data !== `${word} a`)\n findings.push({\n file: `(core) ${name}`,\n detail: 'keeps a run of spaces instead of one space',\n });\n }\n return findings;\n },\n },\n ];\n },\n};\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"guard.d.ts","sourceRoot":"","sources":["../../../src/modules/import-paths/guard.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAS,MAAM,sBAAsB,CAAC;AAE9D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AAKzE,MAAM,WAAW,eAAe;IAC9B,aAAa,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAChD,UAAU,EAAE,aAAa,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC1D;AAsCD,eAAO,MAAM,eAAe,EAAE,sBAAsB,CAAC,eAAe,CAiCnE,CAAC;AAEF,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,UAAU,EAClB,KAAK,EAAE,eAAe,GACrB,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAM3C"}
1
+ {"version":3,"file":"guard.d.ts","sourceRoot":"","sources":["../../../src/modules/import-paths/guard.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAS,MAAM,sBAAsB,CAAC;AAE9D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AAKzE,MAAM,WAAW,eAAe;IAC9B,aAAa,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAChD,UAAU,EAAE,aAAa,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC1D;AAuCD,eAAO,MAAM,eAAe,EAAE,sBAAsB,CAAC,eAAe,CAiCnE,CAAC;AAEF,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,UAAU,EAClB,KAAK,EAAE,eAAe,GACrB,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAM3C"}
@@ -1,12 +1,12 @@
1
- import { isTestPath, isUnderRoot, kindOfFile } from '../../node/layout.js';
1
+ import { declaredRoots, isTestPath, isUnderRoot, kindOfFile } from '../../node/layout.js';
2
2
  import { importAliasesProblem, reviewedProblems } from '../../support/facts.js';
3
3
  import { analyzeImportPaths, createImportPathProfile, findUnusedIndexMembers } from './analyzer.js';
4
4
  import { REPOSITORY_GUARD_TIMEOUT_MS } from '../../support/timeouts.js';
5
5
  const SCOPE = {
6
- roots: ['app', 'functions'],
6
+ roots: ['app', 'functions', 'e2e', 'hostedE2e', 'scripts', 'workers'],
7
7
  kinds: ['code'],
8
8
  tests: true,
9
- optionalRoots: ['functions'],
9
+ optionalRoots: ['functions', 'e2e', 'hostedE2e', 'scripts', 'workers'],
10
10
  };
11
11
  function readModules(files) {
12
12
  const modules = new Map();
@@ -15,8 +15,9 @@ function readModules(files) {
15
15
  return modules;
16
16
  }
17
17
  function analyzedRoots(reader) {
18
- const { app, functions, sharedSource } = reader.layout.roots;
19
- return [app.path, ...[functions, sharedSource].flatMap((root) => (root ? [root.path] : []))];
18
+ return declaredRoots(reader.layout)
19
+ .filter(({ role }) => role !== 'coreDist')
20
+ .map(({ root }) => root.path);
20
21
  }
21
22
  function excludedModuleExists(reader, p) {
22
23
  return ((reader.layout.exclusions ?? []).some((e) => isUnderRoot(p, e.path)) &&
@@ -1 +1 @@
1
- {"version":3,"file":"guard.js","sourceRoot":"","sources":["../../../src/modules/import-paths/guard.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Finding, GuardContext } from '../../guard.js';\nimport type { RepoReader, Scope } from '../../node/reader.js';\nimport { isTestPath, isUnderRoot, kindOfFile } from '../../node/layout.js';\nimport type { GenericGuardDefinition } from '../../node/suite/config.js';\nimport { importAliasesProblem, reviewedProblems } from '../../support/facts.js';\nimport { analyzeImportPaths, createImportPathProfile, findUnusedIndexMembers } from './analyzer.js';\nimport { REPOSITORY_GUARD_TIMEOUT_MS } from '../../support/timeouts.js';\n\nexport interface ImportPathFacts {\n importAliases: Readonly<Record<string, string>>;\n exceptions: ReadonlyArray<{ file: string; why: string }>;\n}\n\nconst SCOPE: Scope = {\n roots: ['app', 'functions'],\n kinds: ['code'],\n tests: true,\n optionalRoots: ['functions'],\n};\n\nfunction readModules(files: ReturnType<RepoReader['files']>) {\n const modules = new Map<string, { text: string; sourceFile: import('typescript').SourceFile }>();\n for (const file of files)\n modules.set(file.path, { text: file.text, sourceFile: file.sourceFile });\n return modules;\n}\n\nfunction analyzedRoots(reader: RepoReader) {\n const { app, functions, sharedSource } = reader.layout.roots;\n return [app.path, ...[functions, sharedSource].flatMap((root) => (root ? [root.path] : []))];\n}\n\nfunction excludedModuleExists(reader: RepoReader, p: string): boolean {\n return (\n (reader.layout.exclusions ?? []).some((e) => isUnderRoot(p, e.path)) &&\n kindOfFile(p.slice(p.lastIndexOf('/') + 1)) === 'code' &&\n reader.exists(p)\n );\n}\n\nfunction profileFor(reader: RepoReader, facts: ImportPathFacts) {\n return createImportPathProfile({\n importAliases: { ...facts.importAliases },\n exceptions: facts.exceptions.map((e) => ({ ...e })),\n isTestFile: (p: string) => isTestPath(reader.layout, p),\n isExcludedModule: (p: string) => excludedModuleExists(reader, p),\n });\n}\n\nexport const importPathGuard: GenericGuardDefinition<ImportPathFacts> = {\n id: 'importPaths',\n absentWhen: false,\n factProblems(facts) {\n const problems: string[] = [];\n importAliasesProblem(facts?.importAliases, 'importAliases', problems);\n reviewedProblems(facts?.exceptions, 'exceptions', problems, { key: 'file' });\n return problems;\n },\n build(facts) {\n return [\n {\n title: 'every definition has exactly one import path',\n description:\n 'A module that is not a folder index never re-exports; a folder index forwards only files inside its folder; ' +\n 'code outside a folder that has an index imports through it, never past it; code inside a folder imports its ' +\n 'siblings directly, never through an index of a folder that contains it. Import the owner — the sibling file or the package.',\n timeoutMs: REPOSITORY_GUARD_TIMEOUT_MS,\n run(context: GuardContext): Finding[] {\n const files = context.files(SCOPE);\n const result = analyzeImportPaths({\n files: readModules(files),\n roots: analyzedRoots(context.reader),\n profile: profileFor(context.reader, facts),\n });\n return result.violations.map((v) => ({\n file: v.file,\n detail: `${v.kind.replace(/-/g, ' ')}: ${v.detail}`,\n }));\n },\n },\n ];\n },\n};\n\nexport function unusedIndexMembers(\n reader: RepoReader,\n facts: ImportPathFacts,\n): Array<{ file: string; unused: string[] }> {\n return findUnusedIndexMembers({\n files: readModules(reader.files(SCOPE)),\n roots: analyzedRoots(reader),\n profile: profileFor(reader, facts),\n });\n}\n"]}
1
+ {"version":3,"file":"guard.js","sourceRoot":"","sources":["../../../src/modules/import-paths/guard.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Finding, GuardContext } from '../../guard.js';\nimport type { RepoReader, Scope } from '../../node/reader.js';\nimport { declaredRoots, isTestPath, isUnderRoot, kindOfFile } from '../../node/layout.js';\nimport type { GenericGuardDefinition } from '../../node/suite/config.js';\nimport { importAliasesProblem, reviewedProblems } from '../../support/facts.js';\nimport { analyzeImportPaths, createImportPathProfile, findUnusedIndexMembers } from './analyzer.js';\nimport { REPOSITORY_GUARD_TIMEOUT_MS } from '../../support/timeouts.js';\n\nexport interface ImportPathFacts {\n importAliases: Readonly<Record<string, string>>;\n exceptions: ReadonlyArray<{ file: string; why: string }>;\n}\n\nconst SCOPE: Scope = {\n roots: ['app', 'functions', 'e2e', 'hostedE2e', 'scripts', 'workers'],\n kinds: ['code'],\n tests: true,\n optionalRoots: ['functions', 'e2e', 'hostedE2e', 'scripts', 'workers'],\n};\n\nfunction readModules(files: ReturnType<RepoReader['files']>) {\n const modules = new Map<string, { text: string; sourceFile: import('typescript').SourceFile }>();\n for (const file of files)\n modules.set(file.path, { text: file.text, sourceFile: file.sourceFile });\n return modules;\n}\n\nfunction analyzedRoots(reader: RepoReader) {\n return declaredRoots(reader.layout)\n .filter(({ role }) => role !== 'coreDist')\n .map(({ root }) => root.path);\n}\n\nfunction excludedModuleExists(reader: RepoReader, p: string): boolean {\n return (\n (reader.layout.exclusions ?? []).some((e) => isUnderRoot(p, e.path)) &&\n kindOfFile(p.slice(p.lastIndexOf('/') + 1)) === 'code' &&\n reader.exists(p)\n );\n}\n\nfunction profileFor(reader: RepoReader, facts: ImportPathFacts) {\n return createImportPathProfile({\n importAliases: { ...facts.importAliases },\n exceptions: facts.exceptions.map((e) => ({ ...e })),\n isTestFile: (p: string) => isTestPath(reader.layout, p),\n isExcludedModule: (p: string) => excludedModuleExists(reader, p),\n });\n}\n\nexport const importPathGuard: GenericGuardDefinition<ImportPathFacts> = {\n id: 'importPaths',\n absentWhen: false,\n factProblems(facts) {\n const problems: string[] = [];\n importAliasesProblem(facts?.importAliases, 'importAliases', problems);\n reviewedProblems(facts?.exceptions, 'exceptions', problems, { key: 'file' });\n return problems;\n },\n build(facts) {\n return [\n {\n title: 'every definition has exactly one import path',\n description:\n 'A module that is not a folder index never re-exports; a folder index forwards only files inside its folder; ' +\n 'code outside a folder that has an index imports through it, never past it; code inside a folder imports its ' +\n 'siblings directly, never through an index of a folder that contains it. Import the owner — the sibling file or the package.',\n timeoutMs: REPOSITORY_GUARD_TIMEOUT_MS,\n run(context: GuardContext): Finding[] {\n const files = context.files(SCOPE);\n const result = analyzeImportPaths({\n files: readModules(files),\n roots: analyzedRoots(context.reader),\n profile: profileFor(context.reader, facts),\n });\n return result.violations.map((v) => ({\n file: v.file,\n detail: `${v.kind.replace(/-/g, ' ')}: ${v.detail}`,\n }));\n },\n },\n ];\n },\n};\n\nexport function unusedIndexMembers(\n reader: RepoReader,\n facts: ImportPathFacts,\n): Array<{ file: string; unused: string[] }> {\n return findUnusedIndexMembers({\n files: readModules(reader.files(SCOPE)),\n roots: analyzedRoots(reader),\n profile: profileFor(reader, facts),\n });\n}\n"]}
@@ -0,0 +1,9 @@
1
+ import type { GenericGuardDefinition } from '../../node/suite/config.js';
2
+ import { type FileView } from '../../reader/view.js';
3
+ import { type AnchoredEntry, type Occurrence } from '../../support/anchored-review.js';
4
+ export declare function blanketCasts(source: FileView | string): Occurrence[];
5
+ export interface BlanketCastFacts {
6
+ reviewed: readonly AnchoredEntry[];
7
+ }
8
+ export declare const blanketCastGuard: GenericGuardDefinition<BlanketCastFacts>;
9
+ //# sourceMappingURL=blanket-casts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blanket-casts.d.ts","sourceRoot":"","sources":["../../../src/modules/repo/blanket-casts.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AAEzE,OAAO,EAAc,KAAK,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAEjE,OAAO,EAGL,KAAK,aAAa,EAClB,KAAK,UAAU,EAChB,MAAM,kCAAkC,CAAC;AAgB1C,wBAAgB,YAAY,CAAC,MAAM,EAAE,QAAQ,GAAG,MAAM,GAAG,UAAU,EAAE,CAwBpE;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,SAAS,aAAa,EAAE,CAAC;CACpC;AAED,eAAO,MAAM,gBAAgB,EAAE,sBAAsB,CAAC,gBAAgB,CA6BrE,CAAC"}
@@ -0,0 +1,67 @@
1
+ import ts from 'typescript';
2
+ import { viewOfText } from '../../reader/view.js';
3
+ import { forEachNode } from '../../support/ast.js';
4
+ import { anchoredFindings, anchoredProblems, } from '../../support/anchored-review.js';
5
+ import { objectProblem } from '../../support/facts.js';
6
+ import { REPOSITORY_GUARD_TIMEOUT_MS } from '../../support/timeouts.js';
7
+ const SCOPE = {
8
+ roots: ['app', 'functions', 'sharedSource', 'e2e', 'hostedE2e', 'scripts', 'workers'],
9
+ kinds: ['code'],
10
+ tests: true,
11
+ optionalRoots: ['functions', 'sharedSource', 'e2e', 'hostedE2e', 'scripts', 'workers'],
12
+ };
13
+ const isCast = (node) => ts.isAsExpression(node) || ts.isTypeAssertionExpression(node);
14
+ const isKeyword = (type, kind) => type.kind === kind;
15
+ export function blanketCasts(source) {
16
+ const file = typeof source === 'string' ? viewOfText('sample.ts', source) : source;
17
+ const sf = file.sourceFile;
18
+ const hits = [];
19
+ forEachNode(sf, (node) => {
20
+ if (!isCast(node))
21
+ return;
22
+ if (isKeyword(node.type, ts.SyntaxKind.NeverKeyword))
23
+ hits.push({
24
+ start: node.getStart(sf),
25
+ end: node.getEnd(),
26
+ detail: 'an `as never` cast switches type checking off for the value',
27
+ });
28
+ else {
29
+ let inner = node.expression;
30
+ while (ts.isParenthesizedExpression(inner))
31
+ inner = inner.expression;
32
+ if (isCast(inner) && isKeyword(inner.type, ts.SyntaxKind.UnknownKeyword))
33
+ hits.push({
34
+ start: node.getStart(sf),
35
+ end: node.getEnd(),
36
+ detail: 'an `as unknown as` cast forces one type onto a value of another',
37
+ });
38
+ }
39
+ });
40
+ return hits;
41
+ }
42
+ export const blanketCastGuard = {
43
+ id: 'blanketCasts',
44
+ absentWhen: false,
45
+ factProblems(facts) {
46
+ const problems = [];
47
+ if (!objectProblem(facts, 'the facts', problems))
48
+ return problems;
49
+ anchoredProblems(facts.reviewed, 'reviewed', problems);
50
+ return problems;
51
+ },
52
+ build(facts) {
53
+ return [
54
+ {
55
+ title: 'no cast switches type checking off',
56
+ description: 'An `as never` or `as unknown as X` cast tells the compiler to stop checking a value, so a wrong shape ' +
57
+ 'compiles and fails only at run time; the no-unsafe lint rules cannot see it. Give the value its real ' +
58
+ 'type: a typed fake or builder in a test, the owner type at a call site, a typed mapping between two ' +
59
+ 'unions. A cast that is the only way, or clearly the best way, is a reviewed entry whose anchor names ' +
60
+ 'that one cast, with its why; an entry that no longer matches a cast is stale.',
61
+ timeoutMs: REPOSITORY_GUARD_TIMEOUT_MS,
62
+ run: (context) => anchoredFindings(context.files(SCOPE).map((file) => ({ file, hits: blanketCasts(file) })), facts.reviewed, 'its anchor names no blanket cast'),
63
+ },
64
+ ];
65
+ },
66
+ };
67
+ //# sourceMappingURL=blanket-casts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blanket-casts.js","sourceRoot":"","sources":["../../../src/modules/repo/blanket-casts.ts"],"names":[],"mappings":"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","sourcesContent":["import ts from 'typescript';\nimport type { GenericGuardDefinition } from '../../node/suite/config.js';\nimport type { Scope } from '../../node/reader.js';\nimport { viewOfText, type FileView } from '../../reader/view.js';\nimport { forEachNode } from '../../support/ast.js';\nimport {\n anchoredFindings,\n anchoredProblems,\n type AnchoredEntry,\n type Occurrence,\n} from '../../support/anchored-review.js';\nimport { objectProblem } from '../../support/facts.js';\nimport { REPOSITORY_GUARD_TIMEOUT_MS } from '../../support/timeouts.js';\n\nconst SCOPE: Scope = {\n roots: ['app', 'functions', 'sharedSource', 'e2e', 'hostedE2e', 'scripts', 'workers'],\n kinds: ['code'],\n tests: true,\n optionalRoots: ['functions', 'sharedSource', 'e2e', 'hostedE2e', 'scripts', 'workers'],\n};\n\nconst isCast = (node: ts.Node): node is ts.AsExpression | ts.TypeAssertion =>\n ts.isAsExpression(node) || ts.isTypeAssertionExpression(node);\n\nconst isKeyword = (type: ts.TypeNode, kind: ts.SyntaxKind) => type.kind === kind;\n\nexport function blanketCasts(source: FileView | string): Occurrence[] {\n const file = typeof source === 'string' ? viewOfText('sample.ts', source) : source;\n const sf = file.sourceFile;\n const hits: Occurrence[] = [];\n forEachNode(sf, (node) => {\n if (!isCast(node)) return;\n if (isKeyword(node.type, ts.SyntaxKind.NeverKeyword))\n hits.push({\n start: node.getStart(sf),\n end: node.getEnd(),\n detail: 'an `as never` cast switches type checking off for the value',\n });\n else {\n let inner = node.expression;\n while (ts.isParenthesizedExpression(inner)) inner = inner.expression;\n if (isCast(inner) && isKeyword(inner.type, ts.SyntaxKind.UnknownKeyword))\n hits.push({\n start: node.getStart(sf),\n end: node.getEnd(),\n detail: 'an `as unknown as` cast forces one type onto a value of another',\n });\n }\n });\n return hits;\n}\n\nexport interface BlanketCastFacts {\n reviewed: readonly AnchoredEntry[];\n}\n\nexport const blanketCastGuard: GenericGuardDefinition<BlanketCastFacts> = {\n id: 'blanketCasts',\n absentWhen: false,\n factProblems(facts) {\n const problems: string[] = [];\n if (!objectProblem(facts, 'the facts', problems)) return problems;\n anchoredProblems(facts.reviewed, 'reviewed', problems);\n return problems;\n },\n build(facts) {\n return [\n {\n title: 'no cast switches type checking off',\n description:\n 'An `as never` or `as unknown as X` cast tells the compiler to stop checking a value, so a wrong shape ' +\n 'compiles and fails only at run time; the no-unsafe lint rules cannot see it. Give the value its real ' +\n 'type: a typed fake or builder in a test, the owner type at a call site, a typed mapping between two ' +\n 'unions. A cast that is the only way, or clearly the best way, is a reviewed entry whose anchor names ' +\n 'that one cast, with its why; an entry that no longer matches a cast is stale.',\n timeoutMs: REPOSITORY_GUARD_TIMEOUT_MS,\n run: (context) =>\n anchoredFindings(\n context.files(SCOPE).map((file) => ({ file, hits: blanketCasts(file) })),\n facts.reviewed,\n 'its anchor names no blanket cast',\n ),\n },\n ];\n },\n};\n"]}
@@ -0,0 +1,17 @@
1
+ import ts from 'typescript';
2
+ import type { GenericGuardDefinition } from '../../node/suite/config.js';
3
+ import { type FileView } from '../../reader/view.js';
4
+ import { type AnchoredEntry, type Occurrence } from '../../support/anchored-review.js';
5
+ export interface ActionReach {
6
+ actionIn(file: FileView, root: ts.Node): string[] | undefined;
7
+ }
8
+ export declare function createActionReach(files: readonly FileView[], helpers?: readonly string[]): ActionReach;
9
+ export declare function retriedActions(source: FileView | string, helpers?: readonly string[], reach?: ActionReach): Occurrence[];
10
+ export declare function swallowedActions(source: FileView | string, helpers?: readonly string[], reach?: ActionReach): Occurrence[];
11
+ export interface E2eActionFacts {
12
+ actionHelpers: readonly string[];
13
+ reviewed: readonly AnchoredEntry[];
14
+ }
15
+ export declare const retryLoopActionGuard: GenericGuardDefinition<E2eActionFacts>;
16
+ export declare const swallowedActionGuard: GenericGuardDefinition<E2eActionFacts>;
17
+ //# sourceMappingURL=e2e-actions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"e2e-actions.d.ts","sourceRoot":"","sources":["../../../src/modules/repo/e2e-actions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AAEzE,OAAO,EAAc,KAAK,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAGjE,OAAO,EAGL,KAAK,aAAa,EAClB,KAAK,UAAU,EAChB,MAAM,kCAAkC,CAAC;AAmI1C,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC;CAC/D;AAED,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,SAAS,QAAQ,EAAE,EAC1B,OAAO,GAAE,SAAS,MAAM,EAAO,GAC9B,WAAW,CAgDb;AAcD,wBAAgB,cAAc,CAC5B,MAAM,EAAE,QAAQ,GAAG,MAAM,EACzB,OAAO,GAAE,SAAS,MAAM,EAAO,EAC/B,KAAK,CAAC,EAAE,WAAW,GAClB,UAAU,EAAE,CA4Bd;AAmBD,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,QAAQ,GAAG,MAAM,EACzB,OAAO,GAAE,SAAS,MAAM,EAAO,EAC/B,KAAK,CAAC,EAAE,WAAW,GAClB,UAAU,EAAE,CA8Cd;AAWD,MAAM,WAAW,cAAc;IAC7B,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,QAAQ,EAAE,SAAS,aAAa,EAAE,CAAC;CACpC;AAUD,eAAO,MAAM,oBAAoB,EAAE,sBAAsB,CAAC,cAAc,CA8BvE,CAAC;AAEF,eAAO,MAAM,oBAAoB,EAAE,sBAAsB,CAAC,cAAc,CA6BvE,CAAC"}
@@ -0,0 +1,329 @@
1
+ import ts from 'typescript';
2
+ import { viewOfText } from '../../reader/view.js';
3
+ import { calleeName, forEachNode } from '../../support/ast.js';
4
+ import { resolveRelative } from '../../support/paths.js';
5
+ import { anchoredFindings, anchoredProblems, } from '../../support/anchored-review.js';
6
+ import { namesProblem, objectProblem } from '../../support/facts.js';
7
+ import { REPOSITORY_GUARD_TIMEOUT_MS } from '../../support/timeouts.js';
8
+ // Playwright's locator and page operations that act on the page: input, and navigation.
9
+ const PLAYWRIGHT_ACTIONS = new Set([
10
+ 'check',
11
+ 'clear',
12
+ 'click',
13
+ 'dblclick',
14
+ 'dispatchEvent',
15
+ 'dragTo',
16
+ 'fill',
17
+ 'goBack',
18
+ 'goForward',
19
+ 'goto',
20
+ 'hover',
21
+ 'press',
22
+ 'pressSequentially',
23
+ 'reload',
24
+ 'selectOption',
25
+ 'selectText',
26
+ 'setChecked',
27
+ 'setInputFiles',
28
+ 'tap',
29
+ 'type',
30
+ 'uncheck',
31
+ ]);
32
+ // Playwright's state reads: strict locators throw when a locator matches more than one element.
33
+ const STATE_PROBES = new Set([
34
+ 'isChecked',
35
+ 'isDisabled',
36
+ 'isEditable',
37
+ 'isEnabled',
38
+ 'isHidden',
39
+ 'isVisible',
40
+ ]);
41
+ const E2E_SCOPE = {
42
+ roots: ['e2e', 'hostedE2e'],
43
+ kinds: ['code'],
44
+ tests: true,
45
+ optionalRoots: ['hostedE2e'],
46
+ };
47
+ const actionNamesOf = (helpers) => new Set([...PLAYWRIGHT_ACTIONS, ...helpers]);
48
+ const isFunctionNode = (node) => ts.isArrowFunction(node) || ts.isFunctionExpression(node);
49
+ // `expect(fn).toPass(…)` and `expect.poll(fn)` retry `fn` until it passes.
50
+ function retriedCallbacks(sf) {
51
+ const callbacks = [];
52
+ forEachNode(sf, (node) => {
53
+ if (!ts.isCallExpression(node) || !ts.isPropertyAccessExpression(node.expression))
54
+ return;
55
+ const method = node.expression.name.text;
56
+ const target = node.expression.expression;
57
+ if (method === 'toPass' && ts.isCallExpression(target)) {
58
+ const fn = target.arguments[0];
59
+ if (fn && isFunctionNode(fn) && calleeName(target)?.toLowerCase().endsWith('expect'))
60
+ callbacks.push(fn);
61
+ }
62
+ if (method === 'poll' && calleeName(node) === 'poll') {
63
+ const fn = node.arguments[0];
64
+ if (fn && isFunctionNode(fn))
65
+ callbacks.push(fn);
66
+ }
67
+ });
68
+ return callbacks;
69
+ }
70
+ const isRepeatingLoop = (node) => ts.isForStatement(node) || ts.isWhileStatement(node) || ts.isDoStatement(node);
71
+ function functionBodiesOf(sf) {
72
+ const bodies = new Map();
73
+ const add = (name, body) => bodies.set(name, [...(bodies.get(name) ?? []), body]);
74
+ forEachNode(sf, (node) => {
75
+ if (ts.isFunctionDeclaration(node) && node.name && node.body)
76
+ add(node.name.text, node.body);
77
+ if (ts.isVariableDeclaration(node) &&
78
+ ts.isIdentifier(node.name) &&
79
+ node.initializer &&
80
+ isFunctionNode(node.initializer))
81
+ add(node.name.text, node.initializer.body);
82
+ });
83
+ return bodies;
84
+ }
85
+ const SOURCE_EXTENSIONS = ['.ts', '.tsx', '.mts', '.cts', '.js', '.jsx', '.mjs', '.cjs'];
86
+ function moduleCandidates(base) {
87
+ const stem = base.replace(/\.(?:[cm]?js|jsx)$/, '');
88
+ return [
89
+ base,
90
+ ...SOURCE_EXTENSIONS.map((ext) => stem + ext),
91
+ ...SOURCE_EXTENSIONS.map((ext) => `${base}/index${ext}`),
92
+ ];
93
+ }
94
+ // Where an imported function lives: the file and the name it is declared under there.
95
+ function namedImportsOf(file, paths) {
96
+ const imports = new Map();
97
+ for (const statement of file.sourceFile.statements) {
98
+ if (!ts.isImportDeclaration(statement) || !ts.isStringLiteral(statement.moduleSpecifier))
99
+ continue;
100
+ const bindings = statement.importClause?.namedBindings;
101
+ if (!bindings || !ts.isNamedImports(bindings))
102
+ continue;
103
+ const base = resolveRelative(file.path, statement.moduleSpecifier.text)?.path;
104
+ const target = base && moduleCandidates(base).find((candidate) => paths.has(candidate));
105
+ if (!target)
106
+ continue;
107
+ for (const element of bindings.elements)
108
+ imports.set(element.name.text, {
109
+ path: target,
110
+ name: (element.propertyName ?? element.name).text,
111
+ });
112
+ }
113
+ return imports;
114
+ }
115
+ export function createActionReach(files, helpers = []) {
116
+ const actions = actionNamesOf(helpers);
117
+ const byPath = new Map(files.map((file) => [file.path, file]));
118
+ const paths = new Set(byPath.keys());
119
+ const bodiesCache = new Map();
120
+ const importsCache = new Map();
121
+ const bodiesOf = (file) => {
122
+ let bodies = bodiesCache.get(file.path);
123
+ if (!bodies)
124
+ bodiesCache.set(file.path, (bodies = functionBodiesOf(file.sourceFile)));
125
+ return bodies;
126
+ };
127
+ const importsOf = (file) => {
128
+ let imports = importsCache.get(file.path);
129
+ if (!imports)
130
+ importsCache.set(file.path, (imports = namedImportsOf(file, paths)));
131
+ return imports;
132
+ };
133
+ function search(file, root, seen) {
134
+ let found;
135
+ forEachNode(root, (node) => {
136
+ if (found || !ts.isCallExpression(node))
137
+ return;
138
+ const name = calleeName(node);
139
+ if (!name)
140
+ return;
141
+ if (actions.has(name)) {
142
+ found = [name];
143
+ return;
144
+ }
145
+ if (!ts.isIdentifier(node.expression))
146
+ return;
147
+ const imported = importsOf(file).get(name);
148
+ const local = bodiesOf(file).get(name);
149
+ const owner = local ? file : imported ? byPath.get(imported.path) : undefined;
150
+ const declared = local ? name : imported?.name;
151
+ if (!owner || !declared)
152
+ return;
153
+ const key = `${owner.path}#${declared}`;
154
+ if (seen.has(key))
155
+ return;
156
+ seen.add(key);
157
+ for (const body of bodiesOf(owner).get(declared) ?? []) {
158
+ const inner = search(owner, body, seen);
159
+ if (inner) {
160
+ found = [name, ...inner];
161
+ return;
162
+ }
163
+ }
164
+ });
165
+ return found;
166
+ }
167
+ return { actionIn: (file, root) => search(file, root, new Set()) };
168
+ }
169
+ // `['openWork', 'goto']` reads "goto() through openWork".
170
+ function describeAction(chain) {
171
+ const action = `${chain[chain.length - 1]}()`;
172
+ return chain.length > 1 ? `${action} through ${chain.slice(0, -1).join(' → ')}` : action;
173
+ }
174
+ const within = (inner, outer) => inner !== outer && inner.pos >= outer.pos && inner.end <= outer.end;
175
+ const viewOf = (source) => typeof source === 'string' ? viewOfText('sample.spec.ts', source) : source;
176
+ export function retriedActions(source, helpers = [], reach) {
177
+ const file = viewOf(source);
178
+ const sf = file.sourceFile;
179
+ const reachFrom = reach ?? createActionReach([file], helpers);
180
+ const loops = [];
181
+ forEachNode(sf, (node) => {
182
+ if (isRepeatingLoop(node))
183
+ loops.push(node);
184
+ });
185
+ const acting = [
186
+ ...retriedCallbacks(sf).map((node) => ({
187
+ node,
188
+ body: node.body,
189
+ kind: 'a retry loop',
190
+ tail: ' until it passes',
191
+ })),
192
+ ...loops.map((node) => ({ node, body: node, kind: 'a for / while / do loop', tail: '' })),
193
+ ].flatMap((loop) => {
194
+ const chain = reachFrom.actionIn(file, loop.body);
195
+ return chain ? [{ ...loop, chain }] : [];
196
+ });
197
+ return acting
198
+ .filter((loop) => !acting.some((outer) => within(loop.node, outer.node)))
199
+ .sort((a, b) => a.node.getStart(sf) - b.node.getStart(sf))
200
+ .map((loop) => ({
201
+ start: loop.node.getStart(sf),
202
+ end: loop.node.getEnd(),
203
+ detail: `${loop.kind} repeats ${describeAction(loop.chain)}${loop.tail}`,
204
+ }));
205
+ }
206
+ function catchesRejection(handler) {
207
+ if (!isFunctionNode(handler))
208
+ return true;
209
+ let rethrows = false;
210
+ forEachNode(handler.body, (node) => {
211
+ if (ts.isThrowStatement(node))
212
+ rethrows = true;
213
+ });
214
+ return !rethrows;
215
+ }
216
+ // The call a `.catch()` is chained onto: `x.click().catch(…)` → the `click()` call.
217
+ function caughtCall(catchCall) {
218
+ if (!ts.isPropertyAccessExpression(catchCall.expression))
219
+ return undefined;
220
+ const receiver = catchCall.expression.expression;
221
+ const call = ts.isParenthesizedExpression(receiver) ? receiver.expression : receiver;
222
+ return ts.isCallExpression(call) ? call : undefined;
223
+ }
224
+ export function swallowedActions(source, helpers = [], reach) {
225
+ const file = viewOf(source);
226
+ const sf = file.sourceFile;
227
+ const reachFrom = reach ?? createActionReach([file], helpers);
228
+ const hits = [];
229
+ forEachNode(sf, (node) => {
230
+ if (ts.isCallExpression(node) &&
231
+ ts.isPropertyAccessExpression(node.expression) &&
232
+ node.expression.name.text === 'catch' &&
233
+ node.arguments.length === 1 &&
234
+ catchesRejection(node.arguments[0])) {
235
+ const call = caughtCall(node);
236
+ if (!call)
237
+ return;
238
+ const name = calleeName(call);
239
+ const chain = reachFrom.actionIn(file, call);
240
+ if (chain)
241
+ hits.push({
242
+ start: node.getStart(sf),
243
+ end: node.getEnd(),
244
+ detail: `a .catch() swallows a failed ${describeAction(chain)}`,
245
+ });
246
+ else if (name && STATE_PROBES.has(name))
247
+ hits.push({
248
+ start: node.getStart(sf),
249
+ end: node.getEnd(),
250
+ detail: `a .catch() turns a failed ${name}() — an ambiguous locator included — into a value`,
251
+ });
252
+ }
253
+ if (ts.isTryStatement(node) && node.catchClause) {
254
+ let rethrows = false;
255
+ forEachNode(node.catchClause.block, (inner) => {
256
+ if (ts.isThrowStatement(inner))
257
+ rethrows = true;
258
+ });
259
+ if (rethrows)
260
+ return;
261
+ const chain = reachFrom.actionIn(file, node.tryBlock);
262
+ if (chain)
263
+ hits.push({
264
+ start: node.getStart(sf),
265
+ end: node.getEnd(),
266
+ detail: `a try/catch swallows a failed ${describeAction(chain)}`,
267
+ });
268
+ }
269
+ });
270
+ return hits;
271
+ }
272
+ function withReach(files, helpers, find) {
273
+ const reach = createActionReach(files, helpers);
274
+ return files.map((file) => ({ file, hits: find(file, reach) }));
275
+ }
276
+ function e2eActionProblems(facts) {
277
+ const problems = [];
278
+ if (!objectProblem(facts, 'the facts', problems))
279
+ return problems;
280
+ namesProblem(facts.actionHelpers, 'actionHelpers', problems, { nonEmpty: false });
281
+ anchoredProblems(facts.reviewed, 'reviewed', problems);
282
+ return problems;
283
+ }
284
+ export const retryLoopActionGuard = {
285
+ id: 'retryLoopActions',
286
+ absentWhen: 'the repository has no end-to-end suite',
287
+ absentOnlyWithoutRoot: 'e2e',
288
+ factProblems: e2eActionProblems,
289
+ build(facts) {
290
+ return [
291
+ {
292
+ title: 'no end-to-end retry loop repeats a user action',
293
+ description: 'A toPass() or expect.poll() callback, or a for (;;) / while / do loop, that clicks, types, or navigates ' +
294
+ 'repeats the action until it happens to work, so the failure a real user would hit passes as green ' +
295
+ '(RULE-STD-QUALITY-003). Walking a list is written for…of, which is not a repeat. Act once, ' +
296
+ 'then assert with a web-first assertion; a loop that only re-reads state is fine. The actions are ' +
297
+ "Playwright's input and navigation calls plus the app's declared actionHelpers, reached directly or through " +
298
+ 'a plain call to a function the suite declares in the same file or imports from its own files. A loop that must act — ' +
299
+ 'polling a background result by reloading — is a reviewed entry whose anchor names that one loop, with ' +
300
+ 'its why; an entry that no longer matches a loop is stale.',
301
+ timeoutMs: REPOSITORY_GUARD_TIMEOUT_MS,
302
+ run: (context) => anchoredFindings(withReach(context.files(E2E_SCOPE), facts.actionHelpers, (file, reach) => retriedActions(file, facts.actionHelpers, reach)), facts.reviewed, 'its anchor names no retry loop that acts'),
303
+ },
304
+ ];
305
+ },
306
+ };
307
+ export const swallowedActionGuard = {
308
+ id: 'swallowedE2eActions',
309
+ absentWhen: 'the repository has no end-to-end suite',
310
+ absentOnlyWithoutRoot: 'e2e',
311
+ factProblems: e2eActionProblems,
312
+ build(facts) {
313
+ return [
314
+ {
315
+ title: 'no end-to-end action or state read swallows its failure',
316
+ description: 'A .catch() or a try/catch that discards a failed click, fill, or navigation — made directly or through a plain ' +
317
+ "call to one of the suite's own functions — lets the next step run on a " +
318
+ 'page the action never reached, and one on isVisible(), isEnabled(), or isChecked() turns an ambiguous ' +
319
+ '(strict-mode) locator into a silent "no". Let the failure throw: an action whose outcome is uncertain ' +
320
+ "goes through the suite's outcome-verified helper, and a branch reads state with no catch. A catch that " +
321
+ 'must stay is a reviewed entry whose anchor names that one occurrence, with its why; an entry that no ' +
322
+ 'longer matches one is stale.',
323
+ timeoutMs: REPOSITORY_GUARD_TIMEOUT_MS,
324
+ run: (context) => anchoredFindings(withReach(context.files(E2E_SCOPE), facts.actionHelpers, (file, reach) => swallowedActions(file, facts.actionHelpers, reach)), facts.reviewed, 'its anchor names no swallowed action or state read'),
325
+ },
326
+ ];
327
+ },
328
+ };
329
+ //# sourceMappingURL=e2e-actions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"e2e-actions.js","sourceRoot":"","sources":["../../../src/modules/repo/e2e-actions.ts"],"names":[],"mappings":"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","sourcesContent":["import ts from 'typescript';\nimport type { GenericGuardDefinition } from '../../node/suite/config.js';\nimport type { Scope } from '../../node/reader.js';\nimport { viewOfText, type FileView } from '../../reader/view.js';\nimport { calleeName, forEachNode } from '../../support/ast.js';\nimport { resolveRelative } from '../../support/paths.js';\nimport {\n anchoredFindings,\n anchoredProblems,\n type AnchoredEntry,\n type Occurrence,\n} from '../../support/anchored-review.js';\nimport { namesProblem, objectProblem } from '../../support/facts.js';\nimport { REPOSITORY_GUARD_TIMEOUT_MS } from '../../support/timeouts.js';\n\n// Playwright's locator and page operations that act on the page: input, and navigation.\nconst PLAYWRIGHT_ACTIONS: ReadonlySet<string> = new Set([\n 'check',\n 'clear',\n 'click',\n 'dblclick',\n 'dispatchEvent',\n 'dragTo',\n 'fill',\n 'goBack',\n 'goForward',\n 'goto',\n 'hover',\n 'press',\n 'pressSequentially',\n 'reload',\n 'selectOption',\n 'selectText',\n 'setChecked',\n 'setInputFiles',\n 'tap',\n 'type',\n 'uncheck',\n]);\n\n// Playwright's state reads: strict locators throw when a locator matches more than one element.\nconst STATE_PROBES: ReadonlySet<string> = new Set([\n 'isChecked',\n 'isDisabled',\n 'isEditable',\n 'isEnabled',\n 'isHidden',\n 'isVisible',\n]);\n\nconst E2E_SCOPE: Scope = {\n roots: ['e2e', 'hostedE2e'],\n kinds: ['code'],\n tests: true,\n optionalRoots: ['hostedE2e'],\n};\n\nconst actionNamesOf = (helpers: readonly string[]) => new Set([...PLAYWRIGHT_ACTIONS, ...helpers]);\n\nconst isFunctionNode = (node: ts.Node): node is ts.ArrowFunction | ts.FunctionExpression =>\n ts.isArrowFunction(node) || ts.isFunctionExpression(node);\n\n// `expect(fn).toPass(…)` and `expect.poll(fn)` retry `fn` until it passes.\nfunction retriedCallbacks(sf: ts.SourceFile): Array<ts.ArrowFunction | ts.FunctionExpression> {\n const callbacks: Array<ts.ArrowFunction | ts.FunctionExpression> = [];\n forEachNode(sf, (node) => {\n if (!ts.isCallExpression(node) || !ts.isPropertyAccessExpression(node.expression)) return;\n const method = node.expression.name.text;\n const target = node.expression.expression;\n if (method === 'toPass' && ts.isCallExpression(target)) {\n const fn = target.arguments[0];\n if (fn && isFunctionNode(fn) && calleeName(target)?.toLowerCase().endsWith('expect'))\n callbacks.push(fn);\n }\n if (method === 'poll' && calleeName(node) === 'poll') {\n const fn = node.arguments[0];\n if (fn && isFunctionNode(fn)) callbacks.push(fn);\n }\n });\n return callbacks;\n}\n\nconst isRepeatingLoop = (\n node: ts.Node,\n): node is ts.ForStatement | ts.WhileStatement | ts.DoStatement =>\n ts.isForStatement(node) || ts.isWhileStatement(node) || ts.isDoStatement(node);\n\ntype FunctionBodies = ReadonlyMap<string, readonly ts.Node[]>;\n\nfunction functionBodiesOf(sf: ts.SourceFile): FunctionBodies {\n const bodies = new Map<string, ts.Node[]>();\n const add = (name: string, body: ts.Node) =>\n bodies.set(name, [...(bodies.get(name) ?? []), body]);\n forEachNode(sf, (node) => {\n if (ts.isFunctionDeclaration(node) && node.name && node.body) add(node.name.text, node.body);\n if (\n ts.isVariableDeclaration(node) &&\n ts.isIdentifier(node.name) &&\n node.initializer &&\n isFunctionNode(node.initializer)\n )\n add(node.name.text, node.initializer.body);\n });\n return bodies;\n}\n\nconst SOURCE_EXTENSIONS = ['.ts', '.tsx', '.mts', '.cts', '.js', '.jsx', '.mjs', '.cjs'];\n\nfunction moduleCandidates(base: string): string[] {\n const stem = base.replace(/\\.(?:[cm]?js|jsx)$/, '');\n return [\n base,\n ...SOURCE_EXTENSIONS.map((ext) => stem + ext),\n ...SOURCE_EXTENSIONS.map((ext) => `${base}/index${ext}`),\n ];\n}\n\n// Where an imported function lives: the file and the name it is declared under there.\nfunction namedImportsOf(\n file: FileView,\n paths: ReadonlySet<string>,\n): Map<string, { path: string; name: string }> {\n const imports = new Map<string, { path: string; name: string }>();\n for (const statement of file.sourceFile.statements) {\n if (!ts.isImportDeclaration(statement) || !ts.isStringLiteral(statement.moduleSpecifier))\n continue;\n const bindings = statement.importClause?.namedBindings;\n if (!bindings || !ts.isNamedImports(bindings)) continue;\n const base = resolveRelative(file.path, statement.moduleSpecifier.text)?.path;\n const target = base && moduleCandidates(base).find((candidate) => paths.has(candidate));\n if (!target) continue;\n for (const element of bindings.elements)\n imports.set(element.name.text, {\n path: target,\n name: (element.propertyName ?? element.name).text,\n });\n }\n return imports;\n}\n\n// Finds the first action a piece of code performs, directly or through the plain function calls it\n// makes into functions declared in the same file or imported from the suite's own files.\nexport interface ActionReach {\n actionIn(file: FileView, root: ts.Node): string[] | undefined;\n}\n\nexport function createActionReach(\n files: readonly FileView[],\n helpers: readonly string[] = [],\n): ActionReach {\n const actions = actionNamesOf(helpers);\n const byPath = new Map(files.map((file) => [file.path, file]));\n const paths = new Set(byPath.keys());\n const bodiesCache = new Map<string, FunctionBodies>();\n const importsCache = new Map<string, Map<string, { path: string; name: string }>>();\n const bodiesOf = (file: FileView) => {\n let bodies = bodiesCache.get(file.path);\n if (!bodies) bodiesCache.set(file.path, (bodies = functionBodiesOf(file.sourceFile)));\n return bodies;\n };\n const importsOf = (file: FileView) => {\n let imports = importsCache.get(file.path);\n if (!imports) importsCache.set(file.path, (imports = namedImportsOf(file, paths)));\n return imports;\n };\n\n function search(file: FileView, root: ts.Node, seen: Set<string>): string[] | undefined {\n let found: string[] | undefined;\n forEachNode(root, (node) => {\n if (found || !ts.isCallExpression(node)) return;\n const name = calleeName(node);\n if (!name) return;\n if (actions.has(name)) {\n found = [name];\n return;\n }\n if (!ts.isIdentifier(node.expression)) return;\n const imported = importsOf(file).get(name);\n const local = bodiesOf(file).get(name);\n const owner = local ? file : imported ? byPath.get(imported.path) : undefined;\n const declared = local ? name : imported?.name;\n if (!owner || !declared) return;\n const key = `${owner.path}#${declared}`;\n if (seen.has(key)) return;\n seen.add(key);\n for (const body of bodiesOf(owner).get(declared) ?? []) {\n const inner = search(owner, body, seen);\n if (inner) {\n found = [name, ...inner];\n return;\n }\n }\n });\n return found;\n }\n\n return { actionIn: (file, root) => search(file, root, new Set()) };\n}\n\n// `['openWork', 'goto']` reads \"goto() through openWork\".\nfunction describeAction(chain: readonly string[]): string {\n const action = `${chain[chain.length - 1]}()`;\n return chain.length > 1 ? `${action} through ${chain.slice(0, -1).join(' → ')}` : action;\n}\n\nconst within = (inner: ts.Node, outer: ts.Node): boolean =>\n inner !== outer && inner.pos >= outer.pos && inner.end <= outer.end;\n\nconst viewOf = (source: FileView | string) =>\n typeof source === 'string' ? viewOfText('sample.spec.ts', source) : source;\n\nexport function retriedActions(\n source: FileView | string,\n helpers: readonly string[] = [],\n reach?: ActionReach,\n): Occurrence[] {\n const file = viewOf(source);\n const sf = file.sourceFile;\n const reachFrom = reach ?? createActionReach([file], helpers);\n const loops: ts.Node[] = [];\n forEachNode(sf, (node) => {\n if (isRepeatingLoop(node)) loops.push(node);\n });\n const acting = [\n ...retriedCallbacks(sf).map((node) => ({\n node,\n body: node.body,\n kind: 'a retry loop',\n tail: ' until it passes',\n })),\n ...loops.map((node) => ({ node, body: node, kind: 'a for / while / do loop', tail: '' })),\n ].flatMap((loop) => {\n const chain = reachFrom.actionIn(file, loop.body);\n return chain ? [{ ...loop, chain }] : [];\n });\n return acting\n .filter((loop) => !acting.some((outer) => within(loop.node, outer.node)))\n .sort((a, b) => a.node.getStart(sf) - b.node.getStart(sf))\n .map((loop) => ({\n start: loop.node.getStart(sf),\n end: loop.node.getEnd(),\n detail: `${loop.kind} repeats ${describeAction(loop.chain)}${loop.tail}`,\n }));\n}\n\nfunction catchesRejection(handler: ts.Expression): boolean {\n if (!isFunctionNode(handler)) return true;\n let rethrows = false;\n forEachNode(handler.body, (node) => {\n if (ts.isThrowStatement(node)) rethrows = true;\n });\n return !rethrows;\n}\n\n// The call a `.catch()` is chained onto: `x.click().catch(…)` → the `click()` call.\nfunction caughtCall(catchCall: ts.CallExpression): ts.CallExpression | undefined {\n if (!ts.isPropertyAccessExpression(catchCall.expression)) return undefined;\n const receiver = catchCall.expression.expression;\n const call = ts.isParenthesizedExpression(receiver) ? receiver.expression : receiver;\n return ts.isCallExpression(call) ? call : undefined;\n}\n\nexport function swallowedActions(\n source: FileView | string,\n helpers: readonly string[] = [],\n reach?: ActionReach,\n): Occurrence[] {\n const file = viewOf(source);\n const sf = file.sourceFile;\n const reachFrom = reach ?? createActionReach([file], helpers);\n const hits: Occurrence[] = [];\n forEachNode(sf, (node) => {\n if (\n ts.isCallExpression(node) &&\n ts.isPropertyAccessExpression(node.expression) &&\n node.expression.name.text === 'catch' &&\n node.arguments.length === 1 &&\n catchesRejection(node.arguments[0])\n ) {\n const call = caughtCall(node);\n if (!call) return;\n const name = calleeName(call);\n const chain = reachFrom.actionIn(file, call);\n if (chain)\n hits.push({\n start: node.getStart(sf),\n end: node.getEnd(),\n detail: `a .catch() swallows a failed ${describeAction(chain)}`,\n });\n else if (name && STATE_PROBES.has(name))\n hits.push({\n start: node.getStart(sf),\n end: node.getEnd(),\n detail: `a .catch() turns a failed ${name}() — an ambiguous locator included — into a value`,\n });\n }\n if (ts.isTryStatement(node) && node.catchClause) {\n let rethrows = false;\n forEachNode(node.catchClause.block, (inner) => {\n if (ts.isThrowStatement(inner)) rethrows = true;\n });\n if (rethrows) return;\n const chain = reachFrom.actionIn(file, node.tryBlock);\n if (chain)\n hits.push({\n start: node.getStart(sf),\n end: node.getEnd(),\n detail: `a try/catch swallows a failed ${describeAction(chain)}`,\n });\n }\n });\n return hits;\n}\n\nfunction withReach(\n files: readonly FileView[],\n helpers: readonly string[],\n find: (file: FileView, reach: ActionReach) => Occurrence[],\n): Array<{ file: FileView; hits: Occurrence[] }> {\n const reach = createActionReach(files, helpers);\n return files.map((file) => ({ file, hits: find(file, reach) }));\n}\n\nexport interface E2eActionFacts {\n actionHelpers: readonly string[];\n reviewed: readonly AnchoredEntry[];\n}\n\nfunction e2eActionProblems(facts: E2eActionFacts): string[] {\n const problems: string[] = [];\n if (!objectProblem(facts, 'the facts', problems)) return problems;\n namesProblem(facts.actionHelpers, 'actionHelpers', problems, { nonEmpty: false });\n anchoredProblems(facts.reviewed, 'reviewed', problems);\n return problems;\n}\n\nexport const retryLoopActionGuard: GenericGuardDefinition<E2eActionFacts> = {\n id: 'retryLoopActions',\n absentWhen: 'the repository has no end-to-end suite',\n absentOnlyWithoutRoot: 'e2e',\n factProblems: e2eActionProblems,\n build(facts) {\n return [\n {\n title: 'no end-to-end retry loop repeats a user action',\n description:\n 'A toPass() or expect.poll() callback, or a for (;;) / while / do loop, that clicks, types, or navigates ' +\n 'repeats the action until it happens to work, so the failure a real user would hit passes as green ' +\n '(RULE-STD-QUALITY-003). Walking a list is written for…of, which is not a repeat. Act once, ' +\n 'then assert with a web-first assertion; a loop that only re-reads state is fine. The actions are ' +\n \"Playwright's input and navigation calls plus the app's declared actionHelpers, reached directly or through \" +\n 'a plain call to a function the suite declares in the same file or imports from its own files. A loop that must act — ' +\n 'polling a background result by reloading — is a reviewed entry whose anchor names that one loop, with ' +\n 'its why; an entry that no longer matches a loop is stale.',\n timeoutMs: REPOSITORY_GUARD_TIMEOUT_MS,\n run: (context) =>\n anchoredFindings(\n withReach(context.files(E2E_SCOPE), facts.actionHelpers, (file, reach) =>\n retriedActions(file, facts.actionHelpers, reach),\n ),\n facts.reviewed,\n 'its anchor names no retry loop that acts',\n ),\n },\n ];\n },\n};\n\nexport const swallowedActionGuard: GenericGuardDefinition<E2eActionFacts> = {\n id: 'swallowedE2eActions',\n absentWhen: 'the repository has no end-to-end suite',\n absentOnlyWithoutRoot: 'e2e',\n factProblems: e2eActionProblems,\n build(facts) {\n return [\n {\n title: 'no end-to-end action or state read swallows its failure',\n description:\n 'A .catch() or a try/catch that discards a failed click, fill, or navigation — made directly or through a plain ' +\n \"call to one of the suite's own functions — lets the next step run on a \" +\n 'page the action never reached, and one on isVisible(), isEnabled(), or isChecked() turns an ambiguous ' +\n '(strict-mode) locator into a silent \"no\". Let the failure throw: an action whose outcome is uncertain ' +\n \"goes through the suite's outcome-verified helper, and a branch reads state with no catch. A catch that \" +\n 'must stay is a reviewed entry whose anchor names that one occurrence, with its why; an entry that no ' +\n 'longer matches one is stale.',\n timeoutMs: REPOSITORY_GUARD_TIMEOUT_MS,\n run: (context) =>\n anchoredFindings(\n withReach(context.files(E2E_SCOPE), facts.actionHelpers, (file, reach) =>\n swallowedActions(file, facts.actionHelpers, reach),\n ),\n facts.reviewed,\n 'its anchor names no swallowed action or state read',\n ),\n },\n ];\n },\n};\n"]}
@@ -1,8 +1,10 @@
1
1
  import type { Absent, GenericGuardDefinition } from '../../node/suite/config.js';
2
2
  import { type EntryPointFacts, type IsolatedRootsFacts, type NoOutsideCheckoutFacts, type TsconfigAliasFacts } from './cross-root.js';
3
+ import { type BlanketCastFacts } from './blanket-casts.js';
3
4
  import { type CiParityFacts } from './ci-parity.js';
4
5
  import { type DependencyOverrideFacts } from './dependency-overrides.js';
5
6
  import { type DocumentDriftFacts } from './document-drift.js';
7
+ import { type E2eActionFacts } from './e2e-actions.js';
6
8
  import { type EnvPublicValuesFacts } from './env-public-values.js';
7
9
  import { type InstalledPackagesAgnosticFacts } from './installed-packages-agnostic.js';
8
10
  import { type LineEndingFacts } from './line-endings.js';
@@ -21,6 +23,9 @@ import { type WorkerConfigFacts } from './worker-config.js';
21
23
  export type RepoGuardFacts = {
22
24
  shellSafeChildProcesses: ShellSafeFacts;
23
25
  lineEndings: LineEndingFacts;
26
+ blanketCasts: BlanketCastFacts;
27
+ retryLoopActions: E2eActionFacts | Absent;
28
+ swallowedE2eActions: E2eActionFacts | Absent;
24
29
  walkerReadFailure: WalkerReadFailureFacts;
25
30
  testSourceRoot: TestSourceRootFacts | Absent;
26
31
  testCaseModuleLoads: TestCaseModuleLoadFacts;
@@ -1 +1 @@
1
- {"version":3,"file":"guards.d.ts","sourceRoot":"","sources":["../../../src/modules/repo/guards.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AACjF,OAAO,EAKL,KAAK,eAAe,EACpB,KAAK,kBAAkB,EACvB,KAAK,sBAAsB,EAC3B,KAAK,kBAAkB,EACxB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAiB,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACnE,OAAO,EAA2B,KAAK,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AAClG,OAAO,EAAsB,KAAK,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAClF,OAAO,EAAwB,KAAK,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AACzF,OAAO,EAEL,KAAK,8BAA8B,EACpC,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAmB,KAAK,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAC1E,OAAO,EAA2B,KAAK,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AAClG,OAAO,EAAsB,KAAK,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AACnF,OAAO,EAAsB,KAAK,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AACnF,OAAO,EAAmB,KAAK,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAC1E,OAAO,EAAkB,KAAK,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACtE,OAAO,EAA2B,KAAK,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AAClG,OAAO,EAA2B,KAAK,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AACpG,OAAO,EAAoB,KAAK,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAC7E,OAAO,EAAuB,KAAK,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AACtF,OAAO,EAA0B,KAAK,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAC/F,OAAO,EAA0B,KAAK,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAC/F,OAAO,EAAqB,KAAK,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAE/E,MAAM,MAAM,cAAc,GAAG;IAC3B,uBAAuB,EAAE,cAAc,CAAC;IACxC,WAAW,EAAE,eAAe,CAAC;IAC7B,iBAAiB,EAAE,sBAAsB,CAAC;IAC1C,cAAc,EAAE,mBAAmB,GAAG,MAAM,CAAC;IAC7C,mBAAmB,EAAE,uBAAuB,CAAC;IAC7C,mBAAmB,EAAE,uBAAuB,CAAC;IAC7C,WAAW,EAAE,eAAe,CAAC;IAC7B,YAAY,EAAE,gBAAgB,CAAC;IAC/B,kBAAkB,EAAE,uBAAuB,CAAC;IAC5C,qBAAqB,EAAE,iBAAiB,GAAG,MAAM,CAAC;IAClD,iBAAiB,EAAE,sBAAsB,GAAG,MAAM,CAAC;IACnD,aAAa,EAAE,kBAAkB,CAAC;IAClC,qBAAqB,EAAE,kBAAkB,CAAC;IAC1C,kBAAkB,EAAE,eAAe,CAAC;IACpC,iBAAiB,EAAE,sBAAsB,CAAC;IAC1C,cAAc,EAAE,kBAAkB,GAAG,MAAM,CAAC;IAC5C,cAAc,EAAE,kBAAkB,GAAG,MAAM,CAAC;IAC5C,aAAa,EAAE,kBAAkB,GAAG,MAAM,CAAC;IAC3C,eAAe,EAAE,oBAAoB,GAAG,MAAM,CAAC;IAC/C,yBAAyB,EAAE,8BAA8B,CAAC;IAC1D,QAAQ,EAAE,aAAa,GAAG,MAAM,CAAC;IACjC,mBAAmB,EAAE,uBAAuB,GAAG,MAAM,CAAC;CACvD,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,aAAa,CAAC,sBAAsB,CAAC,KAAK,CAAC,CAuBnB,CAAC"}
1
+ {"version":3,"file":"guards.d.ts","sourceRoot":"","sources":["../../../src/modules/repo/guards.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,9 @@
1
1
  import { entryPointGuard, isolatedRootsGuard, noOutsideCheckoutGuard, tsconfigAliasGuard, } from './cross-root.js';
2
+ import { blanketCastGuard } from './blanket-casts.js';
2
3
  import { ciParityGuard } from './ci-parity.js';
3
4
  import { dependencyOverrideGuard } from './dependency-overrides.js';
4
5
  import { documentDriftGuard } from './document-drift.js';
6
+ import { retryLoopActionGuard, swallowedActionGuard } from './e2e-actions.js';
5
7
  import { envPublicValuesGuard } from './env-public-values.js';
6
8
  import { installedPackagesAgnosticGuard, } from './installed-packages-agnostic.js';
7
9
  import { lineEndingGuard } from './line-endings.js';
@@ -20,6 +22,9 @@ import { workerConfigGuard } from './worker-config.js';
20
22
  export const REPO_GUARDS = Object.freeze([
21
23
  shellSafeGuard,
22
24
  lineEndingGuard,
25
+ blanketCastGuard,
26
+ retryLoopActionGuard,
27
+ swallowedActionGuard,
23
28
  walkerReadFailureGuard,
24
29
  testSourceRootGuard,
25
30
  testCaseModuleLoadGuard,
@@ -1 +1 @@
1
- {"version":3,"file":"guards.js","sourceRoot":"","sources":["../../../src/modules/repo/guards.ts"],"names":[],"mappings":"AACA,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,sBAAsB,EACtB,kBAAkB,GAKnB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,aAAa,EAAsB,MAAM,gBAAgB,CAAC;AACnE,OAAO,EAAE,uBAAuB,EAAgC,MAAM,2BAA2B,CAAC;AAClG,OAAO,EAAE,kBAAkB,EAA2B,MAAM,qBAAqB,CAAC;AAClF,OAAO,EAAE,oBAAoB,EAA6B,MAAM,wBAAwB,CAAC;AACzF,OAAO,EACL,8BAA8B,GAE/B,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAwB,MAAM,mBAAmB,CAAC;AAC1E,OAAO,EAAE,uBAAuB,EAAgC,MAAM,2BAA2B,CAAC;AAClG,OAAO,EAAE,kBAAkB,EAA2B,MAAM,sBAAsB,CAAC;AACnF,OAAO,EAAE,kBAAkB,EAA2B,MAAM,sBAAsB,CAAC;AACnF,OAAO,EAAE,eAAe,EAAwB,MAAM,mBAAmB,CAAC;AAC1E,OAAO,EAAE,cAAc,EAAuB,MAAM,iBAAiB,CAAC;AACtE,OAAO,EAAE,uBAAuB,EAAgC,MAAM,2BAA2B,CAAC;AAClG,OAAO,EAAE,uBAAuB,EAAgC,MAAM,6BAA6B,CAAC;AACpG,OAAO,EAAE,gBAAgB,EAAyB,MAAM,oBAAoB,CAAC;AAC7E,OAAO,EAAE,mBAAmB,EAA4B,MAAM,uBAAuB,CAAC;AACtF,OAAO,EAAE,sBAAsB,EAA+B,MAAM,0BAA0B,CAAC;AAC/F,OAAO,EAAE,sBAAsB,EAA+B,MAAM,0BAA0B,CAAC;AAC/F,OAAO,EAAE,iBAAiB,EAA0B,MAAM,oBAAoB,CAAC;AA2B/E,MAAM,CAAC,MAAM,WAAW,GAAiD,MAAM,CAAC,MAAM,CAAC;IACrF,cAAc;IACd,eAAe;IACf,sBAAsB;IACtB,mBAAmB;IACnB,uBAAuB;IACvB,uBAAuB;IACvB,eAAe;IACf,gBAAgB;IAChB,uBAAuB;IACvB,iBAAiB;IACjB,sBAAsB;IACtB,kBAAkB;IAClB,kBAAkB;IAClB,eAAe;IACf,sBAAsB;IACtB,kBAAkB;IAClB,kBAAkB;IAClB,kBAAkB;IAClB,oBAAoB;IACpB,8BAA8B;IAC9B,aAAa;IACb,uBAAuB;CACwB,CAAC,CAAC","sourcesContent":["import type { Absent, GenericGuardDefinition } from '../../node/suite/config.js';\nimport {\n entryPointGuard,\n isolatedRootsGuard,\n noOutsideCheckoutGuard,\n tsconfigAliasGuard,\n type EntryPointFacts,\n type IsolatedRootsFacts,\n type NoOutsideCheckoutFacts,\n type TsconfigAliasFacts,\n} from './cross-root.js';\nimport { ciParityGuard, type CiParityFacts } from './ci-parity.js';\nimport { dependencyOverrideGuard, type DependencyOverrideFacts } from './dependency-overrides.js';\nimport { documentDriftGuard, type DocumentDriftFacts } from './document-drift.js';\nimport { envPublicValuesGuard, type EnvPublicValuesFacts } from './env-public-values.js';\nimport {\n installedPackagesAgnosticGuard,\n type InstalledPackagesAgnosticFacts,\n} from './installed-packages-agnostic.js';\nimport { lineEndingGuard, type LineEndingFacts } from './line-endings.js';\nimport { localOnlyVariablesGuard, type LocalOnlyVariablesFacts } from './local-only-variables.js';\nimport { projectScriptGuard, type ProjectScriptFacts } from './project-scripts.js';\nimport { secretBindingGuard, type SecretBindingFacts } from './secret-bindings.js';\nimport { sharedFakeGuard, type SharedFakeFacts } from './shared-fakes.js';\nimport { shellSafeGuard, type ShellSafeFacts } from './shell-safe.js';\nimport { swallowedRejectionGuard, type SwallowedRejectionFacts } from './swallowed-rejections.js';\nimport { testCaseModuleLoadGuard, type TestCaseModuleLoadFacts } from './test-case-module-loads.js';\nimport { testTimeoutGuard, type TestTimeoutFacts } from './test-timeouts.js';\nimport { testSourceRootGuard, type TestSourceRootFacts } from './test-source-root.js';\nimport { walkerReadFailureGuard, type WalkerReadFailureFacts } from './walker-read-failure.js';\nimport { workerBoundedBodyGuard, type WorkerBoundedBodyFacts } from './worker-bounded-body.js';\nimport { workerConfigGuard, type WorkerConfigFacts } from './worker-config.js';\n\nexport type RepoGuardFacts = {\n shellSafeChildProcesses: ShellSafeFacts;\n lineEndings: LineEndingFacts;\n walkerReadFailure: WalkerReadFailureFacts;\n testSourceRoot: TestSourceRootFacts | Absent;\n testCaseModuleLoads: TestCaseModuleLoadFacts;\n swallowedRejections: SwallowedRejectionFacts;\n sharedFakes: SharedFakeFacts;\n testTimeouts: TestTimeoutFacts;\n localOnlyVariables: LocalOnlyVariablesFacts;\n workerConfigIsolation: WorkerConfigFacts | Absent;\n workerBoundedBody: WorkerBoundedBodyFacts | Absent;\n isolatedRoots: IsolatedRootsFacts;\n tsconfigAliasesInRoot: TsconfigAliasFacts;\n packageEntryPoints: EntryPointFacts;\n noOutsideCheckout: NoOutsideCheckoutFacts;\n projectScripts: ProjectScriptFacts | Absent;\n secretBindings: SecretBindingFacts | Absent;\n documentDrift: DocumentDriftFacts | Absent;\n envPublicValues: EnvPublicValuesFacts | Absent;\n installedPackagesAgnostic: InstalledPackagesAgnosticFacts;\n ciParity: CiParityFacts | Absent;\n dependencyOverrides: DependencyOverrideFacts | Absent;\n};\n\nexport const REPO_GUARDS: ReadonlyArray<GenericGuardDefinition<never>> = Object.freeze([\n shellSafeGuard,\n lineEndingGuard,\n walkerReadFailureGuard,\n testSourceRootGuard,\n testCaseModuleLoadGuard,\n swallowedRejectionGuard,\n sharedFakeGuard,\n testTimeoutGuard,\n localOnlyVariablesGuard,\n workerConfigGuard,\n workerBoundedBodyGuard,\n isolatedRootsGuard,\n tsconfigAliasGuard,\n entryPointGuard,\n noOutsideCheckoutGuard,\n projectScriptGuard,\n secretBindingGuard,\n documentDriftGuard,\n envPublicValuesGuard,\n installedPackagesAgnosticGuard,\n ciParityGuard,\n dependencyOverrideGuard,\n] as ReadonlyArray<GenericGuardDefinition<never>>);\n"]}
1
+ {"version":3,"file":"guards.js","sourceRoot":"","sources":["../../../src/modules/repo/guards.ts"],"names":[],"mappings":"AACA,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,sBAAsB,EACtB,kBAAkB,GAKnB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,gBAAgB,EAAyB,MAAM,oBAAoB,CAAC;AAC7E,OAAO,EAAE,aAAa,EAAsB,MAAM,gBAAgB,CAAC;AACnE,OAAO,EAAE,uBAAuB,EAAgC,MAAM,2BAA2B,CAAC;AAClG,OAAO,EAAE,kBAAkB,EAA2B,MAAM,qBAAqB,CAAC;AAClF,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAuB,MAAM,kBAAkB,CAAC;AACnG,OAAO,EAAE,oBAAoB,EAA6B,MAAM,wBAAwB,CAAC;AACzF,OAAO,EACL,8BAA8B,GAE/B,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAwB,MAAM,mBAAmB,CAAC;AAC1E,OAAO,EAAE,uBAAuB,EAAgC,MAAM,2BAA2B,CAAC;AAClG,OAAO,EAAE,kBAAkB,EAA2B,MAAM,sBAAsB,CAAC;AACnF,OAAO,EAAE,kBAAkB,EAA2B,MAAM,sBAAsB,CAAC;AACnF,OAAO,EAAE,eAAe,EAAwB,MAAM,mBAAmB,CAAC;AAC1E,OAAO,EAAE,cAAc,EAAuB,MAAM,iBAAiB,CAAC;AACtE,OAAO,EAAE,uBAAuB,EAAgC,MAAM,2BAA2B,CAAC;AAClG,OAAO,EAAE,uBAAuB,EAAgC,MAAM,6BAA6B,CAAC;AACpG,OAAO,EAAE,gBAAgB,EAAyB,MAAM,oBAAoB,CAAC;AAC7E,OAAO,EAAE,mBAAmB,EAA4B,MAAM,uBAAuB,CAAC;AACtF,OAAO,EAAE,sBAAsB,EAA+B,MAAM,0BAA0B,CAAC;AAC/F,OAAO,EAAE,sBAAsB,EAA+B,MAAM,0BAA0B,CAAC;AAC/F,OAAO,EAAE,iBAAiB,EAA0B,MAAM,oBAAoB,CAAC;AA8B/E,MAAM,CAAC,MAAM,WAAW,GAAiD,MAAM,CAAC,MAAM,CAAC;IACrF,cAAc;IACd,eAAe;IACf,gBAAgB;IAChB,oBAAoB;IACpB,oBAAoB;IACpB,sBAAsB;IACtB,mBAAmB;IACnB,uBAAuB;IACvB,uBAAuB;IACvB,eAAe;IACf,gBAAgB;IAChB,uBAAuB;IACvB,iBAAiB;IACjB,sBAAsB;IACtB,kBAAkB;IAClB,kBAAkB;IAClB,eAAe;IACf,sBAAsB;IACtB,kBAAkB;IAClB,kBAAkB;IAClB,kBAAkB;IAClB,oBAAoB;IACpB,8BAA8B;IAC9B,aAAa;IACb,uBAAuB;CACwB,CAAC,CAAC","sourcesContent":["import type { Absent, GenericGuardDefinition } from '../../node/suite/config.js';\nimport {\n entryPointGuard,\n isolatedRootsGuard,\n noOutsideCheckoutGuard,\n tsconfigAliasGuard,\n type EntryPointFacts,\n type IsolatedRootsFacts,\n type NoOutsideCheckoutFacts,\n type TsconfigAliasFacts,\n} from './cross-root.js';\nimport { blanketCastGuard, type BlanketCastFacts } from './blanket-casts.js';\nimport { ciParityGuard, type CiParityFacts } from './ci-parity.js';\nimport { dependencyOverrideGuard, type DependencyOverrideFacts } from './dependency-overrides.js';\nimport { documentDriftGuard, type DocumentDriftFacts } from './document-drift.js';\nimport { retryLoopActionGuard, swallowedActionGuard, type E2eActionFacts } from './e2e-actions.js';\nimport { envPublicValuesGuard, type EnvPublicValuesFacts } from './env-public-values.js';\nimport {\n installedPackagesAgnosticGuard,\n type InstalledPackagesAgnosticFacts,\n} from './installed-packages-agnostic.js';\nimport { lineEndingGuard, type LineEndingFacts } from './line-endings.js';\nimport { localOnlyVariablesGuard, type LocalOnlyVariablesFacts } from './local-only-variables.js';\nimport { projectScriptGuard, type ProjectScriptFacts } from './project-scripts.js';\nimport { secretBindingGuard, type SecretBindingFacts } from './secret-bindings.js';\nimport { sharedFakeGuard, type SharedFakeFacts } from './shared-fakes.js';\nimport { shellSafeGuard, type ShellSafeFacts } from './shell-safe.js';\nimport { swallowedRejectionGuard, type SwallowedRejectionFacts } from './swallowed-rejections.js';\nimport { testCaseModuleLoadGuard, type TestCaseModuleLoadFacts } from './test-case-module-loads.js';\nimport { testTimeoutGuard, type TestTimeoutFacts } from './test-timeouts.js';\nimport { testSourceRootGuard, type TestSourceRootFacts } from './test-source-root.js';\nimport { walkerReadFailureGuard, type WalkerReadFailureFacts } from './walker-read-failure.js';\nimport { workerBoundedBodyGuard, type WorkerBoundedBodyFacts } from './worker-bounded-body.js';\nimport { workerConfigGuard, type WorkerConfigFacts } from './worker-config.js';\n\nexport type RepoGuardFacts = {\n shellSafeChildProcesses: ShellSafeFacts;\n lineEndings: LineEndingFacts;\n blanketCasts: BlanketCastFacts;\n retryLoopActions: E2eActionFacts | Absent;\n swallowedE2eActions: E2eActionFacts | Absent;\n walkerReadFailure: WalkerReadFailureFacts;\n testSourceRoot: TestSourceRootFacts | Absent;\n testCaseModuleLoads: TestCaseModuleLoadFacts;\n swallowedRejections: SwallowedRejectionFacts;\n sharedFakes: SharedFakeFacts;\n testTimeouts: TestTimeoutFacts;\n localOnlyVariables: LocalOnlyVariablesFacts;\n workerConfigIsolation: WorkerConfigFacts | Absent;\n workerBoundedBody: WorkerBoundedBodyFacts | Absent;\n isolatedRoots: IsolatedRootsFacts;\n tsconfigAliasesInRoot: TsconfigAliasFacts;\n packageEntryPoints: EntryPointFacts;\n noOutsideCheckout: NoOutsideCheckoutFacts;\n projectScripts: ProjectScriptFacts | Absent;\n secretBindings: SecretBindingFacts | Absent;\n documentDrift: DocumentDriftFacts | Absent;\n envPublicValues: EnvPublicValuesFacts | Absent;\n installedPackagesAgnostic: InstalledPackagesAgnosticFacts;\n ciParity: CiParityFacts | Absent;\n dependencyOverrides: DependencyOverrideFacts | Absent;\n};\n\nexport const REPO_GUARDS: ReadonlyArray<GenericGuardDefinition<never>> = Object.freeze([\n shellSafeGuard,\n lineEndingGuard,\n blanketCastGuard,\n retryLoopActionGuard,\n swallowedActionGuard,\n walkerReadFailureGuard,\n testSourceRootGuard,\n testCaseModuleLoadGuard,\n swallowedRejectionGuard,\n sharedFakeGuard,\n testTimeoutGuard,\n localOnlyVariablesGuard,\n workerConfigGuard,\n workerBoundedBodyGuard,\n isolatedRootsGuard,\n tsconfigAliasGuard,\n entryPointGuard,\n noOutsideCheckoutGuard,\n projectScriptGuard,\n secretBindingGuard,\n documentDriftGuard,\n envPublicValuesGuard,\n installedPackagesAgnosticGuard,\n ciParityGuard,\n dependencyOverrideGuard,\n] as ReadonlyArray<GenericGuardDefinition<never>>);\n"]}
@@ -10,6 +10,10 @@ export interface GenericGuardDefinition<Facts> {
10
10
  factProblems(facts: Facts): string[];
11
11
  build(facts: Facts): Guard[];
12
12
  }
13
+ export interface ConfiguredGuardDefinition {
14
+ factProblems(facts: unknown): string[];
15
+ build(facts: unknown): Guard[];
16
+ }
13
17
  export declare const isAbsent: (value: unknown) => value is Absent;
14
18
  export declare function configProblems(config: {
15
19
  layout: RepoLayout;
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/node/suite/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAE5C,OAAO,EAAiB,KAAK,UAAU,EAAE,KAAK,QAAQ,EAAE,MAAM,cAAc,CAAC;AAE7E,MAAM,WAAW,MAAM;IACrB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,sBAAsB,CAAC,KAAK;IAC3C,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,GAAG,KAAK,CAAC;IAC3B,qBAAqB,CAAC,EAAE,QAAQ,CAAC;IACjC,YAAY,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,EAAE,CAAC;IACrC,KAAK,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK,EAAE,CAAC;CAC9B;AAED,eAAO,MAAM,QAAQ,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,MAIL,CAAC;AAE/C,wBAAgB,cAAc,CAC5B,MAAM,EAAE;IACN,MAAM,EAAE,UAAU,CAAC;IACnB,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC3C,QAAQ,EAAE,SAAS,KAAK,EAAE,CAAC;CAC5B,EACD,WAAW,EAAE,aAAa,CAAC,sBAAsB,CAAC,KAAK,CAAC,CAAC,GACxD,MAAM,EAAE,CA+CV;AAED,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAC1C,WAAW,EAAE,aAAa,CAAC,sBAAsB,CAAC,KAAK,CAAC,CAAC,GACxD,KAAK,EAAE,CAIT"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/node/suite/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAE5C,OAAO,EAAiB,KAAK,UAAU,EAAE,KAAK,QAAQ,EAAE,MAAM,cAAc,CAAC;AAE7E,MAAM,WAAW,MAAM;IACrB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,sBAAsB,CAAC,KAAK;IAC3C,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,GAAG,KAAK,CAAC;IAC3B,qBAAqB,CAAC,EAAE,QAAQ,CAAC;IACjC,YAAY,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,EAAE,CAAC;IACrC,KAAK,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK,EAAE,CAAC;CAC9B;AAID,MAAM,WAAW,yBAAyB;IACxC,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,EAAE,CAAC;IACvC,KAAK,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,EAAE,CAAC;CAChC;AAED,eAAO,MAAM,QAAQ,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,MAIL,CAAC;AAE/C,wBAAgB,cAAc,CAC5B,MAAM,EAAE;IACN,MAAM,EAAE,UAAU,CAAC;IACnB,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC3C,QAAQ,EAAE,SAAS,KAAK,EAAE,CAAC;CAC5B,EACD,WAAW,EAAE,aAAa,CAAC,sBAAsB,CAAC,KAAK,CAAC,CAAC,GACxD,MAAM,EAAE,CAgDV;AAED,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAC1C,WAAW,EAAE,aAAa,CAAC,sBAAsB,CAAC,KAAK,CAAC,CAAC,GACxD,KAAK,EAAE,CAKT"}
@@ -32,7 +32,8 @@ export function configProblems(config, definitions) {
32
32
  }
33
33
  continue;
34
34
  }
35
- for (const problem of definition.factProblems(value))
35
+ const configured = definition;
36
+ for (const problem of configured.factProblems(value))
36
37
  problems.push(`generic.${definition.id}: ${problem}`);
37
38
  }
38
39
  const titles = new Set();
@@ -50,6 +51,9 @@ export function configProblems(config, definitions) {
50
51
  return problems;
51
52
  }
52
53
  export function buildGenericGuards(generic, definitions) {
53
- return definitions.flatMap((d) => isAbsent(generic[d.id]) ? [] : d.build(generic[d.id]));
54
+ return definitions.flatMap((definition) => {
55
+ const configured = definition;
56
+ return isAbsent(generic[definition.id]) ? [] : configured.build(generic[definition.id]);
57
+ });
54
58
  }
55
59
  //# sourceMappingURL=config.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/node/suite/config.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Guard } from '../../guard.js';\nimport { layoutProblems } from '../reader.js';\nimport { declaredRoots, type RepoLayout, type RootRole } from '../layout.js';\n\nexport interface Absent {\n absent: string;\n}\n\nexport interface GenericGuardDefinition<Facts> {\n id: string;\n absentWhen: string | false;\n absentOnlyWithoutRoot?: RootRole;\n factProblems(facts: Facts): string[];\n build(facts: Facts): Guard[];\n}\n\nexport const isAbsent = (value: unknown): value is Absent =>\n !!value &&\n typeof value === 'object' &&\n Object.keys(value).length === 1 &&\n typeof (value as Absent).absent === 'string';\n\nexport function configProblems(\n config: {\n layout: RepoLayout;\n generic: Readonly<Record<string, unknown>>;\n business: readonly Guard[];\n },\n definitions: ReadonlyArray<GenericGuardDefinition<never>>,\n): string[] {\n const problems: string[] = [];\n if (!config || typeof config !== 'object') return ['the guard config is missing'];\n problems.push(...layoutProblems(config.layout));\n const generic = config.generic ?? {};\n const known = new Set(definitions.map((d) => d.id));\n for (const key of Object.keys(generic))\n if (!known.has(key)) problems.push(`generic.${key} names no guard this package carries`);\n for (const definition of definitions) {\n if (!(definition.id in generic)) {\n problems.push(\n `generic.${definition.id} is missing — every guard needs its facts${definition.absentWhen ? ' or an { absent: why }' : ''}`,\n );\n continue;\n }\n const value = generic[definition.id];\n if (isAbsent(value)) {\n if (!definition.absentWhen)\n problems.push(`generic.${definition.id} cannot be declared absent: every app runs it`);\n else if (!value.absent.trim())\n problems.push(`generic.${definition.id} is declared absent with no why`);\n else if (\n definition.absentOnlyWithoutRoot &&\n config.layout?.roots &&\n declaredRoots(config.layout).some((r) => r.role === definition.absentOnlyWithoutRoot)\n ) {\n problems.push(\n `generic.${definition.id} is declared absent, but the layout declares a ${definition.absentOnlyWithoutRoot} root for it to check`,\n );\n }\n continue;\n }\n for (const problem of definition.factProblems(value as never))\n problems.push(`generic.${definition.id}: ${problem}`);\n }\n const titles = new Set<string>();\n for (const guard of config.business ?? []) {\n if (!guard || typeof guard.run !== 'function' || !guard.title) {\n problems.push('a business guard was not built from a shape');\n continue;\n }\n if (titles.has(guard.title))\n problems.push(`two business guards share the title \"${guard.title}\"`);\n titles.add(guard.title);\n if (!guard.samples) problems.push(`business guard \"${guard.title}\" carries no samples`);\n }\n return problems;\n}\n\nexport function buildGenericGuards(\n generic: Readonly<Record<string, unknown>>,\n definitions: ReadonlyArray<GenericGuardDefinition<never>>,\n): Guard[] {\n return definitions.flatMap((d) =>\n isAbsent(generic[d.id]) ? [] : d.build(generic[d.id] as never),\n );\n}\n"]}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/node/suite/config.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Guard } from '../../guard.js';\nimport { layoutProblems } from '../reader.js';\nimport { declaredRoots, type RepoLayout, type RootRole } from '../layout.js';\n\nexport interface Absent {\n absent: string;\n}\n\nexport interface GenericGuardDefinition<Facts> {\n id: string;\n absentWhen: string | false;\n absentOnlyWithoutRoot?: RootRole;\n factProblems(facts: Facts): string[];\n build(facts: Facts): Guard[];\n}\n\n// The suite holds guards of every facts type in one list, so it hands each the config value as it\n// arrived; factProblems checks that value before build runs.\nexport interface ConfiguredGuardDefinition {\n factProblems(facts: unknown): string[];\n build(facts: unknown): Guard[];\n}\n\nexport const isAbsent = (value: unknown): value is Absent =>\n !!value &&\n typeof value === 'object' &&\n Object.keys(value).length === 1 &&\n typeof (value as Absent).absent === 'string';\n\nexport function configProblems(\n config: {\n layout: RepoLayout;\n generic: Readonly<Record<string, unknown>>;\n business: readonly Guard[];\n },\n definitions: ReadonlyArray<GenericGuardDefinition<never>>,\n): string[] {\n const problems: string[] = [];\n if (!config || typeof config !== 'object') return ['the guard config is missing'];\n problems.push(...layoutProblems(config.layout));\n const generic = config.generic ?? {};\n const known = new Set(definitions.map((d) => d.id));\n for (const key of Object.keys(generic))\n if (!known.has(key)) problems.push(`generic.${key} names no guard this package carries`);\n for (const definition of definitions) {\n if (!(definition.id in generic)) {\n problems.push(\n `generic.${definition.id} is missing — every guard needs its facts${definition.absentWhen ? ' or an { absent: why }' : ''}`,\n );\n continue;\n }\n const value = generic[definition.id];\n if (isAbsent(value)) {\n if (!definition.absentWhen)\n problems.push(`generic.${definition.id} cannot be declared absent: every app runs it`);\n else if (!value.absent.trim())\n problems.push(`generic.${definition.id} is declared absent with no why`);\n else if (\n definition.absentOnlyWithoutRoot &&\n config.layout?.roots &&\n declaredRoots(config.layout).some((r) => r.role === definition.absentOnlyWithoutRoot)\n ) {\n problems.push(\n `generic.${definition.id} is declared absent, but the layout declares a ${definition.absentOnlyWithoutRoot} root for it to check`,\n );\n }\n continue;\n }\n const configured: ConfiguredGuardDefinition = definition;\n for (const problem of configured.factProblems(value))\n problems.push(`generic.${definition.id}: ${problem}`);\n }\n const titles = new Set<string>();\n for (const guard of config.business ?? []) {\n if (!guard || typeof guard.run !== 'function' || !guard.title) {\n problems.push('a business guard was not built from a shape');\n continue;\n }\n if (titles.has(guard.title))\n problems.push(`two business guards share the title \"${guard.title}\"`);\n titles.add(guard.title);\n if (!guard.samples) problems.push(`business guard \"${guard.title}\" carries no samples`);\n }\n return problems;\n}\n\nexport function buildGenericGuards(\n generic: Readonly<Record<string, unknown>>,\n definitions: ReadonlyArray<GenericGuardDefinition<never>>,\n): Guard[] {\n return definitions.flatMap((definition) => {\n const configured: ConfiguredGuardDefinition = definition;\n return isAbsent(generic[definition.id]) ? [] : configured.build(generic[definition.id]);\n });\n}\n"]}
@@ -2,6 +2,7 @@ export interface VitestAssertion {
2
2
  status?: string;
3
3
  title?: string;
4
4
  fullName?: string;
5
+ duration?: number | null;
5
6
  failureMessages?: string[];
6
7
  }
7
8
  export interface VitestFileResult {
@@ -1 +1 @@
1
- {"version":3,"file":"vitest-report.d.ts","sourceRoot":"","sources":["../../../src/node/tooling/vitest-report.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;CAC5B;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gBAAgB,CAAC,EAAE,eAAe,EAAE,CAAC;CACtC;AAED,MAAM,WAAW,gBAAgB;IAC/B,WAAW,CAAC,EAAE,gBAAgB,EAAE,CAAC;IACjC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;CACtB;AAGD,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAcvE;AAID,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,gBAAgB,GAAG,IAAI,GAAG;IACtE,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,aAAa,EAAE,YAAY,EAAE,CAAC;IAC9B,YAAY,EAAE,WAAW,EAAE,CAAC;IAC5B,MAAM,EAAE,YAAY,GAAG,IAAI,CAAC;CAC7B,CAgCA;AAED,wBAAgB,kBAAkB,CAAC,EACjC,IAAI,EACJ,MAAM,EACN,aAAa,GACd,EAAE;IACD,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,YAAY,GAAG,IAAI,CAAC;IAC5B,aAAa,EAAE,YAAY,EAAE,CAAC;CAC/B,GAAG;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAWhD;AAED,wBAAgB,mBAAmB,CAAC,aAAa,EAAE,YAAY,EAAE,GAAG,MAAM,CAUzE"}
1
+ {"version":3,"file":"vitest-report.d.ts","sourceRoot":"","sources":["../../../src/node/tooling/vitest-report.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;CAC5B;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gBAAgB,CAAC,EAAE,eAAe,EAAE,CAAC;CACtC;AAED,MAAM,WAAW,gBAAgB;IAC/B,WAAW,CAAC,EAAE,gBAAgB,EAAE,CAAC;IACjC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;CACtB;AAGD,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAcvE;AAcD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,gBAAgB,GAAG,IAAI,GAAG;IACtE,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,aAAa,EAAE,YAAY,EAAE,CAAC;IAC9B,YAAY,EAAE,WAAW,EAAE,CAAC;IAC5B,MAAM,EAAE,YAAY,GAAG,IAAI,CAAC;CAC7B,CAoCA;AAED,wBAAgB,kBAAkB,CAAC,EACjC,IAAI,EACJ,MAAM,EACN,aAAa,GACd,EAAE;IACD,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,YAAY,GAAG,IAAI,CAAC;IAC5B,aAAa,EAAE,YAAY,EAAE,CAAC;CAC/B,GAAG;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAWhD;AAED,wBAAgB,mBAAmB,CAAC,aAAa,EAAE,YAAY,EAAE,GAAG,MAAM,CAUzE"}
@@ -17,6 +17,15 @@ export function parseVitestJson(stdout) {
17
17
  }
18
18
  }
19
19
  }
20
+ const TIMEOUT_PLACEHOLDER = 'Error: STACK_TRACE_ERROR';
21
+ // Vitest reports a test that passed its time limit with the stack of a placeholder error, so the
22
+ // report carries `STACK_TRACE_ERROR` where the "timed out" message should be.
23
+ function readableFailureMessage(message, durationMs) {
24
+ if (!message.startsWith(TIMEOUT_PLACEHOLDER))
25
+ return message;
26
+ const ran = typeof durationMs === 'number' ? ` after ${Math.round(durationMs)} ms` : '';
27
+ return `Timed out${ran}: the test passed its time limit.${message.slice(TIMEOUT_PLACEHOLDER.length)}`;
28
+ }
20
29
  // A test file that failed to load carries its error as the file's own `message` and adds nothing
21
30
  // to `numFailedTests`.
22
31
  export function collectVitestFailures(report) {
@@ -31,7 +40,11 @@ export function collectVitestFailures(report) {
31
40
  const title = a.fullName || a.title || '';
32
41
  if (a.status === 'failed') {
33
42
  failedTests += 1;
34
- failures.push({ file: file.name, title, messages: a.failureMessages ?? [] });
43
+ failures.push({
44
+ file: file.name,
45
+ title,
46
+ messages: (a.failureMessages ?? []).map((m) => readableFailureMessage(m, a.duration)),
47
+ });
35
48
  }
36
49
  else if (a.status === 'skipped' || a.status === 'pending' || a.status === 'todo') {
37
50
  skippedTests.push({ file: file.name, title, status: a.status });
@@ -1 +1 @@
1
- {"version":3,"file":"vitest-report.js","sourceRoot":"","sources":["../../../src/node/tooling/vitest-report.ts"],"names":[],"mappings":"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","sourcesContent":["export interface VitestAssertion {\n status?: string;\n title?: string;\n fullName?: string;\n failureMessages?: string[];\n}\n\nexport interface VitestFileResult {\n name: string;\n status?: string;\n message?: string;\n assertionResults?: VitestAssertion[];\n}\n\nexport interface VitestJsonReport {\n testResults?: VitestFileResult[];\n numPassedTests?: number;\n numFailedTests?: number;\n numPendingTests?: number;\n numTodoTests?: number;\n numTotalTests?: number;\n}\n\nexport interface TestFailure {\n file: string;\n title: string;\n messages: string[];\n}\n\nexport interface SuiteFailure {\n file: string;\n message: string;\n}\n\nexport interface SkippedTest {\n file: string;\n title: string;\n status: string;\n}\n\nexport interface VitestTotals {\n passed: number;\n failed: number;\n skipped: number;\n total: number;\n failedSuites: number;\n}\n\n// Vitest may print other text around the JSON object.\nexport function parseVitestJson(stdout: string): VitestJsonReport | null {\n const trimmed = stdout.trim();\n try {\n return JSON.parse(trimmed) as VitestJsonReport;\n } catch {\n const first = trimmed.indexOf('{');\n const last = trimmed.lastIndexOf('}');\n if (first === -1 || last === -1 || last < first) return null;\n try {\n return JSON.parse(trimmed.slice(first, last + 1)) as VitestJsonReport;\n } catch {\n return null;\n }\n }\n}\n\n// A test file that failed to load carries its error as the file's own `message` and adds nothing\n// to `numFailedTests`.\nexport function collectVitestFailures(report: VitestJsonReport | null): {\n failures: TestFailure[];\n suiteFailures: SuiteFailure[];\n skippedTests: SkippedTest[];\n totals: VitestTotals | null;\n} {\n if (!report) return { failures: [], suiteFailures: [], skippedTests: [], totals: null };\n const failures: TestFailure[] = [];\n const suiteFailures: SuiteFailure[] = [];\n const skippedTests: SkippedTest[] = [];\n for (const file of report.testResults ?? []) {\n let failedTests = 0;\n for (const a of file.assertionResults ?? []) {\n const title = a.fullName || a.title || '';\n if (a.status === 'failed') {\n failedTests += 1;\n failures.push({ file: file.name, title, messages: a.failureMessages ?? [] });\n } else if (a.status === 'skipped' || a.status === 'pending' || a.status === 'todo') {\n skippedTests.push({ file: file.name, title, status: a.status });\n }\n }\n if (file.status === 'failed' && failedTests === 0) {\n suiteFailures.push({\n file: file.name,\n message: file.message || '(no error message in the report)',\n });\n }\n }\n const totals: VitestTotals = {\n passed: report.numPassedTests ?? 0,\n failed: report.numFailedTests ?? 0,\n // `.skip` and `xit` land in numPendingTests; `.todo` is counted on its own.\n skipped: (report.numPendingTests ?? 0) + (report.numTodoTests ?? 0),\n total: report.numTotalTests ?? 0,\n failedSuites: suiteFailures.length,\n };\n return { failures, suiteFailures, skippedTests, totals };\n}\n\nexport function vitestStageVerdict({\n code,\n totals,\n suiteFailures,\n}: {\n code: number;\n totals: VitestTotals | null;\n suiteFailures: SuiteFailure[];\n}): { ok: boolean; skippedReason: string | null } {\n const skipped = totals?.skipped ?? 0;\n const ok =\n code === 0 &&\n totals !== null &&\n totals.failed === 0 &&\n skipped === 0 &&\n suiteFailures.length === 0;\n const skippedReason =\n skipped > 0 ? `${skipped} skipped/pending test(s) make this stage non-green.` : null;\n return { ok, skippedReason };\n}\n\nexport function formatSuiteFailures(suiteFailures: SuiteFailure[]): string {\n return suiteFailures\n .map(\n (suite) =>\n `Failed to load: ${suite.file}\\n${suite.message\n .split('\\n')\n .map((line) => ` ${line}`)\n .join('\\n')}`,\n )\n .join('\\n\\n');\n}\n"]}
1
+ {"version":3,"file":"vitest-report.js","sourceRoot":"","sources":["../../../src/node/tooling/vitest-report.ts"],"names":[],"mappings":"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","sourcesContent":["export interface VitestAssertion {\n status?: string;\n title?: string;\n fullName?: string;\n duration?: number | null;\n failureMessages?: string[];\n}\n\nexport interface VitestFileResult {\n name: string;\n status?: string;\n message?: string;\n assertionResults?: VitestAssertion[];\n}\n\nexport interface VitestJsonReport {\n testResults?: VitestFileResult[];\n numPassedTests?: number;\n numFailedTests?: number;\n numPendingTests?: number;\n numTodoTests?: number;\n numTotalTests?: number;\n}\n\nexport interface TestFailure {\n file: string;\n title: string;\n messages: string[];\n}\n\nexport interface SuiteFailure {\n file: string;\n message: string;\n}\n\nexport interface SkippedTest {\n file: string;\n title: string;\n status: string;\n}\n\nexport interface VitestTotals {\n passed: number;\n failed: number;\n skipped: number;\n total: number;\n failedSuites: number;\n}\n\n// Vitest may print other text around the JSON object.\nexport function parseVitestJson(stdout: string): VitestJsonReport | null {\n const trimmed = stdout.trim();\n try {\n return JSON.parse(trimmed) as VitestJsonReport;\n } catch {\n const first = trimmed.indexOf('{');\n const last = trimmed.lastIndexOf('}');\n if (first === -1 || last === -1 || last < first) return null;\n try {\n return JSON.parse(trimmed.slice(first, last + 1)) as VitestJsonReport;\n } catch {\n return null;\n }\n }\n}\n\nconst TIMEOUT_PLACEHOLDER = 'Error: STACK_TRACE_ERROR';\n\n// Vitest reports a test that passed its time limit with the stack of a placeholder error, so the\n// report carries `STACK_TRACE_ERROR` where the \"timed out\" message should be.\nfunction readableFailureMessage(message: string, durationMs?: number | null): string {\n if (!message.startsWith(TIMEOUT_PLACEHOLDER)) return message;\n const ran = typeof durationMs === 'number' ? ` after ${Math.round(durationMs)} ms` : '';\n return `Timed out${ran}: the test passed its time limit.${message.slice(TIMEOUT_PLACEHOLDER.length)}`;\n}\n\n// A test file that failed to load carries its error as the file's own `message` and adds nothing\n// to `numFailedTests`.\nexport function collectVitestFailures(report: VitestJsonReport | null): {\n failures: TestFailure[];\n suiteFailures: SuiteFailure[];\n skippedTests: SkippedTest[];\n totals: VitestTotals | null;\n} {\n if (!report) return { failures: [], suiteFailures: [], skippedTests: [], totals: null };\n const failures: TestFailure[] = [];\n const suiteFailures: SuiteFailure[] = [];\n const skippedTests: SkippedTest[] = [];\n for (const file of report.testResults ?? []) {\n let failedTests = 0;\n for (const a of file.assertionResults ?? []) {\n const title = a.fullName || a.title || '';\n if (a.status === 'failed') {\n failedTests += 1;\n failures.push({\n file: file.name,\n title,\n messages: (a.failureMessages ?? []).map((m) => readableFailureMessage(m, a.duration)),\n });\n } else if (a.status === 'skipped' || a.status === 'pending' || a.status === 'todo') {\n skippedTests.push({ file: file.name, title, status: a.status });\n }\n }\n if (file.status === 'failed' && failedTests === 0) {\n suiteFailures.push({\n file: file.name,\n message: file.message || '(no error message in the report)',\n });\n }\n }\n const totals: VitestTotals = {\n passed: report.numPassedTests ?? 0,\n failed: report.numFailedTests ?? 0,\n // `.skip` and `xit` land in numPendingTests; `.todo` is counted on its own.\n skipped: (report.numPendingTests ?? 0) + (report.numTodoTests ?? 0),\n total: report.numTotalTests ?? 0,\n failedSuites: suiteFailures.length,\n };\n return { failures, suiteFailures, skippedTests, totals };\n}\n\nexport function vitestStageVerdict({\n code,\n totals,\n suiteFailures,\n}: {\n code: number;\n totals: VitestTotals | null;\n suiteFailures: SuiteFailure[];\n}): { ok: boolean; skippedReason: string | null } {\n const skipped = totals?.skipped ?? 0;\n const ok =\n code === 0 &&\n totals !== null &&\n totals.failed === 0 &&\n skipped === 0 &&\n suiteFailures.length === 0;\n const skippedReason =\n skipped > 0 ? `${skipped} skipped/pending test(s) make this stage non-green.` : null;\n return { ok, skippedReason };\n}\n\nexport function formatSuiteFailures(suiteFailures: SuiteFailure[]): string {\n return suiteFailures\n .map(\n (suite) =>\n `Failed to load: ${suite.file}\\n${suite.message\n .split('\\n')\n .map((line) => ` ${line}`)\n .join('\\n')}`,\n )\n .join('\\n\\n');\n}\n"]}
@@ -0,0 +1,18 @@
1
+ import type { Finding } from '../guard.js';
2
+ import type { FileView } from '../reader/view.js';
3
+ export interface AnchoredEntry {
4
+ path: string;
5
+ anchor: string;
6
+ why: string;
7
+ }
8
+ export interface Occurrence {
9
+ start: number;
10
+ end: number;
11
+ detail: string;
12
+ }
13
+ export declare function anchoredProblems(value: unknown, what: string, problems: string[]): void;
14
+ export declare function anchoredFindings(hitsByFile: ReadonlyArray<{
15
+ file: FileView;
16
+ hits: readonly Occurrence[];
17
+ }>, reviewed: readonly AnchoredEntry[], staleDetail: string): Finding[];
18
+ //# sourceMappingURL=anchored-review.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anchored-review.d.ts","sourceRoot":"","sources":["../../src/support/anchored-review.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAGlD,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI,CAKvF;AAID,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,aAAa,CAAC;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,SAAS,UAAU,EAAE,CAAA;CAAE,CAAC,EAC1E,QAAQ,EAAE,SAAS,aAAa,EAAE,EAClC,WAAW,EAAE,MAAM,GAClB,OAAO,EAAE,CAqCX"}
@@ -0,0 +1,50 @@
1
+ import { isText, reviewedProblems } from './facts.js';
2
+ export function anchoredProblems(value, what, problems) {
3
+ reviewedProblems(value, what, problems, {
4
+ id: (entry) => (isText(entry.anchor) ? `${String(entry.path)} ${entry.anchor}` : undefined),
5
+ idNames: 'anchor (the source text naming the one occurrence it excuses)',
6
+ });
7
+ }
8
+ // An entry excuses exactly the one occurrence whose source text holds its anchor, so a second
9
+ // occurrence in the same file is never covered by it.
10
+ export function anchoredFindings(hitsByFile, reviewed, staleDetail) {
11
+ const findings = [];
12
+ const byPath = new Map();
13
+ for (const entry of reviewed)
14
+ byPath.set(entry.path, [...(byPath.get(entry.path) ?? []), entry]);
15
+ const matched = new Set();
16
+ for (const { file, hits } of hitsByFile) {
17
+ const entries = byPath.get(file.path) ?? [];
18
+ for (const hit of hits) {
19
+ const text = file.text.slice(hit.start, hit.end);
20
+ const owners = entries.filter((entry) => text.includes(entry.anchor));
21
+ if (owners.length === 1 && !matched.has(owners[0])) {
22
+ matched.add(owners[0]);
23
+ continue;
24
+ }
25
+ const line = file.lineOf(hit.start);
26
+ if (owners.length > 1)
27
+ findings.push({
28
+ file: file.path,
29
+ line,
30
+ detail: `${hit.detail}, and ${owners.length} reviewed anchors match it — make each anchor name one occurrence`,
31
+ });
32
+ else if (owners.length === 1)
33
+ findings.push({
34
+ file: file.path,
35
+ line,
36
+ detail: `${hit.detail}, and its reviewed anchor "${owners[0].anchor}" already excuses another occurrence — make each anchor name one occurrence`,
37
+ });
38
+ else
39
+ findings.push({ file: file.path, line, detail: hit.detail });
40
+ }
41
+ }
42
+ for (const entry of reviewed)
43
+ if (!matched.has(entry))
44
+ findings.push({
45
+ file: entry.path,
46
+ detail: `stale reviewed entry "${entry.anchor}": ${staleDetail}`,
47
+ });
48
+ return findings;
49
+ }
50
+ //# sourceMappingURL=anchored-review.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anchored-review.js","sourceRoot":"","sources":["../../src/support/anchored-review.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AActD,MAAM,UAAU,gBAAgB,CAAC,KAAc,EAAE,IAAY,EAAE,QAAkB;IAC/E,gBAAgB,CAAC,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE;QACtC,EAAE,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;QAC3F,OAAO,EAAE,+DAA+D;KACzE,CAAC,CAAC;AACL,CAAC;AAED,8FAA8F;AAC9F,sDAAsD;AACtD,MAAM,UAAU,gBAAgB,CAC9B,UAA0E,EAC1E,QAAkC,EAClC,WAAmB;IAEnB,MAAM,QAAQ,GAAc,EAAE,CAAC;IAC/B,MAAM,MAAM,GAAG,IAAI,GAAG,EAA2B,CAAC;IAClD,KAAK,MAAM,KAAK,IAAI,QAAQ;QAAE,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC;IACjG,MAAM,OAAO,GAAG,IAAI,GAAG,EAAiB,CAAC;IACzC,KAAK,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,UAAU,EAAE,CAAC;QACxC,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5C,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC;YACjD,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;YACtE,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;gBACnD,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;gBACvB,SAAS;YACX,CAAC;YACD,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YACpC,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;gBACnB,QAAQ,CAAC,IAAI,CAAC;oBACZ,IAAI,EAAE,IAAI,CAAC,IAAI;oBACf,IAAI;oBACJ,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,SAAS,MAAM,CAAC,MAAM,mEAAmE;iBAC/G,CAAC,CAAC;iBACA,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;gBAC1B,QAAQ,CAAC,IAAI,CAAC;oBACZ,IAAI,EAAE,IAAI,CAAC,IAAI;oBACf,IAAI;oBACJ,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,8BAA8B,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,6EAA6E;iBACjJ,CAAC,CAAC;;gBACA,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;QACpE,CAAC;IACH,CAAC;IACD,KAAK,MAAM,KAAK,IAAI,QAAQ;QAC1B,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC;YACrB,QAAQ,CAAC,IAAI,CAAC;gBACZ,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,MAAM,EAAE,yBAAyB,KAAK,CAAC,MAAM,MAAM,WAAW,EAAE;aACjE,CAAC,CAAC;IACP,OAAO,QAAQ,CAAC;AAClB,CAAC","sourcesContent":["import type { Finding } from '../guard.js';\nimport type { FileView } from '../reader/view.js';\nimport { isText, reviewedProblems } from './facts.js';\n\nexport interface AnchoredEntry {\n path: string;\n anchor: string;\n why: string;\n}\n\nexport interface Occurrence {\n start: number;\n end: number;\n detail: string;\n}\n\nexport function anchoredProblems(value: unknown, what: string, problems: string[]): void {\n reviewedProblems(value, what, problems, {\n id: (entry) => (isText(entry.anchor) ? `${String(entry.path)} ${entry.anchor}` : undefined),\n idNames: 'anchor (the source text naming the one occurrence it excuses)',\n });\n}\n\n// An entry excuses exactly the one occurrence whose source text holds its anchor, so a second\n// occurrence in the same file is never covered by it.\nexport function anchoredFindings(\n hitsByFile: ReadonlyArray<{ file: FileView; hits: readonly Occurrence[] }>,\n reviewed: readonly AnchoredEntry[],\n staleDetail: string,\n): Finding[] {\n const findings: Finding[] = [];\n const byPath = new Map<string, AnchoredEntry[]>();\n for (const entry of reviewed) byPath.set(entry.path, [...(byPath.get(entry.path) ?? []), entry]);\n const matched = new Set<AnchoredEntry>();\n for (const { file, hits } of hitsByFile) {\n const entries = byPath.get(file.path) ?? [];\n for (const hit of hits) {\n const text = file.text.slice(hit.start, hit.end);\n const owners = entries.filter((entry) => text.includes(entry.anchor));\n if (owners.length === 1 && !matched.has(owners[0])) {\n matched.add(owners[0]);\n continue;\n }\n const line = file.lineOf(hit.start);\n if (owners.length > 1)\n findings.push({\n file: file.path,\n line,\n detail: `${hit.detail}, and ${owners.length} reviewed anchors match it — make each anchor name one occurrence`,\n });\n else if (owners.length === 1)\n findings.push({\n file: file.path,\n line,\n detail: `${hit.detail}, and its reviewed anchor \"${owners[0].anchor}\" already excuses another occurrence — make each anchor name one occurrence`,\n });\n else findings.push({ file: file.path, line, detail: hit.detail });\n }\n }\n for (const entry of reviewed)\n if (!matched.has(entry))\n findings.push({\n file: entry.path,\n detail: `stale reviewed entry \"${entry.anchor}\": ${staleDetail}`,\n });\n return findings;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ttt-productions/repo-guards",
3
- "version": "0.1.21",
3
+ "version": "0.1.23",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/ttt-productions/ttt-packages.git",