@gate-forge/core 0.1.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/LICENSE +202 -0
- package/dist/baselines/adoption.d.ts +76 -0
- package/dist/baselines/adoption.d.ts.map +1 -0
- package/dist/baselines/adoption.js +163 -0
- package/dist/baselines/adoption.js.map +1 -0
- package/dist/baselines/index.d.ts +102 -0
- package/dist/baselines/index.d.ts.map +1 -0
- package/dist/baselines/index.js +186 -0
- package/dist/baselines/index.js.map +1 -0
- package/dist/canonical-json.d.ts +51 -0
- package/dist/canonical-json.d.ts.map +1 -0
- package/dist/canonical-json.js +112 -0
- package/dist/canonical-json.js.map +1 -0
- package/dist/classifier/bind.d.ts +64 -0
- package/dist/classifier/bind.d.ts.map +1 -0
- package/dist/classifier/bind.js +188 -0
- package/dist/classifier/bind.js.map +1 -0
- package/dist/classifier/classify.d.ts +154 -0
- package/dist/classifier/classify.d.ts.map +1 -0
- package/dist/classifier/classify.js +1207 -0
- package/dist/classifier/classify.js.map +1 -0
- package/dist/classifier/glob.d.ts +48 -0
- package/dist/classifier/glob.d.ts.map +1 -0
- package/dist/classifier/glob.js +104 -0
- package/dist/classifier/glob.js.map +1 -0
- package/dist/classifier/index.d.ts +10 -0
- package/dist/classifier/index.d.ts.map +1 -0
- package/dist/classifier/index.js +21 -0
- package/dist/classifier/index.js.map +1 -0
- package/dist/classifier/schema.d.ts +150 -0
- package/dist/classifier/schema.d.ts.map +1 -0
- package/dist/classifier/schema.js +139 -0
- package/dist/classifier/schema.js.map +1 -0
- package/dist/config/index.d.ts +259 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +523 -0
- package/dist/config/index.js.map +1 -0
- package/dist/fingerprints.d.ts +56 -0
- package/dist/fingerprints.d.ts.map +1 -0
- package/dist/fingerprints.js +59 -0
- package/dist/fingerprints.js.map +1 -0
- package/dist/graph/build.d.ts +16 -0
- package/dist/graph/build.d.ts.map +1 -0
- package/dist/graph/build.js +423 -0
- package/dist/graph/build.js.map +1 -0
- package/dist/graph/index.d.ts +12 -0
- package/dist/graph/index.d.ts.map +1 -0
- package/dist/graph/index.js +10 -0
- package/dist/graph/index.js.map +1 -0
- package/dist/graph/schema.d.ts +448 -0
- package/dist/graph/schema.d.ts.map +1 -0
- package/dist/graph/schema.js +237 -0
- package/dist/graph/schema.js.map +1 -0
- package/dist/graph/symbols.d.ts +129 -0
- package/dist/graph/symbols.d.ts.map +1 -0
- package/dist/graph/symbols.js +177 -0
- package/dist/graph/symbols.js.map +1 -0
- package/dist/graph/util.d.ts +40 -0
- package/dist/graph/util.d.ts.map +1 -0
- package/dist/graph/util.js +41 -0
- package/dist/graph/util.js.map +1 -0
- package/dist/index.d.ts +683 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +563 -0
- package/dist/index.js.map +1 -0
- package/dist/mapping/resolve.d.ts +177 -0
- package/dist/mapping/resolve.d.ts.map +1 -0
- package/dist/mapping/resolve.js +523 -0
- package/dist/mapping/resolve.js.map +1 -0
- package/dist/policy/coverage.d.ts +79 -0
- package/dist/policy/coverage.d.ts.map +1 -0
- package/dist/policy/coverage.js +119 -0
- package/dist/policy/coverage.js.map +1 -0
- package/dist/policy/evaluate.d.ts +275 -0
- package/dist/policy/evaluate.d.ts.map +1 -0
- package/dist/policy/evaluate.js +382 -0
- package/dist/policy/evaluate.js.map +1 -0
- package/dist/policy/index.d.ts +15 -0
- package/dist/policy/index.d.ts.map +1 -0
- package/dist/policy/index.js +14 -0
- package/dist/policy/index.js.map +1 -0
- package/dist/policy/trusted.d.ts +59 -0
- package/dist/policy/trusted.d.ts.map +1 -0
- package/dist/policy/trusted.js +96 -0
- package/dist/policy/trusted.js.map +1 -0
- package/dist/provenance.d.ts +170 -0
- package/dist/provenance.d.ts.map +1 -0
- package/dist/provenance.js +306 -0
- package/dist/provenance.js.map +1 -0
- package/dist/receipt/index.d.ts +74 -0
- package/dist/receipt/index.d.ts.map +1 -0
- package/dist/receipt/index.js +154 -0
- package/dist/receipt/index.js.map +1 -0
- package/dist/report/index.d.ts +100 -0
- package/dist/report/index.d.ts.map +1 -0
- package/dist/report/index.js +355 -0
- package/dist/report/index.js.map +1 -0
- package/dist/schemas/adoption.d.ts +40 -0
- package/dist/schemas/adoption.d.ts.map +1 -0
- package/dist/schemas/adoption.js +87 -0
- package/dist/schemas/adoption.js.map +1 -0
- package/dist/schemas/baseline.d.ts +19 -0
- package/dist/schemas/baseline.d.ts.map +1 -0
- package/dist/schemas/baseline.js +45 -0
- package/dist/schemas/baseline.js.map +1 -0
- package/dist/schemas/claim.d.ts +32 -0
- package/dist/schemas/claim.d.ts.map +1 -0
- package/dist/schemas/claim.js +34 -0
- package/dist/schemas/claim.js.map +1 -0
- package/dist/schemas/classification-policy.d.ts +84 -0
- package/dist/schemas/classification-policy.d.ts.map +1 -0
- package/dist/schemas/classification-policy.js +130 -0
- package/dist/schemas/classification-policy.js.map +1 -0
- package/dist/schemas/classification-signal.d.ts +125 -0
- package/dist/schemas/classification-signal.d.ts.map +1 -0
- package/dist/schemas/classification-signal.js +126 -0
- package/dist/schemas/classification-signal.js.map +1 -0
- package/dist/schemas/classification.d.ts +120 -0
- package/dist/schemas/classification.d.ts.map +1 -0
- package/dist/schemas/classification.js +154 -0
- package/dist/schemas/classification.js.map +1 -0
- package/dist/schemas/common.d.ts +73 -0
- package/dist/schemas/common.d.ts.map +1 -0
- package/dist/schemas/common.js +60 -0
- package/dist/schemas/common.js.map +1 -0
- package/dist/schemas/coverage-policy.d.ts +89 -0
- package/dist/schemas/coverage-policy.d.ts.map +1 -0
- package/dist/schemas/coverage-policy.js +81 -0
- package/dist/schemas/coverage-policy.js.map +1 -0
- package/dist/schemas/evidence.d.ts +65 -0
- package/dist/schemas/evidence.d.ts.map +1 -0
- package/dist/schemas/evidence.js +64 -0
- package/dist/schemas/evidence.js.map +1 -0
- package/dist/schemas/execution-result.d.ts +214 -0
- package/dist/schemas/execution-result.d.ts.map +1 -0
- package/dist/schemas/execution-result.js +240 -0
- package/dist/schemas/execution-result.js.map +1 -0
- package/dist/schemas/gate-receipt.d.ts +86 -0
- package/dist/schemas/gate-receipt.d.ts.map +1 -0
- package/dist/schemas/gate-receipt.js +193 -0
- package/dist/schemas/gate-receipt.js.map +1 -0
- package/dist/schemas/index.d.ts +22 -0
- package/dist/schemas/index.d.ts.map +1 -0
- package/dist/schemas/index.js +22 -0
- package/dist/schemas/index.js.map +1 -0
- package/dist/schemas/obligation.d.ts +37 -0
- package/dist/schemas/obligation.d.ts.map +1 -0
- package/dist/schemas/obligation.js +46 -0
- package/dist/schemas/obligation.js.map +1 -0
- package/dist/schemas/plugin.d.ts +19 -0
- package/dist/schemas/plugin.d.ts.map +1 -0
- package/dist/schemas/plugin.js +20 -0
- package/dist/schemas/plugin.js.map +1 -0
- package/dist/schemas/policy.d.ts +73 -0
- package/dist/schemas/policy.d.ts.map +1 -0
- package/dist/schemas/policy.js +46 -0
- package/dist/schemas/policy.js.map +1 -0
- package/dist/schemas/resource.d.ts +26 -0
- package/dist/schemas/resource.d.ts.map +1 -0
- package/dist/schemas/resource.js +29 -0
- package/dist/schemas/resource.js.map +1 -0
- package/dist/schemas/run-manifest.d.ts +72 -0
- package/dist/schemas/run-manifest.d.ts.map +1 -0
- package/dist/schemas/run-manifest.js +125 -0
- package/dist/schemas/run-manifest.js.map +1 -0
- package/dist/schemas/runner-adapter.d.ts +169 -0
- package/dist/schemas/runner-adapter.d.ts.map +1 -0
- package/dist/schemas/runner-adapter.js +2 -0
- package/dist/schemas/runner-adapter.js.map +1 -0
- package/dist/schemas/test-catalog.d.ts +472 -0
- package/dist/schemas/test-catalog.d.ts.map +1 -0
- package/dist/schemas/test-catalog.js +311 -0
- package/dist/schemas/test-catalog.js.map +1 -0
- package/dist/schemas/test-map.d.ts +99 -0
- package/dist/schemas/test-map.d.ts.map +1 -0
- package/dist/schemas/test-map.js +103 -0
- package/dist/schemas/test-map.js.map +1 -0
- package/dist/schemas/verdict.d.ts +73 -0
- package/dist/schemas/verdict.d.ts.map +1 -0
- package/dist/schemas/verdict.js +92 -0
- package/dist/schemas/verdict.js.map +1 -0
- package/dist/schemas/waiver.d.ts +36 -0
- package/dist/schemas/waiver.d.ts.map +1 -0
- package/dist/schemas/waiver.js +42 -0
- package/dist/schemas/waiver.js.map +1 -0
- package/dist/supervision/index.d.ts +170 -0
- package/dist/supervision/index.d.ts.map +1 -0
- package/dist/supervision/index.js +354 -0
- package/dist/supervision/index.js.map +1 -0
- package/dist/testing/clock.d.ts +50 -0
- package/dist/testing/clock.d.ts.map +1 -0
- package/dist/testing/clock.js +75 -0
- package/dist/testing/clock.js.map +1 -0
- package/dist/testing/env.d.ts +66 -0
- package/dist/testing/env.d.ts.map +1 -0
- package/dist/testing/env.js +106 -0
- package/dist/testing/env.js.map +1 -0
- package/dist/testing/gate-runner.d.ts +141 -0
- package/dist/testing/gate-runner.d.ts.map +1 -0
- package/dist/testing/gate-runner.js +173 -0
- package/dist/testing/gate-runner.js.map +1 -0
- package/dist/testing/gf19.d.ts +106 -0
- package/dist/testing/gf19.d.ts.map +1 -0
- package/dist/testing/gf19.js +282 -0
- package/dist/testing/gf19.js.map +1 -0
- package/dist/testing/index.d.ts +20 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +14 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/red-probe.d.ts +138 -0
- package/dist/testing/red-probe.d.ts.map +1 -0
- package/dist/testing/red-probe.js +251 -0
- package/dist/testing/red-probe.js.map +1 -0
- package/dist/testing/temp-repo.d.ts +126 -0
- package/dist/testing/temp-repo.d.ts.map +1 -0
- package/dist/testing/temp-repo.js +214 -0
- package/dist/testing/temp-repo.js.map +1 -0
- package/dist/verdict/cause.d.ts +99 -0
- package/dist/verdict/cause.d.ts.map +1 -0
- package/dist/verdict/cause.js +206 -0
- package/dist/verdict/cause.js.map +1 -0
- package/dist/verdict/evaluate.d.ts +135 -0
- package/dist/verdict/evaluate.d.ts.map +1 -0
- package/dist/verdict/evaluate.js +1513 -0
- package/dist/verdict/evaluate.js.map +1 -0
- package/dist/verdict/index.d.ts +21 -0
- package/dist/verdict/index.d.ts.map +1 -0
- package/dist/verdict/index.js +20 -0
- package/dist/verdict/index.js.map +1 -0
- package/dist/verdict/pack-verifiers.d.ts +97 -0
- package/dist/verdict/pack-verifiers.d.ts.map +1 -0
- package/dist/verdict/pack-verifiers.js +763 -0
- package/dist/verdict/pack-verifiers.js.map +1 -0
- package/dist/verdict/registry.d.ts +180 -0
- package/dist/verdict/registry.d.ts.map +1 -0
- package/dist/verdict/registry.js +67 -0
- package/dist/verdict/registry.js.map +1 -0
- package/dist/waivers/index.d.ts +103 -0
- package/dist/waivers/index.d.ts.map +1 -0
- package/dist/waivers/index.js +192 -0
- package/dist/waivers/index.js.map +1 -0
- package/package.json +40 -0
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RED-PROBE discipline (fixture harness, G7): a green suite proves
|
|
3
|
+
* nothing unless the same guard FAILS against deliberately broken
|
|
4
|
+
* behavior. Every gate/fixture guard ships as a probe pair — the guard
|
|
5
|
+
* against correct behavior (must pass) and the identical guard against a
|
|
6
|
+
* deliberately broken variant (must fail) — and a broken probe that
|
|
7
|
+
* PASSES is a fake green: the loudest possible failure.
|
|
8
|
+
*
|
|
9
|
+
* Suite-level proof (CI-proof mode) runs the actual vitest suite twice —
|
|
10
|
+
* normal vs deliberately-broken sources — via {@link spawnVitest} and
|
|
11
|
+
* {@link writeProbeSuite}; the broken run must exit non-zero. See
|
|
12
|
+
* `docs/testing/RED_PROBE.md` for the per-phase recording discipline.
|
|
13
|
+
*/
|
|
14
|
+
import { spawnSync } from 'node:child_process';
|
|
15
|
+
import { existsSync, mkdirSync, rmSync, symlinkSync, writeFileSync } from 'node:fs';
|
|
16
|
+
import { dirname, join } from 'node:path';
|
|
17
|
+
import { fileURLToPath } from 'node:url';
|
|
18
|
+
/** Thrown by {@link runRedProbe} when a probe is not honest. */
|
|
19
|
+
export class RedProbeFailure extends Error {
|
|
20
|
+
/** The failing record. */
|
|
21
|
+
record;
|
|
22
|
+
constructor(record) {
|
|
23
|
+
super(record.failure ?? `red-probe '${record.name}' is not honest`);
|
|
24
|
+
this.name = 'RedProbeFailure';
|
|
25
|
+
this.record = record;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Runs one probe pair and classifies the outcome.
|
|
30
|
+
*
|
|
31
|
+
* Args:
|
|
32
|
+
* probe: the green/broken guard pair.
|
|
33
|
+
*
|
|
34
|
+
* Returns:
|
|
35
|
+
* RedProbeRecord: the classified outcome (never throws).
|
|
36
|
+
*/
|
|
37
|
+
export async function runRedProbe(probe) {
|
|
38
|
+
let greenPassed = true;
|
|
39
|
+
let greenError;
|
|
40
|
+
try {
|
|
41
|
+
await probe.green();
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
greenPassed = false;
|
|
45
|
+
greenError = error;
|
|
46
|
+
}
|
|
47
|
+
let brokenFailed = false;
|
|
48
|
+
let brokenError;
|
|
49
|
+
try {
|
|
50
|
+
await probe.broken();
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
brokenFailed = true;
|
|
54
|
+
brokenError = error;
|
|
55
|
+
}
|
|
56
|
+
const record = {
|
|
57
|
+
name: probe.name,
|
|
58
|
+
greenPassed,
|
|
59
|
+
brokenFailed,
|
|
60
|
+
ok: greenPassed && brokenFailed,
|
|
61
|
+
};
|
|
62
|
+
if (!greenPassed) {
|
|
63
|
+
record.failure =
|
|
64
|
+
`guard FAILS on correct behavior (the guard itself is broken): ${errorMessage(greenError)}`;
|
|
65
|
+
}
|
|
66
|
+
else if (!brokenFailed) {
|
|
67
|
+
record.failure =
|
|
68
|
+
`FAKE GREEN: the guard passed against deliberately broken behavior — it asserts nothing. ${errorMessage(brokenError)}`;
|
|
69
|
+
}
|
|
70
|
+
return record;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Extracts a one-line message from a thrown value.
|
|
74
|
+
*
|
|
75
|
+
* Args:
|
|
76
|
+
* error: anything thrown.
|
|
77
|
+
*
|
|
78
|
+
* Returns:
|
|
79
|
+
* string: the message, or a fallback description.
|
|
80
|
+
*/
|
|
81
|
+
function errorMessage(error) {
|
|
82
|
+
if (error === undefined)
|
|
83
|
+
return '(no error thrown)';
|
|
84
|
+
if (error instanceof Error)
|
|
85
|
+
return error.message.split('\n')[0] ?? error.message;
|
|
86
|
+
return String(error);
|
|
87
|
+
}
|
|
88
|
+
/** Thrown by {@link runRedProbes} when any probe is not honest. */
|
|
89
|
+
export class RedProbeSuiteError extends Error {
|
|
90
|
+
/** All records, honest or not. */
|
|
91
|
+
records;
|
|
92
|
+
constructor(records) {
|
|
93
|
+
const bad = records.filter((record) => !record.ok);
|
|
94
|
+
super(`red-probe suite FAILED: ${bad.length} of ${records.length} probe(s) are fake-green or broken guards\n` +
|
|
95
|
+
bad.map((record) => ` - ${record.name}: ${record.failure}`).join('\n'));
|
|
96
|
+
this.name = 'RedProbeSuiteError';
|
|
97
|
+
this.records = records;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Runs probes sequentially and throws {@link RedProbeSuiteError} when any
|
|
102
|
+
* is not honest (pass `{throwOnFailure: false}` to only collect).
|
|
103
|
+
*
|
|
104
|
+
* Args:
|
|
105
|
+
* probes: probe pairs, run in order.
|
|
106
|
+
* options: `throwOnFailure` default true.
|
|
107
|
+
*
|
|
108
|
+
* Returns:
|
|
109
|
+
* RedProbeRecord[]: one record per probe, in order.
|
|
110
|
+
*/
|
|
111
|
+
export async function runRedProbes(probes, { throwOnFailure = true } = {}) {
|
|
112
|
+
const records = [];
|
|
113
|
+
for (const probe of probes) {
|
|
114
|
+
records.push(await runRedProbe(probe));
|
|
115
|
+
}
|
|
116
|
+
if (throwOnFailure && records.some((record) => !record.ok)) {
|
|
117
|
+
throw new RedProbeSuiteError(records);
|
|
118
|
+
}
|
|
119
|
+
return records;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Renders probe records as a markdown table — the body of a run record.
|
|
123
|
+
*
|
|
124
|
+
* Args:
|
|
125
|
+
* records: probe outcomes.
|
|
126
|
+
*
|
|
127
|
+
* Returns:
|
|
128
|
+
* string: markdown table (probe / green passed / broken failed / verdict).
|
|
129
|
+
*/
|
|
130
|
+
export function formatProbeRecords(records) {
|
|
131
|
+
const rows = records.map((record) => `| ${record.name} | ${record.greenPassed ? 'yes' : 'NO'} | ${record.brokenFailed ? 'yes' : 'NO'} | ${record.ok ? 'ok' : 'FAKE GREEN / BROKEN GUARD'} |`);
|
|
132
|
+
return [
|
|
133
|
+
'| probe | green passed | broken failed | verdict |',
|
|
134
|
+
'| --- | --- | --- | --- |',
|
|
135
|
+
...rows,
|
|
136
|
+
'',
|
|
137
|
+
].join('\n');
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Writes a red-probe run record (markdown) to an explicit file path,
|
|
141
|
+
* creating parent directories. The path is caller-owned so the harness
|
|
142
|
+
* never writes outside its permission.
|
|
143
|
+
*
|
|
144
|
+
* Args:
|
|
145
|
+
* path: destination file path.
|
|
146
|
+
* records: probe outcomes to record.
|
|
147
|
+
*/
|
|
148
|
+
export function writeProbeRecords(path, records) {
|
|
149
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
150
|
+
const stamp = new Date().toISOString();
|
|
151
|
+
const header = `# Red-probe run record\n\nRecorded: ${stamp}\n\n`;
|
|
152
|
+
writeFileSync(path, header + formatProbeRecords(records), 'utf8');
|
|
153
|
+
}
|
|
154
|
+
/** The absolute path of the workspace's `node_modules/vitest/vitest.mjs`. */
|
|
155
|
+
let cachedVitestCli;
|
|
156
|
+
/**
|
|
157
|
+
* Locates the vitest CLI by walking up from this module to the nearest
|
|
158
|
+
* `node_modules` that contains vitest (works under npm workspaces
|
|
159
|
+
* hoisting regardless of package layout).
|
|
160
|
+
*
|
|
161
|
+
* Returns:
|
|
162
|
+
* string: absolute path to vitest's CLI entry.
|
|
163
|
+
* @throws Error when vitest cannot be found above this module.
|
|
164
|
+
*/
|
|
165
|
+
function vitestCliPath() {
|
|
166
|
+
if (cachedVitestCli !== undefined)
|
|
167
|
+
return cachedVitestCli;
|
|
168
|
+
let directory = dirname(fileURLToPath(import.meta.url));
|
|
169
|
+
for (;;) {
|
|
170
|
+
const candidate = join(directory, 'node_modules', 'vitest', 'vitest.mjs');
|
|
171
|
+
if (existsSync(candidate)) {
|
|
172
|
+
cachedVitestCli = candidate;
|
|
173
|
+
return candidate;
|
|
174
|
+
}
|
|
175
|
+
const parent = dirname(directory);
|
|
176
|
+
if (parent === directory) {
|
|
177
|
+
throw new Error('vitest installation not found above the testing harness');
|
|
178
|
+
}
|
|
179
|
+
directory = parent;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Runs the workspace's vitest suite as a child process (`vitest run`),
|
|
184
|
+
* fully offline. CI-proof mode runs a probe suite twice — normal vs
|
|
185
|
+
* deliberately-broken sources — and requires the broken run to exit
|
|
186
|
+
* non-zero.
|
|
187
|
+
*
|
|
188
|
+
* Args:
|
|
189
|
+
* args: extra CLI arguments (file filters, --config, …).
|
|
190
|
+
* options: cwd, env additions, timeout.
|
|
191
|
+
*
|
|
192
|
+
* Returns:
|
|
193
|
+
* VitestRunResult: exit code and captured output.
|
|
194
|
+
*/
|
|
195
|
+
export function spawnVitest(args, options = {}) {
|
|
196
|
+
const result = spawnSync(process.execPath, [vitestCliPath(), 'run', ...args], {
|
|
197
|
+
cwd: options.cwd,
|
|
198
|
+
env: { ...process.env, ...options.env, CI: '1' },
|
|
199
|
+
timeout: options.timeoutMs ?? 120_000,
|
|
200
|
+
encoding: 'utf8',
|
|
201
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
202
|
+
});
|
|
203
|
+
return {
|
|
204
|
+
exitCode: result.status ?? -1,
|
|
205
|
+
stdout: result.stdout ?? '',
|
|
206
|
+
stderr: result.stderr ?? '',
|
|
207
|
+
timedOut: result.signal === 'SIGTERM',
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Materializes a probe suite in a directory and symlinks the workspace's
|
|
212
|
+
* `node_modules` into it, so probe files can `import ... from 'vitest'`
|
|
213
|
+
* even though the directory lives in the OS tmpdir. Clean up with
|
|
214
|
+
* `rmSync(dir, {recursive: true, force: true})` — removing the symlink
|
|
215
|
+
* never traverses into the real node_modules.
|
|
216
|
+
*
|
|
217
|
+
* Args:
|
|
218
|
+
* dir: target directory (created recursively).
|
|
219
|
+
* files: file-tree spec relative to `dir` (e.g. a `vitest.config.mjs`
|
|
220
|
+
* exporting a plain object and one or more `*.test.mjs` probes).
|
|
221
|
+
*
|
|
222
|
+
* Returns:
|
|
223
|
+
* string: the directory, ready for {@link spawnVitest} with `cwd: dir`.
|
|
224
|
+
*/
|
|
225
|
+
export function writeProbeSuite(dir, files) {
|
|
226
|
+
mkdirSync(dir, { recursive: true });
|
|
227
|
+
for (const [relative, content] of Object.entries(files)) {
|
|
228
|
+
const absolute = join(dir, ...relative.split('/'));
|
|
229
|
+
mkdirSync(dirname(absolute), { recursive: true });
|
|
230
|
+
writeFileSync(absolute, content, 'utf8');
|
|
231
|
+
}
|
|
232
|
+
const link = join(dir, 'node_modules');
|
|
233
|
+
if (!existsSync(link)) {
|
|
234
|
+
symlinkSync(dirname(vitestCliPath()), link, 'dir');
|
|
235
|
+
}
|
|
236
|
+
return dir;
|
|
237
|
+
}
|
|
238
|
+
/** Test-only: drops the cached vitest CLI path (path resolution tests). */
|
|
239
|
+
export function resetVitestCliCache() {
|
|
240
|
+
cachedVitestCli = undefined;
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Removes a probe-suite directory created by {@link writeProbeSuite}.
|
|
244
|
+
*
|
|
245
|
+
* Args:
|
|
246
|
+
* dir: directory to remove.
|
|
247
|
+
*/
|
|
248
|
+
export function cleanupProbeSuite(dir) {
|
|
249
|
+
rmSync(dir, { recursive: true, force: true });
|
|
250
|
+
}
|
|
251
|
+
//# sourceMappingURL=red-probe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"red-probe.js","sourceRoot":"","sources":["../../src/testing/red-probe.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The fixed timestamp (git raw-date form) stamped onto every commit as
|
|
3
|
+
* both author and committer date. Determinism cornerstone: change it and
|
|
4
|
+
* every fixture SHA changes.
|
|
5
|
+
*/
|
|
6
|
+
export declare const FIXED_GIT_DATE = "2026-01-01T00:00:00+0000";
|
|
7
|
+
/** Result of one git invocation. */
|
|
8
|
+
export interface GitResult {
|
|
9
|
+
status: number;
|
|
10
|
+
stdout: string;
|
|
11
|
+
stderr: string;
|
|
12
|
+
}
|
|
13
|
+
/** Options for {@link TempRepo} construction. */
|
|
14
|
+
export interface TempRepoOptions {
|
|
15
|
+
/** Files written before the first command runs (repo-root-relative, posix keys). */
|
|
16
|
+
files?: Record<string, string>;
|
|
17
|
+
/** Initial branch name. Default `main`. */
|
|
18
|
+
branch?: string;
|
|
19
|
+
/** tmpdir prefix. Default `gateforge-fixture-`. */
|
|
20
|
+
prefix?: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* A disposable git repository under the OS tmpdir.
|
|
24
|
+
*
|
|
25
|
+
* Args:
|
|
26
|
+
* options: optional initial file tree, branch name, and tmpdir prefix.
|
|
27
|
+
*
|
|
28
|
+
* Returns:
|
|
29
|
+
* TempRepo: use `writeFiles`/`stage`/`commit` to build history,
|
|
30
|
+
* `cleanup` (or {@link withTempRepo}) to remove it.
|
|
31
|
+
*/
|
|
32
|
+
export declare class TempRepo {
|
|
33
|
+
/** Absolute filesystem path of the repository root. */
|
|
34
|
+
readonly root: string;
|
|
35
|
+
private readonly gitEnv;
|
|
36
|
+
constructor(options?: TempRepoOptions);
|
|
37
|
+
/**
|
|
38
|
+
* Runs one git command in the repository.
|
|
39
|
+
*
|
|
40
|
+
* Args:
|
|
41
|
+
* args: argument vector (without the `git` binary).
|
|
42
|
+
* allowFailure: when true a non-zero exit does not throw.
|
|
43
|
+
*
|
|
44
|
+
* Returns:
|
|
45
|
+
* GitResult: exit status plus captured stdout/stderr.
|
|
46
|
+
* @throws Error naming the failing command when `allowFailure` is false.
|
|
47
|
+
*/
|
|
48
|
+
git(args: string[], { allowFailure }?: {
|
|
49
|
+
allowFailure?: boolean;
|
|
50
|
+
}): GitResult;
|
|
51
|
+
/**
|
|
52
|
+
* Writes a file-tree spec into the repository. Keys are
|
|
53
|
+
* repo-root-relative posix paths; parent directories are created.
|
|
54
|
+
*
|
|
55
|
+
* Args:
|
|
56
|
+
* files: mapping of relative posix path to UTF-8 file content.
|
|
57
|
+
* @throws Error on absolute paths or `..` segments (spec writers must
|
|
58
|
+
* stay inside the repo).
|
|
59
|
+
*/
|
|
60
|
+
writeFiles(files: Record<string, string>): void;
|
|
61
|
+
/**
|
|
62
|
+
* Stages files. With no argument stages every change (`git add -A`).
|
|
63
|
+
*
|
|
64
|
+
* Args:
|
|
65
|
+
* paths: repo-root-relative paths to stage.
|
|
66
|
+
*/
|
|
67
|
+
stage(paths?: string[]): void;
|
|
68
|
+
/**
|
|
69
|
+
* Commits the staged tree with a fixed identity and date.
|
|
70
|
+
*
|
|
71
|
+
* Args:
|
|
72
|
+
* message: commit message.
|
|
73
|
+
* Returns:
|
|
74
|
+
* string: the resulting HEAD sha (40-char hex).
|
|
75
|
+
*/
|
|
76
|
+
commit(message: string): string;
|
|
77
|
+
/**
|
|
78
|
+
* Writes files, stages everything, and commits — the common fixture beat.
|
|
79
|
+
*
|
|
80
|
+
* Args:
|
|
81
|
+
* files: file-tree spec to write before staging.
|
|
82
|
+
* message: commit message.
|
|
83
|
+
* Returns:
|
|
84
|
+
* string: the resulting HEAD sha.
|
|
85
|
+
*/
|
|
86
|
+
commitFiles(files: Record<string, string>, message: string): string;
|
|
87
|
+
/**
|
|
88
|
+
* Reads the current HEAD sha.
|
|
89
|
+
*
|
|
90
|
+
* Returns:
|
|
91
|
+
* string | null: 40-char hex sha, or null while the branch is unborn.
|
|
92
|
+
*/
|
|
93
|
+
headSha(): string | null;
|
|
94
|
+
/**
|
|
95
|
+
* Lists currently staged (index vs HEAD) files — what the real
|
|
96
|
+
* `local-staged` changed-file provider would report (pin #5).
|
|
97
|
+
*
|
|
98
|
+
* Returns:
|
|
99
|
+
* string[]: repo-root-relative posix paths (possibly empty).
|
|
100
|
+
*/
|
|
101
|
+
stagedFiles(): string[];
|
|
102
|
+
/**
|
|
103
|
+
* Resolves a repo-root-relative posix path to an absolute path.
|
|
104
|
+
*
|
|
105
|
+
* Args:
|
|
106
|
+
* relativePath: posix path relative to the repository root.
|
|
107
|
+
*/
|
|
108
|
+
path(relativePath: string): string;
|
|
109
|
+
/**
|
|
110
|
+
* Removes the repository. Idempotent; safe to call more than once.
|
|
111
|
+
*/
|
|
112
|
+
cleanup(): void;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Runs a fixture body against a fresh {@link TempRepo}, cleaning up on
|
|
116
|
+
* every path (sync throw, async rejection, success).
|
|
117
|
+
*
|
|
118
|
+
* Args:
|
|
119
|
+
* options: TempRepo construction options.
|
|
120
|
+
* body: receives the repo; may be async.
|
|
121
|
+
*
|
|
122
|
+
* Returns:
|
|
123
|
+
* The body's return value (awaited when async).
|
|
124
|
+
*/
|
|
125
|
+
export declare function withTempRepo<T>(options: TempRepoOptions, body: (repo: TempRepo) => T): T | Promise<T>;
|
|
126
|
+
//# sourceMappingURL=temp-repo.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"temp-repo.d.ts","sourceRoot":"","sources":["../../src/testing/temp-repo.ts"],"names":[],"mappings":"AAeA;;;;GAIG;AACH,eAAO,MAAM,cAAc,6BAA6B,CAAC;AAiBzD,oCAAoC;AACpC,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,iDAAiD;AACjD,MAAM,WAAW,eAAe;IAC9B,oFAAoF;IACpF,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,2CAA2C;IAC3C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,mDAAmD;IACnD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;GASG;AACH,qBAAa,QAAQ;IACnB,uDAAuD;IACvD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAoB;gBAE/B,OAAO,GAAE,eAAoB;IAgBzC;;;;;;;;;;OAUG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,YAAoB,EAAE,GAAE;QAAE,YAAY,CAAC,EAAE,OAAO,CAAA;KAAO,GAAG,SAAS;IAoBzF;;;;;;;;OAQG;IACH,UAAU,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI;IAY/C;;;;;OAKG;IACH,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI;IAI7B;;;;;;;OAOG;IACH,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAO/B;;;;;;;;OAQG;IACH,WAAW,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM;IAMnE;;;;;OAKG;IACH,OAAO,IAAI,MAAM,GAAG,IAAI;IAKxB;;;;;;OAMG;IACH,WAAW,IAAI,MAAM,EAAE;IAKvB;;;;;OAKG;IACH,IAAI,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM;IAIlC;;OAEG;IACH,OAAO,IAAI,IAAI;CAGhB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAC5B,OAAO,EAAE,eAAe,EACxB,IAAI,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,CAAC,GAC1B,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAchB"}
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Temporary git repository builder (fixture harness, G7).
|
|
3
|
+
*
|
|
4
|
+
* Creates throwaway git repositories under the OS tmpdir for adversarial
|
|
5
|
+
* fixtures (GF-01..GF-24). Everything runs offline through `child_process`
|
|
6
|
+
* git: no network, no wall-clock-dependent output, no reliance on the
|
|
7
|
+
* developer's git configuration. Author/committer identity and both dates
|
|
8
|
+
* are pinned to fixed values, so identical file-tree specs produce
|
|
9
|
+
* byte-identical commit SHAs across machines — fixtures may assert on SHAs.
|
|
10
|
+
*/
|
|
11
|
+
import { spawnSync } from 'node:child_process';
|
|
12
|
+
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
|
|
13
|
+
import { tmpdir } from 'node:os';
|
|
14
|
+
import { dirname, join } from 'node:path';
|
|
15
|
+
/**
|
|
16
|
+
* The fixed timestamp (git raw-date form) stamped onto every commit as
|
|
17
|
+
* both author and committer date. Determinism cornerstone: change it and
|
|
18
|
+
* every fixture SHA changes.
|
|
19
|
+
*/
|
|
20
|
+
export const FIXED_GIT_DATE = '2026-01-01T00:00:00+0000';
|
|
21
|
+
/** Fixed author/committer identity for fixture commits. */
|
|
22
|
+
const FIXED_IDENTITY = {
|
|
23
|
+
GIT_AUTHOR_NAME: 'gateforge fixtures',
|
|
24
|
+
GIT_AUTHOR_EMAIL: 'fixtures@gateforge.invalid',
|
|
25
|
+
GIT_COMMITTER_NAME: 'gateforge fixtures',
|
|
26
|
+
GIT_COMMITTER_EMAIL: 'fixtures@gateforge.invalid',
|
|
27
|
+
};
|
|
28
|
+
/** Flags stripping every source of nondeterminism from the git invocation. */
|
|
29
|
+
const FIXED_FLAGS = [
|
|
30
|
+
'-c', 'commit.gpgsign=false',
|
|
31
|
+
'-c', 'core.autocrlf=false',
|
|
32
|
+
'-c', 'gc.auto=0',
|
|
33
|
+
];
|
|
34
|
+
/**
|
|
35
|
+
* A disposable git repository under the OS tmpdir.
|
|
36
|
+
*
|
|
37
|
+
* Args:
|
|
38
|
+
* options: optional initial file tree, branch name, and tmpdir prefix.
|
|
39
|
+
*
|
|
40
|
+
* Returns:
|
|
41
|
+
* TempRepo: use `writeFiles`/`stage`/`commit` to build history,
|
|
42
|
+
* `cleanup` (or {@link withTempRepo}) to remove it.
|
|
43
|
+
*/
|
|
44
|
+
export class TempRepo {
|
|
45
|
+
/** Absolute filesystem path of the repository root. */
|
|
46
|
+
root;
|
|
47
|
+
gitEnv;
|
|
48
|
+
constructor(options = {}) {
|
|
49
|
+
this.root = mkdtempSync(join(tmpdir(), options.prefix ?? 'gateforge-fixture-'));
|
|
50
|
+
// GIT_CONFIG_GLOBAL=/dev/null + GIT_CONFIG_NOSYSTEM=1 insulate fixtures
|
|
51
|
+
// from user/system git config (hooks, signing, identity defaults).
|
|
52
|
+
this.gitEnv = {
|
|
53
|
+
...process.env,
|
|
54
|
+
...FIXED_IDENTITY,
|
|
55
|
+
GIT_AUTHOR_DATE: FIXED_GIT_DATE,
|
|
56
|
+
GIT_COMMITTER_DATE: FIXED_GIT_DATE,
|
|
57
|
+
GIT_CONFIG_GLOBAL: '/dev/null',
|
|
58
|
+
GIT_CONFIG_NOSYSTEM: '1',
|
|
59
|
+
};
|
|
60
|
+
this.git(['init', '--initial-branch', options.branch ?? 'main', '--quiet']);
|
|
61
|
+
if (options.files !== undefined)
|
|
62
|
+
this.writeFiles(options.files);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Runs one git command in the repository.
|
|
66
|
+
*
|
|
67
|
+
* Args:
|
|
68
|
+
* args: argument vector (without the `git` binary).
|
|
69
|
+
* allowFailure: when true a non-zero exit does not throw.
|
|
70
|
+
*
|
|
71
|
+
* Returns:
|
|
72
|
+
* GitResult: exit status plus captured stdout/stderr.
|
|
73
|
+
* @throws Error naming the failing command when `allowFailure` is false.
|
|
74
|
+
*/
|
|
75
|
+
git(args, { allowFailure = false } = {}) {
|
|
76
|
+
const result = spawnSync('git', [...FIXED_FLAGS, ...args], {
|
|
77
|
+
cwd: this.root,
|
|
78
|
+
env: this.gitEnv,
|
|
79
|
+
encoding: 'utf8',
|
|
80
|
+
maxBuffer: 16 * 1024 * 1024,
|
|
81
|
+
});
|
|
82
|
+
if (!allowFailure && result.status !== 0) {
|
|
83
|
+
throw new Error(`git ${args.join(' ')} failed (exit ${result.status}) in ${this.root}\n` +
|
|
84
|
+
`stdout: ${result.stdout}\nstderr: ${result.stderr}`);
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
status: result.status ?? -1,
|
|
88
|
+
stdout: result.stdout ?? '',
|
|
89
|
+
stderr: result.stderr ?? '',
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Writes a file-tree spec into the repository. Keys are
|
|
94
|
+
* repo-root-relative posix paths; parent directories are created.
|
|
95
|
+
*
|
|
96
|
+
* Args:
|
|
97
|
+
* files: mapping of relative posix path to UTF-8 file content.
|
|
98
|
+
* @throws Error on absolute paths or `..` segments (spec writers must
|
|
99
|
+
* stay inside the repo).
|
|
100
|
+
*/
|
|
101
|
+
writeFiles(files) {
|
|
102
|
+
for (const [rawKey, content] of Object.entries(files)) {
|
|
103
|
+
const key = rawKey.split('\\').join('/');
|
|
104
|
+
if (key.startsWith('/') || key.split('/').includes('..')) {
|
|
105
|
+
throw new Error(`file-tree spec key escapes the repo root: '${rawKey}'`);
|
|
106
|
+
}
|
|
107
|
+
const absolute = join(this.root, ...key.split('/'));
|
|
108
|
+
mkdirSync(dirname(absolute), { recursive: true });
|
|
109
|
+
writeFileSync(absolute, content, 'utf8');
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Stages files. With no argument stages every change (`git add -A`).
|
|
114
|
+
*
|
|
115
|
+
* Args:
|
|
116
|
+
* paths: repo-root-relative paths to stage.
|
|
117
|
+
*/
|
|
118
|
+
stage(paths) {
|
|
119
|
+
this.git(['add', ...(paths ?? ['-A'])]);
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Commits the staged tree with a fixed identity and date.
|
|
123
|
+
*
|
|
124
|
+
* Args:
|
|
125
|
+
* message: commit message.
|
|
126
|
+
* Returns:
|
|
127
|
+
* string: the resulting HEAD sha (40-char hex).
|
|
128
|
+
*/
|
|
129
|
+
commit(message) {
|
|
130
|
+
this.git(['commit', '--no-gpg-sign', '--allow-empty', '--quiet', '-m', message]);
|
|
131
|
+
const sha = this.headSha();
|
|
132
|
+
if (sha === null)
|
|
133
|
+
throw new Error('commit succeeded but HEAD is unborn');
|
|
134
|
+
return sha;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Writes files, stages everything, and commits — the common fixture beat.
|
|
138
|
+
*
|
|
139
|
+
* Args:
|
|
140
|
+
* files: file-tree spec to write before staging.
|
|
141
|
+
* message: commit message.
|
|
142
|
+
* Returns:
|
|
143
|
+
* string: the resulting HEAD sha.
|
|
144
|
+
*/
|
|
145
|
+
commitFiles(files, message) {
|
|
146
|
+
this.writeFiles(files);
|
|
147
|
+
this.stage();
|
|
148
|
+
return this.commit(message);
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Reads the current HEAD sha.
|
|
152
|
+
*
|
|
153
|
+
* Returns:
|
|
154
|
+
* string | null: 40-char hex sha, or null while the branch is unborn.
|
|
155
|
+
*/
|
|
156
|
+
headSha() {
|
|
157
|
+
const result = this.git(['rev-parse', 'HEAD'], { allowFailure: true });
|
|
158
|
+
return result.status === 0 ? result.stdout.trim() : null;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Lists currently staged (index vs HEAD) files — what the real
|
|
162
|
+
* `local-staged` changed-file provider would report (pin #5).
|
|
163
|
+
*
|
|
164
|
+
* Returns:
|
|
165
|
+
* string[]: repo-root-relative posix paths (possibly empty).
|
|
166
|
+
*/
|
|
167
|
+
stagedFiles() {
|
|
168
|
+
const result = this.git(['diff', '--cached', '--name-only']);
|
|
169
|
+
return result.stdout.split('\n').map((line) => line.trim()).filter((line) => line.length > 0);
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Resolves a repo-root-relative posix path to an absolute path.
|
|
173
|
+
*
|
|
174
|
+
* Args:
|
|
175
|
+
* relativePath: posix path relative to the repository root.
|
|
176
|
+
*/
|
|
177
|
+
path(relativePath) {
|
|
178
|
+
return join(this.root, ...relativePath.split('/'));
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Removes the repository. Idempotent; safe to call more than once.
|
|
182
|
+
*/
|
|
183
|
+
cleanup() {
|
|
184
|
+
rmSync(this.root, { recursive: true, force: true });
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Runs a fixture body against a fresh {@link TempRepo}, cleaning up on
|
|
189
|
+
* every path (sync throw, async rejection, success).
|
|
190
|
+
*
|
|
191
|
+
* Args:
|
|
192
|
+
* options: TempRepo construction options.
|
|
193
|
+
* body: receives the repo; may be async.
|
|
194
|
+
*
|
|
195
|
+
* Returns:
|
|
196
|
+
* The body's return value (awaited when async).
|
|
197
|
+
*/
|
|
198
|
+
export function withTempRepo(options, body) {
|
|
199
|
+
const repo = new TempRepo(options);
|
|
200
|
+
let result;
|
|
201
|
+
try {
|
|
202
|
+
result = body(repo);
|
|
203
|
+
}
|
|
204
|
+
catch (error) {
|
|
205
|
+
repo.cleanup();
|
|
206
|
+
throw error;
|
|
207
|
+
}
|
|
208
|
+
if (result instanceof Promise) {
|
|
209
|
+
return result.finally(() => repo.cleanup());
|
|
210
|
+
}
|
|
211
|
+
repo.cleanup();
|
|
212
|
+
return result;
|
|
213
|
+
}
|
|
214
|
+
//# sourceMappingURL=temp-repo.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"temp-repo.js","sourceRoot":"","sources":["../../src/testing/temp-repo.ts"],"names":[],"mappings":"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"}
|