@ricsam/isolate 0.0.1 → 0.1.1

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 (299) hide show
  1. package/README.md +243 -34
  2. package/dist/cjs/bridge/diagnostics.cjs +58 -0
  3. package/dist/cjs/bridge/diagnostics.cjs.map +10 -0
  4. package/dist/cjs/bridge/legacy-adapters.cjs +242 -0
  5. package/dist/cjs/bridge/legacy-adapters.cjs.map +10 -0
  6. package/dist/cjs/bridge/request-context.cjs +59 -0
  7. package/dist/cjs/bridge/request-context.cjs.map +10 -0
  8. package/dist/cjs/bridge/runtime-bindings.cjs +367 -0
  9. package/dist/cjs/bridge/runtime-bindings.cjs.map +10 -0
  10. package/dist/cjs/browser/browser-runtime.cjs +157 -0
  11. package/dist/cjs/browser/browser-runtime.cjs.map +10 -0
  12. package/dist/cjs/daemon.cjs +91 -0
  13. package/dist/cjs/daemon.cjs.map +10 -0
  14. package/dist/cjs/files/index.cjs +140 -0
  15. package/dist/cjs/files/index.cjs.map +10 -0
  16. package/dist/cjs/host/create-isolate-host.cjs +235 -0
  17. package/dist/cjs/host/create-isolate-host.cjs.map +10 -0
  18. package/dist/cjs/host/index.cjs +47 -0
  19. package/dist/cjs/host/index.cjs.map +10 -0
  20. package/dist/cjs/index.cjs +55 -0
  21. package/dist/cjs/index.cjs.map +10 -0
  22. package/dist/cjs/internal/client/connection.cjs +1919 -0
  23. package/dist/cjs/internal/client/connection.cjs.map +10 -0
  24. package/dist/cjs/internal/client/index.cjs +48 -0
  25. package/dist/cjs/internal/client/index.cjs.map +10 -0
  26. package/dist/cjs/internal/client/types.cjs +30 -0
  27. package/dist/cjs/internal/client/types.cjs.map +9 -0
  28. package/dist/cjs/internal/console/index.cjs +506 -0
  29. package/dist/cjs/internal/console/index.cjs.map +10 -0
  30. package/dist/cjs/internal/console/utils.cjs +70 -0
  31. package/dist/cjs/internal/console/utils.cjs.map +10 -0
  32. package/dist/cjs/internal/core/index.cjs +2745 -0
  33. package/dist/cjs/internal/core/index.cjs.map +10 -0
  34. package/dist/cjs/internal/crypto/index.cjs +470 -0
  35. package/dist/cjs/internal/crypto/index.cjs.map +10 -0
  36. package/dist/cjs/internal/daemon/callback-fs-handler.cjs +355 -0
  37. package/dist/cjs/internal/daemon/callback-fs-handler.cjs.map +10 -0
  38. package/dist/cjs/internal/daemon/connection.cjs +1952 -0
  39. package/dist/cjs/internal/daemon/connection.cjs.map +10 -0
  40. package/dist/cjs/internal/daemon/daemon.cjs +98 -0
  41. package/dist/cjs/internal/daemon/daemon.cjs.map +10 -0
  42. package/dist/cjs/internal/daemon/index.cjs +145 -0
  43. package/dist/cjs/internal/daemon/index.cjs.map +10 -0
  44. package/dist/cjs/internal/daemon/runtime-pool.cjs +106 -0
  45. package/dist/cjs/internal/daemon/runtime-pool.cjs.map +10 -0
  46. package/dist/cjs/internal/daemon/types.cjs +30 -0
  47. package/dist/cjs/internal/daemon/types.cjs.map +9 -0
  48. package/dist/cjs/internal/encoding/index.cjs +419 -0
  49. package/dist/cjs/internal/encoding/index.cjs.map +10 -0
  50. package/dist/cjs/internal/fetch/consistency/origins.cjs +598 -0
  51. package/dist/cjs/internal/fetch/consistency/origins.cjs.map +10 -0
  52. package/dist/cjs/internal/fetch/index.cjs +2640 -0
  53. package/dist/cjs/internal/fetch/index.cjs.map +10 -0
  54. package/dist/cjs/internal/fetch/stream-state.cjs +256 -0
  55. package/dist/cjs/internal/fetch/stream-state.cjs.map +10 -0
  56. package/dist/cjs/internal/fs/index.cjs +847 -0
  57. package/dist/cjs/internal/fs/index.cjs.map +10 -0
  58. package/dist/cjs/internal/fs/node-adapter.cjs +254 -0
  59. package/dist/cjs/internal/fs/node-adapter.cjs.map +10 -0
  60. package/dist/cjs/internal/module-loader/bundle.cjs +482 -0
  61. package/dist/cjs/internal/module-loader/bundle.cjs.map +10 -0
  62. package/dist/cjs/internal/module-loader/index.cjs +240 -0
  63. package/dist/cjs/internal/module-loader/index.cjs.map +10 -0
  64. package/dist/cjs/internal/module-loader/mappings.cjs +120 -0
  65. package/dist/cjs/internal/module-loader/mappings.cjs.map +10 -0
  66. package/dist/cjs/internal/module-loader/resolve.cjs +177 -0
  67. package/dist/cjs/internal/module-loader/resolve.cjs.map +10 -0
  68. package/dist/cjs/internal/module-loader/strip-types.cjs +236 -0
  69. package/dist/cjs/internal/module-loader/strip-types.cjs.map +10 -0
  70. package/dist/cjs/internal/path/index.cjs +503 -0
  71. package/dist/cjs/internal/path/index.cjs.map +10 -0
  72. package/dist/cjs/internal/playwright/client.cjs +49 -0
  73. package/dist/cjs/internal/playwright/client.cjs.map +10 -0
  74. package/dist/cjs/internal/playwright/handler.cjs +1416 -0
  75. package/dist/cjs/internal/playwright/handler.cjs.map +10 -0
  76. package/dist/cjs/internal/playwright/index.cjs +1289 -0
  77. package/dist/cjs/internal/playwright/index.cjs.map +10 -0
  78. package/dist/cjs/internal/playwright/types.cjs +47 -0
  79. package/dist/cjs/internal/playwright/types.cjs.map +10 -0
  80. package/dist/cjs/internal/protocol/codec.cjs +510 -0
  81. package/dist/cjs/internal/protocol/codec.cjs.map +10 -0
  82. package/dist/cjs/internal/protocol/framing.cjs +141 -0
  83. package/dist/cjs/internal/protocol/framing.cjs.map +10 -0
  84. package/dist/cjs/internal/protocol/index.cjs +110 -0
  85. package/dist/cjs/internal/protocol/index.cjs.map +10 -0
  86. package/dist/cjs/internal/protocol/marshalValue.cjs +518 -0
  87. package/dist/cjs/internal/protocol/marshalValue.cjs.map +10 -0
  88. package/dist/cjs/internal/protocol/serialization.cjs +109 -0
  89. package/dist/cjs/internal/protocol/serialization.cjs.map +10 -0
  90. package/dist/cjs/internal/protocol/types.cjs +181 -0
  91. package/dist/cjs/internal/protocol/types.cjs.map +10 -0
  92. package/dist/cjs/internal/runtime/index.cjs +1235 -0
  93. package/dist/cjs/internal/runtime/index.cjs.map +10 -0
  94. package/dist/cjs/internal/server/index.cjs +223 -0
  95. package/dist/cjs/internal/server/index.cjs.map +10 -0
  96. package/dist/cjs/internal/test-environment/index.cjs +1415 -0
  97. package/dist/cjs/internal/test-environment/index.cjs.map +10 -0
  98. package/dist/cjs/internal/timers/index.cjs +200 -0
  99. package/dist/cjs/internal/timers/index.cjs.map +10 -0
  100. package/dist/cjs/internal/transform/index.cjs +361 -0
  101. package/dist/cjs/internal/transform/index.cjs.map +10 -0
  102. package/dist/cjs/internal/typecheck/index.cjs +60 -0
  103. package/dist/cjs/internal/typecheck/index.cjs.map +10 -0
  104. package/dist/cjs/internal/typecheck/isolate-types.cjs +2614 -0
  105. package/dist/cjs/internal/typecheck/isolate-types.cjs.map +10 -0
  106. package/dist/cjs/internal/typecheck/typecheck.cjs +131 -0
  107. package/dist/cjs/internal/typecheck/typecheck.cjs.map +10 -0
  108. package/dist/cjs/modules/index.cjs +160 -0
  109. package/dist/cjs/modules/index.cjs.map +10 -0
  110. package/dist/cjs/package.json +5 -0
  111. package/dist/cjs/runtime/script-runtime.cjs +97 -0
  112. package/dist/cjs/runtime/script-runtime.cjs.map +10 -0
  113. package/dist/cjs/server/app-server.cjs +158 -0
  114. package/dist/cjs/server/app-server.cjs.map +10 -0
  115. package/dist/cjs/testing/integration-helpers.cjs +127 -0
  116. package/dist/cjs/testing/integration-helpers.cjs.map +10 -0
  117. package/dist/cjs/typecheck/index.cjs +96 -0
  118. package/dist/cjs/typecheck/index.cjs.map +10 -0
  119. package/dist/cjs/types.cjs +30 -0
  120. package/dist/cjs/types.cjs.map +9 -0
  121. package/dist/mjs/bridge/diagnostics.mjs +18 -0
  122. package/dist/mjs/bridge/diagnostics.mjs.map +10 -0
  123. package/dist/mjs/bridge/legacy-adapters.mjs +178 -0
  124. package/dist/mjs/bridge/legacy-adapters.mjs.map +10 -0
  125. package/dist/mjs/bridge/request-context.mjs +19 -0
  126. package/dist/mjs/bridge/request-context.mjs.map +10 -0
  127. package/dist/mjs/bridge/runtime-bindings.mjs +303 -0
  128. package/dist/mjs/bridge/runtime-bindings.mjs.map +10 -0
  129. package/dist/mjs/browser/browser-runtime.mjs +93 -0
  130. package/dist/mjs/browser/browser-runtime.mjs.map +10 -0
  131. package/dist/mjs/daemon.mjs +91 -0
  132. package/dist/mjs/daemon.mjs.map +10 -0
  133. package/dist/mjs/files/index.mjs +76 -0
  134. package/dist/mjs/files/index.mjs.map +10 -0
  135. package/dist/mjs/host/create-isolate-host.mjs +171 -0
  136. package/dist/mjs/host/create-isolate-host.mjs.map +10 -0
  137. package/dist/mjs/host/index.mjs +7 -0
  138. package/dist/mjs/host/index.mjs.map +10 -0
  139. package/dist/mjs/index.mjs +15 -0
  140. package/dist/mjs/index.mjs.map +10 -0
  141. package/dist/mjs/internal/client/connection.mjs +1872 -0
  142. package/dist/mjs/internal/client/connection.mjs.map +10 -0
  143. package/dist/mjs/internal/client/index.mjs +8 -0
  144. package/dist/mjs/internal/client/index.mjs.map +10 -0
  145. package/dist/mjs/internal/client/types.mjs +2 -0
  146. package/dist/mjs/internal/client/types.mjs.map +9 -0
  147. package/dist/mjs/internal/console/index.mjs +442 -0
  148. package/dist/mjs/internal/console/index.mjs.map +10 -0
  149. package/dist/mjs/internal/console/utils.mjs +30 -0
  150. package/dist/mjs/internal/console/utils.mjs.map +10 -0
  151. package/dist/mjs/internal/core/index.mjs +2681 -0
  152. package/dist/mjs/internal/core/index.mjs.map +10 -0
  153. package/dist/mjs/internal/crypto/index.mjs +406 -0
  154. package/dist/mjs/internal/crypto/index.mjs.map +10 -0
  155. package/dist/mjs/internal/daemon/callback-fs-handler.mjs +315 -0
  156. package/dist/mjs/internal/daemon/callback-fs-handler.mjs.map +10 -0
  157. package/dist/mjs/internal/daemon/connection.mjs +1931 -0
  158. package/dist/mjs/internal/daemon/connection.mjs.map +10 -0
  159. package/dist/mjs/internal/daemon/daemon.mjs +98 -0
  160. package/dist/mjs/internal/daemon/daemon.mjs.map +10 -0
  161. package/dist/mjs/internal/daemon/index.mjs +105 -0
  162. package/dist/mjs/internal/daemon/index.mjs.map +10 -0
  163. package/dist/mjs/internal/daemon/runtime-pool.mjs +66 -0
  164. package/dist/mjs/internal/daemon/runtime-pool.mjs.map +10 -0
  165. package/dist/mjs/internal/daemon/types.mjs +2 -0
  166. package/dist/mjs/internal/daemon/types.mjs.map +9 -0
  167. package/dist/mjs/internal/encoding/index.mjs +379 -0
  168. package/dist/mjs/internal/encoding/index.mjs.map +10 -0
  169. package/dist/mjs/internal/fetch/consistency/origins.mjs +558 -0
  170. package/dist/mjs/internal/fetch/consistency/origins.mjs.map +10 -0
  171. package/dist/mjs/internal/fetch/index.mjs +2580 -0
  172. package/dist/mjs/internal/fetch/index.mjs.map +10 -0
  173. package/dist/mjs/internal/fetch/stream-state.mjs +216 -0
  174. package/dist/mjs/internal/fetch/stream-state.mjs.map +10 -0
  175. package/dist/mjs/internal/fs/index.mjs +783 -0
  176. package/dist/mjs/internal/fs/index.mjs.map +10 -0
  177. package/dist/mjs/internal/fs/node-adapter.mjs +190 -0
  178. package/dist/mjs/internal/fs/node-adapter.mjs.map +10 -0
  179. package/dist/mjs/internal/module-loader/bundle.mjs +418 -0
  180. package/dist/mjs/internal/module-loader/bundle.mjs.map +10 -0
  181. package/dist/mjs/internal/module-loader/index.mjs +185 -0
  182. package/dist/mjs/internal/module-loader/index.mjs.map +10 -0
  183. package/dist/mjs/internal/module-loader/mappings.mjs +80 -0
  184. package/dist/mjs/internal/module-loader/mappings.mjs.map +10 -0
  185. package/dist/mjs/internal/module-loader/resolve.mjs +113 -0
  186. package/dist/mjs/internal/module-loader/resolve.mjs.map +10 -0
  187. package/dist/mjs/internal/module-loader/strip-types.mjs +172 -0
  188. package/dist/mjs/internal/module-loader/strip-types.mjs.map +10 -0
  189. package/dist/mjs/internal/path/index.mjs +463 -0
  190. package/dist/mjs/internal/path/index.mjs.map +10 -0
  191. package/dist/mjs/internal/playwright/client.mjs +13 -0
  192. package/dist/mjs/internal/playwright/client.mjs.map +10 -0
  193. package/dist/mjs/internal/playwright/handler.mjs +1378 -0
  194. package/dist/mjs/internal/playwright/handler.mjs.map +10 -0
  195. package/dist/mjs/internal/playwright/index.mjs +1234 -0
  196. package/dist/mjs/internal/playwright/index.mjs.map +10 -0
  197. package/dist/mjs/internal/playwright/types.mjs +7 -0
  198. package/dist/mjs/internal/playwright/types.mjs.map +10 -0
  199. package/dist/mjs/internal/protocol/codec.mjs +470 -0
  200. package/dist/mjs/internal/protocol/codec.mjs.map +10 -0
  201. package/dist/mjs/internal/protocol/framing.mjs +101 -0
  202. package/dist/mjs/internal/protocol/framing.mjs.map +10 -0
  203. package/dist/mjs/internal/protocol/index.mjs +98 -0
  204. package/dist/mjs/internal/protocol/index.mjs.map +10 -0
  205. package/dist/mjs/internal/protocol/marshalValue.mjs +494 -0
  206. package/dist/mjs/internal/protocol/marshalValue.mjs.map +10 -0
  207. package/dist/mjs/internal/protocol/serialization.mjs +69 -0
  208. package/dist/mjs/internal/protocol/serialization.mjs.map +10 -0
  209. package/dist/mjs/internal/protocol/types.mjs +141 -0
  210. package/dist/mjs/internal/protocol/types.mjs.map +10 -0
  211. package/dist/mjs/internal/runtime/index.mjs +1198 -0
  212. package/dist/mjs/internal/runtime/index.mjs.map +10 -0
  213. package/dist/mjs/internal/server/index.mjs +183 -0
  214. package/dist/mjs/internal/server/index.mjs.map +10 -0
  215. package/dist/mjs/internal/test-environment/index.mjs +1351 -0
  216. package/dist/mjs/internal/test-environment/index.mjs.map +10 -0
  217. package/dist/mjs/internal/timers/index.mjs +136 -0
  218. package/dist/mjs/internal/timers/index.mjs.map +10 -0
  219. package/dist/mjs/internal/transform/index.mjs +321 -0
  220. package/dist/mjs/internal/transform/index.mjs.map +10 -0
  221. package/dist/mjs/internal/typecheck/index.mjs +35 -0
  222. package/dist/mjs/internal/typecheck/index.mjs.map +10 -0
  223. package/dist/mjs/internal/typecheck/isolate-types.mjs +2574 -0
  224. package/dist/mjs/internal/typecheck/isolate-types.mjs.map +10 -0
  225. package/dist/mjs/internal/typecheck/typecheck.mjs +91 -0
  226. package/dist/mjs/internal/typecheck/typecheck.mjs.map +10 -0
  227. package/dist/mjs/modules/index.mjs +96 -0
  228. package/dist/mjs/modules/index.mjs.map +10 -0
  229. package/dist/mjs/package.json +5 -0
  230. package/dist/mjs/runtime/script-runtime.mjs +57 -0
  231. package/dist/mjs/runtime/script-runtime.mjs.map +10 -0
  232. package/dist/mjs/server/app-server.mjs +118 -0
  233. package/dist/mjs/server/app-server.mjs.map +10 -0
  234. package/dist/mjs/testing/integration-helpers.mjs +63 -0
  235. package/dist/mjs/testing/integration-helpers.mjs.map +10 -0
  236. package/dist/mjs/typecheck/index.mjs +56 -0
  237. package/dist/mjs/typecheck/index.mjs.map +10 -0
  238. package/dist/mjs/types.mjs +2 -0
  239. package/dist/mjs/types.mjs.map +9 -0
  240. package/dist/types/bridge/diagnostics.d.ts +12 -0
  241. package/dist/types/bridge/legacy-adapters.d.ts +14 -0
  242. package/dist/types/bridge/request-context.d.ts +10 -0
  243. package/dist/types/bridge/runtime-bindings.d.ts +14 -0
  244. package/dist/types/browser/browser-runtime.d.ts +3 -0
  245. package/dist/types/daemon.d.ts +2 -0
  246. package/dist/types/files/index.d.ts +5 -0
  247. package/dist/types/host/create-isolate-host.d.ts +2 -0
  248. package/dist/types/host/index.d.ts +1 -0
  249. package/dist/types/index.d.ts +5 -0
  250. package/dist/types/internal/client/connection.d.ts +9 -0
  251. package/dist/types/internal/client/index.d.ts +8 -0
  252. package/dist/types/internal/client/types.d.ts +198 -0
  253. package/dist/types/internal/console/index.d.ts +108 -0
  254. package/dist/types/internal/console/utils.d.ts +27 -0
  255. package/dist/types/internal/core/index.d.ts +119 -0
  256. package/dist/types/internal/crypto/index.d.ts +18 -0
  257. package/dist/types/internal/daemon/callback-fs-handler.d.ts +28 -0
  258. package/dist/types/internal/daemon/connection.d.ts +9 -0
  259. package/dist/types/internal/daemon/daemon.d.ts +2 -0
  260. package/dist/types/internal/daemon/index.d.ts +14 -0
  261. package/dist/types/internal/daemon/runtime-pool.d.ts +16 -0
  262. package/dist/types/internal/daemon/types.d.ts +211 -0
  263. package/dist/types/internal/encoding/index.d.ts +21 -0
  264. package/dist/types/internal/fetch/consistency/origins.d.ts +179 -0
  265. package/dist/types/internal/fetch/index.d.ts +93 -0
  266. package/dist/types/internal/fetch/stream-state.d.ts +65 -0
  267. package/dist/types/internal/fs/index.d.ts +70 -0
  268. package/dist/types/internal/fs/node-adapter.d.ts +24 -0
  269. package/dist/types/internal/module-loader/bundle.d.ts +33 -0
  270. package/dist/types/internal/module-loader/index.d.ts +30 -0
  271. package/dist/types/internal/module-loader/mappings.d.ts +47 -0
  272. package/dist/types/internal/module-loader/resolve.d.ts +26 -0
  273. package/dist/types/internal/module-loader/strip-types.d.ts +19 -0
  274. package/dist/types/internal/path/index.d.ts +23 -0
  275. package/dist/types/internal/playwright/client.d.ts +7 -0
  276. package/dist/types/internal/playwright/handler.d.ts +44 -0
  277. package/dist/types/internal/playwright/index.d.ts +14 -0
  278. package/dist/types/internal/playwright/types.d.ts +145 -0
  279. package/dist/types/internal/protocol/codec.d.ts +242 -0
  280. package/dist/types/internal/protocol/framing.d.ts +89 -0
  281. package/dist/types/internal/protocol/index.d.ts +10 -0
  282. package/dist/types/internal/protocol/marshalValue.d.ts +79 -0
  283. package/dist/types/internal/protocol/serialization.d.ts +23 -0
  284. package/dist/types/internal/protocol/types.d.ts +996 -0
  285. package/dist/types/internal/runtime/index.d.ts +200 -0
  286. package/dist/types/internal/server/index.d.ts +42 -0
  287. package/dist/types/internal/test-environment/index.d.ts +112 -0
  288. package/dist/types/internal/timers/index.d.ts +22 -0
  289. package/dist/types/internal/transform/index.d.ts +36 -0
  290. package/dist/types/internal/typecheck/index.d.ts +7 -0
  291. package/dist/types/internal/typecheck/isolate-types.d.ts +94 -0
  292. package/dist/types/internal/typecheck/typecheck.d.ts +148 -0
  293. package/dist/types/modules/index.d.ts +2 -0
  294. package/dist/types/runtime/script-runtime.d.ts +6 -0
  295. package/dist/types/server/app-server.d.ts +3 -0
  296. package/dist/types/testing/integration-helpers.d.ts +9 -0
  297. package/dist/types/typecheck/index.d.ts +8 -0
  298. package/dist/types/types.d.ts +233 -0
  299. package/package.json +82 -6
@@ -0,0 +1,1351 @@
1
+ // src/internal/test-environment/index.ts
2
+ import IsolatedVM from "isolated-vm";
3
+ var testEnvironmentCode = `
4
+ (function() {
5
+ // ============================================================
6
+ // Internal State
7
+ // ============================================================
8
+
9
+ // Mock registry and call counter
10
+ let __mockCallOrder = 0;
11
+ const __mockRegistry = [];
12
+
13
+ // Assertion counting state
14
+ let __expectedAssertions = null;
15
+ let __assertionCount = 0;
16
+ let __hasAssertionsFlag = false;
17
+
18
+ function createMockState() {
19
+ return {
20
+ calls: [],
21
+ results: [],
22
+ contexts: [],
23
+ instances: [],
24
+ invocationCallOrder: [],
25
+ lastCall: undefined,
26
+ };
27
+ }
28
+
29
+ function createSuite(name, skip = false, only = false) {
30
+ return {
31
+ name,
32
+ tests: [],
33
+ children: [],
34
+ beforeAll: [],
35
+ afterAll: [],
36
+ beforeEach: [],
37
+ afterEach: [],
38
+ skip,
39
+ only,
40
+ };
41
+ }
42
+
43
+ const rootSuite = createSuite('root');
44
+ let currentSuite = rootSuite;
45
+ const suiteStack = [rootSuite];
46
+
47
+ // Event callback (set from host)
48
+ let eventCallback = null;
49
+
50
+ function emitEvent(event) {
51
+ if (eventCallback) {
52
+ try {
53
+ eventCallback(JSON.stringify(event));
54
+ } catch (e) {
55
+ // Ignore callback errors
56
+ }
57
+ }
58
+ }
59
+
60
+ // ============================================================
61
+ // TestError class for rich error info
62
+ // ============================================================
63
+
64
+ class TestError extends Error {
65
+ constructor(message, matcherName, expected, actual) {
66
+ super(message);
67
+ this.name = 'TestError';
68
+ this.matcherName = matcherName;
69
+ this.expected = expected;
70
+ this.actual = actual;
71
+ }
72
+ }
73
+
74
+ // ============================================================
75
+ // Asymmetric Matcher Infrastructure
76
+ // ============================================================
77
+
78
+ const ASYMMETRIC_MATCHER = Symbol('asymmetricMatcher');
79
+
80
+ function isAsymmetricMatcher(obj) {
81
+ return obj && obj[ASYMMETRIC_MATCHER] === true;
82
+ }
83
+
84
+ // Deep equality with asymmetric matcher support
85
+ function asymmetricDeepEqual(a, b) {
86
+ if (isAsymmetricMatcher(b)) return b.asymmetricMatch(a);
87
+ if (isAsymmetricMatcher(a)) return a.asymmetricMatch(b);
88
+ if (a === b) return true;
89
+ if (typeof a !== typeof b) return false;
90
+ if (typeof a !== 'object' || a === null || b === null) return false;
91
+ if (Array.isArray(a) !== Array.isArray(b)) return false;
92
+ const keysA = Object.keys(a);
93
+ const keysB = Object.keys(b);
94
+ if (keysA.length !== keysB.length) return false;
95
+ for (const key of keysA) {
96
+ if (!keysB.includes(key)) return false;
97
+ if (!asymmetricDeepEqual(a[key], b[key])) return false;
98
+ }
99
+ return true;
100
+ }
101
+
102
+ // ============================================================
103
+ // Deep Equality Helper
104
+ // ============================================================
105
+
106
+ function deepEqual(a, b) {
107
+ if (a === b) return true;
108
+ if (typeof a !== typeof b) return false;
109
+ if (typeof a !== 'object' || a === null || b === null) return false;
110
+
111
+ if (Array.isArray(a) !== Array.isArray(b)) return false;
112
+
113
+ const keysA = Object.keys(a);
114
+ const keysB = Object.keys(b);
115
+ if (keysA.length !== keysB.length) return false;
116
+
117
+ for (const key of keysA) {
118
+ if (!keysB.includes(key)) return false;
119
+ if (!deepEqual(a[key], b[key])) return false;
120
+ }
121
+ return true;
122
+ }
123
+
124
+ function strictDeepEqual(a, b) {
125
+ if (a === b) return true;
126
+ if (typeof a !== typeof b) return false;
127
+ if (typeof a !== 'object' || a === null || b === null) return false;
128
+
129
+ // Check prototypes
130
+ if (Object.getPrototypeOf(a) !== Object.getPrototypeOf(b)) return false;
131
+
132
+ if (Array.isArray(a) !== Array.isArray(b)) return false;
133
+
134
+ // For arrays, check sparse arrays (holes)
135
+ if (Array.isArray(a)) {
136
+ if (a.length !== b.length) return false;
137
+ for (let i = 0; i < a.length; i++) {
138
+ const aHasIndex = i in a;
139
+ const bHasIndex = i in b;
140
+ if (aHasIndex !== bHasIndex) return false;
141
+ if (aHasIndex && !strictDeepEqual(a[i], b[i])) return false;
142
+ }
143
+ return true;
144
+ }
145
+
146
+ // Check for undefined properties vs missing properties
147
+ const keysA = Object.keys(a);
148
+ const keysB = Object.keys(b);
149
+ if (keysA.length !== keysB.length) return false;
150
+
151
+ for (const key of keysA) {
152
+ if (!keysB.includes(key)) return false;
153
+ if (!strictDeepEqual(a[key], b[key])) return false;
154
+ }
155
+
156
+ // Check for symbol properties
157
+ const symbolsA = Object.getOwnPropertySymbols(a);
158
+ const symbolsB = Object.getOwnPropertySymbols(b);
159
+ if (symbolsA.length !== symbolsB.length) return false;
160
+
161
+ for (const sym of symbolsA) {
162
+ if (!symbolsB.includes(sym)) return false;
163
+ if (!strictDeepEqual(a[sym], b[sym])) return false;
164
+ }
165
+
166
+ return true;
167
+ }
168
+
169
+ function getNestedProperty(obj, path) {
170
+ const parts = path.split('.');
171
+ let current = obj;
172
+ for (const part of parts) {
173
+ if (current == null || !(part in current)) {
174
+ return { exists: false };
175
+ }
176
+ current = current[part];
177
+ }
178
+ return { exists: true, value: current };
179
+ }
180
+
181
+ function formatValue(val) {
182
+ if (val === null) return 'null';
183
+ if (val === undefined) return 'undefined';
184
+ if (typeof val === 'string') return JSON.stringify(val);
185
+ if (typeof val === 'object') {
186
+ try {
187
+ return JSON.stringify(val);
188
+ } catch {
189
+ return String(val);
190
+ }
191
+ }
192
+ return String(val);
193
+ }
194
+
195
+ // ============================================================
196
+ // expect() Implementation
197
+ // ============================================================
198
+
199
+ function expect(actual) {
200
+ function createMatchers(negated = false) {
201
+ const assert = (condition, message, matcherName, expected) => {
202
+ __assertionCount++;
203
+ const pass = negated ? !condition : condition;
204
+ if (!pass) {
205
+ throw new TestError(message, matcherName, expected, actual);
206
+ }
207
+ };
208
+
209
+ const matchers = {
210
+ toBe(expected) {
211
+ assert(
212
+ actual === expected,
213
+ negated
214
+ ? \`Expected \${formatValue(actual)} not to be \${formatValue(expected)}\`
215
+ : \`Expected \${formatValue(actual)} to be \${formatValue(expected)}\`,
216
+ 'toBe',
217
+ expected
218
+ );
219
+ },
220
+
221
+ toEqual(expected) {
222
+ assert(
223
+ asymmetricDeepEqual(actual, expected),
224
+ negated
225
+ ? \`Expected \${formatValue(actual)} not to equal \${formatValue(expected)}\`
226
+ : \`Expected \${formatValue(actual)} to equal \${formatValue(expected)}\`,
227
+ 'toEqual',
228
+ expected
229
+ );
230
+ },
231
+
232
+ toStrictEqual(expected) {
233
+ assert(
234
+ strictDeepEqual(actual, expected),
235
+ negated
236
+ ? \`Expected \${formatValue(actual)} not to strictly equal \${formatValue(expected)}\`
237
+ : \`Expected \${formatValue(actual)} to strictly equal \${formatValue(expected)}\`,
238
+ 'toStrictEqual',
239
+ expected
240
+ );
241
+ },
242
+
243
+ toBeTruthy() {
244
+ assert(
245
+ !!actual,
246
+ negated
247
+ ? \`Expected \${formatValue(actual)} not to be truthy\`
248
+ : \`Expected \${formatValue(actual)} to be truthy\`,
249
+ 'toBeTruthy',
250
+ true
251
+ );
252
+ },
253
+
254
+ toBeFalsy() {
255
+ assert(
256
+ !actual,
257
+ negated
258
+ ? \`Expected \${formatValue(actual)} not to be falsy\`
259
+ : \`Expected \${formatValue(actual)} to be falsy\`,
260
+ 'toBeFalsy',
261
+ false
262
+ );
263
+ },
264
+
265
+ toBeNull() {
266
+ assert(
267
+ actual === null,
268
+ negated
269
+ ? \`Expected \${formatValue(actual)} not to be null\`
270
+ : \`Expected \${formatValue(actual)} to be null\`,
271
+ 'toBeNull',
272
+ null
273
+ );
274
+ },
275
+
276
+ toBeUndefined() {
277
+ assert(
278
+ actual === undefined,
279
+ negated
280
+ ? \`Expected \${formatValue(actual)} not to be undefined\`
281
+ : \`Expected \${formatValue(actual)} to be undefined\`,
282
+ 'toBeUndefined',
283
+ undefined
284
+ );
285
+ },
286
+
287
+ toBeDefined() {
288
+ assert(
289
+ actual !== undefined,
290
+ negated
291
+ ? \`Expected \${formatValue(actual)} not to be defined\`
292
+ : \`Expected \${formatValue(actual)} to be defined\`,
293
+ 'toBeDefined',
294
+ 'defined'
295
+ );
296
+ },
297
+
298
+ toContain(item) {
299
+ let contains = false;
300
+ if (Array.isArray(actual)) {
301
+ contains = actual.includes(item);
302
+ } else if (typeof actual === 'string') {
303
+ contains = actual.includes(item);
304
+ }
305
+ assert(
306
+ contains,
307
+ negated
308
+ ? \`Expected \${formatValue(actual)} not to contain \${formatValue(item)}\`
309
+ : \`Expected \${formatValue(actual)} to contain \${formatValue(item)}\`,
310
+ 'toContain',
311
+ item
312
+ );
313
+ },
314
+
315
+ toThrow(expected) {
316
+ if (typeof actual !== 'function') {
317
+ throw new Error('toThrow requires a function');
318
+ }
319
+
320
+ let threw = false;
321
+ let error = null;
322
+ try {
323
+ actual();
324
+ } catch (e) {
325
+ threw = true;
326
+ error = e;
327
+ }
328
+
329
+ if (expected !== undefined) {
330
+ const matches = threw && (
331
+ (typeof expected === 'string' && error.message.includes(expected)) ||
332
+ (expected instanceof RegExp && expected.test(error.message)) ||
333
+ (typeof expected === 'function' && error instanceof expected)
334
+ );
335
+ assert(
336
+ matches,
337
+ negated
338
+ ? \`Expected function not to throw \${formatValue(expected)}\`
339
+ : \`Expected function to throw \${formatValue(expected)}, but \${threw ? \`threw: \${error.message}\` : 'did not throw'}\`,
340
+ 'toThrow',
341
+ expected
342
+ );
343
+ } else {
344
+ assert(
345
+ threw,
346
+ negated
347
+ ? \`Expected function not to throw\`
348
+ : \`Expected function to throw\`,
349
+ 'toThrow',
350
+ 'any error'
351
+ );
352
+ }
353
+ },
354
+
355
+ toBeInstanceOf(cls) {
356
+ assert(
357
+ actual instanceof cls,
358
+ negated
359
+ ? \`Expected \${formatValue(actual)} not to be instance of \${cls.name || cls}\`
360
+ : \`Expected \${formatValue(actual)} to be instance of \${cls.name || cls}\`,
361
+ 'toBeInstanceOf',
362
+ cls.name || cls
363
+ );
364
+ },
365
+
366
+ toHaveLength(length) {
367
+ const actualLength = actual?.length;
368
+ assert(
369
+ actualLength === length,
370
+ negated
371
+ ? \`Expected length not to be \${length}, but got \${actualLength}\`
372
+ : \`Expected length to be \${length}, but got \${actualLength}\`,
373
+ 'toHaveLength',
374
+ length
375
+ );
376
+ },
377
+
378
+ toMatch(pattern) {
379
+ let matches = false;
380
+ if (typeof pattern === 'string') {
381
+ matches = actual.includes(pattern);
382
+ } else if (pattern instanceof RegExp) {
383
+ matches = pattern.test(actual);
384
+ }
385
+ assert(
386
+ matches,
387
+ negated
388
+ ? \`Expected \${formatValue(actual)} not to match \${pattern}\`
389
+ : \`Expected \${formatValue(actual)} to match \${pattern}\`,
390
+ 'toMatch',
391
+ pattern
392
+ );
393
+ },
394
+
395
+ toHaveProperty(path, value) {
396
+ const prop = getNestedProperty(actual, path);
397
+ const hasProperty = prop.exists;
398
+ const valueMatches = arguments.length < 2 || asymmetricDeepEqual(prop.value, value);
399
+
400
+ assert(
401
+ hasProperty && valueMatches,
402
+ negated
403
+ ? \`Expected \${formatValue(actual)} not to have property \${path}\${arguments.length >= 2 ? \` with value \${formatValue(value)}\` : ''}\`
404
+ : \`Expected \${formatValue(actual)} to have property \${path}\${arguments.length >= 2 ? \` with value \${formatValue(value)}\` : ''}\`,
405
+ 'toHaveProperty',
406
+ arguments.length >= 2 ? { path, value } : { path }
407
+ );
408
+ },
409
+
410
+ toBeGreaterThan(expected) {
411
+ assert(
412
+ actual > expected,
413
+ negated
414
+ ? \`Expected \${formatValue(actual)} not to be greater than \${formatValue(expected)}\`
415
+ : \`Expected \${formatValue(actual)} to be greater than \${formatValue(expected)}\`,
416
+ 'toBeGreaterThan',
417
+ expected
418
+ );
419
+ },
420
+
421
+ toBeGreaterThanOrEqual(expected) {
422
+ assert(
423
+ actual >= expected,
424
+ negated
425
+ ? \`Expected \${formatValue(actual)} not to be greater than or equal to \${formatValue(expected)}\`
426
+ : \`Expected \${formatValue(actual)} to be greater than or equal to \${formatValue(expected)}\`,
427
+ 'toBeGreaterThanOrEqual',
428
+ expected
429
+ );
430
+ },
431
+
432
+ toBeLessThan(expected) {
433
+ assert(
434
+ actual < expected,
435
+ negated
436
+ ? \`Expected \${formatValue(actual)} not to be less than \${formatValue(expected)}\`
437
+ : \`Expected \${formatValue(actual)} to be less than \${formatValue(expected)}\`,
438
+ 'toBeLessThan',
439
+ expected
440
+ );
441
+ },
442
+
443
+ toBeLessThanOrEqual(expected) {
444
+ assert(
445
+ actual <= expected,
446
+ negated
447
+ ? \`Expected \${formatValue(actual)} not to be less than or equal to \${formatValue(expected)}\`
448
+ : \`Expected \${formatValue(actual)} to be less than or equal to \${formatValue(expected)}\`,
449
+ 'toBeLessThanOrEqual',
450
+ expected
451
+ );
452
+ },
453
+
454
+ toBeCloseTo(expected, numDigits = 2) {
455
+ const precision = Math.pow(10, -numDigits) / 2;
456
+ const pass = Math.abs(actual - expected) < precision;
457
+ assert(
458
+ pass,
459
+ negated
460
+ ? \`Expected \${formatValue(actual)} not to be close to \${formatValue(expected)} (precision: \${numDigits} digits)\`
461
+ : \`Expected \${formatValue(actual)} to be close to \${formatValue(expected)} (precision: \${numDigits} digits)\`,
462
+ 'toBeCloseTo',
463
+ expected
464
+ );
465
+ },
466
+
467
+ toBeNaN() {
468
+ assert(
469
+ Number.isNaN(actual),
470
+ negated
471
+ ? \`Expected \${formatValue(actual)} not to be NaN\`
472
+ : \`Expected \${formatValue(actual)} to be NaN\`,
473
+ 'toBeNaN',
474
+ NaN
475
+ );
476
+ },
477
+
478
+ toMatchObject(expected) {
479
+ function matchesObject(obj, pattern) {
480
+ if (isAsymmetricMatcher(pattern)) return pattern.asymmetricMatch(obj);
481
+ if (typeof pattern !== 'object' || pattern === null) return obj === pattern;
482
+ if (typeof obj !== 'object' || obj === null) return false;
483
+ for (const key of Object.keys(pattern)) {
484
+ if (!(key in obj)) return false;
485
+ if (!matchesObject(obj[key], pattern[key])) return false;
486
+ }
487
+ return true;
488
+ }
489
+ assert(
490
+ matchesObject(actual, expected),
491
+ negated
492
+ ? \`Expected \${formatValue(actual)} not to match object \${formatValue(expected)}\`
493
+ : \`Expected \${formatValue(actual)} to match object \${formatValue(expected)}\`,
494
+ 'toMatchObject',
495
+ expected
496
+ );
497
+ },
498
+
499
+ toContainEqual(item) {
500
+ const contains = Array.isArray(actual) && actual.some(el => asymmetricDeepEqual(el, item));
501
+ assert(
502
+ contains,
503
+ negated
504
+ ? \`Expected array not to contain equal \${formatValue(item)}\`
505
+ : \`Expected array to contain equal \${formatValue(item)}\`,
506
+ 'toContainEqual',
507
+ item
508
+ );
509
+ },
510
+
511
+ toBeTypeOf(expectedType) {
512
+ const actualType = typeof actual;
513
+ assert(
514
+ actualType === expectedType,
515
+ negated
516
+ ? \`Expected typeof \${formatValue(actual)} not to be "\${expectedType}"\`
517
+ : \`Expected typeof \${formatValue(actual)} to be "\${expectedType}", got "\${actualType}"\`,
518
+ 'toBeTypeOf',
519
+ expectedType
520
+ );
521
+ },
522
+
523
+ // Mock matchers
524
+ toHaveBeenCalled() {
525
+ if (!actual.__isMockFunction) throw new Error('toHaveBeenCalled requires a mock function');
526
+ assert(actual.mock.calls.length > 0,
527
+ negated ? \`Expected mock not to have been called\` : \`Expected mock to have been called\`,
528
+ 'toHaveBeenCalled', 'called');
529
+ },
530
+
531
+ toHaveBeenCalledTimes(n) {
532
+ if (!actual.__isMockFunction) throw new Error('toHaveBeenCalledTimes requires a mock function');
533
+ assert(actual.mock.calls.length === n,
534
+ negated ? \`Expected mock not to have been called \${n} times\`
535
+ : \`Expected mock to be called \${n} times, got \${actual.mock.calls.length}\`,
536
+ 'toHaveBeenCalledTimes', n);
537
+ },
538
+
539
+ toHaveBeenCalledWith(...expectedArgs) {
540
+ if (!actual.__isMockFunction) throw new Error('toHaveBeenCalledWith requires a mock function');
541
+ const match = actual.mock.calls.some(args => asymmetricDeepEqual(args, expectedArgs));
542
+ assert(match,
543
+ negated ? \`Expected mock not to have been called with \${formatValue(expectedArgs)}\`
544
+ : \`Expected mock to have been called with \${formatValue(expectedArgs)}\`,
545
+ 'toHaveBeenCalledWith', expectedArgs);
546
+ },
547
+
548
+ toHaveBeenLastCalledWith(...expectedArgs) {
549
+ if (!actual.__isMockFunction) throw new Error('toHaveBeenLastCalledWith requires a mock function');
550
+ assert(actual.mock.lastCall && asymmetricDeepEqual(actual.mock.lastCall, expectedArgs),
551
+ negated ? \`Expected last call not to be \${formatValue(expectedArgs)}\`
552
+ : \`Expected last call to be \${formatValue(expectedArgs)}, got \${formatValue(actual.mock.lastCall)}\`,
553
+ 'toHaveBeenLastCalledWith', expectedArgs);
554
+ },
555
+
556
+ toHaveBeenNthCalledWith(n, ...expectedArgs) {
557
+ if (!actual.__isMockFunction) throw new Error('toHaveBeenNthCalledWith requires a mock function');
558
+ const nthCall = actual.mock.calls[n - 1];
559
+ assert(nthCall && asymmetricDeepEqual(nthCall, expectedArgs),
560
+ negated ? \`Expected call \${n} not to be \${formatValue(expectedArgs)}\`
561
+ : \`Expected call \${n} to be \${formatValue(expectedArgs)}, got \${formatValue(nthCall)}\`,
562
+ 'toHaveBeenNthCalledWith', expectedArgs);
563
+ },
564
+
565
+ toHaveReturned() {
566
+ if (!actual.__isMockFunction) throw new Error('toHaveReturned requires a mock function');
567
+ const hasReturned = actual.mock.results.some(r => r.type === 'return');
568
+ assert(hasReturned,
569
+ negated ? \`Expected mock not to have returned\` : \`Expected mock to have returned\`,
570
+ 'toHaveReturned', 'returned');
571
+ },
572
+
573
+ toHaveReturnedWith(value) {
574
+ if (!actual.__isMockFunction) throw new Error('toHaveReturnedWith requires a mock function');
575
+ const match = actual.mock.results.some(r => r.type === 'return' && asymmetricDeepEqual(r.value, value));
576
+ assert(match,
577
+ negated ? \`Expected mock not to have returned \${formatValue(value)}\`
578
+ : \`Expected mock to have returned \${formatValue(value)}\`,
579
+ 'toHaveReturnedWith', value);
580
+ },
581
+
582
+ toHaveLastReturnedWith(value) {
583
+ if (!actual.__isMockFunction) throw new Error('toHaveLastReturnedWith requires a mock function');
584
+ const returns = actual.mock.results.filter(r => r.type === 'return');
585
+ const last = returns[returns.length - 1];
586
+ assert(last && asymmetricDeepEqual(last.value, value),
587
+ negated ? \`Expected last return not to be \${formatValue(value)}\`
588
+ : \`Expected last return to be \${formatValue(value)}, got \${formatValue(last?.value)}\`,
589
+ 'toHaveLastReturnedWith', value);
590
+ },
591
+
592
+ toHaveReturnedTimes(n) {
593
+ if (!actual.__isMockFunction) throw new Error('toHaveReturnedTimes requires a mock function');
594
+ const returnCount = actual.mock.results.filter(r => r.type === 'return').length;
595
+ assert(returnCount === n,
596
+ negated ? \`Expected mock not to have returned \${n} times\`
597
+ : \`Expected mock to have returned \${n} times, got \${returnCount}\`,
598
+ 'toHaveReturnedTimes', n);
599
+ },
600
+
601
+ toHaveNthReturnedWith(n, value) {
602
+ if (!actual.__isMockFunction) throw new Error('toHaveNthReturnedWith requires a mock function');
603
+ const returns = actual.mock.results.filter(r => r.type === 'return');
604
+ const nthReturn = returns[n - 1];
605
+ assert(nthReturn && asymmetricDeepEqual(nthReturn.value, value),
606
+ negated ? \`Expected return \${n} not to be \${formatValue(value)}\`
607
+ : \`Expected return \${n} to be \${formatValue(value)}, got \${formatValue(nthReturn?.value)}\`,
608
+ 'toHaveNthReturnedWith', value);
609
+ },
610
+ };
611
+
612
+ return matchers;
613
+ }
614
+
615
+ const matchers = createMatchers(false);
616
+ matchers.not = createMatchers(true);
617
+
618
+ // Promise matchers using Proxy
619
+ matchers.resolves = new Proxy({}, {
620
+ get(_, matcherName) {
621
+ if (matcherName === 'not') {
622
+ return new Proxy({}, {
623
+ get(_, negatedMatcherName) {
624
+ return async (...args) => {
625
+ const result = await actual;
626
+ return expect(result).not[negatedMatcherName](...args);
627
+ };
628
+ }
629
+ });
630
+ }
631
+ return async (...args) => {
632
+ const result = await actual;
633
+ return expect(result)[matcherName](...args);
634
+ };
635
+ }
636
+ });
637
+
638
+ matchers.rejects = new Proxy({}, {
639
+ get(_, matcherName) {
640
+ if (matcherName === 'not') {
641
+ return new Proxy({}, {
642
+ get(_, negatedMatcherName) {
643
+ return async (...args) => {
644
+ let error;
645
+ try {
646
+ await actual;
647
+ throw new TestError('Expected promise to reject', 'rejects', 'rejection', undefined);
648
+ } catch (e) {
649
+ if (e instanceof TestError && e.matcherName === 'rejects') throw e;
650
+ error = e;
651
+ }
652
+ return expect(error).not[negatedMatcherName](...args);
653
+ };
654
+ }
655
+ });
656
+ }
657
+ return async (...args) => {
658
+ let error;
659
+ try {
660
+ await actual;
661
+ throw new TestError('Expected promise to reject', 'rejects', 'rejection', undefined);
662
+ } catch (e) {
663
+ if (e instanceof TestError && e.matcherName === 'rejects') throw e;
664
+ error = e;
665
+ }
666
+ return expect(error)[matcherName](...args);
667
+ };
668
+ }
669
+ });
670
+
671
+ return matchers;
672
+ }
673
+
674
+ // Asymmetric matcher implementations
675
+ expect.anything = () => ({
676
+ [ASYMMETRIC_MATCHER]: true,
677
+ asymmetricMatch: (other) => other !== null && other !== undefined,
678
+ toString: () => 'anything()',
679
+ });
680
+
681
+ expect.any = (constructor) => ({
682
+ [ASYMMETRIC_MATCHER]: true,
683
+ asymmetricMatch: (other) => {
684
+ if (constructor === String) return typeof other === 'string' || other instanceof String;
685
+ if (constructor === Number) return typeof other === 'number' || other instanceof Number;
686
+ if (constructor === Boolean) return typeof other === 'boolean' || other instanceof Boolean;
687
+ if (constructor === Function) return typeof other === 'function';
688
+ if (constructor === Object) return typeof other === 'object' && other !== null;
689
+ if (constructor === Array) return Array.isArray(other);
690
+ return other instanceof constructor;
691
+ },
692
+ toString: () => \`any(\${constructor.name || constructor})\`,
693
+ });
694
+
695
+ expect.stringContaining = (str) => ({
696
+ [ASYMMETRIC_MATCHER]: true,
697
+ asymmetricMatch: (other) => typeof other === 'string' && other.includes(str),
698
+ toString: () => \`stringContaining("\${str}")\`,
699
+ });
700
+
701
+ expect.stringMatching = (pattern) => ({
702
+ [ASYMMETRIC_MATCHER]: true,
703
+ asymmetricMatch: (other) => {
704
+ if (typeof other !== 'string') return false;
705
+ return typeof pattern === 'string' ? other.includes(pattern) : pattern.test(other);
706
+ },
707
+ toString: () => \`stringMatching(\${pattern})\`,
708
+ });
709
+
710
+ expect.arrayContaining = (expected) => ({
711
+ [ASYMMETRIC_MATCHER]: true,
712
+ asymmetricMatch: (other) => {
713
+ if (!Array.isArray(other)) return false;
714
+ return expected.every(exp => other.some(item => asymmetricDeepEqual(item, exp)));
715
+ },
716
+ toString: () => \`arrayContaining(\${formatValue(expected)})\`,
717
+ });
718
+
719
+ expect.objectContaining = (expected) => ({
720
+ [ASYMMETRIC_MATCHER]: true,
721
+ asymmetricMatch: (other) => {
722
+ if (typeof other !== 'object' || other === null) return false;
723
+ for (const key of Object.keys(expected)) {
724
+ if (!(key in other)) return false;
725
+ if (!asymmetricDeepEqual(other[key], expected[key])) return false;
726
+ }
727
+ return true;
728
+ },
729
+ toString: () => \`objectContaining(\${formatValue(expected)})\`,
730
+ });
731
+
732
+ // Assertion counting
733
+ expect.assertions = (n) => {
734
+ __expectedAssertions = n;
735
+ };
736
+
737
+ expect.hasAssertions = () => {
738
+ __hasAssertionsFlag = true;
739
+ };
740
+
741
+ // ============================================================
742
+ // Test Registration Functions
743
+ // ============================================================
744
+
745
+ function describe(name, fn) {
746
+ const suite = createSuite(name);
747
+ currentSuite.children.push(suite);
748
+
749
+ const parentSuite = currentSuite;
750
+ currentSuite = suite;
751
+ suiteStack.push(suite);
752
+
753
+ fn();
754
+
755
+ suiteStack.pop();
756
+ currentSuite = parentSuite;
757
+ }
758
+
759
+ describe.skip = function(name, fn) {
760
+ const suite = createSuite(name, true, false);
761
+ currentSuite.children.push(suite);
762
+
763
+ const parentSuite = currentSuite;
764
+ currentSuite = suite;
765
+ suiteStack.push(suite);
766
+
767
+ fn();
768
+
769
+ suiteStack.pop();
770
+ currentSuite = parentSuite;
771
+ };
772
+
773
+ describe.only = function(name, fn) {
774
+ const suite = createSuite(name, false, true);
775
+ currentSuite.children.push(suite);
776
+
777
+ const parentSuite = currentSuite;
778
+ currentSuite = suite;
779
+ suiteStack.push(suite);
780
+
781
+ fn();
782
+
783
+ suiteStack.pop();
784
+ currentSuite = parentSuite;
785
+ };
786
+
787
+ function test(name, fn) {
788
+ currentSuite.tests.push({
789
+ name,
790
+ fn,
791
+ skip: false,
792
+ only: false,
793
+ });
794
+ }
795
+
796
+ test.skip = function(name, fn) {
797
+ currentSuite.tests.push({
798
+ name,
799
+ fn,
800
+ skip: true,
801
+ only: false,
802
+ });
803
+ };
804
+
805
+ test.only = function(name, fn) {
806
+ currentSuite.tests.push({
807
+ name,
808
+ fn,
809
+ skip: false,
810
+ only: true,
811
+ });
812
+ };
813
+
814
+ test.todo = function(name) {
815
+ currentSuite.tests.push({
816
+ name,
817
+ fn: null,
818
+ skip: false,
819
+ only: false,
820
+ todo: true,
821
+ });
822
+ };
823
+
824
+ const it = test;
825
+ it.skip = test.skip;
826
+ it.only = test.only;
827
+ it.todo = test.todo;
828
+
829
+ // ============================================================
830
+ // Lifecycle Hooks
831
+ // ============================================================
832
+
833
+ function beforeEach(fn) {
834
+ currentSuite.beforeEach.push(fn);
835
+ }
836
+
837
+ function afterEach(fn) {
838
+ currentSuite.afterEach.push(fn);
839
+ }
840
+
841
+ function beforeAll(fn) {
842
+ currentSuite.beforeAll.push(fn);
843
+ }
844
+
845
+ function afterAll(fn) {
846
+ currentSuite.afterAll.push(fn);
847
+ }
848
+
849
+ // ============================================================
850
+ // Mock Implementation
851
+ // ============================================================
852
+
853
+ const mock = {
854
+ fn(implementation) {
855
+ const mockState = createMockState();
856
+ let defaultImpl = implementation;
857
+ const onceImpls = [];
858
+ const onceReturns = [];
859
+ let returnVal, resolvedVal, rejectedVal;
860
+ let returnSet = false, resolvedSet = false, rejectedSet = false;
861
+
862
+ function mockFn(...args) {
863
+ mockState.calls.push(args);
864
+ mockState.contexts.push(this);
865
+ mockState.lastCall = args;
866
+ mockState.invocationCallOrder.push(++__mockCallOrder);
867
+
868
+ let result;
869
+ try {
870
+ if (onceImpls.length > 0) {
871
+ result = onceImpls.shift().apply(this, args);
872
+ } else if (onceReturns.length > 0) {
873
+ result = onceReturns.shift();
874
+ } else if (returnSet) {
875
+ result = returnVal;
876
+ } else if (resolvedSet) {
877
+ result = Promise.resolve(resolvedVal);
878
+ } else if (rejectedSet) {
879
+ result = Promise.reject(rejectedVal);
880
+ } else if (defaultImpl) {
881
+ result = defaultImpl.apply(this, args);
882
+ }
883
+ mockState.results.push({ type: 'return', value: result });
884
+ return result;
885
+ } catch (e) {
886
+ mockState.results.push({ type: 'throw', value: e });
887
+ throw e;
888
+ }
889
+ }
890
+
891
+ mockFn.__isMockFunction = true;
892
+ mockFn.mock = mockState;
893
+
894
+ // Configuration methods
895
+ mockFn.mockReturnValue = (v) => { returnVal = v; returnSet = true; return mockFn; };
896
+ mockFn.mockReturnValueOnce = (v) => { onceReturns.push(v); return mockFn; };
897
+ mockFn.mockResolvedValue = (v) => { resolvedVal = v; resolvedSet = true; return mockFn; };
898
+ mockFn.mockRejectedValue = (v) => { rejectedVal = v; rejectedSet = true; return mockFn; };
899
+ mockFn.mockImplementation = (fn) => { defaultImpl = fn; return mockFn; };
900
+ mockFn.mockImplementationOnce = (fn) => { onceImpls.push(fn); return mockFn; };
901
+
902
+ // Clearing methods
903
+ mockFn.mockClear = () => {
904
+ mockState.calls = []; mockState.results = []; mockState.contexts = [];
905
+ mockState.instances = []; mockState.invocationCallOrder = []; mockState.lastCall = undefined;
906
+ return mockFn;
907
+ };
908
+ mockFn.mockReset = () => {
909
+ mockFn.mockClear();
910
+ defaultImpl = undefined; returnVal = resolvedVal = rejectedVal = undefined;
911
+ returnSet = resolvedSet = rejectedSet = false;
912
+ onceImpls.length = 0; onceReturns.length = 0;
913
+ return mockFn;
914
+ };
915
+ mockFn.mockRestore = () => mockFn.mockReset();
916
+
917
+ __mockRegistry.push(mockFn);
918
+ return mockFn;
919
+ },
920
+
921
+ spyOn(object, methodName) {
922
+ const original = object[methodName];
923
+ if (typeof original !== 'function') {
924
+ throw new Error(\`Cannot spy on \${methodName}: not a function\`);
925
+ }
926
+ const spy = mock.fn(original);
927
+ spy.__originalMethod = original;
928
+ spy.__spyTarget = object;
929
+ spy.__spyMethodName = methodName;
930
+ spy.__isSpyFunction = true;
931
+ spy.mockRestore = () => {
932
+ object[methodName] = original;
933
+ const idx = __mockRegistry.indexOf(spy);
934
+ if (idx !== -1) __mockRegistry.splice(idx, 1);
935
+ return spy;
936
+ };
937
+ object[methodName] = spy;
938
+ return spy;
939
+ },
940
+
941
+ clearAllMocks() {
942
+ for (const fn of __mockRegistry) fn.mockClear();
943
+ },
944
+
945
+ resetAllMocks() {
946
+ for (const fn of __mockRegistry) fn.mockReset();
947
+ },
948
+
949
+ restoreAllMocks() {
950
+ for (let i = __mockRegistry.length - 1; i >= 0; i--) {
951
+ if (__mockRegistry[i].__isSpyFunction) __mockRegistry[i].mockRestore();
952
+ }
953
+ },
954
+ };
955
+
956
+ // ============================================================
957
+ // Test Runner Helpers
958
+ // ============================================================
959
+
960
+ function checkForOnly(suite) {
961
+ if (suite.only) return true;
962
+ for (const t of suite.tests) {
963
+ if (t.only) return true;
964
+ }
965
+ for (const child of suite.children) {
966
+ if (checkForOnly(child)) return true;
967
+ }
968
+ return false;
969
+ }
970
+
971
+ function suiteHasOnly(suite) {
972
+ if (suite.only) return true;
973
+ for (const t of suite.tests) {
974
+ if (t.only) return true;
975
+ }
976
+ for (const child of suite.children) {
977
+ if (suiteHasOnly(child)) return true;
978
+ }
979
+ return false;
980
+ }
981
+
982
+ function countTests(suite, hasOnly) {
983
+ let count = 0;
984
+ if (hasOnly && !suiteHasOnly(suite)) return 0;
985
+ if (suite.skip) return suite.tests.length;
986
+
987
+ for (const t of suite.tests) {
988
+ if (hasOnly && !t.only && !suite.only) continue;
989
+ count++;
990
+ }
991
+ for (const child of suite.children) {
992
+ count += countTests(child, hasOnly);
993
+ }
994
+ return count;
995
+ }
996
+
997
+ function countSuites(suite, hasOnly) {
998
+ let count = 0;
999
+ if (hasOnly && !suiteHasOnly(suite)) return 0;
1000
+
1001
+ for (const child of suite.children) {
1002
+ count++;
1003
+ count += countSuites(child, hasOnly);
1004
+ }
1005
+ return count;
1006
+ }
1007
+
1008
+ // ============================================================
1009
+ // Test Runner
1010
+ // ============================================================
1011
+
1012
+ async function __runAllTests() {
1013
+ const testResults = [];
1014
+ const suiteResults = [];
1015
+ const hasOnly = checkForOnly(rootSuite);
1016
+ const runStart = Date.now();
1017
+
1018
+ // Emit runStart
1019
+ const testCount = countTests(rootSuite, hasOnly);
1020
+ const suiteCount = countSuites(rootSuite, hasOnly);
1021
+ emitEvent({ type: 'runStart', testCount, suiteCount });
1022
+
1023
+ async function runSuite(suite, parentHooks, pathArray, depth) {
1024
+ // Skip if this suite doesn't have any .only when .only exists elsewhere
1025
+ if (hasOnly && !suiteHasOnly(suite)) return;
1026
+
1027
+ const suitePath = [...pathArray];
1028
+ const fullName = suitePath.join(' > ');
1029
+ const suiteInfo = {
1030
+ name: suite.name,
1031
+ path: suitePath.slice(0, -1),
1032
+ fullName,
1033
+ depth,
1034
+ };
1035
+
1036
+ // Emit suiteStart (only for non-root suites)
1037
+ if (suite !== rootSuite) {
1038
+ emitEvent({ type: 'suiteStart', suite: suiteInfo });
1039
+ }
1040
+
1041
+ const suiteStart = Date.now();
1042
+ let suitePassed = 0;
1043
+ let suiteFailed = 0;
1044
+ let suiteSkipped = 0;
1045
+ let suiteTodo = 0;
1046
+
1047
+ // Skip if suite is marked as skip
1048
+ if (suite.skip) {
1049
+ // Mark all tests in this suite as skipped
1050
+ for (const t of suite.tests) {
1051
+ const testFullName = fullName ? fullName + ' > ' + t.name : t.name;
1052
+ const testInfo = {
1053
+ name: t.name,
1054
+ suitePath: suitePath,
1055
+ fullName: testFullName,
1056
+ };
1057
+ emitEvent({ type: 'testStart', test: testInfo });
1058
+
1059
+ const testResult = {
1060
+ name: t.name,
1061
+ suitePath: suitePath,
1062
+ fullName: testFullName,
1063
+ status: 'skip',
1064
+ duration: 0,
1065
+ };
1066
+ testResults.push(testResult);
1067
+ suiteSkipped++;
1068
+
1069
+ emitEvent({ type: 'testEnd', test: testResult });
1070
+ }
1071
+ } else {
1072
+ // Run beforeAll hooks
1073
+ for (const hook of suite.beforeAll) {
1074
+ await hook();
1075
+ }
1076
+
1077
+ // Run tests
1078
+ for (const t of suite.tests) {
1079
+ const testFullName = fullName ? fullName + ' > ' + t.name : t.name;
1080
+ const testInfo = {
1081
+ name: t.name,
1082
+ suitePath: suitePath,
1083
+ fullName: testFullName,
1084
+ };
1085
+
1086
+ // Skip if .only is used and this test isn't .only AND the suite doesn't have .only
1087
+ if (hasOnly && !t.only && !suite.only) continue;
1088
+
1089
+ emitEvent({ type: 'testStart', test: testInfo });
1090
+
1091
+ // Skip if test is marked as skip
1092
+ if (t.skip) {
1093
+ const testResult = {
1094
+ name: t.name,
1095
+ suitePath: suitePath,
1096
+ fullName: testFullName,
1097
+ status: 'skip',
1098
+ duration: 0,
1099
+ };
1100
+ testResults.push(testResult);
1101
+ suiteSkipped++;
1102
+ emitEvent({ type: 'testEnd', test: testResult });
1103
+ continue;
1104
+ }
1105
+
1106
+ // Handle todo tests (no function provided)
1107
+ if (t.todo) {
1108
+ const testResult = {
1109
+ name: t.name,
1110
+ suitePath: suitePath,
1111
+ fullName: testFullName,
1112
+ status: 'todo',
1113
+ duration: 0,
1114
+ };
1115
+ testResults.push(testResult);
1116
+ suiteTodo++;
1117
+ emitEvent({ type: 'testEnd', test: testResult });
1118
+ continue;
1119
+ }
1120
+
1121
+ const testStart = Date.now();
1122
+ // Reset assertion counting state before each test
1123
+ __expectedAssertions = null;
1124
+ __assertionCount = 0;
1125
+ __hasAssertionsFlag = false;
1126
+
1127
+ try {
1128
+ // Run all beforeEach hooks (parent first, then current)
1129
+ for (const hook of [...parentHooks.beforeEach, ...suite.beforeEach]) {
1130
+ await hook();
1131
+ }
1132
+
1133
+ // Run test
1134
+ await t.fn();
1135
+
1136
+ // Run all afterEach hooks (current first, then parent)
1137
+ for (const hook of [...suite.afterEach, ...parentHooks.afterEach]) {
1138
+ await hook();
1139
+ }
1140
+
1141
+ // Verify assertion counts after test passes
1142
+ if (__hasAssertionsFlag && __assertionCount === 0) {
1143
+ throw new TestError('Expected at least one assertion', 'hasAssertions', '>0', 0);
1144
+ }
1145
+ if (__expectedAssertions !== null && __assertionCount !== __expectedAssertions) {
1146
+ throw new TestError(
1147
+ \`Expected \${__expectedAssertions} assertions, got \${__assertionCount}\`,
1148
+ 'assertions', __expectedAssertions, __assertionCount
1149
+ );
1150
+ }
1151
+
1152
+ const testResult = {
1153
+ name: t.name,
1154
+ suitePath: suitePath,
1155
+ fullName: testFullName,
1156
+ status: 'pass',
1157
+ duration: Date.now() - testStart,
1158
+ };
1159
+ testResults.push(testResult);
1160
+ suitePassed++;
1161
+ emitEvent({ type: 'testEnd', test: testResult });
1162
+ } catch (err) {
1163
+ const testError = {
1164
+ message: err.message || String(err),
1165
+ stack: err.stack,
1166
+ };
1167
+ // If it's a TestError, include matcher info
1168
+ if (err.matcherName !== undefined) {
1169
+ testError.matcherName = err.matcherName;
1170
+ testError.expected = err.expected;
1171
+ testError.actual = err.actual;
1172
+ }
1173
+ const testResult = {
1174
+ name: t.name,
1175
+ suitePath: suitePath,
1176
+ fullName: testFullName,
1177
+ status: 'fail',
1178
+ duration: Date.now() - testStart,
1179
+ error: testError,
1180
+ };
1181
+ testResults.push(testResult);
1182
+ suiteFailed++;
1183
+ emitEvent({ type: 'testEnd', test: testResult });
1184
+ }
1185
+ }
1186
+
1187
+ // Run child suites
1188
+ for (const child of suite.children) {
1189
+ const childPath = [...suitePath, child.name];
1190
+ await runSuite(child, {
1191
+ beforeEach: [...parentHooks.beforeEach, ...suite.beforeEach],
1192
+ afterEach: [...suite.afterEach, ...parentHooks.afterEach],
1193
+ }, childPath, depth + 1);
1194
+ }
1195
+
1196
+ // Run afterAll hooks
1197
+ for (const hook of suite.afterAll) {
1198
+ await hook();
1199
+ }
1200
+ }
1201
+
1202
+ // Emit suiteEnd (only for non-root suites)
1203
+ if (suite !== rootSuite) {
1204
+ const suiteResult = {
1205
+ ...suiteInfo,
1206
+ passed: suitePassed,
1207
+ failed: suiteFailed,
1208
+ skipped: suiteSkipped,
1209
+ todo: suiteTodo,
1210
+ duration: Date.now() - suiteStart,
1211
+ };
1212
+ suiteResults.push(suiteResult);
1213
+ emitEvent({ type: 'suiteEnd', suite: suiteResult });
1214
+ }
1215
+ }
1216
+
1217
+ await runSuite(rootSuite, { beforeEach: [], afterEach: [] }, [], -1);
1218
+
1219
+ const passed = testResults.filter(r => r.status === 'pass').length;
1220
+ const failed = testResults.filter(r => r.status === 'fail').length;
1221
+ const skipped = testResults.filter(r => r.status === 'skip').length;
1222
+ const todo = testResults.filter(r => r.status === 'todo').length;
1223
+
1224
+ const runResults = {
1225
+ passed,
1226
+ failed,
1227
+ skipped,
1228
+ todo,
1229
+ total: testResults.length,
1230
+ duration: Date.now() - runStart,
1231
+ success: failed === 0,
1232
+ suites: suiteResults,
1233
+ tests: testResults,
1234
+ };
1235
+
1236
+ emitEvent({ type: 'runEnd', results: runResults });
1237
+
1238
+ return JSON.stringify(runResults);
1239
+ }
1240
+
1241
+ // ============================================================
1242
+ // Helper Functions
1243
+ // ============================================================
1244
+
1245
+ function __hasTests() {
1246
+ function checkSuite(suite) {
1247
+ if (suite.tests.length > 0) return true;
1248
+ for (const child of suite.children) {
1249
+ if (checkSuite(child)) return true;
1250
+ }
1251
+ return false;
1252
+ }
1253
+ return checkSuite(rootSuite);
1254
+ }
1255
+
1256
+ function __getTestCount() {
1257
+ function countInSuite(suite) {
1258
+ let count = suite.tests.length;
1259
+ for (const child of suite.children) {
1260
+ count += countInSuite(child);
1261
+ }
1262
+ return count;
1263
+ }
1264
+ return countInSuite(rootSuite);
1265
+ }
1266
+
1267
+ // Reset function to clear state between runs
1268
+ function __resetTestEnvironment() {
1269
+ rootSuite.tests = [];
1270
+ rootSuite.children = [];
1271
+ rootSuite.beforeAll = [];
1272
+ rootSuite.afterAll = [];
1273
+ rootSuite.beforeEach = [];
1274
+ rootSuite.afterEach = [];
1275
+ currentSuite = rootSuite;
1276
+ suiteStack.length = 0;
1277
+ suiteStack.push(rootSuite);
1278
+ // Clear mocks
1279
+ __mockCallOrder = 0;
1280
+ mock.restoreAllMocks();
1281
+ __mockRegistry.length = 0;
1282
+ }
1283
+
1284
+ function __setEventCallback(callback) {
1285
+ eventCallback = callback;
1286
+ }
1287
+
1288
+ // ============================================================
1289
+ // Expose Globals
1290
+ // ============================================================
1291
+
1292
+ globalThis.describe = describe;
1293
+ globalThis.test = test;
1294
+ globalThis.it = it;
1295
+ globalThis.expect = expect;
1296
+ globalThis.beforeEach = beforeEach;
1297
+ globalThis.afterEach = afterEach;
1298
+ globalThis.beforeAll = beforeAll;
1299
+ globalThis.afterAll = afterAll;
1300
+ globalThis.mock = mock;
1301
+ globalThis.jest = mock; // Jest compatibility alias
1302
+ globalThis.__runAllTests = __runAllTests;
1303
+ globalThis.__resetTestEnvironment = __resetTestEnvironment;
1304
+ globalThis.__hasTests = __hasTests;
1305
+ globalThis.__getTestCount = __getTestCount;
1306
+ globalThis.__setEventCallback = __setEventCallback;
1307
+ })();
1308
+ `;
1309
+ async function setupTestEnvironment(context, options) {
1310
+ context.evalSync(testEnvironmentCode);
1311
+ if (options?.onEvent) {
1312
+ const eventCallbackRef = new IsolatedVM.Reference((eventJson) => {
1313
+ try {
1314
+ const event = JSON.parse(eventJson);
1315
+ options.onEvent(event);
1316
+ } catch {}
1317
+ });
1318
+ const global = context.global;
1319
+ global.setSync("__eventCallbackRef", eventCallbackRef);
1320
+ context.evalSync(`
1321
+ __setEventCallback((eventJson) => {
1322
+ __eventCallbackRef.applySync(undefined, [eventJson]);
1323
+ });
1324
+ `);
1325
+ }
1326
+ return {
1327
+ dispose() {
1328
+ try {
1329
+ context.evalSync("__resetTestEnvironment()");
1330
+ } catch {}
1331
+ }
1332
+ };
1333
+ }
1334
+ async function runTests(context) {
1335
+ const resultJson = await context.eval("__runAllTests()", { promise: true });
1336
+ return JSON.parse(resultJson);
1337
+ }
1338
+ function hasTests(context) {
1339
+ return context.evalSync("__hasTests()");
1340
+ }
1341
+ function getTestCount(context) {
1342
+ return context.evalSync("__getTestCount()");
1343
+ }
1344
+ export {
1345
+ setupTestEnvironment,
1346
+ runTests,
1347
+ hasTests,
1348
+ getTestCount
1349
+ };
1350
+
1351
+ //# debugId=7B1CD7E5E406645664756E2164756E21