poe-code 13.0.3 → 13.0.4

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 (57) hide show
  1. package/dist/index.js +14418 -13628
  2. package/dist/index.js.map +4 -4
  3. package/dist/metafile.json +1 -1
  4. package/package.json +1 -1
  5. package/packages/safe-js/dist/chunks/{chunk-73LWNNUG.js → chunk-WGS5B4OT.js} +14505 -13744
  6. package/packages/safe-js/dist/chunks/chunk-WGS5B4OT.js.map +7 -0
  7. package/packages/safe-js/dist/chunks/{chunk-QCJ7UAMC.js → chunk-ZGWWEL43.js} +2 -2
  8. package/packages/safe-js/dist/cli.js +2 -2
  9. package/packages/safe-js/dist/core.js +1 -1
  10. package/packages/safe-js/dist/index.d.ts +11 -3
  11. package/packages/safe-js/dist/index.js +14 -6
  12. package/packages/safe-js/dist/index.js.map +4 -4
  13. package/packages/safe-js/dist/interp/async.d.ts +2 -0
  14. package/packages/safe-js/dist/interp/async.js +31 -15
  15. package/packages/safe-js/dist/interp/budget.d.ts +29 -0
  16. package/packages/safe-js/dist/interp/budget.js +143 -0
  17. package/packages/safe-js/dist/interp/globals/console-json.d.ts +2 -1
  18. package/packages/safe-js/dist/interp/globals/console-json.js +24 -6
  19. package/packages/safe-js/dist/interp/globals/regex.d.ts +2 -1
  20. package/packages/safe-js/dist/interp/globals/regex.js +24 -3
  21. package/packages/safe-js/dist/interp/host-bridge.d.ts +2 -1
  22. package/packages/safe-js/dist/interp/host-bridge.js +98 -55
  23. package/packages/safe-js/dist/interp/host-call.d.ts +3 -2
  24. package/packages/safe-js/dist/interp/host-call.js +91 -52
  25. package/packages/safe-js/dist/interp/interpreter.d.ts +4 -1
  26. package/packages/safe-js/dist/interp/interpreter.js +115 -79
  27. package/packages/safe-js/dist/interp/methods/string.d.ts +2 -1
  28. package/packages/safe-js/dist/interp/methods/string.js +111 -100
  29. package/packages/safe-js/dist/interp/regex/compile-guard.d.ts +33 -0
  30. package/packages/safe-js/dist/interp/regex/compile-guard.js +179 -0
  31. package/packages/safe-js/dist/interp/regex/parse.d.ts +2 -1
  32. package/packages/safe-js/dist/interp/regex/parse.js +87 -16
  33. package/packages/safe-js/dist/interp/values.d.ts +7 -2
  34. package/packages/safe-js/dist/interp/values.js +91 -9
  35. package/packages/safe-js/dist/modules/registry.d.ts +2 -1
  36. package/packages/safe-js/dist/modules/registry.js +1 -0
  37. package/packages/safe-js/dist/parse/hash.d.ts +2 -1
  38. package/packages/safe-js/dist/parse/hash.js +7 -4
  39. package/packages/safe-js/dist/parse/parser.d.ts +5 -3
  40. package/packages/safe-js/dist/parse/parser.js +53 -27
  41. package/packages/safe-js/dist/parse/tokenizer.d.ts +2 -0
  42. package/packages/safe-js/dist/parse/tokenizer.js +48 -26
  43. package/packages/safe-js/dist/parse.d.ts +3 -1
  44. package/packages/safe-js/dist/parse.js +3 -1
  45. package/packages/safe-js/dist/restore.d.ts +2 -1
  46. package/packages/safe-js/dist/restore.js +3 -3
  47. package/packages/safe-js/dist/run.js +289 -263
  48. package/packages/safe-js/dist/snapshot/migration.d.ts +3 -2
  49. package/packages/safe-js/dist/snapshot/migration.js +12 -5
  50. package/packages/safe-js/dist/snapshot/replay-data.d.ts +2 -1
  51. package/packages/safe-js/dist/snapshot/replay-data.js +172 -162
  52. package/packages/safe-js/dist/snapshot/replay-inputs.d.ts +2 -1
  53. package/packages/safe-js/dist/snapshot/replay-inputs.js +18 -11
  54. package/packages/safe-js/dist/snapshot/restore.d.ts +2 -2
  55. package/packages/safe-js/dist/snapshot/restore.js +142 -113
  56. package/packages/safe-js/dist/chunks/chunk-73LWNNUG.js.map +0 -7
  57. /package/packages/safe-js/dist/chunks/{chunk-QCJ7UAMC.js.map → chunk-ZGWWEL43.js.map} +0 -0
@@ -1,5 +1,11 @@
1
1
  import { replaceErrorStack } from "../error/shape.js";
2
2
  export const REGEX_STEP_LIMIT = 2_000;
3
+ export const REGEX_COMPILE_LIMITS = Object.freeze({
4
+ sourceLength: 4_096,
5
+ flagsLength: 8,
6
+ depth: 64,
7
+ allocations: 16_384
8
+ });
3
9
  const DEADLINE_CHECK_INTERVAL = 1_024;
4
10
  export class SandboxError extends Error {
5
11
  code;
@@ -42,6 +48,12 @@ export class Budget {
42
48
  retainedDataSize = 0;
43
49
  retainedData = new Map();
44
50
  retainedValueSources = new Map();
51
+ compileGeneration = 0;
52
+ activeCompileOwner;
53
+ compileUses = 0;
54
+ provisionalScopes = 0;
55
+ compileTickets = new Map();
56
+ completedCompileTickets = new Set();
45
57
  constructor(options = {}) {
46
58
  this.deadline = normalizeDeadline(options.deadline);
47
59
  this.limits = Object.freeze({
@@ -122,6 +134,126 @@ export class Budget {
122
134
  for (const values of this.retainedValueSources.values())
123
135
  yield* values();
124
136
  }
137
+ acquireCompileOwner(reset = false, owner) {
138
+ if ((owner !== undefined &&
139
+ (owner.budget !== this || owner.generation !== this.compileGeneration)) ||
140
+ (this.activeCompileOwner !== undefined && this.activeCompileOwner !== owner) ||
141
+ (reset && (this.compileUses !== 0 || owner !== undefined))) {
142
+ throw new SandboxError("reentry");
143
+ }
144
+ if (reset)
145
+ this.reset();
146
+ const selected = owner ?? Object.freeze({ budget: this, generation: this.compileGeneration });
147
+ this.activeCompileOwner = selected;
148
+ this.compileUses += 1;
149
+ let released = false;
150
+ return {
151
+ owner: selected,
152
+ release: () => {
153
+ if (released)
154
+ return;
155
+ released = true;
156
+ this.compileUses -= 1;
157
+ if (this.compileUses === 0)
158
+ this.activeCompileOwner = undefined;
159
+ }
160
+ };
161
+ }
162
+ createCompileTicket(owner) {
163
+ if (owner.budget !== this ||
164
+ owner.generation !== this.compileGeneration ||
165
+ this.activeCompileOwner !== owner) {
166
+ throw new SandboxError("reentry");
167
+ }
168
+ const ticket = Object.freeze({ owner });
169
+ this.compileTickets.set(ticket, 0);
170
+ return ticket;
171
+ }
172
+ compileTicketUsage(ticket) {
173
+ return ticket.owner.generation === this.compileGeneration
174
+ ? (this.compileTickets.get(ticket) ?? 0)
175
+ : 0;
176
+ }
177
+ resizeCompileTicket(ticket, usage) {
178
+ if (ticket.owner.generation !== this.compileGeneration || !this.compileTickets.has(ticket)) {
179
+ throw new SandboxError("reentry");
180
+ }
181
+ this.setRetainedDataUsage(ticket, usage);
182
+ this.compileTickets.set(ticket, usage);
183
+ }
184
+ discardCompileTicket(ticket) {
185
+ if (ticket.owner.generation !== this.compileGeneration)
186
+ return;
187
+ const usage = this.compileTickets.get(ticket);
188
+ if (usage === undefined)
189
+ return;
190
+ this.compileTickets.delete(ticket);
191
+ this.completedCompileTickets.delete(ticket);
192
+ this.retainedData.delete(ticket);
193
+ this.retainedDataSize -= usage;
194
+ this.currentDataSize -= usage;
195
+ }
196
+ reconcileCompileData(usage, included, transferred = included, retainedOwner, complete = false) {
197
+ let includedUsage = 0;
198
+ let transferredUsage = 0;
199
+ let discardedUsage = 0;
200
+ const releasing = [];
201
+ const retained = new Set();
202
+ for (const ticket of included) {
203
+ const charge = this.compileTicketUsage(ticket);
204
+ includedUsage += charge;
205
+ if (charge > 0 &&
206
+ transferred.has(ticket) &&
207
+ (this.provisionalScopes === 0 || retainedOwner !== undefined)) {
208
+ releasing.push(ticket);
209
+ transferredUsage += charge;
210
+ }
211
+ else if (charge > 0 && complete && transferred.has(ticket)) {
212
+ retained.add(ticket);
213
+ }
214
+ }
215
+ if (retainedOwner === undefined) {
216
+ for (const ticket of this.completedCompileTickets) {
217
+ if (included.has(ticket))
218
+ continue;
219
+ releasing.push(ticket);
220
+ discardedUsage += this.compileTicketUsage(ticket);
221
+ }
222
+ }
223
+ const oldOwnerUsage = retainedOwner === undefined ? 0 : (this.retainedData.get(retainedOwner) ?? 0);
224
+ const nextRetained = this.retainedDataSize -
225
+ transferredUsage -
226
+ discardedUsage +
227
+ (retainedOwner === undefined ? 0 : usage - oldOwnerUsage);
228
+ const measured = retainedOwner === undefined
229
+ ? usage - includedUsage + transferredUsage
230
+ : this.currentDataSize - this.retainedDataSize;
231
+ const total = measured + nextRetained;
232
+ this.checkDataUsage(total);
233
+ for (const ticket of releasing) {
234
+ this.compileTickets.delete(ticket);
235
+ this.completedCompileTickets.delete(ticket);
236
+ this.retainedData.delete(ticket);
237
+ }
238
+ if (retainedOwner !== undefined) {
239
+ if (usage === 0)
240
+ this.retainedData.delete(retainedOwner);
241
+ else
242
+ this.retainedData.set(retainedOwner, usage);
243
+ }
244
+ this.retainedDataSize = nextRetained;
245
+ this.currentDataSize = total;
246
+ this.peakDataSize = Math.max(this.peakDataSize, total);
247
+ for (const ticket of retained)
248
+ this.completedCompileTickets.add(ticket);
249
+ return retained;
250
+ }
251
+ chargeDataUsage(usage) {
252
+ const total = this.currentDataSize + usage;
253
+ this.checkDataUsage(total);
254
+ this.currentDataSize = total;
255
+ this.peakDataSize = Math.max(this.peakDataSize, total);
256
+ }
125
257
  provisionDataUsage(usage) {
126
258
  const previous = this.currentDataSize;
127
259
  const previousRetained = this.retainedDataSize;
@@ -129,11 +261,16 @@ export class Budget {
129
261
  this.checkDataUsage(next);
130
262
  this.currentDataSize = next;
131
263
  this.peakDataSize = Math.max(this.peakDataSize, next);
264
+ const generation = this.compileGeneration;
265
+ this.provisionalScopes += 1;
132
266
  let released = false;
133
267
  return () => {
134
268
  if (released)
135
269
  return;
136
270
  released = true;
271
+ if (generation !== this.compileGeneration)
272
+ return;
273
+ this.provisionalScopes -= 1;
137
274
  this.currentDataSize = previous + this.retainedDataSize - previousRetained;
138
275
  };
139
276
  }
@@ -144,6 +281,12 @@ export class Budget {
144
281
  return this.enterDepth();
145
282
  }
146
283
  reset() {
284
+ if (this.compileUses !== 0)
285
+ throw new SandboxError("reentry");
286
+ this.compileGeneration += 1;
287
+ this.provisionalScopes = 0;
288
+ this.compileTickets.clear();
289
+ this.completedCompileTickets.clear();
147
290
  this.stepsUsed = 0;
148
291
  this.peakCallDepth = 0;
149
292
  this.currentCallDepth = 0;
@@ -1,4 +1,4 @@
1
- import type { Budget } from "../budget.js";
1
+ import type { Budget, CompileOwner } from "../budget.js";
2
2
  import type { HostCallJournal } from "../host-call.js";
3
3
  import { type SandboxObject } from "../values.js";
4
4
  export type ConsoleSink = {
@@ -7,6 +7,7 @@ export type ConsoleSink = {
7
7
  };
8
8
  export type ConsoleJsonGlobalsOptions = {
9
9
  budget: Budget;
10
+ compileOwner?: CompileOwner;
10
11
  sink?: ConsoleSink;
11
12
  hostCalls?: HostCallJournal;
12
13
  };
@@ -1,3 +1,4 @@
1
+ import { CompileScope } from "../regex/compile-guard.js";
1
2
  import { wrapCallerInjectedBindings } from "../host-bridge.js";
2
3
  import { allocateProducedSandboxValue, createSandboxClosure, deepCopyFromSandbox, isSandboxClosure, isSandboxPromise } from "../values.js";
3
4
  export function createConsoleJsonGlobals(options) {
@@ -19,17 +20,33 @@ export function createConsoleJsonGlobals(options) {
19
20
  ? {
20
21
  error: createSandboxClosure({
21
22
  sandbox: true,
22
- call: async (args) => {
23
- sink.error(...args.map((value) => deepCopyFromSandbox(value)));
24
- return undefined;
23
+ call: async (args, context) => {
24
+ const operation = options.budget.acquireCompileOwner(false, options.compileOwner ?? context?.compilation?.owner);
25
+ const compilation = new CompileScope(operation.owner);
26
+ try {
27
+ sink.error(...args.map((value) => deepCopyFromSandbox(value, { compilation })));
28
+ return undefined;
29
+ }
30
+ finally {
31
+ compilation.dispose();
32
+ operation.release();
33
+ }
25
34
  },
26
35
  name: "error"
27
36
  }),
28
37
  log: createSandboxClosure({
29
38
  sandbox: true,
30
- call: async (args) => {
31
- sink.log(...args.map((value) => deepCopyFromSandbox(value)));
32
- return undefined;
39
+ call: async (args, context) => {
40
+ const operation = options.budget.acquireCompileOwner(false, options.compileOwner ?? context?.compilation?.owner);
41
+ const compilation = new CompileScope(operation.owner);
42
+ try {
43
+ sink.log(...args.map((value) => deepCopyFromSandbox(value, { compilation })));
44
+ return undefined;
45
+ }
46
+ finally {
47
+ compilation.dispose();
48
+ operation.release();
49
+ }
33
50
  },
34
51
  name: "log"
35
52
  })
@@ -45,6 +62,7 @@ export function createConsoleJsonGlobals(options) {
45
62
  }
46
63
  }, {
47
64
  budget: options.budget,
65
+ compileOwner: options.compileOwner,
48
66
  hostCalls: options.hostCalls,
49
67
  moduleId: "<console>"
50
68
  })
@@ -1,4 +1,5 @@
1
1
  import { type SandboxClosure } from "../values.js";
2
- export declare function createRegexGlobals(): {
2
+ import { type CompileOwner } from "../budget.js";
3
+ export declare function createRegexGlobals(owner?: CompileOwner): {
3
4
  RegExp: SandboxClosure;
4
5
  };
@@ -1,6 +1,27 @@
1
- import { createSandboxClosure, createSandboxRegex } from "../values.js";
2
- export function createRegexGlobals() {
3
- const construct = (args) => createSandboxRegex(String(args[0] ?? ""), String(args[1] ?? ""));
1
+ import { createSandboxClosure, createSandboxRegex, reconcileCompiledValues } from "../values.js";
2
+ import { CompileScope } from "../regex/compile-guard.js";
3
+ import { SandboxError } from "../budget.js";
4
+ export function createRegexGlobals(owner) {
5
+ const construct = (args, context) => {
6
+ const selected = owner ?? context?.compilation?.owner;
7
+ if (context?.compilation?.owner !== undefined && selected !== context.compilation.owner) {
8
+ throw new SandboxError("reentry");
9
+ }
10
+ const operation = selected?.budget.acquireCompileOwner(false, selected);
11
+ const compilation = context?.compilation ?? new CompileScope(selected);
12
+ try {
13
+ const regex = createSandboxRegex(String(args[0] ?? ""), String(args[1] ?? ""), 0, compilation);
14
+ if (context?.compilation === undefined && selected !== undefined) {
15
+ reconcileCompiledValues(selected.budget, [regex], compilation);
16
+ }
17
+ return regex;
18
+ }
19
+ finally {
20
+ if (context?.compilation === undefined)
21
+ compilation.dispose();
22
+ operation?.release();
23
+ }
24
+ };
4
25
  return {
5
26
  RegExp: createSandboxClosure({ sandbox: true, name: "RegExp", call: construct, construct })
6
27
  };
@@ -1,4 +1,4 @@
1
- import { type Budget } from "./budget.js";
1
+ import { type Budget, type CompileOwner } from "./budget.js";
2
2
  import { type PendingHostCallPolicyMode } from "../snapshot/policy.js";
3
3
  import { type HostCallJournal, type HostCallOutcome } from "./host-call.js";
4
4
  import { type SandboxClosure, type SandboxValue } from "./values.js";
@@ -10,6 +10,7 @@ type HostBridgeOptions = {
10
10
  registerCapabilities?: boolean;
11
11
  capabilityPath?: readonly string[];
12
12
  budget: Budget;
13
+ compileOwner?: CompileOwner;
13
14
  hostCalls?: HostCallJournal;
14
15
  moduleId?: string;
15
16
  operation?: string;
@@ -1,6 +1,7 @@
1
1
  import { normalizeClosureResult } from "./async.js";
2
2
  import { attachErrorSpan, replaceErrorStack } from "../error/shape.js";
3
3
  import { SandboxError } from "./budget.js";
4
+ import { CompileScope } from "./regex/compile-guard.js";
4
5
  import { checkFloat32Allocation, copyFloat32Storage, float32DataProperties, float32Storage, isFloat32Array } from "./float32.js";
5
6
  import { createSubsetErrorValue } from "./exceptions.js";
6
7
  import { bindOtelSpan, getBoundOtelSpan } from "../observability/otel.js";
@@ -31,16 +32,22 @@ export function readHostOperationPolicy(operation) {
31
32
  return hostOperationPolicies.get(operation);
32
33
  }
33
34
  export function wrapCallerInjectedBindings(bindings, options) {
34
- options = { ...options, registerCapabilities: true };
35
- const state = { seen: new WeakMap() };
36
- const copied = Object.fromEntries(Object.entries(bindings).map(([name, value]) => [
37
- name,
38
- typeof value === "function"
39
- ? wrapCallerInjectedFunction(name, value, { ...options, capabilityPath: [name] }, state)
40
- : copyHostValueToSandbox(value, [], { ...options, operation: name, capabilityPath: [name] }, state, "<root>")
41
- ]));
42
- options.budget.provisionDataUsage(measureSandboxData(Object.values(copied)));
43
- return copied;
35
+ const operation = options.budget.acquireCompileOwner(false, options.compileOwner);
36
+ options = { ...options, registerCapabilities: true, compileOwner: operation.owner };
37
+ try {
38
+ const state = { seen: new WeakMap() };
39
+ const copied = Object.fromEntries(Object.entries(bindings).map(([name, value]) => [
40
+ name,
41
+ typeof value === "function"
42
+ ? wrapCallerInjectedFunction(name, value, { ...options, capabilityPath: [name] }, state)
43
+ : copyHostValueToSandbox(value, [], { ...options, operation: name, capabilityPath: [name] }, state, "<root>")
44
+ ]));
45
+ options.budget.chargeDataUsage(measureSandboxData(Object.values(copied)));
46
+ return copied;
47
+ }
48
+ finally {
49
+ operation.release();
50
+ }
44
51
  }
45
52
  function wrapCallerInjectedFunction(name, value, options, state) {
46
53
  const existing = state.seen.get(value) ?? options.hostCalls?.nativeClosures.get(value);
@@ -52,6 +59,8 @@ function wrapCallerInjectedFunction(name, value, options, state) {
52
59
  ...(isAsyncFunction(callable) ? { async: true } : {}),
53
60
  cancellationSignal: options.signal,
54
61
  call: (args, context) => {
62
+ const operationLease = options.budget.acquireCompileOwner(false, options.compileOwner);
63
+ const compilation = new CompileScope(operationLease.owner);
55
64
  try {
56
65
  if (options.signal?.aborted)
57
66
  throw readAbortReason(options.signal);
@@ -67,7 +76,8 @@ function wrapCallerInjectedFunction(name, value, options, state) {
67
76
  restored: []
68
77
  };
69
78
  const hostArgs = deepCopyFromSandbox([...args], {
70
- wrapClosure: (closure) => wrapSandboxClosureForHost(closure, stackFrames, options.budget, callbacks)
79
+ compilation,
80
+ wrapClosure: (closure) => wrapSandboxClosureForHost(closure, stackFrames, options.budget, operationLease.owner, callbacks)
71
81
  });
72
82
  const hostCalls = options.hostCalls;
73
83
  const operation = options.operation ?? bindingName;
@@ -112,13 +122,27 @@ function wrapCallerInjectedFunction(name, value, options, state) {
112
122
  return {
113
123
  token,
114
124
  start: () => {
115
- const callback = callbacks.entries.get(invocation.id);
116
- if (callback === undefined)
117
- throw new TypeError("Missing restored host callback.");
118
- const args = decodeReplayData(invocation.arguments, {
119
- resolveCapability: hostCalls.resolveCapability
120
- });
121
- void callback(args, token).then(resolve, reject);
125
+ const callbackLease = options.budget.acquireCompileOwner(false, options.compileOwner);
126
+ const callbackCompilation = new CompileScope(callbackLease.owner);
127
+ try {
128
+ const callback = callbacks.entries.get(invocation.id);
129
+ if (callback === undefined)
130
+ throw new TypeError("Missing restored host callback.");
131
+ const args = decodeReplayData(invocation.arguments, {
132
+ resolveCapability: hostCalls.resolveCapability
133
+ }, callbackCompilation);
134
+ void callback(args, token)
135
+ .then(resolve, reject)
136
+ .finally(() => {
137
+ callbackCompilation.dispose();
138
+ callbackLease.release();
139
+ });
140
+ }
141
+ catch (error) {
142
+ callbackCompilation.dispose();
143
+ callbackLease.release();
144
+ throw error;
145
+ }
122
146
  }
123
147
  };
124
148
  }));
@@ -128,12 +152,17 @@ function wrapCallerInjectedFunction(name, value, options, state) {
128
152
  return executeHostCall(issued.record, issued.restored, () => invokeHostCallback(() => Reflect.apply(callable, undefined, hostArgs), options), stackFrames, options, context?.span, callbacks, hostOperationReplayHandlers.get(value)?.bind(undefined, hostArgs));
129
153
  }
130
154
  catch (error) {
155
+ compilation.dispose();
131
156
  if (isFatalBridgeError(error) ||
132
157
  (typeof error === "object" && error !== null && sandboxErrorTypes.has(error))) {
133
158
  throw error;
134
159
  }
135
160
  throw createHostErrorValue(error, context?.stack ?? [], options.budget, context?.span);
136
161
  }
162
+ finally {
163
+ compilation.dispose();
164
+ operationLease.release();
165
+ }
137
166
  },
138
167
  name: bindingName,
139
168
  properties: (closure) => {
@@ -345,7 +374,7 @@ function copyHostResultToSandbox(result, stackFrames, options) {
345
374
  const value = copyHostValueToSandbox(result, stackFrames, { ...options, registerCapabilities: false }, {
346
375
  seen: new WeakMap()
347
376
  }, "<root>");
348
- options.budget.provisionDataUsage(measureSandboxData([value]));
377
+ options.budget.chargeDataUsage(measureSandboxData([value]));
349
378
  return value;
350
379
  }
351
380
  function createHostErrorValue(reason, stackFrames, budget, span, state = { seen: new WeakMap() }, chargeBudget = false) {
@@ -375,7 +404,7 @@ function createHostErrorValue(reason, stackFrames, budget, span, state = { seen:
375
404
  if (data !== undefined) {
376
405
  const copied = copyHostValueToSandbox(data, stackFrames, { budget, errorData: true }, state, "<error>");
377
406
  Object.defineProperties(error, Object.getOwnPropertyDescriptors(copied));
378
- budget.provisionDataUsage(measureSandboxData([error]));
407
+ budget.chargeDataUsage(measureSandboxData([error]));
379
408
  }
380
409
  }
381
410
  attachErrorSpan(error, span);
@@ -400,21 +429,24 @@ function copyHostErrorMetadata(error, reason, budget, chargeBudget) {
400
429
  resumeChecks?.();
401
430
  }
402
431
  }
403
- function wrapSandboxClosureForHost(closure, stackFrames, budget, callbacks) {
432
+ function wrapSandboxClosureForHost(closure, stackFrames, budget, compileOwner, callbacks) {
404
433
  const existing = callbacks?.seen.get(closure);
405
434
  if (existing !== undefined)
406
435
  return existing;
407
436
  const id = (callbacks?.entries.size ?? 0) + 1;
408
437
  const invoke = async (sandboxArgs, token) => {
438
+ const operation = budget.acquireCompileOwner(false, compileOwner);
439
+ const compilation = new CompileScope(operation.owner);
409
440
  let leaveRunning;
410
441
  let leaveCall;
411
- const wrapClosure = (nestedClosure) => wrapSandboxClosureForHost(nestedClosure, stackFrames, budget, callbacks);
442
+ const wrapClosure = (nestedClosure) => wrapSandboxClosureForHost(nestedClosure, stackFrames, budget, compileOwner, callbacks);
412
443
  try {
413
444
  leaveRunning = enterRunningState(closure);
414
445
  leaveCall = budget.enterCall();
415
446
  let result;
416
447
  try {
417
448
  result = closure.call(sandboxArgs, {
449
+ compilation,
418
450
  stack: stackFrames,
419
451
  thisValue: undefined
420
452
  });
@@ -422,12 +454,14 @@ function wrapSandboxClosureForHost(closure, stackFrames, budget, callbacks) {
422
454
  catch (error) {
423
455
  if (isSandboxLikeValue(error)) {
424
456
  throw deepCopyFromSandbox(error, {
457
+ compilation,
425
458
  wrapClosure
426
459
  });
427
460
  }
428
461
  throw error;
429
462
  }
430
463
  return await deepCopyFromSandbox(normalizeClosureResult(result, budget), {
464
+ compilation,
431
465
  wrapClosure
432
466
  });
433
467
  }
@@ -437,6 +471,8 @@ function wrapSandboxClosureForHost(closure, stackFrames, budget, callbacks) {
437
471
  throw error;
438
472
  }
439
473
  finally {
474
+ compilation.dispose();
475
+ operation.release();
440
476
  leaveCall?.();
441
477
  leaveRunning?.();
442
478
  if (token !== undefined)
@@ -444,41 +480,48 @@ function wrapSandboxClosureForHost(closure, stackFrames, budget, callbacks) {
444
480
  }
445
481
  };
446
482
  const wrapped = async (...args) => {
447
- const sandboxArgs = copyHostValueToSandbox([...args], stackFrames, {
448
- budget,
449
- hostCalls: callbacks?.journal
450
- }, { seen: new WeakMap() }, "<callback>");
451
- const restored = callbacks?.nextReissuedInvocation === undefined
452
- ? undefined
453
- : callbacks.restored[callbacks.nextReissuedInvocation++];
454
- if (restored !== undefined) {
455
- if (restored.id !== id ||
456
- JSON.stringify(encodeReplayData(sandboxArgs, {
457
- identifyCapability: callbacks?.journal?.identifyCapability
458
- })) !== JSON.stringify(restored.arguments)) {
459
- const error = new HostCallResumabilityError(callbacks.record, "external-reconciliation", "Host callback arguments changed while re-issuing the operation; external reconciliation is required.");
460
- promiseReplayContext.getStore()?.fail(error);
461
- throw error;
483
+ const operation = budget.acquireCompileOwner(false, compileOwner);
484
+ try {
485
+ const sandboxArgs = copyHostValueToSandbox([...args], stackFrames, {
486
+ budget,
487
+ compileOwner,
488
+ hostCalls: callbacks?.journal
489
+ }, { seen: new WeakMap() }, "<callback>");
490
+ const restored = callbacks?.nextReissuedInvocation === undefined
491
+ ? undefined
492
+ : callbacks.restored[callbacks.nextReissuedInvocation++];
493
+ if (restored !== undefined) {
494
+ if (restored.id !== id ||
495
+ JSON.stringify(encodeReplayData(sandboxArgs, {
496
+ identifyCapability: callbacks?.journal?.identifyCapability
497
+ })) !== JSON.stringify(restored.arguments)) {
498
+ const error = new HostCallResumabilityError(callbacks.record, "external-reconciliation", "Host callback arguments changed while re-issuing the operation; external reconciliation is required.");
499
+ promiseReplayContext.getStore()?.fail(error);
500
+ throw error;
501
+ }
502
+ return await restored.result;
462
503
  }
463
- return await restored.result;
504
+ const replay = promiseReplayContext.getStore();
505
+ const catchUp = replay?.waitForLiveExecution();
506
+ if (catchUp !== undefined)
507
+ await catchUp;
508
+ const token = callbacks?.record === undefined
509
+ ? undefined
510
+ : callbacks.journal?.recordCallback(callbacks.record, id, sandboxArgs, replay?.currentStep ?? 0);
511
+ if (token !== undefined)
512
+ replay?.beginCallback(token);
513
+ const pending = invoke(sandboxArgs, token);
514
+ callbacks?.active.add(pending);
515
+ void pending.then(() => {
516
+ callbacks?.active.delete(pending);
517
+ }, () => {
518
+ callbacks?.active.delete(pending);
519
+ });
520
+ return await pending;
521
+ }
522
+ finally {
523
+ operation.release();
464
524
  }
465
- const replay = promiseReplayContext.getStore();
466
- const catchUp = replay?.waitForLiveExecution();
467
- if (catchUp !== undefined)
468
- await catchUp;
469
- const token = callbacks?.record === undefined
470
- ? undefined
471
- : callbacks.journal?.recordCallback(callbacks.record, id, sandboxArgs, replay?.currentStep ?? 0);
472
- if (token !== undefined)
473
- replay?.beginCallback(token);
474
- const pending = invoke(sandboxArgs, token);
475
- callbacks?.active.add(pending);
476
- void pending.then(() => {
477
- callbacks?.active.delete(pending);
478
- }, () => {
479
- callbacks?.active.delete(pending);
480
- });
481
- return pending;
482
525
  };
483
526
  if (closure.length !== undefined)
484
527
  Object.defineProperty(wrapped, "length", { value: closure.length });
@@ -1,5 +1,5 @@
1
1
  import { type SandboxClosure, type SandboxValue } from "./values.js";
2
- import type { Budget } from "./budget.js";
2
+ import type { Budget, CompileOwner } from "./budget.js";
3
3
  import { type ReplayData } from "../snapshot/replay-data.js";
4
4
  import { type PendingHostCallPolicyMode } from "../snapshot/policy.js";
5
5
  export type HostCallLifecycle = "created" | "running" | "settled" | "consumed" | "cancelled";
@@ -77,6 +77,7 @@ export declare class HostCallJournal {
77
77
  private readonly sourceHash;
78
78
  private readonly resumeProvider?;
79
79
  private budget?;
80
+ private compileOwner?;
80
81
  readonly runId: string;
81
82
  private nextCall;
82
83
  private readonly records;
@@ -96,7 +97,7 @@ export declare class HostCallJournal {
96
97
  private readonly capabilityWaiters;
97
98
  readonly identifyCapability: (key: SandboxClosure) => string | undefined;
98
99
  readonly resolveCapability: (id: string) => SandboxClosure;
99
- constructor(sourceHash: string, records?: readonly HostCallRecord[], resumeProvider?: HostCallResumeProvider | undefined, replay?: unknown, budget?: Budget | undefined);
100
+ constructor(sourceHash: string, records?: readonly HostCallRecord[], resumeProvider?: HostCallResumeProvider | undefined, replay?: unknown, budget?: Budget | undefined, compileOwner?: CompileOwner | undefined);
100
101
  issue(input: {
101
102
  moduleId: string;
102
103
  operation: string;