@miller-tech/uap 1.32.0 → 1.34.0
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.
- package/README.md +10 -4
- package/dist/.tsbuildinfo +1 -1
- package/dist/bin/cli.js +1 -0
- package/dist/bin/cli.js.map +1 -1
- package/dist/cli/deliver.d.ts +3 -0
- package/dist/cli/deliver.d.ts.map +1 -1
- package/dist/cli/deliver.js +5 -0
- package/dist/cli/deliver.js.map +1 -1
- package/dist/delivery/applier.d.ts +42 -3
- package/dist/delivery/applier.d.ts.map +1 -1
- package/dist/delivery/applier.js +162 -7
- package/dist/delivery/applier.js.map +1 -1
- package/dist/delivery/convergence-loop.d.ts +7 -0
- package/dist/delivery/convergence-loop.d.ts.map +1 -1
- package/dist/delivery/convergence-loop.js +78 -10
- package/dist/delivery/convergence-loop.js.map +1 -1
- package/dist/delivery/explorer.d.ts +4 -2
- package/dist/delivery/explorer.d.ts.map +1 -1
- package/dist/delivery/explorer.js +2 -2
- package/dist/delivery/explorer.js.map +1 -1
- package/dist/delivery/index.d.ts +3 -1
- package/dist/delivery/index.d.ts.map +1 -1
- package/dist/delivery/index.js +3 -1
- package/dist/delivery/index.js.map +1 -1
- package/dist/delivery/integrity.d.ts +44 -0
- package/dist/delivery/integrity.d.ts.map +1 -0
- package/dist/delivery/integrity.js +120 -0
- package/dist/delivery/integrity.js.map +1 -0
- package/dist/delivery/spec-imports.d.ts +54 -0
- package/dist/delivery/spec-imports.d.ts.map +1 -0
- package/dist/delivery/spec-imports.js +243 -0
- package/dist/delivery/spec-imports.js.map +1 -0
- package/package.json +1 -1
package/dist/delivery/index.d.ts
CHANGED
|
@@ -12,8 +12,10 @@ export { exploreAndCommit, DEFAULT_STRATEGY_SEEDS, MAX_CANDIDATES, type Candidat
|
|
|
12
12
|
export { createModelJudge, extractJson, type Judge, type JudgeCandidate, type JudgeVerdict, } from './judge.js';
|
|
13
13
|
export { createModelCritic, parseFixList, type Critic, type Critique, type CritiqueInput, } from './critic.js';
|
|
14
14
|
export { detectRungs, runLadder, runRung, formatFeedback, type GateRung, type RungResult, type RungFailureReason, type LadderResult, type LadderOptions, } from './verifier-ladder.js';
|
|
15
|
-
export { applyFileBlocks, applyFileBlocksWithRollback, parseFileBlocks, type Applier, type ApplyResult, type FileBlock, type RevertibleApply, } from './applier.js';
|
|
15
|
+
export { applyFileBlocks, applyFileBlocksWithRollback, parseFileBlocks, findProtectedTestFiles, isGateConfigBasename, isTestFilePath, type Applier, type ApplyOptions, type ApplyResult, type FileBlock, type RevertibleApply, } from './applier.js';
|
|
16
16
|
export { planAutoOptimization, DELIVERY_COMPLEXITY_THRESHOLDS, type AutoPlan, type DeliveryComplexity, } from './auto-optimizer.js';
|
|
17
|
+
export { captureIntegrity, verifyAndRestore, integrityViolationFeedback, type IntegritySnapshot, type IntegrityCheck, } from './integrity.js';
|
|
18
|
+
export { snapshotProtection, expandSpecImports, resolveRelativeImport, isOraclePath, type ProtectionSnapshot, } from './spec-imports.js';
|
|
17
19
|
export { generateStrategySeeds, parseSeedArray, seedsFromIdeas, type IdeationOptions, } from './ideation.js';
|
|
18
20
|
export { createHaloDeliveryTracer, type HaloDeliveryTracer, type HaloDeliveryTracerOptions, } from './halo-trace.js';
|
|
19
21
|
export { createRunCoordinator, collectAppliedFiles, type RunCoordinator, type RunCoordinatorOptions, } from './run-coordinator.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/delivery/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,oBAAoB,EACpB,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,EACvB,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAClB,KAAK,aAAa,GACnB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EACL,0BAA0B,EAC1B,uBAAuB,EACvB,KAAK,oBAAoB,EACzB,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,KAAK,cAAc,GACpB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,qBAAqB,EACrB,iBAAiB,EACjB,eAAe,EACf,eAAe,EACf,mBAAmB,EACnB,yBAAyB,EACzB,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACtB,KAAK,uBAAuB,GAC7B,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,gBAAgB,EAChB,sBAAsB,EACtB,cAAc,EACd,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,YAAY,GAClB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,gBAAgB,EAChB,WAAW,EACX,KAAK,KAAK,EACV,KAAK,cAAc,EACnB,KAAK,YAAY,GAClB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,iBAAiB,EACjB,YAAY,EACZ,KAAK,MAAM,EACX,KAAK,QAAQ,EACb,KAAK,aAAa,GACnB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,WAAW,EACX,SAAS,EACT,OAAO,EACP,cAAc,EACd,KAAK,QAAQ,EACb,KAAK,UAAU,EACf,KAAK,iBAAiB,EACtB,KAAK,YAAY,EACjB,KAAK,aAAa,GACnB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,eAAe,EACf,2BAA2B,EAC3B,eAAe,EACf,KAAK,OAAO,EACZ,KAAK,WAAW,EAChB,KAAK,SAAS,EACd,KAAK,eAAe,GACrB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,oBAAoB,EACpB,8BAA8B,EAC9B,KAAK,QAAQ,EACb,KAAK,kBAAkB,GACxB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,qBAAqB,EACrB,cAAc,EACd,cAAc,EACd,KAAK,eAAe,GACrB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,wBAAwB,EACxB,KAAK,kBAAkB,EACvB,KAAK,yBAAyB,GAC/B,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,KAAK,cAAc,EACnB,KAAK,qBAAqB,GAC3B,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,kBAAkB,EAAE,KAAK,yBAAyB,EAAE,MAAM,mCAAmC,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/delivery/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,oBAAoB,EACpB,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,EACvB,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAClB,KAAK,aAAa,GACnB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EACL,0BAA0B,EAC1B,uBAAuB,EACvB,KAAK,oBAAoB,EACzB,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,KAAK,cAAc,GACpB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,qBAAqB,EACrB,iBAAiB,EACjB,eAAe,EACf,eAAe,EACf,mBAAmB,EACnB,yBAAyB,EACzB,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACtB,KAAK,uBAAuB,GAC7B,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,gBAAgB,EAChB,sBAAsB,EACtB,cAAc,EACd,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,YAAY,GAClB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,gBAAgB,EAChB,WAAW,EACX,KAAK,KAAK,EACV,KAAK,cAAc,EACnB,KAAK,YAAY,GAClB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,iBAAiB,EACjB,YAAY,EACZ,KAAK,MAAM,EACX,KAAK,QAAQ,EACb,KAAK,aAAa,GACnB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,WAAW,EACX,SAAS,EACT,OAAO,EACP,cAAc,EACd,KAAK,QAAQ,EACb,KAAK,UAAU,EACf,KAAK,iBAAiB,EACtB,KAAK,YAAY,EACjB,KAAK,aAAa,GACnB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,eAAe,EACf,2BAA2B,EAC3B,eAAe,EACf,sBAAsB,EACtB,oBAAoB,EACpB,cAAc,EACd,KAAK,OAAO,EACZ,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,SAAS,EACd,KAAK,eAAe,GACrB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,oBAAoB,EACpB,8BAA8B,EAC9B,KAAK,QAAQ,EACb,KAAK,kBAAkB,GACxB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,0BAA0B,EAC1B,KAAK,iBAAiB,EACtB,KAAK,cAAc,GACpB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,qBAAqB,EACrB,YAAY,EACZ,KAAK,kBAAkB,GACxB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,qBAAqB,EACrB,cAAc,EACd,cAAc,EACd,KAAK,eAAe,GACrB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,wBAAwB,EACxB,KAAK,kBAAkB,EACvB,KAAK,yBAAyB,GAC/B,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,KAAK,cAAc,EACnB,KAAK,qBAAqB,GAC3B,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,kBAAkB,EAAE,KAAK,yBAAyB,EAAE,MAAM,mCAAmC,CAAC"}
|
package/dist/delivery/index.js
CHANGED
|
@@ -12,8 +12,10 @@ export { exploreAndCommit, DEFAULT_STRATEGY_SEEDS, MAX_CANDIDATES, } from './exp
|
|
|
12
12
|
export { createModelJudge, extractJson, } from './judge.js';
|
|
13
13
|
export { createModelCritic, parseFixList, } from './critic.js';
|
|
14
14
|
export { detectRungs, runLadder, runRung, formatFeedback, } from './verifier-ladder.js';
|
|
15
|
-
export { applyFileBlocks, applyFileBlocksWithRollback, parseFileBlocks, } from './applier.js';
|
|
15
|
+
export { applyFileBlocks, applyFileBlocksWithRollback, parseFileBlocks, findProtectedTestFiles, isGateConfigBasename, isTestFilePath, } from './applier.js';
|
|
16
16
|
export { planAutoOptimization, DELIVERY_COMPLEXITY_THRESHOLDS, } from './auto-optimizer.js';
|
|
17
|
+
export { captureIntegrity, verifyAndRestore, integrityViolationFeedback, } from './integrity.js';
|
|
18
|
+
export { snapshotProtection, expandSpecImports, resolveRelativeImport, isOraclePath, } from './spec-imports.js';
|
|
17
19
|
export { generateStrategySeeds, parseSeedArray, seedsFromIdeas, } from './ideation.js';
|
|
18
20
|
export { createHaloDeliveryTracer, } from './halo-trace.js';
|
|
19
21
|
export { createRunCoordinator, collectAppliedFiles, } from './run-coordinator.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/delivery/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,oBAAoB,GAarB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EACL,0BAA0B,EAC1B,uBAAuB,GAKxB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,qBAAqB,EACrB,iBAAiB,EACjB,eAAe,EACf,eAAe,EACf,mBAAmB,EACnB,yBAAyB,GAM1B,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,gBAAgB,EAChB,sBAAsB,EACtB,cAAc,GAKf,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,gBAAgB,EAChB,WAAW,GAIZ,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,iBAAiB,EACjB,YAAY,GAIb,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,WAAW,EACX,SAAS,EACT,OAAO,EACP,cAAc,GAMf,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,eAAe,EACf,2BAA2B,EAC3B,eAAe,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/delivery/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,oBAAoB,GAarB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EACL,0BAA0B,EAC1B,uBAAuB,GAKxB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,qBAAqB,EACrB,iBAAiB,EACjB,eAAe,EACf,eAAe,EACf,mBAAmB,EACnB,yBAAyB,GAM1B,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,gBAAgB,EAChB,sBAAsB,EACtB,cAAc,GAKf,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,gBAAgB,EAChB,WAAW,GAIZ,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,iBAAiB,EACjB,YAAY,GAIb,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,WAAW,EACX,SAAS,EACT,OAAO,EACP,cAAc,GAMf,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,eAAe,EACf,2BAA2B,EAC3B,eAAe,EACf,sBAAsB,EACtB,oBAAoB,EACpB,cAAc,GAMf,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,oBAAoB,EACpB,8BAA8B,GAG/B,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,0BAA0B,GAG3B,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,qBAAqB,EACrB,YAAY,GAEb,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,qBAAqB,EACrB,cAAc,EACd,cAAc,GAEf,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,wBAAwB,GAGzB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,oBAAoB,EACpB,mBAAmB,GAGpB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,kBAAkB,EAAkC,MAAM,mCAAmC,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gate Integrity Guard — runtime tamper detection for protected files
|
|
3
|
+
*
|
|
4
|
+
* The applier's protectedFiles filter only constrains what the MODEL writes
|
|
5
|
+
* through file blocks. Gate execution runs project code — including test
|
|
6
|
+
* files the model just created — and that code can `writeFileSync` over a
|
|
7
|
+
* protected spec or helper at runtime, defeating the static filter.
|
|
8
|
+
*
|
|
9
|
+
* This guard snapshots the bytes of every protected file after the baseline
|
|
10
|
+
* run, then re-verifies after each gate run: any mutated protected file is
|
|
11
|
+
* restored from the snapshot and the run's gate result is discarded as a
|
|
12
|
+
* GATE INTEGRITY VIOLATION, with feedback telling the model exactly why.
|
|
13
|
+
* Protected paths that did not exist at snapshot time ("reserved" oracle
|
|
14
|
+
* paths) are restored to absence — a runtime-fabricated golden is removed.
|
|
15
|
+
*/
|
|
16
|
+
interface IntegrityRecord {
|
|
17
|
+
/** sha256 of the file's bytes; null when the path was absent at capture */
|
|
18
|
+
hash: string | null;
|
|
19
|
+
/** Raw bytes for restoration; null when over budget (hash-verify only) */
|
|
20
|
+
content: Buffer | null;
|
|
21
|
+
}
|
|
22
|
+
export type IntegritySnapshot = Map<string, IntegrityRecord>;
|
|
23
|
+
export interface IntegrityCheck {
|
|
24
|
+
/** Protected files whose on-disk state changed during the gate run */
|
|
25
|
+
tampered: string[];
|
|
26
|
+
/** Subset of tampered that was restored to its snapshot state */
|
|
27
|
+
restored: string[];
|
|
28
|
+
/** Subset of tampered that could not be restored (over budget / IO error) */
|
|
29
|
+
unrecoverable: string[];
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Capture the on-disk state of every protected file (original-case relative
|
|
33
|
+
* paths). Absent paths are recorded as reserved. Fail-soft per file.
|
|
34
|
+
*/
|
|
35
|
+
export declare function captureIntegrity(projectRoot: string, files: string[]): IntegritySnapshot;
|
|
36
|
+
/**
|
|
37
|
+
* Verify every captured file against its snapshot; restore what changed.
|
|
38
|
+
* Returns what was tampered with so the caller can discard gate results.
|
|
39
|
+
*/
|
|
40
|
+
export declare function verifyAndRestore(projectRoot: string, snapshot: IntegritySnapshot): IntegrityCheck;
|
|
41
|
+
/** Render the violation feedback prepended to discarded gate results. */
|
|
42
|
+
export declare function integrityViolationFeedback(check: IntegrityCheck): string;
|
|
43
|
+
export {};
|
|
44
|
+
//# sourceMappingURL=integrity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"integrity.d.ts","sourceRoot":"","sources":["../../src/delivery/integrity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAWH,UAAU,eAAe;IACvB,2EAA2E;IAC3E,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,0EAA0E;IAC1E,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED,MAAM,MAAM,iBAAiB,GAAG,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;AAE7D,MAAM,WAAW,cAAc;IAC7B,sEAAsE;IACtE,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,iEAAiE;IACjE,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,6EAA6E;IAC7E,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB;AAMD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,iBAAiB,CAqBxF;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,iBAAiB,GAAG,cAAc,CA+CjG;AAED,yEAAyE;AACzE,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,cAAc,GAAG,MAAM,CAYxE"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gate Integrity Guard — runtime tamper detection for protected files
|
|
3
|
+
*
|
|
4
|
+
* The applier's protectedFiles filter only constrains what the MODEL writes
|
|
5
|
+
* through file blocks. Gate execution runs project code — including test
|
|
6
|
+
* files the model just created — and that code can `writeFileSync` over a
|
|
7
|
+
* protected spec or helper at runtime, defeating the static filter.
|
|
8
|
+
*
|
|
9
|
+
* This guard snapshots the bytes of every protected file after the baseline
|
|
10
|
+
* run, then re-verifies after each gate run: any mutated protected file is
|
|
11
|
+
* restored from the snapshot and the run's gate result is discarded as a
|
|
12
|
+
* GATE INTEGRITY VIOLATION, with feedback telling the model exactly why.
|
|
13
|
+
* Protected paths that did not exist at snapshot time ("reserved" oracle
|
|
14
|
+
* paths) are restored to absence — a runtime-fabricated golden is removed.
|
|
15
|
+
*/
|
|
16
|
+
import { createHash } from 'crypto';
|
|
17
|
+
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'fs';
|
|
18
|
+
import { dirname, resolve } from 'path';
|
|
19
|
+
/** Per-file restoration budget; larger files are hash-verified only. */
|
|
20
|
+
const MAX_RESTORE_BYTES = 1_000_000;
|
|
21
|
+
/** Total bytes of restoration content kept in memory. */
|
|
22
|
+
const MAX_TOTAL_RESTORE_BYTES = 32_000_000;
|
|
23
|
+
function sha256(buf) {
|
|
24
|
+
return createHash('sha256').update(buf).digest('hex');
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Capture the on-disk state of every protected file (original-case relative
|
|
28
|
+
* paths). Absent paths are recorded as reserved. Fail-soft per file.
|
|
29
|
+
*/
|
|
30
|
+
export function captureIntegrity(projectRoot, files) {
|
|
31
|
+
const root = resolve(projectRoot);
|
|
32
|
+
const snapshot = new Map();
|
|
33
|
+
let totalBytes = 0;
|
|
34
|
+
for (const rel of files) {
|
|
35
|
+
const abs = resolve(root, rel);
|
|
36
|
+
try {
|
|
37
|
+
if (!existsSync(abs)) {
|
|
38
|
+
snapshot.set(rel, { hash: null, content: null });
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
const bytes = readFileSync(abs);
|
|
42
|
+
const keep = bytes.length <= MAX_RESTORE_BYTES && totalBytes + bytes.length <= MAX_TOTAL_RESTORE_BYTES;
|
|
43
|
+
if (keep)
|
|
44
|
+
totalBytes += bytes.length;
|
|
45
|
+
snapshot.set(rel, { hash: sha256(bytes), content: keep ? bytes : null });
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
// Unreadable — skip; the applier-level protection still applies.
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return snapshot;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Verify every captured file against its snapshot; restore what changed.
|
|
55
|
+
* Returns what was tampered with so the caller can discard gate results.
|
|
56
|
+
*/
|
|
57
|
+
export function verifyAndRestore(projectRoot, snapshot) {
|
|
58
|
+
const root = resolve(projectRoot);
|
|
59
|
+
const check = { tampered: [], restored: [], unrecoverable: [] };
|
|
60
|
+
for (const [rel, record] of snapshot) {
|
|
61
|
+
const abs = resolve(root, rel);
|
|
62
|
+
try {
|
|
63
|
+
const existsNow = existsSync(abs);
|
|
64
|
+
if (record.hash === null) {
|
|
65
|
+
// Reserved path: must stay absent. A runtime-fabricated oracle is removed.
|
|
66
|
+
if (existsNow) {
|
|
67
|
+
check.tampered.push(rel);
|
|
68
|
+
rmSync(abs, { force: true });
|
|
69
|
+
check.restored.push(rel);
|
|
70
|
+
}
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
if (!existsNow) {
|
|
74
|
+
check.tampered.push(rel);
|
|
75
|
+
if (record.content) {
|
|
76
|
+
mkdirSync(dirname(abs), { recursive: true });
|
|
77
|
+
writeFileSync(abs, record.content);
|
|
78
|
+
check.restored.push(rel);
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
check.unrecoverable.push(rel);
|
|
82
|
+
}
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
const bytes = readFileSync(abs);
|
|
86
|
+
if (sha256(bytes) !== record.hash) {
|
|
87
|
+
check.tampered.push(rel);
|
|
88
|
+
if (record.content) {
|
|
89
|
+
writeFileSync(abs, record.content);
|
|
90
|
+
check.restored.push(rel);
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
check.unrecoverable.push(rel);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
check.unrecoverable.push(rel);
|
|
99
|
+
if (!check.tampered.includes(rel))
|
|
100
|
+
check.tampered.push(rel);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return check;
|
|
104
|
+
}
|
|
105
|
+
/** Render the violation feedback prepended to discarded gate results. */
|
|
106
|
+
export function integrityViolationFeedback(check) {
|
|
107
|
+
const lines = [
|
|
108
|
+
`GATE INTEGRITY VIOLATION: test execution modified protected file(s): ${check.tampered.join(', ')}.`,
|
|
109
|
+
'Gate results for this turn are DISCARDED. Protected test/oracle files must never be written —',
|
|
110
|
+
'not via file blocks and not at runtime from test code. Implement the source instead.',
|
|
111
|
+
];
|
|
112
|
+
if (check.unrecoverable.length > 0) {
|
|
113
|
+
lines.push(`Could not restore: ${check.unrecoverable.join(', ')} — manual attention needed.`);
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
lines.push('All modified files were restored to their original state.');
|
|
117
|
+
}
|
|
118
|
+
return lines.join('\n');
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=integrity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"integrity.js","sourceRoot":"","sources":["../../src/delivery/integrity.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spec Transitive-Import Protection
|
|
3
|
+
*
|
|
4
|
+
* A protected spec is only as trustworthy as its oracle material: helpers
|
|
5
|
+
* that build expectations, fixtures/data files holding expected output, and
|
|
6
|
+
* mocks shaping the environment. If those live OUTSIDE test directories the
|
|
7
|
+
* applier's test-file protection misses them, and a model can satisfy the
|
|
8
|
+
* spec by rewriting what it asserts against.
|
|
9
|
+
*
|
|
10
|
+
* This module walks each spec's import graph and protects what specs import
|
|
11
|
+
* as oracle material:
|
|
12
|
+
*
|
|
13
|
+
* - relative imports resolving into fixture/helper/mock-conventional
|
|
14
|
+
* locations (directory segments or basename markers)
|
|
15
|
+
* - data files (.json/.yaml/.txt/.csv/.snap/…) — referenced via imports OR
|
|
16
|
+
* quoted string literals (readFileSync paths), since a data file imported
|
|
17
|
+
* by a spec is expected-output material, never the unit under test
|
|
18
|
+
* - the transitive imports of anything protected above (helper chains)
|
|
19
|
+
*
|
|
20
|
+
* Deliberately NOT protected: plain code the spec imports (../src/duration)
|
|
21
|
+
* — that is the unit under test, the very thing the model must write.
|
|
22
|
+
* Distinguishing "helper" from "implementation" beyond naming conventions is
|
|
23
|
+
* undecidable here; the conventional set keeps brownfield delivery possible
|
|
24
|
+
* while closing the fixture/helper channel. All analysis is fail-soft.
|
|
25
|
+
*/
|
|
26
|
+
export interface ProtectionSnapshot {
|
|
27
|
+
/** Lower-cased relative paths for the applier membership check */
|
|
28
|
+
protectedFiles: Set<string>;
|
|
29
|
+
/** Original-case paths for prompts/diagnostics */
|
|
30
|
+
display: string[];
|
|
31
|
+
}
|
|
32
|
+
/** True when `rel` ('/'-separated) is oracle material by location or name. */
|
|
33
|
+
export declare function isOraclePath(rel: string): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Resolve a relative import specifier the way bundlers/loaders do:
|
|
36
|
+
* exact file, TS-style .js→.ts swap, appended extensions, directory index.
|
|
37
|
+
* Returns EVERY existing root-relative candidate ('/'-separated) — this is
|
|
38
|
+
* protection, not module resolution, so when both x.js and x.ts exist we
|
|
39
|
+
* protect both rather than guess which one the runner loads.
|
|
40
|
+
*/
|
|
41
|
+
export declare function resolveRelativeImport(fromFileAbs: string, specifier: string, projectRootAbs: string): string[];
|
|
42
|
+
/**
|
|
43
|
+
* Expand a set of spec files (original-case, root-relative) to the oracle
|
|
44
|
+
* material they transitively reference. Returns original-case paths,
|
|
45
|
+
* EXCLUDING the seeds themselves. Bounded and fail-soft.
|
|
46
|
+
*/
|
|
47
|
+
export declare function expandSpecImports(projectRoot: string, specFiles: string[]): string[];
|
|
48
|
+
/**
|
|
49
|
+
* Full gate-integrity snapshot: pre-existing test files plus the oracle
|
|
50
|
+
* material their import graphs reference. The membership set is lower-cased
|
|
51
|
+
* (case-insensitive matching); `display` keeps original casing for prompts.
|
|
52
|
+
*/
|
|
53
|
+
export declare function snapshotProtection(projectRoot: string): ProtectionSnapshot;
|
|
54
|
+
//# sourceMappingURL=spec-imports.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spec-imports.d.ts","sourceRoot":"","sources":["../../src/delivery/spec-imports.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAsDH,MAAM,WAAW,kBAAkB;IACjC,kEAAkE;IAClE,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC5B,kDAAkD;IAClD,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,8EAA8E;AAC9E,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAOjD;AAUD;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,cAAc,EAAE,MAAM,GACrB,MAAM,EAAE,CAqBV;AAyCD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAmDpF;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,kBAAkB,CAkB1E"}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spec Transitive-Import Protection
|
|
3
|
+
*
|
|
4
|
+
* A protected spec is only as trustworthy as its oracle material: helpers
|
|
5
|
+
* that build expectations, fixtures/data files holding expected output, and
|
|
6
|
+
* mocks shaping the environment. If those live OUTSIDE test directories the
|
|
7
|
+
* applier's test-file protection misses them, and a model can satisfy the
|
|
8
|
+
* spec by rewriting what it asserts against.
|
|
9
|
+
*
|
|
10
|
+
* This module walks each spec's import graph and protects what specs import
|
|
11
|
+
* as oracle material:
|
|
12
|
+
*
|
|
13
|
+
* - relative imports resolving into fixture/helper/mock-conventional
|
|
14
|
+
* locations (directory segments or basename markers)
|
|
15
|
+
* - data files (.json/.yaml/.txt/.csv/.snap/…) — referenced via imports OR
|
|
16
|
+
* quoted string literals (readFileSync paths), since a data file imported
|
|
17
|
+
* by a spec is expected-output material, never the unit under test
|
|
18
|
+
* - the transitive imports of anything protected above (helper chains)
|
|
19
|
+
*
|
|
20
|
+
* Deliberately NOT protected: plain code the spec imports (../src/duration)
|
|
21
|
+
* — that is the unit under test, the very thing the model must write.
|
|
22
|
+
* Distinguishing "helper" from "implementation" beyond naming conventions is
|
|
23
|
+
* undecidable here; the conventional set keeps brownfield delivery possible
|
|
24
|
+
* while closing the fixture/helper channel. All analysis is fail-soft.
|
|
25
|
+
*/
|
|
26
|
+
import { readFileSync, statSync } from 'fs';
|
|
27
|
+
import { dirname, isAbsolute, relative, resolve, sep } from 'path';
|
|
28
|
+
import { isTestFilePath, listTestFiles } from './applier.js';
|
|
29
|
+
/** Directory names whose contents are oracle material when a spec imports them. */
|
|
30
|
+
const ORACLE_DIR_SEGMENTS = new Set([
|
|
31
|
+
'fixtures',
|
|
32
|
+
'fixture',
|
|
33
|
+
'__fixtures__',
|
|
34
|
+
'helpers',
|
|
35
|
+
'helper',
|
|
36
|
+
'mocks',
|
|
37
|
+
'mock',
|
|
38
|
+
'__mocks__',
|
|
39
|
+
'stubs',
|
|
40
|
+
'stub',
|
|
41
|
+
'testdata',
|
|
42
|
+
'test-data',
|
|
43
|
+
'test-utils',
|
|
44
|
+
'testutils',
|
|
45
|
+
'__snapshots__',
|
|
46
|
+
'snapshots',
|
|
47
|
+
'golden',
|
|
48
|
+
'goldens',
|
|
49
|
+
]);
|
|
50
|
+
/** Basename markers for oracle files outside conventional directories.
|
|
51
|
+
* `setup`/`matchers` cover runner bootstrap files (vitest.setup.ts,
|
|
52
|
+
* custom-matcher modules) that shape what "passing" means. */
|
|
53
|
+
const ORACLE_BASENAME_RE = /\.(fixture|fixtures|mock|mocks|stub|stubs|helper|helpers|setup|matchers?)\.[^.]+$/;
|
|
54
|
+
const DATA_EXTS = 'json|jsonc|json5|ya?ml|txt|csv|tsv|xml|html|snap|golden';
|
|
55
|
+
/** Data extensions: imported/referenced by a spec ⇒ expected-output material. */
|
|
56
|
+
const DATA_EXT_RE = new RegExp(`\\.(${DATA_EXTS})$`, 'i');
|
|
57
|
+
/** import/export-from/dynamic-import/require/side-effect-import specifiers.
|
|
58
|
+
* Matches inside comments/strings too — over-protection only, accepted.
|
|
59
|
+
* The clause length cap bounds regex cost on minified/pathological lines. */
|
|
60
|
+
const IMPORT_RE = /(?:import|export)\s[^'"`;]{0,2048}?from\s*['"]([^'"]+)['"]|import\s*\(\s*['"]([^'"]+)['"]\s*\)|require\s*\(\s*['"]([^'"]+)['"]\s*\)|import\s+['"]([^'"]+)['"]/g;
|
|
61
|
+
/** Quoted relative-ish paths with data extensions (readFileSync etc.). */
|
|
62
|
+
const DATA_LITERAL_RE = new RegExp(`['"]([^'"\\n]{1,300}?\\.(?:${DATA_EXTS}))['"]`, 'gi');
|
|
63
|
+
const CODE_EXTS = ['.ts', '.tsx', '.mts', '.cts', '.js', '.jsx', '.mjs', '.cjs'];
|
|
64
|
+
const MAX_SPEC_BYTES = 2_000_000;
|
|
65
|
+
const MAX_GRAPH_FILES = 500;
|
|
66
|
+
const MAX_GRAPH_DEPTH = 8;
|
|
67
|
+
const MAX_GRAPH_BYTES = 20_000_000;
|
|
68
|
+
/** True when `rel` ('/'-separated) is oracle material by location or name. */
|
|
69
|
+
export function isOraclePath(rel) {
|
|
70
|
+
const segments = rel.split('/');
|
|
71
|
+
const base = segments[segments.length - 1].toLowerCase();
|
|
72
|
+
if (base === 'conftest.py')
|
|
73
|
+
return true;
|
|
74
|
+
if (segments.slice(0, -1).some((s) => ORACLE_DIR_SEGMENTS.has(s.toLowerCase())))
|
|
75
|
+
return true;
|
|
76
|
+
if (ORACLE_BASENAME_RE.test(base))
|
|
77
|
+
return true;
|
|
78
|
+
return DATA_EXT_RE.test(base);
|
|
79
|
+
}
|
|
80
|
+
function isFile(abs) {
|
|
81
|
+
try {
|
|
82
|
+
return statSync(abs).isFile();
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Resolve a relative import specifier the way bundlers/loaders do:
|
|
90
|
+
* exact file, TS-style .js→.ts swap, appended extensions, directory index.
|
|
91
|
+
* Returns EVERY existing root-relative candidate ('/'-separated) — this is
|
|
92
|
+
* protection, not module resolution, so when both x.js and x.ts exist we
|
|
93
|
+
* protect both rather than guess which one the runner loads.
|
|
94
|
+
*/
|
|
95
|
+
export function resolveRelativeImport(fromFileAbs, specifier, projectRootAbs) {
|
|
96
|
+
if (!specifier.startsWith('./') && !specifier.startsWith('../'))
|
|
97
|
+
return [];
|
|
98
|
+
const base = resolve(dirname(fromFileAbs), specifier);
|
|
99
|
+
const candidates = [base];
|
|
100
|
+
// TS sources import emitted-name './x.js' while the file on disk is x.ts
|
|
101
|
+
const jsSwap = base.match(/^(.*)\.([mc]?)js$/);
|
|
102
|
+
if (jsSwap) {
|
|
103
|
+
candidates.push(`${jsSwap[1]}.${jsSwap[2]}ts`, `${jsSwap[1]}.tsx`);
|
|
104
|
+
}
|
|
105
|
+
for (const ext of CODE_EXTS)
|
|
106
|
+
candidates.push(base + ext);
|
|
107
|
+
for (const ext of CODE_EXTS)
|
|
108
|
+
candidates.push(resolve(base, `index${ext}`));
|
|
109
|
+
const found = [];
|
|
110
|
+
for (const candidate of candidates) {
|
|
111
|
+
if (!isFile(candidate))
|
|
112
|
+
continue;
|
|
113
|
+
const rel = relative(projectRootAbs, candidate);
|
|
114
|
+
if (rel === '..' || rel.startsWith(`..${sep}`) || isAbsolute(rel))
|
|
115
|
+
continue;
|
|
116
|
+
found.push(rel.split(sep).join('/'));
|
|
117
|
+
}
|
|
118
|
+
return found;
|
|
119
|
+
}
|
|
120
|
+
function extractImportSpecifiers(source) {
|
|
121
|
+
const specs = [];
|
|
122
|
+
for (const match of source.matchAll(IMPORT_RE)) {
|
|
123
|
+
const spec = match[1] ?? match[2] ?? match[3] ?? match[4];
|
|
124
|
+
if (spec)
|
|
125
|
+
specs.push(spec);
|
|
126
|
+
}
|
|
127
|
+
return specs;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Quoted data-file references (readFileSync paths), resolved against BOTH
|
|
131
|
+
* the spec's dir and the root — runtimes read relative to cwd, authors write
|
|
132
|
+
* relative to the spec, and protecting both is fail-safe. A literal under an
|
|
133
|
+
* oracle-conventional directory is protected even when the file does NOT yet
|
|
134
|
+
* exist ("reserved"): otherwise a spec reading a missing goldens/output.json
|
|
135
|
+
* invites the model to fabricate the golden instead of the implementation.
|
|
136
|
+
*/
|
|
137
|
+
function extractDataLiterals(source, fromFileAbs, rootAbs) {
|
|
138
|
+
const found = [];
|
|
139
|
+
for (const match of source.matchAll(DATA_LITERAL_RE)) {
|
|
140
|
+
const literal = match[1];
|
|
141
|
+
if (literal.includes('://') || literal.startsWith('/'))
|
|
142
|
+
continue;
|
|
143
|
+
for (const baseDir of [dirname(fromFileAbs), rootAbs]) {
|
|
144
|
+
const abs = resolve(baseDir, literal);
|
|
145
|
+
const rel = relative(rootAbs, abs);
|
|
146
|
+
if (rel === '..' || rel.startsWith(`..${sep}`) || isAbsolute(rel))
|
|
147
|
+
continue;
|
|
148
|
+
const relPosix = rel.split(sep).join('/');
|
|
149
|
+
const segments = relPosix.split('/');
|
|
150
|
+
const inOracleDir = segments
|
|
151
|
+
.slice(0, -1)
|
|
152
|
+
.some((seg) => ORACLE_DIR_SEGMENTS.has(seg.toLowerCase()));
|
|
153
|
+
if (isFile(abs) || inOracleDir) {
|
|
154
|
+
found.push(relPosix);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return found;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Expand a set of spec files (original-case, root-relative) to the oracle
|
|
162
|
+
* material they transitively reference. Returns original-case paths,
|
|
163
|
+
* EXCLUDING the seeds themselves. Bounded and fail-soft.
|
|
164
|
+
*/
|
|
165
|
+
export function expandSpecImports(projectRoot, specFiles) {
|
|
166
|
+
const rootAbs = resolve(projectRoot);
|
|
167
|
+
const protectedExtra = new Set();
|
|
168
|
+
const visited = new Set();
|
|
169
|
+
let bytesRead = 0;
|
|
170
|
+
// Queue entries: [relPath, depth] — we recurse through specs and oracle
|
|
171
|
+
// files, never through plain implementation code.
|
|
172
|
+
const queue = specFiles.map((f) => [f, 0]);
|
|
173
|
+
while (queue.length > 0 && visited.size < MAX_GRAPH_FILES && bytesRead < MAX_GRAPH_BYTES) {
|
|
174
|
+
const [rel, depth] = queue.shift();
|
|
175
|
+
if (depth > MAX_GRAPH_DEPTH || visited.has(rel))
|
|
176
|
+
continue;
|
|
177
|
+
visited.add(rel);
|
|
178
|
+
// Data files are protected as leaves, never scanned: their contents are
|
|
179
|
+
// arbitrary data, and code regexes over fixture JSON manufacture
|
|
180
|
+
// second-order false positives.
|
|
181
|
+
if (DATA_EXT_RE.test(rel))
|
|
182
|
+
continue;
|
|
183
|
+
const abs = resolve(rootAbs, rel);
|
|
184
|
+
let source;
|
|
185
|
+
try {
|
|
186
|
+
const st = statSync(abs);
|
|
187
|
+
if (!st.isFile() || st.size > MAX_SPEC_BYTES)
|
|
188
|
+
continue;
|
|
189
|
+
bytesRead += st.size;
|
|
190
|
+
source = readFileSync(abs, 'utf-8');
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
const referenced = new Set();
|
|
196
|
+
for (const spec of extractImportSpecifiers(source)) {
|
|
197
|
+
for (const resolved of resolveRelativeImport(abs, spec, rootAbs)) {
|
|
198
|
+
referenced.add(resolved);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
for (const dataRel of extractDataLiterals(source, abs, rootAbs)) {
|
|
202
|
+
referenced.add(dataRel);
|
|
203
|
+
}
|
|
204
|
+
for (const ref of referenced) {
|
|
205
|
+
if (!isOraclePath(ref) && !isTestFilePath(ref))
|
|
206
|
+
continue; // unit under test stays writable
|
|
207
|
+
// Added unconditionally: a test-path ref may live where the directory
|
|
208
|
+
// walk cannot see it (hidden dir, skipped segment, beyond depth), so
|
|
209
|
+
// relying on listTestFiles to have found it would leave a gap.
|
|
210
|
+
protectedExtra.add(ref);
|
|
211
|
+
// Helper chains: a protected file's own oracle imports are protected
|
|
212
|
+
queue.push([ref, depth + 1]);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return [...protectedExtra];
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Full gate-integrity snapshot: pre-existing test files plus the oracle
|
|
219
|
+
* material their import graphs reference. The membership set is lower-cased
|
|
220
|
+
* (case-insensitive matching); `display` keeps original casing for prompts.
|
|
221
|
+
*/
|
|
222
|
+
export function snapshotProtection(projectRoot) {
|
|
223
|
+
let tests = [];
|
|
224
|
+
try {
|
|
225
|
+
tests = listTestFiles(projectRoot);
|
|
226
|
+
}
|
|
227
|
+
catch {
|
|
228
|
+
tests = [];
|
|
229
|
+
}
|
|
230
|
+
let extra = [];
|
|
231
|
+
try {
|
|
232
|
+
extra = expandSpecImports(projectRoot, tests);
|
|
233
|
+
}
|
|
234
|
+
catch {
|
|
235
|
+
extra = [];
|
|
236
|
+
}
|
|
237
|
+
const display = [...new Set([...tests, ...extra])].sort();
|
|
238
|
+
return {
|
|
239
|
+
protectedFiles: new Set(display.map((f) => f.toLowerCase())),
|
|
240
|
+
display,
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
//# sourceMappingURL=spec-imports.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spec-imports.js","sourceRoot":"","sources":["../../src/delivery/spec-imports.ts"],"names":[],"mappings":"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"}
|