@poe-platform/safe-js 0.1.721 → 0.1.723

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 (85) hide show
  1. package/README.md +36 -6
  2. package/dist/config-mutations/types.d.ts +3 -1
  3. package/dist/safe-js/chunks/{chunk-B4M6QVUV.js → chunk-3QQYJ4RQ.js} +3 -2
  4. package/dist/safe-js/chunks/chunk-3QQYJ4RQ.js.map +7 -0
  5. package/dist/safe-js/chunks/{chunk-BEXRQET4.js → chunk-5MT2LF3W.js} +3 -3
  6. package/dist/safe-js/chunks/{chunk-JNLY7IMV.js → chunk-7FCMZUZJ.js} +4 -4
  7. package/dist/safe-js/chunks/{chunk-BHYBD6SY.js → chunk-7TL6Z6LT.js} +58 -8
  8. package/dist/safe-js/chunks/chunk-7TL6Z6LT.js.map +7 -0
  9. package/dist/safe-js/chunks/{chunk-HYD4K4HN.js → chunk-BRBOVGNC.js} +63 -1
  10. package/dist/safe-js/chunks/chunk-BRBOVGNC.js.map +7 -0
  11. package/dist/safe-js/chunks/{chunk-4CX3HYRY.js → chunk-HY3Q377V.js} +2 -2
  12. package/dist/safe-js/chunks/{chunk-DNA7BMBY.js → chunk-IZ52PAOD.js} +8540 -7309
  13. package/dist/safe-js/chunks/{chunk-DNA7BMBY.js.map → chunk-IZ52PAOD.js.map} +4 -4
  14. package/dist/safe-js/chunks/{chunk-A4L72WXV.js → chunk-KHJDAEET.js} +10 -11
  15. package/dist/safe-js/chunks/chunk-KHJDAEET.js.map +7 -0
  16. package/dist/safe-js/chunks/{chunk-Q7B6JIMI.js → chunk-M6HUSN7X.js} +404 -117
  17. package/dist/safe-js/chunks/chunk-M6HUSN7X.js.map +7 -0
  18. package/dist/safe-js/chunks/{chunk-LPBTSTDG.js → chunk-QMZG576E.js} +4 -4
  19. package/dist/safe-js/chunks/{chunk-ILYJBA2D.js → chunk-RYVHEOHM.js} +1109 -100
  20. package/dist/safe-js/chunks/chunk-RYVHEOHM.js.map +7 -0
  21. package/dist/safe-js/chunks/{chunk-IS7YUCOF.js → chunk-SWQHZKYM.js} +2 -2
  22. package/dist/safe-js/chunks/{chunk-Y4LNCDCP.js → chunk-WF7HYDJ4.js} +4 -5
  23. package/dist/safe-js/chunks/{chunk-Y4LNCDCP.js.map → chunk-WF7HYDJ4.js.map} +2 -2
  24. package/dist/safe-js/chunks/{chunk-GMPZBMPA.js → chunk-Y5ZXQHL6.js} +3 -3
  25. package/dist/safe-js/chunks/{cli-runtime-YK2WE33M.js → cli-runtime-EPXCJ5AG.js} +14 -14
  26. package/dist/safe-js/chunks/{dump-ATWBOZWV.js → dump-TWVEBH5W.js} +4 -4
  27. package/dist/safe-js/chunks/{fs-D72Y52I5.js → fs-DSNHTHBT.js} +4 -4
  28. package/dist/safe-js/chunks/harness-RLA643GD.js +10 -0
  29. package/dist/safe-js/chunks/mcp-IEY7HXXT.js +11 -0
  30. package/dist/safe-js/chunks/migration-file-QMI45CSP.js +12 -0
  31. package/dist/safe-js/chunks/restore-R5PLLBST.js +13 -0
  32. package/dist/safe-js/chunks/run-QATVEFVQ.js +17 -0
  33. package/dist/safe-js/chunks/runtime-modules-5D7KDFMZ.js +10 -0
  34. package/dist/safe-js/cli.js +2 -2
  35. package/dist/safe-js/core.js +7 -7
  36. package/dist/safe-js/index.js +14 -14
  37. package/dist/safe-js/interp/async.d.ts +4 -1
  38. package/dist/safe-js/interp/budget.d.ts +12 -6
  39. package/dist/safe-js/interp/collection-iterator.d.ts +1 -1
  40. package/dist/safe-js/interp/host-bridge.d.ts +2 -0
  41. package/dist/safe-js/interp/host-capabilities.d.ts +13 -4
  42. package/dist/safe-js/interp/interpreter.d.ts +5 -1
  43. package/dist/safe-js/interp/intl-numberformat.d.ts +2 -2
  44. package/dist/safe-js/interp/jobs.d.ts +8 -0
  45. package/dist/safe-js/interp/scope.d.ts +9 -2
  46. package/dist/safe-js/interp/typed-array.d.ts +2 -2
  47. package/dist/safe-js/interp/values.d.ts +1 -0
  48. package/dist/safe-js/modules/registry.d.ts +4 -1
  49. package/dist/safe-js/modules/source-graph.d.ts +19 -1
  50. package/dist/safe-js/parse/compact-spans.d.ts +18 -0
  51. package/dist/safe-js/parse/dynamic-source.d.ts +3 -1
  52. package/dist/safe-js/parse/function-source.d.ts +1 -1
  53. package/dist/safe-js/parse/parser.d.ts +8 -3
  54. package/dist/safe-js/parse/tokenizer.d.ts +4 -0
  55. package/dist/safe-js/realm.d.ts +16 -3
  56. package/dist/safe-js/restore.d.ts +1 -0
  57. package/dist/safe-js/run.d.ts +10 -0
  58. package/package.json +2 -2
  59. package/dist/safe-js/chunks/chunk-A4L72WXV.js.map +0 -7
  60. package/dist/safe-js/chunks/chunk-B4M6QVUV.js.map +0 -7
  61. package/dist/safe-js/chunks/chunk-BHYBD6SY.js.map +0 -7
  62. package/dist/safe-js/chunks/chunk-HYD4K4HN.js.map +0 -7
  63. package/dist/safe-js/chunks/chunk-ILYJBA2D.js.map +0 -7
  64. package/dist/safe-js/chunks/chunk-Q7B6JIMI.js.map +0 -7
  65. package/dist/safe-js/chunks/harness-LUYCGWHZ.js +0 -10
  66. package/dist/safe-js/chunks/mcp-IWKZB2R2.js +0 -11
  67. package/dist/safe-js/chunks/migration-file-IXO5MLP4.js +0 -12
  68. package/dist/safe-js/chunks/restore-SI7HOC6C.js +0 -13
  69. package/dist/safe-js/chunks/run-SHI2PYR6.js +0 -17
  70. package/dist/safe-js/chunks/runtime-modules-OO72HTVF.js +0 -10
  71. /package/dist/safe-js/chunks/{chunk-BEXRQET4.js.map → chunk-5MT2LF3W.js.map} +0 -0
  72. /package/dist/safe-js/chunks/{chunk-JNLY7IMV.js.map → chunk-7FCMZUZJ.js.map} +0 -0
  73. /package/dist/safe-js/chunks/{chunk-4CX3HYRY.js.map → chunk-HY3Q377V.js.map} +0 -0
  74. /package/dist/safe-js/chunks/{chunk-LPBTSTDG.js.map → chunk-QMZG576E.js.map} +0 -0
  75. /package/dist/safe-js/chunks/{chunk-IS7YUCOF.js.map → chunk-SWQHZKYM.js.map} +0 -0
  76. /package/dist/safe-js/chunks/{chunk-GMPZBMPA.js.map → chunk-Y5ZXQHL6.js.map} +0 -0
  77. /package/dist/safe-js/chunks/{cli-runtime-YK2WE33M.js.map → cli-runtime-EPXCJ5AG.js.map} +0 -0
  78. /package/dist/safe-js/chunks/{dump-ATWBOZWV.js.map → dump-TWVEBH5W.js.map} +0 -0
  79. /package/dist/safe-js/chunks/{fs-D72Y52I5.js.map → fs-DSNHTHBT.js.map} +0 -0
  80. /package/dist/safe-js/chunks/{harness-LUYCGWHZ.js.map → harness-RLA643GD.js.map} +0 -0
  81. /package/dist/safe-js/chunks/{mcp-IWKZB2R2.js.map → mcp-IEY7HXXT.js.map} +0 -0
  82. /package/dist/safe-js/chunks/{migration-file-IXO5MLP4.js.map → migration-file-QMI45CSP.js.map} +0 -0
  83. /package/dist/safe-js/chunks/{restore-SI7HOC6C.js.map → restore-R5PLLBST.js.map} +0 -0
  84. /package/dist/safe-js/chunks/{run-SHI2PYR6.js.map → run-QATVEFVQ.js.map} +0 -0
  85. /package/dist/safe-js/chunks/{runtime-modules-OO72HTVF.js.map → runtime-modules-5D7KDFMZ.js.map} +0 -0
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  restore
3
- } from "./chunk-A4L72WXV.js";
3
+ } from "./chunk-KHJDAEET.js";
4
4
  import {
5
5
  attachDumpController,
6
6
  createDumpController
7
- } from "./chunk-BEXRQET4.js";
7
+ } from "./chunk-5MT2LF3W.js";
8
8
  import {
9
9
  getOwnErrorCode,
10
10
  hasOwnErrorCode
@@ -12,7 +12,7 @@ import {
12
12
  import {
13
13
  hashParsedAst,
14
14
  hashSource
15
- } from "./chunk-GMPZBMPA.js";
15
+ } from "./chunk-Y5ZXQHL6.js";
16
16
  import {
17
17
  DUMP_FORMAT_VERSION,
18
18
  EXECUTION_SEMANTICS,
@@ -36,6 +36,7 @@ import {
36
36
  createEvalSource,
37
37
  createGuestReference,
38
38
  createHostCallbackContext,
39
+ createImmutableEmptyModuleEnvironment,
39
40
  createLiveHostObject,
40
41
  createModuleEnvironment,
41
42
  createModuleNamespace,
@@ -48,6 +49,7 @@ import {
48
49
  decodeReplayData,
49
50
  deepCopyFromSandbox,
50
51
  deepCopyToSandbox,
52
+ denyGuestStringCompilation,
51
53
  encodeReplayData,
52
54
  enterRunningState,
53
55
  enterSnapshotRun,
@@ -55,6 +57,7 @@ import {
55
57
  getExtensionSetup,
56
58
  getRealmGlobalObject,
57
59
  guestProxyStates,
60
+ hostObjectGuestRoot,
58
61
  inMemoryRunSnapshots,
59
62
  interpret,
60
63
  invokeBuiltinClosure,
@@ -97,7 +100,7 @@ import {
97
100
  withSandboxPromiseRejectionTracker,
98
101
  wrapCallerInjectedBindings,
99
102
  wrapCancelableBindings
100
- } from "./chunk-DNA7BMBY.js";
103
+ } from "./chunk-IZ52PAOD.js";
101
104
  import {
102
105
  Budget,
103
106
  CompileScope,
@@ -112,20 +115,21 @@ import {
112
115
  parseSourceModule,
113
116
  replaceErrorStack,
114
117
  safeAddEvent
115
- } from "./chunk-ILYJBA2D.js";
118
+ } from "./chunk-RYVHEOHM.js";
116
119
 
117
120
  // packages/safe-js/src/realm.ts
118
- import { AsyncLocalStorage } from "node:async_hooks";
121
+ import { AsyncLocalStorage as AsyncLocalStorage2 } from "node:async_hooks";
119
122
  import { types } from "node:util";
120
123
 
121
124
  // packages/safe-js/src/modules/source-graph.ts
125
+ import { AsyncLocalStorage } from "node:async_hooks";
122
126
  var ambiguous = /* @__PURE__ */ Symbol("ambiguous module export");
123
127
  var sourceRecords = /* @__PURE__ */ new WeakSet();
124
128
  var SourceModuleGraph = class {
125
129
  constructor(options) {
126
130
  this.options = options;
127
131
  this.jobs = options.jobs ?? new SandboxJobQueue();
128
- attachSourceLoader(options.modules, this.import.bind(this));
132
+ attachSourceLoader(options.modules, this.import.bind(this), { preserveImmutableNamespaces: true });
129
133
  options.budget?.setRetainedValues(this, () => [...this.records.values()].flatMap((record) => [record.id, record.source, ...record.parsed.requests, ...record.scope.retainedDataRoots()]));
130
134
  }
131
135
  options;
@@ -133,60 +137,169 @@ var SourceModuleGraph = class {
133
137
  records = /* @__PURE__ */ new Map();
134
138
  requests = /* @__PURE__ */ new Map();
135
139
  requestDataSize = 0;
140
+ lookupDataSize = 0;
136
141
  pendingImports = /* @__PURE__ */ new Set();
142
+ fulfilledImports = 0;
143
+ rejectedImports = 0;
144
+ importGroup = new AsyncLocalStorage();
137
145
  jobs;
138
146
  evaluationStack = [];
139
147
  evaluationIndex = 0;
140
148
  close() {
141
149
  this.options.budget?.setRetainedValues(this, void 0);
142
150
  this.options.budget?.setRetainedDataUsage(this.requests, 0);
151
+ for (const record of this.records.values()) {
152
+ record.explicitExports.clear();
153
+ record.localImports.clear();
154
+ record.starExports.length = 0;
155
+ }
156
+ this.options.budget?.setRetainedDataUsage(this.records, 0);
157
+ this.lookupDataSize = 0;
143
158
  this.requestDataSize = 0;
144
159
  this.records.clear();
145
160
  this.requests.clear();
146
161
  }
162
+ sourceModuleStatus() {
163
+ return Object.freeze({
164
+ pendingImports: this.pendingImports.size,
165
+ preparedModules: this.records.size,
166
+ fulfilledImports: this.fulfilledImports,
167
+ rejectedImports: this.rejectedImports
168
+ });
169
+ }
147
170
  import(specifier, referrer) {
171
+ const group = this.importGroup.getStore();
172
+ const cancelDeadline = this.startImportDeadline();
148
173
  const pending = (async () => {
149
174
  const entry = await this.load(specifier, referrer);
150
175
  if (!isRecord(entry)) return entry;
151
176
  await this.loadGraph(entry);
152
- await this.link(entry);
177
+ if (this.options.serializePreparation) await this.jobs.run(() => this.link(entry));
178
+ else await this.link(entry);
153
179
  await this.evaluate(entry);
154
180
  await this.jobs.drain();
155
181
  return this.namespace(entry);
156
182
  })();
157
183
  this.pendingImports.add(pending);
158
- void pending.then(() => this.pendingImports.delete(pending), () => this.pendingImports.delete(pending));
184
+ group?.add(pending);
185
+ const release = () => {
186
+ cancelDeadline();
187
+ this.pendingImports.delete(pending);
188
+ group?.delete(pending);
189
+ };
190
+ void pending.then(
191
+ () => {
192
+ this.fulfilledImports++;
193
+ release();
194
+ },
195
+ () => {
196
+ this.rejectedImports++;
197
+ release();
198
+ }
199
+ );
159
200
  return pending;
160
201
  }
161
- async settle() {
202
+ startImportDeadline() {
203
+ const policy = this.options.importDeadline;
204
+ const signal = this.options.signal;
205
+ if (policy === void 0 || signal?.aborted) return () => {
206
+ };
207
+ const deadline = Date.now() + policy.timeoutMs;
208
+ let expired = false;
209
+ const expire = (reason) => {
210
+ if (expired) return;
211
+ expired = true;
212
+ cancel();
213
+ policy.abort(reason);
214
+ };
215
+ const releaseDeadline = this.options.budget?.acquireDeadline(deadline, expire);
216
+ const timer = setTimeout(() => {
217
+ expire(new SandboxError({
218
+ budget: "deadline",
219
+ current: Date.now(),
220
+ limit: deadline
221
+ }));
222
+ }, policy.timeoutMs);
223
+ const cancel = () => {
224
+ clearTimeout(timer);
225
+ releaseDeadline?.();
226
+ signal?.removeEventListener("abort", cancel);
227
+ };
228
+ signal?.addEventListener("abort", cancel, { once: true });
229
+ return cancel;
230
+ }
231
+ withoutImportGroup(task) {
232
+ return this.importGroup.exit(task);
233
+ }
234
+ withImportGroup(task) {
235
+ const group = /* @__PURE__ */ new Set();
236
+ return this.importGroup.run(group, async () => {
237
+ const result = await task();
238
+ await this.settle(group);
239
+ return result;
240
+ });
241
+ }
242
+ async settle(pendingImports = this.pendingImports) {
162
243
  do {
163
- await Promise.allSettled([...this.pendingImports]);
244
+ await Promise.allSettled([...pendingImports]);
164
245
  await this.jobs.drain();
165
- } while (this.pendingImports.size > 0);
246
+ } while (pendingImports.size > 0);
166
247
  }
167
248
  async evaluateSource(source, beforeEvaluation) {
168
- const entry = this.register(source);
249
+ const entry = this.options.serializePreparation ? await this.jobs.run(() => this.register(source)) : this.register(source);
169
250
  await this.loadGraph(entry);
170
- await this.link(entry);
251
+ if (this.options.serializePreparation) await this.jobs.run(() => this.link(entry));
252
+ else await this.link(entry);
171
253
  beforeEvaluation?.(entry.parsed.module);
172
254
  await this.evaluate(entry);
173
255
  await this.jobs.drain();
174
256
  return this.namespace(entry);
175
257
  }
176
258
  register(source) {
259
+ this.options.assertActive?.();
260
+ this.options.signal?.throwIfAborted();
177
261
  const known = this.records.get(source.id);
178
262
  if (known !== void 0) {
179
263
  if (known.source !== source.source) throw new TypeError(`Source identity '${source.id}' changed within the graph.`);
180
264
  return known;
181
265
  }
182
266
  this.options.budget?.chargeDataUsage(source.source.length + source.id.length + 1);
183
- const parsed = parseSourceModule(source.source, source.id, this.options.compilation?.owner ?? this.options.compileOwner);
267
+ const parsed = parseSourceModule(source.source, source.id, this.options.compilation?.owner ?? this.options.compileOwner, { sharedPositions: true, compactAst: true });
268
+ const explicitExports = /* @__PURE__ */ new Map();
269
+ const localImports = /* @__PURE__ */ new Map();
270
+ const starExports = [];
271
+ for (const entry of parsed.exports) {
272
+ this.options.budget?.visitNode();
273
+ if (entry.exported === void 0) {
274
+ const lookupDataSize = this.lookupDataSize + 1;
275
+ this.options.budget?.setRetainedDataUsage(this.records, lookupDataSize);
276
+ this.lookupDataSize = lookupDataSize;
277
+ starExports.push(entry);
278
+ } else if (!explicitExports.has(entry.exported)) {
279
+ const lookupDataSize = this.lookupDataSize + entry.exported.length + 2;
280
+ this.options.budget?.setRetainedDataUsage(this.records, lookupDataSize);
281
+ this.lookupDataSize = lookupDataSize;
282
+ explicitExports.set(entry.exported, entry);
283
+ }
284
+ }
285
+ for (const entry of parsed.imports) {
286
+ this.options.budget?.visitNode();
287
+ if (!localImports.has(entry.local)) {
288
+ const lookupDataSize = this.lookupDataSize + entry.local.length + 2;
289
+ this.options.budget?.setRetainedDataUsage(this.records, lookupDataSize);
290
+ this.lookupDataSize = lookupDataSize;
291
+ localImports.set(entry.local, entry);
292
+ }
293
+ }
184
294
  const importMeta = /* @__PURE__ */ Object.create(null);
185
295
  setSandboxPrototype(importMeta, null);
186
296
  const record = {
187
297
  id: source.id,
188
298
  source: source.source,
189
299
  parsed,
300
+ explicitExports,
301
+ localImports,
302
+ starExports,
190
303
  scope: new Scope({ this: void 0 }, this.options.scope, importMeta, { functionBoundary: true }),
191
304
  dependencies: /* @__PURE__ */ new Map()
192
305
  };
@@ -212,7 +325,7 @@ var SourceModuleGraph = class {
212
325
  if (source === void 0) throw new TypeError(`Source resolver denied '${specifier}' from '${referrer}'.`);
213
326
  if (typeof source.id !== "string" || source.id.length === 0 || typeof source.source !== "string")
214
327
  throw new TypeError("Source resolver must supply a nonempty identity and source text.");
215
- return this.register(source);
328
+ return this.options.serializePreparation ? this.jobs.run(() => this.register(source)) : this.register(source);
216
329
  })();
217
330
  this.requests.set(key, request);
218
331
  return request;
@@ -322,24 +435,17 @@ var SourceModuleGraph = class {
322
435
  const leave = this.options.budget?.enterCall();
323
436
  try {
324
437
  visited.add(key);
325
- const explicit = record.parsed.exports.find((entry) => {
326
- this.options.budget?.visitNode();
327
- return entry.exported === name;
328
- });
438
+ const explicit = record.explicitExports.get(name);
329
439
  if (explicit?.local !== void 0) {
330
- const imported = record.parsed.imports.find((entry) => {
331
- this.options.budget?.visitNode();
332
- return entry.local === explicit.local;
333
- });
440
+ const imported = record.localImports.get(explicit.local);
334
441
  if (imported === void 0 || imported.namespace === true) return { record, name: explicit.local };
335
442
  return this.resolveDependency(record, imported.request, imported.imported, visited);
336
443
  }
337
444
  if (explicit?.request !== void 0) return this.resolveDependency(record, explicit.request, explicit.imported, visited, explicit.namespace);
338
445
  if (name === "default") return void 0;
339
446
  let resolved;
340
- for (const entry of record.parsed.exports) {
447
+ for (const entry of record.starExports) {
341
448
  this.options.budget?.visitNode();
342
- if (entry.exported !== void 0) continue;
343
449
  const candidate = this.resolveDependency(record, entry.request, name, visited);
344
450
  if (candidate === ambiguous) return ambiguous;
345
451
  if (candidate === void 0) continue;
@@ -466,7 +572,7 @@ var SourceModuleGraph = class {
466
572
  async execute(record, modulePhase, onSuspend) {
467
573
  const result = await interpret(
468
574
  { type: "BlockStatement", body: record.parsed.module.body, span: record.parsed.module.span },
469
- { ...this.options, scope: record.scope, useScopeDirectly: true, jobs: this.jobs, nested: true, modulePhase, onSuspend }
575
+ { ...this.options, scope: record.scope, useScopeDirectly: true, captureSnapshot: false, jobs: this.jobs, nested: true, modulePhase, onSuspend }
470
576
  );
471
577
  this.stats.nodeVisits += result.stats.nodeVisits;
472
578
  if (!result.ok) throw new Error(result.error.message);
@@ -495,6 +601,10 @@ function createReplayableRandom(options = {}) {
495
601
  }
496
602
 
497
603
  // packages/safe-js/src/realm.ts
604
+ var retainedArrayPrototype = Array.prototype;
605
+ var nativeRetainedArrayPrototype = Object.setPrototypeOf;
606
+ var nativeRetainedArrayAppend = Function.prototype.call.bind(Array.prototype.push);
607
+ var freezeSourceReference = Object.freeze;
498
608
  var RealmState = class {
499
609
  constructor(options, queue = new SandboxJobQueue()) {
500
610
  this.options = options;
@@ -622,7 +732,7 @@ var RealmState = class {
622
732
  lease;
623
733
  compilation;
624
734
  controller = new AbortController();
625
- phase = new AsyncLocalStorage();
735
+ phase = new AsyncLocalStorage2();
626
736
  queue;
627
737
  tracker = createSandboxPromiseRejectionTracker();
628
738
  bridge;
@@ -634,6 +744,8 @@ var RealmState = class {
634
744
  resources;
635
745
  callbacks = /* @__PURE__ */ new Map();
636
746
  pendingCallbacks = /* @__PURE__ */ new Set();
747
+ pendingWork = /* @__PURE__ */ new Set();
748
+ releaseReconciliation;
637
749
  callbackCache = /* @__PURE__ */ new WeakMap();
638
750
  hostObjects = /* @__PURE__ */ new Set();
639
751
  guestReferences = /* @__PURE__ */ new Map();
@@ -659,11 +771,17 @@ var RealmState = class {
659
771
  signal: this.controller.signal,
660
772
  realm: this.bridge
661
773
  });
662
- retainedRoots = () => [
663
- ...this.callbacks.values(),
664
- ...Array.from(this.pendingCallbacks, (pending) => pending.closure),
665
- ...this.guestReferences.values()
666
- ];
774
+ retainedRoots = () => {
775
+ const roots = nativeRetainedArrayPrototype([], null);
776
+ for (const object of this.hostObjects) {
777
+ const root = hostObjectGuestRoot(object);
778
+ if (root !== void 0) nativeRetainedArrayAppend(roots, root);
779
+ }
780
+ for (const closure of this.callbacks.values()) nativeRetainedArrayAppend(roots, closure);
781
+ for (const pending of this.pendingCallbacks) nativeRetainedArrayAppend(roots, pending.closure);
782
+ for (const reference of this.guestReferences.values()) nativeRetainedArrayAppend(roots, reference);
783
+ return nativeRetainedArrayPrototype(roots, retainedArrayPrototype);
784
+ };
667
785
  captureArguments = (operation, args, copy) => {
668
786
  const from = this.retainedOperations.get(operation)?.from ?? args.length;
669
787
  const values = copy(args.slice(0, from));
@@ -760,6 +878,34 @@ var RealmState = class {
760
878
  void this.dispose().catch(() => void 0);
761
879
  });
762
880
  }
881
+ trackWork(pending) {
882
+ this.pendingWork.add(pending);
883
+ void pending.then(
884
+ () => {
885
+ this.pendingWork.delete(pending);
886
+ },
887
+ () => {
888
+ this.pendingWork.delete(pending);
889
+ }
890
+ );
891
+ return pending;
892
+ }
893
+ reconcileWhenIdle() {
894
+ if (this.closed || this.active !== void 0 || this.pendingCallbacks.size > 0) return;
895
+ this.releaseReconciliation?.();
896
+ this.releaseReconciliation = void 0;
897
+ try {
898
+ reconcileCompiledValues(
899
+ this.budget,
900
+ [...this.scope?.retainedDataRoots() ?? [], ...this.retainedRoots()],
901
+ this.compilation
902
+ );
903
+ } catch (error) {
904
+ this.poison(error);
905
+ void this.dispose().catch(() => void 0);
906
+ throw error;
907
+ }
908
+ }
763
909
  chargeWork = (units = 1) => {
764
910
  this.assertOpen();
765
911
  if (!Number.isSafeInteger(units) || units < 0)
@@ -858,6 +1004,7 @@ var RealmState = class {
858
1004
  owner: this,
859
1005
  assertActive: this.assertOpen,
860
1006
  chargeWork: this.chargeWork,
1007
+ chargeGuestData: (units) => this.budget.chargeDataUsage(units),
861
1008
  checkLength: (length) => this.budget.allocateArrayLength(length),
862
1009
  checkString: (value) => {
863
1010
  this.budget.allocateString(value);
@@ -960,12 +1107,19 @@ var RealmState = class {
960
1107
  };
961
1108
  invokeCallback = this.executeCallback.bind(this);
962
1109
  async executeCallback(callback, options = {}, completeSynchronous) {
963
- if (this.closed || this.failure !== void 0) await this.dispose();
1110
+ if ((this.closed || this.failure !== void 0) && this.options.callbackScheduling !== "after-prefix")
1111
+ await this.dispose();
964
1112
  this.assertOpen();
965
1113
  const closure = readGuestCallback(callback, this);
966
1114
  this.checkCollection(this.pendingCallbacks.size + 1, this.limits.callbacks, "pending callback");
967
- const record = { closure };
1115
+ const scheduled = this.options.callbackScheduling === "after-prefix";
1116
+ const record = {
1117
+ closure,
1118
+ prefixComplete: false
1119
+ };
968
1120
  this.pendingCallbacks.add(record);
1121
+ if (scheduled) this.releaseReconciliation ??= this.budget.deferReconciliation();
1122
+ const rejectionOwner = scheduled ? this.tracker.startOperation() : void 0;
969
1123
  const invoke = async () => {
970
1124
  this.assertOpen();
971
1125
  readGuestCallback(callback, this);
@@ -996,6 +1150,7 @@ var RealmState = class {
996
1150
  if (isSandboxPromise(value) && value.synchronousPrefix !== void 0)
997
1151
  await value.synchronousPrefix;
998
1152
  this.assertOpen();
1153
+ record.prefixComplete = true;
999
1154
  completeSynchronous?.();
1000
1155
  const settled = await suspendJob(settlement);
1001
1156
  return this.exportValue(settled);
@@ -1005,25 +1160,37 @@ var RealmState = class {
1005
1160
  }
1006
1161
  };
1007
1162
  try {
1008
- const active = this.active !== void 0;
1009
- const pending = withSandboxPromiseRejectionTracker(
1010
- this.tracker,
1011
- () => runResources.run(
1012
- this.resources,
1013
- () => withCancellationSignal(
1014
- this.controller.signal,
1015
- () => active && this.phase.getStore()?.active ? runAsyncPrefix(invoke, completeSynchronous === void 0 && this.phase.getStore()?.extension !== void 0) : this.queue.run(invoke)
1163
+ const active = this.active !== void 0 || scheduled && this.phase.getStore()?.active === true;
1164
+ const execute = () => this.tracker.withOperation(
1165
+ rejectionOwner,
1166
+ () => withSandboxPromiseRejectionTracker(
1167
+ this.tracker,
1168
+ () => runResources.run(
1169
+ this.resources,
1170
+ () => withCancellationSignal(
1171
+ this.controller.signal,
1172
+ () => active && this.phase.getStore()?.active ? runAsyncPrefix(invoke, completeSynchronous === void 0 && this.phase.getStore()?.extension !== void 0) : this.queue.run(invoke)
1173
+ )
1016
1174
  )
1017
1175
  )
1018
1176
  );
1019
- record.promise = active ? pending : this.perform(() => pending);
1020
- return await record.promise;
1177
+ const pending = scheduled && this.sourceGraph !== void 0 ? this.sourceGraph.withoutImportGroup(execute) : execute();
1178
+ record.promise = scheduled ? this.trackWork(pending) : active ? pending : this.perform(() => pending);
1179
+ const result = await record.promise;
1180
+ if (scheduled) {
1181
+ await this.checkUnhandledRejection(rejectionOwner);
1182
+ this.assertOpen();
1183
+ }
1184
+ return result;
1021
1185
  } catch (error) {
1022
- if (error instanceof SandboxError) this.poison(error);
1186
+ if (scheduled || error instanceof SandboxError) this.poison(error);
1187
+ if (scheduled) void this.dispose().catch(() => void 0);
1023
1188
  throw error;
1024
1189
  } finally {
1190
+ this.tracker.finishOperation(rejectionOwner);
1025
1191
  this.pendingCallbacks.delete(record);
1026
- if (!this.closed && this.active === void 0)
1192
+ if (scheduled) this.reconcileWhenIdle();
1193
+ else if (!this.closed && this.active === void 0)
1027
1194
  reconcileCompiledValues(
1028
1195
  this.budget,
1029
1196
  [...this.scope?.retainedDataRoots() ?? [], ...this.retainedRoots()],
@@ -1138,37 +1305,83 @@ var RealmState = class {
1138
1305
  );
1139
1306
  if (this.options.classicScripts)
1140
1307
  this.scope.declare("this", "const", getRealmGlobalObject(this.budget));
1308
+ if (this.options.stringCompilation === "deny") denyGuestStringCompilation(this.scope);
1309
+ }
1310
+ sourceModuleStatus = () => {
1311
+ this.assertOpen();
1312
+ return this.sourceGraph?.sourceModuleStatus() ?? Object.freeze({
1313
+ pendingImports: 0,
1314
+ preparedModules: 0,
1315
+ fulfilledImports: 0,
1316
+ rejectedImports: 0
1317
+ });
1318
+ };
1319
+ ensureSourceGraph() {
1320
+ this.assertOpen();
1321
+ this.initialize();
1322
+ if (this.sourceGraph !== void 0) return this.sourceGraph;
1323
+ const moduleOptions = { ...this.bridgeOptions(), wrappedModules: this.convertedModules };
1324
+ const modules = Object.keys(this.modules).length === 0 && this.convertedModules.size === 0 ? createImmutableEmptyModuleEnvironment(moduleOptions) : createModuleEnvironment(this.modules, moduleOptions);
1325
+ return this.sourceGraph ??= new SourceModuleGraph({
1326
+ resolver: this.options.sourceResolver ?? (() => void 0),
1327
+ scope: this.scope,
1328
+ modules,
1329
+ budget: this.budget,
1330
+ compilation: this.compilation,
1331
+ signal: this.controller.signal,
1332
+ jobs: this.queue,
1333
+ assertActive: this.assertOpen,
1334
+ surfaceUnhandledThrows: true,
1335
+ serializePreparation: this.options.callbackScheduling === "after-prefix",
1336
+ ...this.options.sourceImportTimeoutMs === void 0 ? {} : {
1337
+ importDeadline: {
1338
+ timeoutMs: this.options.sourceImportTimeoutMs,
1339
+ abort: (reason) => {
1340
+ this.poison(reason);
1341
+ void this.close().catch(() => void 0);
1342
+ }
1343
+ }
1344
+ }
1345
+ });
1141
1346
  }
1142
- async evaluateRaw(source, filename = "<realm>", nested = false, sourceType) {
1347
+ async evaluateRaw(source, filename = "<realm>", nested = false, sourceType, reportUnhandledThrows = false) {
1143
1348
  this.assertOpen();
1144
1349
  if (typeof source !== "string") throw new TypeError("Realm source must be a string.");
1145
1350
  if (sourceType === "module") {
1146
- this.initialize();
1147
- this.sourceGraph ??= new SourceModuleGraph({
1148
- resolver: this.options.sourceResolver ?? (() => void 0),
1149
- scope: this.scope,
1150
- modules: createModuleEnvironment(this.modules, { ...this.bridgeOptions(), wrappedModules: this.convertedModules }),
1151
- budget: this.budget,
1152
- compilation: this.compilation,
1153
- signal: this.controller.signal,
1154
- jobs: this.queue,
1155
- assertActive: this.assertOpen,
1156
- surfaceUnhandledThrows: true
1157
- });
1158
- const before = this.sourceGraph.stats.nodeVisits;
1159
- const namespace = await this.sourceGraph.evaluateSource({ id: filename, source }, (module) => {
1160
- this.sourceModuleHash = hashParsedAst(module);
1161
- });
1162
- await this.sourceGraph.settle();
1163
- return { ok: true, returnValue: namespace, snapshot: { bindings: {} }, stats: { nodeVisits: this.sourceGraph.stats.nodeVisits - before, currentDataSize: this.budget.currentDataSize, peakDataSize: this.budget.peakDataSize } };
1351
+ const prepare = () => {
1352
+ this.ensureSourceGraph();
1353
+ };
1354
+ const scheduled = this.options.callbackScheduling === "after-prefix";
1355
+ if (scheduled) await this.queue.run(prepare);
1356
+ else prepare();
1357
+ const graph = this.sourceGraph;
1358
+ const before = graph.stats.nodeVisits;
1359
+ const evaluate = async () => {
1360
+ const namespace2 = await graph.evaluateSource({ id: filename, source }, (module) => {
1361
+ this.sourceModuleHash = hashParsedAst(module);
1362
+ });
1363
+ if (!scheduled) await graph.settle();
1364
+ return namespace2;
1365
+ };
1366
+ const namespace = scheduled ? await graph.withImportGroup(evaluate) : await evaluate();
1367
+ return { ok: true, returnValue: namespace, snapshot: { bindings: {} }, stats: { nodeVisits: graph.stats.nodeVisits - before, currentDataSize: this.budget.currentDataSize, peakDataSize: this.budget.peakDataSize } };
1164
1368
  }
1165
- const script = this.options.classicScripts ? createEvalSource(source, {}, this.lease.owner, filename) : void 0;
1166
- const releaseSource = script ? retainValues(this.budget, () => [script.source]) : void 0;
1369
+ const script = this.options.classicScripts ? createEvalSource(source, {}, this.lease.owner, filename, { compactAst: true }) : void 0;
1370
+ const sourceReference = script && this.options.sourceResolver !== void 0 ? freezeSourceReference({ referrer: filename }) : void 0;
1371
+ const releaseSource = script ? retainValues(this.budget, () => [script.source, sourceReference]) : void 0;
1167
1372
  try {
1168
1373
  if (script) this.budget.chargeDataUsage(measureSandboxData([script.source]));
1374
+ if (sourceReference) this.budget.chargeDataUsage(measureSandboxData([sourceReference]));
1169
1375
  const module = script?.node ?? parseExecutableModule(source, filename, this.lease.owner);
1170
1376
  this.initialize();
1171
- const moduleEnvironment = createModuleEnvironment(this.modules, { ...this.bridgeOptions(), wrappedModules: this.convertedModules });
1377
+ const moduleOptions = { ...this.bridgeOptions(), wrappedModules: this.convertedModules };
1378
+ const moduleEnvironment = script && Object.keys(this.modules).length === 0 && this.convertedModules.size === 0 ? createImmutableEmptyModuleEnvironment(moduleOptions) : createModuleEnvironment(this.modules, moduleOptions);
1379
+ if (sourceReference)
1380
+ attachSourceLoader(
1381
+ moduleEnvironment,
1382
+ (specifier, referrer) => this.ensureSourceGraph().import(specifier, referrer),
1383
+ { preserveImmutableNamespaces: true }
1384
+ );
1172
1385
  const imports = script ? {} : resolveModuleImports(module, this.modules, {
1173
1386
  environment: moduleEnvironment,
1174
1387
  ...this.bridgeOptions(),
@@ -1188,12 +1401,14 @@ var RealmState = class {
1188
1401
  },
1189
1402
  {
1190
1403
  ...script ? { script: { strict: script.strict } } : {},
1404
+ sourceReference,
1191
1405
  scope: this.scope,
1192
1406
  useScopeDirectly: true,
1193
1407
  budget: this.budget,
1194
1408
  compilation: this.compilation,
1195
1409
  signal: this.controller.signal,
1196
1410
  surfaceUnhandledThrows: true,
1411
+ reportUnhandledThrows: script !== void 0 && reportUnhandledThrows,
1197
1412
  jobs: this.queue,
1198
1413
  nested,
1199
1414
  assertActive: this.assertOpen
@@ -1208,7 +1423,7 @@ var RealmState = class {
1208
1423
  evaluateNested = async (source, extension) => {
1209
1424
  this.assertOpen();
1210
1425
  const phase = this.phase.getStore();
1211
- if (!phase?.active || phase.extension !== extension || phase.evaluating || this.active === void 0)
1426
+ if (!phase?.active || phase.extension !== extension || phase.evaluating || this.active === void 0 && !(this.options.callbackScheduling === "after-prefix" && this.pendingCallbacks.size > 0))
1212
1427
  throw new SandboxError("reentry");
1213
1428
  const leave = this.budget.enterCall();
1214
1429
  phase.evaluating = true;
@@ -1231,39 +1446,65 @@ var RealmState = class {
1231
1446
  }
1232
1447
  };
1233
1448
  evaluate = async (source, options = {}) => this.perform(async () => {
1234
- const result = await this.evaluateRaw(source, options.filename, false, options.sourceType);
1449
+ const discard = Object.getOwnPropertyDescriptor(options, "discardResult");
1450
+ if (discard && (!("value" in discard) || discard.value !== void 0 && typeof discard.value !== "boolean"))
1451
+ throw new TypeError("Realm evaluation discardResult must be an own-data boolean.");
1452
+ const scheduled = this.options.callbackScheduling === "after-prefix";
1453
+ const reportUnhandledThrows = options.sourceType !== "module" && this.options.classicScriptErrors === "report";
1454
+ const result = scheduled && options.sourceType !== "module" ? await this.queue.run(() => this.evaluateRaw(source, options.filename, true, void 0, reportUnhandledThrows)) : await this.evaluateRaw(source, options.filename, false, options.sourceType, reportUnhandledThrows);
1235
1455
  if (!result.ok) {
1236
- await this.dispose();
1237
- return { ok: false, error: result.error, stats: result.stats };
1456
+ const recoverable = reportUnhandledThrows && result.error.code === "UNCAUGHT_EXCEPTION";
1457
+ if (!scheduled && !recoverable) await this.dispose();
1458
+ return { ok: false, error: result.error, stats: result.stats, ...recoverable ? { recoverable: true } : {} };
1238
1459
  }
1239
- return { ok: true, returnValue: this.exportValue(result.returnValue), stats: result.stats };
1240
- });
1241
- async perform(task) {
1242
- if (this.closed || this.failure !== void 0) await this.dispose();
1460
+ return { ok: true, ...discard?.value === true ? {} : { returnValue: this.exportValue(result.returnValue) }, stats: result.stats };
1461
+ }, (result) => !result.ok && !result.recoverable);
1462
+ async checkUnhandledRejection(owner) {
1463
+ const unhandled = await this.tracker.findUnhandledRejection(owner);
1464
+ if (unhandled !== void 0) {
1465
+ const error = new Error(
1466
+ `Unhandled guest promise rejection: ${describeThrownValue(unhandled.reason)}`
1467
+ );
1468
+ error.name = "UnhandledRejectionError";
1469
+ throw error;
1470
+ }
1471
+ }
1472
+ async perform(task, failed) {
1473
+ if ((this.closed || this.failure !== void 0) && this.options.callbackScheduling !== "after-prefix")
1474
+ await this.dispose();
1243
1475
  this.assertOpen();
1244
1476
  if (this.active !== void 0) throw new SandboxError("reentry");
1477
+ const scheduled = this.options.callbackScheduling === "after-prefix";
1478
+ if (scheduled && (this.phase.getStore()?.active || [...this.pendingCallbacks].some((record) => !record.prefixComplete)))
1479
+ throw new SandboxError("reentry");
1480
+ const rejectionOwner = scheduled ? this.tracker.startOperation() : void 0;
1245
1481
  const pending = Promise.resolve().then(
1246
- () => withSandboxPromiseRejectionTracker(
1247
- this.tracker,
1248
- () => runResources.run(
1249
- this.resources,
1250
- () => withCancellationSignal(this.controller.signal, task)
1482
+ () => this.tracker.withOperation(
1483
+ rejectionOwner,
1484
+ () => withSandboxPromiseRejectionTracker(
1485
+ this.tracker,
1486
+ () => runResources.run(
1487
+ this.resources,
1488
+ () => withCancellationSignal(this.controller.signal, task)
1489
+ )
1251
1490
  )
1252
1491
  )
1253
1492
  );
1254
1493
  this.active = pending;
1494
+ if (scheduled) this.trackWork(pending);
1255
1495
  try {
1256
1496
  const result = await pending;
1257
- await this.queue.drain();
1258
- const unhandled = await this.tracker.findUnhandledRejection();
1259
- if (unhandled !== void 0) {
1260
- const error = new Error(
1261
- `Unhandled guest promise rejection: ${describeThrownValue(unhandled.reason)}`
1262
- );
1263
- error.name = "UnhandledRejectionError";
1264
- throw error;
1497
+ if (scheduled && failed?.(result)) {
1498
+ this.closed = true;
1499
+ this.controller.abort(new Error("SafeJS source evaluation failed."));
1500
+ await this.dispose();
1501
+ return result;
1265
1502
  }
1503
+ if (scheduled) this.assertOpen();
1504
+ await this.queue.drain();
1505
+ await this.checkUnhandledRejection(rejectionOwner);
1266
1506
  if (this.failure !== void 0) throw this.failure.reason;
1507
+ if (scheduled) this.assertOpen();
1267
1508
  if (!this.closed)
1268
1509
  reconcileCompiledValues(
1269
1510
  this.budget,
@@ -1281,12 +1522,22 @@ var RealmState = class {
1281
1522
  }
1282
1523
  throw reason;
1283
1524
  } finally {
1284
- this.active = void 0;
1525
+ this.tracker.finishOperation(rejectionOwner);
1526
+ if (this.active === pending) this.active = void 0;
1527
+ if (scheduled) this.reconcileWhenIdle();
1285
1528
  }
1286
1529
  }
1287
1530
  close = () => {
1288
1531
  this.closed = true;
1289
1532
  this.controller.abort(new Error("SafeJS realm is closed."));
1533
+ if (this.options.callbackScheduling === "after-prefix") {
1534
+ const disposal = this.dispose();
1535
+ if (this.phase.getStore()?.active) {
1536
+ void disposal.catch(() => void 0);
1537
+ return Promise.reject(new SandboxError("reentry"));
1538
+ }
1539
+ return disposal;
1540
+ }
1290
1541
  if (this.phase.getStore()?.active) return this.dispose();
1291
1542
  return Promise.allSettled([
1292
1543
  this.active,
@@ -1298,6 +1549,21 @@ var RealmState = class {
1298
1549
  this.closed = true;
1299
1550
  this.controller.abort(new Error("SafeJS realm is closed."));
1300
1551
  this.options.signal?.removeEventListener("abort", this.abort);
1552
+ if (this.options.callbackScheduling === "after-prefix") {
1553
+ this.disposal = Promise.resolve().then(async () => {
1554
+ while (this.pendingWork.size > 0) await Promise.allSettled([...this.pendingWork]);
1555
+ await this.sourceGraph?.settle();
1556
+ await this.queue.settle();
1557
+ this.releaseReconciliation?.();
1558
+ this.releaseReconciliation = void 0;
1559
+ await this.releaseResources();
1560
+ }).finally(() => this.queue.finish());
1561
+ return this.disposal;
1562
+ }
1563
+ this.disposal = this.releaseResources().finally(() => this.queue.finish());
1564
+ return this.disposal;
1565
+ }
1566
+ async releaseResources() {
1301
1567
  for (const callback of this.callbacks.keys()) revokeGuestCallback(callback, this);
1302
1568
  this.callbacks.clear();
1303
1569
  for (const object of this.hostObjects) revokeHostObject(object, this);
@@ -1309,28 +1575,25 @@ var RealmState = class {
1309
1575
  this.budget.setRetainedValues(this, void 0);
1310
1576
  this.sourceGraph?.close();
1311
1577
  releaseObjectPrototype(this.budget);
1312
- this.disposal = (async () => {
1313
- const errors = [];
1314
- for (const cleanup of this.cleanups.splice(0).reverse()) {
1315
- try {
1316
- await cleanup();
1317
- } catch (error) {
1318
- errors.push(error);
1319
- }
1578
+ const errors = [];
1579
+ for (const cleanup of this.cleanups.splice(0).reverse()) {
1580
+ try {
1581
+ await cleanup();
1582
+ } catch (error) {
1583
+ errors.push(error);
1320
1584
  }
1321
- this.scope = void 0;
1322
- this.convertedModules.clear();
1323
- for (const key of Object.keys(this.globals)) delete this.globals[key];
1324
- for (const key of Object.keys(this.modules)) delete this.modules[key];
1325
- for (const key of Object.keys(this.builtinBindings))
1326
- Reflect.deleteProperty(this.builtinBindings, key);
1327
- this.nativeConversions.seen = /* @__PURE__ */ new WeakMap();
1328
- reconcileCompiledValues(this.budget, [], this.compilation);
1329
- this.compilation.dispose();
1330
- this.lease.release();
1331
- if (errors.length > 0) throw new AggregateError(errors, "Realm cleanup failed.");
1332
- })().finally(() => this.queue.finish());
1333
- return this.disposal;
1585
+ }
1586
+ this.scope = void 0;
1587
+ this.convertedModules.clear();
1588
+ for (const key of Object.keys(this.globals)) delete this.globals[key];
1589
+ for (const key of Object.keys(this.modules)) delete this.modules[key];
1590
+ for (const key of Object.keys(this.builtinBindings))
1591
+ Reflect.deleteProperty(this.builtinBindings, key);
1592
+ this.nativeConversions.seen = /* @__PURE__ */ new WeakMap();
1593
+ reconcileCompiledValues(this.budget, [], this.compilation);
1594
+ this.compilation.dispose();
1595
+ this.lease.release();
1596
+ if (errors.length > 0) throw new AggregateError(errors, "Realm cleanup failed.");
1334
1597
  }
1335
1598
  };
1336
1599
  function readModules(input) {
@@ -1359,12 +1622,17 @@ function assertNames(actual, expected, label) {
1359
1622
  function createRealm(options = {}) {
1360
1623
  const state = new RealmState(readRealmOptions(options));
1361
1624
  return Object.freeze(attachExecutionControl({
1625
+ stringCompilation: state.options.stringCompilation ?? "allow",
1626
+ classicScriptErrors: state.options.classicScriptErrors ?? "fatal",
1627
+ supportsDiscardResult: true,
1628
+ sourceImportTimeoutMs: state.options.sourceImportTimeoutMs,
1362
1629
  extensions: Object.freeze(state.extensions.map((extension) => extension.manifest)),
1363
1630
  evaluate: state.evaluate,
1364
1631
  startCallback: state.startCallback,
1365
1632
  invokeCallback: state.invokeCallback,
1366
1633
  releaseCallback: state.releaseCallback,
1367
1634
  releaseGuestReference: state.releaseGuestReference,
1635
+ sourceModuleStatus: state.sourceModuleStatus,
1368
1636
  close: state.close
1369
1637
  }, state.queue));
1370
1638
  }
@@ -1374,6 +1642,7 @@ async function runWithExtensions(source, options, jobs) {
1374
1642
  "Live extension runs do not support snapshots or entryPointArgs; use a persistent realm."
1375
1643
  );
1376
1644
  const state = new RealmState(readRealmOptions(options, true), jobs);
1645
+ if (options.sourceLocation !== void 0) state.resources.sourceLocation = options.sourceLocation;
1377
1646
  try {
1378
1647
  const result = await state.perform(() => state.evaluateRaw(source, options.filename, false, options.sourceType));
1379
1648
  if (result.ok && options.sourceType !== "module") encodeReplayData(result.returnValue);
@@ -1415,11 +1684,25 @@ function readRealmOptions(value, oneShot = false) {
1415
1684
  "clock",
1416
1685
  "limits"
1417
1686
  ]);
1687
+ if (!oneShot) supported.add("stringCompilation");
1418
1688
  if (!oneShot) supported.add("classicScripts");
1689
+ if (!oneShot) supported.add("classicScriptErrors");
1690
+ if (!oneShot) supported.add("callbackScheduling");
1691
+ if (!oneShot) supported.add("sourceImportTimeoutMs");
1419
1692
  for (const [key, entry] of Object.entries(options)) {
1420
- if (supported.has(key) || oneShot && (key === "filename" || key === "sourceType" || entry === void 0)) continue;
1693
+ if (supported.has(key) || oneShot && (key === "filename" || key === "sourceType" || key === "sourceLocation" || entry === void 0)) continue;
1421
1694
  throw new TypeError(`Unsupported ${oneShot ? "extension-run" : "realm"} option '${key}'.`);
1422
1695
  }
1696
+ if (options.callbackScheduling !== void 0 && options.callbackScheduling !== "after-prefix")
1697
+ throw new TypeError("Realm callbackScheduling must be 'after-prefix'.");
1698
+ if (options.sourceImportTimeoutMs !== void 0 && (!Number.isSafeInteger(options.sourceImportTimeoutMs) || Number(options.sourceImportTimeoutMs) < 1 || Number(options.sourceImportTimeoutMs) > 2147483647))
1699
+ throw new TypeError("Realm sourceImportTimeoutMs must be an integer between 1 and 2147483647.");
1700
+ if (options.stringCompilation !== void 0 && options.stringCompilation !== "allow" && options.stringCompilation !== "deny")
1701
+ throw new TypeError("Realm stringCompilation must be 'allow' or 'deny'.");
1702
+ if (options.classicScriptErrors !== void 0 && options.classicScriptErrors !== "fatal" && options.classicScriptErrors !== "report")
1703
+ throw new TypeError("Realm classicScriptErrors must be 'fatal' or 'report'.");
1704
+ if (options.classicScriptErrors === "report" && options.classicScripts !== true)
1705
+ throw new TypeError("Reporting classic Script errors requires classicScripts:true.");
1423
1706
  return options;
1424
1707
  }
1425
1708
 
@@ -1940,7 +2223,10 @@ function run(source, options = {}) {
1940
2223
  const operation = budget.acquireCompileOwner(true);
1941
2224
  const compilation = new CompileScope(operation.owner);
1942
2225
  try {
1943
- const restoredSnapshot = options.snapshot === void 0 ? void 0 : restore(options.snapshot, { source }, operation.owner);
2226
+ const restoredSnapshot = options.snapshot === void 0 ? void 0 : restore(options.snapshot, {
2227
+ source,
2228
+ ...options.importSpecifiers ? { importSpecifiers: options.importSpecifiers } : {}
2229
+ }, operation.owner);
1944
2230
  const promiseReplay = new PromiseReplay(restoredSnapshot?.promiseReplay);
1945
2231
  return await promiseReplayContext.run(promiseReplay, async () => {
1946
2232
  const deactivateOtelSink = activateOtelSink(options.otelSink);
@@ -1964,15 +2250,16 @@ function run(source, options = {}) {
1964
2250
  const functionSourceText = executionSemantics === "jobs-v8" || executionSemantics === EXECUTION_SEMANTICS;
1965
2251
  runResources.getStore().functionSourceText = functionSourceText;
1966
2252
  runResources.getStore().hostDataMetadata = executionSemantics === EXECUTION_SEMANTICS;
2253
+ if (options.sourceLocation !== void 0) runResources.getStore().sourceLocation = options.sourceLocation;
1967
2254
  const convertInitialInput = (convert) => executionSemantics === "jobs-v6" ? convert() : promiseReplayContext.exit(convert);
1968
2255
  if (restoredSnapshot !== void 0) {
1969
2256
  leaveSnapshotRun = enterSnapshotRun(options.snapshot ?? restoredSnapshot);
1970
2257
  }
1971
2258
  promiseReplay.attachBudget(budget);
1972
2259
  const filename = options.filename ?? "<input>";
1973
- const module = parseExecutableModule(source, filename, operation.owner);
2260
+ const module = parseExecutableModule(source, filename, operation.owner, options.importSpecifiers);
1974
2261
  promiseReplay.validateNodes(module);
1975
- const sourceHash = findRegexLiteral(module) === void 0 ? hashSource(source, operation.owner, functionSourceText) : hashParsedAst(module, functionSourceText);
2262
+ const sourceHash = findRegexLiteral(module) === void 0 && options.importSpecifiers === void 0 ? hashSource(source, operation.owner, functionSourceText) : hashParsedAst(module, functionSourceText);
1976
2263
  const hostCalls = new HostCallJournal(
1977
2264
  sourceHash,
1978
2265
  readHostCallSnapshot(restoredSnapshot),
@@ -2511,4 +2798,4 @@ export {
2511
2798
  UnhandledRejectionError,
2512
2799
  run
2513
2800
  };
2514
- //# sourceMappingURL=chunk-Q7B6JIMI.js.map
2801
+ //# sourceMappingURL=chunk-M6HUSN7X.js.map