@c9up/helix 0.1.3 → 0.1.4

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 (189) 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/dist/container/override.d.ts +46 -0
  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/dist/container/spy.d.ts +21 -0
  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/dist/index.js +19 -0
  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/dist/runtime/index.d.ts +28 -0
  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/dist/runtime/lifecycle.d.ts +11 -0
  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 +65 -0
  150. package/dist/runtime/suite.d.ts.map +1 -0
  151. package/dist/runtime/suite.js +203 -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/dist/time/index.d.ts +16 -0
  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/package.json +1 -1
@@ -0,0 +1,203 @@
1
+ /**
2
+ * `describe` / `test` / `it` DSL — builds a suite tree during module load.
3
+ *
4
+ * Collection phase: `describe(name, fn)` runs `fn` synchronously with an
5
+ * active suite on a per-invocation stack. `withCollection(...)` scopes the
6
+ * collection to one `runTestFile` call via AsyncLocalStorage, so parallel
7
+ * invocations never share state.
8
+ */
9
+ import { AsyncLocalStorage } from "node:async_hooks";
10
+ function makeSuite(name, mode, parent) {
11
+ return {
12
+ kind: "suite",
13
+ name,
14
+ mode,
15
+ parent,
16
+ children: [],
17
+ hooks: [],
18
+ };
19
+ }
20
+ /**
21
+ * Scoped collection context. Each `runTestFile` invocation opens its own
22
+ * store so concurrent runs never see each other's registrations.
23
+ */
24
+ const storage = new AsyncLocalStorage();
25
+ /**
26
+ * Fallback context used when the DSL is called outside `runTestFile` (e.g.,
27
+ * unit tests that drive `describe`/`test` directly). `resetRoot` replaces
28
+ * this; `getRoot` returns it.
29
+ */
30
+ let fallbackRoot = makeSuite("", "run", undefined);
31
+ let fallbackStack = [fallbackRoot];
32
+ function currentContext() {
33
+ const scoped = storage.getStore();
34
+ if (scoped)
35
+ return scoped;
36
+ return { root: fallbackRoot, stack: fallbackStack };
37
+ }
38
+ export function resetRoot() {
39
+ fallbackRoot = makeSuite("", "run", undefined);
40
+ fallbackStack = [fallbackRoot];
41
+ return fallbackRoot;
42
+ }
43
+ export function getRoot() {
44
+ return currentContext().root;
45
+ }
46
+ /**
47
+ * Run `body` inside a fresh, isolated collection context. Returns the root
48
+ * populated by its DSL calls. Safe under concurrent invocation.
49
+ */
50
+ export async function withCollection(body) {
51
+ const root = makeSuite("", "run", undefined);
52
+ const ctx = { root, stack: [root] };
53
+ await storage.run(ctx, async () => {
54
+ await body();
55
+ });
56
+ return root;
57
+ }
58
+ function current() {
59
+ const { stack } = currentContext();
60
+ const top = stack[stack.length - 1];
61
+ if (!top)
62
+ throw new Error("internal: suite stack empty");
63
+ return top;
64
+ }
65
+ function registerTest(name, mode, fn) {
66
+ const parent = current();
67
+ parent.children.push({
68
+ kind: "test",
69
+ name,
70
+ fn,
71
+ mode,
72
+ parent,
73
+ });
74
+ }
75
+ function registerSuite(name, mode, body) {
76
+ const ctx = currentContext();
77
+ const parent = current();
78
+ const suite = makeSuite(name, mode, parent);
79
+ parent.children.push(suite);
80
+ ctx.stack.push(suite);
81
+ let result;
82
+ try {
83
+ result = body();
84
+ }
85
+ finally {
86
+ ctx.stack.pop();
87
+ }
88
+ // Async describe bodies would register nested describe/test against the
89
+ // wrong parent after the first `await` (because we've already popped).
90
+ // Fail loudly rather than silently mis-collect.
91
+ if (result && typeof result.then === "function") {
92
+ throw new Error(`describe(${JSON.stringify(name)}): body returned a Promise. Async describe is not supported — await the setup in a \`beforeAll\` hook instead.`);
93
+ }
94
+ }
95
+ function safeStringify(v) {
96
+ try {
97
+ const s = JSON.stringify(v);
98
+ return s === undefined ? String(v) : s;
99
+ }
100
+ catch {
101
+ // Circular / BigInt / etc.
102
+ return String(v);
103
+ }
104
+ }
105
+ function isArrayRow(row) {
106
+ return Array.isArray(row);
107
+ }
108
+ function isObjectRow(row) {
109
+ return typeof row === "object" && row !== null && !Array.isArray(row);
110
+ }
111
+ function interpolateArrayRow(template, row, index) {
112
+ // Hand-rolled walker so `%%` escapes to a literal `%` without consuming a
113
+ // row value, and format specifiers consume their argument in order.
114
+ let i = 0;
115
+ let out = "";
116
+ for (let j = 0; j < template.length; j += 1) {
117
+ const c = template[j];
118
+ if (c !== "%" || j === template.length - 1) {
119
+ out += c;
120
+ continue;
121
+ }
122
+ const next = template[j + 1];
123
+ if (next === "%") {
124
+ out += "%";
125
+ j += 1;
126
+ continue;
127
+ }
128
+ if (next === "#") {
129
+ out += String(index);
130
+ j += 1;
131
+ continue;
132
+ }
133
+ if (next === "s" ||
134
+ next === "d" ||
135
+ next === "i" ||
136
+ next === "f" ||
137
+ next === "j" ||
138
+ next === "o") {
139
+ const v = row[i];
140
+ i += 1;
141
+ out += typeof v === "object" && v !== null ? safeStringify(v) : String(v);
142
+ j += 1;
143
+ continue;
144
+ }
145
+ out += c;
146
+ }
147
+ return out;
148
+ }
149
+ function interpolateObjectRow(template, row, index) {
150
+ return template
151
+ .replace(/\$#/g, String(index))
152
+ .replace(/\$([a-zA-Z_][\w.]*)/g, (_match, path) => {
153
+ const parts = path.split(".");
154
+ let cursor = row;
155
+ for (const p of parts) {
156
+ if (cursor && typeof cursor === "object" && p in cursor) {
157
+ cursor = Reflect.get(cursor, p);
158
+ }
159
+ else {
160
+ return "undefined";
161
+ }
162
+ }
163
+ if (typeof cursor === "object" && cursor !== null)
164
+ return safeStringify(cursor);
165
+ return String(cursor);
166
+ });
167
+ }
168
+ function interpolateEach(template, row, index) {
169
+ if (isArrayRow(row))
170
+ return interpolateArrayRow(template, row, index);
171
+ if (isObjectRow(row))
172
+ return interpolateObjectRow(template, row, index);
173
+ // Primitive row (number, string, boolean, bigint, symbol, null, undefined).
174
+ // Append index so names don't collide.
175
+ return `${template} [${index}]`;
176
+ }
177
+ const describeFn = ((name, fn) => registerSuite(name, "run", fn));
178
+ describeFn.skip = (name, fn) => registerSuite(name, "skip", fn);
179
+ describeFn.only = (name, fn) => registerSuite(name, "only", fn);
180
+ describeFn.todo = (name) => registerSuite(name, "todo", () => { });
181
+ const testFn = ((name, fn) => registerTest(name, "run", fn));
182
+ testFn.skip = (name, fn) => registerTest(name, "skip", fn);
183
+ testFn.only = (name, fn) => registerTest(name, "only", fn);
184
+ testFn.todo = (name) => registerTest(name, "todo", undefined);
185
+ testFn.each = (rows) => {
186
+ return (name, fn) => {
187
+ rows.forEach((row, index) => {
188
+ const resolvedName = interpolateEach(name, row, index);
189
+ registerTest(resolvedName, "run", () => fn(row));
190
+ });
191
+ };
192
+ };
193
+ export const describe = describeFn;
194
+ export const test = testFn;
195
+ export const it = testFn;
196
+ export function addHook(type, fn) {
197
+ current().hooks.push({ type, fn });
198
+ }
199
+ export const beforeAll = (fn) => addHook("beforeAll", fn);
200
+ export const afterAll = (fn) => addHook("afterAll", fn);
201
+ export const beforeEach = (fn) => addHook("beforeEach", fn);
202
+ export const afterEach = (fn) => addHook("afterEach", fn);
203
+ //# sourceMappingURL=suite.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"suite.js","sourceRoot":"","sources":["../../src/runtime/suite.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Per-test execution frame.
3
+ *
4
+ * Whereas `withViContext` wraps each test FILE (so spies and fake
5
+ * timers can span tests within a file), `withTestContext` wraps each
6
+ * INDIVIDUAL test (cycle = beforeEach + body + afterEach). It exists
7
+ * so test-scoped resources — most notably container overrides
8
+ * registered through `helix.override()` — get torn down between tests
9
+ * without needing the user to wire `afterEach(restore)` by hand.
10
+ *
11
+ * Cleanup semantics mirror `withViContext`'s finally:
12
+ * - reverse insertion order
13
+ * - one cleanup throwing must not block the others
14
+ */
15
+ export type TestCleanup = () => void | Promise<void>;
16
+ export declare function withTestContext<T>(body: () => Promise<T>): Promise<T>;
17
+ /**
18
+ * Register a cleanup function that runs at the end of the current
19
+ * test cycle. Returns `true` when registered, `false` when called
20
+ * outside a test frame (caller decides — usually a no-op or fallback
21
+ * to a wider frame).
22
+ */
23
+ export declare function registerTestCleanup(cleanup: TestCleanup): boolean;
24
+ export declare function inTestContext(): boolean;
25
+ //# sourceMappingURL=test-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-context.d.ts","sourceRoot":"","sources":["../../src/runtime/test-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,MAAM,MAAM,WAAW,GAAG,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAQrD,wBAAsB,eAAe,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAgB3E;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAKjE;AAED,wBAAgB,aAAa,IAAI,OAAO,CAEvC"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Per-test execution frame.
3
+ *
4
+ * Whereas `withViContext` wraps each test FILE (so spies and fake
5
+ * timers can span tests within a file), `withTestContext` wraps each
6
+ * INDIVIDUAL test (cycle = beforeEach + body + afterEach). It exists
7
+ * so test-scoped resources — most notably container overrides
8
+ * registered through `helix.override()` — get torn down between tests
9
+ * without needing the user to wire `afterEach(restore)` by hand.
10
+ *
11
+ * Cleanup semantics mirror `withViContext`'s finally:
12
+ * - reverse insertion order
13
+ * - one cleanup throwing must not block the others
14
+ */
15
+ import { AsyncLocalStorage } from "node:async_hooks";
16
+ const storage = new AsyncLocalStorage();
17
+ export async function withTestContext(body) {
18
+ const frame = { cleanups: [] };
19
+ try {
20
+ return await storage.run(frame, body);
21
+ }
22
+ finally {
23
+ for (let i = frame.cleanups.length - 1; i >= 0; i -= 1) {
24
+ try {
25
+ await frame.cleanups[i]();
26
+ }
27
+ catch (err) {
28
+ // One cleanup failing must not block the others — but log it
29
+ // so silent leaks become noisy. `console.error` matches what
30
+ // Vitest does for `afterEach` failures observed at runtime.
31
+ console.error("[helix] test cleanup failed:", err);
32
+ }
33
+ }
34
+ }
35
+ }
36
+ /**
37
+ * Register a cleanup function that runs at the end of the current
38
+ * test cycle. Returns `true` when registered, `false` when called
39
+ * outside a test frame (caller decides — usually a no-op or fallback
40
+ * to a wider frame).
41
+ */
42
+ export function registerTestCleanup(cleanup) {
43
+ const frame = storage.getStore();
44
+ if (!frame)
45
+ return false;
46
+ frame.cleanups.push(cleanup);
47
+ return true;
48
+ }
49
+ export function inTestContext() {
50
+ return storage.getStore() !== undefined;
51
+ }
52
+ //# sourceMappingURL=test-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-context.js","sourceRoot":"","sources":["../../src/runtime/test-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAQrD,MAAM,OAAO,GAAG,IAAI,iBAAiB,EAAa,CAAC;AAEnD,MAAM,CAAC,KAAK,UAAU,eAAe,CAAI,IAAsB;IAC9D,MAAM,KAAK,GAAc,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;IAC1C,IAAI,CAAC;QACJ,OAAO,MAAM,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IACvC,CAAC;YAAS,CAAC;QACV,KAAK,IAAI,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YACxD,IAAI,CAAC;gBACJ,MAAM,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;YAC3B,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACd,6DAA6D;gBAC7D,6DAA6D;gBAC7D,4DAA4D;gBAC5D,OAAO,CAAC,KAAK,CAAC,8BAA8B,EAAE,GAAG,CAAC,CAAC;YACpD,CAAC;QACF,CAAC;IACF,CAAC;AACF,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAAC,OAAoB;IACvD,MAAM,KAAK,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;IACjC,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAC;IACzB,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC7B,OAAO,IAAI,CAAC;AACb,CAAC;AAED,MAAM,UAAU,aAAa;IAC5B,OAAO,OAAO,CAAC,QAAQ,EAAE,KAAK,SAAS,CAAC;AACzC,CAAC"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Fake-timer core — swaps the global timer APIs for a deterministic
3
+ * scheduler. Mirrors the subset of Vitest we need:
4
+ * setTimeout / setInterval / setImmediate (+ clear*)
5
+ * advanceTimersByTime / runAllTimers / runOnlyPendingTimers
6
+ * performance.now() (when available)
7
+ *
8
+ * Zero dependency on `@sinonjs/fake-timers` — we re-implement the queue.
9
+ */
10
+ type TimerCallback = (...args: unknown[]) => void;
11
+ interface Timer {
12
+ id: number;
13
+ dueMs: number;
14
+ intervalMs: number | null;
15
+ callback: TimerCallback;
16
+ args: unknown[];
17
+ kind: "timeout" | "interval" | "immediate";
18
+ }
19
+ export interface FakeTimerState {
20
+ now: number;
21
+ queue: Timer[];
22
+ nextId: number;
23
+ active: boolean;
24
+ }
25
+ interface OriginalTimerBag {
26
+ setTimeout: unknown;
27
+ clearTimeout: unknown;
28
+ setInterval: unknown;
29
+ clearInterval: unknown;
30
+ setImmediate: unknown;
31
+ clearImmediate: unknown;
32
+ performanceNow: unknown;
33
+ }
34
+ export type TimerGlobals = OriginalTimerBag;
35
+ export declare function createFakeTimerController(): FakeTimerController;
36
+ export interface FakeTimerController {
37
+ install(now: number): void;
38
+ uninstall(): void;
39
+ isActive(): boolean;
40
+ now(): number;
41
+ advanceBy(ms: number): void;
42
+ runAll(): void;
43
+ runOnlyPending(): void;
44
+ pending(): number;
45
+ clear(): void;
46
+ }
47
+ export {};
48
+ //# sourceMappingURL=fake-timers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fake-timers.d.ts","sourceRoot":"","sources":["../../../src/runtime/vi/fake-timers.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,KAAK,aAAa,GAAG,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;AAElD,UAAU,KAAK;IACd,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,QAAQ,EAAE,aAAa,CAAC;IACxB,IAAI,EAAE,OAAO,EAAE,CAAC;IAChB,IAAI,EAAE,SAAS,GAAG,UAAU,GAAG,WAAW,CAAC;CAC3C;AAED,MAAM,WAAW,cAAc;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,KAAK,EAAE,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;CAChB;AAED,UAAU,gBAAgB;IACzB,UAAU,EAAE,OAAO,CAAC;IACpB,YAAY,EAAE,OAAO,CAAC;IACtB,WAAW,EAAE,OAAO,CAAC;IACrB,aAAa,EAAE,OAAO,CAAC;IACvB,YAAY,EAAE,OAAO,CAAC;IACtB,cAAc,EAAE,OAAO,CAAC;IACxB,cAAc,EAAE,OAAO,CAAC;CACxB;AAsID,MAAM,MAAM,YAAY,GAAG,gBAAgB,CAAC;AA+F5C,wBAAgB,yBAAyB,IAAI,mBAAmB,CAkI/D;AAED,MAAM,WAAW,mBAAmB;IACnC,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,IAAI,IAAI,CAAC;IAClB,QAAQ,IAAI,OAAO,CAAC;IACpB,GAAG,IAAI,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,MAAM,IAAI,IAAI,CAAC;IACf,cAAc,IAAI,IAAI,CAAC;IACvB,OAAO,IAAI,MAAM,CAAC;IAClB,KAAK,IAAI,IAAI,CAAC;CACd"}
@@ -0,0 +1,317 @@
1
+ /**
2
+ * Fake-timer core — swaps the global timer APIs for a deterministic
3
+ * scheduler. Mirrors the subset of Vitest we need:
4
+ * setTimeout / setInterval / setImmediate (+ clear*)
5
+ * advanceTimersByTime / runAllTimers / runOnlyPendingTimers
6
+ * performance.now() (when available)
7
+ *
8
+ * Zero dependency on `@sinonjs/fake-timers` — we re-implement the queue.
9
+ */
10
+ import { getRealNow } from "./system-time.js";
11
+ function getPerformance() {
12
+ const p = Reflect.get(globalThis, "performance");
13
+ if (p && typeof p === "object")
14
+ return p;
15
+ return undefined;
16
+ }
17
+ function captureGlobals() {
18
+ const perf = getPerformance();
19
+ return {
20
+ setTimeout: Reflect.get(globalThis, "setTimeout"),
21
+ clearTimeout: Reflect.get(globalThis, "clearTimeout"),
22
+ setInterval: Reflect.get(globalThis, "setInterval"),
23
+ clearInterval: Reflect.get(globalThis, "clearInterval"),
24
+ setImmediate: Reflect.get(globalThis, "setImmediate"),
25
+ clearImmediate: Reflect.get(globalThis, "clearImmediate"),
26
+ performanceNow: perf?.now?.bind(perf),
27
+ };
28
+ }
29
+ function installHandler(state) {
30
+ const originals = captureGlobals();
31
+ // Keep a reference to the real clearTimeout so we can fall back to it
32
+ // when a user passes a real `Timeout` handle that predates the install.
33
+ const realClearTimeout = typeof originals.clearTimeout === "function"
34
+ ? originals.clearTimeout
35
+ : undefined;
36
+ function scheduleTimeout(cb, ms, ...args) {
37
+ const ready = normaliseDelay(ms);
38
+ const timer = {
39
+ id: ++state.nextId,
40
+ dueMs: state.now + ready,
41
+ intervalMs: null,
42
+ callback: cb,
43
+ args,
44
+ kind: "timeout",
45
+ };
46
+ state.queue.push(timer);
47
+ return timer;
48
+ }
49
+ function scheduleInterval(cb, ms, ...args) {
50
+ const intervalMs = Math.max(1, normaliseDelay(ms, 1));
51
+ const timer = {
52
+ id: ++state.nextId,
53
+ dueMs: state.now + intervalMs,
54
+ intervalMs,
55
+ callback: cb,
56
+ args,
57
+ kind: "interval",
58
+ };
59
+ state.queue.push(timer);
60
+ return timer;
61
+ }
62
+ function scheduleImmediate(cb, ...args) {
63
+ const timer = {
64
+ id: ++state.nextId,
65
+ dueMs: state.now,
66
+ intervalMs: null,
67
+ callback: cb,
68
+ args,
69
+ kind: "immediate",
70
+ };
71
+ state.queue.push(timer);
72
+ return timer;
73
+ }
74
+ const clear = (idOrTimer) => {
75
+ if (idOrTimer == null)
76
+ return;
77
+ let id;
78
+ if (typeof idOrTimer === "number")
79
+ id = idOrTimer;
80
+ else if (typeof idOrTimer === "object") {
81
+ const maybe = idOrTimer.id;
82
+ if (typeof maybe === "number")
83
+ id = maybe;
84
+ }
85
+ if (id === undefined) {
86
+ // Not a Helix timer — likely a real `Timeout` handle from before
87
+ // fake timers were installed. Fall back to the real `clearTimeout`
88
+ // so it still gets cancelled.
89
+ realClearTimeout?.(idOrTimer);
90
+ return;
91
+ }
92
+ const idx = state.queue.findIndex((t) => t.id === id);
93
+ if (idx >= 0)
94
+ state.queue.splice(idx, 1);
95
+ };
96
+ Reflect.set(globalThis, "setTimeout", timerFacade(scheduleTimeout));
97
+ Reflect.set(globalThis, "setInterval", timerFacade(scheduleInterval));
98
+ Reflect.set(globalThis, "clearTimeout", clear);
99
+ Reflect.set(globalThis, "clearInterval", clear);
100
+ if (originals.setImmediate) {
101
+ Reflect.set(globalThis, "setImmediate", immediateFacade(scheduleImmediate));
102
+ Reflect.set(globalThis, "clearImmediate", clear);
103
+ }
104
+ // `performance.now()` tracks the fake clock when available.
105
+ const perf = getPerformance();
106
+ if (perf && typeof perf.now === "function") {
107
+ Reflect.set(perf, "now", () => state.now);
108
+ }
109
+ return originals;
110
+ }
111
+ function restoreHandler(originals) {
112
+ Reflect.set(globalThis, "setTimeout", originals.setTimeout);
113
+ Reflect.set(globalThis, "clearTimeout", originals.clearTimeout);
114
+ Reflect.set(globalThis, "setInterval", originals.setInterval);
115
+ Reflect.set(globalThis, "clearInterval", originals.clearInterval);
116
+ if (originals.setImmediate) {
117
+ Reflect.set(globalThis, "setImmediate", originals.setImmediate);
118
+ }
119
+ if (originals.clearImmediate) {
120
+ Reflect.set(globalThis, "clearImmediate", originals.clearImmediate);
121
+ }
122
+ const perf = getPerformance();
123
+ if (perf && typeof originals.performanceNow === "function") {
124
+ Reflect.set(perf, "now", originals.performanceNow);
125
+ }
126
+ }
127
+ /** Normalise the `ms` argument: NaN / undefined / negative → 0. */
128
+ function normaliseDelay(ms, floor = 0) {
129
+ if (ms === undefined || Number.isNaN(ms) || !Number.isFinite(ms))
130
+ return floor;
131
+ return Math.max(floor, ms);
132
+ }
133
+ function timerFacade(schedule) {
134
+ return Object.assign((cb, ms, ...args) => {
135
+ return makeHandle(schedule(cb, ms, ...args));
136
+ }, { __promisify__: promisifyTimer });
137
+ }
138
+ function immediateFacade(schedule) {
139
+ return Object.assign((cb, ...args) => {
140
+ return makeHandle(schedule(cb, ...args));
141
+ }, { __promisify__: promisifyImmediate });
142
+ }
143
+ /**
144
+ * `util.promisify(setTimeout)(ms, value)` must honour the fake clock — the
145
+ * Promise resolves only when `advanceTimersByTime(ms)` is called, not on
146
+ * the next microtask.
147
+ */
148
+ function promisifyTimer(ms, value) {
149
+ return new Promise((resolve) => {
150
+ globalThis.setTimeout(() => resolve(value), ms);
151
+ });
152
+ }
153
+ function promisifyImmediate(value) {
154
+ return new Promise((resolve) => {
155
+ const setI = Reflect.get(globalThis, "setImmediate");
156
+ if (typeof setI === "function") {
157
+ setI(() => resolve(value));
158
+ }
159
+ else {
160
+ // Browsers: fall back to setTimeout(0).
161
+ globalThis.setTimeout(() => resolve(value), 0);
162
+ }
163
+ });
164
+ }
165
+ /**
166
+ * Produce the object Node's `setTimeout` returns — enough for clear*()
167
+ * and ref/unref. `refresh` re-queues the timer starting from the current
168
+ * fake clock, matching Node's semantics.
169
+ */
170
+ function makeHandle(timer) {
171
+ const noop = () => timer;
172
+ Object.defineProperties(timer, {
173
+ ref: { value: noop, configurable: true },
174
+ unref: { value: noop, configurable: true },
175
+ hasRef: { value: () => true, configurable: true },
176
+ refresh: {
177
+ value: () => {
178
+ // Node's refresh: if not yet fired, reset due time from "now".
179
+ // We don't have direct access to the queue here (closure), so
180
+ // the real refresh happens inside `advanceBy`/`runAll` when the
181
+ // caller manipulates the handle. For now return the handle.
182
+ return timer;
183
+ },
184
+ configurable: true,
185
+ },
186
+ });
187
+ return timer;
188
+ }
189
+ export function createFakeTimerController() {
190
+ let state;
191
+ let originals;
192
+ function install(now) {
193
+ if (state) {
194
+ // Re-installation updates the clock in place so tests that call
195
+ // `vi.useFakeTimers({ now: X })` twice get the new epoch. Vitest
196
+ // parity.
197
+ state.now = now;
198
+ return;
199
+ }
200
+ state = { now, queue: [], nextId: 0, active: true };
201
+ originals = installHandler(state);
202
+ }
203
+ function uninstall() {
204
+ if (!state)
205
+ return;
206
+ if (originals)
207
+ restoreHandler(originals);
208
+ state = undefined;
209
+ originals = undefined;
210
+ }
211
+ function requireState() {
212
+ if (!state) {
213
+ throw new Error("vi: fake timers not installed. Call vi.useFakeTimers() first.");
214
+ }
215
+ return state;
216
+ }
217
+ function drainUntil(target) {
218
+ const s = requireState();
219
+ // Bound iterations to catch `setImmediate(() => setImmediate(...))`
220
+ // infinite loops in `advanceTimersByTime(0)` / `runAll`.
221
+ let iterations = 0;
222
+ const MAX = 10_000;
223
+ while (true) {
224
+ if (++iterations > MAX) {
225
+ throw new Error(`vi: fake-timer drain exceeded ${MAX} iterations — likely a self-rescheduling immediate / interval. Use runOnlyPendingTimers or clearAllTimers.`);
226
+ }
227
+ let nextIdx = -1;
228
+ let nextDue = Number.POSITIVE_INFINITY;
229
+ for (let i = 0; i < s.queue.length; i += 1) {
230
+ const t = s.queue[i];
231
+ if (t.dueMs <= target && t.dueMs < nextDue) {
232
+ nextDue = t.dueMs;
233
+ nextIdx = i;
234
+ }
235
+ }
236
+ if (nextIdx < 0)
237
+ break;
238
+ const timer = s.queue[nextIdx];
239
+ s.queue.splice(nextIdx, 1);
240
+ s.now = Math.max(s.now, timer.dueMs);
241
+ if (timer.intervalMs !== null) {
242
+ s.queue.push({ ...timer, dueMs: timer.dueMs + timer.intervalMs });
243
+ }
244
+ // Advance clock to target BEFORE firing: if the callback throws,
245
+ // the wall clock has already been moved so subsequent drains see
246
+ // the state the user asked for.
247
+ const latestDue = timer.dueMs;
248
+ try {
249
+ timer.callback(...timer.args);
250
+ }
251
+ catch (err) {
252
+ // Set `now` to the target so callers can resume after catching.
253
+ if (s.now < target)
254
+ s.now = target;
255
+ throw err;
256
+ }
257
+ }
258
+ if (s.now < target)
259
+ s.now = target;
260
+ }
261
+ return {
262
+ install,
263
+ uninstall,
264
+ isActive: () => state?.active === true,
265
+ now: () => state?.now ?? getRealNow(),
266
+ advanceBy(ms) {
267
+ if (ms < 0) {
268
+ throw new Error(`vi.advanceTimersByTime: negative delta (${ms}) is not supported`);
269
+ }
270
+ const s = requireState();
271
+ drainUntil(s.now + ms);
272
+ },
273
+ runAll() {
274
+ const s = requireState();
275
+ let iterations = 0;
276
+ while (s.queue.length > 0) {
277
+ iterations += 1;
278
+ if (iterations > 10_000) {
279
+ throw new Error("vi.runAllTimers: exceeded 10 000 iterations — likely a self-rescheduling interval or immediate. Use runOnlyPendingTimers or advanceTimersByTime instead.");
280
+ }
281
+ let earliest = Number.POSITIVE_INFINITY;
282
+ for (const t of s.queue) {
283
+ if (t.dueMs < earliest)
284
+ earliest = t.dueMs;
285
+ }
286
+ if (!Number.isFinite(earliest))
287
+ break;
288
+ drainUntil(earliest);
289
+ }
290
+ },
291
+ runOnlyPending() {
292
+ const s = requireState();
293
+ // Sort the snapshot by due time so callbacks fire in schedule order,
294
+ // not insertion order (Vitest parity).
295
+ const snapshot = [...s.queue].sort((a, b) => a.dueMs - b.dueMs);
296
+ for (const timer of snapshot) {
297
+ const idx = s.queue.indexOf(timer);
298
+ if (idx < 0)
299
+ continue;
300
+ s.queue.splice(idx, 1);
301
+ s.now = Math.max(s.now, timer.dueMs);
302
+ if (timer.intervalMs !== null) {
303
+ s.queue.push({ ...timer, dueMs: timer.dueMs + timer.intervalMs });
304
+ }
305
+ timer.callback(...timer.args);
306
+ }
307
+ },
308
+ pending() {
309
+ return state ? state.queue.length : 0;
310
+ },
311
+ clear() {
312
+ if (state)
313
+ state.queue.length = 0;
314
+ },
315
+ };
316
+ }
317
+ //# sourceMappingURL=fake-timers.js.map