@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.
Files changed (235) hide show
  1. package/dist/cli/coverage/aggregate.d.ts +24 -0
  2. package/dist/cli/coverage/aggregate.d.ts.map +1 -0
  3. package/dist/cli/coverage/aggregate.js +215 -0
  4. package/dist/cli/coverage/aggregate.js.map +1 -0
  5. package/dist/cli/coverage/collect.d.ts +13 -0
  6. package/dist/cli/coverage/collect.d.ts.map +1 -0
  7. package/dist/cli/coverage/collect.js +66 -0
  8. package/dist/cli/coverage/collect.js.map +1 -0
  9. package/dist/cli/coverage/diff/base.d.ts +17 -0
  10. package/dist/cli/coverage/diff/base.d.ts.map +1 -0
  11. package/dist/cli/coverage/diff/base.js +44 -0
  12. package/dist/cli/coverage/diff/base.js.map +1 -0
  13. package/dist/cli/coverage/diff/index.d.ts +25 -0
  14. package/dist/cli/coverage/diff/index.d.ts.map +1 -0
  15. package/dist/cli/coverage/diff/index.js +119 -0
  16. package/dist/cli/coverage/diff/index.js.map +1 -0
  17. package/dist/cli/coverage/diff/overlay.d.ts +19 -0
  18. package/dist/cli/coverage/diff/overlay.d.ts.map +1 -0
  19. package/dist/cli/coverage/diff/overlay.js +58 -0
  20. package/dist/cli/coverage/diff/overlay.js.map +1 -0
  21. package/dist/cli/coverage/diff/parse.d.ts +27 -0
  22. package/dist/cli/coverage/diff/parse.d.ts.map +1 -0
  23. package/dist/cli/coverage/diff/parse.js +105 -0
  24. package/dist/cli/coverage/diff/parse.js.map +1 -0
  25. package/dist/cli/coverage/diff/reporters.d.ts +12 -0
  26. package/dist/cli/coverage/diff/reporters.d.ts.map +1 -0
  27. package/dist/cli/coverage/diff/reporters.js +59 -0
  28. package/dist/cli/coverage/diff/reporters.js.map +1 -0
  29. package/dist/cli/coverage/diff/types.d.ts +46 -0
  30. package/dist/cli/coverage/diff/types.d.ts.map +1 -0
  31. package/dist/cli/coverage/diff/types.js +15 -0
  32. package/dist/cli/coverage/diff/types.js.map +1 -0
  33. package/dist/cli/coverage/filter.d.ts +13 -0
  34. package/dist/cli/coverage/filter.d.ts.map +1 -0
  35. package/dist/cli/coverage/filter.js +60 -0
  36. package/dist/cli/coverage/filter.js.map +1 -0
  37. package/dist/cli/coverage/glob.d.ts +4 -0
  38. package/dist/cli/coverage/glob.d.ts.map +1 -0
  39. package/dist/cli/coverage/glob.js +0 -0
  40. package/dist/cli/coverage/glob.js.map +1 -0
  41. package/dist/cli/coverage/index.d.ts +40 -0
  42. package/dist/cli/coverage/index.d.ts.map +1 -0
  43. package/dist/cli/coverage/index.js +85 -0
  44. package/dist/cli/coverage/index.js.map +1 -0
  45. package/dist/cli/coverage/reporters/json.d.ts +13 -0
  46. package/dist/cli/coverage/reporters/json.d.ts.map +1 -0
  47. package/dist/cli/coverage/reporters/json.js +37 -0
  48. package/dist/cli/coverage/reporters/json.js.map +1 -0
  49. package/dist/cli/coverage/reporters/lcov.d.ts +13 -0
  50. package/dist/cli/coverage/reporters/lcov.d.ts.map +1 -0
  51. package/dist/cli/coverage/reporters/lcov.js +51 -0
  52. package/dist/cli/coverage/reporters/lcov.js.map +1 -0
  53. package/dist/cli/coverage/reporters/text.d.ts +8 -0
  54. package/dist/cli/coverage/reporters/text.d.ts.map +1 -0
  55. package/dist/cli/coverage/reporters/text.js +39 -0
  56. package/dist/cli/coverage/reporters/text.js.map +1 -0
  57. package/dist/cli/coverage/thresholds.d.ts +14 -0
  58. package/dist/cli/coverage/thresholds.d.ts.map +1 -0
  59. package/dist/cli/coverage/thresholds.js +58 -0
  60. package/dist/cli/coverage/thresholds.js.map +1 -0
  61. package/dist/cli/coverage/types.d.ts +118 -0
  62. package/dist/cli/coverage/types.d.ts.map +1 -0
  63. package/dist/cli/coverage/types.js +8 -0
  64. package/dist/cli/coverage/types.js.map +1 -0
  65. package/dist/cli/discover.d.ts +27 -0
  66. package/dist/cli/discover.d.ts.map +1 -0
  67. package/dist/cli/discover.js +142 -0
  68. package/dist/cli/discover.js.map +1 -0
  69. package/dist/cli/native.d.ts +43 -0
  70. package/dist/cli/native.d.ts.map +1 -0
  71. package/dist/cli/native.js +64 -0
  72. package/dist/cli/native.js.map +1 -0
  73. package/dist/cli/pool.d.ts +55 -0
  74. package/dist/cli/pool.d.ts.map +1 -0
  75. package/dist/cli/pool.js +404 -0
  76. package/dist/cli/pool.js.map +1 -0
  77. package/dist/cli/reporter.d.ts +51 -0
  78. package/dist/cli/reporter.d.ts.map +1 -0
  79. package/dist/cli/reporter.js +124 -0
  80. package/dist/cli/reporter.js.map +1 -0
  81. package/dist/cli/run.d.ts +61 -0
  82. package/dist/cli/run.d.ts.map +1 -0
  83. package/dist/cli/run.js +302 -0
  84. package/dist/cli/run.js.map +1 -0
  85. package/dist/cli/summary.d.ts +18 -0
  86. package/dist/cli/summary.d.ts.map +1 -0
  87. package/dist/cli/summary.js +20 -0
  88. package/dist/cli/summary.js.map +1 -0
  89. package/dist/cli/watch/loop.d.ts +26 -0
  90. package/dist/cli/watch/loop.d.ts.map +1 -0
  91. package/dist/cli/watch/loop.js +133 -0
  92. package/dist/cli/watch/loop.js.map +1 -0
  93. package/dist/cli/watch/types.d.ts +22 -0
  94. package/dist/cli/watch/types.d.ts.map +1 -0
  95. package/dist/cli/watch/types.js +6 -0
  96. package/dist/cli/watch/types.js.map +1 -0
  97. package/dist/cli/watch/watcher.d.ts +25 -0
  98. package/dist/cli/watch/watcher.d.ts.map +1 -0
  99. package/dist/cli/watch/watcher.js +122 -0
  100. package/dist/cli/watch/watcher.js.map +1 -0
  101. package/dist/container/index.d.ts +7 -0
  102. package/dist/container/index.d.ts.map +1 -0
  103. package/dist/container/index.js +6 -0
  104. package/dist/container/index.js.map +1 -0
  105. package/{src/container/override.ts → dist/container/override.d.ts} +6 -46
  106. package/dist/container/override.d.ts.map +1 -0
  107. package/dist/container/override.js +67 -0
  108. package/dist/container/override.js.map +1 -0
  109. package/{src/container/spy.ts → dist/container/spy.d.ts} +2 -6
  110. package/dist/container/spy.d.ts.map +1 -0
  111. package/dist/container/spy.js +23 -0
  112. package/dist/container/spy.js.map +1 -0
  113. package/dist/index.d.ts +20 -0
  114. package/dist/index.d.ts.map +1 -0
  115. package/{src/index.ts → dist/index.js} +2 -25
  116. package/dist/index.js.map +1 -0
  117. package/dist/runtime/assertion-error.d.ts +20 -0
  118. package/dist/runtime/assertion-error.d.ts.map +1 -0
  119. package/dist/runtime/assertion-error.js +23 -0
  120. package/dist/runtime/assertion-error.js.map +1 -0
  121. package/dist/runtime/cli-worker.d.ts +25 -0
  122. package/dist/runtime/cli-worker.d.ts.map +1 -0
  123. package/dist/runtime/cli-worker.js +128 -0
  124. package/dist/runtime/cli-worker.js.map +1 -0
  125. package/dist/runtime/equals.d.ts +25 -0
  126. package/dist/runtime/equals.d.ts.map +1 -0
  127. package/dist/runtime/equals.js +343 -0
  128. package/dist/runtime/equals.js.map +1 -0
  129. package/dist/runtime/expect.d.ts +32 -0
  130. package/dist/runtime/expect.d.ts.map +1 -0
  131. package/dist/runtime/expect.js +121 -0
  132. package/dist/runtime/expect.js.map +1 -0
  133. package/{src/runtime/index.ts → dist/runtime/index.d.ts} +5 -27
  134. package/dist/runtime/index.d.ts.map +1 -0
  135. package/dist/runtime/index.js +17 -0
  136. package/dist/runtime/index.js.map +1 -0
  137. package/{src/runtime/lifecycle.ts → dist/runtime/lifecycle.d.ts} +2 -8
  138. package/dist/runtime/lifecycle.d.ts.map +1 -0
  139. package/dist/runtime/lifecycle.js +10 -0
  140. package/dist/runtime/lifecycle.js.map +1 -0
  141. package/dist/runtime/matchers.d.ts +42 -0
  142. package/dist/runtime/matchers.d.ts.map +1 -0
  143. package/dist/runtime/matchers.js +375 -0
  144. package/dist/runtime/matchers.js.map +1 -0
  145. package/dist/runtime/run.d.ts +55 -0
  146. package/dist/runtime/run.d.ts.map +1 -0
  147. package/dist/runtime/run.js +456 -0
  148. package/dist/runtime/run.js.map +1 -0
  149. package/dist/runtime/suite.d.ts +91 -0
  150. package/dist/runtime/suite.d.ts.map +1 -0
  151. package/dist/runtime/suite.js +206 -0
  152. package/dist/runtime/suite.js.map +1 -0
  153. package/dist/runtime/test-context.d.ts +25 -0
  154. package/dist/runtime/test-context.d.ts.map +1 -0
  155. package/dist/runtime/test-context.js +52 -0
  156. package/dist/runtime/test-context.js.map +1 -0
  157. package/dist/runtime/vi/fake-timers.d.ts +48 -0
  158. package/dist/runtime/vi/fake-timers.d.ts.map +1 -0
  159. package/dist/runtime/vi/fake-timers.js +317 -0
  160. package/dist/runtime/vi/fake-timers.js.map +1 -0
  161. package/dist/runtime/vi/index.d.ts +70 -0
  162. package/dist/runtime/vi/index.d.ts.map +1 -0
  163. package/dist/runtime/vi/index.js +209 -0
  164. package/dist/runtime/vi/index.js.map +1 -0
  165. package/dist/runtime/vi/spy.d.ts +54 -0
  166. package/dist/runtime/vi/spy.d.ts.map +1 -0
  167. package/dist/runtime/vi/spy.js +159 -0
  168. package/dist/runtime/vi/spy.js.map +1 -0
  169. package/dist/runtime/vi/spyOn.d.ts +28 -0
  170. package/dist/runtime/vi/spyOn.d.ts.map +1 -0
  171. package/dist/runtime/vi/spyOn.js +121 -0
  172. package/dist/runtime/vi/spyOn.js.map +1 -0
  173. package/dist/runtime/vi/system-time.d.ts +30 -0
  174. package/dist/runtime/vi/system-time.d.ts.map +1 -0
  175. package/dist/runtime/vi/system-time.js +100 -0
  176. package/dist/runtime/vi/system-time.js.map +1 -0
  177. package/dist/runtime/worker.d.ts +26 -0
  178. package/dist/runtime/worker.d.ts.map +1 -0
  179. package/dist/runtime/worker.js +195 -0
  180. package/dist/runtime/worker.js.map +1 -0
  181. package/dist/time/freeze.d.ts +80 -0
  182. package/dist/time/freeze.d.ts.map +1 -0
  183. package/dist/time/freeze.js +184 -0
  184. package/dist/time/freeze.js.map +1 -0
  185. package/{src/time/index.ts → dist/time/index.d.ts} +1 -1
  186. package/dist/time/index.d.ts.map +1 -0
  187. package/dist/time/index.js +15 -0
  188. package/dist/time/index.js.map +1 -0
  189. package/index.darwin-arm64.node +0 -0
  190. package/index.darwin-x64.node +0 -0
  191. package/index.linux-arm64-gnu.node +0 -0
  192. package/index.linux-x64-gnu.node +0 -0
  193. package/index.win32-x64-msvc.node +0 -0
  194. package/package.json +2 -2
  195. package/src/cli/coverage/aggregate.ts +0 -231
  196. package/src/cli/coverage/collect.ts +0 -63
  197. package/src/cli/coverage/diff/base.ts +0 -46
  198. package/src/cli/coverage/diff/index.ts +0 -160
  199. package/src/cli/coverage/diff/overlay.ts +0 -62
  200. package/src/cli/coverage/diff/parse.ts +0 -121
  201. package/src/cli/coverage/diff/reporters.ts +0 -82
  202. package/src/cli/coverage/diff/types.ts +0 -46
  203. package/src/cli/coverage/filter.ts +0 -71
  204. package/src/cli/coverage/glob.ts +0 -0
  205. package/src/cli/coverage/index.ts +0 -126
  206. package/src/cli/coverage/reporters/json.ts +0 -40
  207. package/src/cli/coverage/reporters/lcov.ts +0 -54
  208. package/src/cli/coverage/reporters/text.ts +0 -48
  209. package/src/cli/coverage/thresholds.ts +0 -73
  210. package/src/cli/coverage/types.ts +0 -93
  211. package/src/cli/discover.ts +0 -174
  212. package/src/cli/native.ts +0 -104
  213. package/src/cli/pool.ts +0 -486
  214. package/src/cli/reporter.ts +0 -155
  215. package/src/cli/run.ts +0 -440
  216. package/src/cli/summary.ts +0 -42
  217. package/src/cli/watch/loop.ts +0 -159
  218. package/src/cli/watch/types.ts +0 -22
  219. package/src/cli/watch/watcher.ts +0 -145
  220. package/src/container/index.ts +0 -16
  221. package/src/runtime/assertion-error.ts +0 -38
  222. package/src/runtime/cli-worker.ts +0 -140
  223. package/src/runtime/equals.ts +0 -400
  224. package/src/runtime/expect.ts +0 -173
  225. package/src/runtime/matchers.ts +0 -452
  226. package/src/runtime/run.ts +0 -573
  227. package/src/runtime/suite.ts +0 -310
  228. package/src/runtime/test-context.ts +0 -59
  229. package/src/runtime/vi/fake-timers.ts +0 -410
  230. package/src/runtime/vi/index.ts +0 -254
  231. package/src/runtime/vi/spy.ts +0 -224
  232. package/src/runtime/vi/spyOn.ts +0 -155
  233. package/src/runtime/vi/system-time.ts +0 -121
  234. package/src/runtime/worker.ts +0 -239
  235. 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":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,EAAE,aAAa,EAAsB,MAAM,cAAc,CAAC;AAajE,MAAM,cAAc,GAAG,gDAAgD,CAAC;AACxE,MAAM,YAAY,GAAG,sCAAsC,CAAC;AAE5D,SAAS,eAAe;IACvB,OAAO;QACN,OAAO,EAAE;YACR,MAAM,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE;YAC7D,KAAK,EAAE,EAAE;YACT,UAAU,EAAE,EAAE;YACd,UAAU,EAAE,CAAC;SACb;QACD,QAAQ,EAAE,CAAC;KACX,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC7B,IAAqB,EACrB,OAAkC;IAElC,kEAAkE;IAClE,sEAAsE;IACtE,2DAA2D;IAC3D,IAAI,WAAW,GAAe,eAAe,EAAE,CAAC;IAChD,IAAI,QAAmC,CAAC;IACxC,IAAI,MAAM,GAAG,KAAK,CAAC;IACnB,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,IAAI,OAAkC,CAAC;IACvC,IAAI,cAA0B,CAAC;IAC/B,MAAM,OAAO,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QAC7C,cAAc,GAAG,OAAO,CAAC;IAC1B,CAAC,CAAC,CAAC;IAEH,SAAS,aAAa;QACrB,IAAI,OAAO;YAAE,OAAO;QACpB,OAAO,GAAG,IAAI,CAAC;QACf,KAAK,CAAC,KAAK,IAAI,EAAE;YAChB,IAAI,OAAO;gBAAE,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;YACnC,IAAI,QAAQ;gBAAE,MAAM,QAAQ,CAAC;YAC7B,cAAc,EAAE,CAAC;QAClB,CAAC,CAAC,EAAE,CAAC;IACN,CAAC;IAED,MAAM,QAAQ,GAAG,GAAS,EAAE,CAAC,aAAa,EAAE,CAAC;IAC7C,MAAM,OAAO,GAAG,GAAS,EAAE,CAAC,aAAa,EAAE,CAAC;IAC5C,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAC/B,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;QACjB,IAAI,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACzB,OAAO,GAAG,IAAI,CAAC;QAChB,CAAC;aAAM,CAAC;YACP,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QAChE,CAAC;IACF,CAAC;IAED,SAAS,OAAO;QACf,IAAI,OAAO;YAAE,OAAO;QACpB,IAAI,QAAQ,EAAE,CAAC;YACd,MAAM,GAAG,IAAI,CAAC;YACd,OAAO;QACR,CAAC;QACD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,YAAY,IAAI,CAAC,CAAC;QAC1C,QAAQ,GAAG,CAAC,KAAK,IAAI,EAAE;YACtB,IAAI,CAAC;gBACJ,WAAW,GAAG,MAAM,OAAO,EAAE,CAAC;YAC/B,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACd,OAAO,CAAC,MAAM,CAAC,KAAK,CACnB,4BAA4B,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAChF,CAAC;YACH,CAAC;oBAAS,CAAC;gBACV,QAAQ,GAAG,SAAS,CAAC;gBACrB,IAAI,CAAC,OAAO,EAAE,CAAC;oBACd,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,cAAc,IAAI,CAAC,CAAC;gBAC7C,CAAC;gBACD,IAAI,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;oBACxB,MAAM,GAAG,KAAK,CAAC;oBACf,OAAO,EAAE,CAAC;gBACX,CAAC;YACF,CAAC;QACF,CAAC,CAAC,EAAE,CAAC;IACN,CAAC;IAED,mEAAmE;IACnE,qEAAqE;IACrE,gEAAgE;IAChE,aAAa;IACb,QAAQ,GAAG,CAAC,KAAK,IAAI,EAAE;QACtB,IAAI,CAAC;YACJ,WAAW,GAAG,MAAM,OAAO,EAAE,CAAC;QAC/B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACd,OAAO,CAAC,MAAM,CAAC,KAAK,CACnB,4BAA4B,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAChF,CAAC;QACH,CAAC;gBAAS,CAAC;YACV,QAAQ,GAAG,SAAS,CAAC;QACtB,CAAC;IACF,CAAC,CAAC,EAAE,CAAC;IACL,MAAM,QAAQ,CAAC;IAEf,IAAI,OAAO,EAAE,CAAC;QACb,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QAChC,IAAI,CAAC,MAAM,EAAE,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACnD,OAAO,EAAE,GAAG,WAAW,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;IACxC,CAAC;IAED,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,cAAc,IAAI,CAAC,CAAC;IAE5C,IAAI,iBAAoC,CAAC;IACzC,OAAO,GAAG,aAAa,CAAC;QACvB,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,UAAU,EAAE,IAAI,CAAC,UAAU;QAC3B,QAAQ,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE;QACzB,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YAChB,iBAAiB,GAAG,GAAG,CAAC;YACxB,aAAa,EAAE,CAAC;QACjB,CAAC;KACD,CAAC,CAAC;IAEH,MAAM,OAAO,CAAC;IAEd,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAChC,IAAI,CAAC,MAAM,EAAE,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAEnD,IAAI,iBAAiB,EAAE,CAAC;QACvB,OAAO,CAAC,MAAM,CAAC,KAAK,CACnB,gCAAgC,iBAAiB,CAAC,OAAO,IAAI,CAC7D,CAAC;IACH,CAAC;IAED,OAAO,EAAE,GAAG,WAAW,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;AACxC,CAAC"}
@@ -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,6 @@
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 {};
6
+ //# sourceMappingURL=types.js.map
@@ -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,6 @@
1
+ /**
2
+ * `@c9up/helix/container` — barrel for the test-container facade.
3
+ */
4
+ export { clearActiveContainer, override, overrideOn, useContainer, } from "./override.js";
5
+ export { spy } from "./spy.js";
6
+ //# sourceMappingURL=index.js.map
@@ -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
- override(token: ContainerToken, value: unknown): void;
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
- container: HelixContainer,
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
- return vi.fn(implementation);
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"}
@@ -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 type {
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