@poe-platform/safe-js 0.1.730 → 0.1.731

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 (100) hide show
  1. package/README.md +5 -5
  2. package/dist/safe-js/chunks/{chunk-SJZWSPWD.js → chunk-2GVBG23M.js} +1 -1
  3. package/dist/safe-js/chunks/{chunk-SJZWSPWD.js.map → chunk-2GVBG23M.js.map} +1 -1
  4. package/dist/safe-js/chunks/{chunk-VIMROAQA.js → chunk-E4XLBT7R.js} +2 -2
  5. package/dist/safe-js/chunks/chunk-F6CV74HU.js +114 -0
  6. package/dist/safe-js/chunks/chunk-F6CV74HU.js.map +7 -0
  7. package/dist/safe-js/chunks/{chunk-OZLTLIHX.js → chunk-G6WTEIJJ.js} +2 -2
  8. package/dist/safe-js/chunks/{chunk-XX57YCGZ.js → chunk-GNEORAQI.js} +55 -50
  9. package/dist/safe-js/chunks/chunk-GNEORAQI.js.map +7 -0
  10. package/dist/safe-js/chunks/chunk-GVKXZW53.js +76 -0
  11. package/dist/safe-js/chunks/chunk-GVKXZW53.js.map +7 -0
  12. package/dist/safe-js/chunks/{chunk-3LSNBW5N.js → chunk-IRDQDZ5R.js} +4 -4
  13. package/dist/safe-js/chunks/{chunk-MRULOD5J.js → chunk-K4T4EBED.js} +59 -38
  14. package/dist/safe-js/chunks/chunk-K4T4EBED.js.map +7 -0
  15. package/dist/safe-js/chunks/{chunk-MFCEBCGV.js → chunk-KETIFEA7.js} +17 -8
  16. package/dist/safe-js/chunks/chunk-KETIFEA7.js.map +7 -0
  17. package/dist/safe-js/chunks/{chunk-S5YDMMM3.js → chunk-MDIWQFYU.js} +95 -169
  18. package/dist/safe-js/chunks/{chunk-S5YDMMM3.js.map → chunk-MDIWQFYU.js.map} +4 -4
  19. package/dist/safe-js/chunks/{chunk-MW2QLYVN.js → chunk-MGHE7ZFI.js} +74 -63
  20. package/dist/safe-js/chunks/chunk-MGHE7ZFI.js.map +7 -0
  21. package/dist/safe-js/chunks/{chunk-EN724VH3.js → chunk-O4IDRXW7.js} +4 -4
  22. package/dist/safe-js/chunks/{chunk-EA7AHM2X.js → chunk-PSVV3IG2.js} +3 -3
  23. package/dist/safe-js/chunks/{chunk-Y5EZYHU6.js → chunk-QKO4FATZ.js} +33 -5
  24. package/dist/safe-js/chunks/chunk-QKO4FATZ.js.map +7 -0
  25. package/dist/safe-js/chunks/{chunk-EG3QWREA.js → chunk-RULU2F5J.js} +2 -2
  26. package/dist/safe-js/chunks/chunk-UQPYTZOZ.js +72 -0
  27. package/dist/safe-js/chunks/chunk-UQPYTZOZ.js.map +7 -0
  28. package/dist/safe-js/chunks/{chunk-LRSWTDIV.js → chunk-W4SWUHCQ.js} +58 -23
  29. package/dist/safe-js/chunks/chunk-W4SWUHCQ.js.map +7 -0
  30. package/dist/safe-js/chunks/{chunk-W3IY6FKO.js → chunk-WS2FJBH6.js} +2 -2
  31. package/dist/safe-js/chunks/{chunk-MVLDRXWS.js → chunk-X3CKAZQ4.js} +3 -3
  32. package/dist/safe-js/chunks/chunk-YUQFFRUF.js +26 -0
  33. package/dist/safe-js/chunks/chunk-YUQFFRUF.js.map +7 -0
  34. package/dist/safe-js/chunks/{cli-runtime-WSVW5DNT.js → cli-runtime-NUEFUM64.js} +51 -30
  35. package/dist/safe-js/chunks/cli-runtime-NUEFUM64.js.map +7 -0
  36. package/dist/safe-js/chunks/{dump-UNYK24YS.js → dump-RGRC3CG3.js} +5 -4
  37. package/dist/safe-js/chunks/harness-UMOMFXDT.js +11 -0
  38. package/dist/safe-js/chunks/mcp-S672263Q.js +12 -0
  39. package/dist/safe-js/chunks/migration-file-KRXQYI3Q.js +15 -0
  40. package/dist/safe-js/chunks/restore-GEO7VN2R.js +14 -0
  41. package/dist/safe-js/chunks/run-ZSFX66RN.js +20 -0
  42. package/dist/safe-js/chunks/runtime-modules-A3U2DKFL.js +11 -0
  43. package/dist/safe-js/chunks/source-files-L3SGK63Z.js +10 -0
  44. package/dist/safe-js/cli-runtime.d.ts +2 -0
  45. package/dist/safe-js/cli.js +4 -4
  46. package/dist/safe-js/core.d.ts +1 -0
  47. package/dist/safe-js/core.js +15 -8
  48. package/dist/safe-js/error/shape.js +170 -0
  49. package/dist/safe-js/index.js +42 -33
  50. package/dist/safe-js/index.js.map +2 -2
  51. package/dist/safe-js/interp/budget.js +600 -0
  52. package/dist/safe-js/interp/internal-symbols.js +1 -0
  53. package/dist/safe-js/interp/promise-replay.d.ts +1 -1
  54. package/dist/safe-js/interp/template-cache.js +7 -0
  55. package/dist/safe-js/migration-file.d.ts +2 -0
  56. package/dist/safe-js/modules/fs-config.d.ts +3 -1
  57. package/dist/safe-js/modules/fs.d.ts +8 -4
  58. package/dist/safe-js/modules/fs.js +16 -0
  59. package/dist/safe-js/modules/source-files.d.ts +3 -2
  60. package/dist/safe-js/platform/context.js +58 -0
  61. package/dist/safe-js/platform/node.js +64 -0
  62. package/dist/safe-js/platform/types.js +63 -0
  63. package/dist/safe-js/platform/workerd.js +39 -0
  64. package/dist/safe-js/runner/run-harness.d.ts +2 -0
  65. package/dist/safe-js/snapshot/backend.d.ts +2 -0
  66. package/package.json +9 -2
  67. package/dist/safe-js/chunks/chunk-FCCA27RC.js +0 -69
  68. package/dist/safe-js/chunks/chunk-FCCA27RC.js.map +0 -7
  69. package/dist/safe-js/chunks/chunk-LRSWTDIV.js.map +0 -7
  70. package/dist/safe-js/chunks/chunk-MFCEBCGV.js.map +0 -7
  71. package/dist/safe-js/chunks/chunk-MRULOD5J.js.map +0 -7
  72. package/dist/safe-js/chunks/chunk-MW2QLYVN.js.map +0 -7
  73. package/dist/safe-js/chunks/chunk-XX57YCGZ.js.map +0 -7
  74. package/dist/safe-js/chunks/chunk-Y5EZYHU6.js.map +0 -7
  75. package/dist/safe-js/chunks/cli-runtime-WSVW5DNT.js.map +0 -7
  76. package/dist/safe-js/chunks/fs-AOV77RQC.js +0 -13
  77. package/dist/safe-js/chunks/harness-ZIEO7NAV.js +0 -10
  78. package/dist/safe-js/chunks/mcp-E2AJXEQM.js +0 -11
  79. package/dist/safe-js/chunks/migration-file-3SWGNDS2.js +0 -12
  80. package/dist/safe-js/chunks/restore-FCTWPLU3.js +0 -13
  81. package/dist/safe-js/chunks/run-ESJHEXCE.js +0 -17
  82. package/dist/safe-js/chunks/runtime-modules-7V6BWCRK.js +0 -10
  83. package/dist/safe-js/chunks/source-files-73OHLNGH.js +0 -8
  84. /package/dist/safe-js/chunks/{chunk-VIMROAQA.js.map → chunk-E4XLBT7R.js.map} +0 -0
  85. /package/dist/safe-js/chunks/{chunk-OZLTLIHX.js.map → chunk-G6WTEIJJ.js.map} +0 -0
  86. /package/dist/safe-js/chunks/{chunk-3LSNBW5N.js.map → chunk-IRDQDZ5R.js.map} +0 -0
  87. /package/dist/safe-js/chunks/{chunk-EN724VH3.js.map → chunk-O4IDRXW7.js.map} +0 -0
  88. /package/dist/safe-js/chunks/{chunk-EA7AHM2X.js.map → chunk-PSVV3IG2.js.map} +0 -0
  89. /package/dist/safe-js/chunks/{chunk-EG3QWREA.js.map → chunk-RULU2F5J.js.map} +0 -0
  90. /package/dist/safe-js/chunks/{chunk-W3IY6FKO.js.map → chunk-WS2FJBH6.js.map} +0 -0
  91. /package/dist/safe-js/chunks/{chunk-MVLDRXWS.js.map → chunk-X3CKAZQ4.js.map} +0 -0
  92. /package/dist/safe-js/chunks/{dump-UNYK24YS.js.map → dump-RGRC3CG3.js.map} +0 -0
  93. /package/dist/safe-js/chunks/{fs-AOV77RQC.js.map → harness-UMOMFXDT.js.map} +0 -0
  94. /package/dist/safe-js/chunks/{harness-ZIEO7NAV.js.map → mcp-S672263Q.js.map} +0 -0
  95. /package/dist/safe-js/chunks/{mcp-E2AJXEQM.js.map → migration-file-KRXQYI3Q.js.map} +0 -0
  96. /package/dist/safe-js/chunks/{migration-file-3SWGNDS2.js.map → restore-GEO7VN2R.js.map} +0 -0
  97. /package/dist/safe-js/chunks/{restore-FCTWPLU3.js.map → run-ZSFX66RN.js.map} +0 -0
  98. /package/dist/safe-js/chunks/{run-ESJHEXCE.js.map → runtime-modules-A3U2DKFL.js.map} +0 -0
  99. /package/dist/safe-js/chunks/{runtime-modules-7V6BWCRK.js.map → source-files-L3SGK63Z.js.map} +0 -0
  100. /package/dist/safe-js/{chunks/source-files-73OHLNGH.js.map → modules/fs.js.map} +0 -0
package/README.md CHANGED
@@ -36,7 +36,7 @@ console.log(result.returnValue);
36
36
 
37
37
  `run()` takes source text, not a file path. Success returns `ok`, `returnValue`, `snapshot`, and `stats`. Handle both an `ok: false` result and a rejected promise: parsing, budget exhaustion, cancellation, and some execution failures can reject. Top-level `await` in this example lets rejections reach Node.
38
38
 
39
- `@poe-platform/safe-js/core` exposes `run`, `createRealm`, `defineExtension`, `lint`, `Budget`, and replayable-random helpers. The shared filesystem lives in `@poe-platform/safe-fs`, with a portable `/core` entry. Existing `@poe-platform/safe-js/fs`, `/fs/core`, and `/fs/node` imports re-export it. Legacy `poe-code/safe-js` imports remain available through the CLI package but use a separate runtime; keep factories and errors within one import family.
39
+ `@poe-platform/safe-js/core` exposes `run`, `createRealm`, `defineExtension`, `lint`, `Budget`, and replayable-random helpers. The `/core`, `/workerd`, and `/modules/fs` workspace entries bundle with the `workerd` or `browser` condition without Node built-ins. Supply a `@poe-code/safe-fs` adapter for filesystem capabilities; `createRootedSourceResolver(root, adapter)` grants portable source imports. File snapshots, migrations, harnesses, and file-running helpers also accept an `adapter`. The shared filesystem lives in `@poe-platform/safe-fs`, with a portable `/core` entry. Existing `@poe-platform/safe-js/fs`, `/fs/core`, and `/fs/node` imports re-export it. Legacy `poe-code/safe-js` imports remain available through the CLI package but use a separate runtime; keep factories and errors within one import family.
40
40
 
41
41
  ## Supported features
42
42
 
@@ -237,7 +237,7 @@ Execution gives host timers and cancellation turns between guest nodes after ela
237
237
  | `extensions` | Explicit `defineExtension(...)` registrations; `[]`. Setup runs once, on first evaluation, not on construction or unused close. |
238
238
  | `grants` | Granted capability names; `[]`. Every requested capability must be granted before any extension setup runs. |
239
239
  | `builtinOverrides` | Optional `{ console: "extension-name" }` authorizes that registered extension to replace only the builtin console. It must declare `console` and export a host object created in the realm. No overrides by default. |
240
- | `limits` | Positive integer caps: `extensions: 32`, `hostObjects: 1024`, `callbacks: 1024`, `guestReferences: 1024`, `cleanups: 1024`, `nestedEvaluations: 16`. Collection budgets also apply. |
240
+ | `limits` | Optional positive integer caps for `extensions`, `hostObjects`, `callbacks`, `guestReferences`, `cleanups`, and `nestedEvaluations`. Every cap defaults to `Infinity` and accepts explicit `Infinity`. Collection budgets also apply. |
241
241
  | `stringCompilation` | Optional `"allow"` (default) or `"deny"`. Denial raises a catchable guest EvalError for string eval and Function/AsyncFunction/GeneratorFunction/AsyncGeneratorFunction compilation, including retained callbacks. Host source evaluation remains available. This is separate from WebAssembly compilation policy. |
242
242
  | `classicScripts` | Optional boolean, default `false`. Evaluations use classic Script grammar with persistent globals: top-level `this` is the intrinsic global object, `var` and functions create global properties, and `let`/`const` remain lexical. Injected capabilities stay immutable lexical bindings. Explicit source modules retain module semantics. |
243
243
  | `classicScriptErrors` | Optional `"fatal"` (default) or `"report"`; reporting requires `classicScripts: true`. Ordinary escaped Script exceptions return `ok: false` with `recoverable: true` while preserving realm state. Syntax, budget, cancellation, module, callback and unhandled rejection failures remain fatal. |
@@ -445,7 +445,7 @@ For one-shot use, `run(source, { extensions, grants, ... })` accepts the same re
445
445
  | `snapshotIntervalMs` | Periodic checkpoint interval when persistence is configured: 30,000 ms; `0` disables periodic writes. Capture happens at interpreter yield points. |
446
446
  | `hostCallResumeProvider` | Reconciles pending external operations on restore; returns a matching `HostCallResumeProof`. |
447
447
 
448
- `new Budget(options?)` accepts optional limits. A custom budget replaces the default, so include `maxCallDepth` if you want that guard.
448
+ `new Budget(options?)` accepts optional limits, each unlimited by default. Explicit `Infinity` also disables a limit. Set `maxCallDepth` to opt into a recursion guard in `run()` or `createRealm()`.
449
449
 
450
450
  | Option | Limit |
451
451
  | --- | --- |
@@ -536,7 +536,7 @@ Factories return exports to register in `modules`; calling a factory alone does
536
536
  | --- | --- |
537
537
  | `makeAgentModule(spawnAgent, options?)` | Inject the agent runner. Options: `defaultRetry`, `onEvent`, `otelSink`. Exposes `spawn`, `spawn.retry`, and `spawn.parallel`; call options follow this table. |
538
538
  | `makeMcpModule(options)` | Required `servers` map: stdio `{ command, args?, cwd?, env? }` or HTTP `{ url, headers? }`. Options: `requestTimeoutMs` (30,000), `closeTimeoutMs` (1,000), `maxToolPages` (100), `signal`, and injected `fetch`/`spawn`. Named clients expose `tools`, `tool`, `toolBatch`, and `close`; close managed clients when finished. A custom connector function is also accepted. |
539
- | `makeFsModule(options?)` | Node-backed `{ root?, fs? }`, or shared-filesystem `{ adapter, root?, cwd?, signal? }`; do not combine `fs` and `adapter`. Node access without `root` is unconfined. Rooted calls sharing the same adapter or Node implementation are serialized through path checks and backend completion to prevent guest namespace races. External host mutations or distinct adapters over the same storage require independent backend confinement. With an adapter, `root` confines access and `cwd` selects the virtual relative-path base; without `root`, explicit `cwd` also confines access. Omitting both uses virtual `/`. Adapter text reads pass a byte cap derived conservatively from the guest string and remaining data limits to the backend before decoding. Each read is also capped at 419,430 input bytes. A shared 16 MiB host read allowance reserves ten bytes per input byte for collection, copies, encoding expansion and admission; concurrent reads that exceed it reject, and reservations release after the underlying read settles and decoding finishes, including when cancellation rejects the caller earlier. Native Node/injected `fs` implementations retain their own read behavior. Read text with `readFile(path, "utf8")`; see the [module methods](src/modules/fs.ts) and [filesystem package](../safe-fs/README.md). |
539
+ | `makeFsModule(options?)` | Node-backed `{ root?, fs? }`, or shared-filesystem `{ adapter, root?, cwd?, signal? }`; do not combine `fs` and `adapter`. Node access without `root` is unconfined. Rooted calls sharing the same adapter or Node implementation are serialized through path checks and backend completion to prevent guest namespace races. External host mutations or distinct adapters over the same storage require independent backend confinement. With an adapter, `root` confines access and `cwd` selects the virtual relative-path base; without `root`, explicit `cwd` also confines access. Omitting both uses virtual `/`. Adapter text reads pass a byte cap derived conservatively from the guest string and remaining data limits to the backend before decoding. Optional `readFileMaxBytes` and `hostReadMemoryLimit` default to `Infinity` and accept explicit `Infinity`. A finite host allowance reserves ten bytes per input byte for collection, copies, encoding expansion and admission across concurrent reads from that module; reservations release after the underlying read settles and decoding finishes, including when cancellation rejects the caller earlier. Native Node/injected `fs` implementations retain their own read behavior. Read text with `readFile(path, "utf8")`; see the [module methods](src/modules/fs.ts) and [filesystem package](../safe-fs/README.md). |
540
540
  | `makeEnvModule(namesOrOptions)` | Allowed-name array or `{ allow, values? }`; exposes `get(name)`. `parseEnvConfig(json)` accepts the object form. |
541
541
  | `makeTimeModule(options?)` | `now`, `random`, `seed`, `signal`; exposes `now`, `random`, `sleep`, `uuid`. Defaults to host time/randomness; `seed` makes the random generator deterministic, and explicit `random` takes precedence. |
542
542
  | `makeLogModule(sink)` | Sends timestamped `info`, `error`, and `event` entries to your callback. |
@@ -548,7 +548,7 @@ Factories return exports to register in `modules`; calling a factory alone does
548
548
 
549
549
  `spawn.retry(definition, options, retryOptions)` and `defaultRetry` use `{ maxAttempts, backoffMs, isErrorRetryable?, isRetryable? }`. `spawn.parallel(calls, options?)` accepts definition/options tuples or spawn-handle factories; options are `check`, `maxConcurrent`, `failFast`, and `signal`. Usage helpers are `createSpawnUsageAccumulator()` and `runWithSpawnUsageAccumulator(accumulator, operation)`.
550
550
 
551
- **Config files.** `parseMcpConfig(json, directory)` accepts `servers` and the three numeric MCP limits, resolving stdio paths against `directory`. `parseFsConfig(json)` accepts `{ adapter: { type, options }, root?, cwd? }`; `resolveFsConfig(config, { registry? })` constructs the adapter for `makeFsModule`. Built-in types are `memory` and `real`; `real` requires an absolute host `options.root`. Outer `root`/`cwd` are absolute virtual paths. Custom registry descriptors supply `validateOptions` and `create`. Signals and injected functions are SDK options, not JSON fields.
551
+ **Config files.** `parseMcpConfig(json, directory)` accepts `servers` and the three numeric MCP limits, resolving stdio paths against `directory`. `parseFsConfig(json)` accepts `{ adapter: { type, options }, root?, cwd?, readFileMaxBytes?, hostReadMemoryLimit? }`; `resolveFsConfig(config, { registry? })` constructs the adapter for `makeFsModule`. Built-in types are `memory` and `real`; `real` requires an absolute host `options.root`. Outer `root`/`cwd` are absolute virtual paths. Custom registry descriptors supply `validateOptions` and `create`. Signals and injected functions are SDK options, not JSON fields.
552
552
 
553
553
  </details>
554
554
 
@@ -14,4 +14,4 @@ export {
14
14
  getOwnErrorCode,
15
15
  hasOwnErrorCode
16
16
  };
17
- //# sourceMappingURL=chunk-SJZWSPWD.js.map
17
+ //# sourceMappingURL=chunk-2GVBG23M.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/error-codes.ts"],
4
- "sourcesContent": ["export function getOwnErrorCode(error: unknown): string | undefined {\n if (\n typeof error !== \"object\" ||\n error === null ||\n !Object.prototype.hasOwnProperty.call(error, \"code\")\n ) {\n return undefined;\n }\n\n const code = (error as { code?: unknown }).code;\n return typeof code === \"string\" ? code : undefined;\n}\n\nexport function hasOwnErrorCode(error: unknown, code: string): error is NodeJS.ErrnoException {\n return getOwnErrorCode(error) === code;\n}\n"],
4
+ "sourcesContent": ["export function getOwnErrorCode(error: unknown): string | undefined {\n if (\n typeof error !== \"object\" ||\n error === null ||\n !Object.prototype.hasOwnProperty.call(error, \"code\")\n ) {\n return undefined;\n }\n\n const code = (error as { code?: unknown }).code;\n return typeof code === \"string\" ? code : undefined;\n}\n\nexport function hasOwnErrorCode(error: unknown, code: string): error is NodeJS.ErrnoException {\n return getOwnErrorCode(error) === code;\n}\n\nexport type SystemError = Error & { code?: string; errno?: number; syscall?: string; path?: string; dest?: string };\n"],
5
5
  "mappings": ";AAAO,SAAS,gBAAgB,OAAoC;AAClE,MACE,OAAO,UAAU,YACjB,UAAU,QACV,CAAC,OAAO,UAAU,eAAe,KAAK,OAAO,MAAM,GACnD;AACA,WAAO;AAAA,EACT;AAEA,QAAM,OAAQ,MAA6B;AAC3C,SAAO,OAAO,SAAS,WAAW,OAAO;AAC3C;AAEO,SAAS,gBAAgB,OAAgB,MAA8C;AAC5F,SAAO,gBAAgB,KAAK,MAAM;AACpC;",
6
6
  "names": []
7
7
  }
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  isSandboxClosure
3
- } from "./chunk-S5YDMMM3.js";
3
+ } from "./chunk-MDIWQFYU.js";
4
4
 
5
5
  // packages/safe-js/src/lint/runtime-modules.ts
6
6
  var AsyncFunction = (async () => void 0).constructor;
@@ -34,4 +34,4 @@ function isAsyncRuntimeExport(value) {
34
34
  export {
35
35
  createLintModulesFromRuntimeRegistry
36
36
  };
37
- //# sourceMappingURL=chunk-VIMROAQA.js.map
37
+ //# sourceMappingURL=chunk-E4XLBT7R.js.map
@@ -0,0 +1,114 @@
1
+ import {
2
+ paths_default
3
+ } from "./chunk-YUQFFRUF.js";
4
+ import {
5
+ constants,
6
+ hostFs
7
+ } from "./chunk-UQPYTZOZ.js";
8
+
9
+ // packages/safe-js/src/modules/source-files.ts
10
+ import { resolvePath } from "@poe-platform/safe-fs/core";
11
+ async function createRootedSourceResolver(root, adapter) {
12
+ const realpath = (filename) => adapter ? adapter.realpath(filename) : hostFs.realpath(filename);
13
+ const stat = async (filename) => adapter ? sourceStat(await adapter.stat(filename)) : await hostFs.stat(filename);
14
+ const granted = adapter ? resolvePath("/", root) : paths_default.resolve(root);
15
+ const directory = await realpath(granted);
16
+ if (!(await stat(directory)).isDirectory()) throw new TypeError("Source module root must be a directory.");
17
+ const contains = (filename) => {
18
+ const relative = paths_default.relative(directory, filename);
19
+ return relative !== ".." && !relative.startsWith(`..${paths_default.sep}`) && !paths_default.isAbsolute(relative);
20
+ };
21
+ const resolver = async (specifier, referrer, { signal }) => {
22
+ signal?.throwIfAborted();
23
+ if (!specifier.startsWith("./") && !specifier.startsWith("../") || !paths_default.isAbsolute(referrer)) return void 0;
24
+ const relative = paths_default.relative(granted, referrer);
25
+ const canonicalReferrer = relative !== ".." && !relative.startsWith(`..${paths_default.sep}`) && !paths_default.isAbsolute(relative) ? paths_default.resolve(directory, relative) : referrer;
26
+ if (!contains(canonicalReferrer)) return void 0;
27
+ const candidate = paths_default.resolve(paths_default.dirname(canonicalReferrer), specifier);
28
+ if (!contains(candidate)) return void 0;
29
+ try {
30
+ const id = await realpath(candidate);
31
+ signal?.throwIfAborted();
32
+ if (!contains(id)) return void 0;
33
+ const expected = await stat(id);
34
+ signal?.throwIfAborted();
35
+ if (!expected.isFile()) return void 0;
36
+ const handle = adapter ? await openSourceFile(adapter, id, signal) : await hostFs.open(id, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
37
+ try {
38
+ signal?.throwIfAborted();
39
+ const actual = await handle.stat();
40
+ signal?.throwIfAborted();
41
+ if (!actual.isFile() || ("identity" in actual && "identity" in expected ? !sameSourceIdentity(actual.identity, expected.identity) : actual.dev !== expected.dev || actual.ino !== expected.ino)) return void 0;
42
+ const currentId = await realpath(id);
43
+ signal?.throwIfAborted();
44
+ if (currentId !== id) return void 0;
45
+ const source = await handle.readFile({ encoding: "utf8", signal });
46
+ signal?.throwIfAborted();
47
+ return { id, source };
48
+ } finally {
49
+ await handle.close();
50
+ }
51
+ } catch (error) {
52
+ signal?.throwIfAborted();
53
+ if (error !== null && typeof error === "object" && "code" in error && ["ENOENT", "ENOTDIR", "ELOOP"].includes(String(error.code))) return void 0;
54
+ throw error;
55
+ }
56
+ };
57
+ return Object.assign(resolver, { async entryId(filename) {
58
+ if (filename === void 0) return paths_default.join(directory, "<entry>");
59
+ const supplied = paths_default.resolve(granted, filename);
60
+ const relative = paths_default.relative(granted, supplied);
61
+ const candidate = relative !== ".." && !relative.startsWith(`..${paths_default.sep}`) && !paths_default.isAbsolute(relative) ? paths_default.resolve(directory, relative) : supplied;
62
+ if (!contains(candidate)) throw new TypeError("Source entry must be inside its granted root.");
63
+ try {
64
+ const id = await realpath(candidate);
65
+ if (!contains(id)) throw new TypeError("Source entry must be inside its granted root.");
66
+ return id;
67
+ } catch (error) {
68
+ if (error !== null && typeof error === "object" && "code" in error && error.code === "ENOENT") return candidate;
69
+ throw error;
70
+ }
71
+ } });
72
+ }
73
+ function sourceStat(value) {
74
+ return {
75
+ identity: value,
76
+ dev: value.dev,
77
+ ino: value.ino,
78
+ isFile: () => value.type === "file",
79
+ isDirectory: () => value.type === "directory"
80
+ };
81
+ }
82
+ function sameSourceIdentity(actual, expected) {
83
+ if (expected.identityScope !== void 0 && expected.opaqueIdentity !== void 0) {
84
+ return actual.identityScope === expected.identityScope && actual.opaqueIdentity === expected.opaqueIdentity && (expected.opaqueVersion === void 0 || actual.opaqueVersion === expected.opaqueVersion);
85
+ }
86
+ return expected.dev !== void 0 && expected.ino !== void 0 && actual.dev === expected.dev && actual.ino === expected.ino;
87
+ }
88
+ async function openSourceFile(adapter, id, signal) {
89
+ if (!adapter.openReadFile) throw new TypeError("Source filesystem must support retained reads.");
90
+ const handle = await adapter.openReadFile(id, signal ? { signal } : {});
91
+ return {
92
+ stat: async () => sourceStat(await handle.stat(signal ? { signal } : {})),
93
+ close: handle.close.bind(handle),
94
+ async readFile(_options) {
95
+ const decoder = new TextDecoder("utf-8", { ignoreBOM: true });
96
+ const chunks = [];
97
+ let position = 0;
98
+ for (; ; ) {
99
+ signal?.throwIfAborted();
100
+ const chunk = await handle.read(position, 64 * 1024, signal ? { signal } : {});
101
+ if (chunk.byteLength === 0) break;
102
+ chunks.push(decoder.decode(chunk, { stream: true }));
103
+ position += chunk.byteLength;
104
+ }
105
+ chunks.push(decoder.decode());
106
+ return chunks.join("");
107
+ }
108
+ };
109
+ }
110
+
111
+ export {
112
+ createRootedSourceResolver
113
+ };
114
+ //# sourceMappingURL=chunk-F6CV74HU.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/modules/source-files.ts"],
4
+ "sourcesContent": ["import {hostFs, fsConstants as constants} from \"#safe-js-platform\";\nimport {resolvePath, type FileSystem, type FileStat} from \"@poe-code/safe-fs/core\";\n\nimport path from \"./paths.js\";\nimport type {SourceResolver} from \"./source-graph.js\";\n\n/** Explicit filesystem grant. No package lookup, extension search, or URL loading. */\nexport async function createRootedSourceResolver(root: string, adapter?: FileSystem): Promise<SourceResolver & {\n entryId(filename?: string): Promise<string>;\n}> {\n const realpath = (filename: string) => adapter ? adapter.realpath(filename) : hostFs.realpath(filename);\n const stat = async (filename: string) => adapter ? sourceStat(await adapter.stat(filename)) : await hostFs.stat(filename);\n const granted = adapter ? resolvePath(\"/\", root) : path.resolve(root);\n const directory = await realpath(granted);\n if (!(await stat(directory)).isDirectory()) throw new TypeError(\"Source module root must be a directory.\");\n const contains = (filename: string) => {\n const relative = path.relative(directory,filename);\n return relative !== \"..\" && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative);\n };\n const resolver: SourceResolver = async (specifier,referrer,{signal}) => {\n signal?.throwIfAborted();\n if ((!specifier.startsWith(\"./\") && !specifier.startsWith(\"../\")) || !path.isAbsolute(referrer)) return undefined;\n const relative = path.relative(granted,referrer);\n const canonicalReferrer = relative !== \"..\" && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative)\n ? path.resolve(directory,relative) : referrer;\n if (!contains(canonicalReferrer)) return undefined;\n const candidate = path.resolve(path.dirname(canonicalReferrer),specifier);\n if (!contains(candidate)) return undefined;\n try {\n const id = await realpath(candidate);\n signal?.throwIfAborted();\n if (!contains(id)) return undefined;\n const expected = await stat(id);\n signal?.throwIfAborted();\n if (!expected.isFile()) return undefined;\n const handle = adapter\n ? await openSourceFile(adapter, id, signal)\n : await hostFs.open(id,constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);\n try {\n signal?.throwIfAborted();\n const actual = await handle.stat();\n signal?.throwIfAborted();\n // Read the checked inode, never reopen its pathname. These checks deny\n // observed replacements; Node's path APIs do not provide atomic ancestor\n // confinement against an adversary repeatedly swapping directories.\n if (!actual.isFile() || (\"identity\" in actual && \"identity\" in expected\n ? !sameSourceIdentity(actual.identity, expected.identity)\n : actual.dev !== expected.dev || actual.ino !== expected.ino)) return undefined;\n const currentId = await realpath(id);\n signal?.throwIfAborted();\n if (currentId !== id) return undefined;\n const source = await handle.readFile({encoding:\"utf8\",signal});\n signal?.throwIfAborted();\n return {id,source};\n } finally {await handle.close();}\n } catch (error) {\n signal?.throwIfAborted();\n if (error !== null && typeof error === \"object\" && \"code\" in error &&\n [\"ENOENT\",\"ENOTDIR\",\"ELOOP\"].includes(String(error.code))) return undefined;\n throw error;\n }\n };\n return Object.assign(resolver, {async entryId(filename?: string): Promise<string> {\n if (filename === undefined) return path.join(directory,\"<entry>\");\n const supplied = path.resolve(granted,filename);\n const relative = path.relative(granted,supplied);\n const candidate = relative !== \"..\" && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative)\n ? path.resolve(directory,relative) : supplied;\n if (!contains(candidate)) throw new TypeError(\"Source entry must be inside its granted root.\");\n try {\n const id = await realpath(candidate);\n if (!contains(id)) throw new TypeError(\"Source entry must be inside its granted root.\");\n return id;\n } catch (error) {\n if (error !== null && typeof error === \"object\" && \"code\" in error && error.code === \"ENOENT\") return candidate;\n throw error;\n }\n }});\n}\n\nfunction sourceStat(value: FileStat) {\n return {identity: value, dev: value.dev, ino: value.ino,\n isFile: () => value.type === \"file\", isDirectory: () => value.type === \"directory\"};\n}\n\nfunction sameSourceIdentity(actual: FileStat, expected: FileStat): boolean {\n if (expected.identityScope !== undefined && expected.opaqueIdentity !== undefined) {\n return actual.identityScope === expected.identityScope && actual.opaqueIdentity === expected.opaqueIdentity &&\n (expected.opaqueVersion === undefined || actual.opaqueVersion === expected.opaqueVersion);\n }\n return expected.dev !== undefined && expected.ino !== undefined && actual.dev === expected.dev && actual.ino === expected.ino;\n}\n\nasync function openSourceFile(adapter: FileSystem, id: string, signal?: AbortSignal) {\n if (!adapter.openReadFile) throw new TypeError(\"Source filesystem must support retained reads.\");\n const handle = await adapter.openReadFile(id, signal ? {signal} : {});\n return {\n stat: async () => sourceStat(await handle.stat(signal ? {signal} : {})),\n close: handle.close.bind(handle),\n async readFile(_options: {encoding: \"utf8\"; signal?: AbortSignal}) {\n const decoder = new TextDecoder(\"utf-8\", {ignoreBOM: true});\n const chunks: string[] = [];\n let position = 0;\n for (;;) {\n signal?.throwIfAborted();\n const chunk = await handle.read(position, 64 * 1024, signal ? {signal} : {});\n if (chunk.byteLength === 0) break;\n chunks.push(decoder.decode(chunk, {stream: true}));\n position += chunk.byteLength;\n }\n chunks.push(decoder.decode());\n return chunks.join(\"\");\n }\n };\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  hasOwnErrorCode
3
- } from "./chunk-SJZWSPWD.js";
3
+ } from "./chunk-2GVBG23M.js";
4
4
 
5
5
  // packages/safe-js/src/output-stream.ts
6
6
  function createBrokenPipeState() {
@@ -57,4 +57,4 @@ export {
57
57
  withBrokenPipeGuard,
58
58
  addBrokenPipeListener
59
59
  };
60
- //# sourceMappingURL=chunk-OZLTLIHX.js.map
60
+ //# sourceMappingURL=chunk-G6WTEIJJ.js.map
@@ -1,18 +1,21 @@
1
1
  import {
2
- restore
3
- } from "./chunk-EN724VH3.js";
4
- import {
5
- attachDumpController,
6
- createDumpController
7
- } from "./chunk-MVLDRXWS.js";
2
+ fsCodec
3
+ } from "./chunk-GVKXZW53.js";
8
4
  import {
9
5
  getOwnErrorCode,
10
6
  hasOwnErrorCode
11
- } from "./chunk-SJZWSPWD.js";
7
+ } from "./chunk-2GVBG23M.js";
8
+ import {
9
+ restore
10
+ } from "./chunk-O4IDRXW7.js";
12
11
  import {
13
12
  hashParsedAst,
14
13
  hashSource
15
- } from "./chunk-EA7AHM2X.js";
14
+ } from "./chunk-PSVV3IG2.js";
15
+ import {
16
+ attachDumpController,
17
+ createDumpController
18
+ } from "./chunk-X3CKAZQ4.js";
16
19
  import {
17
20
  DUMP_FORMAT_VERSION,
18
21
  EXECUTION_SEMANTICS,
@@ -35,7 +38,6 @@ import {
35
38
  createBuiltinBindings,
36
39
  createEvalSource,
37
40
  createGuestReference,
38
- createHostCallbackContext,
39
41
  createImmutableEmptyModuleEnvironment,
40
42
  createLiveHostObject,
41
43
  createModuleEnvironment,
@@ -100,7 +102,7 @@ import {
100
102
  withSandboxPromiseRejectionTracker,
101
103
  wrapCallerInjectedBindings,
102
104
  wrapCancelableBindings
103
- } from "./chunk-S5YDMMM3.js";
105
+ } from "./chunk-MDIWQFYU.js";
104
106
  import {
105
107
  Budget,
106
108
  CompileScope,
@@ -115,14 +117,18 @@ import {
115
117
  parseSourceModule,
116
118
  replaceErrorStack,
117
119
  safeAddEvent
118
- } from "./chunk-Y5EZYHU6.js";
119
-
120
- // packages/safe-js/src/realm.ts
121
- import { AsyncLocalStorage as AsyncLocalStorage2 } from "node:async_hooks";
122
- import { types } from "node:util";
120
+ } from "./chunk-QKO4FATZ.js";
121
+ import {
122
+ dirname
123
+ } from "./chunk-YUQFFRUF.js";
124
+ import {
125
+ AsyncLocalStorage,
126
+ createHostCallbackContext,
127
+ hostFs,
128
+ types
129
+ } from "./chunk-UQPYTZOZ.js";
123
130
 
124
131
  // packages/safe-js/src/modules/source-graph.ts
125
- import { AsyncLocalStorage } from "node:async_hooks";
126
132
  var ambiguous = /* @__PURE__ */ Symbol("ambiguous module export");
127
133
  var sourceRecords = /* @__PURE__ */ new WeakSet();
128
134
  var SourceModuleGraph = class {
@@ -612,17 +618,17 @@ var RealmState = class {
612
618
  this.queue = queue;
613
619
  const limitInput = readDataRecord(options.limits ?? {}, "Realm limits");
614
620
  this.limits = {
615
- extensions: 32,
616
- hostObjects: 1024,
617
- callbacks: 1024,
618
- guestReferences: 1024,
619
- cleanups: 1024,
620
- nestedEvaluations: 16
621
+ extensions: Infinity,
622
+ hostObjects: Infinity,
623
+ callbacks: Infinity,
624
+ guestReferences: Infinity,
625
+ cleanups: Infinity,
626
+ nestedEvaluations: Infinity
621
627
  };
622
628
  for (const [name, value] of Object.entries(limitInput)) {
623
- if (!Object.hasOwn(this.limits, name) || !Number.isSafeInteger(value) || Number(value) < 1)
624
- throw new TypeError("Realm limits must be positive safe integers with supported names.");
625
- this.limits[name] = Number(value);
629
+ if (!Object.hasOwn(this.limits, name) || value !== void 0 && value !== Infinity && (!Number.isSafeInteger(value) || Number(value) < 1))
630
+ throw new TypeError("Realm limits must be positive safe integers or Infinity with supported names.");
631
+ if (value !== void 0) this.limits[name] = Number(value);
626
632
  }
627
633
  if (options.extensions !== void 0 && (!Array.isArray(options.extensions) || types.isProxy(options.extensions)))
628
634
  throw new TypeError("Extensions must be a registration array.");
@@ -655,7 +661,7 @@ var RealmState = class {
655
661
  throw new TypeError("Console override requires a registered extension declaring console.");
656
662
  this.consoleExtension = extensionName;
657
663
  }
658
- this.budget = options.budget ?? new Budget({ maxCallDepth: 1e3 });
664
+ this.budget = options.budget ?? new Budget();
659
665
  this.lease = this.budget.acquireRealmOwner();
660
666
  this.compilation = new CompileScope(this.lease.owner);
661
667
  this.bridge = {
@@ -731,7 +737,7 @@ var RealmState = class {
731
737
  lease;
732
738
  compilation;
733
739
  controller = new AbortController();
734
- phase = new AsyncLocalStorage2();
740
+ phase = new AsyncLocalStorage();
735
741
  queue;
736
742
  tracker = createSandboxPromiseRejectionTracker();
737
743
  bridge;
@@ -1733,9 +1739,7 @@ function readRealmOptions(value, oneShot = false) {
1733
1739
  }
1734
1740
 
1735
1741
  // packages/safe-js/src/snapshot/backend.ts
1736
- import { randomUUID } from "node:crypto";
1737
- import { readFile, rename, stat, unlink, writeFile } from "node:fs/promises";
1738
- import { dirname } from "node:path";
1742
+ import { createFsBridge } from "@poe-platform/safe-fs/core";
1739
1743
  var DEFAULT_WRITE_MAX_ATTEMPTS = 3;
1740
1744
  var DEFAULT_WRITE_RETRY_DELAY_MS = 100;
1741
1745
  var LOCKED_FILE_ERROR_CODES = /* @__PURE__ */ new Set(["EACCES", "EBUSY", "EPERM"]);
@@ -1743,15 +1747,17 @@ var pendingOperations = /* @__PURE__ */ new Map();
1743
1747
  var FileSnapshotBackend = class {
1744
1748
  constructor(path, options = {}) {
1745
1749
  this.path = path;
1750
+ this.#io = options.adapter === void 0 ? hostFs : createFsBridge(options.adapter, { codec: fsCodec });
1746
1751
  this.#writeMaxAttempts = options.writeMaxAttempts ?? DEFAULT_WRITE_MAX_ATTEMPTS;
1747
1752
  this.#writeRetryDelayMs = options.writeRetryDelayMs ?? DEFAULT_WRITE_RETRY_DELAY_MS;
1748
1753
  }
1749
1754
  path;
1755
+ #io;
1750
1756
  #writeMaxAttempts;
1751
1757
  #writeRetryDelayMs;
1752
1758
  async read() {
1753
1759
  try {
1754
- return JSON.parse(await readFile(this.path, "utf8"));
1760
+ return JSON.parse(await this.#io.readFile(this.path, "utf8"));
1755
1761
  } catch (error) {
1756
1762
  if (hasErrorCode(error, "ENOENT")) {
1757
1763
  return void 0;
@@ -1765,7 +1771,7 @@ var FileSnapshotBackend = class {
1765
1771
  async write(snapshot) {
1766
1772
  await enqueueOperation(
1767
1773
  this.path,
1768
- () => writeSnapshotAtomically(this.path, snapshot, {
1774
+ () => writeSnapshotAtomically(this.#io, this.path, snapshot, {
1769
1775
  maxAttempts: this.#writeMaxAttempts,
1770
1776
  retryDelayMs: this.#writeRetryDelayMs
1771
1777
  })
@@ -1774,7 +1780,7 @@ var FileSnapshotBackend = class {
1774
1780
  async remove() {
1775
1781
  await enqueueOperation(this.path, async () => {
1776
1782
  try {
1777
- await unlink(this.path);
1783
+ await this.#io.unlink(this.path);
1778
1784
  } catch (error) {
1779
1785
  if (!hasErrorCode(error, "ENOENT")) {
1780
1786
  throw error;
@@ -1783,14 +1789,14 @@ var FileSnapshotBackend = class {
1783
1789
  });
1784
1790
  }
1785
1791
  };
1786
- async function writeSnapshotAtomically(snapshotPath, snapshot, options) {
1792
+ async function writeSnapshotAtomically(io, snapshotPath, snapshot, options) {
1787
1793
  const parentPath = dirname(snapshotPath);
1788
1794
  const contents = serializeSafeJSSnapshot(snapshot);
1789
- await assertParentDirectoryExists(snapshotPath, parentPath);
1795
+ await assertParentDirectoryExists(io, snapshotPath, parentPath);
1790
1796
  for (let attempt = 1; attempt <= options.maxAttempts; attempt += 1) {
1791
1797
  try {
1792
- const temporaryPath = `${snapshotPath}.${randomUUID()}.tmp`;
1793
- await writeSnapshotOnce(temporaryPath, snapshotPath, contents);
1798
+ const temporaryPath = `${snapshotPath}.${globalThis.crypto.randomUUID()}.tmp`;
1799
+ await writeSnapshotOnce(io, temporaryPath, snapshotPath, contents);
1794
1800
  return;
1795
1801
  } catch (error) {
1796
1802
  if (hasErrorCode(error, "EEXIST")) {
@@ -1819,9 +1825,9 @@ async function writeSnapshotAtomically(snapshotPath, snapshot, options) {
1819
1825
  }
1820
1826
  }
1821
1827
  }
1822
- async function assertParentDirectoryExists(snapshotPath, parentPath) {
1828
+ async function assertParentDirectoryExists(io, snapshotPath, parentPath) {
1823
1829
  try {
1824
- const parent = await stat(parentPath);
1830
+ const parent = await io.stat(parentPath);
1825
1831
  if (!parent.isDirectory()) {
1826
1832
  throw new Error(
1827
1833
  `Cannot write snapshot at ${snapshotPath}: parent path ${parentPath} is not a directory`
@@ -1839,24 +1845,24 @@ async function assertParentDirectoryExists(snapshotPath, parentPath) {
1839
1845
  throw error;
1840
1846
  }
1841
1847
  }
1842
- async function writeSnapshotOnce(temporaryPath, snapshotPath, contents) {
1848
+ async function writeSnapshotOnce(io, temporaryPath, snapshotPath, contents) {
1843
1849
  let temporaryCreated = false;
1844
1850
  let renamed = false;
1845
1851
  try {
1846
1852
  try {
1847
- await writeFile(temporaryPath, contents, { encoding: "utf8", flag: "wx" });
1853
+ await io.writeFile(temporaryPath, contents, { encoding: "utf8", flag: "wx" });
1848
1854
  temporaryCreated = true;
1849
1855
  } catch (error) {
1850
1856
  if (!hasErrorCode(error, "EEXIST")) {
1851
- await removeTemporarySnapshot(temporaryPath).catch(() => void 0);
1857
+ await removeTemporarySnapshot(io, temporaryPath).catch(() => void 0);
1852
1858
  }
1853
1859
  throw error;
1854
1860
  }
1855
- await rename(temporaryPath, snapshotPath);
1861
+ await io.rename(temporaryPath, snapshotPath);
1856
1862
  renamed = true;
1857
1863
  } finally {
1858
1864
  if (temporaryCreated && !renamed) {
1859
- await removeTemporarySnapshot(temporaryPath).catch(() => void 0);
1865
+ await removeTemporarySnapshot(io, temporaryPath).catch(() => void 0);
1860
1866
  }
1861
1867
  }
1862
1868
  }
@@ -1873,9 +1879,9 @@ async function enqueueOperation(path, operation) {
1873
1879
  }
1874
1880
  }
1875
1881
  }
1876
- async function removeTemporarySnapshot(temporaryPath) {
1882
+ async function removeTemporarySnapshot(io, temporaryPath) {
1877
1883
  try {
1878
- await unlink(temporaryPath);
1884
+ await io.unlink(temporaryPath);
1879
1885
  } catch (error) {
1880
1886
  if (!hasErrorCode(error, "ENOENT")) {
1881
1887
  throw error;
@@ -2212,7 +2218,6 @@ var UnhandledRejectionError = class extends Error {
2212
2218
  replaceErrorStack(this);
2213
2219
  }
2214
2220
  };
2215
- var DEFAULT_MAX_CALL_DEPTH = 1e3;
2216
2221
  function run(source, options = {}) {
2217
2222
  const jobs = new SandboxJobQueue();
2218
2223
  if (options.extensions !== void 0 || options.builtinOverrides !== void 0 || options.sourceType !== void 0 || options.sourceResolver !== void 0 || options.sourceRoot !== void 0) {
@@ -2221,7 +2226,7 @@ function run(source, options = {}) {
2221
2226
  throw new TypeError("Source resolution requires sourceType: 'module'.");
2222
2227
  if (options.sourceRoot !== void 0) {
2223
2228
  if (options.sourceResolver !== void 0) throw new TypeError("Specify sourceRoot or sourceResolver, not both.");
2224
- const { createRootedSourceResolver } = await import("./source-files-73OHLNGH.js");
2229
+ const { createRootedSourceResolver } = await import("./source-files-L3SGK63Z.js");
2225
2230
  const { sourceRoot, ...resolved } = options;
2226
2231
  const sourceResolver = await createRootedSourceResolver(sourceRoot);
2227
2232
  return runWithExtensions(source, { ...resolved, filename: await sourceResolver.entryId(options.filename), sourceResolver }, jobs);
@@ -2245,7 +2250,7 @@ function run(source, options = {}) {
2245
2250
  const detachExecutionFailure = promiseTracker.onFatalRejection((error) => jobs.interrupt(error));
2246
2251
  let completedSnapshot;
2247
2252
  const execute = async () => {
2248
- const budget = options.budget ?? new Budget({ maxCallDepth: DEFAULT_MAX_CALL_DEPTH });
2253
+ const budget = options.budget ?? new Budget();
2249
2254
  const operation = budget.acquireCompileOwner(true);
2250
2255
  const compilation = new CompileScope(operation.owner);
2251
2256
  try {
@@ -2824,4 +2829,4 @@ export {
2824
2829
  UnhandledRejectionError,
2825
2830
  run
2826
2831
  };
2827
- //# sourceMappingURL=chunk-XX57YCGZ.js.map
2832
+ //# sourceMappingURL=chunk-GNEORAQI.js.map