@ttt-productions/repo-guards 0.1.28 → 0.1.30

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.
@@ -6,6 +6,7 @@ import { type DependencyOverrideFacts } from './dependency-overrides.js';
6
6
  import { type DocumentDriftFacts } from './document-drift.js';
7
7
  import { type E2eActionFacts } from './e2e-actions.js';
8
8
  import { type EnvPublicValuesFacts } from './env-public-values.js';
9
+ import { type HandBuiltReadFacts } from './hand-built-reads.js';
9
10
  import { type InstalledPackagesAgnosticFacts } from './installed-packages-agnostic.js';
10
11
  import { type LineEndingFacts } from './line-endings.js';
11
12
  import { type LocalOnlyVariablesFacts } from './local-only-variables.js';
@@ -33,6 +34,7 @@ export type RepoGuardFacts = {
33
34
  swallowedRejections: SwallowedRejectionFacts;
34
35
  sharedFakes: SharedFakeFacts;
35
36
  typedMocks: TypedMockFacts;
37
+ handBuiltReads: HandBuiltReadFacts;
36
38
  testTimeouts: TestTimeoutFacts;
37
39
  localOnlyVariables: LocalOnlyVariablesFacts;
38
40
  workerConfigIsolation: WorkerConfigFacts | Absent;
@@ -1 +1 @@
1
- {"version":3,"file":"guards.d.ts","sourceRoot":"","sources":["../../../src/modules/repo/guards.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"guards.d.ts","sourceRoot":"","sources":["../../../src/modules/repo/guards.ts"],"names":[],"mappings":"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"}
@@ -5,6 +5,7 @@ import { dependencyOverrideGuard } from './dependency-overrides.js';
5
5
  import { documentDriftGuard } from './document-drift.js';
6
6
  import { retryLoopActionGuard, swallowedActionGuard } from './e2e-actions.js';
7
7
  import { envPublicValuesGuard } from './env-public-values.js';
8
+ import { handBuiltReadGuard } from './hand-built-reads.js';
8
9
  import { installedPackagesAgnosticGuard, } from './installed-packages-agnostic.js';
9
10
  import { lineEndingGuard } from './line-endings.js';
10
11
  import { localOnlyVariablesGuard } from './local-only-variables.js';
@@ -32,6 +33,7 @@ export const REPO_GUARDS = Object.freeze([
32
33
  swallowedRejectionGuard,
33
34
  sharedFakeGuard,
34
35
  typedMockGuard,
36
+ handBuiltReadGuard,
35
37
  testTimeoutGuard,
36
38
  localOnlyVariablesGuard,
37
39
  workerConfigGuard,
@@ -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,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,cAAc,EAAuB,MAAM,kBAAkB,CAAC;AACvE,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;AA+B/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,cAAc;IACd,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 { typedMockGuard, type TypedMockFacts } from './typed-mocks.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 typedMocks: TypedMockFacts;\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 typedMockGuard,\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,EAAE,kBAAkB,EAA2B,MAAM,uBAAuB,CAAC;AACpF,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,cAAc,EAAuB,MAAM,kBAAkB,CAAC;AACvE,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;AAgC/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,cAAc;IACd,kBAAkB;IAClB,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 { handBuiltReadGuard, type HandBuiltReadFacts } from './hand-built-reads.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 { typedMockGuard, type TypedMockFacts } from './typed-mocks.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 typedMocks: TypedMockFacts;\n handBuiltReads: HandBuiltReadFacts;\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 typedMockGuard,\n handBuiltReadGuard,\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"]}
@@ -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 handBuiltReads(source: FileView | string): Occurrence[];
5
+ export interface HandBuiltReadFacts {
6
+ reviewed: readonly AnchoredEntry[];
7
+ }
8
+ export declare const handBuiltReadGuard: GenericGuardDefinition<HandBuiltReadFacts>;
9
+ //# sourceMappingURL=hand-built-reads.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hand-built-reads.d.ts","sourceRoot":"","sources":["../../../src/modules/repo/hand-built-reads.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;AAwC1C,wBAAgB,cAAc,CAAC,MAAM,EAAE,QAAQ,GAAG,MAAM,GAAG,UAAU,EAAE,CA6EtE;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,SAAS,aAAa,EAAE,CAAC;CACpC;AAED,eAAO,MAAM,kBAAkB,EAAE,sBAAsB,CAAC,kBAAkB,CA+BzE,CAAC"}
@@ -0,0 +1,141 @@
1
+ import ts from 'typescript';
2
+ import { viewOfText } from '../../reader/view.js';
3
+ import { forEachNode, unwrap } 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', 'scripts', 'workers'],
9
+ kinds: ['code'],
10
+ tests: 'only',
11
+ optionalRoots: ['functions', 'scripts', 'workers'],
12
+ };
13
+ const OBSERVERS = new Set(['QueryObserver', 'InfiniteQueryObserver']);
14
+ const propertyNameOf = (name) => ts.isIdentifier(name) || ts.isStringLiteralLike(name) ? name.text : null;
15
+ const calledMember = (node, member) => {
16
+ const inner = unwrap(node);
17
+ return ts.isCallExpression(inner) &&
18
+ ts.isPropertyAccessExpression(inner.expression) &&
19
+ inner.expression.name.text === member
20
+ ? inner
21
+ : null;
22
+ };
23
+ const isRetryStateType = (type) => !!type &&
24
+ ts.isTypeReferenceNode(type) &&
25
+ ts.isIdentifier(type.typeName) &&
26
+ type.typeName.text === 'ReadRetryState';
27
+ const constructedObserver = (node) => {
28
+ const inner = unwrap(node);
29
+ return ts.isNewExpression(inner) &&
30
+ ts.isIdentifier(inner.expression) &&
31
+ OBSERVERS.has(inner.expression.text)
32
+ ? inner.expression.text
33
+ : null;
34
+ };
35
+ export function handBuiltReads(source) {
36
+ const file = typeof source === 'string' ? viewOfText('sample.test.ts', source) : source;
37
+ const sf = file.sourceFile;
38
+ const hits = [];
39
+ const hit = (node, detail) => hits.push({ start: node.getStart(sf), end: node.getEnd(), detail });
40
+ const declaredIn = (scope, name) => {
41
+ const statements = ts.isSourceFile(scope) || ts.isBlock(scope) ? scope.statements : [];
42
+ for (const statement of statements) {
43
+ if (!ts.isVariableStatement(statement))
44
+ continue;
45
+ for (const declaration of statement.declarationList.declarations)
46
+ if (ts.isIdentifier(declaration.name) && declaration.name.text === name)
47
+ return declaration;
48
+ }
49
+ return null;
50
+ };
51
+ const declarationOf = (identifier) => {
52
+ for (let scope = identifier.parent; scope; scope = scope.parent) {
53
+ const declaration = declaredIn(scope, identifier.text);
54
+ if (declaration)
55
+ return declaration;
56
+ }
57
+ return null;
58
+ };
59
+ const subscribed = new Set();
60
+ forEachNode(sf, (node) => {
61
+ if (!ts.isCallExpression(node) || !ts.isPropertyAccessExpression(node.expression))
62
+ return;
63
+ if (node.expression.name.text !== 'subscribe')
64
+ return;
65
+ const target = unwrap(node.expression.expression);
66
+ const declaration = ts.isIdentifier(target) ? declarationOf(target) : null;
67
+ if (declaration)
68
+ subscribed.add(declaration);
69
+ });
70
+ const observerOf = (node) => {
71
+ const inner = unwrap(node);
72
+ if (!ts.isIdentifier(inner)) {
73
+ const kind = constructedObserver(inner);
74
+ return kind ? { kind, site: inner } : null;
75
+ }
76
+ const declaration = declarationOf(inner);
77
+ if (!declaration?.initializer || subscribed.has(declaration))
78
+ return null;
79
+ const kind = constructedObserver(declaration.initializer);
80
+ return kind ? { kind, site: declaration.initializer } : null;
81
+ };
82
+ const reported = new Set();
83
+ forEachNode(sf, (node) => {
84
+ if (ts.isPropertyAssignment(node) &&
85
+ propertyNameOf(node.name) === 'retryState' &&
86
+ ts.isObjectLiteralExpression(unwrap(node.initializer)))
87
+ hit(node, 'a read’s retryState is written as a literal');
88
+ else if (ts.isVariableDeclaration(node) &&
89
+ isRetryStateType(node.type) &&
90
+ node.initializer &&
91
+ ts.isObjectLiteralExpression(unwrap(node.initializer)))
92
+ hit(node, 'a ReadRetryState is written as a literal');
93
+ else if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression)) {
94
+ if (node.expression.name.text === 'getCurrentResult') {
95
+ const observer = observerOf(node.expression.expression);
96
+ if (observer && !reported.has(observer.site)) {
97
+ reported.add(observer.site);
98
+ hit(observer.site, `a test reads a result off an observer it built itself (${observer.kind})`);
99
+ }
100
+ return;
101
+ }
102
+ if (node.expression.name.text !== 'setState')
103
+ return;
104
+ const build = calledMember(node.expression.expression, 'build');
105
+ if (build && ts.isPropertyAccessExpression(build.expression)) {
106
+ const cache = calledMember(build.expression.expression, 'getQueryCache');
107
+ if (cache)
108
+ hit(node, 'a test seeds a query’s state into the cache by hand');
109
+ }
110
+ }
111
+ });
112
+ return hits;
113
+ }
114
+ export const handBuiltReadGuard = {
115
+ id: 'handBuiltReads',
116
+ absentWhen: false,
117
+ factProblems(facts) {
118
+ const problems = [];
119
+ if (!objectProblem(facts, 'the facts', problems))
120
+ return problems;
121
+ anchoredProblems(facts.reviewed, 'reviewed', problems);
122
+ return problems;
123
+ },
124
+ build(facts) {
125
+ return [
126
+ {
127
+ title: 'no test builds a read’s state by hand',
128
+ description: 'A test takes a read’s state from the read builders or from the real hook, never from a hand-written ' +
129
+ '`retryState` or `ReadRetryState` literal, a result read off a `QueryObserver` / `InfiniteQueryObserver` ' +
130
+ 'it constructed itself, or a query cache seeded with `getQueryCache().build(…).setState(…)`. Each of those type-checks ' +
131
+ 'while combining flags no real read reaches, so a case passes on a state the product never sees. An ' +
132
+ 'observer the test subscribes, watching a real query run, is not a hand-built state and passes. A ' +
133
+ 'test whose subject is that shape itself is a reviewed entry whose anchor names the one construct, ' +
134
+ 'with its why; an entry that no longer matches one is stale.',
135
+ timeoutMs: REPOSITORY_GUARD_TIMEOUT_MS,
136
+ run: (context) => anchoredFindings(context.files(SCOPE).map((file) => ({ file, hits: handBuiltReads(file) })), facts.reviewed, 'its anchor names no hand-built read'),
137
+ },
138
+ ];
139
+ },
140
+ };
141
+ //# sourceMappingURL=hand-built-reads.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hand-built-reads.js","sourceRoot":"","sources":["../../../src/modules/repo/hand-built-reads.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, unwrap } 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', 'scripts', 'workers'],\n kinds: ['code'],\n tests: 'only',\n optionalRoots: ['functions', 'scripts', 'workers'],\n};\n\nconst OBSERVERS = new Set(['QueryObserver', 'InfiniteQueryObserver']);\n\nconst propertyNameOf = (name: ts.PropertyName): string | null =>\n ts.isIdentifier(name) || ts.isStringLiteralLike(name) ? name.text : null;\n\nconst calledMember = (node: ts.Expression, member: string): ts.CallExpression | null => {\n const inner = unwrap(node);\n return ts.isCallExpression(inner) &&\n ts.isPropertyAccessExpression(inner.expression) &&\n inner.expression.name.text === member\n ? inner\n : null;\n};\n\nconst isRetryStateType = (type: ts.TypeNode | undefined): boolean =>\n !!type &&\n ts.isTypeReferenceNode(type) &&\n ts.isIdentifier(type.typeName) &&\n type.typeName.text === 'ReadRetryState';\n\nconst constructedObserver = (node: ts.Expression): string | null => {\n const inner = unwrap(node);\n return ts.isNewExpression(inner) &&\n ts.isIdentifier(inner.expression) &&\n OBSERVERS.has(inner.expression.text)\n ? inner.expression.text\n : null;\n};\n\nexport function handBuiltReads(source: FileView | string): Occurrence[] {\n const file = typeof source === 'string' ? viewOfText('sample.test.ts', source) : source;\n const sf = file.sourceFile;\n const hits: Occurrence[] = [];\n const hit = (node: ts.Node, detail: string) =>\n hits.push({ start: node.getStart(sf), end: node.getEnd(), detail });\n const declaredIn = (scope: ts.Node, name: string): ts.VariableDeclaration | null => {\n const statements = ts.isSourceFile(scope) || ts.isBlock(scope) ? scope.statements : [];\n for (const statement of statements) {\n if (!ts.isVariableStatement(statement)) continue;\n for (const declaration of statement.declarationList.declarations)\n if (ts.isIdentifier(declaration.name) && declaration.name.text === name) return declaration;\n }\n return null;\n };\n const declarationOf = (identifier: ts.Identifier): ts.VariableDeclaration | null => {\n for (let scope: ts.Node | undefined = identifier.parent; scope; scope = scope.parent) {\n const declaration = declaredIn(scope, identifier.text);\n if (declaration) return declaration;\n }\n return null;\n };\n const subscribed = new Set<ts.VariableDeclaration>();\n forEachNode(sf, (node) => {\n if (!ts.isCallExpression(node) || !ts.isPropertyAccessExpression(node.expression)) return;\n if (node.expression.name.text !== 'subscribe') return;\n const target = unwrap(node.expression.expression);\n const declaration = ts.isIdentifier(target) ? declarationOf(target) : null;\n if (declaration) subscribed.add(declaration);\n });\n const observerOf = (node: ts.Expression): { kind: string; site: ts.Node } | null => {\n const inner = unwrap(node);\n if (!ts.isIdentifier(inner)) {\n const kind = constructedObserver(inner);\n return kind ? { kind, site: inner } : null;\n }\n const declaration = declarationOf(inner);\n if (!declaration?.initializer || subscribed.has(declaration)) return null;\n const kind = constructedObserver(declaration.initializer);\n return kind ? { kind, site: declaration.initializer } : null;\n };\n const reported = new Set<ts.Node>();\n forEachNode(sf, (node) => {\n if (\n ts.isPropertyAssignment(node) &&\n propertyNameOf(node.name) === 'retryState' &&\n ts.isObjectLiteralExpression(unwrap(node.initializer))\n )\n hit(node, 'a read’s retryState is written as a literal');\n else if (\n ts.isVariableDeclaration(node) &&\n isRetryStateType(node.type) &&\n node.initializer &&\n ts.isObjectLiteralExpression(unwrap(node.initializer))\n )\n hit(node, 'a ReadRetryState is written as a literal');\n else if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression)) {\n if (node.expression.name.text === 'getCurrentResult') {\n const observer = observerOf(node.expression.expression);\n if (observer && !reported.has(observer.site)) {\n reported.add(observer.site);\n hit(\n observer.site,\n `a test reads a result off an observer it built itself (${observer.kind})`,\n );\n }\n return;\n }\n if (node.expression.name.text !== 'setState') return;\n const build = calledMember(node.expression.expression, 'build');\n if (build && ts.isPropertyAccessExpression(build.expression)) {\n const cache = calledMember(build.expression.expression, 'getQueryCache');\n if (cache) hit(node, 'a test seeds a query’s state into the cache by hand');\n }\n }\n });\n return hits;\n}\n\nexport interface HandBuiltReadFacts {\n reviewed: readonly AnchoredEntry[];\n}\n\nexport const handBuiltReadGuard: GenericGuardDefinition<HandBuiltReadFacts> = {\n id: 'handBuiltReads',\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 test builds a read’s state by hand',\n description:\n 'A test takes a read’s state from the read builders or from the real hook, never from a hand-written ' +\n '`retryState` or `ReadRetryState` literal, a result read off a `QueryObserver` / `InfiniteQueryObserver` ' +\n 'it constructed itself, or a query cache seeded with `getQueryCache().build(…).setState(…)`. Each of those type-checks ' +\n 'while combining flags no real read reaches, so a case passes on a state the product never sees. An ' +\n 'observer the test subscribes, watching a real query run, is not a hand-built state and passes. A ' +\n 'test whose subject is that shape itself is a reviewed entry whose anchor names the one construct, ' +\n 'with its why; an entry that no longer matches one is stale.',\n timeoutMs: REPOSITORY_GUARD_TIMEOUT_MS,\n run: (context) =>\n anchoredFindings(\n context.files(SCOPE).map((file) => ({ file, hits: handBuiltReads(file) })),\n facts.reviewed,\n 'its anchor names no hand-built read',\n ),\n },\n ];\n },\n};\n"]}
@@ -4,10 +4,15 @@ export interface TypedMockHelper {
4
4
  owner: string;
5
5
  names: readonly string[];
6
6
  }
7
+ export interface FakeCall {
8
+ name: string;
9
+ overridesArgument: number;
10
+ }
7
11
  export interface ForbiddenOverride {
8
12
  module: string;
9
13
  names: readonly string[];
10
14
  why: string;
15
+ fakeCalls?: readonly FakeCall[];
11
16
  }
12
17
  export interface TypedMockFacts {
13
18
  importAliases: Readonly<Record<string, string>>;
@@ -1 +1 @@
1
- {"version":3,"file":"typed-mocks.d.ts","sourceRoot":"","sources":["../../../src/modules/repo/typed-mocks.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AAwBzE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAWpD,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,cAAc;IAC7B,aAAa,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAChD,KAAK,EAAE,SAAS,UAAU,EAAE,CAAC;IAC7B,YAAY,EAAE,SAAS,eAAe,EAAE,CAAC;IACzC,kBAAkB,EAAE,SAAS,iBAAiB,EAAE,CAAC;CAClD;AA+QD,eAAO,MAAM,cAAc,EAAE,sBAAsB,CAAC,cAAc,CAmCjE,CAAC"}
1
+ {"version":3,"file":"typed-mocks.d.ts","sourceRoot":"","sources":["../../../src/modules/repo/typed-mocks.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AAwBzE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAWpD,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,SAAS,QAAQ,EAAE,CAAC;CACjC;AAED,MAAM,WAAW,cAAc;IAC7B,aAAa,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAChD,KAAK,EAAE,SAAS,UAAU,EAAE,CAAC;IAC7B,YAAY,EAAE,SAAS,eAAe,EAAE,CAAC;IACzC,kBAAkB,EAAE,SAAS,iBAAiB,EAAE,CAAC;CAClD;AAuTD,eAAO,MAAM,cAAc,EAAE,sBAAsB,CAAC,cAAc,CAqCjE,CAAC"}
@@ -153,12 +153,32 @@ const isOriginalSpread = (expression, original) => {
153
153
  callee.expression.text === 'vi' &&
154
154
  callee.name.text === 'importActual'));
155
155
  };
156
- function overridesOf(returned, original) {
156
+ function calleeName(call) {
157
+ const callee = unwrap(call.expression);
158
+ if (ts.isIdentifier(callee))
159
+ return callee.text;
160
+ if (ts.isPropertyAccessExpression(callee))
161
+ return callee.name.text;
162
+ return null;
163
+ }
164
+ function overridesOf(returned, original, fakeCalls) {
157
165
  const value = awaited(returned);
158
166
  const objects = [];
159
167
  const out = [];
168
+ const name = ts.isCallExpression(value) ? calleeName(value) : null;
169
+ const overridesAt = name === null ? undefined : fakeCalls.get(name);
160
170
  if (ts.isObjectLiteralExpression(value))
161
171
  objects.push(value);
172
+ else if (ts.isCallExpression(value) && overridesAt !== undefined) {
173
+ const argument = value.arguments[overridesAt];
174
+ if (argument) {
175
+ const arg = unwrap(argument);
176
+ if (ts.isObjectLiteralExpression(arg))
177
+ objects.push(arg);
178
+ else
179
+ out.push({ unread: arg });
180
+ }
181
+ }
162
182
  else if (ts.isCallExpression(value))
163
183
  for (const argument of value.arguments) {
164
184
  const arg = unwrap(argument);
@@ -195,10 +215,11 @@ function checkForbidden(reader, facts, files) {
195
215
  if (!fn)
196
216
  continue;
197
217
  const forbidden = new Set(entry.names);
218
+ const fakeCalls = new Map((entry.fakeCalls ?? []).map((call) => [call.name, call.overridesArgument]));
198
219
  for (const returned of returnedExpressions(fn)) {
199
220
  if (!returned)
200
221
  continue;
201
- for (const read of overridesOf(returned, parameterName(fn))) {
222
+ for (const read of overridesOf(returned, parameterName(fn), fakeCalls)) {
202
223
  if ('unread' in read) {
203
224
  findings.push({
204
225
  file: file.path,
@@ -256,6 +277,21 @@ function factProblems(facts) {
256
277
  seen.add(entry.module);
257
278
  namesProblem(entry.names, `forbiddenOverrides entry ${entry.module}'s names`, problems);
258
279
  textProblem(entry.why, `forbiddenOverrides entry ${entry.module}'s why`, problems);
280
+ if (entry.fakeCalls === undefined)
281
+ continue;
282
+ if (!Array.isArray(entry.fakeCalls)) {
283
+ problems.push(`forbiddenOverrides entry ${entry.module}'s fakeCalls is not a list`);
284
+ continue;
285
+ }
286
+ for (const call of entry.fakeCalls) {
287
+ const fake = (call && typeof call === 'object' ? call : {});
288
+ if (!isText(fake.name))
289
+ problems.push(`a fakeCalls entry of ${entry.module} names no fake`);
290
+ if (typeof fake.overridesArgument !== 'number' ||
291
+ !Number.isInteger(fake.overridesArgument) ||
292
+ fake.overridesArgument < 1)
293
+ problems.push(`fakeCalls entry ${String(fake.name)} of ${entry.module} needs overridesArgument, the position of its override object after importOriginal`);
294
+ }
259
295
  }
260
296
  }
261
297
  return problems;
@@ -285,7 +321,9 @@ export const typedMockGuard = {
285
321
  'a stand-in that behaves differently on the path a case proves lets the case pass on behavior the app ' +
286
322
  'never has. The guard reads the names each override object sets at its top level; a spread of anything ' +
287
323
  'but the real module, a computed name, or an override object it cannot see fails, since it could hide a ' +
288
- 'listed one. Each entry names its module, the exports, and why the real one runs.',
324
+ 'listed one. Each entry names its module, the exports, and why the real one runs. An entry may name ' +
325
+ 'its shared fake’s calls with the position of their override object: for a call to that fake only that ' +
326
+ 'argument is read, and the arguments before it are the fake’s state.',
289
327
  timeoutMs: REPOSITORY_GUARD_TIMEOUT_MS,
290
328
  run(context) {
291
329
  return checkForbidden(context.reader, facts, context.files(SCOPE));
@@ -1 +1 @@
1
- {"version":3,"file":"typed-mocks.js","sourceRoot":"","sources":["../../../src/modules/repo/typed-mocks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAKjC,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EACL,OAAO,EACP,UAAU,EACV,YAAY,EACZ,eAAe,EACf,eAAe,EACf,aAAa,EACb,mBAAmB,GAGpB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EACL,oBAAoB,EACpB,MAAM,EACN,YAAY,EACZ,aAAa,EACb,WAAW,EACX,WAAW,GACZ,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AAChG,OAAO,EAAE,2BAA2B,EAAE,MAAM,2BAA2B,CAAC;AAGxE,MAAM,UAAU,GAAwB,IAAI,GAAG,CAAC,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC,CAAC;AAE1E,MAAM,KAAK,GAAU;IACnB,KAAK,EAAE,CAAC,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,SAAS,CAAC;IACjD,KAAK,EAAE,CAAC,MAAM,CAAC;IACf,KAAK,EAAE,MAAM;IACb,aAAa,EAAE,CAAC,WAAW,EAAE,SAAS,EAAE,SAAS,CAAC;CACnD,CAAC;AA2BF,SAAS,OAAO,CAAC,MAAkB,EAAE,IAAc,EAAE,OAAgC;IACnF,MAAM,GAAG,GAAW,EAAE,CAAC;IACvB,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;QAChC,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC;YAAE,SAAS;QACzE,IAAI,CAAC,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,SAAS;QAC9C,GAAG,CAAC,IAAI,CAAC;YACP,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,MAAM,EAAE,gBAAgB,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC;YAC/D,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;SAChC,CAAC,CAAC;IACL,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAQD,SAAS,SAAS,CAAC,MAAc,EAAE,IAAY,EAAE,IAAY;IAC3D,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC/B,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM;QAAE,OAAO,MAAM,CAAC;IACzC,OAAO,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC;AACjD,CAAC;AAED,SAAS,UAAU,CACjB,IAAuB,EACvB,MAAc,EACd,QAA8B,EAC9B,OAAiC;IAEjC,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;IACrD,OAAO,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACrE,CAAC;AAED,SAAS,aAAa,CACpB,UAAgC,EAChC,EAAW,EACX,MAAc,EACd,QAA8B,EAC9B,OAAiC;IAEjC,MAAM,IAAI,GAAG,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACrD,IAAI,CAAC,IAAI,IAAI,CAAC,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC;QACrC,OAAO,mEAAmE,CAAC;IAC7E,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;IACzD,IAAI,CAAC,IAAI;QAAE,OAAO,+EAA+E,CAAC;IAClG,IAAI,IAAI,KAAK,MAAM;QAAE,OAAO,IAAI,CAAC;IACjC,IAAI,IAAI,CAAC,aAAa,EAAE,MAAM;QAC5B,OAAO,mFAAmF,CAAC;IAC7F,MAAM,QAAQ,GAAG,aAAa,CAAC,EAAE,CAAC,CAAC;IACnC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC;IAC/B,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC5C,IAAI,CAAC,QAAQ,IAAI,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,IAAI,KAAK,QAAQ;QAC9E,OAAO,mFAAmF,CAAC;IAC7F,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,QAAQ,CAAC,KAAqB;IACrC,MAAM,MAAM,GAAG,IAAI,GAAG,EAGnB,CAAC;IACJ,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK;QAAE,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;IACzE,KAAK,MAAM,MAAM,IAAI,KAAK,CAAC,YAAY,EAAE,CAAC;QACxC,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACzC,IAAI,OAAO,EAAE,IAAI,KAAK,MAAM;YAAE,SAAS;QACvC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE;YACvB,IAAI,EAAE,QAAQ;YACd,KAAK,EAAE,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,IAAI,EAAE,CAAC,EAAE,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;SAC7D,CAAC,CAAC;IACL,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,UAAU,CAAC,KAAqB;IACvC,OAAO,IAAI,GAAG,CAAC;QACb,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC;QACxC,GAAG,KAAK,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC;KAC1C,CAAC,CAAC;AACL,CAAC;AAED,gGAAgG;AAChG,SAAS,YAAY,CAAC,MAAkB,EAAE,KAAa;IACrD,IAAI,aAAa,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC;QAC/C,OAAO,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,iDAAiD,CAAC;IACzF,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAClC,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACxE,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACjE,MAAM,QAAQ,GAAG,MAAM,CAAC,WAAW,CACjC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,cAAc,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,cAAc,CAAC,CACjF,CAAC;IACF,IAAI,QAAQ,KAAK,IAAI;QAAE,OAAO,wBAAwB,IAAI,gCAAgC,CAAC;IAC3F,MAAM,OAAO,GAAa,IAAI,CAAC,KAAK,CAAC,QAAQ,CAA2B,CAAC,OAAO,CAAC;IACjF,MAAM,GAAG,GAAG,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC;IAC3C,OAAO,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,GAAG,IAAI,OAAO;QAC7D,CAAC,CAAC,IAAI;QACN,CAAC,CAAC,GAAG,IAAI,eAAe,GAAG,8BAA8B,CAAC;AAC9D,CAAC;AAED,SAAS,UAAU,CAAC,MAAkB,EAAE,KAAqB,EAAE,KAAiB;IAC9E,MAAM,QAAQ,GAAc,EAAE,CAAC;IAC/B,MAAM,OAAO,GAAG,SAAS,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;IAC/C,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC/B,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;IAC/B,KAAK,MAAM,KAAK,IAAI,IAAI,EAAE,CAAC;QACzB,MAAM,OAAO,GAAG,YAAY,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QAC5C,IAAI,OAAO;YAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;IAC/D,CAAC;IACD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,SAAS;QAClC,MAAM,OAAO,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,gBAAgB,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;QACrF,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,EAAE,CAAC;YAClD,MAAM,MAAM,GAAG,CAAC,IAAa,EAAE,GAAW,EAAE,EAAE,CAC5C,QAAQ,CAAC,IAAI,CAAC;gBACZ,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,IAAI,EAAE,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC;gBAC5B,MAAM,EAAE,SAAS,IAAI,CAAC,MAAM,wBAAwB,GAAG,EAAE;aAC1D,CAAC,CAAC;YACL,IAAI,CAAC,IAAI,CAAC,QAAQ;gBAChB,QAAQ,CAAC,IAAI,CAAC;oBACZ,IAAI,EAAE,IAAI,CAAC,IAAI;oBACf,IAAI,EAAE,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC;oBACjC,MAAM,EACJ,SAAS,IAAI,CAAC,MAAM,oEAAoE;wBACxF,8CAA8C;iBACjD,CAAC,CAAC;YACL,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC;gBAAE,SAAS;YAC7D,MAAM,EAAE,GAAG,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACzC,IAAI,CAAC,EAAE,EAAE,CAAC;gBACR,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,4DAA4D,CAAC,CAAC;gBAChF,SAAS;YACX,CAAC;YACD,MAAM,OAAO,GAAG,mBAAmB,CAAC,EAAE,CAAC,CAAC;YACxC,IAAI,CAAC,OAAO,CAAC,MAAM;gBAAE,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,iBAAiB,CAAC,CAAC;YAC1D,MAAM,QAAQ,GAAG,UAAU,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;YACzC,KAAK,MAAM,QAAQ,IAAI,OAAO,EAAE,CAAC;gBAC/B,MAAM,GAAG,GAAG,aAAa,CAAC,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;gBACnE,IAAI,GAAG;oBAAE,MAAM,CAAC,QAAQ,IAAI,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;YAC9C,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,gBAAgB,GAAG,CAAC,UAAyB,EAAE,QAAuB,EAAW,EAAE;IACvF,MAAM,KAAK,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAClC,IAAI,CAAC,EAAE,CAAC,gBAAgB,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAC9C,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IACxC,OAAO,CACL,CAAC,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,IAAI,KAAK,QAAQ,CAAC;QACrD,CAAC,EAAE,CAAC,0BAA0B,CAAC,MAAM,CAAC;YACpC,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,UAAU,CAAC;YAClC,MAAM,CAAC,UAAU,CAAC,IAAI,KAAK,IAAI;YAC/B,MAAM,CAAC,IAAI,CAAC,IAAI,KAAK,cAAc,CAAC,CACvC,CAAC;AACJ,CAAC,CAAC;AAIF,SAAS,WAAW,CAAC,QAAuB,EAAE,QAAuB;IACnE,MAAM,KAAK,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IAChC,MAAM,OAAO,GAAiC,EAAE,CAAC;IACjD,MAAM,GAAG,GAAmB,EAAE,CAAC;IAC/B,IAAI,EAAE,CAAC,yBAAyB,CAAC,KAAK,CAAC;QAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;SACxD,IAAI,EAAE,CAAC,gBAAgB,CAAC,KAAK,CAAC;QACjC,KAAK,MAAM,QAAQ,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;YACvC,MAAM,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;YAC7B,IAAI,EAAE,CAAC,yBAAyB,CAAC,GAAG,CAAC;gBAAE,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;iBACpD,IAAI,CAAC,CAAC,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ,CAAC;gBAAE,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;QACvF,CAAC;IACH,KAAK,MAAM,MAAM,IAAI,OAAO;QAC1B,KAAK,MAAM,QAAQ,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;YACzC,IAAI,EAAE,CAAC,kBAAkB,CAAC,QAAQ,CAAC,EAAE,CAAC;gBACpC,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC;oBAAE,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;gBACrF,SAAS;YACX,CAAC;YACD,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC;YAC3B,IAAI,IAAI,IAAI,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC;gBACjE,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;;gBAC3C,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;QACtC,CAAC;IACH,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,cAAc,CAAC,MAAkB,EAAE,KAAqB,EAAE,KAAiB;IAClF,MAAM,QAAQ,GAAc,EAAE,CAAC;IAC/B,MAAM,OAAO,GAAG,SAAS,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;IAC/C,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,kBAAkB,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;IACzF,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,EAAE,CAAC;YAClD,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACxC,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC;gBAAE,SAAS;YACvE,MAAM,EAAE,GAAG,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACzC,IAAI,CAAC,EAAE;gBAAE,SAAS;YAClB,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACvC,KAAK,MAAM,QAAQ,IAAI,mBAAmB,CAAC,EAAE,CAAC,EAAE,CAAC;gBAC/C,IAAI,CAAC,QAAQ;oBAAE,SAAS;gBACxB,KAAK,MAAM,IAAI,IAAI,WAAW,CAAC,QAAQ,EAAE,aAAa,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;oBAC5D,IAAI,QAAQ,IAAI,IAAI,EAAE,CAAC;wBACrB,QAAQ,CAAC,IAAI,CAAC;4BACZ,IAAI,EAAE,IAAI,CAAC,IAAI;4BACf,IAAI,EAAE,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC;4BACnC,MAAM,EAAE,aAAa,IAAI,CAAC,MAAM,6EAA6E,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,oBAAoB,KAAK,CAAC,GAAG,EAAE;yBACnK,CAAC,CAAC;wBACH,SAAS;oBACX,CAAC;oBACD,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;wBAAE,SAAS;oBACxC,QAAQ,CAAC,IAAI,CAAC;wBACZ,IAAI,EAAE,IAAI,CAAC,IAAI;wBACf,IAAI,EAAE,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC;wBACjC,MAAM,EAAE,aAAa,IAAI,CAAC,MAAM,MAAM,IAAI,CAAC,IAAI,uCAAuC,KAAK,CAAC,GAAG,EAAE;qBAClG,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,SAAS,YAAY,CAAC,KAAqB;IACzC,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,WAAW,EAAE,QAAQ,CAAC;QAAE,OAAO,QAAQ,CAAC;IAClE,oBAAoB,CAAC,KAAK,CAAC,aAAa,EAAE,eAAe,EAAE,QAAQ,CAAC,CAAC;IACrE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC;QAC7B,QAAQ,CAAC,IAAI,CAAC,kDAAkD,CAAC,CAAC;;QAElE,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAkB,EAAE,CAAC;YAC5C,MAAM,KAAK,GAAG,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAA4B,CAAC;YACxF,WAAW,CAAC,KAAK,CAAC,MAAM,EAAE,2BAA2B,EAAE,QAAQ,CAAC,CAAC;YACjE,WAAW,CAAC,KAAK,CAAC,KAAK,EAAE,eAAe,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QACpF,CAAC;IACH,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,YAAY,CAAC;QACpC,QAAQ,CAAC,IAAI,CAAC,yDAAyD,CAAC,CAAC;;QAEzE,KAAK,MAAM,MAAM,IAAI,KAAK,CAAC,YAAyB,EAAE,CAAC;YACrD,MAAM,KAAK,GAAG,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAA4B,CAAC;YAC9F,WAAW,CAAC,KAAK,CAAC,KAAK,EAAE,8BAA8B,EAAE,QAAQ,CAAC,CAAC;YACnE,YAAY,CAAC,KAAK,CAAC,KAAK,EAAE,sBAAsB,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QAC3F,CAAC;IACH,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,kBAAkB,CAAC;QAC1C,QAAQ,CAAC,IAAI,CAAC,+DAA+D,CAAC,CAAC;SAC5E,CAAC;QACJ,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;QAC/B,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,kBAA+B,EAAE,CAAC;YACxD,MAAM,KAAK,GAAG,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAA4B,CAAC;YACrF,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC1B,QAAQ,CAAC,IAAI,CAAC,gDAAgD,CAAC,CAAC;gBAChE,SAAS;YACX,CAAC;YACD,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC;gBAAE,QAAQ,CAAC,IAAI,CAAC,4BAA4B,KAAK,CAAC,MAAM,QAAQ,CAAC,CAAC;YAC5F,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YACvB,YAAY,CAAC,KAAK,CAAC,KAAK,EAAE,4BAA4B,KAAK,CAAC,MAAM,UAAU,EAAE,QAAQ,CAAC,CAAC;YACxF,WAAW,CAAC,KAAK,CAAC,GAAG,EAAE,4BAA4B,KAAK,CAAC,MAAM,QAAQ,EAAE,QAAQ,CAAC,CAAC;QACrF,CAAC;IACH,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,CAAC,MAAM,cAAc,GAA2C;IACpE,EAAE,EAAE,YAAY;IAChB,UAAU,EAAE,KAAK;IACjB,YAAY;IACZ,KAAK,CAAC,KAAK;QACT,OAAO;YACL;gBACE,KAAK,EAAE,2DAA2D;gBAClE,WAAW,EACT,sGAAsG;oBACtG,qGAAqG;oBACrG,wGAAwG;oBACxG,2GAA2G;oBAC3G,sGAAsG;oBACtG,yFAAyF;gBAC3F,SAAS,EAAE,2BAA2B;gBACtC,GAAG,CAAC,OAAO;oBACT,OAAO,UAAU,CAAC,OAAO,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;gBACjE,CAAC;aACF;YACD;gBACE,KAAK,EAAE,kEAAkE;gBACzE,WAAW,EACT,0GAA0G;oBAC1G,uGAAuG;oBACvG,wGAAwG;oBACxG,yGAAyG;oBACzG,kFAAkF;gBACpF,SAAS,EAAE,2BAA2B;gBACtC,GAAG,CAAC,OAAO;oBACT,OAAO,cAAc,CAAC,OAAO,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;gBACrE,CAAC;aACF;SACF,CAAC;IACJ,CAAC;CACF,CAAC","sourcesContent":["import ts from 'typescript';\nimport { join } from 'node:path';\nimport type { Finding } from '../../guard.js';\nimport type { RepoReader, Scope } from '../../node/reader.js';\nimport type { GenericGuardDefinition } from '../../node/suite/config.js';\nimport type { FileView } from '../../reader/view.js';\nimport { lineOfNode, unwrap } from '../../support/ast.js';\nimport {\n awaited,\n bindingsOf,\n calledExport,\n factoryFunction,\n isRunnerOptions,\n parameterName,\n returnedExpressions,\n type Binding,\n type Factory,\n} from '../../support/mock-factories.js';\nimport {\n importAliasesProblem,\n isText,\n namesProblem,\n objectProblem,\n pathProblem,\n textProblem,\n} from '../../support/facts.js';\nimport { aliasList, isLocalModule, resolveSpecifier } from '../../support/resolve-specifier.js';\nimport { REPOSITORY_GUARD_TIMEOUT_MS } from '../../support/timeouts.js';\nimport type { SharedFake } from './shared-fakes.js';\n\nconst MOCK_CALLS: ReadonlySet<string> = new Set(['vi.mock', 'vi.doMock']);\n\nconst SCOPE: Scope = {\n roots: ['app', 'functions', 'scripts', 'workers'],\n kinds: ['code'],\n tests: 'only',\n optionalRoots: ['functions', 'scripts', 'workers'],\n};\n\nexport interface TypedMockHelper {\n owner: string;\n names: readonly string[];\n}\n\nexport interface ForbiddenOverride {\n module: string;\n names: readonly string[];\n why: string;\n}\n\nexport interface TypedMockFacts {\n importAliases: Readonly<Record<string, string>>;\n fakes: readonly SharedFake[];\n typedHelpers: readonly TypedMockHelper[];\n forbiddenOverrides: readonly ForbiddenOverride[];\n}\n\ninterface Site {\n node: ts.CallExpression;\n module: string;\n byImport: boolean;\n factory: ts.Expression | undefined;\n}\n\nfunction sitesOf(reader: RepoReader, file: FileView, aliases: Array<[string, string]>): Site[] {\n const out: Site[] = [];\n for (const site of file.imports) {\n if (site.kind !== 'mock' || !MOCK_CALLS.has(site.callee ?? '')) continue;\n if (!ts.isCallExpression(site.node)) continue;\n out.push({\n node: site.node,\n module: resolveSpecifier(reader, file.path, site.spec, aliases),\n byImport: site.byImport,\n factory: site.node.arguments[1],\n });\n }\n return out;\n}\n\ntype OwnerKind = 'fake' | 'helper';\ntype Owners = ReadonlyMap<\n string,\n { kind: 'fake' } | { kind: 'helper'; names: ReadonlySet<string> }\n>;\n\nfunction ownerKind(owners: Owners, path: string, name: string): OwnerKind | null {\n const owner = owners.get(path);\n if (!owner) return null;\n if (owner.kind === 'fake') return 'fake';\n return owner.names.has(name) ? 'helper' : null;\n}\n\nfunction calleeKind(\n call: ts.CallExpression,\n owners: Owners,\n bindings: Map<string, Binding>,\n resolve: (spec: string) => string,\n): OwnerKind | null {\n const target = calledExport(call, bindings, resolve);\n return target ? ownerKind(owners, target.path, target.name) : null;\n}\n\nfunction untypedReason(\n expression: ts.Expression | null,\n fn: Factory,\n owners: Owners,\n bindings: Map<string, Binding>,\n resolve: (spec: string) => string,\n): string | null {\n const call = expression ? awaited(expression) : null;\n if (!call || !ts.isCallExpression(call))\n return 'returns something other than a shared fake or a typed mock helper';\n const kind = calleeKind(call, owners, bindings, resolve);\n if (!kind) return 'returns through a function that is no listed shared fake or typed mock helper';\n if (kind === 'fake') return null;\n if (call.typeArguments?.length)\n return 'writes the type of its typed mock helper instead of taking it from importOriginal';\n const original = parameterName(fn);\n const [first] = call.arguments;\n const passed = first ? unwrap(first) : null;\n if (!original || !passed || !ts.isIdentifier(passed) || passed.text !== original)\n return \"hands its typed mock helper something other than the factory's own importOriginal\";\n return null;\n}\n\nfunction ownersOf(facts: TypedMockFacts): Owners {\n const owners = new Map<\n string,\n { kind: 'fake' } | { kind: 'helper'; names: ReadonlySet<string> }\n >();\n for (const fake of facts.fakes) owners.set(fake.owner, { kind: 'fake' });\n for (const helper of facts.typedHelpers) {\n const already = owners.get(helper.owner);\n if (already?.kind === 'fake') continue;\n owners.set(helper.owner, {\n kind: 'helper',\n names: new Set([...(already?.names ?? []), ...helper.names]),\n });\n }\n return owners;\n}\n\nfunction ownerFiles(facts: TypedMockFacts): Set<string> {\n return new Set([\n ...facts.fakes.map((fake) => fake.owner),\n ...facts.typedHelpers.map((h) => h.owner),\n ]);\n}\n\n// A package-owned helper is named by its package entry (`@scope/pkg/testing`), not a repo file.\nfunction missingOwner(reader: RepoReader, owner: string): string | null {\n if (isLocalModule(owner) || owner.startsWith('.'))\n return reader.exists(owner) ? null : 'this typed fake or helper file no longer exists';\n const segments = owner.split('/');\n const name = segments.slice(0, owner.startsWith('@') ? 2 : 1).join('/');\n const subpath = segments.slice(name.split('/').length).join('/');\n const manifest = reader.readOutside(\n join(reader.layout.repoRoot, 'node_modules', ...name.split('/'), 'package.json'),\n );\n if (manifest === null) return `no installed package ${name} holds this typed helper entry`;\n const exports: unknown = (JSON.parse(manifest) as { exports?: unknown }).exports;\n const key = subpath ? `./${subpath}` : '.';\n return exports && typeof exports === 'object' && key in exports\n ? null\n : `${name} exports no ${key} entry for this typed helper`;\n}\n\nfunction checkTyped(reader: RepoReader, facts: TypedMockFacts, files: FileView[]): Finding[] {\n const findings: Finding[] = [];\n const aliases = aliasList(facts.importAliases);\n const owners = ownersOf(facts);\n const skip = ownerFiles(facts);\n for (const owner of skip) {\n const missing = missingOwner(reader, owner);\n if (missing) findings.push({ file: owner, detail: missing });\n }\n for (const file of files) {\n if (skip.has(file.path)) continue;\n const resolve = (spec: string) => resolveSpecifier(reader, file.path, spec, aliases);\n for (const site of sitesOf(reader, file, aliases)) {\n const report = (node: ts.Node, why: string) =>\n findings.push({\n file: file.path,\n line: lineOfNode(file, node),\n detail: `mocks ${site.module} with a factory that ${why}`,\n });\n if (!site.byImport)\n findings.push({\n file: file.path,\n line: lineOfNode(file, site.node),\n detail:\n `mocks ${site.module} by a string path — name it through import('…'), so the factory's ` +\n \"importOriginal carries the module's own type\",\n });\n if (!site.factory || isRunnerOptions(site.factory)) continue;\n const fn = factoryFunction(site.factory);\n if (!fn) {\n report(site.node, 'the guard cannot read, since it is not written at the mock');\n continue;\n }\n const returns = returnedExpressions(fn);\n if (!returns.length) report(site.node, 'returns nothing');\n const bindings = bindingsOf(fn, resolve);\n for (const returned of returns) {\n const why = untypedReason(returned, fn, owners, bindings, resolve);\n if (why) report(returned ?? site.node, why);\n }\n }\n }\n return findings;\n}\n\nconst isOriginalSpread = (expression: ts.Expression, original: string | null): boolean => {\n const value = awaited(expression);\n if (!ts.isCallExpression(value)) return false;\n const callee = unwrap(value.expression);\n return (\n (ts.isIdentifier(callee) && callee.text === original) ||\n (ts.isPropertyAccessExpression(callee) &&\n ts.isIdentifier(callee.expression) &&\n callee.expression.text === 'vi' &&\n callee.name.text === 'importActual')\n );\n};\n\ntype OverrideRead = { name: string; node: ts.Node } | { unread: ts.Node };\n\nfunction overridesOf(returned: ts.Expression, original: string | null): OverrideRead[] {\n const value = awaited(returned);\n const objects: ts.ObjectLiteralExpression[] = [];\n const out: OverrideRead[] = [];\n if (ts.isObjectLiteralExpression(value)) objects.push(value);\n else if (ts.isCallExpression(value))\n for (const argument of value.arguments) {\n const arg = unwrap(argument);\n if (ts.isObjectLiteralExpression(arg)) objects.push(arg);\n else if (!(ts.isIdentifier(arg) && arg.text === original)) out.push({ unread: arg });\n }\n for (const object of objects)\n for (const property of object.properties) {\n if (ts.isSpreadAssignment(property)) {\n if (!isOriginalSpread(property.expression, original)) out.push({ unread: property });\n continue;\n }\n const name = property.name;\n if (name && (ts.isIdentifier(name) || ts.isStringLiteralLike(name)))\n out.push({ name: name.text, node: property });\n else out.push({ unread: property });\n }\n return out;\n}\n\nfunction checkForbidden(reader: RepoReader, facts: TypedMockFacts, files: FileView[]): Finding[] {\n const findings: Finding[] = [];\n const aliases = aliasList(facts.importAliases);\n const byModule = new Map(facts.forbiddenOverrides.map((entry) => [entry.module, entry]));\n for (const file of files) {\n for (const site of sitesOf(reader, file, aliases)) {\n const entry = byModule.get(site.module);\n if (!entry || !site.factory || isRunnerOptions(site.factory)) continue;\n const fn = factoryFunction(site.factory);\n if (!fn) continue;\n const forbidden = new Set(entry.names);\n for (const returned of returnedExpressions(fn)) {\n if (!returned) continue;\n for (const read of overridesOf(returned, parameterName(fn))) {\n if ('unread' in read) {\n findings.push({\n file: file.path,\n line: lineOfNode(file, read.unread),\n detail: `overrides ${site.module} through names the guard cannot read — write each override by name, since ${entry.names.join(', ')} must stay real: ${entry.why}`,\n });\n continue;\n }\n if (!forbidden.has(read.name)) continue;\n findings.push({\n file: file.path,\n line: lineOfNode(file, read.node),\n detail: `overrides ${site.module}'s ${read.name} — the real one runs in every test: ${entry.why}`,\n });\n }\n }\n }\n }\n return findings;\n}\n\nfunction factProblems(facts: TypedMockFacts): string[] {\n const problems: string[] = [];\n if (!objectProblem(facts, 'the facts', problems)) return problems;\n importAliasesProblem(facts.importAliases, 'importAliases', problems);\n if (!Array.isArray(facts.fakes))\n problems.push('fakes is missing (declare [] when there is none)');\n else\n for (const fake of facts.fakes as unknown[]) {\n const entry = (fake && typeof fake === 'object' ? fake : {}) as Record<string, unknown>;\n textProblem(entry.module, 'an entry of fakes’ module', problems);\n pathProblem(entry.owner, `fakes entry ${String(entry.module)}'s owner`, problems);\n }\n if (!Array.isArray(facts.typedHelpers))\n problems.push('typedHelpers is missing (declare [] when there is none)');\n else\n for (const helper of facts.typedHelpers as unknown[]) {\n const entry = (helper && typeof helper === 'object' ? helper : {}) as Record<string, unknown>;\n pathProblem(entry.owner, \"a typedHelpers entry's owner\", problems);\n namesProblem(entry.names, `typedHelpers entry ${String(entry.owner)}'s names`, problems);\n }\n if (!Array.isArray(facts.forbiddenOverrides))\n problems.push('forbiddenOverrides is missing (declare [] when there is none)');\n else {\n const seen = new Set<string>();\n for (const raw of facts.forbiddenOverrides as unknown[]) {\n const entry = (raw && typeof raw === 'object' ? raw : {}) as Record<string, unknown>;\n if (!isText(entry.module)) {\n problems.push('an entry of forbiddenOverrides names no module');\n continue;\n }\n if (seen.has(entry.module)) problems.push(`forbiddenOverrides lists ${entry.module} twice`);\n seen.add(entry.module);\n namesProblem(entry.names, `forbiddenOverrides entry ${entry.module}'s names`, problems);\n textProblem(entry.why, `forbiddenOverrides entry ${entry.module}'s why`, problems);\n }\n }\n return problems;\n}\n\nexport const typedMockGuard: GenericGuardDefinition<TypedMockFacts> = {\n id: 'typedMocks',\n absentWhen: false,\n factProblems,\n build(facts) {\n return [\n {\n title: 'every module mock is typed against the module it replaces',\n description:\n \"A test's module mock names its module through `import('…')` and returns, on every path, through the \" +\n \"module's shared fake or a typed mock helper. A helper takes its module type from the factory's own \" +\n '`importOriginal`, never from a type argument the test writes, so a wrong module, a misspelled export, ' +\n 'and a wrong signature fail to compile. A factory that returns a hand-written object is never compared to ' +\n 'the module, so a double that no longer matches it still passes. A mock with no factory, or with the ' +\n 'runner’s options object (`{ spy: true }`) in its place, is the runner’s own and passes.',\n timeoutMs: REPOSITORY_GUARD_TIMEOUT_MS,\n run(context) {\n return checkTyped(context.reader, facts, context.files(SCOPE));\n },\n },\n {\n title: 'no test overrides an export whose real behavior every test needs',\n description:\n 'A listed export is never replaced in a test, by its shared fake’s overrides or by a factory of its own: ' +\n 'a stand-in that behaves differently on the path a case proves lets the case pass on behavior the app ' +\n 'never has. The guard reads the names each override object sets at its top level; a spread of anything ' +\n 'but the real module, a computed name, or an override object it cannot see fails, since it could hide a ' +\n 'listed one. Each entry names its module, the exports, and why the real one runs.',\n timeoutMs: REPOSITORY_GUARD_TIMEOUT_MS,\n run(context) {\n return checkForbidden(context.reader, facts, context.files(SCOPE));\n },\n },\n ];\n },\n};\n"]}
1
+ {"version":3,"file":"typed-mocks.js","sourceRoot":"","sources":["../../../src/modules/repo/typed-mocks.ts"],"names":[],"mappings":"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","sourcesContent":["import ts from 'typescript';\nimport { join } from 'node:path';\nimport type { Finding } from '../../guard.js';\nimport type { RepoReader, Scope } from '../../node/reader.js';\nimport type { GenericGuardDefinition } from '../../node/suite/config.js';\nimport type { FileView } from '../../reader/view.js';\nimport { lineOfNode, unwrap } from '../../support/ast.js';\nimport {\n awaited,\n bindingsOf,\n calledExport,\n factoryFunction,\n isRunnerOptions,\n parameterName,\n returnedExpressions,\n type Binding,\n type Factory,\n} from '../../support/mock-factories.js';\nimport {\n importAliasesProblem,\n isText,\n namesProblem,\n objectProblem,\n pathProblem,\n textProblem,\n} from '../../support/facts.js';\nimport { aliasList, isLocalModule, resolveSpecifier } from '../../support/resolve-specifier.js';\nimport { REPOSITORY_GUARD_TIMEOUT_MS } from '../../support/timeouts.js';\nimport type { SharedFake } from './shared-fakes.js';\n\nconst MOCK_CALLS: ReadonlySet<string> = new Set(['vi.mock', 'vi.doMock']);\n\nconst SCOPE: Scope = {\n roots: ['app', 'functions', 'scripts', 'workers'],\n kinds: ['code'],\n tests: 'only',\n optionalRoots: ['functions', 'scripts', 'workers'],\n};\n\nexport interface TypedMockHelper {\n owner: string;\n names: readonly string[];\n}\n\nexport interface FakeCall {\n name: string;\n overridesArgument: number;\n}\n\nexport interface ForbiddenOverride {\n module: string;\n names: readonly string[];\n why: string;\n fakeCalls?: readonly FakeCall[];\n}\n\nexport interface TypedMockFacts {\n importAliases: Readonly<Record<string, string>>;\n fakes: readonly SharedFake[];\n typedHelpers: readonly TypedMockHelper[];\n forbiddenOverrides: readonly ForbiddenOverride[];\n}\n\ninterface Site {\n node: ts.CallExpression;\n module: string;\n byImport: boolean;\n factory: ts.Expression | undefined;\n}\n\nfunction sitesOf(reader: RepoReader, file: FileView, aliases: Array<[string, string]>): Site[] {\n const out: Site[] = [];\n for (const site of file.imports) {\n if (site.kind !== 'mock' || !MOCK_CALLS.has(site.callee ?? '')) continue;\n if (!ts.isCallExpression(site.node)) continue;\n out.push({\n node: site.node,\n module: resolveSpecifier(reader, file.path, site.spec, aliases),\n byImport: site.byImport,\n factory: site.node.arguments[1],\n });\n }\n return out;\n}\n\ntype OwnerKind = 'fake' | 'helper';\ntype Owners = ReadonlyMap<\n string,\n { kind: 'fake' } | { kind: 'helper'; names: ReadonlySet<string> }\n>;\n\nfunction ownerKind(owners: Owners, path: string, name: string): OwnerKind | null {\n const owner = owners.get(path);\n if (!owner) return null;\n if (owner.kind === 'fake') return 'fake';\n return owner.names.has(name) ? 'helper' : null;\n}\n\nfunction calleeKind(\n call: ts.CallExpression,\n owners: Owners,\n bindings: Map<string, Binding>,\n resolve: (spec: string) => string,\n): OwnerKind | null {\n const target = calledExport(call, bindings, resolve);\n return target ? ownerKind(owners, target.path, target.name) : null;\n}\n\nfunction untypedReason(\n expression: ts.Expression | null,\n fn: Factory,\n owners: Owners,\n bindings: Map<string, Binding>,\n resolve: (spec: string) => string,\n): string | null {\n const call = expression ? awaited(expression) : null;\n if (!call || !ts.isCallExpression(call))\n return 'returns something other than a shared fake or a typed mock helper';\n const kind = calleeKind(call, owners, bindings, resolve);\n if (!kind) return 'returns through a function that is no listed shared fake or typed mock helper';\n if (kind === 'fake') return null;\n if (call.typeArguments?.length)\n return 'writes the type of its typed mock helper instead of taking it from importOriginal';\n const original = parameterName(fn);\n const [first] = call.arguments;\n const passed = first ? unwrap(first) : null;\n if (!original || !passed || !ts.isIdentifier(passed) || passed.text !== original)\n return \"hands its typed mock helper something other than the factory's own importOriginal\";\n return null;\n}\n\nfunction ownersOf(facts: TypedMockFacts): Owners {\n const owners = new Map<\n string,\n { kind: 'fake' } | { kind: 'helper'; names: ReadonlySet<string> }\n >();\n for (const fake of facts.fakes) owners.set(fake.owner, { kind: 'fake' });\n for (const helper of facts.typedHelpers) {\n const already = owners.get(helper.owner);\n if (already?.kind === 'fake') continue;\n owners.set(helper.owner, {\n kind: 'helper',\n names: new Set([...(already?.names ?? []), ...helper.names]),\n });\n }\n return owners;\n}\n\nfunction ownerFiles(facts: TypedMockFacts): Set<string> {\n return new Set([\n ...facts.fakes.map((fake) => fake.owner),\n ...facts.typedHelpers.map((h) => h.owner),\n ]);\n}\n\n// A package-owned helper is named by its package entry (`@scope/pkg/testing`), not a repo file.\nfunction missingOwner(reader: RepoReader, owner: string): string | null {\n if (isLocalModule(owner) || owner.startsWith('.'))\n return reader.exists(owner) ? null : 'this typed fake or helper file no longer exists';\n const segments = owner.split('/');\n const name = segments.slice(0, owner.startsWith('@') ? 2 : 1).join('/');\n const subpath = segments.slice(name.split('/').length).join('/');\n const manifest = reader.readOutside(\n join(reader.layout.repoRoot, 'node_modules', ...name.split('/'), 'package.json'),\n );\n if (manifest === null) return `no installed package ${name} holds this typed helper entry`;\n const exports: unknown = (JSON.parse(manifest) as { exports?: unknown }).exports;\n const key = subpath ? `./${subpath}` : '.';\n return exports && typeof exports === 'object' && key in exports\n ? null\n : `${name} exports no ${key} entry for this typed helper`;\n}\n\nfunction checkTyped(reader: RepoReader, facts: TypedMockFacts, files: FileView[]): Finding[] {\n const findings: Finding[] = [];\n const aliases = aliasList(facts.importAliases);\n const owners = ownersOf(facts);\n const skip = ownerFiles(facts);\n for (const owner of skip) {\n const missing = missingOwner(reader, owner);\n if (missing) findings.push({ file: owner, detail: missing });\n }\n for (const file of files) {\n if (skip.has(file.path)) continue;\n const resolve = (spec: string) => resolveSpecifier(reader, file.path, spec, aliases);\n for (const site of sitesOf(reader, file, aliases)) {\n const report = (node: ts.Node, why: string) =>\n findings.push({\n file: file.path,\n line: lineOfNode(file, node),\n detail: `mocks ${site.module} with a factory that ${why}`,\n });\n if (!site.byImport)\n findings.push({\n file: file.path,\n line: lineOfNode(file, site.node),\n detail:\n `mocks ${site.module} by a string path — name it through import('…'), so the factory's ` +\n \"importOriginal carries the module's own type\",\n });\n if (!site.factory || isRunnerOptions(site.factory)) continue;\n const fn = factoryFunction(site.factory);\n if (!fn) {\n report(site.node, 'the guard cannot read, since it is not written at the mock');\n continue;\n }\n const returns = returnedExpressions(fn);\n if (!returns.length) report(site.node, 'returns nothing');\n const bindings = bindingsOf(fn, resolve);\n for (const returned of returns) {\n const why = untypedReason(returned, fn, owners, bindings, resolve);\n if (why) report(returned ?? site.node, why);\n }\n }\n }\n return findings;\n}\n\nconst isOriginalSpread = (expression: ts.Expression, original: string | null): boolean => {\n const value = awaited(expression);\n if (!ts.isCallExpression(value)) return false;\n const callee = unwrap(value.expression);\n return (\n (ts.isIdentifier(callee) && callee.text === original) ||\n (ts.isPropertyAccessExpression(callee) &&\n ts.isIdentifier(callee.expression) &&\n callee.expression.text === 'vi' &&\n callee.name.text === 'importActual')\n );\n};\n\ntype OverrideRead = { name: string; node: ts.Node } | { unread: ts.Node };\n\nfunction calleeName(call: ts.CallExpression): string | null {\n const callee = unwrap(call.expression);\n if (ts.isIdentifier(callee)) return callee.text;\n if (ts.isPropertyAccessExpression(callee)) return callee.name.text;\n return null;\n}\n\nfunction overridesOf(\n returned: ts.Expression,\n original: string | null,\n fakeCalls: ReadonlyMap<string, number>,\n): OverrideRead[] {\n const value = awaited(returned);\n const objects: ts.ObjectLiteralExpression[] = [];\n const out: OverrideRead[] = [];\n const name = ts.isCallExpression(value) ? calleeName(value) : null;\n const overridesAt = name === null ? undefined : fakeCalls.get(name);\n if (ts.isObjectLiteralExpression(value)) objects.push(value);\n else if (ts.isCallExpression(value) && overridesAt !== undefined) {\n const argument = value.arguments[overridesAt];\n if (argument) {\n const arg = unwrap(argument);\n if (ts.isObjectLiteralExpression(arg)) objects.push(arg);\n else out.push({ unread: arg });\n }\n } else if (ts.isCallExpression(value))\n for (const argument of value.arguments) {\n const arg = unwrap(argument);\n if (ts.isObjectLiteralExpression(arg)) objects.push(arg);\n else if (!(ts.isIdentifier(arg) && arg.text === original)) out.push({ unread: arg });\n }\n for (const object of objects)\n for (const property of object.properties) {\n if (ts.isSpreadAssignment(property)) {\n if (!isOriginalSpread(property.expression, original)) out.push({ unread: property });\n continue;\n }\n const name = property.name;\n if (name && (ts.isIdentifier(name) || ts.isStringLiteralLike(name)))\n out.push({ name: name.text, node: property });\n else out.push({ unread: property });\n }\n return out;\n}\n\nfunction checkForbidden(reader: RepoReader, facts: TypedMockFacts, files: FileView[]): Finding[] {\n const findings: Finding[] = [];\n const aliases = aliasList(facts.importAliases);\n const byModule = new Map(facts.forbiddenOverrides.map((entry) => [entry.module, entry]));\n for (const file of files) {\n for (const site of sitesOf(reader, file, aliases)) {\n const entry = byModule.get(site.module);\n if (!entry || !site.factory || isRunnerOptions(site.factory)) continue;\n const fn = factoryFunction(site.factory);\n if (!fn) continue;\n const forbidden = new Set(entry.names);\n const fakeCalls = new Map(\n (entry.fakeCalls ?? []).map((call) => [call.name, call.overridesArgument]),\n );\n for (const returned of returnedExpressions(fn)) {\n if (!returned) continue;\n for (const read of overridesOf(returned, parameterName(fn), fakeCalls)) {\n if ('unread' in read) {\n findings.push({\n file: file.path,\n line: lineOfNode(file, read.unread),\n detail: `overrides ${site.module} through names the guard cannot read — write each override by name, since ${entry.names.join(', ')} must stay real: ${entry.why}`,\n });\n continue;\n }\n if (!forbidden.has(read.name)) continue;\n findings.push({\n file: file.path,\n line: lineOfNode(file, read.node),\n detail: `overrides ${site.module}'s ${read.name} — the real one runs in every test: ${entry.why}`,\n });\n }\n }\n }\n }\n return findings;\n}\n\nfunction factProblems(facts: TypedMockFacts): string[] {\n const problems: string[] = [];\n if (!objectProblem(facts, 'the facts', problems)) return problems;\n importAliasesProblem(facts.importAliases, 'importAliases', problems);\n if (!Array.isArray(facts.fakes))\n problems.push('fakes is missing (declare [] when there is none)');\n else\n for (const fake of facts.fakes as unknown[]) {\n const entry = (fake && typeof fake === 'object' ? fake : {}) as Record<string, unknown>;\n textProblem(entry.module, 'an entry of fakes’ module', problems);\n pathProblem(entry.owner, `fakes entry ${String(entry.module)}'s owner`, problems);\n }\n if (!Array.isArray(facts.typedHelpers))\n problems.push('typedHelpers is missing (declare [] when there is none)');\n else\n for (const helper of facts.typedHelpers as unknown[]) {\n const entry = (helper && typeof helper === 'object' ? helper : {}) as Record<string, unknown>;\n pathProblem(entry.owner, \"a typedHelpers entry's owner\", problems);\n namesProblem(entry.names, `typedHelpers entry ${String(entry.owner)}'s names`, problems);\n }\n if (!Array.isArray(facts.forbiddenOverrides))\n problems.push('forbiddenOverrides is missing (declare [] when there is none)');\n else {\n const seen = new Set<string>();\n for (const raw of facts.forbiddenOverrides as unknown[]) {\n const entry = (raw && typeof raw === 'object' ? raw : {}) as Record<string, unknown>;\n if (!isText(entry.module)) {\n problems.push('an entry of forbiddenOverrides names no module');\n continue;\n }\n if (seen.has(entry.module)) problems.push(`forbiddenOverrides lists ${entry.module} twice`);\n seen.add(entry.module);\n namesProblem(entry.names, `forbiddenOverrides entry ${entry.module}'s names`, problems);\n textProblem(entry.why, `forbiddenOverrides entry ${entry.module}'s why`, problems);\n if (entry.fakeCalls === undefined) continue;\n if (!Array.isArray(entry.fakeCalls)) {\n problems.push(`forbiddenOverrides entry ${entry.module}'s fakeCalls is not a list`);\n continue;\n }\n for (const call of entry.fakeCalls as unknown[]) {\n const fake = (call && typeof call === 'object' ? call : {}) as Record<string, unknown>;\n if (!isText(fake.name)) problems.push(`a fakeCalls entry of ${entry.module} names no fake`);\n if (\n typeof fake.overridesArgument !== 'number' ||\n !Number.isInteger(fake.overridesArgument) ||\n fake.overridesArgument < 1\n )\n problems.push(\n `fakeCalls entry ${String(fake.name)} of ${entry.module} needs overridesArgument, the position of its override object after importOriginal`,\n );\n }\n }\n }\n return problems;\n}\n\nexport const typedMockGuard: GenericGuardDefinition<TypedMockFacts> = {\n id: 'typedMocks',\n absentWhen: false,\n factProblems,\n build(facts) {\n return [\n {\n title: 'every module mock is typed against the module it replaces',\n description:\n \"A test's module mock names its module through `import('…')` and returns, on every path, through the \" +\n \"module's shared fake or a typed mock helper. A helper takes its module type from the factory's own \" +\n '`importOriginal`, never from a type argument the test writes, so a wrong module, a misspelled export, ' +\n 'and a wrong signature fail to compile. A factory that returns a hand-written object is never compared to ' +\n 'the module, so a double that no longer matches it still passes. A mock with no factory, or with the ' +\n 'runner’s options object (`{ spy: true }`) in its place, is the runner’s own and passes.',\n timeoutMs: REPOSITORY_GUARD_TIMEOUT_MS,\n run(context) {\n return checkTyped(context.reader, facts, context.files(SCOPE));\n },\n },\n {\n title: 'no test overrides an export whose real behavior every test needs',\n description:\n 'A listed export is never replaced in a test, by its shared fake’s overrides or by a factory of its own: ' +\n 'a stand-in that behaves differently on the path a case proves lets the case pass on behavior the app ' +\n 'never has. The guard reads the names each override object sets at its top level; a spread of anything ' +\n 'but the real module, a computed name, or an override object it cannot see fails, since it could hide a ' +\n 'listed one. Each entry names its module, the exports, and why the real one runs. An entry may name ' +\n 'its shared fake’s calls with the position of their override object: for a call to that fake only that ' +\n 'argument is read, and the arguments before it are the fake’s state.',\n timeoutMs: REPOSITORY_GUARD_TIMEOUT_MS,\n run(context) {\n return checkForbidden(context.reader, facts, context.files(SCOPE));\n },\n },\n ];\n },\n};\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ttt-productions/repo-guards",
3
- "version": "0.1.28",
3
+ "version": "0.1.30",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/ttt-productions/ttt-packages.git",