@ricsam/isolate 0.1.4 → 0.1.6

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 (80) hide show
  1. package/README.md +45 -2
  2. package/dist/cjs/internal/async-context/index.cjs +401 -0
  3. package/dist/cjs/internal/async-context/index.cjs.map +10 -0
  4. package/dist/cjs/internal/client/connection.cjs +175 -123
  5. package/dist/cjs/internal/client/connection.cjs.map +3 -3
  6. package/dist/cjs/internal/console/index.cjs +2 -2
  7. package/dist/cjs/internal/console/index.cjs.map +2 -2
  8. package/dist/cjs/internal/core/index.cjs +22 -6
  9. package/dist/cjs/internal/core/index.cjs.map +3 -3
  10. package/dist/cjs/internal/crypto/index.cjs +2 -2
  11. package/dist/cjs/internal/crypto/index.cjs.map +2 -2
  12. package/dist/cjs/internal/daemon/connection.cjs +77 -12
  13. package/dist/cjs/internal/daemon/connection.cjs.map +3 -3
  14. package/dist/cjs/internal/encoding/index.cjs.map +1 -1
  15. package/dist/cjs/internal/fetch/index.cjs +119 -18
  16. package/dist/cjs/internal/fetch/index.cjs.map +3 -3
  17. package/dist/cjs/internal/fetch/stream-state.cjs.map +1 -1
  18. package/dist/cjs/internal/fs/index.cjs +2 -2
  19. package/dist/cjs/internal/fs/index.cjs.map +2 -2
  20. package/dist/cjs/internal/module-loader/bundle.cjs +277 -1
  21. package/dist/cjs/internal/module-loader/bundle.cjs.map +3 -3
  22. package/dist/cjs/internal/path/index.cjs.map +1 -1
  23. package/dist/cjs/internal/playwright/index.cjs +2 -2
  24. package/dist/cjs/internal/playwright/index.cjs.map +2 -2
  25. package/dist/cjs/internal/runtime/index.cjs +78 -6
  26. package/dist/cjs/internal/runtime/index.cjs.map +3 -3
  27. package/dist/cjs/internal/test-environment/index.cjs +2 -2
  28. package/dist/cjs/internal/test-environment/index.cjs.map +2 -2
  29. package/dist/cjs/internal/timers/index.cjs +42 -7
  30. package/dist/cjs/internal/timers/index.cjs.map +3 -3
  31. package/dist/cjs/internal/typecheck/isolate-types.cjs +36 -1
  32. package/dist/cjs/internal/typecheck/isolate-types.cjs.map +3 -3
  33. package/dist/cjs/package.json +1 -1
  34. package/dist/mjs/internal/async-context/index.mjs +361 -0
  35. package/dist/mjs/internal/async-context/index.mjs.map +10 -0
  36. package/dist/mjs/internal/client/connection.mjs +176 -123
  37. package/dist/mjs/internal/client/connection.mjs.map +3 -3
  38. package/dist/mjs/internal/console/index.mjs +2 -2
  39. package/dist/mjs/internal/console/index.mjs.map +2 -2
  40. package/dist/mjs/internal/core/index.mjs +22 -6
  41. package/dist/mjs/internal/core/index.mjs.map +3 -3
  42. package/dist/mjs/internal/crypto/index.mjs +2 -2
  43. package/dist/mjs/internal/crypto/index.mjs.map +2 -2
  44. package/dist/mjs/internal/daemon/connection.mjs +77 -12
  45. package/dist/mjs/internal/daemon/connection.mjs.map +3 -3
  46. package/dist/mjs/internal/encoding/index.mjs.map +1 -1
  47. package/dist/mjs/internal/fetch/index.mjs +119 -18
  48. package/dist/mjs/internal/fetch/index.mjs.map +3 -3
  49. package/dist/mjs/internal/fetch/stream-state.mjs.map +1 -1
  50. package/dist/mjs/internal/fs/index.mjs +2 -2
  51. package/dist/mjs/internal/fs/index.mjs.map +2 -2
  52. package/dist/mjs/internal/module-loader/bundle.mjs +277 -1
  53. package/dist/mjs/internal/module-loader/bundle.mjs.map +3 -3
  54. package/dist/mjs/internal/path/index.mjs.map +1 -1
  55. package/dist/mjs/internal/playwright/index.mjs +2 -2
  56. package/dist/mjs/internal/playwright/index.mjs.map +2 -2
  57. package/dist/mjs/internal/runtime/index.mjs +78 -6
  58. package/dist/mjs/internal/runtime/index.mjs.map +3 -3
  59. package/dist/mjs/internal/test-environment/index.mjs +2 -2
  60. package/dist/mjs/internal/test-environment/index.mjs.map +2 -2
  61. package/dist/mjs/internal/timers/index.mjs +42 -7
  62. package/dist/mjs/internal/timers/index.mjs.map +3 -3
  63. package/dist/mjs/internal/typecheck/isolate-types.mjs +36 -1
  64. package/dist/mjs/internal/typecheck/isolate-types.mjs.map +3 -3
  65. package/dist/mjs/package.json +1 -1
  66. package/dist/types/internal/async-context/index.d.ts +5 -0
  67. package/dist/types/internal/console/index.d.ts +1 -1
  68. package/dist/types/internal/core/index.d.ts +2 -2
  69. package/dist/types/internal/crypto/index.d.ts +1 -1
  70. package/dist/types/internal/daemon/types.d.ts +1 -0
  71. package/dist/types/internal/encoding/index.d.ts +1 -1
  72. package/dist/types/internal/fetch/index.d.ts +1 -1
  73. package/dist/types/internal/fetch/stream-state.d.ts +1 -1
  74. package/dist/types/internal/fs/index.d.ts +1 -1
  75. package/dist/types/internal/path/index.d.ts +1 -1
  76. package/dist/types/internal/playwright/index.d.ts +1 -1
  77. package/dist/types/internal/test-environment/index.d.ts +1 -1
  78. package/dist/types/internal/timers/index.d.ts +1 -1
  79. package/dist/types/internal/typecheck/isolate-types.d.ts +2 -2
  80. package/package.json +8 -3
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/internal/test-environment/index.ts"],
4
4
  "sourcesContent": [
5
- "import type ivm from \"isolated-vm\";\nimport IsolatedVM from \"isolated-vm\";\n\n// ============================================================\n// Test Environment Options\n// ============================================================\n\nexport interface TestEnvironmentOptions {\n /** Receive test lifecycle events */\n onEvent?: (event: TestEvent) => void;\n /** Timeout for individual tests (ms) */\n testTimeout?: number;\n}\n\n// ============================================================\n// Event Types (discriminated union)\n// ============================================================\n\nexport type TestEvent =\n | { type: \"runStart\"; testCount: number; suiteCount: number }\n | { type: \"suiteStart\"; suite: SuiteInfo }\n | { type: \"suiteEnd\"; suite: SuiteResult }\n | { type: \"testStart\"; test: TestInfo }\n | { type: \"testEnd\"; test: TestResult }\n | { type: \"runEnd\"; results: RunResults };\n\n// ============================================================\n// Suite Types\n// ============================================================\n\nexport interface SuiteInfo {\n name: string;\n /** Ancestry path: [\"outer\", \"inner\"] */\n path: string[];\n /** Full display name: \"outer > inner\" */\n fullName: string;\n /** Nesting depth (0 for root-level suites) */\n depth: number;\n}\n\nexport interface SuiteResult extends SuiteInfo {\n passed: number;\n failed: number;\n skipped: number;\n todo: number;\n duration: number;\n}\n\n// ============================================================\n// Test Types\n// ============================================================\n\nexport interface TestInfo {\n name: string;\n /** Suite ancestry */\n suitePath: string[];\n /** Full display name: \"suite > test name\" */\n fullName: string;\n}\n\nexport interface TestResult extends TestInfo {\n status: \"pass\" | \"fail\" | \"skip\" | \"todo\";\n duration: number;\n error?: TestError;\n}\n\nexport interface TestError {\n message: string;\n stack?: string;\n /** For assertion failures */\n expected?: unknown;\n actual?: unknown;\n /** e.g., \"toBe\", \"toEqual\", \"toContain\" */\n matcherName?: string;\n}\n\n// ============================================================\n// Run Results\n// ============================================================\n\nexport interface RunResults {\n passed: number;\n failed: number;\n skipped: number;\n todo: number;\n total: number;\n duration: number;\n success: boolean;\n suites: SuiteResult[];\n tests: TestResult[];\n}\n\n// ============================================================\n// Handle Interface\n// ============================================================\n\nexport interface TestEnvironmentHandle {\n dispose(): void;\n}\n\nconst testEnvironmentCode = `\n(function() {\n // ============================================================\n // Internal State\n // ============================================================\n\n // Mock registry and call counter\n let __mockCallOrder = 0;\n const __mockRegistry = [];\n\n // Assertion counting state\n let __expectedAssertions = null;\n let __assertionCount = 0;\n let __hasAssertionsFlag = false;\n\n function createMockState() {\n return {\n calls: [],\n results: [],\n contexts: [],\n instances: [],\n invocationCallOrder: [],\n lastCall: undefined,\n };\n }\n\n function createSuite(name, skip = false, only = false) {\n return {\n name,\n tests: [],\n children: [],\n beforeAll: [],\n afterAll: [],\n beforeEach: [],\n afterEach: [],\n skip,\n only,\n };\n }\n\n const rootSuite = createSuite('root');\n let currentSuite = rootSuite;\n const suiteStack = [rootSuite];\n\n // Event callback (set from host)\n let eventCallback = null;\n\n function emitEvent(event) {\n if (eventCallback) {\n try {\n eventCallback(JSON.stringify(event));\n } catch (e) {\n // Ignore callback errors\n }\n }\n }\n\n // ============================================================\n // TestError class for rich error info\n // ============================================================\n\n class TestError extends Error {\n constructor(message, matcherName, expected, actual) {\n super(message);\n this.name = 'TestError';\n this.matcherName = matcherName;\n this.expected = expected;\n this.actual = actual;\n }\n }\n\n // ============================================================\n // Asymmetric Matcher Infrastructure\n // ============================================================\n\n const ASYMMETRIC_MATCHER = Symbol('asymmetricMatcher');\n\n function isAsymmetricMatcher(obj) {\n return obj && obj[ASYMMETRIC_MATCHER] === true;\n }\n\n // Deep equality with asymmetric matcher support\n function asymmetricDeepEqual(a, b) {\n if (isAsymmetricMatcher(b)) return b.asymmetricMatch(a);\n if (isAsymmetricMatcher(a)) return a.asymmetricMatch(b);\n if (a === b) return true;\n if (typeof a !== typeof b) return false;\n if (typeof a !== 'object' || a === null || b === null) return false;\n if (Array.isArray(a) !== Array.isArray(b)) return false;\n const keysA = Object.keys(a);\n const keysB = Object.keys(b);\n if (keysA.length !== keysB.length) return false;\n for (const key of keysA) {\n if (!keysB.includes(key)) return false;\n if (!asymmetricDeepEqual(a[key], b[key])) return false;\n }\n return true;\n }\n\n // ============================================================\n // Deep Equality Helper\n // ============================================================\n\n function deepEqual(a, b) {\n if (a === b) return true;\n if (typeof a !== typeof b) return false;\n if (typeof a !== 'object' || a === null || b === null) return false;\n\n if (Array.isArray(a) !== Array.isArray(b)) return false;\n\n const keysA = Object.keys(a);\n const keysB = Object.keys(b);\n if (keysA.length !== keysB.length) return false;\n\n for (const key of keysA) {\n if (!keysB.includes(key)) return false;\n if (!deepEqual(a[key], b[key])) return false;\n }\n return true;\n }\n\n function strictDeepEqual(a, b) {\n if (a === b) return true;\n if (typeof a !== typeof b) return false;\n if (typeof a !== 'object' || a === null || b === null) return false;\n\n // Check prototypes\n if (Object.getPrototypeOf(a) !== Object.getPrototypeOf(b)) return false;\n\n if (Array.isArray(a) !== Array.isArray(b)) return false;\n\n // For arrays, check sparse arrays (holes)\n if (Array.isArray(a)) {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n const aHasIndex = i in a;\n const bHasIndex = i in b;\n if (aHasIndex !== bHasIndex) return false;\n if (aHasIndex && !strictDeepEqual(a[i], b[i])) return false;\n }\n return true;\n }\n\n // Check for undefined properties vs missing properties\n const keysA = Object.keys(a);\n const keysB = Object.keys(b);\n if (keysA.length !== keysB.length) return false;\n\n for (const key of keysA) {\n if (!keysB.includes(key)) return false;\n if (!strictDeepEqual(a[key], b[key])) return false;\n }\n\n // Check for symbol properties\n const symbolsA = Object.getOwnPropertySymbols(a);\n const symbolsB = Object.getOwnPropertySymbols(b);\n if (symbolsA.length !== symbolsB.length) return false;\n\n for (const sym of symbolsA) {\n if (!symbolsB.includes(sym)) return false;\n if (!strictDeepEqual(a[sym], b[sym])) return false;\n }\n\n return true;\n }\n\n function getNestedProperty(obj, path) {\n const parts = path.split('.');\n let current = obj;\n for (const part of parts) {\n if (current == null || !(part in current)) {\n return { exists: false };\n }\n current = current[part];\n }\n return { exists: true, value: current };\n }\n\n function formatValue(val) {\n if (val === null) return 'null';\n if (val === undefined) return 'undefined';\n if (typeof val === 'string') return JSON.stringify(val);\n if (typeof val === 'object') {\n try {\n return JSON.stringify(val);\n } catch {\n return String(val);\n }\n }\n return String(val);\n }\n\n // ============================================================\n // expect() Implementation\n // ============================================================\n\n function expect(actual) {\n function createMatchers(negated = false) {\n const assert = (condition, message, matcherName, expected) => {\n __assertionCount++;\n const pass = negated ? !condition : condition;\n if (!pass) {\n throw new TestError(message, matcherName, expected, actual);\n }\n };\n\n const matchers = {\n toBe(expected) {\n assert(\n actual === expected,\n negated\n ? \\`Expected \\${formatValue(actual)} not to be \\${formatValue(expected)}\\`\n : \\`Expected \\${formatValue(actual)} to be \\${formatValue(expected)}\\`,\n 'toBe',\n expected\n );\n },\n\n toEqual(expected) {\n assert(\n asymmetricDeepEqual(actual, expected),\n negated\n ? \\`Expected \\${formatValue(actual)} not to equal \\${formatValue(expected)}\\`\n : \\`Expected \\${formatValue(actual)} to equal \\${formatValue(expected)}\\`,\n 'toEqual',\n expected\n );\n },\n\n toStrictEqual(expected) {\n assert(\n strictDeepEqual(actual, expected),\n negated\n ? \\`Expected \\${formatValue(actual)} not to strictly equal \\${formatValue(expected)}\\`\n : \\`Expected \\${formatValue(actual)} to strictly equal \\${formatValue(expected)}\\`,\n 'toStrictEqual',\n expected\n );\n },\n\n toBeTruthy() {\n assert(\n !!actual,\n negated\n ? \\`Expected \\${formatValue(actual)} not to be truthy\\`\n : \\`Expected \\${formatValue(actual)} to be truthy\\`,\n 'toBeTruthy',\n true\n );\n },\n\n toBeFalsy() {\n assert(\n !actual,\n negated\n ? \\`Expected \\${formatValue(actual)} not to be falsy\\`\n : \\`Expected \\${formatValue(actual)} to be falsy\\`,\n 'toBeFalsy',\n false\n );\n },\n\n toBeNull() {\n assert(\n actual === null,\n negated\n ? \\`Expected \\${formatValue(actual)} not to be null\\`\n : \\`Expected \\${formatValue(actual)} to be null\\`,\n 'toBeNull',\n null\n );\n },\n\n toBeUndefined() {\n assert(\n actual === undefined,\n negated\n ? \\`Expected \\${formatValue(actual)} not to be undefined\\`\n : \\`Expected \\${formatValue(actual)} to be undefined\\`,\n 'toBeUndefined',\n undefined\n );\n },\n\n toBeDefined() {\n assert(\n actual !== undefined,\n negated\n ? \\`Expected \\${formatValue(actual)} not to be defined\\`\n : \\`Expected \\${formatValue(actual)} to be defined\\`,\n 'toBeDefined',\n 'defined'\n );\n },\n\n toContain(item) {\n let contains = false;\n if (Array.isArray(actual)) {\n contains = actual.includes(item);\n } else if (typeof actual === 'string') {\n contains = actual.includes(item);\n }\n assert(\n contains,\n negated\n ? \\`Expected \\${formatValue(actual)} not to contain \\${formatValue(item)}\\`\n : \\`Expected \\${formatValue(actual)} to contain \\${formatValue(item)}\\`,\n 'toContain',\n item\n );\n },\n\n toThrow(expected) {\n if (typeof actual !== 'function') {\n throw new Error('toThrow requires a function');\n }\n\n let threw = false;\n let error = null;\n try {\n actual();\n } catch (e) {\n threw = true;\n error = e;\n }\n\n if (expected !== undefined) {\n const matches = threw && (\n (typeof expected === 'string' && error.message.includes(expected)) ||\n (expected instanceof RegExp && expected.test(error.message)) ||\n (typeof expected === 'function' && error instanceof expected)\n );\n assert(\n matches,\n negated\n ? \\`Expected function not to throw \\${formatValue(expected)}\\`\n : \\`Expected function to throw \\${formatValue(expected)}, but \\${threw ? \\`threw: \\${error.message}\\` : 'did not throw'}\\`,\n 'toThrow',\n expected\n );\n } else {\n assert(\n threw,\n negated\n ? \\`Expected function not to throw\\`\n : \\`Expected function to throw\\`,\n 'toThrow',\n 'any error'\n );\n }\n },\n\n toBeInstanceOf(cls) {\n assert(\n actual instanceof cls,\n negated\n ? \\`Expected \\${formatValue(actual)} not to be instance of \\${cls.name || cls}\\`\n : \\`Expected \\${formatValue(actual)} to be instance of \\${cls.name || cls}\\`,\n 'toBeInstanceOf',\n cls.name || cls\n );\n },\n\n toHaveLength(length) {\n const actualLength = actual?.length;\n assert(\n actualLength === length,\n negated\n ? \\`Expected length not to be \\${length}, but got \\${actualLength}\\`\n : \\`Expected length to be \\${length}, but got \\${actualLength}\\`,\n 'toHaveLength',\n length\n );\n },\n\n toMatch(pattern) {\n let matches = false;\n if (typeof pattern === 'string') {\n matches = actual.includes(pattern);\n } else if (pattern instanceof RegExp) {\n matches = pattern.test(actual);\n }\n assert(\n matches,\n negated\n ? \\`Expected \\${formatValue(actual)} not to match \\${pattern}\\`\n : \\`Expected \\${formatValue(actual)} to match \\${pattern}\\`,\n 'toMatch',\n pattern\n );\n },\n\n toHaveProperty(path, value) {\n const prop = getNestedProperty(actual, path);\n const hasProperty = prop.exists;\n const valueMatches = arguments.length < 2 || asymmetricDeepEqual(prop.value, value);\n\n assert(\n hasProperty && valueMatches,\n negated\n ? \\`Expected \\${formatValue(actual)} not to have property \\${path}\\${arguments.length >= 2 ? \\` with value \\${formatValue(value)}\\` : ''}\\`\n : \\`Expected \\${formatValue(actual)} to have property \\${path}\\${arguments.length >= 2 ? \\` with value \\${formatValue(value)}\\` : ''}\\`,\n 'toHaveProperty',\n arguments.length >= 2 ? { path, value } : { path }\n );\n },\n\n toBeGreaterThan(expected) {\n assert(\n actual > expected,\n negated\n ? \\`Expected \\${formatValue(actual)} not to be greater than \\${formatValue(expected)}\\`\n : \\`Expected \\${formatValue(actual)} to be greater than \\${formatValue(expected)}\\`,\n 'toBeGreaterThan',\n expected\n );\n },\n\n toBeGreaterThanOrEqual(expected) {\n assert(\n actual >= expected,\n negated\n ? \\`Expected \\${formatValue(actual)} not to be greater than or equal to \\${formatValue(expected)}\\`\n : \\`Expected \\${formatValue(actual)} to be greater than or equal to \\${formatValue(expected)}\\`,\n 'toBeGreaterThanOrEqual',\n expected\n );\n },\n\n toBeLessThan(expected) {\n assert(\n actual < expected,\n negated\n ? \\`Expected \\${formatValue(actual)} not to be less than \\${formatValue(expected)}\\`\n : \\`Expected \\${formatValue(actual)} to be less than \\${formatValue(expected)}\\`,\n 'toBeLessThan',\n expected\n );\n },\n\n toBeLessThanOrEqual(expected) {\n assert(\n actual <= expected,\n negated\n ? \\`Expected \\${formatValue(actual)} not to be less than or equal to \\${formatValue(expected)}\\`\n : \\`Expected \\${formatValue(actual)} to be less than or equal to \\${formatValue(expected)}\\`,\n 'toBeLessThanOrEqual',\n expected\n );\n },\n\n toBeCloseTo(expected, numDigits = 2) {\n const precision = Math.pow(10, -numDigits) / 2;\n const pass = Math.abs(actual - expected) < precision;\n assert(\n pass,\n negated\n ? \\`Expected \\${formatValue(actual)} not to be close to \\${formatValue(expected)} (precision: \\${numDigits} digits)\\`\n : \\`Expected \\${formatValue(actual)} to be close to \\${formatValue(expected)} (precision: \\${numDigits} digits)\\`,\n 'toBeCloseTo',\n expected\n );\n },\n\n toBeNaN() {\n assert(\n Number.isNaN(actual),\n negated\n ? \\`Expected \\${formatValue(actual)} not to be NaN\\`\n : \\`Expected \\${formatValue(actual)} to be NaN\\`,\n 'toBeNaN',\n NaN\n );\n },\n\n toMatchObject(expected) {\n function matchesObject(obj, pattern) {\n if (isAsymmetricMatcher(pattern)) return pattern.asymmetricMatch(obj);\n if (typeof pattern !== 'object' || pattern === null) return obj === pattern;\n if (typeof obj !== 'object' || obj === null) return false;\n for (const key of Object.keys(pattern)) {\n if (!(key in obj)) return false;\n if (!matchesObject(obj[key], pattern[key])) return false;\n }\n return true;\n }\n assert(\n matchesObject(actual, expected),\n negated\n ? \\`Expected \\${formatValue(actual)} not to match object \\${formatValue(expected)}\\`\n : \\`Expected \\${formatValue(actual)} to match object \\${formatValue(expected)}\\`,\n 'toMatchObject',\n expected\n );\n },\n\n toContainEqual(item) {\n const contains = Array.isArray(actual) && actual.some(el => asymmetricDeepEqual(el, item));\n assert(\n contains,\n negated\n ? \\`Expected array not to contain equal \\${formatValue(item)}\\`\n : \\`Expected array to contain equal \\${formatValue(item)}\\`,\n 'toContainEqual',\n item\n );\n },\n\n toBeTypeOf(expectedType) {\n const actualType = typeof actual;\n assert(\n actualType === expectedType,\n negated\n ? \\`Expected typeof \\${formatValue(actual)} not to be \"\\${expectedType}\"\\`\n : \\`Expected typeof \\${formatValue(actual)} to be \"\\${expectedType}\", got \"\\${actualType}\"\\`,\n 'toBeTypeOf',\n expectedType\n );\n },\n\n // Mock matchers\n toHaveBeenCalled() {\n if (!actual.__isMockFunction) throw new Error('toHaveBeenCalled requires a mock function');\n assert(actual.mock.calls.length > 0,\n negated ? \\`Expected mock not to have been called\\` : \\`Expected mock to have been called\\`,\n 'toHaveBeenCalled', 'called');\n },\n\n toHaveBeenCalledTimes(n) {\n if (!actual.__isMockFunction) throw new Error('toHaveBeenCalledTimes requires a mock function');\n assert(actual.mock.calls.length === n,\n negated ? \\`Expected mock not to have been called \\${n} times\\`\n : \\`Expected mock to be called \\${n} times, got \\${actual.mock.calls.length}\\`,\n 'toHaveBeenCalledTimes', n);\n },\n\n toHaveBeenCalledWith(...expectedArgs) {\n if (!actual.__isMockFunction) throw new Error('toHaveBeenCalledWith requires a mock function');\n const match = actual.mock.calls.some(args => asymmetricDeepEqual(args, expectedArgs));\n assert(match,\n negated ? \\`Expected mock not to have been called with \\${formatValue(expectedArgs)}\\`\n : \\`Expected mock to have been called with \\${formatValue(expectedArgs)}\\`,\n 'toHaveBeenCalledWith', expectedArgs);\n },\n\n toHaveBeenLastCalledWith(...expectedArgs) {\n if (!actual.__isMockFunction) throw new Error('toHaveBeenLastCalledWith requires a mock function');\n assert(actual.mock.lastCall && asymmetricDeepEqual(actual.mock.lastCall, expectedArgs),\n negated ? \\`Expected last call not to be \\${formatValue(expectedArgs)}\\`\n : \\`Expected last call to be \\${formatValue(expectedArgs)}, got \\${formatValue(actual.mock.lastCall)}\\`,\n 'toHaveBeenLastCalledWith', expectedArgs);\n },\n\n toHaveBeenNthCalledWith(n, ...expectedArgs) {\n if (!actual.__isMockFunction) throw new Error('toHaveBeenNthCalledWith requires a mock function');\n const nthCall = actual.mock.calls[n - 1];\n assert(nthCall && asymmetricDeepEqual(nthCall, expectedArgs),\n negated ? \\`Expected call \\${n} not to be \\${formatValue(expectedArgs)}\\`\n : \\`Expected call \\${n} to be \\${formatValue(expectedArgs)}, got \\${formatValue(nthCall)}\\`,\n 'toHaveBeenNthCalledWith', expectedArgs);\n },\n\n toHaveReturned() {\n if (!actual.__isMockFunction) throw new Error('toHaveReturned requires a mock function');\n const hasReturned = actual.mock.results.some(r => r.type === 'return');\n assert(hasReturned,\n negated ? \\`Expected mock not to have returned\\` : \\`Expected mock to have returned\\`,\n 'toHaveReturned', 'returned');\n },\n\n toHaveReturnedWith(value) {\n if (!actual.__isMockFunction) throw new Error('toHaveReturnedWith requires a mock function');\n const match = actual.mock.results.some(r => r.type === 'return' && asymmetricDeepEqual(r.value, value));\n assert(match,\n negated ? \\`Expected mock not to have returned \\${formatValue(value)}\\`\n : \\`Expected mock to have returned \\${formatValue(value)}\\`,\n 'toHaveReturnedWith', value);\n },\n\n toHaveLastReturnedWith(value) {\n if (!actual.__isMockFunction) throw new Error('toHaveLastReturnedWith requires a mock function');\n const returns = actual.mock.results.filter(r => r.type === 'return');\n const last = returns[returns.length - 1];\n assert(last && asymmetricDeepEqual(last.value, value),\n negated ? \\`Expected last return not to be \\${formatValue(value)}\\`\n : \\`Expected last return to be \\${formatValue(value)}, got \\${formatValue(last?.value)}\\`,\n 'toHaveLastReturnedWith', value);\n },\n\n toHaveReturnedTimes(n) {\n if (!actual.__isMockFunction) throw new Error('toHaveReturnedTimes requires a mock function');\n const returnCount = actual.mock.results.filter(r => r.type === 'return').length;\n assert(returnCount === n,\n negated ? \\`Expected mock not to have returned \\${n} times\\`\n : \\`Expected mock to have returned \\${n} times, got \\${returnCount}\\`,\n 'toHaveReturnedTimes', n);\n },\n\n toHaveNthReturnedWith(n, value) {\n if (!actual.__isMockFunction) throw new Error('toHaveNthReturnedWith requires a mock function');\n const returns = actual.mock.results.filter(r => r.type === 'return');\n const nthReturn = returns[n - 1];\n assert(nthReturn && asymmetricDeepEqual(nthReturn.value, value),\n negated ? \\`Expected return \\${n} not to be \\${formatValue(value)}\\`\n : \\`Expected return \\${n} to be \\${formatValue(value)}, got \\${formatValue(nthReturn?.value)}\\`,\n 'toHaveNthReturnedWith', value);\n },\n };\n\n return matchers;\n }\n\n const matchers = createMatchers(false);\n matchers.not = createMatchers(true);\n\n // Promise matchers using Proxy\n matchers.resolves = new Proxy({}, {\n get(_, matcherName) {\n if (matcherName === 'not') {\n return new Proxy({}, {\n get(_, negatedMatcherName) {\n return async (...args) => {\n const result = await actual;\n return expect(result).not[negatedMatcherName](...args);\n };\n }\n });\n }\n return async (...args) => {\n const result = await actual;\n return expect(result)[matcherName](...args);\n };\n }\n });\n\n matchers.rejects = new Proxy({}, {\n get(_, matcherName) {\n if (matcherName === 'not') {\n return new Proxy({}, {\n get(_, negatedMatcherName) {\n return async (...args) => {\n let error;\n try {\n await actual;\n throw new TestError('Expected promise to reject', 'rejects', 'rejection', undefined);\n } catch (e) {\n if (e instanceof TestError && e.matcherName === 'rejects') throw e;\n error = e;\n }\n return expect(error).not[negatedMatcherName](...args);\n };\n }\n });\n }\n return async (...args) => {\n let error;\n try {\n await actual;\n throw new TestError('Expected promise to reject', 'rejects', 'rejection', undefined);\n } catch (e) {\n if (e instanceof TestError && e.matcherName === 'rejects') throw e;\n error = e;\n }\n return expect(error)[matcherName](...args);\n };\n }\n });\n\n return matchers;\n }\n\n // Asymmetric matcher implementations\n expect.anything = () => ({\n [ASYMMETRIC_MATCHER]: true,\n asymmetricMatch: (other) => other !== null && other !== undefined,\n toString: () => 'anything()',\n });\n\n expect.any = (constructor) => ({\n [ASYMMETRIC_MATCHER]: true,\n asymmetricMatch: (other) => {\n if (constructor === String) return typeof other === 'string' || other instanceof String;\n if (constructor === Number) return typeof other === 'number' || other instanceof Number;\n if (constructor === Boolean) return typeof other === 'boolean' || other instanceof Boolean;\n if (constructor === Function) return typeof other === 'function';\n if (constructor === Object) return typeof other === 'object' && other !== null;\n if (constructor === Array) return Array.isArray(other);\n return other instanceof constructor;\n },\n toString: () => \\`any(\\${constructor.name || constructor})\\`,\n });\n\n expect.stringContaining = (str) => ({\n [ASYMMETRIC_MATCHER]: true,\n asymmetricMatch: (other) => typeof other === 'string' && other.includes(str),\n toString: () => \\`stringContaining(\"\\${str}\")\\`,\n });\n\n expect.stringMatching = (pattern) => ({\n [ASYMMETRIC_MATCHER]: true,\n asymmetricMatch: (other) => {\n if (typeof other !== 'string') return false;\n return typeof pattern === 'string' ? other.includes(pattern) : pattern.test(other);\n },\n toString: () => \\`stringMatching(\\${pattern})\\`,\n });\n\n expect.arrayContaining = (expected) => ({\n [ASYMMETRIC_MATCHER]: true,\n asymmetricMatch: (other) => {\n if (!Array.isArray(other)) return false;\n return expected.every(exp => other.some(item => asymmetricDeepEqual(item, exp)));\n },\n toString: () => \\`arrayContaining(\\${formatValue(expected)})\\`,\n });\n\n expect.objectContaining = (expected) => ({\n [ASYMMETRIC_MATCHER]: true,\n asymmetricMatch: (other) => {\n if (typeof other !== 'object' || other === null) return false;\n for (const key of Object.keys(expected)) {\n if (!(key in other)) return false;\n if (!asymmetricDeepEqual(other[key], expected[key])) return false;\n }\n return true;\n },\n toString: () => \\`objectContaining(\\${formatValue(expected)})\\`,\n });\n\n // Assertion counting\n expect.assertions = (n) => {\n __expectedAssertions = n;\n };\n\n expect.hasAssertions = () => {\n __hasAssertionsFlag = true;\n };\n\n // ============================================================\n // Test Registration Functions\n // ============================================================\n\n function describe(name, fn) {\n const suite = createSuite(name);\n currentSuite.children.push(suite);\n\n const parentSuite = currentSuite;\n currentSuite = suite;\n suiteStack.push(suite);\n\n fn();\n\n suiteStack.pop();\n currentSuite = parentSuite;\n }\n\n describe.skip = function(name, fn) {\n const suite = createSuite(name, true, false);\n currentSuite.children.push(suite);\n\n const parentSuite = currentSuite;\n currentSuite = suite;\n suiteStack.push(suite);\n\n fn();\n\n suiteStack.pop();\n currentSuite = parentSuite;\n };\n\n describe.only = function(name, fn) {\n const suite = createSuite(name, false, true);\n currentSuite.children.push(suite);\n\n const parentSuite = currentSuite;\n currentSuite = suite;\n suiteStack.push(suite);\n\n fn();\n\n suiteStack.pop();\n currentSuite = parentSuite;\n };\n\n function test(name, fn) {\n currentSuite.tests.push({\n name,\n fn,\n skip: false,\n only: false,\n });\n }\n\n test.skip = function(name, fn) {\n currentSuite.tests.push({\n name,\n fn,\n skip: true,\n only: false,\n });\n };\n\n test.only = function(name, fn) {\n currentSuite.tests.push({\n name,\n fn,\n skip: false,\n only: true,\n });\n };\n\n test.todo = function(name) {\n currentSuite.tests.push({\n name,\n fn: null,\n skip: false,\n only: false,\n todo: true,\n });\n };\n\n const it = test;\n it.skip = test.skip;\n it.only = test.only;\n it.todo = test.todo;\n\n // ============================================================\n // Lifecycle Hooks\n // ============================================================\n\n function beforeEach(fn) {\n currentSuite.beforeEach.push(fn);\n }\n\n function afterEach(fn) {\n currentSuite.afterEach.push(fn);\n }\n\n function beforeAll(fn) {\n currentSuite.beforeAll.push(fn);\n }\n\n function afterAll(fn) {\n currentSuite.afterAll.push(fn);\n }\n\n // ============================================================\n // Mock Implementation\n // ============================================================\n\n const mock = {\n fn(implementation) {\n const mockState = createMockState();\n let defaultImpl = implementation;\n const onceImpls = [];\n const onceReturns = [];\n let returnVal, resolvedVal, rejectedVal;\n let returnSet = false, resolvedSet = false, rejectedSet = false;\n\n function mockFn(...args) {\n mockState.calls.push(args);\n mockState.contexts.push(this);\n mockState.lastCall = args;\n mockState.invocationCallOrder.push(++__mockCallOrder);\n\n let result;\n try {\n if (onceImpls.length > 0) {\n result = onceImpls.shift().apply(this, args);\n } else if (onceReturns.length > 0) {\n result = onceReturns.shift();\n } else if (returnSet) {\n result = returnVal;\n } else if (resolvedSet) {\n result = Promise.resolve(resolvedVal);\n } else if (rejectedSet) {\n result = Promise.reject(rejectedVal);\n } else if (defaultImpl) {\n result = defaultImpl.apply(this, args);\n }\n mockState.results.push({ type: 'return', value: result });\n return result;\n } catch (e) {\n mockState.results.push({ type: 'throw', value: e });\n throw e;\n }\n }\n\n mockFn.__isMockFunction = true;\n mockFn.mock = mockState;\n\n // Configuration methods\n mockFn.mockReturnValue = (v) => { returnVal = v; returnSet = true; return mockFn; };\n mockFn.mockReturnValueOnce = (v) => { onceReturns.push(v); return mockFn; };\n mockFn.mockResolvedValue = (v) => { resolvedVal = v; resolvedSet = true; return mockFn; };\n mockFn.mockRejectedValue = (v) => { rejectedVal = v; rejectedSet = true; return mockFn; };\n mockFn.mockImplementation = (fn) => { defaultImpl = fn; return mockFn; };\n mockFn.mockImplementationOnce = (fn) => { onceImpls.push(fn); return mockFn; };\n\n // Clearing methods\n mockFn.mockClear = () => {\n mockState.calls = []; mockState.results = []; mockState.contexts = [];\n mockState.instances = []; mockState.invocationCallOrder = []; mockState.lastCall = undefined;\n return mockFn;\n };\n mockFn.mockReset = () => {\n mockFn.mockClear();\n defaultImpl = undefined; returnVal = resolvedVal = rejectedVal = undefined;\n returnSet = resolvedSet = rejectedSet = false;\n onceImpls.length = 0; onceReturns.length = 0;\n return mockFn;\n };\n mockFn.mockRestore = () => mockFn.mockReset();\n\n __mockRegistry.push(mockFn);\n return mockFn;\n },\n\n spyOn(object, methodName) {\n const original = object[methodName];\n if (typeof original !== 'function') {\n throw new Error(\\`Cannot spy on \\${methodName}: not a function\\`);\n }\n const spy = mock.fn(original);\n spy.__originalMethod = original;\n spy.__spyTarget = object;\n spy.__spyMethodName = methodName;\n spy.__isSpyFunction = true;\n spy.mockRestore = () => {\n object[methodName] = original;\n const idx = __mockRegistry.indexOf(spy);\n if (idx !== -1) __mockRegistry.splice(idx, 1);\n return spy;\n };\n object[methodName] = spy;\n return spy;\n },\n\n clearAllMocks() {\n for (const fn of __mockRegistry) fn.mockClear();\n },\n\n resetAllMocks() {\n for (const fn of __mockRegistry) fn.mockReset();\n },\n\n restoreAllMocks() {\n for (let i = __mockRegistry.length - 1; i >= 0; i--) {\n if (__mockRegistry[i].__isSpyFunction) __mockRegistry[i].mockRestore();\n }\n },\n };\n\n // ============================================================\n // Test Runner Helpers\n // ============================================================\n\n function checkForOnly(suite) {\n if (suite.only) return true;\n for (const t of suite.tests) {\n if (t.only) return true;\n }\n for (const child of suite.children) {\n if (checkForOnly(child)) return true;\n }\n return false;\n }\n\n function suiteHasOnly(suite) {\n if (suite.only) return true;\n for (const t of suite.tests) {\n if (t.only) return true;\n }\n for (const child of suite.children) {\n if (suiteHasOnly(child)) return true;\n }\n return false;\n }\n\n function countTests(suite, hasOnly) {\n let count = 0;\n if (hasOnly && !suiteHasOnly(suite)) return 0;\n if (suite.skip) return suite.tests.length;\n\n for (const t of suite.tests) {\n if (hasOnly && !t.only && !suite.only) continue;\n count++;\n }\n for (const child of suite.children) {\n count += countTests(child, hasOnly);\n }\n return count;\n }\n\n function countSuites(suite, hasOnly) {\n let count = 0;\n if (hasOnly && !suiteHasOnly(suite)) return 0;\n\n for (const child of suite.children) {\n count++;\n count += countSuites(child, hasOnly);\n }\n return count;\n }\n\n // ============================================================\n // Test Runner\n // ============================================================\n\n async function __runAllTests() {\n const testResults = [];\n const suiteResults = [];\n const hasOnly = checkForOnly(rootSuite);\n const runStart = Date.now();\n\n // Emit runStart\n const testCount = countTests(rootSuite, hasOnly);\n const suiteCount = countSuites(rootSuite, hasOnly);\n emitEvent({ type: 'runStart', testCount, suiteCount });\n\n async function runSuite(suite, parentHooks, pathArray, depth) {\n // Skip if this suite doesn't have any .only when .only exists elsewhere\n if (hasOnly && !suiteHasOnly(suite)) return;\n\n const suitePath = [...pathArray];\n const fullName = suitePath.join(' > ');\n const suiteInfo = {\n name: suite.name,\n path: suitePath.slice(0, -1),\n fullName,\n depth,\n };\n\n // Emit suiteStart (only for non-root suites)\n if (suite !== rootSuite) {\n emitEvent({ type: 'suiteStart', suite: suiteInfo });\n }\n\n const suiteStart = Date.now();\n let suitePassed = 0;\n let suiteFailed = 0;\n let suiteSkipped = 0;\n let suiteTodo = 0;\n\n // Skip if suite is marked as skip\n if (suite.skip) {\n // Mark all tests in this suite as skipped\n for (const t of suite.tests) {\n const testFullName = fullName ? fullName + ' > ' + t.name : t.name;\n const testInfo = {\n name: t.name,\n suitePath: suitePath,\n fullName: testFullName,\n };\n emitEvent({ type: 'testStart', test: testInfo });\n\n const testResult = {\n name: t.name,\n suitePath: suitePath,\n fullName: testFullName,\n status: 'skip',\n duration: 0,\n };\n testResults.push(testResult);\n suiteSkipped++;\n\n emitEvent({ type: 'testEnd', test: testResult });\n }\n } else {\n // Run beforeAll hooks\n for (const hook of suite.beforeAll) {\n await hook();\n }\n\n // Run tests\n for (const t of suite.tests) {\n const testFullName = fullName ? fullName + ' > ' + t.name : t.name;\n const testInfo = {\n name: t.name,\n suitePath: suitePath,\n fullName: testFullName,\n };\n\n // Skip if .only is used and this test isn't .only AND the suite doesn't have .only\n if (hasOnly && !t.only && !suite.only) continue;\n\n emitEvent({ type: 'testStart', test: testInfo });\n\n // Skip if test is marked as skip\n if (t.skip) {\n const testResult = {\n name: t.name,\n suitePath: suitePath,\n fullName: testFullName,\n status: 'skip',\n duration: 0,\n };\n testResults.push(testResult);\n suiteSkipped++;\n emitEvent({ type: 'testEnd', test: testResult });\n continue;\n }\n\n // Handle todo tests (no function provided)\n if (t.todo) {\n const testResult = {\n name: t.name,\n suitePath: suitePath,\n fullName: testFullName,\n status: 'todo',\n duration: 0,\n };\n testResults.push(testResult);\n suiteTodo++;\n emitEvent({ type: 'testEnd', test: testResult });\n continue;\n }\n\n const testStart = Date.now();\n // Reset assertion counting state before each test\n __expectedAssertions = null;\n __assertionCount = 0;\n __hasAssertionsFlag = false;\n\n try {\n // Run all beforeEach hooks (parent first, then current)\n for (const hook of [...parentHooks.beforeEach, ...suite.beforeEach]) {\n await hook();\n }\n\n // Run test\n await t.fn();\n\n // Run all afterEach hooks (current first, then parent)\n for (const hook of [...suite.afterEach, ...parentHooks.afterEach]) {\n await hook();\n }\n\n // Verify assertion counts after test passes\n if (__hasAssertionsFlag && __assertionCount === 0) {\n throw new TestError('Expected at least one assertion', 'hasAssertions', '>0', 0);\n }\n if (__expectedAssertions !== null && __assertionCount !== __expectedAssertions) {\n throw new TestError(\n \\`Expected \\${__expectedAssertions} assertions, got \\${__assertionCount}\\`,\n 'assertions', __expectedAssertions, __assertionCount\n );\n }\n\n const testResult = {\n name: t.name,\n suitePath: suitePath,\n fullName: testFullName,\n status: 'pass',\n duration: Date.now() - testStart,\n };\n testResults.push(testResult);\n suitePassed++;\n emitEvent({ type: 'testEnd', test: testResult });\n } catch (err) {\n const testError = {\n message: err.message || String(err),\n stack: err.stack,\n };\n // If it's a TestError, include matcher info\n if (err.matcherName !== undefined) {\n testError.matcherName = err.matcherName;\n testError.expected = err.expected;\n testError.actual = err.actual;\n }\n const testResult = {\n name: t.name,\n suitePath: suitePath,\n fullName: testFullName,\n status: 'fail',\n duration: Date.now() - testStart,\n error: testError,\n };\n testResults.push(testResult);\n suiteFailed++;\n emitEvent({ type: 'testEnd', test: testResult });\n }\n }\n\n // Run child suites\n for (const child of suite.children) {\n const childPath = [...suitePath, child.name];\n await runSuite(child, {\n beforeEach: [...parentHooks.beforeEach, ...suite.beforeEach],\n afterEach: [...suite.afterEach, ...parentHooks.afterEach],\n }, childPath, depth + 1);\n }\n\n // Run afterAll hooks\n for (const hook of suite.afterAll) {\n await hook();\n }\n }\n\n // Emit suiteEnd (only for non-root suites)\n if (suite !== rootSuite) {\n const suiteResult = {\n ...suiteInfo,\n passed: suitePassed,\n failed: suiteFailed,\n skipped: suiteSkipped,\n todo: suiteTodo,\n duration: Date.now() - suiteStart,\n };\n suiteResults.push(suiteResult);\n emitEvent({ type: 'suiteEnd', suite: suiteResult });\n }\n }\n\n await runSuite(rootSuite, { beforeEach: [], afterEach: [] }, [], -1);\n\n const passed = testResults.filter(r => r.status === 'pass').length;\n const failed = testResults.filter(r => r.status === 'fail').length;\n const skipped = testResults.filter(r => r.status === 'skip').length;\n const todo = testResults.filter(r => r.status === 'todo').length;\n\n const runResults = {\n passed,\n failed,\n skipped,\n todo,\n total: testResults.length,\n duration: Date.now() - runStart,\n success: failed === 0,\n suites: suiteResults,\n tests: testResults,\n };\n\n emitEvent({ type: 'runEnd', results: runResults });\n\n return JSON.stringify(runResults);\n }\n\n // ============================================================\n // Helper Functions\n // ============================================================\n\n function __hasTests() {\n function checkSuite(suite) {\n if (suite.tests.length > 0) return true;\n for (const child of suite.children) {\n if (checkSuite(child)) return true;\n }\n return false;\n }\n return checkSuite(rootSuite);\n }\n\n function __getTestCount() {\n function countInSuite(suite) {\n let count = suite.tests.length;\n for (const child of suite.children) {\n count += countInSuite(child);\n }\n return count;\n }\n return countInSuite(rootSuite);\n }\n\n // Reset function to clear state between runs\n function __resetTestEnvironment() {\n rootSuite.tests = [];\n rootSuite.children = [];\n rootSuite.beforeAll = [];\n rootSuite.afterAll = [];\n rootSuite.beforeEach = [];\n rootSuite.afterEach = [];\n currentSuite = rootSuite;\n suiteStack.length = 0;\n suiteStack.push(rootSuite);\n // Clear mocks\n __mockCallOrder = 0;\n mock.restoreAllMocks();\n __mockRegistry.length = 0;\n }\n\n function __setEventCallback(callback) {\n eventCallback = callback;\n }\n\n // ============================================================\n // Expose Globals\n // ============================================================\n\n globalThis.describe = describe;\n globalThis.test = test;\n globalThis.it = it;\n globalThis.expect = expect;\n globalThis.beforeEach = beforeEach;\n globalThis.afterEach = afterEach;\n globalThis.beforeAll = beforeAll;\n globalThis.afterAll = afterAll;\n globalThis.mock = mock;\n globalThis.jest = mock; // Jest compatibility alias\n globalThis.__runAllTests = __runAllTests;\n globalThis.__resetTestEnvironment = __resetTestEnvironment;\n globalThis.__hasTests = __hasTests;\n globalThis.__getTestCount = __getTestCount;\n globalThis.__setEventCallback = __setEventCallback;\n})();\n`;\n\n/**\n * Setup test environment primitives in an isolated-vm context\n *\n * Provides Jest/Vitest-compatible test primitives:\n * - describe, test, it\n * - beforeEach, afterEach, beforeAll, afterAll\n * - expect matchers\n *\n * @example\n * const handle = await setupTestEnvironment(context, {\n * onEvent: (event) => console.log(event),\n * });\n *\n * await context.eval(`\n * describe(\"my tests\", () => {\n * test(\"example\", () => {\n * expect(1 + 1).toBe(2);\n * });\n * });\n * `);\n */\nexport async function setupTestEnvironment(\n context: ivm.Context,\n options?: TestEnvironmentOptions\n): Promise<TestEnvironmentHandle> {\n context.evalSync(testEnvironmentCode);\n\n // Set up event callback if provided\n if (options?.onEvent) {\n const eventCallbackRef = new IsolatedVM.Reference((eventJson: string) => {\n try {\n const event = JSON.parse(eventJson);\n options.onEvent!(event);\n } catch {\n // Ignore parse errors\n }\n });\n\n const global = context.global;\n global.setSync(\"__eventCallbackRef\", eventCallbackRef);\n context.evalSync(`\n __setEventCallback((eventJson) => {\n __eventCallbackRef.applySync(undefined, [eventJson]);\n });\n `);\n }\n\n return {\n dispose() {\n // Reset the test environment state\n try {\n context.evalSync(\"__resetTestEnvironment()\");\n } catch {\n // Context may already be released\n }\n },\n };\n}\n\n/**\n * Run tests in the context and return results\n */\nexport async function runTests(context: ivm.Context): Promise<RunResults> {\n const resultJson = await context.eval(\"__runAllTests()\", { promise: true });\n return JSON.parse(resultJson as string);\n}\n\n/**\n * Check if any tests are registered\n */\nexport function hasTests(context: ivm.Context): boolean {\n return context.evalSync(\"__hasTests()\") as boolean;\n}\n\n/**\n * Get the count of registered tests\n */\nexport function getTestCount(context: ivm.Context): number {\n return context.evalSync(\"__getTestCount()\") as number;\n}\n"
5
+ "import type ivm from \"@ricsam/isolated-vm\";\nimport IsolatedVM from \"@ricsam/isolated-vm\";\n\n// ============================================================\n// Test Environment Options\n// ============================================================\n\nexport interface TestEnvironmentOptions {\n /** Receive test lifecycle events */\n onEvent?: (event: TestEvent) => void;\n /** Timeout for individual tests (ms) */\n testTimeout?: number;\n}\n\n// ============================================================\n// Event Types (discriminated union)\n// ============================================================\n\nexport type TestEvent =\n | { type: \"runStart\"; testCount: number; suiteCount: number }\n | { type: \"suiteStart\"; suite: SuiteInfo }\n | { type: \"suiteEnd\"; suite: SuiteResult }\n | { type: \"testStart\"; test: TestInfo }\n | { type: \"testEnd\"; test: TestResult }\n | { type: \"runEnd\"; results: RunResults };\n\n// ============================================================\n// Suite Types\n// ============================================================\n\nexport interface SuiteInfo {\n name: string;\n /** Ancestry path: [\"outer\", \"inner\"] */\n path: string[];\n /** Full display name: \"outer > inner\" */\n fullName: string;\n /** Nesting depth (0 for root-level suites) */\n depth: number;\n}\n\nexport interface SuiteResult extends SuiteInfo {\n passed: number;\n failed: number;\n skipped: number;\n todo: number;\n duration: number;\n}\n\n// ============================================================\n// Test Types\n// ============================================================\n\nexport interface TestInfo {\n name: string;\n /** Suite ancestry */\n suitePath: string[];\n /** Full display name: \"suite > test name\" */\n fullName: string;\n}\n\nexport interface TestResult extends TestInfo {\n status: \"pass\" | \"fail\" | \"skip\" | \"todo\";\n duration: number;\n error?: TestError;\n}\n\nexport interface TestError {\n message: string;\n stack?: string;\n /** For assertion failures */\n expected?: unknown;\n actual?: unknown;\n /** e.g., \"toBe\", \"toEqual\", \"toContain\" */\n matcherName?: string;\n}\n\n// ============================================================\n// Run Results\n// ============================================================\n\nexport interface RunResults {\n passed: number;\n failed: number;\n skipped: number;\n todo: number;\n total: number;\n duration: number;\n success: boolean;\n suites: SuiteResult[];\n tests: TestResult[];\n}\n\n// ============================================================\n// Handle Interface\n// ============================================================\n\nexport interface TestEnvironmentHandle {\n dispose(): void;\n}\n\nconst testEnvironmentCode = `\n(function() {\n // ============================================================\n // Internal State\n // ============================================================\n\n // Mock registry and call counter\n let __mockCallOrder = 0;\n const __mockRegistry = [];\n\n // Assertion counting state\n let __expectedAssertions = null;\n let __assertionCount = 0;\n let __hasAssertionsFlag = false;\n\n function createMockState() {\n return {\n calls: [],\n results: [],\n contexts: [],\n instances: [],\n invocationCallOrder: [],\n lastCall: undefined,\n };\n }\n\n function createSuite(name, skip = false, only = false) {\n return {\n name,\n tests: [],\n children: [],\n beforeAll: [],\n afterAll: [],\n beforeEach: [],\n afterEach: [],\n skip,\n only,\n };\n }\n\n const rootSuite = createSuite('root');\n let currentSuite = rootSuite;\n const suiteStack = [rootSuite];\n\n // Event callback (set from host)\n let eventCallback = null;\n\n function emitEvent(event) {\n if (eventCallback) {\n try {\n eventCallback(JSON.stringify(event));\n } catch (e) {\n // Ignore callback errors\n }\n }\n }\n\n // ============================================================\n // TestError class for rich error info\n // ============================================================\n\n class TestError extends Error {\n constructor(message, matcherName, expected, actual) {\n super(message);\n this.name = 'TestError';\n this.matcherName = matcherName;\n this.expected = expected;\n this.actual = actual;\n }\n }\n\n // ============================================================\n // Asymmetric Matcher Infrastructure\n // ============================================================\n\n const ASYMMETRIC_MATCHER = Symbol('asymmetricMatcher');\n\n function isAsymmetricMatcher(obj) {\n return obj && obj[ASYMMETRIC_MATCHER] === true;\n }\n\n // Deep equality with asymmetric matcher support\n function asymmetricDeepEqual(a, b) {\n if (isAsymmetricMatcher(b)) return b.asymmetricMatch(a);\n if (isAsymmetricMatcher(a)) return a.asymmetricMatch(b);\n if (a === b) return true;\n if (typeof a !== typeof b) return false;\n if (typeof a !== 'object' || a === null || b === null) return false;\n if (Array.isArray(a) !== Array.isArray(b)) return false;\n const keysA = Object.keys(a);\n const keysB = Object.keys(b);\n if (keysA.length !== keysB.length) return false;\n for (const key of keysA) {\n if (!keysB.includes(key)) return false;\n if (!asymmetricDeepEqual(a[key], b[key])) return false;\n }\n return true;\n }\n\n // ============================================================\n // Deep Equality Helper\n // ============================================================\n\n function deepEqual(a, b) {\n if (a === b) return true;\n if (typeof a !== typeof b) return false;\n if (typeof a !== 'object' || a === null || b === null) return false;\n\n if (Array.isArray(a) !== Array.isArray(b)) return false;\n\n const keysA = Object.keys(a);\n const keysB = Object.keys(b);\n if (keysA.length !== keysB.length) return false;\n\n for (const key of keysA) {\n if (!keysB.includes(key)) return false;\n if (!deepEqual(a[key], b[key])) return false;\n }\n return true;\n }\n\n function strictDeepEqual(a, b) {\n if (a === b) return true;\n if (typeof a !== typeof b) return false;\n if (typeof a !== 'object' || a === null || b === null) return false;\n\n // Check prototypes\n if (Object.getPrototypeOf(a) !== Object.getPrototypeOf(b)) return false;\n\n if (Array.isArray(a) !== Array.isArray(b)) return false;\n\n // For arrays, check sparse arrays (holes)\n if (Array.isArray(a)) {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n const aHasIndex = i in a;\n const bHasIndex = i in b;\n if (aHasIndex !== bHasIndex) return false;\n if (aHasIndex && !strictDeepEqual(a[i], b[i])) return false;\n }\n return true;\n }\n\n // Check for undefined properties vs missing properties\n const keysA = Object.keys(a);\n const keysB = Object.keys(b);\n if (keysA.length !== keysB.length) return false;\n\n for (const key of keysA) {\n if (!keysB.includes(key)) return false;\n if (!strictDeepEqual(a[key], b[key])) return false;\n }\n\n // Check for symbol properties\n const symbolsA = Object.getOwnPropertySymbols(a);\n const symbolsB = Object.getOwnPropertySymbols(b);\n if (symbolsA.length !== symbolsB.length) return false;\n\n for (const sym of symbolsA) {\n if (!symbolsB.includes(sym)) return false;\n if (!strictDeepEqual(a[sym], b[sym])) return false;\n }\n\n return true;\n }\n\n function getNestedProperty(obj, path) {\n const parts = path.split('.');\n let current = obj;\n for (const part of parts) {\n if (current == null || !(part in current)) {\n return { exists: false };\n }\n current = current[part];\n }\n return { exists: true, value: current };\n }\n\n function formatValue(val) {\n if (val === null) return 'null';\n if (val === undefined) return 'undefined';\n if (typeof val === 'string') return JSON.stringify(val);\n if (typeof val === 'object') {\n try {\n return JSON.stringify(val);\n } catch {\n return String(val);\n }\n }\n return String(val);\n }\n\n // ============================================================\n // expect() Implementation\n // ============================================================\n\n function expect(actual) {\n function createMatchers(negated = false) {\n const assert = (condition, message, matcherName, expected) => {\n __assertionCount++;\n const pass = negated ? !condition : condition;\n if (!pass) {\n throw new TestError(message, matcherName, expected, actual);\n }\n };\n\n const matchers = {\n toBe(expected) {\n assert(\n actual === expected,\n negated\n ? \\`Expected \\${formatValue(actual)} not to be \\${formatValue(expected)}\\`\n : \\`Expected \\${formatValue(actual)} to be \\${formatValue(expected)}\\`,\n 'toBe',\n expected\n );\n },\n\n toEqual(expected) {\n assert(\n asymmetricDeepEqual(actual, expected),\n negated\n ? \\`Expected \\${formatValue(actual)} not to equal \\${formatValue(expected)}\\`\n : \\`Expected \\${formatValue(actual)} to equal \\${formatValue(expected)}\\`,\n 'toEqual',\n expected\n );\n },\n\n toStrictEqual(expected) {\n assert(\n strictDeepEqual(actual, expected),\n negated\n ? \\`Expected \\${formatValue(actual)} not to strictly equal \\${formatValue(expected)}\\`\n : \\`Expected \\${formatValue(actual)} to strictly equal \\${formatValue(expected)}\\`,\n 'toStrictEqual',\n expected\n );\n },\n\n toBeTruthy() {\n assert(\n !!actual,\n negated\n ? \\`Expected \\${formatValue(actual)} not to be truthy\\`\n : \\`Expected \\${formatValue(actual)} to be truthy\\`,\n 'toBeTruthy',\n true\n );\n },\n\n toBeFalsy() {\n assert(\n !actual,\n negated\n ? \\`Expected \\${formatValue(actual)} not to be falsy\\`\n : \\`Expected \\${formatValue(actual)} to be falsy\\`,\n 'toBeFalsy',\n false\n );\n },\n\n toBeNull() {\n assert(\n actual === null,\n negated\n ? \\`Expected \\${formatValue(actual)} not to be null\\`\n : \\`Expected \\${formatValue(actual)} to be null\\`,\n 'toBeNull',\n null\n );\n },\n\n toBeUndefined() {\n assert(\n actual === undefined,\n negated\n ? \\`Expected \\${formatValue(actual)} not to be undefined\\`\n : \\`Expected \\${formatValue(actual)} to be undefined\\`,\n 'toBeUndefined',\n undefined\n );\n },\n\n toBeDefined() {\n assert(\n actual !== undefined,\n negated\n ? \\`Expected \\${formatValue(actual)} not to be defined\\`\n : \\`Expected \\${formatValue(actual)} to be defined\\`,\n 'toBeDefined',\n 'defined'\n );\n },\n\n toContain(item) {\n let contains = false;\n if (Array.isArray(actual)) {\n contains = actual.includes(item);\n } else if (typeof actual === 'string') {\n contains = actual.includes(item);\n }\n assert(\n contains,\n negated\n ? \\`Expected \\${formatValue(actual)} not to contain \\${formatValue(item)}\\`\n : \\`Expected \\${formatValue(actual)} to contain \\${formatValue(item)}\\`,\n 'toContain',\n item\n );\n },\n\n toThrow(expected) {\n if (typeof actual !== 'function') {\n throw new Error('toThrow requires a function');\n }\n\n let threw = false;\n let error = null;\n try {\n actual();\n } catch (e) {\n threw = true;\n error = e;\n }\n\n if (expected !== undefined) {\n const matches = threw && (\n (typeof expected === 'string' && error.message.includes(expected)) ||\n (expected instanceof RegExp && expected.test(error.message)) ||\n (typeof expected === 'function' && error instanceof expected)\n );\n assert(\n matches,\n negated\n ? \\`Expected function not to throw \\${formatValue(expected)}\\`\n : \\`Expected function to throw \\${formatValue(expected)}, but \\${threw ? \\`threw: \\${error.message}\\` : 'did not throw'}\\`,\n 'toThrow',\n expected\n );\n } else {\n assert(\n threw,\n negated\n ? \\`Expected function not to throw\\`\n : \\`Expected function to throw\\`,\n 'toThrow',\n 'any error'\n );\n }\n },\n\n toBeInstanceOf(cls) {\n assert(\n actual instanceof cls,\n negated\n ? \\`Expected \\${formatValue(actual)} not to be instance of \\${cls.name || cls}\\`\n : \\`Expected \\${formatValue(actual)} to be instance of \\${cls.name || cls}\\`,\n 'toBeInstanceOf',\n cls.name || cls\n );\n },\n\n toHaveLength(length) {\n const actualLength = actual?.length;\n assert(\n actualLength === length,\n negated\n ? \\`Expected length not to be \\${length}, but got \\${actualLength}\\`\n : \\`Expected length to be \\${length}, but got \\${actualLength}\\`,\n 'toHaveLength',\n length\n );\n },\n\n toMatch(pattern) {\n let matches = false;\n if (typeof pattern === 'string') {\n matches = actual.includes(pattern);\n } else if (pattern instanceof RegExp) {\n matches = pattern.test(actual);\n }\n assert(\n matches,\n negated\n ? \\`Expected \\${formatValue(actual)} not to match \\${pattern}\\`\n : \\`Expected \\${formatValue(actual)} to match \\${pattern}\\`,\n 'toMatch',\n pattern\n );\n },\n\n toHaveProperty(path, value) {\n const prop = getNestedProperty(actual, path);\n const hasProperty = prop.exists;\n const valueMatches = arguments.length < 2 || asymmetricDeepEqual(prop.value, value);\n\n assert(\n hasProperty && valueMatches,\n negated\n ? \\`Expected \\${formatValue(actual)} not to have property \\${path}\\${arguments.length >= 2 ? \\` with value \\${formatValue(value)}\\` : ''}\\`\n : \\`Expected \\${formatValue(actual)} to have property \\${path}\\${arguments.length >= 2 ? \\` with value \\${formatValue(value)}\\` : ''}\\`,\n 'toHaveProperty',\n arguments.length >= 2 ? { path, value } : { path }\n );\n },\n\n toBeGreaterThan(expected) {\n assert(\n actual > expected,\n negated\n ? \\`Expected \\${formatValue(actual)} not to be greater than \\${formatValue(expected)}\\`\n : \\`Expected \\${formatValue(actual)} to be greater than \\${formatValue(expected)}\\`,\n 'toBeGreaterThan',\n expected\n );\n },\n\n toBeGreaterThanOrEqual(expected) {\n assert(\n actual >= expected,\n negated\n ? \\`Expected \\${formatValue(actual)} not to be greater than or equal to \\${formatValue(expected)}\\`\n : \\`Expected \\${formatValue(actual)} to be greater than or equal to \\${formatValue(expected)}\\`,\n 'toBeGreaterThanOrEqual',\n expected\n );\n },\n\n toBeLessThan(expected) {\n assert(\n actual < expected,\n negated\n ? \\`Expected \\${formatValue(actual)} not to be less than \\${formatValue(expected)}\\`\n : \\`Expected \\${formatValue(actual)} to be less than \\${formatValue(expected)}\\`,\n 'toBeLessThan',\n expected\n );\n },\n\n toBeLessThanOrEqual(expected) {\n assert(\n actual <= expected,\n negated\n ? \\`Expected \\${formatValue(actual)} not to be less than or equal to \\${formatValue(expected)}\\`\n : \\`Expected \\${formatValue(actual)} to be less than or equal to \\${formatValue(expected)}\\`,\n 'toBeLessThanOrEqual',\n expected\n );\n },\n\n toBeCloseTo(expected, numDigits = 2) {\n const precision = Math.pow(10, -numDigits) / 2;\n const pass = Math.abs(actual - expected) < precision;\n assert(\n pass,\n negated\n ? \\`Expected \\${formatValue(actual)} not to be close to \\${formatValue(expected)} (precision: \\${numDigits} digits)\\`\n : \\`Expected \\${formatValue(actual)} to be close to \\${formatValue(expected)} (precision: \\${numDigits} digits)\\`,\n 'toBeCloseTo',\n expected\n );\n },\n\n toBeNaN() {\n assert(\n Number.isNaN(actual),\n negated\n ? \\`Expected \\${formatValue(actual)} not to be NaN\\`\n : \\`Expected \\${formatValue(actual)} to be NaN\\`,\n 'toBeNaN',\n NaN\n );\n },\n\n toMatchObject(expected) {\n function matchesObject(obj, pattern) {\n if (isAsymmetricMatcher(pattern)) return pattern.asymmetricMatch(obj);\n if (typeof pattern !== 'object' || pattern === null) return obj === pattern;\n if (typeof obj !== 'object' || obj === null) return false;\n for (const key of Object.keys(pattern)) {\n if (!(key in obj)) return false;\n if (!matchesObject(obj[key], pattern[key])) return false;\n }\n return true;\n }\n assert(\n matchesObject(actual, expected),\n negated\n ? \\`Expected \\${formatValue(actual)} not to match object \\${formatValue(expected)}\\`\n : \\`Expected \\${formatValue(actual)} to match object \\${formatValue(expected)}\\`,\n 'toMatchObject',\n expected\n );\n },\n\n toContainEqual(item) {\n const contains = Array.isArray(actual) && actual.some(el => asymmetricDeepEqual(el, item));\n assert(\n contains,\n negated\n ? \\`Expected array not to contain equal \\${formatValue(item)}\\`\n : \\`Expected array to contain equal \\${formatValue(item)}\\`,\n 'toContainEqual',\n item\n );\n },\n\n toBeTypeOf(expectedType) {\n const actualType = typeof actual;\n assert(\n actualType === expectedType,\n negated\n ? \\`Expected typeof \\${formatValue(actual)} not to be \"\\${expectedType}\"\\`\n : \\`Expected typeof \\${formatValue(actual)} to be \"\\${expectedType}\", got \"\\${actualType}\"\\`,\n 'toBeTypeOf',\n expectedType\n );\n },\n\n // Mock matchers\n toHaveBeenCalled() {\n if (!actual.__isMockFunction) throw new Error('toHaveBeenCalled requires a mock function');\n assert(actual.mock.calls.length > 0,\n negated ? \\`Expected mock not to have been called\\` : \\`Expected mock to have been called\\`,\n 'toHaveBeenCalled', 'called');\n },\n\n toHaveBeenCalledTimes(n) {\n if (!actual.__isMockFunction) throw new Error('toHaveBeenCalledTimes requires a mock function');\n assert(actual.mock.calls.length === n,\n negated ? \\`Expected mock not to have been called \\${n} times\\`\n : \\`Expected mock to be called \\${n} times, got \\${actual.mock.calls.length}\\`,\n 'toHaveBeenCalledTimes', n);\n },\n\n toHaveBeenCalledWith(...expectedArgs) {\n if (!actual.__isMockFunction) throw new Error('toHaveBeenCalledWith requires a mock function');\n const match = actual.mock.calls.some(args => asymmetricDeepEqual(args, expectedArgs));\n assert(match,\n negated ? \\`Expected mock not to have been called with \\${formatValue(expectedArgs)}\\`\n : \\`Expected mock to have been called with \\${formatValue(expectedArgs)}\\`,\n 'toHaveBeenCalledWith', expectedArgs);\n },\n\n toHaveBeenLastCalledWith(...expectedArgs) {\n if (!actual.__isMockFunction) throw new Error('toHaveBeenLastCalledWith requires a mock function');\n assert(actual.mock.lastCall && asymmetricDeepEqual(actual.mock.lastCall, expectedArgs),\n negated ? \\`Expected last call not to be \\${formatValue(expectedArgs)}\\`\n : \\`Expected last call to be \\${formatValue(expectedArgs)}, got \\${formatValue(actual.mock.lastCall)}\\`,\n 'toHaveBeenLastCalledWith', expectedArgs);\n },\n\n toHaveBeenNthCalledWith(n, ...expectedArgs) {\n if (!actual.__isMockFunction) throw new Error('toHaveBeenNthCalledWith requires a mock function');\n const nthCall = actual.mock.calls[n - 1];\n assert(nthCall && asymmetricDeepEqual(nthCall, expectedArgs),\n negated ? \\`Expected call \\${n} not to be \\${formatValue(expectedArgs)}\\`\n : \\`Expected call \\${n} to be \\${formatValue(expectedArgs)}, got \\${formatValue(nthCall)}\\`,\n 'toHaveBeenNthCalledWith', expectedArgs);\n },\n\n toHaveReturned() {\n if (!actual.__isMockFunction) throw new Error('toHaveReturned requires a mock function');\n const hasReturned = actual.mock.results.some(r => r.type === 'return');\n assert(hasReturned,\n negated ? \\`Expected mock not to have returned\\` : \\`Expected mock to have returned\\`,\n 'toHaveReturned', 'returned');\n },\n\n toHaveReturnedWith(value) {\n if (!actual.__isMockFunction) throw new Error('toHaveReturnedWith requires a mock function');\n const match = actual.mock.results.some(r => r.type === 'return' && asymmetricDeepEqual(r.value, value));\n assert(match,\n negated ? \\`Expected mock not to have returned \\${formatValue(value)}\\`\n : \\`Expected mock to have returned \\${formatValue(value)}\\`,\n 'toHaveReturnedWith', value);\n },\n\n toHaveLastReturnedWith(value) {\n if (!actual.__isMockFunction) throw new Error('toHaveLastReturnedWith requires a mock function');\n const returns = actual.mock.results.filter(r => r.type === 'return');\n const last = returns[returns.length - 1];\n assert(last && asymmetricDeepEqual(last.value, value),\n negated ? \\`Expected last return not to be \\${formatValue(value)}\\`\n : \\`Expected last return to be \\${formatValue(value)}, got \\${formatValue(last?.value)}\\`,\n 'toHaveLastReturnedWith', value);\n },\n\n toHaveReturnedTimes(n) {\n if (!actual.__isMockFunction) throw new Error('toHaveReturnedTimes requires a mock function');\n const returnCount = actual.mock.results.filter(r => r.type === 'return').length;\n assert(returnCount === n,\n negated ? \\`Expected mock not to have returned \\${n} times\\`\n : \\`Expected mock to have returned \\${n} times, got \\${returnCount}\\`,\n 'toHaveReturnedTimes', n);\n },\n\n toHaveNthReturnedWith(n, value) {\n if (!actual.__isMockFunction) throw new Error('toHaveNthReturnedWith requires a mock function');\n const returns = actual.mock.results.filter(r => r.type === 'return');\n const nthReturn = returns[n - 1];\n assert(nthReturn && asymmetricDeepEqual(nthReturn.value, value),\n negated ? \\`Expected return \\${n} not to be \\${formatValue(value)}\\`\n : \\`Expected return \\${n} to be \\${formatValue(value)}, got \\${formatValue(nthReturn?.value)}\\`,\n 'toHaveNthReturnedWith', value);\n },\n };\n\n return matchers;\n }\n\n const matchers = createMatchers(false);\n matchers.not = createMatchers(true);\n\n // Promise matchers using Proxy\n matchers.resolves = new Proxy({}, {\n get(_, matcherName) {\n if (matcherName === 'not') {\n return new Proxy({}, {\n get(_, negatedMatcherName) {\n return async (...args) => {\n const result = await actual;\n return expect(result).not[negatedMatcherName](...args);\n };\n }\n });\n }\n return async (...args) => {\n const result = await actual;\n return expect(result)[matcherName](...args);\n };\n }\n });\n\n matchers.rejects = new Proxy({}, {\n get(_, matcherName) {\n if (matcherName === 'not') {\n return new Proxy({}, {\n get(_, negatedMatcherName) {\n return async (...args) => {\n let error;\n try {\n await actual;\n throw new TestError('Expected promise to reject', 'rejects', 'rejection', undefined);\n } catch (e) {\n if (e instanceof TestError && e.matcherName === 'rejects') throw e;\n error = e;\n }\n return expect(error).not[negatedMatcherName](...args);\n };\n }\n });\n }\n return async (...args) => {\n let error;\n try {\n await actual;\n throw new TestError('Expected promise to reject', 'rejects', 'rejection', undefined);\n } catch (e) {\n if (e instanceof TestError && e.matcherName === 'rejects') throw e;\n error = e;\n }\n return expect(error)[matcherName](...args);\n };\n }\n });\n\n return matchers;\n }\n\n // Asymmetric matcher implementations\n expect.anything = () => ({\n [ASYMMETRIC_MATCHER]: true,\n asymmetricMatch: (other) => other !== null && other !== undefined,\n toString: () => 'anything()',\n });\n\n expect.any = (constructor) => ({\n [ASYMMETRIC_MATCHER]: true,\n asymmetricMatch: (other) => {\n if (constructor === String) return typeof other === 'string' || other instanceof String;\n if (constructor === Number) return typeof other === 'number' || other instanceof Number;\n if (constructor === Boolean) return typeof other === 'boolean' || other instanceof Boolean;\n if (constructor === Function) return typeof other === 'function';\n if (constructor === Object) return typeof other === 'object' && other !== null;\n if (constructor === Array) return Array.isArray(other);\n return other instanceof constructor;\n },\n toString: () => \\`any(\\${constructor.name || constructor})\\`,\n });\n\n expect.stringContaining = (str) => ({\n [ASYMMETRIC_MATCHER]: true,\n asymmetricMatch: (other) => typeof other === 'string' && other.includes(str),\n toString: () => \\`stringContaining(\"\\${str}\")\\`,\n });\n\n expect.stringMatching = (pattern) => ({\n [ASYMMETRIC_MATCHER]: true,\n asymmetricMatch: (other) => {\n if (typeof other !== 'string') return false;\n return typeof pattern === 'string' ? other.includes(pattern) : pattern.test(other);\n },\n toString: () => \\`stringMatching(\\${pattern})\\`,\n });\n\n expect.arrayContaining = (expected) => ({\n [ASYMMETRIC_MATCHER]: true,\n asymmetricMatch: (other) => {\n if (!Array.isArray(other)) return false;\n return expected.every(exp => other.some(item => asymmetricDeepEqual(item, exp)));\n },\n toString: () => \\`arrayContaining(\\${formatValue(expected)})\\`,\n });\n\n expect.objectContaining = (expected) => ({\n [ASYMMETRIC_MATCHER]: true,\n asymmetricMatch: (other) => {\n if (typeof other !== 'object' || other === null) return false;\n for (const key of Object.keys(expected)) {\n if (!(key in other)) return false;\n if (!asymmetricDeepEqual(other[key], expected[key])) return false;\n }\n return true;\n },\n toString: () => \\`objectContaining(\\${formatValue(expected)})\\`,\n });\n\n // Assertion counting\n expect.assertions = (n) => {\n __expectedAssertions = n;\n };\n\n expect.hasAssertions = () => {\n __hasAssertionsFlag = true;\n };\n\n // ============================================================\n // Test Registration Functions\n // ============================================================\n\n function describe(name, fn) {\n const suite = createSuite(name);\n currentSuite.children.push(suite);\n\n const parentSuite = currentSuite;\n currentSuite = suite;\n suiteStack.push(suite);\n\n fn();\n\n suiteStack.pop();\n currentSuite = parentSuite;\n }\n\n describe.skip = function(name, fn) {\n const suite = createSuite(name, true, false);\n currentSuite.children.push(suite);\n\n const parentSuite = currentSuite;\n currentSuite = suite;\n suiteStack.push(suite);\n\n fn();\n\n suiteStack.pop();\n currentSuite = parentSuite;\n };\n\n describe.only = function(name, fn) {\n const suite = createSuite(name, false, true);\n currentSuite.children.push(suite);\n\n const parentSuite = currentSuite;\n currentSuite = suite;\n suiteStack.push(suite);\n\n fn();\n\n suiteStack.pop();\n currentSuite = parentSuite;\n };\n\n function test(name, fn) {\n currentSuite.tests.push({\n name,\n fn,\n skip: false,\n only: false,\n });\n }\n\n test.skip = function(name, fn) {\n currentSuite.tests.push({\n name,\n fn,\n skip: true,\n only: false,\n });\n };\n\n test.only = function(name, fn) {\n currentSuite.tests.push({\n name,\n fn,\n skip: false,\n only: true,\n });\n };\n\n test.todo = function(name) {\n currentSuite.tests.push({\n name,\n fn: null,\n skip: false,\n only: false,\n todo: true,\n });\n };\n\n const it = test;\n it.skip = test.skip;\n it.only = test.only;\n it.todo = test.todo;\n\n // ============================================================\n // Lifecycle Hooks\n // ============================================================\n\n function beforeEach(fn) {\n currentSuite.beforeEach.push(fn);\n }\n\n function afterEach(fn) {\n currentSuite.afterEach.push(fn);\n }\n\n function beforeAll(fn) {\n currentSuite.beforeAll.push(fn);\n }\n\n function afterAll(fn) {\n currentSuite.afterAll.push(fn);\n }\n\n // ============================================================\n // Mock Implementation\n // ============================================================\n\n const mock = {\n fn(implementation) {\n const mockState = createMockState();\n let defaultImpl = implementation;\n const onceImpls = [];\n const onceReturns = [];\n let returnVal, resolvedVal, rejectedVal;\n let returnSet = false, resolvedSet = false, rejectedSet = false;\n\n function mockFn(...args) {\n mockState.calls.push(args);\n mockState.contexts.push(this);\n mockState.lastCall = args;\n mockState.invocationCallOrder.push(++__mockCallOrder);\n\n let result;\n try {\n if (onceImpls.length > 0) {\n result = onceImpls.shift().apply(this, args);\n } else if (onceReturns.length > 0) {\n result = onceReturns.shift();\n } else if (returnSet) {\n result = returnVal;\n } else if (resolvedSet) {\n result = Promise.resolve(resolvedVal);\n } else if (rejectedSet) {\n result = Promise.reject(rejectedVal);\n } else if (defaultImpl) {\n result = defaultImpl.apply(this, args);\n }\n mockState.results.push({ type: 'return', value: result });\n return result;\n } catch (e) {\n mockState.results.push({ type: 'throw', value: e });\n throw e;\n }\n }\n\n mockFn.__isMockFunction = true;\n mockFn.mock = mockState;\n\n // Configuration methods\n mockFn.mockReturnValue = (v) => { returnVal = v; returnSet = true; return mockFn; };\n mockFn.mockReturnValueOnce = (v) => { onceReturns.push(v); return mockFn; };\n mockFn.mockResolvedValue = (v) => { resolvedVal = v; resolvedSet = true; return mockFn; };\n mockFn.mockRejectedValue = (v) => { rejectedVal = v; rejectedSet = true; return mockFn; };\n mockFn.mockImplementation = (fn) => { defaultImpl = fn; return mockFn; };\n mockFn.mockImplementationOnce = (fn) => { onceImpls.push(fn); return mockFn; };\n\n // Clearing methods\n mockFn.mockClear = () => {\n mockState.calls = []; mockState.results = []; mockState.contexts = [];\n mockState.instances = []; mockState.invocationCallOrder = []; mockState.lastCall = undefined;\n return mockFn;\n };\n mockFn.mockReset = () => {\n mockFn.mockClear();\n defaultImpl = undefined; returnVal = resolvedVal = rejectedVal = undefined;\n returnSet = resolvedSet = rejectedSet = false;\n onceImpls.length = 0; onceReturns.length = 0;\n return mockFn;\n };\n mockFn.mockRestore = () => mockFn.mockReset();\n\n __mockRegistry.push(mockFn);\n return mockFn;\n },\n\n spyOn(object, methodName) {\n const original = object[methodName];\n if (typeof original !== 'function') {\n throw new Error(\\`Cannot spy on \\${methodName}: not a function\\`);\n }\n const spy = mock.fn(original);\n spy.__originalMethod = original;\n spy.__spyTarget = object;\n spy.__spyMethodName = methodName;\n spy.__isSpyFunction = true;\n spy.mockRestore = () => {\n object[methodName] = original;\n const idx = __mockRegistry.indexOf(spy);\n if (idx !== -1) __mockRegistry.splice(idx, 1);\n return spy;\n };\n object[methodName] = spy;\n return spy;\n },\n\n clearAllMocks() {\n for (const fn of __mockRegistry) fn.mockClear();\n },\n\n resetAllMocks() {\n for (const fn of __mockRegistry) fn.mockReset();\n },\n\n restoreAllMocks() {\n for (let i = __mockRegistry.length - 1; i >= 0; i--) {\n if (__mockRegistry[i].__isSpyFunction) __mockRegistry[i].mockRestore();\n }\n },\n };\n\n // ============================================================\n // Test Runner Helpers\n // ============================================================\n\n function checkForOnly(suite) {\n if (suite.only) return true;\n for (const t of suite.tests) {\n if (t.only) return true;\n }\n for (const child of suite.children) {\n if (checkForOnly(child)) return true;\n }\n return false;\n }\n\n function suiteHasOnly(suite) {\n if (suite.only) return true;\n for (const t of suite.tests) {\n if (t.only) return true;\n }\n for (const child of suite.children) {\n if (suiteHasOnly(child)) return true;\n }\n return false;\n }\n\n function countTests(suite, hasOnly) {\n let count = 0;\n if (hasOnly && !suiteHasOnly(suite)) return 0;\n if (suite.skip) return suite.tests.length;\n\n for (const t of suite.tests) {\n if (hasOnly && !t.only && !suite.only) continue;\n count++;\n }\n for (const child of suite.children) {\n count += countTests(child, hasOnly);\n }\n return count;\n }\n\n function countSuites(suite, hasOnly) {\n let count = 0;\n if (hasOnly && !suiteHasOnly(suite)) return 0;\n\n for (const child of suite.children) {\n count++;\n count += countSuites(child, hasOnly);\n }\n return count;\n }\n\n // ============================================================\n // Test Runner\n // ============================================================\n\n async function __runAllTests() {\n const testResults = [];\n const suiteResults = [];\n const hasOnly = checkForOnly(rootSuite);\n const runStart = Date.now();\n\n // Emit runStart\n const testCount = countTests(rootSuite, hasOnly);\n const suiteCount = countSuites(rootSuite, hasOnly);\n emitEvent({ type: 'runStart', testCount, suiteCount });\n\n async function runSuite(suite, parentHooks, pathArray, depth) {\n // Skip if this suite doesn't have any .only when .only exists elsewhere\n if (hasOnly && !suiteHasOnly(suite)) return;\n\n const suitePath = [...pathArray];\n const fullName = suitePath.join(' > ');\n const suiteInfo = {\n name: suite.name,\n path: suitePath.slice(0, -1),\n fullName,\n depth,\n };\n\n // Emit suiteStart (only for non-root suites)\n if (suite !== rootSuite) {\n emitEvent({ type: 'suiteStart', suite: suiteInfo });\n }\n\n const suiteStart = Date.now();\n let suitePassed = 0;\n let suiteFailed = 0;\n let suiteSkipped = 0;\n let suiteTodo = 0;\n\n // Skip if suite is marked as skip\n if (suite.skip) {\n // Mark all tests in this suite as skipped\n for (const t of suite.tests) {\n const testFullName = fullName ? fullName + ' > ' + t.name : t.name;\n const testInfo = {\n name: t.name,\n suitePath: suitePath,\n fullName: testFullName,\n };\n emitEvent({ type: 'testStart', test: testInfo });\n\n const testResult = {\n name: t.name,\n suitePath: suitePath,\n fullName: testFullName,\n status: 'skip',\n duration: 0,\n };\n testResults.push(testResult);\n suiteSkipped++;\n\n emitEvent({ type: 'testEnd', test: testResult });\n }\n } else {\n // Run beforeAll hooks\n for (const hook of suite.beforeAll) {\n await hook();\n }\n\n // Run tests\n for (const t of suite.tests) {\n const testFullName = fullName ? fullName + ' > ' + t.name : t.name;\n const testInfo = {\n name: t.name,\n suitePath: suitePath,\n fullName: testFullName,\n };\n\n // Skip if .only is used and this test isn't .only AND the suite doesn't have .only\n if (hasOnly && !t.only && !suite.only) continue;\n\n emitEvent({ type: 'testStart', test: testInfo });\n\n // Skip if test is marked as skip\n if (t.skip) {\n const testResult = {\n name: t.name,\n suitePath: suitePath,\n fullName: testFullName,\n status: 'skip',\n duration: 0,\n };\n testResults.push(testResult);\n suiteSkipped++;\n emitEvent({ type: 'testEnd', test: testResult });\n continue;\n }\n\n // Handle todo tests (no function provided)\n if (t.todo) {\n const testResult = {\n name: t.name,\n suitePath: suitePath,\n fullName: testFullName,\n status: 'todo',\n duration: 0,\n };\n testResults.push(testResult);\n suiteTodo++;\n emitEvent({ type: 'testEnd', test: testResult });\n continue;\n }\n\n const testStart = Date.now();\n // Reset assertion counting state before each test\n __expectedAssertions = null;\n __assertionCount = 0;\n __hasAssertionsFlag = false;\n\n try {\n // Run all beforeEach hooks (parent first, then current)\n for (const hook of [...parentHooks.beforeEach, ...suite.beforeEach]) {\n await hook();\n }\n\n // Run test\n await t.fn();\n\n // Run all afterEach hooks (current first, then parent)\n for (const hook of [...suite.afterEach, ...parentHooks.afterEach]) {\n await hook();\n }\n\n // Verify assertion counts after test passes\n if (__hasAssertionsFlag && __assertionCount === 0) {\n throw new TestError('Expected at least one assertion', 'hasAssertions', '>0', 0);\n }\n if (__expectedAssertions !== null && __assertionCount !== __expectedAssertions) {\n throw new TestError(\n \\`Expected \\${__expectedAssertions} assertions, got \\${__assertionCount}\\`,\n 'assertions', __expectedAssertions, __assertionCount\n );\n }\n\n const testResult = {\n name: t.name,\n suitePath: suitePath,\n fullName: testFullName,\n status: 'pass',\n duration: Date.now() - testStart,\n };\n testResults.push(testResult);\n suitePassed++;\n emitEvent({ type: 'testEnd', test: testResult });\n } catch (err) {\n const testError = {\n message: err.message || String(err),\n stack: err.stack,\n };\n // If it's a TestError, include matcher info\n if (err.matcherName !== undefined) {\n testError.matcherName = err.matcherName;\n testError.expected = err.expected;\n testError.actual = err.actual;\n }\n const testResult = {\n name: t.name,\n suitePath: suitePath,\n fullName: testFullName,\n status: 'fail',\n duration: Date.now() - testStart,\n error: testError,\n };\n testResults.push(testResult);\n suiteFailed++;\n emitEvent({ type: 'testEnd', test: testResult });\n }\n }\n\n // Run child suites\n for (const child of suite.children) {\n const childPath = [...suitePath, child.name];\n await runSuite(child, {\n beforeEach: [...parentHooks.beforeEach, ...suite.beforeEach],\n afterEach: [...suite.afterEach, ...parentHooks.afterEach],\n }, childPath, depth + 1);\n }\n\n // Run afterAll hooks\n for (const hook of suite.afterAll) {\n await hook();\n }\n }\n\n // Emit suiteEnd (only for non-root suites)\n if (suite !== rootSuite) {\n const suiteResult = {\n ...suiteInfo,\n passed: suitePassed,\n failed: suiteFailed,\n skipped: suiteSkipped,\n todo: suiteTodo,\n duration: Date.now() - suiteStart,\n };\n suiteResults.push(suiteResult);\n emitEvent({ type: 'suiteEnd', suite: suiteResult });\n }\n }\n\n await runSuite(rootSuite, { beforeEach: [], afterEach: [] }, [], -1);\n\n const passed = testResults.filter(r => r.status === 'pass').length;\n const failed = testResults.filter(r => r.status === 'fail').length;\n const skipped = testResults.filter(r => r.status === 'skip').length;\n const todo = testResults.filter(r => r.status === 'todo').length;\n\n const runResults = {\n passed,\n failed,\n skipped,\n todo,\n total: testResults.length,\n duration: Date.now() - runStart,\n success: failed === 0,\n suites: suiteResults,\n tests: testResults,\n };\n\n emitEvent({ type: 'runEnd', results: runResults });\n\n return JSON.stringify(runResults);\n }\n\n // ============================================================\n // Helper Functions\n // ============================================================\n\n function __hasTests() {\n function checkSuite(suite) {\n if (suite.tests.length > 0) return true;\n for (const child of suite.children) {\n if (checkSuite(child)) return true;\n }\n return false;\n }\n return checkSuite(rootSuite);\n }\n\n function __getTestCount() {\n function countInSuite(suite) {\n let count = suite.tests.length;\n for (const child of suite.children) {\n count += countInSuite(child);\n }\n return count;\n }\n return countInSuite(rootSuite);\n }\n\n // Reset function to clear state between runs\n function __resetTestEnvironment() {\n rootSuite.tests = [];\n rootSuite.children = [];\n rootSuite.beforeAll = [];\n rootSuite.afterAll = [];\n rootSuite.beforeEach = [];\n rootSuite.afterEach = [];\n currentSuite = rootSuite;\n suiteStack.length = 0;\n suiteStack.push(rootSuite);\n // Clear mocks\n __mockCallOrder = 0;\n mock.restoreAllMocks();\n __mockRegistry.length = 0;\n }\n\n function __setEventCallback(callback) {\n eventCallback = callback;\n }\n\n // ============================================================\n // Expose Globals\n // ============================================================\n\n globalThis.describe = describe;\n globalThis.test = test;\n globalThis.it = it;\n globalThis.expect = expect;\n globalThis.beforeEach = beforeEach;\n globalThis.afterEach = afterEach;\n globalThis.beforeAll = beforeAll;\n globalThis.afterAll = afterAll;\n globalThis.mock = mock;\n globalThis.jest = mock; // Jest compatibility alias\n globalThis.__runAllTests = __runAllTests;\n globalThis.__resetTestEnvironment = __resetTestEnvironment;\n globalThis.__hasTests = __hasTests;\n globalThis.__getTestCount = __getTestCount;\n globalThis.__setEventCallback = __setEventCallback;\n})();\n`;\n\n/**\n * Setup test environment primitives in an isolated-vm context\n *\n * Provides Jest/Vitest-compatible test primitives:\n * - describe, test, it\n * - beforeEach, afterEach, beforeAll, afterAll\n * - expect matchers\n *\n * @example\n * const handle = await setupTestEnvironment(context, {\n * onEvent: (event) => console.log(event),\n * });\n *\n * await context.eval(`\n * describe(\"my tests\", () => {\n * test(\"example\", () => {\n * expect(1 + 1).toBe(2);\n * });\n * });\n * `);\n */\nexport async function setupTestEnvironment(\n context: ivm.Context,\n options?: TestEnvironmentOptions\n): Promise<TestEnvironmentHandle> {\n context.evalSync(testEnvironmentCode);\n\n // Set up event callback if provided\n if (options?.onEvent) {\n const eventCallbackRef = new IsolatedVM.Reference((eventJson: string) => {\n try {\n const event = JSON.parse(eventJson);\n options.onEvent!(event);\n } catch {\n // Ignore parse errors\n }\n });\n\n const global = context.global;\n global.setSync(\"__eventCallbackRef\", eventCallbackRef);\n context.evalSync(`\n __setEventCallback((eventJson) => {\n __eventCallbackRef.applySync(undefined, [eventJson]);\n });\n `);\n }\n\n return {\n dispose() {\n // Reset the test environment state\n try {\n context.evalSync(\"__resetTestEnvironment()\");\n } catch {\n // Context may already be released\n }\n },\n };\n}\n\n/**\n * Run tests in the context and return results\n */\nexport async function runTests(context: ivm.Context): Promise<RunResults> {\n const resultJson = await context.eval(\"__runAllTests()\", { promise: true });\n return JSON.parse(resultJson as string);\n}\n\n/**\n * Check if any tests are registered\n */\nexport function hasTests(context: ivm.Context): boolean {\n return context.evalSync(\"__hasTests()\") as boolean;\n}\n\n/**\n * Get the count of registered tests\n */\nexport function getTestCount(context: ivm.Context): number {\n return context.evalSync(\"__getTestCount()\") as number;\n}\n"
6
6
  ],
7
7
  "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACuB,IAAvB;AAmGA,IAAM,sBAAsB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAgzC5B,eAAsB,oBAAoB,CACxC,SACA,SACgC;AAAA,EAChC,QAAQ,SAAS,mBAAmB;AAAA,EAGpC,IAAI,SAAS,SAAS;AAAA,IACpB,MAAM,mBAAmB,IAAI,2BAAW,UAAU,CAAC,cAAsB;AAAA,MACvE,IAAI;AAAA,QACF,MAAM,QAAQ,KAAK,MAAM,SAAS;AAAA,QAClC,QAAQ,QAAS,KAAK;AAAA,QACtB,MAAM;AAAA,KAGT;AAAA,IAED,MAAM,SAAS,QAAQ;AAAA,IACvB,OAAO,QAAQ,sBAAsB,gBAAgB;AAAA,IACrD,QAAQ,SAAS;AAAA;AAAA;AAAA;AAAA,KAIhB;AAAA,EACH;AAAA,EAEA,OAAO;AAAA,IACL,OAAO,GAAG;AAAA,MAER,IAAI;AAAA,QACF,QAAQ,SAAS,0BAA0B;AAAA,QAC3C,MAAM;AAAA;AAAA,EAIZ;AAAA;AAMF,eAAsB,QAAQ,CAAC,SAA2C;AAAA,EACxE,MAAM,aAAa,MAAM,QAAQ,KAAK,mBAAmB,EAAE,SAAS,KAAK,CAAC;AAAA,EAC1E,OAAO,KAAK,MAAM,UAAoB;AAAA;AAMjC,SAAS,QAAQ,CAAC,SAA+B;AAAA,EACtD,OAAO,QAAQ,SAAS,cAAc;AAAA;AAMjC,SAAS,YAAY,CAAC,SAA8B;AAAA,EACzD,OAAO,QAAQ,SAAS,kBAAkB;AAAA;",
8
- "debugId": "542BC6119019237B64756E2164756E21",
8
+ "debugId": "3B4D0BB42B731F2564756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -66,7 +66,7 @@ __export(exports_timers, {
66
66
  setupTimers: () => setupTimers
67
67
  });
68
68
  module.exports = __toCommonJS(exports_timers);
69
- var import_isolated_vm = __toESM(require("isolated-vm"));
69
+ var import_isolated_vm = __toESM(require("@ricsam/isolated-vm"));
70
70
  function releaseIfSupported(handle) {
71
71
  const maybeHandle = handle;
72
72
  if (typeof maybeHandle.release === "function") {
@@ -119,13 +119,27 @@ async function setupTimers(context) {
119
119
  const timersCode = `
120
120
  (function() {
121
121
  const __timers_callbacks = new Map();
122
+ const __wrapAsyncContextCallback = (callback, type) => (
123
+ typeof callback === 'function' && globalThis.__isolateAsyncContextInternals?.wrapCallback
124
+ ? globalThis.__isolateAsyncContextInternals.wrapCallback(callback, { type })
125
+ : callback
126
+ );
127
+ const __releaseAsyncContextCallback = (callback) => (
128
+ typeof callback === 'function' && globalThis.__isolateAsyncContextInternals?.releaseCallback
129
+ ? globalThis.__isolateAsyncContextInternals.releaseCallback(callback)
130
+ : false
131
+ );
122
132
 
123
133
  globalThis.setTimeout = function(callback, delay, ...args) {
124
134
  if (typeof callback !== 'function') {
125
135
  throw new TypeError('Callback must be a function');
126
136
  }
127
137
  const id = __timers_registerTimeout(delay || 0);
128
- __timers_callbacks.set(id, { callback, args });
138
+ __timers_callbacks.set(id, {
139
+ args,
140
+ callback: __wrapAsyncContextCallback(callback, 'Timeout'),
141
+ repeat: false,
142
+ });
129
143
  return id;
130
144
  };
131
145
 
@@ -134,13 +148,21 @@ async function setupTimers(context) {
134
148
  throw new TypeError('Callback must be a function');
135
149
  }
136
150
  const id = __timers_registerInterval(delay || 0);
137
- __timers_callbacks.set(id, { callback, args });
151
+ __timers_callbacks.set(id, {
152
+ args,
153
+ callback: __wrapAsyncContextCallback(callback, 'Timeout'),
154
+ repeat: true,
155
+ });
138
156
  return id;
139
157
  };
140
158
 
141
159
  globalThis.clearTimeout = function(id) {
142
160
  __timers_clear(id);
143
- __timers_callbacks.delete(id);
161
+ const entry = __timers_callbacks.get(id);
162
+ if (entry) {
163
+ __releaseAsyncContextCallback(entry.callback);
164
+ __timers_callbacks.delete(id);
165
+ }
144
166
  };
145
167
 
146
168
  globalThis.clearInterval = globalThis.clearTimeout;
@@ -149,18 +171,31 @@ async function setupTimers(context) {
149
171
  globalThis.__timers_execute = function(id) {
150
172
  const entry = __timers_callbacks.get(id);
151
173
  if (entry) {
152
- entry.callback(...entry.args);
174
+ try {
175
+ entry.callback(...entry.args);
176
+ } finally {
177
+ if (!entry.repeat) {
178
+ __releaseAsyncContextCallback(entry.callback);
179
+ }
180
+ }
153
181
  }
154
182
  };
155
183
 
156
184
  // Called by host clearAll() to clear all callbacks
157
185
  globalThis.__timers_clearCallbacks = function() {
186
+ for (const entry of __timers_callbacks.values()) {
187
+ __releaseAsyncContextCallback(entry.callback);
188
+ }
158
189
  __timers_callbacks.clear();
159
190
  };
160
191
 
161
192
  // Called to remove a one-shot timeout callback after execution
162
193
  globalThis.__timers_removeCallback = function(id) {
163
- __timers_callbacks.delete(id);
194
+ const entry = __timers_callbacks.get(id);
195
+ if (entry) {
196
+ __releaseAsyncContextCallback(entry.callback);
197
+ __timers_callbacks.delete(id);
198
+ }
164
199
  };
165
200
  })();
166
201
  `;
@@ -197,4 +232,4 @@ async function setupTimers(context) {
197
232
  };
198
233
  }
199
234
 
200
- //# debugId=9FDA92E9C86B929164756E2164756E21
235
+ //# debugId=1F15FD83CCC6783564756E2164756E21
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/internal/timers/index.ts"],
4
4
  "sourcesContent": [
5
- "import ivm from \"isolated-vm\";\n\nexport interface TimersHandle {\n /** Clear all pending timers */\n clearAll(): void;\n /** Dispose the timers handle */\n dispose(): void;\n}\n\nfunction releaseIfSupported(handle: unknown): void {\n const maybeHandle = handle as { release?: () => void };\n if (typeof maybeHandle.release === \"function\") {\n maybeHandle.release();\n }\n}\n\n/**\n * Setup timer APIs in an isolated-vm context\n *\n * Injects setTimeout, setInterval, clearTimeout, clearInterval\n *\n * Uses real time - timers fire automatically based on actual elapsed time.\n *\n * @example\n * const handle = await setupTimers(context);\n * await context.eval(`\n * setTimeout(() => console.log(\"hello\"), 1000);\n * `);\n * // Timer will fire automatically after 1 second\n */\nexport async function setupTimers(\n context: ivm.Context\n): Promise<TimersHandle> {\n let nextTimerId = 1;\n const pendingTimers = new Map<number, NodeJS.Timeout>();\n let disposed = false;\n\n const global = context.global;\n\n // Register timeout on host, return ID\n const registerTimeoutCallback = new ivm.Callback((delay: number) => {\n const id = nextTimerId++;\n const normalizedDelay = Math.max(0, delay || 0);\n\n const handle = setTimeout(() => {\n if (disposed || !pendingTimers.has(id)) return;\n pendingTimers.delete(id);\n try {\n context.evalSync(`__timers_execute(${id})`);\n context.evalSync(`__timers_removeCallback(${id})`);\n } catch {\n // Context may have been disposed\n }\n }, normalizedDelay);\n\n pendingTimers.set(id, handle);\n return id;\n });\n global.setSync(\n \"__timers_registerTimeout\",\n registerTimeoutCallback\n );\n\n // Register interval on host, return ID\n const registerIntervalCallback = new ivm.Callback((delay: number) => {\n const id = nextTimerId++;\n const normalizedDelay = Math.max(0, delay || 0);\n\n const handle = setInterval(() => {\n if (disposed || !pendingTimers.has(id)) return;\n try {\n context.evalSync(`__timers_execute(${id})`);\n } catch {\n // Context may have been disposed\n }\n }, normalizedDelay);\n\n pendingTimers.set(id, handle);\n return id;\n });\n global.setSync(\n \"__timers_registerInterval\",\n registerIntervalCallback\n );\n\n // Clear timer by ID\n const clearTimerCallback = new ivm.Callback((id: number) => {\n const handle = pendingTimers.get(id);\n if (handle) {\n clearTimeout(handle); // works for both timeout and interval\n pendingTimers.delete(id);\n }\n });\n global.setSync(\n \"__timers_clear\",\n clearTimerCallback\n );\n\n // Inject JavaScript timer APIs\n const timersCode = `\n(function() {\n const __timers_callbacks = new Map();\n\n globalThis.setTimeout = function(callback, delay, ...args) {\n if (typeof callback !== 'function') {\n throw new TypeError('Callback must be a function');\n }\n const id = __timers_registerTimeout(delay || 0);\n __timers_callbacks.set(id, { callback, args });\n return id;\n };\n\n globalThis.setInterval = function(callback, delay, ...args) {\n if (typeof callback !== 'function') {\n throw new TypeError('Callback must be a function');\n }\n const id = __timers_registerInterval(delay || 0);\n __timers_callbacks.set(id, { callback, args });\n return id;\n };\n\n globalThis.clearTimeout = function(id) {\n __timers_clear(id);\n __timers_callbacks.delete(id);\n };\n\n globalThis.clearInterval = globalThis.clearTimeout;\n\n // Called by host to execute a timer callback\n globalThis.__timers_execute = function(id) {\n const entry = __timers_callbacks.get(id);\n if (entry) {\n entry.callback(...entry.args);\n }\n };\n\n // Called by host clearAll() to clear all callbacks\n globalThis.__timers_clearCallbacks = function() {\n __timers_callbacks.clear();\n };\n\n // Called to remove a one-shot timeout callback after execution\n globalThis.__timers_removeCallback = function(id) {\n __timers_callbacks.delete(id);\n };\n})();\n`;\n\n context.evalSync(timersCode);\n\n return {\n clearAll() {\n for (const handle of pendingTimers.values()) {\n clearTimeout(handle);\n }\n pendingTimers.clear();\n try {\n context.evalSync(\"__timers_clearCallbacks()\");\n } catch {\n // Context may have been disposed\n }\n },\n\n dispose() {\n disposed = true;\n for (const handle of pendingTimers.values()) {\n clearTimeout(handle);\n }\n pendingTimers.clear();\n try {\n context.evalSync(\"__timers_clearCallbacks()\");\n } catch {\n // Context may have been disposed\n }\n try {\n releaseIfSupported(registerTimeoutCallback);\n } catch {\n // Ignore repeated dispose races\n }\n try {\n releaseIfSupported(registerIntervalCallback);\n } catch {\n // Ignore repeated dispose races\n }\n try {\n releaseIfSupported(clearTimerCallback);\n } catch {\n // Ignore repeated dispose races\n }\n },\n };\n}\n"
5
+ "import ivm from \"@ricsam/isolated-vm\";\n\nexport interface TimersHandle {\n /** Clear all pending timers */\n clearAll(): void;\n /** Dispose the timers handle */\n dispose(): void;\n}\n\nfunction releaseIfSupported(handle: unknown): void {\n const maybeHandle = handle as { release?: () => void };\n if (typeof maybeHandle.release === \"function\") {\n maybeHandle.release();\n }\n}\n\n/**\n * Setup timer APIs in an isolated-vm context\n *\n * Injects setTimeout, setInterval, clearTimeout, clearInterval\n *\n * Uses real time - timers fire automatically based on actual elapsed time.\n *\n * @example\n * const handle = await setupTimers(context);\n * await context.eval(`\n * setTimeout(() => console.log(\"hello\"), 1000);\n * `);\n * // Timer will fire automatically after 1 second\n */\nexport async function setupTimers(\n context: ivm.Context\n): Promise<TimersHandle> {\n let nextTimerId = 1;\n const pendingTimers = new Map<number, NodeJS.Timeout>();\n let disposed = false;\n\n const global = context.global;\n\n // Register timeout on host, return ID\n const registerTimeoutCallback = new ivm.Callback((delay: number) => {\n const id = nextTimerId++;\n const normalizedDelay = Math.max(0, delay || 0);\n\n const handle = setTimeout(() => {\n if (disposed || !pendingTimers.has(id)) return;\n pendingTimers.delete(id);\n try {\n context.evalSync(`__timers_execute(${id})`);\n context.evalSync(`__timers_removeCallback(${id})`);\n } catch {\n // Context may have been disposed\n }\n }, normalizedDelay);\n\n pendingTimers.set(id, handle);\n return id;\n });\n global.setSync(\n \"__timers_registerTimeout\",\n registerTimeoutCallback\n );\n\n // Register interval on host, return ID\n const registerIntervalCallback = new ivm.Callback((delay: number) => {\n const id = nextTimerId++;\n const normalizedDelay = Math.max(0, delay || 0);\n\n const handle = setInterval(() => {\n if (disposed || !pendingTimers.has(id)) return;\n try {\n context.evalSync(`__timers_execute(${id})`);\n } catch {\n // Context may have been disposed\n }\n }, normalizedDelay);\n\n pendingTimers.set(id, handle);\n return id;\n });\n global.setSync(\n \"__timers_registerInterval\",\n registerIntervalCallback\n );\n\n // Clear timer by ID\n const clearTimerCallback = new ivm.Callback((id: number) => {\n const handle = pendingTimers.get(id);\n if (handle) {\n clearTimeout(handle); // works for both timeout and interval\n pendingTimers.delete(id);\n }\n });\n global.setSync(\n \"__timers_clear\",\n clearTimerCallback\n );\n\n // Inject JavaScript timer APIs\n const timersCode = `\n(function() {\n const __timers_callbacks = new Map();\n const __wrapAsyncContextCallback = (callback, type) => (\n typeof callback === 'function' && globalThis.__isolateAsyncContextInternals?.wrapCallback\n ? globalThis.__isolateAsyncContextInternals.wrapCallback(callback, { type })\n : callback\n );\n const __releaseAsyncContextCallback = (callback) => (\n typeof callback === 'function' && globalThis.__isolateAsyncContextInternals?.releaseCallback\n ? globalThis.__isolateAsyncContextInternals.releaseCallback(callback)\n : false\n );\n\n globalThis.setTimeout = function(callback, delay, ...args) {\n if (typeof callback !== 'function') {\n throw new TypeError('Callback must be a function');\n }\n const id = __timers_registerTimeout(delay || 0);\n __timers_callbacks.set(id, {\n args,\n callback: __wrapAsyncContextCallback(callback, 'Timeout'),\n repeat: false,\n });\n return id;\n };\n\n globalThis.setInterval = function(callback, delay, ...args) {\n if (typeof callback !== 'function') {\n throw new TypeError('Callback must be a function');\n }\n const id = __timers_registerInterval(delay || 0);\n __timers_callbacks.set(id, {\n args,\n callback: __wrapAsyncContextCallback(callback, 'Timeout'),\n repeat: true,\n });\n return id;\n };\n\n globalThis.clearTimeout = function(id) {\n __timers_clear(id);\n const entry = __timers_callbacks.get(id);\n if (entry) {\n __releaseAsyncContextCallback(entry.callback);\n __timers_callbacks.delete(id);\n }\n };\n\n globalThis.clearInterval = globalThis.clearTimeout;\n\n // Called by host to execute a timer callback\n globalThis.__timers_execute = function(id) {\n const entry = __timers_callbacks.get(id);\n if (entry) {\n try {\n entry.callback(...entry.args);\n } finally {\n if (!entry.repeat) {\n __releaseAsyncContextCallback(entry.callback);\n }\n }\n }\n };\n\n // Called by host clearAll() to clear all callbacks\n globalThis.__timers_clearCallbacks = function() {\n for (const entry of __timers_callbacks.values()) {\n __releaseAsyncContextCallback(entry.callback);\n }\n __timers_callbacks.clear();\n };\n\n // Called to remove a one-shot timeout callback after execution\n globalThis.__timers_removeCallback = function(id) {\n const entry = __timers_callbacks.get(id);\n if (entry) {\n __releaseAsyncContextCallback(entry.callback);\n __timers_callbacks.delete(id);\n }\n };\n})();\n`;\n\n context.evalSync(timersCode);\n\n return {\n clearAll() {\n for (const handle of pendingTimers.values()) {\n clearTimeout(handle);\n }\n pendingTimers.clear();\n try {\n context.evalSync(\"__timers_clearCallbacks()\");\n } catch {\n // Context may have been disposed\n }\n },\n\n dispose() {\n disposed = true;\n for (const handle of pendingTimers.values()) {\n clearTimeout(handle);\n }\n pendingTimers.clear();\n try {\n context.evalSync(\"__timers_clearCallbacks()\");\n } catch {\n // Context may have been disposed\n }\n try {\n releaseIfSupported(registerTimeoutCallback);\n } catch {\n // Ignore repeated dispose races\n }\n try {\n releaseIfSupported(registerIntervalCallback);\n } catch {\n // Ignore repeated dispose races\n }\n try {\n releaseIfSupported(clearTimerCallback);\n } catch {\n // Ignore repeated dispose races\n }\n },\n };\n}\n"
6
6
  ],
7
- "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAgB,IAAhB;AASA,SAAS,kBAAkB,CAAC,QAAuB;AAAA,EACjD,MAAM,cAAc;AAAA,EACpB,IAAI,OAAO,YAAY,YAAY,YAAY;AAAA,IAC7C,YAAY,QAAQ;AAAA,EACtB;AAAA;AAiBF,eAAsB,WAAW,CAC/B,SACuB;AAAA,EACvB,IAAI,cAAc;AAAA,EAClB,MAAM,gBAAgB,IAAI;AAAA,EAC1B,IAAI,WAAW;AAAA,EAEf,MAAM,SAAS,QAAQ;AAAA,EAGvB,MAAM,0BAA0B,IAAI,2BAAI,SAAS,CAAC,UAAkB;AAAA,IAClE,MAAM,KAAK;AAAA,IACX,MAAM,kBAAkB,KAAK,IAAI,GAAG,SAAS,CAAC;AAAA,IAE9C,MAAM,SAAS,WAAW,MAAM;AAAA,MAC9B,IAAI,YAAY,CAAC,cAAc,IAAI,EAAE;AAAA,QAAG;AAAA,MACxC,cAAc,OAAO,EAAE;AAAA,MACvB,IAAI;AAAA,QACF,QAAQ,SAAS,oBAAoB,KAAK;AAAA,QAC1C,QAAQ,SAAS,2BAA2B,KAAK;AAAA,QACjD,MAAM;AAAA,OAGP,eAAe;AAAA,IAElB,cAAc,IAAI,IAAI,MAAM;AAAA,IAC5B,OAAO;AAAA,GACR;AAAA,EACD,OAAO,QACL,4BACA,uBACF;AAAA,EAGA,MAAM,2BAA2B,IAAI,2BAAI,SAAS,CAAC,UAAkB;AAAA,IACnE,MAAM,KAAK;AAAA,IACX,MAAM,kBAAkB,KAAK,IAAI,GAAG,SAAS,CAAC;AAAA,IAE9C,MAAM,SAAS,YAAY,MAAM;AAAA,MAC/B,IAAI,YAAY,CAAC,cAAc,IAAI,EAAE;AAAA,QAAG;AAAA,MACxC,IAAI;AAAA,QACF,QAAQ,SAAS,oBAAoB,KAAK;AAAA,QAC1C,MAAM;AAAA,OAGP,eAAe;AAAA,IAElB,cAAc,IAAI,IAAI,MAAM;AAAA,IAC5B,OAAO;AAAA,GACR;AAAA,EACD,OAAO,QACL,6BACA,wBACF;AAAA,EAGA,MAAM,qBAAqB,IAAI,2BAAI,SAAS,CAAC,OAAe;AAAA,IAC1D,MAAM,SAAS,cAAc,IAAI,EAAE;AAAA,IACnC,IAAI,QAAQ;AAAA,MACV,aAAa,MAAM;AAAA,MACnB,cAAc,OAAO,EAAE;AAAA,IACzB;AAAA,GACD;AAAA,EACD,OAAO,QACL,kBACA,kBACF;AAAA,EAGA,MAAM,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiDnB,QAAQ,SAAS,UAAU;AAAA,EAE3B,OAAO;AAAA,IACL,QAAQ,GAAG;AAAA,MACT,WAAW,UAAU,cAAc,OAAO,GAAG;AAAA,QAC3C,aAAa,MAAM;AAAA,MACrB;AAAA,MACA,cAAc,MAAM;AAAA,MACpB,IAAI;AAAA,QACF,QAAQ,SAAS,2BAA2B;AAAA,QAC5C,MAAM;AAAA;AAAA,IAKV,OAAO,GAAG;AAAA,MACR,WAAW;AAAA,MACX,WAAW,UAAU,cAAc,OAAO,GAAG;AAAA,QAC3C,aAAa,MAAM;AAAA,MACrB;AAAA,MACA,cAAc,MAAM;AAAA,MACpB,IAAI;AAAA,QACF,QAAQ,SAAS,2BAA2B;AAAA,QAC5C,MAAM;AAAA,MAGR,IAAI;AAAA,QACF,mBAAmB,uBAAuB;AAAA,QAC1C,MAAM;AAAA,MAGR,IAAI;AAAA,QACF,mBAAmB,wBAAwB;AAAA,QAC3C,MAAM;AAAA,MAGR,IAAI;AAAA,QACF,mBAAmB,kBAAkB;AAAA,QACrC,MAAM;AAAA;AAAA,EAIZ;AAAA;",
8
- "debugId": "9FDA92E9C86B929164756E2164756E21",
7
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAgB,IAAhB;AASA,SAAS,kBAAkB,CAAC,QAAuB;AAAA,EACjD,MAAM,cAAc;AAAA,EACpB,IAAI,OAAO,YAAY,YAAY,YAAY;AAAA,IAC7C,YAAY,QAAQ;AAAA,EACtB;AAAA;AAiBF,eAAsB,WAAW,CAC/B,SACuB;AAAA,EACvB,IAAI,cAAc;AAAA,EAClB,MAAM,gBAAgB,IAAI;AAAA,EAC1B,IAAI,WAAW;AAAA,EAEf,MAAM,SAAS,QAAQ;AAAA,EAGvB,MAAM,0BAA0B,IAAI,2BAAI,SAAS,CAAC,UAAkB;AAAA,IAClE,MAAM,KAAK;AAAA,IACX,MAAM,kBAAkB,KAAK,IAAI,GAAG,SAAS,CAAC;AAAA,IAE9C,MAAM,SAAS,WAAW,MAAM;AAAA,MAC9B,IAAI,YAAY,CAAC,cAAc,IAAI,EAAE;AAAA,QAAG;AAAA,MACxC,cAAc,OAAO,EAAE;AAAA,MACvB,IAAI;AAAA,QACF,QAAQ,SAAS,oBAAoB,KAAK;AAAA,QAC1C,QAAQ,SAAS,2BAA2B,KAAK;AAAA,QACjD,MAAM;AAAA,OAGP,eAAe;AAAA,IAElB,cAAc,IAAI,IAAI,MAAM;AAAA,IAC5B,OAAO;AAAA,GACR;AAAA,EACD,OAAO,QACL,4BACA,uBACF;AAAA,EAGA,MAAM,2BAA2B,IAAI,2BAAI,SAAS,CAAC,UAAkB;AAAA,IACnE,MAAM,KAAK;AAAA,IACX,MAAM,kBAAkB,KAAK,IAAI,GAAG,SAAS,CAAC;AAAA,IAE9C,MAAM,SAAS,YAAY,MAAM;AAAA,MAC/B,IAAI,YAAY,CAAC,cAAc,IAAI,EAAE;AAAA,QAAG;AAAA,MACxC,IAAI;AAAA,QACF,QAAQ,SAAS,oBAAoB,KAAK;AAAA,QAC1C,MAAM;AAAA,OAGP,eAAe;AAAA,IAElB,cAAc,IAAI,IAAI,MAAM;AAAA,IAC5B,OAAO;AAAA,GACR;AAAA,EACD,OAAO,QACL,6BACA,wBACF;AAAA,EAGA,MAAM,qBAAqB,IAAI,2BAAI,SAAS,CAAC,OAAe;AAAA,IAC1D,MAAM,SAAS,cAAc,IAAI,EAAE;AAAA,IACnC,IAAI,QAAQ;AAAA,MACV,aAAa,MAAM;AAAA,MACnB,cAAc,OAAO,EAAE;AAAA,IACzB;AAAA,GACD;AAAA,EACD,OAAO,QACL,kBACA,kBACF;AAAA,EAGA,MAAM,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoFnB,QAAQ,SAAS,UAAU;AAAA,EAE3B,OAAO;AAAA,IACL,QAAQ,GAAG;AAAA,MACT,WAAW,UAAU,cAAc,OAAO,GAAG;AAAA,QAC3C,aAAa,MAAM;AAAA,MACrB;AAAA,MACA,cAAc,MAAM;AAAA,MACpB,IAAI;AAAA,QACF,QAAQ,SAAS,2BAA2B;AAAA,QAC5C,MAAM;AAAA;AAAA,IAKV,OAAO,GAAG;AAAA,MACR,WAAW;AAAA,MACX,WAAW,UAAU,cAAc,OAAO,GAAG;AAAA,QAC3C,aAAa,MAAM;AAAA,MACrB;AAAA,MACA,cAAc,MAAM;AAAA,MACpB,IAAI;AAAA,QACF,QAAQ,SAAS,2BAA2B;AAAA,QAC5C,MAAM;AAAA,MAGR,IAAI;AAAA,QACF,mBAAmB,uBAAuB;AAAA,QAC1C,MAAM;AAAA,MAGR,IAAI;AAAA,QACF,mBAAmB,wBAAwB;AAAA,QAC3C,MAAM;AAAA,MAGR,IAAI;AAAA,QACF,mBAAmB,kBAAkB;AAAA,QACrC,MAAM;AAAA;AAAA,EAIZ;AAAA;",
8
+ "debugId": "1F15FD83CCC6783564756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -72,6 +72,41 @@ var CORE_TYPES = `/**
72
72
  export {};
73
73
 
74
74
  declare global {
75
+ // ============================================
76
+ // Async Context
77
+ // ============================================
78
+
79
+ interface AsyncContextVariableOptions<T = unknown> {
80
+ defaultValue?: T;
81
+ name?: string;
82
+ }
83
+
84
+ interface AsyncContextVariable<T = unknown> {
85
+ readonly name: string;
86
+ run<R, A extends unknown[]>(value: T, fn: (...args: A) => R, ...args: A): R;
87
+ get(): T;
88
+ }
89
+
90
+ interface AsyncContextVariableConstructor {
91
+ new <T = unknown>(options?: AsyncContextVariableOptions<T>): AsyncContextVariable<T>;
92
+ readonly prototype: AsyncContextVariable<unknown>;
93
+ }
94
+
95
+ interface AsyncContextSnapshot {
96
+ run<R, A extends unknown[]>(fn: (...args: A) => R, ...args: A): R;
97
+ }
98
+
99
+ interface AsyncContextSnapshotConstructor {
100
+ new (): AsyncContextSnapshot;
101
+ wrap<T extends (...args: any[]) => any>(fn: T): T;
102
+ readonly prototype: AsyncContextSnapshot;
103
+ }
104
+
105
+ const AsyncContext: {
106
+ Variable: AsyncContextVariableConstructor;
107
+ Snapshot: AsyncContextSnapshotConstructor;
108
+ };
109
+
75
110
  // ============================================
76
111
  // Web Streams API
77
112
  // ============================================
@@ -2611,4 +2646,4 @@ var TYPE_DEFINITIONS = {
2611
2646
  playwright: PLAYWRIGHT_TYPES
2612
2647
  };
2613
2648
 
2614
- //# debugId=40102DE3B6889F0C64756E2164756E21
2649
+ //# debugId=4C7C616C6AE7C1E764756E2164756E21