@c9up/helix 0.1.3 → 0.1.5
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/dist/cli/coverage/aggregate.d.ts +24 -0
- package/dist/cli/coverage/aggregate.d.ts.map +1 -0
- package/dist/cli/coverage/aggregate.js +215 -0
- package/dist/cli/coverage/aggregate.js.map +1 -0
- package/dist/cli/coverage/collect.d.ts +13 -0
- package/dist/cli/coverage/collect.d.ts.map +1 -0
- package/dist/cli/coverage/collect.js +66 -0
- package/dist/cli/coverage/collect.js.map +1 -0
- package/dist/cli/coverage/diff/base.d.ts +17 -0
- package/dist/cli/coverage/diff/base.d.ts.map +1 -0
- package/dist/cli/coverage/diff/base.js +44 -0
- package/dist/cli/coverage/diff/base.js.map +1 -0
- package/dist/cli/coverage/diff/index.d.ts +25 -0
- package/dist/cli/coverage/diff/index.d.ts.map +1 -0
- package/dist/cli/coverage/diff/index.js +119 -0
- package/dist/cli/coverage/diff/index.js.map +1 -0
- package/dist/cli/coverage/diff/overlay.d.ts +19 -0
- package/dist/cli/coverage/diff/overlay.d.ts.map +1 -0
- package/dist/cli/coverage/diff/overlay.js +58 -0
- package/dist/cli/coverage/diff/overlay.js.map +1 -0
- package/dist/cli/coverage/diff/parse.d.ts +27 -0
- package/dist/cli/coverage/diff/parse.d.ts.map +1 -0
- package/dist/cli/coverage/diff/parse.js +105 -0
- package/dist/cli/coverage/diff/parse.js.map +1 -0
- package/dist/cli/coverage/diff/reporters.d.ts +12 -0
- package/dist/cli/coverage/diff/reporters.d.ts.map +1 -0
- package/dist/cli/coverage/diff/reporters.js +59 -0
- package/dist/cli/coverage/diff/reporters.js.map +1 -0
- package/dist/cli/coverage/diff/types.d.ts +46 -0
- package/dist/cli/coverage/diff/types.d.ts.map +1 -0
- package/dist/cli/coverage/diff/types.js +15 -0
- package/dist/cli/coverage/diff/types.js.map +1 -0
- package/dist/cli/coverage/filter.d.ts +13 -0
- package/dist/cli/coverage/filter.d.ts.map +1 -0
- package/dist/cli/coverage/filter.js +60 -0
- package/dist/cli/coverage/filter.js.map +1 -0
- package/dist/cli/coverage/glob.d.ts +4 -0
- package/dist/cli/coverage/glob.d.ts.map +1 -0
- package/dist/cli/coverage/glob.js +0 -0
- package/dist/cli/coverage/glob.js.map +1 -0
- package/dist/cli/coverage/index.d.ts +40 -0
- package/dist/cli/coverage/index.d.ts.map +1 -0
- package/dist/cli/coverage/index.js +85 -0
- package/dist/cli/coverage/index.js.map +1 -0
- package/dist/cli/coverage/reporters/json.d.ts +13 -0
- package/dist/cli/coverage/reporters/json.d.ts.map +1 -0
- package/dist/cli/coverage/reporters/json.js +37 -0
- package/dist/cli/coverage/reporters/json.js.map +1 -0
- package/dist/cli/coverage/reporters/lcov.d.ts +13 -0
- package/dist/cli/coverage/reporters/lcov.d.ts.map +1 -0
- package/dist/cli/coverage/reporters/lcov.js +51 -0
- package/dist/cli/coverage/reporters/lcov.js.map +1 -0
- package/dist/cli/coverage/reporters/text.d.ts +8 -0
- package/dist/cli/coverage/reporters/text.d.ts.map +1 -0
- package/dist/cli/coverage/reporters/text.js +39 -0
- package/dist/cli/coverage/reporters/text.js.map +1 -0
- package/dist/cli/coverage/thresholds.d.ts +14 -0
- package/dist/cli/coverage/thresholds.d.ts.map +1 -0
- package/dist/cli/coverage/thresholds.js +58 -0
- package/dist/cli/coverage/thresholds.js.map +1 -0
- package/dist/cli/coverage/types.d.ts +118 -0
- package/dist/cli/coverage/types.d.ts.map +1 -0
- package/dist/cli/coverage/types.js +8 -0
- package/dist/cli/coverage/types.js.map +1 -0
- package/dist/cli/discover.d.ts +27 -0
- package/dist/cli/discover.d.ts.map +1 -0
- package/dist/cli/discover.js +142 -0
- package/dist/cli/discover.js.map +1 -0
- package/dist/cli/native.d.ts +43 -0
- package/dist/cli/native.d.ts.map +1 -0
- package/dist/cli/native.js +64 -0
- package/dist/cli/native.js.map +1 -0
- package/dist/cli/pool.d.ts +55 -0
- package/dist/cli/pool.d.ts.map +1 -0
- package/dist/cli/pool.js +404 -0
- package/dist/cli/pool.js.map +1 -0
- package/dist/cli/reporter.d.ts +51 -0
- package/dist/cli/reporter.d.ts.map +1 -0
- package/dist/cli/reporter.js +124 -0
- package/dist/cli/reporter.js.map +1 -0
- package/dist/cli/run.d.ts +61 -0
- package/dist/cli/run.d.ts.map +1 -0
- package/dist/cli/run.js +302 -0
- package/dist/cli/run.js.map +1 -0
- package/dist/cli/summary.d.ts +18 -0
- package/dist/cli/summary.d.ts.map +1 -0
- package/dist/cli/summary.js +20 -0
- package/dist/cli/summary.js.map +1 -0
- package/dist/cli/watch/loop.d.ts +26 -0
- package/dist/cli/watch/loop.d.ts.map +1 -0
- package/dist/cli/watch/loop.js +133 -0
- package/dist/cli/watch/loop.js.map +1 -0
- package/dist/cli/watch/types.d.ts +22 -0
- package/dist/cli/watch/types.d.ts.map +1 -0
- package/dist/cli/watch/types.js +6 -0
- package/dist/cli/watch/types.js.map +1 -0
- package/dist/cli/watch/watcher.d.ts +25 -0
- package/dist/cli/watch/watcher.d.ts.map +1 -0
- package/dist/cli/watch/watcher.js +122 -0
- package/dist/cli/watch/watcher.js.map +1 -0
- package/dist/container/index.d.ts +7 -0
- package/dist/container/index.d.ts.map +1 -0
- package/dist/container/index.js +6 -0
- package/dist/container/index.js.map +1 -0
- package/{src/container/override.ts → dist/container/override.d.ts} +6 -46
- package/dist/container/override.d.ts.map +1 -0
- package/dist/container/override.js +67 -0
- package/dist/container/override.js.map +1 -0
- package/{src/container/spy.ts → dist/container/spy.d.ts} +2 -6
- package/dist/container/spy.d.ts.map +1 -0
- package/dist/container/spy.js +23 -0
- package/dist/container/spy.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/{src/index.ts → dist/index.js} +2 -25
- package/dist/index.js.map +1 -0
- package/dist/runtime/assertion-error.d.ts +20 -0
- package/dist/runtime/assertion-error.d.ts.map +1 -0
- package/dist/runtime/assertion-error.js +23 -0
- package/dist/runtime/assertion-error.js.map +1 -0
- package/dist/runtime/cli-worker.d.ts +25 -0
- package/dist/runtime/cli-worker.d.ts.map +1 -0
- package/dist/runtime/cli-worker.js +128 -0
- package/dist/runtime/cli-worker.js.map +1 -0
- package/dist/runtime/equals.d.ts +25 -0
- package/dist/runtime/equals.d.ts.map +1 -0
- package/dist/runtime/equals.js +343 -0
- package/dist/runtime/equals.js.map +1 -0
- package/dist/runtime/expect.d.ts +32 -0
- package/dist/runtime/expect.d.ts.map +1 -0
- package/dist/runtime/expect.js +121 -0
- package/dist/runtime/expect.js.map +1 -0
- package/{src/runtime/index.ts → dist/runtime/index.d.ts} +5 -27
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +17 -0
- package/dist/runtime/index.js.map +1 -0
- package/{src/runtime/lifecycle.ts → dist/runtime/lifecycle.d.ts} +2 -8
- package/dist/runtime/lifecycle.d.ts.map +1 -0
- package/dist/runtime/lifecycle.js +10 -0
- package/dist/runtime/lifecycle.js.map +1 -0
- package/dist/runtime/matchers.d.ts +42 -0
- package/dist/runtime/matchers.d.ts.map +1 -0
- package/dist/runtime/matchers.js +375 -0
- package/dist/runtime/matchers.js.map +1 -0
- package/dist/runtime/run.d.ts +55 -0
- package/dist/runtime/run.d.ts.map +1 -0
- package/dist/runtime/run.js +456 -0
- package/dist/runtime/run.js.map +1 -0
- package/dist/runtime/suite.d.ts +91 -0
- package/dist/runtime/suite.d.ts.map +1 -0
- package/dist/runtime/suite.js +206 -0
- package/dist/runtime/suite.js.map +1 -0
- package/dist/runtime/test-context.d.ts +25 -0
- package/dist/runtime/test-context.d.ts.map +1 -0
- package/dist/runtime/test-context.js +52 -0
- package/dist/runtime/test-context.js.map +1 -0
- package/dist/runtime/vi/fake-timers.d.ts +48 -0
- package/dist/runtime/vi/fake-timers.d.ts.map +1 -0
- package/dist/runtime/vi/fake-timers.js +317 -0
- package/dist/runtime/vi/fake-timers.js.map +1 -0
- package/dist/runtime/vi/index.d.ts +70 -0
- package/dist/runtime/vi/index.d.ts.map +1 -0
- package/dist/runtime/vi/index.js +209 -0
- package/dist/runtime/vi/index.js.map +1 -0
- package/dist/runtime/vi/spy.d.ts +54 -0
- package/dist/runtime/vi/spy.d.ts.map +1 -0
- package/dist/runtime/vi/spy.js +159 -0
- package/dist/runtime/vi/spy.js.map +1 -0
- package/dist/runtime/vi/spyOn.d.ts +28 -0
- package/dist/runtime/vi/spyOn.d.ts.map +1 -0
- package/dist/runtime/vi/spyOn.js +121 -0
- package/dist/runtime/vi/spyOn.js.map +1 -0
- package/dist/runtime/vi/system-time.d.ts +30 -0
- package/dist/runtime/vi/system-time.d.ts.map +1 -0
- package/dist/runtime/vi/system-time.js +100 -0
- package/dist/runtime/vi/system-time.js.map +1 -0
- package/dist/runtime/worker.d.ts +26 -0
- package/dist/runtime/worker.d.ts.map +1 -0
- package/dist/runtime/worker.js +195 -0
- package/dist/runtime/worker.js.map +1 -0
- package/dist/time/freeze.d.ts +80 -0
- package/dist/time/freeze.d.ts.map +1 -0
- package/dist/time/freeze.js +184 -0
- package/dist/time/freeze.js.map +1 -0
- package/{src/time/index.ts → dist/time/index.d.ts} +1 -1
- package/dist/time/index.d.ts.map +1 -0
- package/dist/time/index.js +15 -0
- package/dist/time/index.js.map +1 -0
- package/index.darwin-arm64.node +0 -0
- package/index.darwin-x64.node +0 -0
- package/index.linux-arm64-gnu.node +0 -0
- package/index.linux-x64-gnu.node +0 -0
- package/index.win32-x64-msvc.node +0 -0
- package/package.json +2 -2
- package/src/cli/coverage/aggregate.ts +0 -231
- package/src/cli/coverage/collect.ts +0 -63
- package/src/cli/coverage/diff/base.ts +0 -46
- package/src/cli/coverage/diff/index.ts +0 -160
- package/src/cli/coverage/diff/overlay.ts +0 -62
- package/src/cli/coverage/diff/parse.ts +0 -121
- package/src/cli/coverage/diff/reporters.ts +0 -82
- package/src/cli/coverage/diff/types.ts +0 -46
- package/src/cli/coverage/filter.ts +0 -71
- package/src/cli/coverage/glob.ts +0 -0
- package/src/cli/coverage/index.ts +0 -126
- package/src/cli/coverage/reporters/json.ts +0 -40
- package/src/cli/coverage/reporters/lcov.ts +0 -54
- package/src/cli/coverage/reporters/text.ts +0 -48
- package/src/cli/coverage/thresholds.ts +0 -73
- package/src/cli/coverage/types.ts +0 -93
- package/src/cli/discover.ts +0 -174
- package/src/cli/native.ts +0 -104
- package/src/cli/pool.ts +0 -486
- package/src/cli/reporter.ts +0 -155
- package/src/cli/run.ts +0 -440
- package/src/cli/summary.ts +0 -42
- package/src/cli/watch/loop.ts +0 -159
- package/src/cli/watch/types.ts +0 -22
- package/src/cli/watch/watcher.ts +0 -145
- package/src/container/index.ts +0 -16
- package/src/runtime/assertion-error.ts +0 -38
- package/src/runtime/cli-worker.ts +0 -140
- package/src/runtime/equals.ts +0 -400
- package/src/runtime/expect.ts +0 -173
- package/src/runtime/matchers.ts +0 -452
- package/src/runtime/run.ts +0 -573
- package/src/runtime/suite.ts +0 -310
- package/src/runtime/test-context.ts +0 -59
- package/src/runtime/vi/fake-timers.ts +0 -410
- package/src/runtime/vi/index.ts +0 -254
- package/src/runtime/vi/spy.ts +0 -224
- package/src/runtime/vi/spyOn.ts +0 -155
- package/src/runtime/vi/system-time.ts +0 -121
- package/src/runtime/worker.ts +0 -239
- package/src/time/freeze.ts +0 -229
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Watch-mode run loop: runs `runOnce` once on startup, installs the
|
|
3
|
+
* watcher, and re-invokes `runOnce` whenever a coalesced change burst
|
|
4
|
+
* fires. Re-runs are serialised — at most one queued follow-up while a
|
|
5
|
+
* run is in flight; further bursts during that window are dropped (the
|
|
6
|
+
* queued run will pick up the latest disk state).
|
|
7
|
+
*
|
|
8
|
+
* On SIGINT (or an aborted external `AbortSignal`), the loop closes
|
|
9
|
+
* the watcher, waits for any in-flight run, and resolves with the
|
|
10
|
+
* LAST `RunOutcome` (with `exitCode: 0`, since a failing run during
|
|
11
|
+
* interactive watch should not poison the exit status — failures
|
|
12
|
+
* already surface as test output).
|
|
13
|
+
*/
|
|
14
|
+
import { createWatcher } from "./watcher.js";
|
|
15
|
+
const BANNER_WAITING = "[helix] watching for changes… (Ctrl-C to exit)";
|
|
16
|
+
const BANNER_RERUN = "[helix] change detected, re-running…";
|
|
17
|
+
function fallbackOutcome() {
|
|
18
|
+
return {
|
|
19
|
+
summary: {
|
|
20
|
+
totals: { pass: 0, fail: 0, skip: 0, todo: 0, fileErrors: 0 },
|
|
21
|
+
files: [],
|
|
22
|
+
fileErrors: [],
|
|
23
|
+
durationMs: 0,
|
|
24
|
+
},
|
|
25
|
+
exitCode: 0,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export async function runWatch(opts, runOnce) {
|
|
29
|
+
// Initialise upfront so the final return never reads `undefined`,
|
|
30
|
+
// even if `runOnce` throws and the catch path's fallback construction
|
|
31
|
+
// itself throws (defence-in-depth against a brittle cast).
|
|
32
|
+
let lastOutcome = fallbackOutcome();
|
|
33
|
+
let inFlight;
|
|
34
|
+
let queued = false;
|
|
35
|
+
let closing = false;
|
|
36
|
+
let watcher;
|
|
37
|
+
let resolveSettled;
|
|
38
|
+
const settled = new Promise((resolve) => {
|
|
39
|
+
resolveSettled = resolve;
|
|
40
|
+
});
|
|
41
|
+
function beginShutdown() {
|
|
42
|
+
if (closing)
|
|
43
|
+
return;
|
|
44
|
+
closing = true;
|
|
45
|
+
void (async () => {
|
|
46
|
+
if (watcher)
|
|
47
|
+
await watcher.close();
|
|
48
|
+
if (inFlight)
|
|
49
|
+
await inFlight;
|
|
50
|
+
resolveSettled();
|
|
51
|
+
})();
|
|
52
|
+
}
|
|
53
|
+
const onSigint = () => beginShutdown();
|
|
54
|
+
const onAbort = () => beginShutdown();
|
|
55
|
+
process.on("SIGINT", onSigint);
|
|
56
|
+
if (opts.signal) {
|
|
57
|
+
if (opts.signal.aborted) {
|
|
58
|
+
closing = true;
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
opts.signal.addEventListener("abort", onAbort, { once: true });
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function trigger() {
|
|
65
|
+
if (closing)
|
|
66
|
+
return;
|
|
67
|
+
if (inFlight) {
|
|
68
|
+
queued = true;
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
process.stdout.write(`${BANNER_RERUN}\n`);
|
|
72
|
+
inFlight = (async () => {
|
|
73
|
+
try {
|
|
74
|
+
lastOutcome = await runOnce();
|
|
75
|
+
}
|
|
76
|
+
catch (err) {
|
|
77
|
+
process.stderr.write(`helix-watch: run failed: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
78
|
+
}
|
|
79
|
+
finally {
|
|
80
|
+
inFlight = undefined;
|
|
81
|
+
if (!closing) {
|
|
82
|
+
process.stdout.write(`${BANNER_WAITING}\n`);
|
|
83
|
+
}
|
|
84
|
+
if (queued && !closing) {
|
|
85
|
+
queued = false;
|
|
86
|
+
trigger();
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
})();
|
|
90
|
+
}
|
|
91
|
+
// Initial run — drive it through the same in-flight machinery so a
|
|
92
|
+
// SIGINT/abort arriving during the first run is handled exactly like
|
|
93
|
+
// one arriving during a re-run (handler awaits `inFlight`, then
|
|
94
|
+
// resolves).
|
|
95
|
+
inFlight = (async () => {
|
|
96
|
+
try {
|
|
97
|
+
lastOutcome = await runOnce();
|
|
98
|
+
}
|
|
99
|
+
catch (err) {
|
|
100
|
+
process.stderr.write(`helix-watch: run failed: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
101
|
+
}
|
|
102
|
+
finally {
|
|
103
|
+
inFlight = undefined;
|
|
104
|
+
}
|
|
105
|
+
})();
|
|
106
|
+
await inFlight;
|
|
107
|
+
if (closing) {
|
|
108
|
+
process.off("SIGINT", onSigint);
|
|
109
|
+
opts.signal?.removeEventListener("abort", onAbort);
|
|
110
|
+
return { ...lastOutcome, exitCode: 0 };
|
|
111
|
+
}
|
|
112
|
+
process.stdout.write(`${BANNER_WAITING}\n`);
|
|
113
|
+
let watcherFatalError;
|
|
114
|
+
watcher = createWatcher({
|
|
115
|
+
root: opts.root,
|
|
116
|
+
include: opts.include,
|
|
117
|
+
exclude: opts.exclude,
|
|
118
|
+
debounceMs: opts.debounceMs,
|
|
119
|
+
onChange: () => trigger(),
|
|
120
|
+
onError: (err) => {
|
|
121
|
+
watcherFatalError = err;
|
|
122
|
+
beginShutdown();
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
await settled;
|
|
126
|
+
process.off("SIGINT", onSigint);
|
|
127
|
+
opts.signal?.removeEventListener("abort", onAbort);
|
|
128
|
+
if (watcherFatalError) {
|
|
129
|
+
process.stderr.write(`helix-watch: watcher error — ${watcherFatalError.message}\n`);
|
|
130
|
+
}
|
|
131
|
+
return { ...lastOutcome, exitCode: 0 };
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=loop.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loop.js","sourceRoot":"","sources":["../../../src/cli/watch/loop.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Watch-mode public types. Kept in their own file so `bin/helix.js` and
|
|
3
|
+
* `run.ts` can import without pulling in the watcher implementation.
|
|
4
|
+
*/
|
|
5
|
+
export interface WatchOptions {
|
|
6
|
+
enabled: boolean;
|
|
7
|
+
/** Debounce window in ms — events within the window collapse to one
|
|
8
|
+
* re-run. Default 200, range [1, 5_000]. */
|
|
9
|
+
debounceMs?: number;
|
|
10
|
+
/** Override the watcher's include globs. Defaults to `coverage.include`
|
|
11
|
+
* when set, otherwise the watch defaults (`src/**`, `tests/**`,
|
|
12
|
+
* `test/**`). */
|
|
13
|
+
include?: string[];
|
|
14
|
+
/** Override the watcher's exclude globs. Defaults to `coverage.exclude`
|
|
15
|
+
* when set, otherwise the watch defaults (`node_modules/**`,
|
|
16
|
+
* `.helix-coverage/**`, etc.). */
|
|
17
|
+
exclude?: string[];
|
|
18
|
+
/** Optional `AbortSignal` to drive shutdown without a real SIGINT —
|
|
19
|
+
* primarily a test seam. */
|
|
20
|
+
signal?: AbortSignal;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/cli/watch/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,YAAY;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB;iDAC6C;IAC7C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;sBAEkB;IAClB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB;;uCAEmC;IACnC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB;iCAC6B;IAC7B,MAAM,CAAC,EAAE,WAAW,CAAC;CACrB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/cli/watch/types.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recursive file watcher for `helix test --watch`. Wraps Node's
|
|
3
|
+
* `fs.watch(root, { recursive: true })` with a glob filter (reusing the
|
|
4
|
+
* coverage glob translator) and a single-timer debounce that coalesces
|
|
5
|
+
* burst events into one `onChange` call.
|
|
6
|
+
*
|
|
7
|
+
* The shape (`createWatcher` returning a `{ close }` handle) is the
|
|
8
|
+
* abstraction a future Rust `notify`-NAPI implementation slots into
|
|
9
|
+
* without touching the loop.
|
|
10
|
+
*/
|
|
11
|
+
export interface CreateWatcherOptions {
|
|
12
|
+
root: string;
|
|
13
|
+
include: string[];
|
|
14
|
+
exclude: string[];
|
|
15
|
+
debounceMs: number;
|
|
16
|
+
onChange: (paths: Set<string>) => void;
|
|
17
|
+
/** Surfaces `FSWatcher` errors (recursive unsupported, EMFILE, etc.)
|
|
18
|
+
* so the loop can stop instead of waiting forever on a dead handle. */
|
|
19
|
+
onError?: (err: Error) => void;
|
|
20
|
+
}
|
|
21
|
+
export interface WatcherHandle {
|
|
22
|
+
close(): Promise<void>;
|
|
23
|
+
}
|
|
24
|
+
export declare function createWatcher(opts: CreateWatcherOptions): WatcherHandle;
|
|
25
|
+
//# sourceMappingURL=watcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watcher.d.ts","sourceRoot":"","sources":["../../../src/cli/watch/watcher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH,MAAM,WAAW,oBAAoB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC;IACvC;4EACwE;IACxE,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,IAAI,CAAC;CAC/B;AAED,MAAM,WAAW,aAAa;IAC7B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AAaD,wBAAgB,aAAa,CAAC,IAAI,EAAE,oBAAoB,GAAG,aAAa,CAuGvE"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recursive file watcher for `helix test --watch`. Wraps Node's
|
|
3
|
+
* `fs.watch(root, { recursive: true })` with a glob filter (reusing the
|
|
4
|
+
* coverage glob translator) and a single-timer debounce that coalesces
|
|
5
|
+
* burst events into one `onChange` call.
|
|
6
|
+
*
|
|
7
|
+
* The shape (`createWatcher` returning a `{ close }` handle) is the
|
|
8
|
+
* abstraction a future Rust `notify`-NAPI implementation slots into
|
|
9
|
+
* without touching the loop.
|
|
10
|
+
*/
|
|
11
|
+
import { watch as fsWatch, realpathSync } from "node:fs";
|
|
12
|
+
import path from "node:path";
|
|
13
|
+
import { compileGlobs, matchesAnyGlob } from "../coverage/glob.js";
|
|
14
|
+
const DEBOUNCE_MIN = 1;
|
|
15
|
+
const DEBOUNCE_MAX = 5_000;
|
|
16
|
+
function tryRealpath(p) {
|
|
17
|
+
try {
|
|
18
|
+
return realpathSync(p);
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return p;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export function createWatcher(opts) {
|
|
25
|
+
if (opts.debounceMs < DEBOUNCE_MIN ||
|
|
26
|
+
opts.debounceMs > DEBOUNCE_MAX ||
|
|
27
|
+
!Number.isFinite(opts.debounceMs)) {
|
|
28
|
+
throw new Error(`createWatcher: debounceMs must be in [${DEBOUNCE_MIN}, ${DEBOUNCE_MAX}], got ${opts.debounceMs}`);
|
|
29
|
+
}
|
|
30
|
+
if (opts.include.length === 0) {
|
|
31
|
+
throw new Error("createWatcher: include must be non-empty (an empty include matches nothing — silent watcher).");
|
|
32
|
+
}
|
|
33
|
+
const include = compileGlobs(opts.include);
|
|
34
|
+
const exclude = compileGlobs(opts.exclude);
|
|
35
|
+
// Realpath the root so events delivered with the canonical path
|
|
36
|
+
// (macOS `/tmp` → `/private/tmp`, Docker bind-mounts, pnpm worktree
|
|
37
|
+
// symlinks) don't get filtered out as "outside root". Mirrors what
|
|
38
|
+
// `coverage/filter.ts` does for the same reason.
|
|
39
|
+
const root = tryRealpath(path.resolve(opts.root));
|
|
40
|
+
const pending = new Set();
|
|
41
|
+
let timer;
|
|
42
|
+
let closed = false;
|
|
43
|
+
function flush() {
|
|
44
|
+
timer = undefined;
|
|
45
|
+
if (closed)
|
|
46
|
+
return;
|
|
47
|
+
if (pending.size === 0)
|
|
48
|
+
return;
|
|
49
|
+
const batch = new Set(pending);
|
|
50
|
+
pending.clear();
|
|
51
|
+
opts.onChange(batch);
|
|
52
|
+
}
|
|
53
|
+
function arm() {
|
|
54
|
+
if (timer)
|
|
55
|
+
clearTimeout(timer);
|
|
56
|
+
timer = setTimeout(flush, opts.debounceMs);
|
|
57
|
+
}
|
|
58
|
+
function shouldNotify(filename) {
|
|
59
|
+
if (!filename)
|
|
60
|
+
return undefined;
|
|
61
|
+
const abs = path.resolve(root, filename);
|
|
62
|
+
const rel = path.relative(root, abs).split(path.sep).join("/");
|
|
63
|
+
if (rel.length === 0 || rel.startsWith("..") || path.isAbsolute(rel)) {
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
if (matchesAnyGlob(exclude, rel))
|
|
67
|
+
return undefined;
|
|
68
|
+
if (!matchesAnyGlob(include, rel))
|
|
69
|
+
return undefined;
|
|
70
|
+
return abs;
|
|
71
|
+
}
|
|
72
|
+
let watcher;
|
|
73
|
+
try {
|
|
74
|
+
watcher = fsWatch(root, { recursive: true }, (_event, filename) => {
|
|
75
|
+
if (closed)
|
|
76
|
+
return;
|
|
77
|
+
const abs = shouldNotify(typeof filename === "string" ? filename : null);
|
|
78
|
+
if (!abs)
|
|
79
|
+
return;
|
|
80
|
+
pending.add(abs);
|
|
81
|
+
arm();
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
catch (err) {
|
|
85
|
+
throw new Error(`createWatcher: fs.watch failed on ${root}: ${err instanceof Error ? err.message : String(err)}`);
|
|
86
|
+
}
|
|
87
|
+
// On `'error'`, surface the failure to the caller and shut down so
|
|
88
|
+
// the loop doesn't sit on a dead handle. Without this, recursive
|
|
89
|
+
// watch failures on Linux<20 leave the user staring at a "watching…"
|
|
90
|
+
// banner that will never tick.
|
|
91
|
+
watcher.on("error", (err) => {
|
|
92
|
+
if (closed)
|
|
93
|
+
return;
|
|
94
|
+
closed = true;
|
|
95
|
+
if (timer) {
|
|
96
|
+
clearTimeout(timer);
|
|
97
|
+
timer = undefined;
|
|
98
|
+
}
|
|
99
|
+
try {
|
|
100
|
+
watcher.close();
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
/* already torn down */
|
|
104
|
+
}
|
|
105
|
+
opts.onError?.(err instanceof Error ? err : new Error(String(err)));
|
|
106
|
+
});
|
|
107
|
+
return {
|
|
108
|
+
async close() {
|
|
109
|
+
if (closed)
|
|
110
|
+
return;
|
|
111
|
+
closed = true;
|
|
112
|
+
if (timer) {
|
|
113
|
+
clearTimeout(timer);
|
|
114
|
+
timer = undefined;
|
|
115
|
+
}
|
|
116
|
+
pending.clear();
|
|
117
|
+
watcher.close();
|
|
118
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=watcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watcher.js","sourceRoot":"","sources":["../../../src/cli/watch/watcher.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@c9up/helix/container` — barrel for the test-container facade.
|
|
3
|
+
*/
|
|
4
|
+
export type { ContainerLike, ContainerToken, } from "../runtime/vi/index.js";
|
|
5
|
+
export { clearActiveContainer, type HelixContainer, override, overrideOn, useContainer, } from "./override.js";
|
|
6
|
+
export { spy } from "./spy.js";
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/container/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,YAAY,EACX,aAAa,EACb,cAAc,GACd,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACN,oBAAoB,EACpB,KAAK,cAAc,EACnB,QAAQ,EACR,UAAU,EACV,YAAY,GACZ,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/container/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,OAAO,EACN,oBAAoB,EAEpB,QAAQ,EACR,UAAU,EACV,YAAY,GACZ,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC"}
|
|
@@ -12,17 +12,11 @@
|
|
|
12
12
|
* by the host app's test bootstrap). For bare unit tests with no host
|
|
13
13
|
* app, callers can pass the container explicitly to `overrideOn`.
|
|
14
14
|
*/
|
|
15
|
-
|
|
16
|
-
import { inTestContext, registerTestCleanup } from "../runtime/test-context.js";
|
|
17
15
|
import type { ContainerLike, ContainerToken } from "../runtime/vi/index.js";
|
|
18
|
-
|
|
19
16
|
/** Container surface helix needs: a value-based override + a restore. */
|
|
20
17
|
export interface HelixContainer extends ContainerLike {
|
|
21
|
-
|
|
18
|
+
override(token: ContainerToken, value: unknown): void;
|
|
22
19
|
}
|
|
23
|
-
|
|
24
|
-
let activeContainer: HelixContainer | undefined;
|
|
25
|
-
|
|
26
20
|
/**
|
|
27
21
|
* Bind a container instance as active for `helix.override(...)` calls.
|
|
28
22
|
*
|
|
@@ -32,22 +26,12 @@ let activeContainer: HelixContainer | undefined;
|
|
|
32
26
|
* frame the assignment is permanent until the next `useContainer` /
|
|
33
27
|
* `clearActiveContainer` call.
|
|
34
28
|
*/
|
|
35
|
-
export function useContainer(container: HelixContainer): void
|
|
36
|
-
const previous = activeContainer;
|
|
37
|
-
activeContainer = container;
|
|
38
|
-
registerTestCleanup(() => {
|
|
39
|
-
activeContainer = previous;
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
|
|
29
|
+
export declare function useContainer(container: HelixContainer): void;
|
|
43
30
|
/** Reset the active container binding. Typically not needed in user
|
|
44
31
|
* code — `useContainer` already auto-restores when called inside a
|
|
45
32
|
* test. Exposed so `afterEach` blocks (and unit tests) can clear the
|
|
46
33
|
* module-scoped slot explicitly. */
|
|
47
|
-
export function clearActiveContainer(): void
|
|
48
|
-
activeContainer = undefined;
|
|
49
|
-
}
|
|
50
|
-
|
|
34
|
+
export declare function clearActiveContainer(): void;
|
|
51
35
|
/**
|
|
52
36
|
* Override a binding on the active container with a value, and queue
|
|
53
37
|
* the restore for end-of-test. Use `overrideOn` when you need to
|
|
@@ -56,31 +40,7 @@ export function clearActiveContainer(): void {
|
|
|
56
40
|
* Throws if called outside a test frame: a queued cleanup would never
|
|
57
41
|
* fire there, so the override would leak across tests silently.
|
|
58
42
|
*/
|
|
59
|
-
export function override(token: ContainerToken, value: unknown): void
|
|
60
|
-
if (!inTestContext()) {
|
|
61
|
-
throw new Error(
|
|
62
|
-
"helix.override: must be called inside a test (no active test frame). Calls from top-level setup leak across tests — use container.override() directly with manual restore() if that is what you want.",
|
|
63
|
-
);
|
|
64
|
-
}
|
|
65
|
-
if (!activeContainer) {
|
|
66
|
-
throw new Error(
|
|
67
|
-
"helix.override: no active container. Call helix.useContainer(container) before invoking override(), or use overrideOn(container, token, value).",
|
|
68
|
-
);
|
|
69
|
-
}
|
|
70
|
-
overrideOn(activeContainer, token, value);
|
|
71
|
-
}
|
|
72
|
-
|
|
43
|
+
export declare function override(token: ContainerToken, value: unknown): void;
|
|
73
44
|
/** Override on a specific container instance. */
|
|
74
|
-
export function overrideOn(
|
|
75
|
-
|
|
76
|
-
token: ContainerToken,
|
|
77
|
-
value: unknown,
|
|
78
|
-
): void {
|
|
79
|
-
container.override(token, value);
|
|
80
|
-
// `registerTestCleanup` returns false when called outside a test
|
|
81
|
-
// frame (e.g. raw vitest tests, REPL, top-level setup). In that
|
|
82
|
-
// case the caller is responsible for `container.restore(token)` —
|
|
83
|
-
// we don't queue a fallback that might fire in an unexpected
|
|
84
|
-
// frame.
|
|
85
|
-
registerTestCleanup(() => container.restore(token));
|
|
86
|
-
}
|
|
45
|
+
export declare function overrideOn(container: HelixContainer, token: ContainerToken, value: unknown): void;
|
|
46
|
+
//# sourceMappingURL=override.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"override.d.ts","sourceRoot":"","sources":["../../src/container/override.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAE5E,yEAAyE;AACzE,MAAM,WAAW,cAAe,SAAQ,aAAa;IACpD,QAAQ,CAAC,KAAK,EAAE,cAAc,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;CACtD;AAID;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,SAAS,EAAE,cAAc,GAAG,IAAI,CAM5D;AAED;;;qCAGqC;AACrC,wBAAgB,oBAAoB,IAAI,IAAI,CAE3C;AAED;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,cAAc,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAYpE;AAED,iDAAiD;AACjD,wBAAgB,UAAU,CACzB,SAAS,EAAE,cAAc,EACzB,KAAK,EAAE,cAAc,EACrB,KAAK,EAAE,OAAO,GACZ,IAAI,CAQN"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helix facade over `@c9up/ream`'s `Container.override(token, value)`.
|
|
3
|
+
*
|
|
4
|
+
* Adds two things to the raw container call:
|
|
5
|
+
* 1. The override is registered against the active test context's
|
|
6
|
+
* auto-restore queue, so it's undone after the test even if the
|
|
7
|
+
* test body never calls `restore()` itself.
|
|
8
|
+
* 2. A clear error when called outside a test (no AsyncLocalStorage
|
|
9
|
+
* frame) — the queued cleanup would never fire there.
|
|
10
|
+
*
|
|
11
|
+
* The active container is passed via `useContainer(container)` (called
|
|
12
|
+
* by the host app's test bootstrap). For bare unit tests with no host
|
|
13
|
+
* app, callers can pass the container explicitly to `overrideOn`.
|
|
14
|
+
*/
|
|
15
|
+
import { inTestContext, registerTestCleanup } from "../runtime/test-context.js";
|
|
16
|
+
let activeContainer;
|
|
17
|
+
/**
|
|
18
|
+
* Bind a container instance as active for `helix.override(...)` calls.
|
|
19
|
+
*
|
|
20
|
+
* If called inside a test frame, the previous active container is
|
|
21
|
+
* captured and restored when the frame ends — so swapping the active
|
|
22
|
+
* container mid-test does not leak into the next test. Outside a test
|
|
23
|
+
* frame the assignment is permanent until the next `useContainer` /
|
|
24
|
+
* `clearActiveContainer` call.
|
|
25
|
+
*/
|
|
26
|
+
export function useContainer(container) {
|
|
27
|
+
const previous = activeContainer;
|
|
28
|
+
activeContainer = container;
|
|
29
|
+
registerTestCleanup(() => {
|
|
30
|
+
activeContainer = previous;
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
/** Reset the active container binding. Typically not needed in user
|
|
34
|
+
* code — `useContainer` already auto-restores when called inside a
|
|
35
|
+
* test. Exposed so `afterEach` blocks (and unit tests) can clear the
|
|
36
|
+
* module-scoped slot explicitly. */
|
|
37
|
+
export function clearActiveContainer() {
|
|
38
|
+
activeContainer = undefined;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Override a binding on the active container with a value, and queue
|
|
42
|
+
* the restore for end-of-test. Use `overrideOn` when you need to
|
|
43
|
+
* target a specific container instance.
|
|
44
|
+
*
|
|
45
|
+
* Throws if called outside a test frame: a queued cleanup would never
|
|
46
|
+
* fire there, so the override would leak across tests silently.
|
|
47
|
+
*/
|
|
48
|
+
export function override(token, value) {
|
|
49
|
+
if (!inTestContext()) {
|
|
50
|
+
throw new Error("helix.override: must be called inside a test (no active test frame). Calls from top-level setup leak across tests — use container.override() directly with manual restore() if that is what you want.");
|
|
51
|
+
}
|
|
52
|
+
if (!activeContainer) {
|
|
53
|
+
throw new Error("helix.override: no active container. Call helix.useContainer(container) before invoking override(), or use overrideOn(container, token, value).");
|
|
54
|
+
}
|
|
55
|
+
overrideOn(activeContainer, token, value);
|
|
56
|
+
}
|
|
57
|
+
/** Override on a specific container instance. */
|
|
58
|
+
export function overrideOn(container, token, value) {
|
|
59
|
+
container.override(token, value);
|
|
60
|
+
// `registerTestCleanup` returns false when called outside a test
|
|
61
|
+
// frame (e.g. raw vitest tests, REPL, top-level setup). In that
|
|
62
|
+
// case the caller is responsible for `container.restore(token)` —
|
|
63
|
+
// we don't queue a fallback that might fire in an unexpected
|
|
64
|
+
// frame.
|
|
65
|
+
registerTestCleanup(() => container.restore(token));
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=override.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"override.js","sourceRoot":"","sources":["../../src/container/override.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAQhF,IAAI,eAA2C,CAAC;AAEhD;;;;;;;;GAQG;AACH,MAAM,UAAU,YAAY,CAAC,SAAyB;IACrD,MAAM,QAAQ,GAAG,eAAe,CAAC;IACjC,eAAe,GAAG,SAAS,CAAC;IAC5B,mBAAmB,CAAC,GAAG,EAAE;QACxB,eAAe,GAAG,QAAQ,CAAC;IAC5B,CAAC,CAAC,CAAC;AACJ,CAAC;AAED;;;qCAGqC;AACrC,MAAM,UAAU,oBAAoB;IACnC,eAAe,GAAG,SAAS,CAAC;AAC7B,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,QAAQ,CAAC,KAAqB,EAAE,KAAc;IAC7D,IAAI,CAAC,aAAa,EAAE,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CACd,uMAAuM,CACvM,CAAC;IACH,CAAC;IACD,IAAI,CAAC,eAAe,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CACd,iJAAiJ,CACjJ,CAAC;IACH,CAAC;IACD,UAAU,CAAC,eAAe,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;AAC3C,CAAC;AAED,iDAAiD;AACjD,MAAM,UAAU,UAAU,CACzB,SAAyB,EACzB,KAAqB,EACrB,KAAc;IAEd,SAAS,CAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IACjC,iEAAiE;IACjE,gEAAgE;IAChE,kEAAkE;IAClE,6DAA6D;IAC7D,SAAS;IACT,mBAAmB,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;AACrD,CAAC"}
|
|
@@ -9,10 +9,7 @@
|
|
|
9
9
|
* // ...
|
|
10
10
|
* expect(fakeMail.send).toHaveBeenCalledOnce();
|
|
11
11
|
*/
|
|
12
|
-
|
|
13
|
-
import { vi } from "../runtime/vi/index.js";
|
|
14
12
|
import type { AnyFn, Spy } from "../runtime/vi/spy.js";
|
|
15
|
-
|
|
16
13
|
/**
|
|
17
14
|
* Wrap (don't bind) `vi.fn` so `spy` always reaches through to the
|
|
18
15
|
* current `vi.fn` even if it gets replaced at runtime — and so the
|
|
@@ -20,6 +17,5 @@ import type { AnyFn, Spy } from "../runtime/vi/spy.js";
|
|
|
20
17
|
* object. The cost over `export const spy = vi.fn` is one extra
|
|
21
18
|
* function call per spawn.
|
|
22
19
|
*/
|
|
23
|
-
export function spy<Fn extends AnyFn>(implementation?: Fn): Spy<Fn
|
|
24
|
-
|
|
25
|
-
}
|
|
20
|
+
export declare function spy<Fn extends AnyFn>(implementation?: Fn): Spy<Fn>;
|
|
21
|
+
//# sourceMappingURL=spy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spy.d.ts","sourceRoot":"","sources":["../../src/container/spy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,sBAAsB,CAAC;AAEvD;;;;;;GAMG;AACH,wBAAgB,GAAG,CAAC,EAAE,SAAS,KAAK,EAAE,cAAc,CAAC,EAAE,EAAE,GAAG,GAAG,CAAC,EAAE,CAAC,CAElE"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `helix.spy()` — Jest-like spy factory.
|
|
3
|
+
*
|
|
4
|
+
* Thin alias over `vi.fn()` so the documented one-liner from FR69 /
|
|
5
|
+
* Story 42.3 reads:
|
|
6
|
+
*
|
|
7
|
+
* const fakeMail = { send: spy() };
|
|
8
|
+
* override('mail', fakeMail);
|
|
9
|
+
* // ...
|
|
10
|
+
* expect(fakeMail.send).toHaveBeenCalledOnce();
|
|
11
|
+
*/
|
|
12
|
+
import { vi } from "../runtime/vi/index.js";
|
|
13
|
+
/**
|
|
14
|
+
* Wrap (don't bind) `vi.fn` so `spy` always reaches through to the
|
|
15
|
+
* current `vi.fn` even if it gets replaced at runtime — and so the
|
|
16
|
+
* captured reference can never go stale relative to the live `vi`
|
|
17
|
+
* object. The cost over `export const spy = vi.fn` is one extra
|
|
18
|
+
* function call per spawn.
|
|
19
|
+
*/
|
|
20
|
+
export function spy(implementation) {
|
|
21
|
+
return vi.fn(implementation);
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=spy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spy.js","sourceRoot":"","sources":["../../src/container/spy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,EAAE,EAAE,MAAM,wBAAwB,CAAC;AAG5C;;;;;;GAMG;AACH,MAAM,UAAU,GAAG,CAAmB,cAAmB;IACxD,OAAO,EAAE,CAAC,EAAE,CAAC,cAAc,CAAC,CAAC;AAC9B,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@c9up/helix` — the framework-agnostic test runtime for the Ream ecosystem.
|
|
3
|
+
*
|
|
4
|
+
* This barrel exposes the Vitest-compatible runtime (describe/test/expect/vi/
|
|
5
|
+
* spies/lifecycle), the container facade, and time-travel — none of which import
|
|
6
|
+
* an ecosystem package. It is fully agnostic: usable in any project.
|
|
7
|
+
*
|
|
8
|
+
* Per-package test fakes/helpers live in EACH package's own `/testing` subpath
|
|
9
|
+
* (a package owns its test surface, not helix): `@c9up/ream/testing`
|
|
10
|
+
* (TestClient, FakeBus, assertEmitted), `@c9up/atlas/testing` (factory,
|
|
11
|
+
* useTransaction), `@c9up/rover/testing` (FakeMail), `@c9up/bay/testing`
|
|
12
|
+
* (FakeQueue), `@c9up/spectrum/testing` (FakeLogger), `@c9up/nova/testing`
|
|
13
|
+
* (FakeNova), `@c9up/relay/testing` (FakeRelay), `@c9up/archive/testing`
|
|
14
|
+
* (FakeStorage). Install a package → you get its testing surface.
|
|
15
|
+
*/
|
|
16
|
+
export * from "./container/index.js";
|
|
17
|
+
export type { Assertion, FileResult, Hook, HookType, MatcherName, MatcherResult, Spy, SuiteResult, TestResult, Vi, } from "./runtime/index.js";
|
|
18
|
+
export { AssertionError, afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, test, vi, } from "./runtime/index.js";
|
|
19
|
+
export * as time from "./time/index.js";
|
|
20
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,cAAc,sBAAsB,CAAC;AACrC,YAAY,EACX,SAAS,EACT,UAAU,EACV,IAAI,EACJ,QAAQ,EACR,WAAW,EACX,aAAa,EACb,GAAG,EACH,WAAW,EACX,UAAU,EACV,EAAE,GACF,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACN,cAAc,EACd,QAAQ,EACR,SAAS,EACT,SAAS,EACT,UAAU,EACV,QAAQ,EACR,MAAM,EACN,EAAE,EACF,IAAI,EACJ,EAAE,GACF,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,IAAI,MAAM,iBAAiB,CAAC"}
|
|
@@ -13,30 +13,7 @@
|
|
|
13
13
|
* (FakeNova), `@c9up/relay/testing` (FakeRelay), `@c9up/archive/testing`
|
|
14
14
|
* (FakeStorage). Install a package → you get its testing surface.
|
|
15
15
|
*/
|
|
16
|
-
|
|
17
16
|
export * from "./container/index.js";
|
|
18
|
-
export
|
|
19
|
-
Assertion,
|
|
20
|
-
FileResult,
|
|
21
|
-
Hook,
|
|
22
|
-
HookType,
|
|
23
|
-
MatcherName,
|
|
24
|
-
MatcherResult,
|
|
25
|
-
Spy,
|
|
26
|
-
SuiteResult,
|
|
27
|
-
TestResult,
|
|
28
|
-
Vi,
|
|
29
|
-
} from "./runtime/index.js";
|
|
30
|
-
export {
|
|
31
|
-
AssertionError,
|
|
32
|
-
afterAll,
|
|
33
|
-
afterEach,
|
|
34
|
-
beforeAll,
|
|
35
|
-
beforeEach,
|
|
36
|
-
describe,
|
|
37
|
-
expect,
|
|
38
|
-
it,
|
|
39
|
-
test,
|
|
40
|
-
vi,
|
|
41
|
-
} from "./runtime/index.js";
|
|
17
|
+
export { AssertionError, afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, test, vi, } from "./runtime/index.js";
|
|
42
18
|
export * as time from "./time/index.js";
|
|
19
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,cAAc,sBAAsB,CAAC;AAarC,OAAO,EACN,cAAc,EACd,QAAQ,EACR,SAAS,EACT,SAAS,EACT,UAAU,EACV,QAAQ,EACR,MAAM,EACN,EAAE,EACF,IAAI,EACJ,EAAE,GACF,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,IAAI,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Assertion error thrown by `expect` — carries structured fields so runners
|
|
3
|
+
* (Vitest-compatible reporters, IDE integrations) can render diffs.
|
|
4
|
+
*/
|
|
5
|
+
export interface AssertionErrorInit {
|
|
6
|
+
message: string;
|
|
7
|
+
actual?: unknown;
|
|
8
|
+
expected?: unknown;
|
|
9
|
+
operator?: string;
|
|
10
|
+
showDiff?: boolean;
|
|
11
|
+
}
|
|
12
|
+
export declare class AssertionError extends Error {
|
|
13
|
+
readonly actual: unknown;
|
|
14
|
+
readonly expected: unknown;
|
|
15
|
+
readonly operator: string | undefined;
|
|
16
|
+
readonly showDiff: boolean;
|
|
17
|
+
constructor(init: AssertionErrorInit);
|
|
18
|
+
}
|
|
19
|
+
export declare function isAssertionError(value: unknown): value is AssertionError;
|
|
20
|
+
//# sourceMappingURL=assertion-error.d.ts.map
|