@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,121 @@
1
+ /**
2
+ * `vi.spyOn(obj, key)` — replaces `obj[key]` with a spy that calls through
3
+ * to the original. `spy.mockRestore()` puts the original back.
4
+ *
5
+ * Supports:
6
+ * - data properties whose value is a function (most methods);
7
+ * - accessor properties (`get` / `set`);
8
+ * - methods inherited via the prototype chain (the spy is installed as an
9
+ * own property on the target, and `mockRestore()` deletes the own
10
+ * property so inheritance resumes).
11
+ *
12
+ * `this` binding: the spy's default implementation uses a `function` form
13
+ * (not arrow) so the caller's receiver is forwarded to the original. This
14
+ * is essential when spying on a prototype method that depends on `this`.
15
+ *
16
+ * Double-spy detection: throws if the property is already a Helix spy, to
17
+ * avoid the subtle double-counting bug where the inner spy's `calls` is
18
+ * incremented by pass-through from the outer one.
19
+ */
20
+ import { createSpy, isSpy } from "./spy.js";
21
+ function findDescriptor(obj, key) {
22
+ let cursor = obj;
23
+ while (cursor !== null) {
24
+ const desc = Object.getOwnPropertyDescriptor(cursor, key);
25
+ if (desc) {
26
+ return { descriptor: desc, onPrototype: cursor !== obj };
27
+ }
28
+ cursor = Object.getPrototypeOf(cursor);
29
+ }
30
+ throw new Error(`vi.spyOn: property "${String(key)}" does not exist on the target`);
31
+ }
32
+ function buildRestore(obj, key, ownDescBefore, onPrototype) {
33
+ return () => {
34
+ if (onPrototype && !ownDescBefore) {
35
+ Reflect.deleteProperty(obj, key);
36
+ return;
37
+ }
38
+ if (ownDescBefore) {
39
+ Object.defineProperty(obj, key, ownDescBefore);
40
+ }
41
+ };
42
+ }
43
+ export function spyOn(obj, key, options = {}) {
44
+ const label = String(key);
45
+ const { descriptor, onPrototype } = findDescriptor(obj, key);
46
+ const ownDescBefore = Object.getOwnPropertyDescriptor(obj, key);
47
+ // Double-spy guard: refuse to spy on an already-spied value.
48
+ if (descriptor.value !== undefined && isSpy(descriptor.value)) {
49
+ throw new Error(`vi.spyOn: "${label}" is already a spy. Call mockRestore() before spying again.`);
50
+ }
51
+ const restore = buildRestore(obj, key, ownDescBefore, onPrototype);
52
+ if (descriptor.get || descriptor.set) {
53
+ const kind = options.accessor ?? "get";
54
+ const originalGet = descriptor.get;
55
+ const originalSet = descriptor.set;
56
+ if (kind === "get") {
57
+ if (!originalGet) {
58
+ throw new Error(`vi.spyOn: "${label}" has no getter (pass { accessor: "set" } to spy on the setter)`);
59
+ }
60
+ // Use `function` so the shim + spy forward the caller's `this`.
61
+ const spy = createSpy({
62
+ name: label,
63
+ defaultImplementation: function () {
64
+ return originalGet.call(this);
65
+ },
66
+ });
67
+ Object.defineProperty(obj, key, {
68
+ configurable: true,
69
+ enumerable: descriptor.enumerable ?? true,
70
+ get() {
71
+ return spy.call(this);
72
+ },
73
+ set: originalSet,
74
+ });
75
+ spy.__setRestore(restore);
76
+ return spy;
77
+ }
78
+ if (!originalSet) {
79
+ throw new Error(`vi.spyOn: "${label}" has no setter`);
80
+ }
81
+ const spy = createSpy({
82
+ name: label,
83
+ defaultImplementation: function (v) {
84
+ originalSet.call(this, v);
85
+ },
86
+ });
87
+ Object.defineProperty(obj, key, {
88
+ configurable: true,
89
+ enumerable: descriptor.enumerable ?? true,
90
+ get: originalGet,
91
+ set(v) {
92
+ spy.call(this, v);
93
+ },
94
+ });
95
+ spy.__setRestore(restore);
96
+ return spy;
97
+ }
98
+ const originalValue = descriptor.value;
99
+ if (typeof originalValue !== "function") {
100
+ throw new Error(`vi.spyOn: "${label}" is not a function (got ${typeof originalValue}). Pass { accessor } for accessors.`);
101
+ }
102
+ const originalFn = originalValue;
103
+ const spy = createSpy({
104
+ name: label,
105
+ defaultImplementation: function (...args) {
106
+ // `this` is the call-site receiver (e.g. the class instance); the
107
+ // original method is forwarded its real receiver. Fixes the bug
108
+ // where `spyOn(Proto, 'method')` used to hard-bind `this = Proto`.
109
+ return originalFn.apply(this, args);
110
+ },
111
+ });
112
+ Object.defineProperty(obj, key, {
113
+ configurable: true,
114
+ enumerable: descriptor.enumerable ?? true,
115
+ writable: descriptor.writable ?? true,
116
+ value: spy,
117
+ });
118
+ spy.__setRestore(restore);
119
+ return spy;
120
+ }
121
+ //# sourceMappingURL=spyOn.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spyOn.js","sourceRoot":"","sources":["../../../src/runtime/vi/spyOn.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAc,SAAS,EAAE,KAAK,EAAY,MAAM,UAAU,CAAC;AASlE,SAAS,cAAc,CACtB,GAAW,EACX,GAAgB;IAEhB,IAAI,MAAM,GAAkB,GAAG,CAAC;IAChC,OAAO,MAAM,KAAK,IAAI,EAAE,CAAC;QACxB,MAAM,IAAI,GAAG,MAAM,CAAC,wBAAwB,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAC1D,IAAI,IAAI,EAAE,CAAC;YACV,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,KAAK,GAAG,EAAE,CAAC;QAC1D,CAAC;QACD,MAAM,GAAG,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC,CAAC;IACD,MAAM,IAAI,KAAK,CACd,uBAAuB,MAAM,CAAC,GAAG,CAAC,gCAAgC,CAClE,CAAC;AACH,CAAC;AAED,SAAS,YAAY,CACpB,GAAW,EACX,GAAgB,EAChB,aAA6C,EAC7C,WAAoB;IAEpB,OAAO,GAAG,EAAE;QACX,IAAI,WAAW,IAAI,CAAC,aAAa,EAAE,CAAC;YACnC,OAAO,CAAC,cAAc,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;YACjC,OAAO;QACR,CAAC;QACD,IAAI,aAAa,EAAE,CAAC;YACnB,MAAM,CAAC,cAAc,CAAC,GAAG,EAAE,GAAG,EAAE,aAAa,CAAC,CAAC;QAChD,CAAC;IACF,CAAC,CAAC;AACH,CAAC;AAED,MAAM,UAAU,KAAK,CACpB,GAAQ,EACR,GAAQ,EACR,UAAwB,EAAE;IAE1B,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IAC1B,MAAM,EAAE,UAAU,EAAE,WAAW,EAAE,GAAG,cAAc,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAC7D,MAAM,aAAa,GAAG,MAAM,CAAC,wBAAwB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAEhE,6DAA6D;IAC7D,IAAI,UAAU,CAAC,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;QAC/D,MAAM,IAAI,KAAK,CACd,cAAc,KAAK,6DAA6D,CAChF,CAAC;IACH,CAAC;IAED,MAAM,OAAO,GAAG,YAAY,CAAC,GAAG,EAAE,GAAG,EAAE,aAAa,EAAE,WAAW,CAAC,CAAC;IAEnE,IAAI,UAAU,CAAC,GAAG,IAAI,UAAU,CAAC,GAAG,EAAE,CAAC;QACtC,MAAM,IAAI,GAAiB,OAAO,CAAC,QAAQ,IAAI,KAAK,CAAC;QACrD,MAAM,WAAW,GAAG,UAAU,CAAC,GAAG,CAAC;QACnC,MAAM,WAAW,GAAG,UAAU,CAAC,GAAG,CAAC;QACnC,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;YACpB,IAAI,CAAC,WAAW,EAAE,CAAC;gBAClB,MAAM,IAAI,KAAK,CACd,cAAc,KAAK,iEAAiE,CACpF,CAAC;YACH,CAAC;YACD,gEAAgE;YAChE,MAAM,GAAG,GAAG,SAAS,CAAC;gBACrB,IAAI,EAAE,KAAK;gBACX,qBAAqB,EAAE;oBACtB,OAAO,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBAC/B,CAAC;aACD,CAAC,CAAC;YACH,MAAM,CAAC,cAAc,CAAC,GAAG,EAAE,GAAG,EAAE;gBAC/B,YAAY,EAAE,IAAI;gBAClB,UAAU,EAAE,UAAU,CAAC,UAAU,IAAI,IAAI;gBACzC,GAAG;oBACF,OAAO,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACvB,CAAC;gBACD,GAAG,EAAE,WAAW;aAChB,CAAC,CAAC;YACH,GAAG,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;YAC1B,OAAO,GAAG,CAAC;QACZ,CAAC;QACD,IAAI,CAAC,WAAW,EAAE,CAAC;YAClB,MAAM,IAAI,KAAK,CAAC,cAAc,KAAK,iBAAiB,CAAC,CAAC;QACvD,CAAC;QACD,MAAM,GAAG,GAAG,SAAS,CAAuB;YAC3C,IAAI,EAAE,KAAK;YACX,qBAAqB,EAAE,UAAyB,CAAU;gBACzD,WAAW,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;YAC3B,CAAC;SACD,CAAC,CAAC;QACH,MAAM,CAAC,cAAc,CAAC,GAAG,EAAE,GAAG,EAAE;YAC/B,YAAY,EAAE,IAAI;YAClB,UAAU,EAAE,UAAU,CAAC,UAAU,IAAI,IAAI;YACzC,GAAG,EAAE,WAAW;YAChB,GAAG,CAAgB,CAAU;gBAC5B,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;YACnB,CAAC;SACD,CAAC,CAAC;QACH,GAAG,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;QAC1B,OAAO,GAAG,CAAC;IACZ,CAAC;IAED,MAAM,aAAa,GAAG,UAAU,CAAC,KAAK,CAAC;IACvC,IAAI,OAAO,aAAa,KAAK,UAAU,EAAE,CAAC;QACzC,MAAM,IAAI,KAAK,CACd,cAAc,KAAK,4BAA4B,OAAO,aAAa,qCAAqC,CACxG,CAAC;IACH,CAAC;IACD,MAAM,UAAU,GAAG,aAAsB,CAAC;IAC1C,MAAM,GAAG,GAAG,SAAS,CAAC;QACrB,IAAI,EAAE,KAAK;QACX,qBAAqB,EAAE,UAAyB,GAAG,IAAe;YACjE,kEAAkE;YAClE,gEAAgE;YAChE,mEAAmE;YACnE,OAAO,UAAU,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACrC,CAAC;KACD,CAAC,CAAC;IACH,MAAM,CAAC,cAAc,CAAC,GAAG,EAAE,GAAG,EAAE;QAC/B,YAAY,EAAE,IAAI;QAClB,UAAU,EAAE,UAAU,CAAC,UAAU,IAAI,IAAI;QACzC,QAAQ,EAAE,UAAU,CAAC,QAAQ,IAAI,IAAI;QACrC,KAAK,EAAE,GAAG;KACV,CAAC,CAAC;IACH,GAAG,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;IAC1B,OAAO,GAAG,CAAC;AACZ,CAAC"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * System-time control — pins `Date.now()` and `new Date()` to a fake
3
+ * epoch. Each `ViContext` owns its own `SystemClock` instance; the Date
4
+ * shim read-through to `context.fakeEpoch`, so concurrent test files
5
+ * never share wall-clock state (AC 6 isolation).
6
+ *
7
+ * The shim is installed globally on first activation across ANY context
8
+ * (Date is a singleton per realm), but the shim ALWAYS reads from the
9
+ * currently-active context via the `readContext` resolver passed in. When
10
+ * all contexts deactivate, the shim uninstalls.
11
+ */
12
+ export interface SystemClock {
13
+ /** Fake epoch in ms, or `null` if this context is on real time. */
14
+ fakeEpoch: number | null;
15
+ }
16
+ export declare function createSystemClock(): SystemClock;
17
+ /**
18
+ * Register one context's interest in the Date shim. Returns an
19
+ * unregister function.
20
+ */
21
+ type ContextResolver = () => SystemClock | undefined;
22
+ export declare function registerSystemClockContext(resolver: ContextResolver): () => void;
23
+ export declare function setFakeEpoch(clock: SystemClock, time: Date | number): void;
24
+ export declare function clearFakeEpoch(clock: SystemClock): void;
25
+ /** Get the original `Date.now` even after the shim is installed. */
26
+ export declare function getRealNow(): number;
27
+ /** Get the original `Date` constructor. */
28
+ export declare function getRealDate(): DateConstructor;
29
+ export {};
30
+ //# sourceMappingURL=system-time.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"system-time.d.ts","sourceRoot":"","sources":["../../../src/runtime/vi/system-time.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,MAAM,WAAW,WAAW;IAC3B,mEAAmE;IACnE,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,wBAAgB,iBAAiB,IAAI,WAAW,CAE/C;AAED;;;GAGG;AACH,KAAK,eAAe,GAAG,MAAM,WAAW,GAAG,SAAS,CAAC;AAerD,wBAAgB,0BAA0B,CACzC,QAAQ,EAAE,eAAe,GACvB,MAAM,IAAI,CAOZ;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,CAe1E;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,CAEvD;AAuCD,oEAAoE;AACpE,wBAAgB,UAAU,IAAI,MAAM,CAEnC;AAED,2CAA2C;AAC3C,wBAAgB,WAAW,IAAI,eAAe,CAE7C"}
@@ -0,0 +1,100 @@
1
+ /**
2
+ * System-time control — pins `Date.now()` and `new Date()` to a fake
3
+ * epoch. Each `ViContext` owns its own `SystemClock` instance; the Date
4
+ * shim read-through to `context.fakeEpoch`, so concurrent test files
5
+ * never share wall-clock state (AC 6 isolation).
6
+ *
7
+ * The shim is installed globally on first activation across ANY context
8
+ * (Date is a singleton per realm), but the shim ALWAYS reads from the
9
+ * currently-active context via the `readContext` resolver passed in. When
10
+ * all contexts deactivate, the shim uninstalls.
11
+ */
12
+ const OriginalDate = globalThis.Date;
13
+ const originalNow = OriginalDate.now.bind(OriginalDate);
14
+ export function createSystemClock() {
15
+ return { fakeEpoch: null };
16
+ }
17
+ const resolvers = new Set();
18
+ let installed = false;
19
+ function resolveEpoch() {
20
+ for (const resolve of resolvers) {
21
+ const ctx = resolve();
22
+ if (ctx?.fakeEpoch !== null && ctx?.fakeEpoch !== undefined) {
23
+ return ctx.fakeEpoch;
24
+ }
25
+ }
26
+ return null;
27
+ }
28
+ export function registerSystemClockContext(resolver) {
29
+ resolvers.add(resolver);
30
+ install();
31
+ return () => {
32
+ resolvers.delete(resolver);
33
+ if (resolvers.size === 0)
34
+ restore();
35
+ };
36
+ }
37
+ export function setFakeEpoch(clock, time) {
38
+ let epoch;
39
+ if (time instanceof OriginalDate) {
40
+ epoch = time.getTime();
41
+ }
42
+ else if (typeof time === "number") {
43
+ epoch = time;
44
+ }
45
+ else {
46
+ throw new Error(`vi.setSystemTime: expected Date or number, got ${typeof time}`);
47
+ }
48
+ if (!Number.isFinite(epoch)) {
49
+ throw new Error(`vi.setSystemTime: expected a finite epoch, got ${epoch}`);
50
+ }
51
+ clock.fakeEpoch = epoch;
52
+ }
53
+ export function clearFakeEpoch(clock) {
54
+ clock.fakeEpoch = null;
55
+ }
56
+ function buildShim() {
57
+ const shim = function DateShim(...args) {
58
+ const epoch = resolveEpoch();
59
+ // Called without `new`: return a toString of the pinned (or real) time.
60
+ if (!new.target) {
61
+ return epoch !== null
62
+ ? new OriginalDate(epoch).toString()
63
+ : new OriginalDate().toString();
64
+ }
65
+ if (args.length === 0 && epoch !== null) {
66
+ return new OriginalDate(epoch);
67
+ }
68
+ return Reflect.construct(OriginalDate, args);
69
+ };
70
+ shim.prototype = OriginalDate.prototype;
71
+ Object.defineProperty(shim, "name", { value: "Date", configurable: true });
72
+ Reflect.set(shim, "now", () => {
73
+ const epoch = resolveEpoch();
74
+ return epoch !== null ? epoch : originalNow();
75
+ });
76
+ Reflect.set(shim, "parse", OriginalDate.parse.bind(OriginalDate));
77
+ Reflect.set(shim, "UTC", OriginalDate.UTC.bind(OriginalDate));
78
+ return shim;
79
+ }
80
+ function install() {
81
+ if (installed)
82
+ return;
83
+ installed = true;
84
+ Reflect.set(globalThis, "Date", buildShim());
85
+ }
86
+ function restore() {
87
+ if (!installed)
88
+ return;
89
+ installed = false;
90
+ Reflect.set(globalThis, "Date", OriginalDate);
91
+ }
92
+ /** Get the original `Date.now` even after the shim is installed. */
93
+ export function getRealNow() {
94
+ return originalNow();
95
+ }
96
+ /** Get the original `Date` constructor. */
97
+ export function getRealDate() {
98
+ return OriginalDate;
99
+ }
100
+ //# sourceMappingURL=system-time.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"system-time.js","sourceRoot":"","sources":["../../../src/runtime/vi/system-time.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Worker entry — loads a single test file, collects its `describe`/`test`
3
+ * declarations, executes them, and emits a `FileResult`.
4
+ *
5
+ * Usage modes:
6
+ * 1. Direct (unit tests): `runTestFile("/abs/path.test.ts")` → `FileResult`
7
+ * 2. Child process (orchestrator): `main()` reads file paths from IPC
8
+ * messages and replies on `process.send`. Exactly one `main()` runs
9
+ * per worker process (guarded); IPC runs are serialized so concurrent
10
+ * messages cannot race on internal state.
11
+ *
12
+ * File collection is scoped per invocation via AsyncLocalStorage (see
13
+ * `suite.ts#withCollection`) — no shared mutable state across concurrent
14
+ * `runTestFile` calls.
15
+ */
16
+ import { type ExecuteOptions, type FileResult } from "./run.js";
17
+ export interface RunFileOptions extends ExecuteOptions {
18
+ /**
19
+ * Bust the ESM module cache so repeated runs of the same path re-execute
20
+ * the file body and re-collect tests. Default: true.
21
+ */
22
+ freshImport?: boolean;
23
+ }
24
+ export declare function runTestFile(absolutePath: string, options?: RunFileOptions): Promise<FileResult>;
25
+ export declare function main(): Promise<void>;
26
+ //# sourceMappingURL=worker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../../src/runtime/worker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,OAAO,EAAE,KAAK,cAAc,EAAe,KAAK,UAAU,EAAE,MAAM,UAAU,CAAC;AAI7E,MAAM,WAAW,cAAe,SAAQ,cAAc;IACrD;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACtB;AAuFD,wBAAsB,WAAW,CAChC,YAAY,EAAE,MAAM,EACpB,OAAO,GAAE,cAAmB,GAC1B,OAAO,CAAC,UAAU,CAAC,CAmBrB;AA2CD,wBAAsB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CA0D1C"}
@@ -0,0 +1,195 @@
1
+ /**
2
+ * Worker entry — loads a single test file, collects its `describe`/`test`
3
+ * declarations, executes them, and emits a `FileResult`.
4
+ *
5
+ * Usage modes:
6
+ * 1. Direct (unit tests): `runTestFile("/abs/path.test.ts")` → `FileResult`
7
+ * 2. Child process (orchestrator): `main()` reads file paths from IPC
8
+ * messages and replies on `process.send`. Exactly one `main()` runs
9
+ * per worker process (guarded); IPC runs are serialized so concurrent
10
+ * messages cannot race on internal state.
11
+ *
12
+ * File collection is scoped per invocation via AsyncLocalStorage (see
13
+ * `suite.ts#withCollection`) — no shared mutable state across concurrent
14
+ * `runTestFile` calls.
15
+ */
16
+ import path from "node:path";
17
+ import { pathToFileURL } from "node:url";
18
+ import { executeRoot } from "./run.js";
19
+ import { withCollection } from "./suite.js";
20
+ import { withViContext } from "./vi/index.js";
21
+ function assertAbsolute(p) {
22
+ if (!path.isAbsolute(p)) {
23
+ throw new Error(`runTestFile: expected absolute path, got "${p}". Resolve against cwd or __dirname before calling.`);
24
+ }
25
+ }
26
+ /** Turn values that `JSON.stringify` rejects into readable fallbacks. */
27
+ function safeValue(v, seen = new WeakSet()) {
28
+ if (v === null || v === undefined)
29
+ return v;
30
+ if (typeof v === "bigint")
31
+ return `${v}n`;
32
+ if (typeof v === "function")
33
+ return `[Function ${v.name || "anonymous"}]`;
34
+ if (typeof v === "symbol")
35
+ return v.toString();
36
+ if (typeof v !== "object")
37
+ return v;
38
+ if (seen.has(v))
39
+ return "[Circular]";
40
+ seen.add(v);
41
+ if (Array.isArray(v))
42
+ return v.map((item) => safeValue(item, seen));
43
+ if (v instanceof Map) {
44
+ const out = [];
45
+ for (const [k, val] of v)
46
+ out.push([safeValue(k, seen), safeValue(val, seen)]);
47
+ return { __type: "Map", entries: out };
48
+ }
49
+ if (v instanceof Set) {
50
+ return { __type: "Set", values: [...v].map((x) => safeValue(x, seen)) };
51
+ }
52
+ if (v instanceof Date)
53
+ return { __type: "Date", iso: v.toISOString() };
54
+ if (v instanceof RegExp)
55
+ return { __type: "RegExp", src: v.source, flags: v.flags };
56
+ if (v instanceof Error) {
57
+ return {
58
+ __type: "Error",
59
+ name: v.name,
60
+ message: v.message,
61
+ stack: v.stack,
62
+ };
63
+ }
64
+ const rec = {};
65
+ for (const key of Object.keys(v)) {
66
+ rec[key] = safeValue(Reflect.get(v, key), seen);
67
+ }
68
+ return rec;
69
+ }
70
+ function sanitizeTest(t) {
71
+ if (!t.error)
72
+ return t;
73
+ return {
74
+ ...t,
75
+ error: {
76
+ ...t.error,
77
+ actual: safeValue(t.error.actual),
78
+ expected: safeValue(t.error.expected),
79
+ },
80
+ };
81
+ }
82
+ function sanitizeSuite(s) {
83
+ return {
84
+ ...s,
85
+ hookErrors: s.hookErrors.map((e) => ({
86
+ ...e,
87
+ actual: safeValue(e.actual),
88
+ expected: safeValue(e.expected),
89
+ })),
90
+ children: s.children.map((c) => "children" in c ? sanitizeSuite(c) : sanitizeTest(c)),
91
+ };
92
+ }
93
+ function sanitize(result) {
94
+ return {
95
+ ...result,
96
+ tests: result.tests.map(sanitizeTest),
97
+ suites: result.suites.map(sanitizeSuite),
98
+ };
99
+ }
100
+ let importCounter = 0;
101
+ export async function runTestFile(absolutePath, options = {}) {
102
+ assertAbsolute(absolutePath);
103
+ const baseUrl = pathToFileURL(absolutePath).href;
104
+ // Cache-busting query param so the ESM loader re-evaluates the module on
105
+ // every call — otherwise `describe`/`test` would only register on the
106
+ // first call and subsequent runs would see an empty suite tree.
107
+ const url = options.freshImport === false
108
+ ? baseUrl
109
+ : `${baseUrl}?helix=${Date.now()}-${++importCounter}`;
110
+ return withViContext(async () => {
111
+ const root = await withCollection(async () => {
112
+ await import(url);
113
+ });
114
+ const raw = await executeRoot(root, absolutePath, {
115
+ timeoutMs: options.timeoutMs,
116
+ });
117
+ return sanitize(raw);
118
+ });
119
+ }
120
+ const FRAME_PREFIX = "__HELIX_RESULT__";
121
+ function send(msg) {
122
+ if (typeof process.send === "function") {
123
+ process.send(msg);
124
+ return;
125
+ }
126
+ // Fallback: framed line on stderr so it doesn't collide with test
127
+ // console.log output on stdout. Parent parses lines starting with the
128
+ // `FRAME_PREFIX` magic.
129
+ process.stderr.write(`${FRAME_PREFIX}${JSON.stringify(msg)}\n`);
130
+ }
131
+ function isWorkerIncoming(v) {
132
+ if (!v || typeof v !== "object")
133
+ return false;
134
+ const r = v;
135
+ return r.type === "run" && typeof r.file === "string";
136
+ }
137
+ let mainStarted = false;
138
+ export async function main() {
139
+ if (mainStarted)
140
+ return;
141
+ mainStarted = true;
142
+ // Unhandled rejections from test code (e.g. a dangling Promise.reject)
143
+ // would crash the worker on Node 15+ or leak silently. Log them and keep
144
+ // the process alive so currently-running tests can complete.
145
+ process.on("unhandledRejection", (reason) => {
146
+ process.stderr.write(`${FRAME_PREFIX}${JSON.stringify({
147
+ type: "error",
148
+ file: undefined,
149
+ message: `unhandledRejection: ${reason instanceof Error ? reason.message : String(reason)}`,
150
+ stack: reason instanceof Error ? reason.stack : undefined,
151
+ })}\n`);
152
+ });
153
+ // IPC mode: parent drives us via `{ type: "run", file }` messages.
154
+ if (typeof process.send === "function") {
155
+ let pending = Promise.resolve();
156
+ process.on("message", (raw) => {
157
+ if (!isWorkerIncoming(raw))
158
+ return;
159
+ const msg = raw;
160
+ // Serialize IPC runs: a second message waits for the first to
161
+ // finish so module-scoped watchers / handlers don't race.
162
+ pending = pending
163
+ .then(() => runTestFile(msg.file, { timeoutMs: msg.timeoutMs }))
164
+ .then((result) => send({ type: "result", result }))
165
+ .catch((err) => {
166
+ const e = err instanceof Error ? err : new Error(String(err));
167
+ send({
168
+ type: "error",
169
+ file: msg.file,
170
+ message: e.message,
171
+ stack: e.stack,
172
+ });
173
+ });
174
+ });
175
+ return;
176
+ }
177
+ // CLI fallback: `node worker.js <file>`.
178
+ const file = process.argv[2];
179
+ if (!file) {
180
+ process.stderr.write("helix worker: missing file arg\n");
181
+ process.exit(2);
182
+ }
183
+ try {
184
+ const abs = path.resolve(file);
185
+ const result = await runTestFile(abs);
186
+ send({ type: "result", result });
187
+ process.exit(result.totals.fail > 0 ? 1 : 0);
188
+ }
189
+ catch (err) {
190
+ const e = err instanceof Error ? err : new Error(String(err));
191
+ send({ type: "error", file, message: e.message, stack: e.stack });
192
+ process.exit(2);
193
+ }
194
+ }
195
+ //# sourceMappingURL=worker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker.js","sourceRoot":"","sources":["../../src/runtime/worker.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Time freeze / travel — ergonomic surface over `vi.setSystemTime`
3
+ * with per-test auto-restore.
4
+ *
5
+ * **Helix is agnostic — no cross-package imports.** This module
6
+ * never imports `@c9up/chronos`. The clock pin happens entirely
7
+ * through `vi.setSystemTime` (which shims `globalThis.Date`); any
8
+ * code in user-land that calls `new Date()` — chronos `DateTime`
9
+ * included — picks up the frozen epoch automatically.
10
+ *
11
+ * Calendar arithmetic for `month` / `year` uses `Date.UTC` math
12
+ * locally so we don't take a runtime dependency on chronos.
13
+ *
14
+ * **Concurrency note.** `vi.setSystemTime` writes to the `vi`
15
+ * context that wraps the current test FILE (`withViContext`), not
16
+ * the per-test frame (`withTestContext`). Two `withTestContext`
17
+ * frames running in parallel inside the same file (e.g. via
18
+ * `Promise.all([withTestContext(A), withTestContext(B)])`) share
19
+ * one Date shim — A's freeze leaks into B. The helix runtime
20
+ * executes tests strictly sequentially (`runSuite`'s for-await
21
+ * loop), so this is not reachable through the documented test
22
+ * path. Callers who manually compose `withTestContext` in parallel
23
+ * inside a single file must serialise their freezes themselves.
24
+ */
25
+ /** Anything with a `toMillis()` method (e.g. chronos `DateTime`,
26
+ * Luxon `DateTime`) is accepted via duck typing — no import. */
27
+ interface MillisLike {
28
+ toMillis(): number;
29
+ }
30
+ /** Inputs accepted by `freeze` / `travelTo`. */
31
+ export type TimeInput = string | number | Date | MillisLike;
32
+ /** Calendar units supported by `time.travel`. `ms` is the bare-metal
33
+ * fallback; `month` and `year` use `Date.UTC` math; the rest are pure
34
+ * ms multipliers. UTC-only — tests should freeze to a UTC instant. */
35
+ export type TimeUnit = "ms" | "second" | "minute" | "hour" | "day" | "week" | "month" | "year";
36
+ /**
37
+ * Pin `Date.now()` / `new Date()` (and anything that reads through
38
+ * them — e.g. `DateTime.now()`) to the given moment. Auto-restored
39
+ * at end-of-test via the helix test frame, so a forgotten
40
+ * `unfreeze()` does not leak into the next test.
41
+ *
42
+ * Throws when called outside a test frame — a queued cleanup would
43
+ * never fire there, so the freeze would leak across tests silently.
44
+ */
45
+ export declare function freeze(input: TimeInput): void;
46
+ /**
47
+ * Advance the clock by `amount` × `unit`. If the clock isn't frozen
48
+ * yet, freezes it relative to the real `Date.now()` first, then
49
+ * applies the delta — so a one-shot `time.travel(7, 'day')` works
50
+ * without an explicit `freeze()`.
51
+ *
52
+ * Always queues a cleanup so the auto-restore path is uniform —
53
+ * cleanups are idempotent (each calls `vi.useRealSystemTime`),
54
+ * accumulating closures across many `travel` calls in one test is
55
+ * intentional and cheap.
56
+ *
57
+ * Throws when called outside a test frame, same reason as `freeze`.
58
+ */
59
+ export declare function travel(amount: number, unit?: TimeUnit): void;
60
+ /**
61
+ * Travel TO a specific moment. Equivalent to `freeze(target)` —
62
+ * exists as an explicit verb so "travel to 2026" reads better than
63
+ * "freeze at 2026" in tests.
64
+ */
65
+ export declare function travelTo(target: TimeInput): void;
66
+ /**
67
+ * Restore the real system clock immediately. Idempotent — safe to
68
+ * call when not currently frozen. Auto-restore still fires at
69
+ * end-of-test, so calling `unfreeze` early is purely a
70
+ * test-readability choice.
71
+ */
72
+ export declare function unfreeze(): void;
73
+ /**
74
+ * Read the currently frozen epoch, or `null` when running on real
75
+ * time. Useful for tests that compose `freeze` with their own clock
76
+ * arithmetic.
77
+ */
78
+ export declare function frozenAt(): number | null;
79
+ export {};
80
+ //# sourceMappingURL=freeze.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"freeze.d.ts","sourceRoot":"","sources":["../../src/time/freeze.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAKH;iEACiE;AACjE,UAAU,UAAU;IACnB,QAAQ,IAAI,MAAM,CAAC;CACnB;AAED,gDAAgD;AAChD,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,UAAU,CAAC;AAE5D;;uEAEuE;AACvE,MAAM,MAAM,QAAQ,GACjB,IAAI,GACJ,QAAQ,GACR,QAAQ,GACR,MAAM,GACN,KAAK,GACL,MAAM,GACN,OAAO,GACP,MAAM,CAAC;AAOV;;;;;;;;GAQG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,CAO7C;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,GAAE,QAAe,GAAG,IAAI,CAqBlE;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAEhD;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,IAAI,IAAI,CAE/B;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,IAAI,MAAM,GAAG,IAAI,CAExC"}