@alwaysmeticulous/backend-recorder-workerd 2.331.3 → 2.333.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/LICENSE +15 -0
  2. package/dist/binding-patch.js +1 -4
  3. package/dist/binding-patch.js.map +1 -1
  4. package/dist/body-capture.js +1 -4
  5. package/dist/body-capture.js.map +1 -1
  6. package/dist/capture-buffer.js +1 -4
  7. package/dist/capture-buffer.js.map +1 -1
  8. package/dist/context.js +1 -4
  9. package/dist/context.js.map +1 -1
  10. package/dist/coverage/replay-coverage.js +1 -4
  11. package/dist/coverage/replay-coverage.js.map +1 -1
  12. package/dist/coverage/runtime.js +1 -4
  13. package/dist/coverage/runtime.js.map +1 -1
  14. package/dist/error-capture.js +1 -4
  15. package/dist/error-capture.js.map +1 -1
  16. package/dist/fetch-patch.js +1 -4
  17. package/dist/fetch-patch.js.map +1 -1
  18. package/dist/index.js +1 -4
  19. package/dist/index.js.map +1 -1
  20. package/dist/kv-capture.js +1 -4
  21. package/dist/kv-capture.js.map +1 -1
  22. package/dist/kv-patch.js +1 -4
  23. package/dist/kv-patch.js.map +1 -1
  24. package/dist/log.js +1 -4
  25. package/dist/log.js.map +1 -1
  26. package/dist/original-fetch.js +1 -4
  27. package/dist/original-fetch.js.map +1 -1
  28. package/dist/outbound-capture.js +1 -4
  29. package/dist/outbound-capture.js.map +1 -1
  30. package/dist/pages-function.js +1 -4
  31. package/dist/pages-function.js.map +1 -1
  32. package/dist/postgres/capture.js +1 -4
  33. package/dist/postgres/capture.js.map +1 -1
  34. package/dist/postgres/with-meticulous-postgres.js +1 -4
  35. package/dist/postgres/with-meticulous-postgres.js.map +1 -1
  36. package/dist/protocol.js +1 -4
  37. package/dist/protocol.js.map +1 -1
  38. package/dist/provisional-session-id.js +1 -4
  39. package/dist/provisional-session-id.js.map +1 -1
  40. package/dist/publish-session-id.js +1 -4
  41. package/dist/publish-session-id.js.map +1 -1
  42. package/dist/publish-shim-version.js +1 -4
  43. package/dist/publish-shim-version.js.map +1 -1
  44. package/dist/redact-body.js +1 -4
  45. package/dist/redact-body.js.map +1 -1
  46. package/dist/replay-fetch.js +1 -4
  47. package/dist/replay-fetch.js.map +1 -1
  48. package/dist/replay-log-tagging.js +1 -4
  49. package/dist/replay-log-tagging.js.map +1 -1
  50. package/dist/replay-sidecar-url.js +1 -4
  51. package/dist/replay-sidecar-url.js.map +1 -1
  52. package/dist/sidecar-client.js +1 -4
  53. package/dist/sidecar-client.js.map +1 -1
  54. package/dist/sidecar-transport.js +1 -4
  55. package/dist/sidecar-transport.js.map +1 -1
  56. package/dist/spans/attributes.js +1 -4
  57. package/dist/spans/attributes.js.map +1 -1
  58. package/dist/spans/readable-span.js +1 -4
  59. package/dist/spans/readable-span.js.map +1 -1
  60. package/dist/spans/serialize-span.js +1 -4
  61. package/dist/spans/serialize-span.js.map +1 -1
  62. package/dist/spans/span-builder.js +1 -4
  63. package/dist/spans/span-builder.js.map +1 -1
  64. package/dist/spans/url-redaction.js +1 -4
  65. package/dist/spans/url-redaction.js.map +1 -1
  66. package/dist/version.js +2 -5
  67. package/dist/version.js.map +1 -1
  68. package/dist/virtual-clock.js +1 -4
  69. package/dist/virtual-clock.js.map +1 -1
  70. package/dist/virtual-random.js +1 -4
  71. package/dist/virtual-random.js.map +1 -1
  72. package/dist/vite/index.js +15 -16
  73. package/dist/vite/index.js.map +1 -1
  74. package/dist/with-meticulous.js +0 -0
  75. package/dist/with-meticulous.js.map +1 -1
  76. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"virtual-clock.js","sources":["../src/virtual-clock.ts"],"sourcesContent":["import { requestCaptureContext } from \"./context\";\nimport { warnOnce } from \"./log\";\n\n/**\n * Freezes the worker's wall clock at the replayed session's last recorded activity.\n *\n * Without this, replay fails even when every outbound call is mocked correctly: credentials\n * minted during the recording (Firebase session cookies, OAuth access tokens) carry\n * short expiries, so by replay time the app rejects its own recorded credentials as stale.\n * Anchoring at the *end* of the recording means everything minted mid-session is already\n * issued but not yet expired.\n *\n * Ported from the Node backend recorder's replay clock, with two differences:\n * - the anchor comes from the request context rather than a session→anchor map, because\n * workerd's AsyncLocalStorage propagates reliably from the inbound handler. The Node\n * version needs a process-wide \"most recently seen session\" fallback, which can\n * resolve the wrong session on a replica serving several at once;\n * - the busy-wait breaker schedules on `setTimeout` — workerd has no `setImmediate`.\n *\n * Only `Date` is patched, matching the Node implementation's scope (no `Intl`, no\n * `Temporal`, no timer virtualisation).\n */\n\nconst OriginalDate = globalThis.Date;\n\n/** Real wall clock, immune to the patch below. */\nexport const nativeDateNow = (): number => OriginalDate.now();\n\n/**\n * The frozen time a replayed request with no anchor gets: 13 May 2026, World Cocktail Day.\n * A replay whose anchor is missing is already degraded, and live time there is the exact\n * non-determinism this file exists to remove — two replays of that session would render\n * different timestamps. A fixed recent date rather than the epoch, since app code routinely\n * renders or rejects an implausibly old timestamp differently. Matches the Node recorder's\n * unattributed fallback (`replay/virtual-clock.ts`).\n *\n * Exported for the tests: this is the time such a replay renders, so changing it changes\n * that replay's output.\n */\nexport const UNANCHORED_BASE_TIME_MS = OriginalDate.UTC(2026, 4, 13);\n\n// ---------------------------------------------------------------------------\n// Busy-wait loop breaker\n// ---------------------------------------------------------------------------\n\n// A frozen clock makes `while (Date.now() < deadline) {}` spin forever. Count reads that\n// return the same frozen value and, past the threshold, nudge the result forward 1ms per\n// extra read until the loop's deadline is met. The nudge is a pure function of the repeat\n// count, so a base and head replay break an identical loop identically.\nconst BREAK_AFTER = 20_000;\n\nlet lastLoopBreakBaseMs = 0;\nlet sameKeyCount = 0;\nlet loopBreakResetScheduled = false;\n\nconst applyLoopBreak = (baseMs: number): number => {\n // Reset at the end of the current macrotask. A synchronous busy-wait blocks the loop so\n // this cannot fire, letting the counter accumulate until it breaks the spin; once the\n // turn yields, the reset runs and the next turn starts fresh.\n if (!loopBreakResetScheduled) {\n loopBreakResetScheduled = true;\n try {\n setTimeout(() => {\n sameKeyCount = 0;\n loopBreakResetScheduled = false;\n }, 0);\n } catch {\n // Timers throw outside a request context in workerd. Nothing to reset in that case,\n // since clock reads there fall through to the real clock anyway.\n loopBreakResetScheduled = false;\n }\n }\n\n if (baseMs === lastLoopBreakBaseMs) {\n sameKeyCount++;\n } else {\n lastLoopBreakBaseMs = baseMs;\n sameKeyCount = 0;\n }\n return sameKeyCount <= BREAK_AFTER\n ? baseMs\n : baseMs + (sameKeyCount - BREAK_AFTER);\n};\n\n// ---------------------------------------------------------------------------\n// Resolver\n// ---------------------------------------------------------------------------\n\n/**\n * The virtual \"now\" for the request currently executing, or null when this is not a replayed\n * request — a recording, a deployed production worker, or a read outside any request (module\n * evaluation, background work) — in which case callers use the real clock. A replayed request\n * always gets a frozen time: its own anchor, or {@link UNANCHORED_BASE_TIME_MS} if it carries\n * none.\n */\nconst resolveVirtualNow = (): number | null => {\n const ctx = requestCaptureContext.getStore();\n if (ctx === undefined || ctx.mode !== \"replay\") {\n return null;\n }\n return applyLoopBreak(ctx.clockAnchorMs ?? UNANCHORED_BASE_TIME_MS);\n};\n\n// ---------------------------------------------------------------------------\n// Date shim\n// ---------------------------------------------------------------------------\n\n/**\n * What `Date.now()` reports. A standalone function rather than only a class static, so the\n * callable shim below can reuse it without referencing an unbound method — it touches no\n * `this`, so there is nothing to lose.\n */\nconst virtualDateNow = (): number => {\n const virtualNow = resolveVirtualNow();\n return virtualNow === null ? OriginalDate.now() : virtualNow;\n};\n\n// `Date` is heavily overloaded; capture args loosely and forward every explicit form\n// unchanged. Only the zero-arg form reads \"now\" and is virtualized.\nclass VirtualDateClass extends OriginalDate {\n constructor(...args: unknown[]) {\n if (args.length > 0) {\n super(...(args as ConstructorParameters<typeof OriginalDate>));\n return;\n }\n const virtualNow = resolveVirtualNow();\n if (virtualNow === null) {\n super();\n return;\n }\n super(virtualNow);\n }\n\n static override now(): number {\n return virtualDateNow();\n }\n}\n\n// Classes cannot be called without `new`, but native `Date()` can be (returning a string),\n// so wrap the class in a plain function supporting both.\nfunction VirtualDate(this: unknown, ...args: unknown[]): Date | string {\n if (!(this instanceof VirtualDate)) {\n return new VirtualDateClass().toString();\n }\n return new VirtualDateClass(...args);\n}\n\nVirtualDate.now = virtualDateNow;\nVirtualDate.parse = OriginalDate.parse;\nVirtualDate.UTC = OriginalDate.UTC;\n\n// Share the prototype so `new Date() instanceof Date` holds and every Date.prototype\n// method resolves on instances.\nObject.defineProperty(VirtualDate, \"prototype\", {\n value: VirtualDateClass.prototype,\n writable: false,\n configurable: false,\n});\n(VirtualDateClass.prototype as { constructor: unknown }).constructor =\n VirtualDate;\n\n// ---------------------------------------------------------------------------\n// Install\n// ---------------------------------------------------------------------------\n\nlet installed = false;\n\n/**\n * Replaces the global `Date` with the per-request virtual clock. Idempotent, and a no-op\n * failure: a worker that will not let its `Date` be replaced still replays, just without\n * clock virtualisation.\n *\n * Called lazily from the replay branch of the request wrapper rather than at module scope:\n * record mode and deployed production workers then pay nothing, and the package declares no\n * `sideEffects`, so a module-scope side effect could legitimately be tree-shaken away by a\n * customer's bundler.\n */\nexport const installVirtualClock = (): void => {\n if (installed) {\n return;\n }\n installed = true;\n try {\n globalThis.Date = VirtualDate as unknown as DateConstructor;\n } catch (error) {\n warnOnce(\n \"virtual-clock\",\n \"Could not install the Meticulous replay clock — recorded credentials may be treated as expired.\",\n error,\n );\n }\n};\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAuBA,MAAM,eAAe,WAAW;;;;;;;;;;;;AAgBhC,MAAa,0BAA0B,aAAa,IAAI,MAAM,GAAG,GAAG;AAUpE,MAAM,cAAc;AAEpB,IAAI,sBAAsB;AAC1B,IAAI,eAAe;AACnB,IAAI,0BAA0B;AAE9B,MAAM,kBAAkB,WAA2B;AAIjD,KAAI,CAAC,yBAAyB;AAC5B,4BAA0B;AAC1B,MAAI;AACF,oBAAiB;AACf,mBAAe;AACf,8BAA0B;MACzB,EAAE;UACC;AAGN,6BAA0B;;;AAI9B,KAAI,WAAW,oBACb;MACK;AACL,wBAAsB;AACtB,iBAAe;;AAEjB,QAAO,gBAAgB,cACnB,SACA,UAAU,eAAe;;;;;;;;;AAc/B,MAAM,0BAAyC;CAC7C,MAAM,MAAM,sBAAsB,UAAU;AAC5C,KAAI,QAAQ,KAAA,KAAa,IAAI,SAAS,SACpC,QAAO;AAET,QAAO,eAAe,IAAI,iBAAiB,wBAAwB;;;;;;;AAYrE,MAAM,uBAA+B;CACnC,MAAM,aAAa,mBAAmB;AACtC,QAAO,eAAe,OAAO,aAAa,KAAK,GAAG;;AAKpD,IAAM,mBAAN,cAA+B,aAAa;CAC1C,YAAY,GAAG,MAAiB;AAC9B,MAAI,KAAK,SAAS,GAAG;AACnB,SAAM,GAAI,KAAoD;AAC9D;;EAEF,MAAM,aAAa,mBAAmB;AACtC,MAAI,eAAe,MAAM;AACvB,UAAO;AACP;;AAEF,QAAM,WAAW;;CAGnB,OAAgB,MAAc;AAC5B,SAAO,gBAAgB;;;AAM3B,SAAS,YAA2B,GAAG,MAAgC;AACrE,KAAI,EAAE,gBAAgB,aACpB,QAAO,IAAI,kBAAkB,CAAC,UAAU;AAE1C,QAAO,IAAI,iBAAiB,GAAG,KAAK;;AAGtC,YAAY,MAAM;AAClB,YAAY,QAAQ,aAAa;AACjC,YAAY,MAAM,aAAa;AAI/B,OAAO,eAAe,aAAa,aAAa;CAC9C,OAAO,iBAAiB;CACxB,UAAU;CACV,cAAc;CACf,CAAC;AACF,iBAAkB,UAAuC,cACvD;AAMF,IAAI,YAAY;;;;;;;;;;;AAYhB,MAAa,4BAAkC;AAC7C,KAAI,UACF;AAEF,aAAY;AACZ,KAAI;AACF,aAAW,OAAO;UACX,OAAO;AACd,WACE,iBACA,mGACA,MACD","debugId":"39c427c3-1a1e-58aa-9021-0bd113903895"}
1
+ {"version":3,"file":"virtual-clock.js","names":[],"sources":["../src/virtual-clock.ts"],"sourcesContent":["import { requestCaptureContext } from \"./context\";\nimport { warnOnce } from \"./log\";\n\n/**\n * Freezes the worker's wall clock at the replayed session's last recorded activity.\n *\n * Without this, replay fails even when every outbound call is mocked correctly: credentials\n * minted during the recording (Firebase session cookies, OAuth access tokens) carry\n * short expiries, so by replay time the app rejects its own recorded credentials as stale.\n * Anchoring at the *end* of the recording means everything minted mid-session is already\n * issued but not yet expired.\n *\n * Ported from the Node backend recorder's replay clock, with two differences:\n * - the anchor comes from the request context rather than a session→anchor map, because\n * workerd's AsyncLocalStorage propagates reliably from the inbound handler. The Node\n * version needs a process-wide \"most recently seen session\" fallback, which can\n * resolve the wrong session on a replica serving several at once;\n * - the busy-wait breaker schedules on `setTimeout` — workerd has no `setImmediate`.\n *\n * Only `Date` is patched, matching the Node implementation's scope (no `Intl`, no\n * `Temporal`, no timer virtualisation).\n */\n\nconst OriginalDate = globalThis.Date;\n\n/** Real wall clock, immune to the patch below. */\nexport const nativeDateNow = (): number => OriginalDate.now();\n\n/**\n * The frozen time a replayed request with no anchor gets: 13 May 2026, World Cocktail Day.\n * A replay whose anchor is missing is already degraded, and live time there is the exact\n * non-determinism this file exists to remove — two replays of that session would render\n * different timestamps. A fixed recent date rather than the epoch, since app code routinely\n * renders or rejects an implausibly old timestamp differently. Matches the Node recorder's\n * unattributed fallback (`replay/virtual-clock.ts`).\n *\n * Exported for the tests: this is the time such a replay renders, so changing it changes\n * that replay's output.\n */\nexport const UNANCHORED_BASE_TIME_MS = OriginalDate.UTC(2026, 4, 13);\n\n// ---------------------------------------------------------------------------\n// Busy-wait loop breaker\n// ---------------------------------------------------------------------------\n\n// A frozen clock makes `while (Date.now() < deadline) {}` spin forever. Count reads that\n// return the same frozen value and, past the threshold, nudge the result forward 1ms per\n// extra read until the loop's deadline is met. The nudge is a pure function of the repeat\n// count, so a base and head replay break an identical loop identically.\nconst BREAK_AFTER = 20_000;\n\nlet lastLoopBreakBaseMs = 0;\nlet sameKeyCount = 0;\nlet loopBreakResetScheduled = false;\n\nconst applyLoopBreak = (baseMs: number): number => {\n // Reset at the end of the current macrotask. A synchronous busy-wait blocks the loop so\n // this cannot fire, letting the counter accumulate until it breaks the spin; once the\n // turn yields, the reset runs and the next turn starts fresh.\n if (!loopBreakResetScheduled) {\n loopBreakResetScheduled = true;\n try {\n setTimeout(() => {\n sameKeyCount = 0;\n loopBreakResetScheduled = false;\n }, 0);\n } catch {\n // Timers throw outside a request context in workerd. Nothing to reset in that case,\n // since clock reads there fall through to the real clock anyway.\n loopBreakResetScheduled = false;\n }\n }\n\n if (baseMs === lastLoopBreakBaseMs) {\n sameKeyCount++;\n } else {\n lastLoopBreakBaseMs = baseMs;\n sameKeyCount = 0;\n }\n return sameKeyCount <= BREAK_AFTER\n ? baseMs\n : baseMs + (sameKeyCount - BREAK_AFTER);\n};\n\n// ---------------------------------------------------------------------------\n// Resolver\n// ---------------------------------------------------------------------------\n\n/**\n * The virtual \"now\" for the request currently executing, or null when this is not a replayed\n * request — a recording, a deployed production worker, or a read outside any request (module\n * evaluation, background work) — in which case callers use the real clock. A replayed request\n * always gets a frozen time: its own anchor, or {@link UNANCHORED_BASE_TIME_MS} if it carries\n * none.\n */\nconst resolveVirtualNow = (): number | null => {\n const ctx = requestCaptureContext.getStore();\n if (ctx === undefined || ctx.mode !== \"replay\") {\n return null;\n }\n return applyLoopBreak(ctx.clockAnchorMs ?? UNANCHORED_BASE_TIME_MS);\n};\n\n// ---------------------------------------------------------------------------\n// Date shim\n// ---------------------------------------------------------------------------\n\n/**\n * What `Date.now()` reports. A standalone function rather than only a class static, so the\n * callable shim below can reuse it without referencing an unbound method — it touches no\n * `this`, so there is nothing to lose.\n */\nconst virtualDateNow = (): number => {\n const virtualNow = resolveVirtualNow();\n return virtualNow === null ? OriginalDate.now() : virtualNow;\n};\n\n// `Date` is heavily overloaded; capture args loosely and forward every explicit form\n// unchanged. Only the zero-arg form reads \"now\" and is virtualized.\nclass VirtualDateClass extends OriginalDate {\n constructor(...args: unknown[]) {\n if (args.length > 0) {\n super(...(args as ConstructorParameters<typeof OriginalDate>));\n return;\n }\n const virtualNow = resolveVirtualNow();\n if (virtualNow === null) {\n super();\n return;\n }\n super(virtualNow);\n }\n\n static override now(): number {\n return virtualDateNow();\n }\n}\n\n// Classes cannot be called without `new`, but native `Date()` can be (returning a string),\n// so wrap the class in a plain function supporting both.\nfunction VirtualDate(this: unknown, ...args: unknown[]): Date | string {\n if (!(this instanceof VirtualDate)) {\n return new VirtualDateClass().toString();\n }\n return new VirtualDateClass(...args);\n}\n\nVirtualDate.now = virtualDateNow;\nVirtualDate.parse = OriginalDate.parse;\nVirtualDate.UTC = OriginalDate.UTC;\n\n// Share the prototype so `new Date() instanceof Date` holds and every Date.prototype\n// method resolves on instances.\nObject.defineProperty(VirtualDate, \"prototype\", {\n value: VirtualDateClass.prototype,\n writable: false,\n configurable: false,\n});\n(VirtualDateClass.prototype as { constructor: unknown }).constructor =\n VirtualDate;\n\n// ---------------------------------------------------------------------------\n// Install\n// ---------------------------------------------------------------------------\n\nlet installed = false;\n\n/**\n * Replaces the global `Date` with the per-request virtual clock. Idempotent, and a no-op\n * failure: a worker that will not let its `Date` be replaced still replays, just without\n * clock virtualisation.\n *\n * Called lazily from the replay branch of the request wrapper rather than at module scope:\n * record mode and deployed production workers then pay nothing, and the package declares no\n * `sideEffects`, so a module-scope side effect could legitimately be tree-shaken away by a\n * customer's bundler.\n */\nexport const installVirtualClock = (): void => {\n if (installed) {\n return;\n }\n installed = true;\n try {\n globalThis.Date = VirtualDate as unknown as DateConstructor;\n } catch (error) {\n warnOnce(\n \"virtual-clock\",\n \"Could not install the Meticulous replay clock — recorded credentials may be treated as expired.\",\n error,\n );\n }\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAuBA,MAAM,eAAe,WAAW;;;;;;;;;;;;AAgBhC,MAAa,0BAA0B,aAAa,IAAI,MAAM,GAAG,GAAG;AAUpE,MAAM,cAAc;AAEpB,IAAI,sBAAsB;AAC1B,IAAI,eAAe;AACnB,IAAI,0BAA0B;AAE9B,MAAM,kBAAkB,WAA2B;AAIjD,KAAI,CAAC,yBAAyB;AAC5B,4BAA0B;AAC1B,MAAI;AACF,oBAAiB;AACf,mBAAe;AACf,8BAA0B;MACzB,EAAE;UACC;AAGN,6BAA0B;;;AAI9B,KAAI,WAAW,oBACb;MACK;AACL,wBAAsB;AACtB,iBAAe;;AAEjB,QAAO,gBAAgB,cACnB,SACA,UAAU,eAAe;;;;;;;;;AAc/B,MAAM,0BAAyC;CAC7C,MAAM,MAAM,sBAAsB,UAAU;AAC5C,KAAI,QAAQ,KAAA,KAAa,IAAI,SAAS,SACpC,QAAO;AAET,QAAO,eAAe,IAAI,iBAAiB,wBAAwB;;;;;;;AAYrE,MAAM,uBAA+B;CACnC,MAAM,aAAa,mBAAmB;AACtC,QAAO,eAAe,OAAO,aAAa,KAAK,GAAG;;AAKpD,IAAM,mBAAN,cAA+B,aAAa;CAC1C,YAAY,GAAG,MAAiB;AAC9B,MAAI,KAAK,SAAS,GAAG;AACnB,SAAM,GAAI,KAAoD;AAC9D;;EAEF,MAAM,aAAa,mBAAmB;AACtC,MAAI,eAAe,MAAM;AACvB,UAAO;AACP;;AAEF,QAAM,WAAW;;CAGnB,OAAgB,MAAc;AAC5B,SAAO,gBAAgB;;;AAM3B,SAAS,YAA2B,GAAG,MAAgC;AACrE,KAAI,EAAE,gBAAgB,aACpB,QAAO,IAAI,kBAAkB,CAAC,UAAU;AAE1C,QAAO,IAAI,iBAAiB,GAAG,KAAK;;AAGtC,YAAY,MAAM;AAClB,YAAY,QAAQ,aAAa;AACjC,YAAY,MAAM,aAAa;AAI/B,OAAO,eAAe,aAAa,aAAa;CAC9C,OAAO,iBAAiB;CACxB,UAAU;CACV,cAAc;CACf,CAAC;AACF,iBAAkB,UAAuC,cACvD;AAMF,IAAI,YAAY;;;;;;;;;;;AAYhB,MAAa,4BAAkC;AAC7C,KAAI,UACF;AAEF,aAAY;AACZ,KAAI;AACF,aAAW,OAAO;UACX,OAAO;AACd,WACE,iBACA,mGACA,MACD"}
@@ -1,5 +1,3 @@
1
-
2
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="bae5b317-9875-525c-b298-245193a2eca5")}catch(e){}}();
3
1
  import { requestCaptureContext } from "./context.js";
4
2
  import { warnOnce } from "./log.js";
5
3
  //#region src/virtual-random.ts
@@ -253,5 +251,4 @@ const installVirtualRandom = () => {
253
251
  //#endregion
254
252
  export { installVirtualRandom };
255
253
 
256
- //# sourceMappingURL=virtual-random.js.map
257
- //# debugId=bae5b317-9875-525c-b298-245193a2eca5
254
+ //# sourceMappingURL=virtual-random.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"virtual-random.js","sources":["../src/virtual-random.ts"],"sourcesContent":["import { requestCaptureContext, type RequestReplayContext } from \"./context\";\nimport { warnOnce } from \"./log\";\n\n/**\n * Per-request randomness virtualization for replay mode.\n *\n * Mocking every outbound `fetch` is not enough to make a replay deterministic: a Worker that\n * mints an id (`crypto.randomUUID()` for a guest id, `getRandomValues` for a token) produces\n * a different value in the base replay than in the head replay, so the rendered HTML differs\n * and any screenshot showing that value diffs forever. This replaces the worker's sources of\n * randomness with seeded generators while serving a replayed request, so two replays of the\n * same recorded session render identically.\n *\n * Deliberately a *generator*, not a recording: like the browser replayer we do not capture\n * the numbers the original session produced, we produce fresh ones from a fixed seed. The\n * app still sees plausible random values, and two replays see the same ones.\n *\n * Ported from the Node backend recorder's `replay/virtual-random.ts`, which in turn mirrors\n * the browser replayer's `mockOutRandomNumberGeneration`, including its butterfly-effect\n * containment: a separate sequence per call stack, seeded from a per-call-site counter, so a\n * change in one part of the app does not shift the numbers another part gets, and a shared\n * helper called from two places does not return the same value twice. Sequences are scoped\n * to the inbound request, not the whole replay: two concurrent requests of the same session\n * that race to the same call site would otherwise consume one shared stream in arrival\n * order, and a Fisher-Yates shuffle (or any other draw) would then differ between replays.\n * Each request starts its sequences over from the same session-scoped seed, and every draw\n * inside a replayed request comes from the seeded PRNG.\n *\n * A call site's first draws used to return one repeated constant before the PRNG started, to\n * damp the seed's positional fragility (see {@link openSequence}). That is gone: returning the\n * same number to consecutive draws is not determinism, it is a collision, and randomness is far\n * more often consumed for uniqueness than for its value. Anything minting an id from\n * consecutive draws — `nanoid`, a hand-rolled uuid, a cache-buster — got one repeated value and\n * broke. The damping was not worth what it cost.\n *\n * Two differences from the Node version, both forced by the runtime:\n *\n * - the session comes from the request context rather than a session resolver with a\n * process-wide fallback, because workerd's AsyncLocalStorage propagates reliably from the\n * inbound handler;\n * - only the globals workerd actually has are patched — there is no `node:crypto` module to\n * mutate.\n *\n * Reads outside a replayed request — module evaluation, a recording, a deployed production\n * worker — fall through to the native generators untouched.\n */\n\nconst nativeMathRandom = Math.random.bind(Math);\nconst nativeRandomUUID = globalThis.crypto?.randomUUID?.bind(globalThis.crypto);\nconst nativeGetRandomValues = globalThis.crypto?.getRandomValues?.bind(\n globalThis.crypto,\n);\n\n/**\n * Frames of the stack that belong to this module and must be skipped to reach the app's call\n * site, counted from the `new Error()` inside {@link getCallStack}.\n *\n * Getting this wrong degrades gracefully rather than breaking: a call site that resolves to\n * one of our own frames merges sequences that should have been separate, which costs\n * butterfly-effect isolation but is still perfectly deterministic.\n */\nconst FRAMES_TO_APP = 3; // getCallStack ← openSequence ← patched global ← app\n\n/** How much of the stack identifies a sequence. Ten frames tells call paths apart cheaply. */\nconst STACK_FRAMES = 10;\n\n/** Stack captures per request, after which sequences are keyed by call site alone. */\nconst MAX_STACK_CAPTURES = 10_000;\n\nconst UNKNOWN_CALL_SITE = \"unknown\";\n\ninterface RequestRandomState {\n frontendSessionId: string;\n generatorBySequenceKey: Map<string, () => number>;\n nextCallerIdByCallSite: Map<string, number>;\n stackCaptures: number;\n}\n\n/**\n * Held on the inbound request object, so two concurrent requests of one replay cannot share\n * a generator. The seed below stays session-scoped: base and head are different replays of\n * the same recording and must draw identical numbers, and two requests of that recording\n * must draw the same first values rather than racing to consume one stream.\n */\nconst stateByRequest = new WeakMap<RequestReplayContext, RequestRandomState>();\n\n// ---------------------------------------------------------------------------\n// Seeded PRNG (Alea)\n// ---------------------------------------------------------------------------\n\n/**\n * Alea, the same algorithm the browser replayer gets from `seedrandom`. Inlined because this\n * package is published to npm and ships no runtime dependencies — a customer's Worker bundle\n * should not grow for this.\n */\nexport const createSeededRandom = (seed: string): (() => number) => {\n let n = 0xefc8249d;\n const mash = (data: string): number => {\n for (let i = 0; i < data.length; i++) {\n n += data.charCodeAt(i);\n let h = 0.02519603282416938 * n;\n n = h >>> 0;\n h -= n;\n h *= n;\n n = h >>> 0;\n h -= n;\n n += h * 0x100000000;\n }\n return (n >>> 0) * 2.3283064365386963e-10;\n };\n\n let s0 = mash(\" \");\n let s1 = mash(\" \");\n let s2 = mash(\" \");\n let c = 1;\n s0 -= mash(seed);\n if (s0 < 0) {\n s0 += 1;\n }\n s1 -= mash(seed);\n if (s1 < 0) {\n s1 += 1;\n }\n s2 -= mash(seed);\n if (s2 < 0) {\n s2 += 1;\n }\n\n return () => {\n const t = 2091639 * s0 + c * 2.3283064365386963e-10;\n s0 = s1;\n s1 = s2;\n c = t | 0;\n s2 = t - c;\n return s2;\n };\n};\n\n// ---------------------------------------------------------------------------\n// Sequence resolution\n// ---------------------------------------------------------------------------\n\nconst getCallStack = (\n frameCount: number = STACK_FRAMES,\n): { stack: string; site: string } => {\n const originalLimit = Error.stackTraceLimit;\n try {\n Error.stackTraceLimit = FRAMES_TO_APP + frameCount;\n const stack = new Error().stack;\n if (stack == null) {\n return { stack: UNKNOWN_CALL_SITE, site: UNKNOWN_CALL_SITE };\n }\n // Line 0 is the \"Error\" header; frame lines follow, innermost first, and the first\n // FRAMES_TO_APP of them are ours.\n const lines = stack.split(\"\\n\").slice(FRAMES_TO_APP + 1);\n return {\n stack: lines.join(\"\\n\") || UNKNOWN_CALL_SITE,\n site: lines[0] ?? UNKNOWN_CALL_SITE,\n };\n } catch {\n return { stack: UNKNOWN_CALL_SITE, site: UNKNOWN_CALL_SITE };\n } finally {\n Error.stackTraceLimit = originalLimit;\n }\n};\n\nconst getRequestState = (): RequestRandomState | undefined => {\n const ctx = requestCaptureContext.getStore();\n if (ctx === undefined || ctx.mode !== \"replay\") {\n return undefined;\n }\n const existing = stateByRequest.get(ctx);\n if (existing !== undefined) {\n return existing;\n }\n const created: RequestRandomState = {\n frontendSessionId: ctx.frontendSessionId,\n generatorBySequenceKey: new Map(),\n nextCallerIdByCallSite: new Map(),\n stackCaptures: 0,\n };\n stateByRequest.set(ctx, created);\n return created;\n};\n\n/**\n * The generator for the sequence the current call belongs to, or undefined outside a\n * replayed request.\n *\n * Sequences are keyed by the whole call stack, so a second call down the same path continues\n * the same sequence and gets a different value. The *seed* comes from a counter kept per\n * immediate call site, which is what keeps a shared id helper honest: two call paths through\n * the same `generateId()` share a call site, so they are seeded 0 and 1 and return different\n * ids.\n *\n * Note what the seed deliberately does NOT contain: any part of the stack. Base and head are\n * different builds of the app, so the same logical call site has a different string in each —\n * a seed built from it would change on every rebuild. The consequence, which the browser\n * replayer shares, is that two *distinct* call sites each seen for the first time both seed at\n * 0 and hand out the same first value. That is the accepted trade: an alternative that\n * separated them (say, an ordinal per call site) would shift every later site's values as soon\n * as a PR added one randomness call, which is the butterfly effect this whole scheme exists to\n * avoid.\n *\n * Because callerIds are handed out in order of first arrival, every draw now depends on that\n * order — there is no longer a fixed-draw phase in front of the PRNG to absorb it, so a PR\n * that routes a new path through a shared helper can shift the values another path draws.\n * That fragility was the reason for the phase; a phase that returned one repeated constant\n * was not a workable way to pay for it.\n */\nconst openSequence = (): (() => number) | undefined => {\n const state = getRequestState();\n if (state === undefined) {\n return undefined;\n }\n\n let sequenceKey = UNKNOWN_CALL_SITE;\n let callSite = UNKNOWN_CALL_SITE;\n if (state.stackCaptures < MAX_STACK_CAPTURES) {\n state.stackCaptures++;\n const captured = getCallStack();\n sequenceKey = captured.stack;\n callSite = captured.site;\n }\n\n const existing = state.generatorBySequenceKey.get(sequenceKey);\n if (existing !== undefined) {\n return existing;\n }\n const callerId = state.nextCallerIdByCallSite.get(callSite) ?? 0;\n // Seeded from the counter only — never from the call-site or stack string itself. Those embed\n // bundle filenames and line numbers, which differ between the base build and the head build\n // of the very same code, so putting one in the seed would reseed every sequence on any\n // rebuild and bring back exactly the diffs this exists to remove. The strings are grouping\n // keys, nothing more.\n const generator = createSeededRandom(\n `meticulous-random-${state.frontendSessionId}-${callerId}`,\n );\n state.generatorBySequenceKey.set(sequenceKey, generator);\n state.nextCallerIdByCallSite.set(callSite, callerId + 1);\n return generator;\n};\n\n// ---------------------------------------------------------------------------\n// Derived primitives\n// ---------------------------------------------------------------------------\n\nconst HEX = \"0123456789abcdef\";\n/** The two bits of a v4 UUID's variant field are fixed, leaving these four values. */\nconst VARIANT = \"89ab\";\n\nconst buildUuid = (draw: () => number): string => {\n let uuid = \"\";\n for (let i = 0; i < 36; i++) {\n if (i === 8 || i === 13 || i === 18 || i === 23) {\n uuid += \"-\";\n } else if (i === 14) {\n uuid += \"4\";\n } else if (i === 19) {\n uuid += VARIANT[Math.floor(draw() * VARIANT.length)];\n } else {\n uuid += HEX[Math.floor(draw() * HEX.length)];\n }\n }\n return uuid;\n};\n\n/**\n * Fills the view's bytes rather than its elements, so every typed array — including the\n * BigInt ones, whose elements cannot be assigned a number — is handled by one path.\n */\nconst fillBytes = (view: ArrayBufferView, draw: () => number): void => {\n const bytes = new Uint8Array(view.buffer, view.byteOffset, view.byteLength);\n for (let i = 0; i < bytes.length; i++) {\n bytes[i] = Math.floor(draw() * 256);\n }\n};\n\n// ---------------------------------------------------------------------------\n// Install\n// ---------------------------------------------------------------------------\n\nlet installed = false;\n\n/**\n * Replaces the worker's sources of randomness with per-request seeded generators. Idempotent,\n * and a no-op failure: a worker that will not let them be replaced still replays, just\n * without randomness virtualisation.\n *\n * Called lazily from the replay branch of the request wrapper rather than at module scope,\n * for the same reasons as the virtual clock: record mode and deployed production workers then\n * pay nothing, and the package declares no `sideEffects`, so a module-scope side effect could\n * legitimately be tree-shaken away by a customer's bundler.\n */\nexport const installVirtualRandom = (): void => {\n if (installed) {\n return;\n }\n installed = true;\n try {\n Math.random = () => openSequence()?.() ?? nativeMathRandom();\n\n if (globalThis.crypto != null) {\n Object.defineProperty(globalThis.crypto, \"randomUUID\", {\n value: (): string => {\n const draw = openSequence();\n if (draw !== undefined) {\n return buildUuid(draw);\n }\n return nativeRandomUUID?.() ?? buildUuid(nativeMathRandom);\n },\n configurable: true,\n writable: true,\n });\n\n Object.defineProperty(globalThis.crypto, \"getRandomValues\", {\n value: <T extends ArrayBufferView>(array: T): T => {\n const draw = openSequence();\n if (draw === undefined) {\n return nativeGetRandomValues?.(array) ?? array;\n }\n fillBytes(array, draw);\n return array;\n },\n configurable: true,\n writable: true,\n });\n }\n } catch (error) {\n warnOnce(\n \"virtual-random\",\n \"Could not install the Meticulous replay random-number generators — ids minted by the app will differ between replays.\",\n error,\n );\n }\n};\n"],"names":[],"mappings":"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","debugId":"bae5b317-9875-525c-b298-245193a2eca5"}
1
+ {"version":3,"file":"virtual-random.js","names":[],"sources":["../src/virtual-random.ts"],"sourcesContent":["import { requestCaptureContext, type RequestReplayContext } from \"./context\";\nimport { warnOnce } from \"./log\";\n\n/**\n * Per-request randomness virtualization for replay mode.\n *\n * Mocking every outbound `fetch` is not enough to make a replay deterministic: a Worker that\n * mints an id (`crypto.randomUUID()` for a guest id, `getRandomValues` for a token) produces\n * a different value in the base replay than in the head replay, so the rendered HTML differs\n * and any screenshot showing that value diffs forever. This replaces the worker's sources of\n * randomness with seeded generators while serving a replayed request, so two replays of the\n * same recorded session render identically.\n *\n * Deliberately a *generator*, not a recording: like the browser replayer we do not capture\n * the numbers the original session produced, we produce fresh ones from a fixed seed. The\n * app still sees plausible random values, and two replays see the same ones.\n *\n * Ported from the Node backend recorder's `replay/virtual-random.ts`, which in turn mirrors\n * the browser replayer's `mockOutRandomNumberGeneration`, including its butterfly-effect\n * containment: a separate sequence per call stack, seeded from a per-call-site counter, so a\n * change in one part of the app does not shift the numbers another part gets, and a shared\n * helper called from two places does not return the same value twice. Sequences are scoped\n * to the inbound request, not the whole replay: two concurrent requests of the same session\n * that race to the same call site would otherwise consume one shared stream in arrival\n * order, and a Fisher-Yates shuffle (or any other draw) would then differ between replays.\n * Each request starts its sequences over from the same session-scoped seed, and every draw\n * inside a replayed request comes from the seeded PRNG.\n *\n * A call site's first draws used to return one repeated constant before the PRNG started, to\n * damp the seed's positional fragility (see {@link openSequence}). That is gone: returning the\n * same number to consecutive draws is not determinism, it is a collision, and randomness is far\n * more often consumed for uniqueness than for its value. Anything minting an id from\n * consecutive draws — `nanoid`, a hand-rolled uuid, a cache-buster — got one repeated value and\n * broke. The damping was not worth what it cost.\n *\n * Two differences from the Node version, both forced by the runtime:\n *\n * - the session comes from the request context rather than a session resolver with a\n * process-wide fallback, because workerd's AsyncLocalStorage propagates reliably from the\n * inbound handler;\n * - only the globals workerd actually has are patched — there is no `node:crypto` module to\n * mutate.\n *\n * Reads outside a replayed request — module evaluation, a recording, a deployed production\n * worker — fall through to the native generators untouched.\n */\n\nconst nativeMathRandom = Math.random.bind(Math);\nconst nativeRandomUUID = globalThis.crypto?.randomUUID?.bind(globalThis.crypto);\nconst nativeGetRandomValues = globalThis.crypto?.getRandomValues?.bind(\n globalThis.crypto,\n);\n\n/**\n * Frames of the stack that belong to this module and must be skipped to reach the app's call\n * site, counted from the `new Error()` inside {@link getCallStack}.\n *\n * Getting this wrong degrades gracefully rather than breaking: a call site that resolves to\n * one of our own frames merges sequences that should have been separate, which costs\n * butterfly-effect isolation but is still perfectly deterministic.\n */\nconst FRAMES_TO_APP = 3; // getCallStack ← openSequence ← patched global ← app\n\n/** How much of the stack identifies a sequence. Ten frames tells call paths apart cheaply. */\nconst STACK_FRAMES = 10;\n\n/** Stack captures per request, after which sequences are keyed by call site alone. */\nconst MAX_STACK_CAPTURES = 10_000;\n\nconst UNKNOWN_CALL_SITE = \"unknown\";\n\ninterface RequestRandomState {\n frontendSessionId: string;\n generatorBySequenceKey: Map<string, () => number>;\n nextCallerIdByCallSite: Map<string, number>;\n stackCaptures: number;\n}\n\n/**\n * Held on the inbound request object, so two concurrent requests of one replay cannot share\n * a generator. The seed below stays session-scoped: base and head are different replays of\n * the same recording and must draw identical numbers, and two requests of that recording\n * must draw the same first values rather than racing to consume one stream.\n */\nconst stateByRequest = new WeakMap<RequestReplayContext, RequestRandomState>();\n\n// ---------------------------------------------------------------------------\n// Seeded PRNG (Alea)\n// ---------------------------------------------------------------------------\n\n/**\n * Alea, the same algorithm the browser replayer gets from `seedrandom`. Inlined because this\n * package is published to npm and ships no runtime dependencies — a customer's Worker bundle\n * should not grow for this.\n */\nexport const createSeededRandom = (seed: string): (() => number) => {\n let n = 0xefc8249d;\n const mash = (data: string): number => {\n for (let i = 0; i < data.length; i++) {\n n += data.charCodeAt(i);\n let h = 0.02519603282416938 * n;\n n = h >>> 0;\n h -= n;\n h *= n;\n n = h >>> 0;\n h -= n;\n n += h * 0x100000000;\n }\n return (n >>> 0) * 2.3283064365386963e-10;\n };\n\n let s0 = mash(\" \");\n let s1 = mash(\" \");\n let s2 = mash(\" \");\n let c = 1;\n s0 -= mash(seed);\n if (s0 < 0) {\n s0 += 1;\n }\n s1 -= mash(seed);\n if (s1 < 0) {\n s1 += 1;\n }\n s2 -= mash(seed);\n if (s2 < 0) {\n s2 += 1;\n }\n\n return () => {\n const t = 2091639 * s0 + c * 2.3283064365386963e-10;\n s0 = s1;\n s1 = s2;\n c = t | 0;\n s2 = t - c;\n return s2;\n };\n};\n\n// ---------------------------------------------------------------------------\n// Sequence resolution\n// ---------------------------------------------------------------------------\n\nconst getCallStack = (\n frameCount: number = STACK_FRAMES,\n): { stack: string; site: string } => {\n const originalLimit = Error.stackTraceLimit;\n try {\n Error.stackTraceLimit = FRAMES_TO_APP + frameCount;\n const stack = new Error().stack;\n if (stack == null) {\n return { stack: UNKNOWN_CALL_SITE, site: UNKNOWN_CALL_SITE };\n }\n // Line 0 is the \"Error\" header; frame lines follow, innermost first, and the first\n // FRAMES_TO_APP of them are ours.\n const lines = stack.split(\"\\n\").slice(FRAMES_TO_APP + 1);\n return {\n stack: lines.join(\"\\n\") || UNKNOWN_CALL_SITE,\n site: lines[0] ?? UNKNOWN_CALL_SITE,\n };\n } catch {\n return { stack: UNKNOWN_CALL_SITE, site: UNKNOWN_CALL_SITE };\n } finally {\n Error.stackTraceLimit = originalLimit;\n }\n};\n\nconst getRequestState = (): RequestRandomState | undefined => {\n const ctx = requestCaptureContext.getStore();\n if (ctx === undefined || ctx.mode !== \"replay\") {\n return undefined;\n }\n const existing = stateByRequest.get(ctx);\n if (existing !== undefined) {\n return existing;\n }\n const created: RequestRandomState = {\n frontendSessionId: ctx.frontendSessionId,\n generatorBySequenceKey: new Map(),\n nextCallerIdByCallSite: new Map(),\n stackCaptures: 0,\n };\n stateByRequest.set(ctx, created);\n return created;\n};\n\n/**\n * The generator for the sequence the current call belongs to, or undefined outside a\n * replayed request.\n *\n * Sequences are keyed by the whole call stack, so a second call down the same path continues\n * the same sequence and gets a different value. The *seed* comes from a counter kept per\n * immediate call site, which is what keeps a shared id helper honest: two call paths through\n * the same `generateId()` share a call site, so they are seeded 0 and 1 and return different\n * ids.\n *\n * Note what the seed deliberately does NOT contain: any part of the stack. Base and head are\n * different builds of the app, so the same logical call site has a different string in each —\n * a seed built from it would change on every rebuild. The consequence, which the browser\n * replayer shares, is that two *distinct* call sites each seen for the first time both seed at\n * 0 and hand out the same first value. That is the accepted trade: an alternative that\n * separated them (say, an ordinal per call site) would shift every later site's values as soon\n * as a PR added one randomness call, which is the butterfly effect this whole scheme exists to\n * avoid.\n *\n * Because callerIds are handed out in order of first arrival, every draw now depends on that\n * order — there is no longer a fixed-draw phase in front of the PRNG to absorb it, so a PR\n * that routes a new path through a shared helper can shift the values another path draws.\n * That fragility was the reason for the phase; a phase that returned one repeated constant\n * was not a workable way to pay for it.\n */\nconst openSequence = (): (() => number) | undefined => {\n const state = getRequestState();\n if (state === undefined) {\n return undefined;\n }\n\n let sequenceKey = UNKNOWN_CALL_SITE;\n let callSite = UNKNOWN_CALL_SITE;\n if (state.stackCaptures < MAX_STACK_CAPTURES) {\n state.stackCaptures++;\n const captured = getCallStack();\n sequenceKey = captured.stack;\n callSite = captured.site;\n }\n\n const existing = state.generatorBySequenceKey.get(sequenceKey);\n if (existing !== undefined) {\n return existing;\n }\n const callerId = state.nextCallerIdByCallSite.get(callSite) ?? 0;\n // Seeded from the counter only — never from the call-site or stack string itself. Those embed\n // bundle filenames and line numbers, which differ between the base build and the head build\n // of the very same code, so putting one in the seed would reseed every sequence on any\n // rebuild and bring back exactly the diffs this exists to remove. The strings are grouping\n // keys, nothing more.\n const generator = createSeededRandom(\n `meticulous-random-${state.frontendSessionId}-${callerId}`,\n );\n state.generatorBySequenceKey.set(sequenceKey, generator);\n state.nextCallerIdByCallSite.set(callSite, callerId + 1);\n return generator;\n};\n\n// ---------------------------------------------------------------------------\n// Derived primitives\n// ---------------------------------------------------------------------------\n\nconst HEX = \"0123456789abcdef\";\n/** The two bits of a v4 UUID's variant field are fixed, leaving these four values. */\nconst VARIANT = \"89ab\";\n\nconst buildUuid = (draw: () => number): string => {\n let uuid = \"\";\n for (let i = 0; i < 36; i++) {\n if (i === 8 || i === 13 || i === 18 || i === 23) {\n uuid += \"-\";\n } else if (i === 14) {\n uuid += \"4\";\n } else if (i === 19) {\n uuid += VARIANT[Math.floor(draw() * VARIANT.length)];\n } else {\n uuid += HEX[Math.floor(draw() * HEX.length)];\n }\n }\n return uuid;\n};\n\n/**\n * Fills the view's bytes rather than its elements, so every typed array — including the\n * BigInt ones, whose elements cannot be assigned a number — is handled by one path.\n */\nconst fillBytes = (view: ArrayBufferView, draw: () => number): void => {\n const bytes = new Uint8Array(view.buffer, view.byteOffset, view.byteLength);\n for (let i = 0; i < bytes.length; i++) {\n bytes[i] = Math.floor(draw() * 256);\n }\n};\n\n// ---------------------------------------------------------------------------\n// Install\n// ---------------------------------------------------------------------------\n\nlet installed = false;\n\n/**\n * Replaces the worker's sources of randomness with per-request seeded generators. Idempotent,\n * and a no-op failure: a worker that will not let them be replaced still replays, just\n * without randomness virtualisation.\n *\n * Called lazily from the replay branch of the request wrapper rather than at module scope,\n * for the same reasons as the virtual clock: record mode and deployed production workers then\n * pay nothing, and the package declares no `sideEffects`, so a module-scope side effect could\n * legitimately be tree-shaken away by a customer's bundler.\n */\nexport const installVirtualRandom = (): void => {\n if (installed) {\n return;\n }\n installed = true;\n try {\n Math.random = () => openSequence()?.() ?? nativeMathRandom();\n\n if (globalThis.crypto != null) {\n Object.defineProperty(globalThis.crypto, \"randomUUID\", {\n value: (): string => {\n const draw = openSequence();\n if (draw !== undefined) {\n return buildUuid(draw);\n }\n return nativeRandomUUID?.() ?? buildUuid(nativeMathRandom);\n },\n configurable: true,\n writable: true,\n });\n\n Object.defineProperty(globalThis.crypto, \"getRandomValues\", {\n value: <T extends ArrayBufferView>(array: T): T => {\n const draw = openSequence();\n if (draw === undefined) {\n return nativeGetRandomValues?.(array) ?? array;\n }\n fillBytes(array, draw);\n return array;\n },\n configurable: true,\n writable: true,\n });\n }\n } catch (error) {\n warnOnce(\n \"virtual-random\",\n \"Could not install the Meticulous replay random-number generators — ids minted by the app will differ between replays.\",\n error,\n );\n }\n};\n"],"mappings":"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"}
@@ -1,7 +1,5 @@
1
-
2
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="e8d45043-758e-5a3e-b440-03ed8b9c29cb")}catch(e){}}();
3
1
  import * as path from "node:path";
4
- //#region ../../node_modules/.pnpm/acorn@8.17.0/node_modules/acorn/dist/acorn.mjs
2
+ //#region ../../node_modules/.pnpm/acorn@8.18.0/node_modules/acorn/dist/acorn.mjs
5
3
  var astralIdentifierCodes = [
6
4
  509,
7
5
  0,
@@ -1230,6 +1228,7 @@ var defaultOptions = {
1230
1228
  allowHashBang: false,
1231
1229
  checkPrivateFields: true,
1232
1230
  locations: false,
1231
+ startLocation: null,
1233
1232
  onToken: null,
1234
1233
  onComment: null,
1235
1234
  ranges: false,
@@ -1295,14 +1294,15 @@ var Parser = function Parser(options, input, startPos) {
1295
1294
  this.reservedWordsStrictBind = wordsRegexp(reservedStrict + " " + reservedWords.strictBind);
1296
1295
  this.input = String(input);
1297
1296
  this.containsEsc = false;
1298
- if (startPos) {
1299
- this.pos = startPos;
1297
+ this.pos = startPos || 0;
1298
+ this.curLine = 1;
1299
+ if (options.startLocation) {
1300
+ this.lineStart = this.pos - options.startLocation.column;
1301
+ this.curLine = options.startLocation.line;
1302
+ } else if (startPos) {
1300
1303
  this.lineStart = this.input.lastIndexOf("\n", startPos - 1) + 1;
1301
- this.curLine = this.input.slice(0, this.lineStart).split(lineBreak).length;
1302
- } else {
1303
- this.pos = this.lineStart = 0;
1304
- this.curLine = 1;
1305
- }
1304
+ if (this.options.locations) this.curLine = this.input.slice(0, this.lineStart).split(lineBreak).length;
1305
+ } else this.lineStart = 0;
1306
1306
  this.type = types$1.eof;
1307
1307
  this.value = null;
1308
1308
  this.start = this.end = this.pos;
@@ -2824,7 +2824,7 @@ pp$5.parseMaybeUnary = function(refDestructuringErrors, sawUnary, incDec, forIni
2824
2824
  expr = this.finishNode(node$1, "UpdateExpression");
2825
2825
  }
2826
2826
  }
2827
- if (!incDec && this.eat(types$1.starstar)) if (sawUnary) this.unexpected(this.lastTokStart);
2827
+ if (!incDec && !(expr.type === "ArrowFunctionExpression" && expr.start === startPos) && this.eat(types$1.starstar)) if (sawUnary) this.unexpected(this.lastTokStart);
2828
2828
  else return this.buildBinary(startPos, startLoc, expr, this.parseMaybeUnary(null, false, false, forInit), "**", false);
2829
2829
  else return expr;
2830
2830
  };
@@ -5084,7 +5084,7 @@ pp.readWord = function() {
5084
5084
  };
5085
5085
  Parser.acorn = {
5086
5086
  Parser,
5087
- version: "8.17.0",
5087
+ version: "8.18.0",
5088
5088
  defaultOptions,
5089
5089
  Position,
5090
5090
  SourceLocation,
@@ -6216,12 +6216,12 @@ const instrumentModule = ({ code, fileName, firstId, runtimeModuleId, resolveOri
6216
6216
  magic.appendLeft(node.start, `${SINK}&&(${SINK}[${id}]=1);`);
6217
6217
  };
6218
6218
  const openFunctionBody = (body) => {
6219
- magic.appendLeft(body.start + 1, `const ${SINK}=${ENTER}();`);
6219
+ magic.appendLeft(body.start + 1, `const ${SINK}=typeof ${ENTER}==="function"?${ENTER}():undefined;`);
6220
6220
  };
6221
6221
  const markConciseArrowBody = (body) => {
6222
6222
  const id = nextId(body);
6223
6223
  if (id === null) return;
6224
- magic.appendLeft(body.start, `(${HIT}(${id}),`);
6224
+ magic.appendLeft(body.start, `(typeof ${HIT}==="function"&&${HIT}(${id}),`);
6225
6225
  magic.appendRight(body.end, ")");
6226
6226
  };
6227
6227
  visitProgram(program, {
@@ -6766,5 +6766,4 @@ const toRelativePath = (filePath, root) => path.relative(root, filePath).split(p
6766
6766
  //#endregion
6767
6767
  export { meticulousCoverage };
6768
6768
 
6769
- //# sourceMappingURL=index.js.map
6770
- //# debugId=e8d45043-758e-5a3e-b440-03ed8b9c29cb
6769
+ //# sourceMappingURL=index.js.map