poe-code 13.0.3 → 13.0.5

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 +14421 -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-BI5UBZ7A.js} +14508 -13744
  6. package/packages/safe-js/dist/chunks/chunk-BI5UBZ7A.js.map +7 -0
  7. package/packages/safe-js/dist/chunks/{chunk-QCJ7UAMC.js → chunk-FH5X7CUS.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 +95 -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-FH5X7CUS.js.map} +0 -0
@@ -1,20 +1,37 @@
1
- export function parseRegex(source, flags = "") {
2
- const parsedFlags = parseFlags(flags);
3
- const parser = new RegexParser(source);
4
- const body = parser.parse();
5
- return {
6
- source,
7
- flags: parsedFlags,
8
- captureCount: parser.captureCount,
9
- body
10
- };
1
+ import { RegexCompileGuard } from "./compile-guard.js";
2
+ export function parseRegex(source, flags = "", compilation, valueUnits = 0) {
3
+ const guard = new RegexCompileGuard(compilation);
4
+ try {
5
+ guard.checkLength(source.length);
6
+ guard.checkLength(flags.length, true);
7
+ guard.allocate(5 + valueUnits);
8
+ const parsedFlags = parseFlags(flags, guard);
9
+ const parser = new RegexParser(source, guard);
10
+ const body = parser.parse();
11
+ guard.allocate(5 + source.length);
12
+ const pattern = { source, flags: parsedFlags, captureCount: parser.captureCount, body };
13
+ guard.retain(pattern, valueUnits);
14
+ return pattern;
15
+ }
16
+ finally {
17
+ guard.close();
18
+ }
11
19
  }
12
20
  class RegexParser {
13
21
  source;
22
+ guard;
14
23
  captureCount = 0;
15
- position = 0;
16
- constructor(source) {
24
+ cursor = 0;
25
+ constructor(source, guard) {
17
26
  this.source = source;
27
+ this.guard = guard;
28
+ }
29
+ get position() {
30
+ return this.cursor;
31
+ }
32
+ set position(next) {
33
+ this.guard.work(Math.max(0, next - this.cursor));
34
+ this.cursor = next;
18
35
  }
19
36
  parse() {
20
37
  const body = this.parseAlternation();
@@ -27,22 +44,34 @@ class RegexParser {
27
44
  return body;
28
45
  }
29
46
  parseAlternation() {
47
+ this.guard.allocate(1);
48
+ this.guard.array(1);
30
49
  const alternatives = [this.parseSequence()];
31
50
  while (this.peek() === "|") {
32
51
  this.position += 1;
52
+ this.guard.array(alternatives.length + 1);
33
53
  alternatives.push(this.parseSequence());
34
54
  }
35
- return alternatives.length === 1 ? alternatives[0] : { type: "alternation", alternatives };
55
+ if (alternatives.length === 1)
56
+ return alternatives[0];
57
+ this.guard.allocate(3);
58
+ return { type: "alternation", alternatives };
36
59
  }
37
60
  parseSequence() {
61
+ this.guard.allocate(1);
38
62
  const elements = [];
39
63
  while (!this.atEnd() && this.peek() !== ")" && this.peek() !== "|") {
64
+ this.guard.array(elements.length + 1);
40
65
  elements.push(this.parseQuantifiedAtom());
41
66
  }
42
67
  if (elements.length === 0) {
68
+ this.guard.allocate(2);
43
69
  return { type: "empty" };
44
70
  }
45
- return elements.length === 1 ? elements[0] : { type: "sequence", elements };
71
+ if (elements.length === 1)
72
+ return elements[0];
73
+ this.guard.allocate(3);
74
+ return { type: "sequence", elements };
46
75
  }
47
76
  parseQuantifiedAtom() {
48
77
  const quantifierStart = this.position;
@@ -62,16 +91,20 @@ class RegexParser {
62
91
  if (!greedy) {
63
92
  this.position += 1;
64
93
  }
94
+ this.guard.allocate(Object.hasOwn(quantifier, "max") ? 6 : 5);
65
95
  return { type: "quantifier", body, ...quantifier, greedy };
66
96
  }
67
97
  parseAtom() {
68
98
  const character = this.take();
69
99
  switch (character) {
70
100
  case ".":
101
+ this.guard.allocate(2);
71
102
  return { type: "dot" };
72
103
  case "^":
104
+ this.guard.allocate(3);
73
105
  return { type: "anchor", kind: "start" };
74
106
  case "$":
107
+ this.guard.allocate(3);
75
108
  return { type: "anchor", kind: "end" };
76
109
  case "(":
77
110
  return this.parseGroup(this.position - 1);
@@ -80,12 +113,15 @@ class RegexParser {
80
113
  case "\\":
81
114
  return this.parseEscape(false, this.position - 1);
82
115
  default:
116
+ this.guard.allocate(3 + character.length);
83
117
  return { type: "literal", value: character };
84
118
  }
85
119
  }
86
120
  parseGroup(start) {
87
121
  let capturing = true;
88
122
  if (this.peek() === "?") {
123
+ this.guard.allocate(Math.min(3, this.source.length - this.position));
124
+ this.guard.work(Math.min(3, this.source.length - this.position));
89
125
  const extension = this.source.slice(this.position, this.position + 3);
90
126
  if (extension.startsWith("?:")) {
91
127
  capturing = false;
@@ -104,12 +140,22 @@ class RegexParser {
104
140
  this.fail("Unsupported group construct", start);
105
141
  }
106
142
  }
143
+ if (capturing)
144
+ this.guard.allocate(1);
107
145
  const index = capturing ? ++this.captureCount : undefined;
108
- const body = this.parseAlternation();
146
+ this.guard.enterGroup();
147
+ let body;
148
+ try {
149
+ body = this.parseAlternation();
150
+ }
151
+ finally {
152
+ this.guard.leaveGroup();
153
+ }
109
154
  if (this.peek() !== ")") {
110
155
  this.fail("Unterminated group", start);
111
156
  }
112
157
  this.position += 1;
158
+ this.guard.allocate(5);
113
159
  return { type: "group", capturing, index, body };
114
160
  }
115
161
  parseCharacterClass(start) {
@@ -117,12 +163,15 @@ class RegexParser {
117
163
  if (negated) {
118
164
  this.position += 1;
119
165
  }
166
+ this.guard.allocate(1);
120
167
  const items = [];
121
168
  while (!this.atEnd()) {
122
169
  if (this.peek() === "]") {
123
170
  this.position += 1;
171
+ this.guard.allocate(4);
124
172
  return { type: "characterClass", negated, items };
125
173
  }
174
+ this.guard.array(items.length + 1);
126
175
  const left = this.parseClassItem(start);
127
176
  if (this.peek() === "-" && this.source[this.position + 1] !== "]") {
128
177
  const rangePosition = this.position;
@@ -134,6 +183,7 @@ class RegexParser {
134
183
  if (left.value.charCodeAt(0) > right.value.charCodeAt(0)) {
135
184
  this.fail("Character class range is out of order", rangePosition);
136
185
  }
186
+ this.guard.allocate(4 + left.value.length + right.value.length);
137
187
  items.push({ type: "range", from: left.value, to: right.value });
138
188
  }
139
189
  else {
@@ -151,6 +201,7 @@ class RegexParser {
151
201
  this.position += 1;
152
202
  const escaped = this.parseEscape(true, escapeStart);
153
203
  if (escaped.type === "literal") {
204
+ this.guard.allocate(3 + escaped.value.length);
154
205
  return { type: "character", value: escaped.value };
155
206
  }
156
207
  if (escaped.type === "characterClass" && escaped.items.length === 1) {
@@ -158,6 +209,7 @@ class RegexParser {
158
209
  }
159
210
  this.fail("Unsupported character class escape", escapeStart);
160
211
  }
212
+ this.guard.allocate(4);
161
213
  return { type: "character", value: this.take() };
162
214
  }
163
215
  parseEscape(inCharacterClass, start) {
@@ -172,11 +224,14 @@ class RegexParser {
172
224
  this.fail("Unicode property escapes are not supported", start);
173
225
  }
174
226
  if (escaped === "x") {
227
+ this.guard.allocate(4);
175
228
  return { type: "literal", value: this.parseHexEscape(2, "hexadecimal", start) };
176
229
  }
177
230
  if (escaped === "u") {
231
+ this.guard.allocate(4);
178
232
  return { type: "literal", value: this.parseHexEscape(4, "Unicode", start) };
179
233
  }
234
+ this.guard.allocate(25);
180
235
  const kinds = {
181
236
  d: { kind: "digit", negated: false },
182
237
  D: { kind: "digit", negated: true },
@@ -187,11 +242,15 @@ class RegexParser {
187
242
  };
188
243
  const kind = kinds[escaped];
189
244
  if (kind !== undefined) {
245
+ this.guard.allocate(9);
246
+ this.guard.array(1);
190
247
  return { type: "characterClass", negated: false, items: [{ type: "kind", ...kind }] };
191
248
  }
192
249
  if (!inCharacterClass && (escaped === "b" || escaped === "B")) {
250
+ this.guard.allocate(3);
193
251
  return { type: "wordBoundary", negated: escaped === "B" };
194
252
  }
253
+ this.guard.allocate(8);
195
254
  const controls = {
196
255
  b: "\b",
197
256
  f: "\f",
@@ -201,10 +260,13 @@ class RegexParser {
201
260
  v: "\v",
202
261
  "0": "\0"
203
262
  };
263
+ this.guard.allocate(4);
204
264
  return { type: "literal", value: controls[escaped] ?? escaped };
205
265
  }
206
266
  parseHexEscape(length, name, start) {
207
267
  const end = this.position + length;
268
+ this.guard.allocate(Math.min(length, this.source.length - this.position));
269
+ this.guard.work(Math.min(length, this.source.length - this.position));
208
270
  const digits = this.source.slice(this.position, end);
209
271
  if (digits.length !== length || !allHexDigits(digits)) {
210
272
  this.fail(`Invalid ${name} escape`, start);
@@ -216,14 +278,17 @@ class RegexParser {
216
278
  const character = this.peek();
217
279
  if (character === "*") {
218
280
  this.position += 1;
281
+ this.guard.allocate(2);
219
282
  return { min: 0 };
220
283
  }
221
284
  if (character === "+") {
222
285
  this.position += 1;
286
+ this.guard.allocate(2);
223
287
  return { min: 1 };
224
288
  }
225
289
  if (character === "?") {
226
290
  this.position += 1;
291
+ this.guard.allocate(3);
227
292
  return { min: 0, max: 1 };
228
293
  }
229
294
  if (character !== "{") {
@@ -238,6 +303,7 @@ class RegexParser {
238
303
  }
239
304
  if (this.peek() === "}") {
240
305
  this.position += 1;
306
+ this.guard.allocate(3);
241
307
  return { min, max: min };
242
308
  }
243
309
  if (this.peek() !== ",") {
@@ -252,6 +318,7 @@ class RegexParser {
252
318
  if (max !== undefined && min > max) {
253
319
  this.fail("Quantifier range is out of order", start);
254
320
  }
321
+ this.guard.allocate(3);
255
322
  return { min, max };
256
323
  }
257
324
  parseDecimal() {
@@ -262,6 +329,8 @@ class RegexParser {
262
329
  if (start === this.position) {
263
330
  return undefined;
264
331
  }
332
+ this.guard.allocate(this.position - start);
333
+ this.guard.work(this.position - start);
265
334
  const value = Number(this.source.slice(start, this.position));
266
335
  if (!Number.isSafeInteger(value)) {
267
336
  this.fail("Quantifier is too large", start);
@@ -283,7 +352,8 @@ class RegexParser {
283
352
  throw new SyntaxError(`${message} at position ${position}`);
284
353
  }
285
354
  }
286
- function parseFlags(flags) {
355
+ function parseFlags(flags, guard) {
356
+ guard.allocate(10);
287
357
  const parsed = {
288
358
  global: false,
289
359
  ignoreCase: false,
@@ -297,6 +367,7 @@ function parseFlags(flags) {
297
367
  s: "dotAll"
298
368
  };
299
369
  for (let position = 0; position < flags.length; position += 1) {
370
+ guard.work(1);
300
371
  const flag = flags[position];
301
372
  const name = names[flag];
302
373
  if (name === undefined) {
@@ -1,4 +1,5 @@
1
- import type { Budget } from "./budget.js";
1
+ import type { Budget, CompileTicket } from "./budget.js";
2
+ import { CompileScope } from "./regex/compile-guard.js";
2
3
  import type { GeneratorChannel } from "./generator.js";
3
4
  import { type RegexPattern } from "./regex/parse.js";
4
5
  export { createSandboxArguments, isSandboxArguments } from "./arguments.js";
@@ -35,6 +36,7 @@ export type SandboxRegex = {
35
36
  readonly [sandboxRegexPattern]: RegexPattern;
36
37
  };
37
38
  export type SandboxCallContext = {
39
+ readonly compilation?: CompileScope;
38
40
  readonly span?: {
39
41
  readonly end: {
40
42
  readonly column: number;
@@ -84,6 +86,7 @@ export type SandboxGenerator = {
84
86
  };
85
87
  type CopyFromSandboxOptions = {
86
88
  wrapClosure?: (value: SandboxClosure) => unknown;
89
+ compilation?: CompileScope;
87
90
  };
88
91
  export declare function createSandboxClosure(input: {
89
92
  async?: boolean;
@@ -110,7 +113,7 @@ export declare function createSandboxGenerator(channel: GeneratorChannel, metada
110
113
  } | undefined): SandboxGenerator;
111
114
  export declare function createSandboxMap(entries?: Iterable<readonly [SandboxValue, SandboxValue]>): SandboxMap;
112
115
  export declare function createSandboxSet(values?: Iterable<SandboxValue>): SandboxSet;
113
- export declare function createSandboxRegex(source: string, flags?: string, lastIndex?: number): SandboxRegex;
116
+ export declare function createSandboxRegex(source: string, flags?: string, lastIndex?: number, compilation?: CompileScope): SandboxRegex;
114
117
  export declare function getSandboxRegexPattern(regex: SandboxRegex): RegexPattern;
115
118
  export declare function isSandboxClosure(value: unknown): value is SandboxClosure;
116
119
  export declare function isSandboxMap(value: unknown): value is SandboxMap;
@@ -124,7 +127,9 @@ export declare function allocateProducedSandboxValue(value: SandboxValue, budget
124
127
  export declare function measureSandboxData(values: Iterable<unknown>, options?: {
125
128
  ignoreClosures?: boolean;
126
129
  ignoreClosureCaptures?: boolean;
130
+ compileTickets?: Set<CompileTicket>;
127
131
  }): number;
132
+ export declare function reconcileCompiledValues(budget: Budget, values: Iterable<unknown>, compilation?: CompileScope, parent?: CompileScope, escaping?: Iterable<unknown>): void;
128
133
  export declare function deepCopyFromSandbox(value: SandboxPromise, options?: CopyFromSandboxOptions): Promise<unknown>;
129
134
  export declare function deepCopyFromSandbox(value: SandboxValue, options?: CopyFromSandboxOptions): unknown;
130
135
  export declare function defineOwnDataProperty(target: object, key: string, value: unknown): void;
@@ -1,4 +1,6 @@
1
1
  import { bindOtelSpan, getBoundOtelSpan } from "../observability/otel.js";
2
+ import { types as nodeTypes } from "node:util";
3
+ import { CompileScope, RegexCompileGuard, regexCompiledData } from "./regex/compile-guard.js";
2
4
  import { copyFloat32Storage, float32DataProperties, float32Storage, isFloat32Array } from "./float32.js";
3
5
  import { SandboxError } from "./budget.js";
4
6
  import { observeSandboxPromise, trackSandboxPromise } from "./promise-tracker.js";
@@ -142,11 +144,15 @@ export function createSandboxSet(values = []) {
142
144
  });
143
145
  return Object.freeze(set);
144
146
  }
145
- export function createSandboxRegex(source, flags = "", lastIndex = 0) {
147
+ export function createSandboxRegex(source, flags = "", lastIndex = 0, compilation) {
148
+ if (typeof source !== "string" || typeof flags !== "string") {
149
+ throw new TypeError("Invalid sandbox RegExp source or flags: expected own string data properties.");
150
+ }
151
+ const pattern = parseRegex(source, flags, compilation, 7 + source.length + flags.length);
146
152
  const regex = { kind: "regex", source, flags, lastIndex };
147
153
  Object.defineProperties(regex, {
148
154
  [sandboxRegexBrand]: { value: true },
149
- [sandboxRegexPattern]: { value: parseRegex(source, flags) }
155
+ [sandboxRegexPattern]: { value: pattern }
150
156
  });
151
157
  return Object.seal(regex);
152
158
  }
@@ -254,7 +260,14 @@ export function measureSandboxData(values, options = {}) {
254
260
  if (isSandboxPromise(value))
255
261
  return;
256
262
  if (isSandboxRegex(value)) {
257
- usage += value.source.length + value.flags.length;
263
+ const { source, flags } = captureRegexData(value);
264
+ const compiled = regexCompiledData(getSandboxRegexPattern(value));
265
+ const staged = compiled.ticket === undefined
266
+ ? 0
267
+ : compiled.ticket.owner.budget.compileTicketUsage(compiled.ticket);
268
+ usage += Math.max(6 + source.length + flags.length + compiled.units, staged - 1);
269
+ if (compiled.ticket !== undefined)
270
+ options.compileTickets?.add(compiled.ticket);
258
271
  return;
259
272
  }
260
273
  const entries = isSandboxArguments(value)
@@ -272,6 +285,43 @@ export function measureSandboxData(values, options = {}) {
272
285
  visit(value);
273
286
  return usage;
274
287
  }
288
+ export function reconcileCompiledValues(budget, values, compilation, parent, escaping = []) {
289
+ while (parent?.closed)
290
+ parent = parent.parent;
291
+ const included = new Set();
292
+ const usage = measureSandboxData(values, { compileTickets: included });
293
+ const kept = new Set();
294
+ if (parent !== undefined)
295
+ measureSandboxData(escaping, { compileTickets: kept });
296
+ const transferred = new Set();
297
+ for (const ticket of included) {
298
+ if (!kept.has(ticket)) {
299
+ transferred.add(ticket);
300
+ }
301
+ }
302
+ const retained = budget.reconcileCompileData(usage, included, transferred, undefined, parent === undefined);
303
+ for (const ticket of retained)
304
+ compilation?.tickets.delete(ticket);
305
+ if (compilation !== undefined && parent !== undefined)
306
+ compilation.forward(included, parent);
307
+ }
308
+ function captureRegexData(value) {
309
+ const source = Object.getOwnPropertyDescriptor(value, "source");
310
+ const flags = Object.getOwnPropertyDescriptor(value, "flags");
311
+ if (source === undefined ||
312
+ !("value" in source) ||
313
+ typeof source.value !== "string" ||
314
+ flags === undefined ||
315
+ !("value" in flags) ||
316
+ typeof flags.value !== "string") {
317
+ throw new TypeError("Invalid sandbox RegExp source or flags: expected own string data properties.");
318
+ }
319
+ const cursor = Object.getOwnPropertyDescriptor(value, "lastIndex");
320
+ if (cursor === undefined || !("value" in cursor)) {
321
+ throw new TypeError("Invalid sandbox RegExp lastIndex: expected an own data property.");
322
+ }
323
+ return { source: source.value, flags: flags.value, lastIndex: cursor.value };
324
+ }
275
325
  export function deepCopyFromSandbox(value, options = {}) {
276
326
  return copyFromSandbox(value, {
277
327
  seen: new WeakMap()
@@ -411,6 +461,31 @@ function copyFromSandbox(value, state, path = "<root>", options, depth = 0) {
411
461
  if (isSandboxPrimitive(value)) {
412
462
  return value;
413
463
  }
464
+ if (nodeTypes.isProxy(value))
465
+ throw new TypeError("Unsupported proxy sandbox value.");
466
+ const regexBrand = Object.getOwnPropertyDescriptor(value, sandboxRegexBrand);
467
+ if (regexBrand !== undefined) {
468
+ if (!("value" in regexBrand) || regexBrand.value !== true) {
469
+ throw new TypeError("Invalid sandbox RegExp brand.");
470
+ }
471
+ const { source, flags, lastIndex } = captureRegexData(value);
472
+ const guard = new RegexCompileGuard(options.compilation);
473
+ try {
474
+ guard.preflight(source, flags);
475
+ const existing = state.seen.get(value);
476
+ if (existing !== undefined)
477
+ return existing;
478
+ guard.allocate(1 + source.length + flags.length);
479
+ const regex = new RegExp(source, flags);
480
+ regex.lastIndex = lastIndex;
481
+ guard.retainScratch();
482
+ state.seen.set(value, regex);
483
+ return regex;
484
+ }
485
+ finally {
486
+ guard.close();
487
+ }
488
+ }
414
489
  if (isFloat32Array(value)) {
415
490
  const existing = state.seen.get(value);
416
491
  if (existing !== undefined)
@@ -441,17 +516,28 @@ function copyFromSandbox(value, state, path = "<root>", options, depth = 0) {
441
516
  }
442
517
  if (isSandboxPromise(value)) {
443
518
  observeSandboxPromise(value);
444
- return value.promise.then((resolved) => copyFromSandbox(resolved, { seen: new WeakMap() }, "<root>", options), (reason) => Promise.reject(reason instanceof SandboxError
445
- ? reason
446
- : copyFromSandbox(reason, { seen: new WeakMap() }, "<root>", options)));
519
+ const copySettlement = (settled) => {
520
+ const owner = options.compilation?.owner;
521
+ const operation = owner?.budget.acquireCompileOwner(false, owner);
522
+ const compilation = new CompileScope(operation?.owner);
523
+ try {
524
+ return copyFromSandbox(settled, { seen: new WeakMap() }, "<root>", {
525
+ ...options,
526
+ compilation
527
+ });
528
+ }
529
+ finally {
530
+ compilation.dispose();
531
+ operation?.release();
532
+ }
533
+ };
534
+ return value.promise.then(copySettlement, (reason) => Promise.reject(reason instanceof SandboxError ? reason : copySettlement(reason)));
447
535
  }
448
536
  if (isSandboxGenerator(value)) {
449
537
  throw new TypeError("Sandbox generators cannot cross into host values.");
450
538
  }
451
539
  if (isSandboxRegex(value)) {
452
- const regex = new RegExp(value.source, value.flags);
453
- regex.lastIndex = value.lastIndex;
454
- return regex;
540
+ throw new TypeError("Invalid sandbox RegExp brand.");
455
541
  }
456
542
  if (isSandboxMap(value)) {
457
543
  const existing = state.seen.get(value);
@@ -1,4 +1,4 @@
1
- import type { Budget } from "../interp/budget.js";
1
+ import type { Budget, CompileOwner } from "../interp/budget.js";
2
2
  import type { HostCallJournal } from "../interp/host-call.js";
3
3
  import { type CallerInjectedBinding } from "../interp/host-bridge.js";
4
4
  import type { SandboxValue } from "../interp/values.js";
@@ -9,6 +9,7 @@ export declare function createUnknownModuleMessage(moduleName: string, moduleNam
9
9
  export declare function createUnknownExportMessage(moduleName: string, exportName: string, availableExports: readonly string[]): string;
10
10
  export declare function resolveModuleImports(module: Module, modules: ModuleRegistry | undefined, options: {
11
11
  budget: Budget;
12
+ compileOwner?: CompileOwner;
12
13
  hostCalls?: HostCallJournal;
13
14
  signal?: AbortSignal;
14
15
  allowMissing?: boolean;
@@ -37,6 +37,7 @@ function bindImportDeclaration(declaration, registry, wrappedModules, bindings,
37
37
  const wrappedExports = wrappedModules.get(moduleName) ??
38
38
  createBindingRecord(wrapCancelableBindings(wrapCallerInjectedBindings(Object.fromEntries(moduleExports), {
39
39
  budget: options.budget,
40
+ compileOwner: options.compileOwner,
40
41
  hostCalls: options.hostCalls,
41
42
  moduleId: moduleName,
42
43
  signal: options.signal
@@ -1,3 +1,4 @@
1
1
  import { type Module, type ParseResult } from "./parser.js";
2
- export declare function hashSource(source: string): string;
2
+ import { type CompileOwner } from "../interp/budget.js";
3
+ export declare function hashSource(source: string, owner?: CompileOwner): string;
3
4
  export declare function hashParsedAst(ast: Module | ParseResult): string;
@@ -1,13 +1,16 @@
1
1
  import { parse, parseModule } from "./parser.js";
2
+ import { SandboxError } from "../interp/budget.js";
2
3
  const FNV_OFFSET_BASIS = 0x811c9dc5;
3
4
  const FNV_PRIME = 0x01000193;
4
5
  const IGNORED_KEYS = new Set(["nodeId", "raw", "span"]);
5
- export function hashSource(source) {
6
+ export function hashSource(source, owner) {
6
7
  try {
7
- return hashParsedAst(parse(source));
8
+ return hashParsedAst(parse(source, "<input>", owner));
8
9
  }
9
- catch {
10
- return hashParsedAst(parseModule(source));
10
+ catch (error) {
11
+ if (error instanceof SandboxError)
12
+ throw error;
13
+ return hashParsedAst(parseModule(source, "<input>", owner));
11
14
  }
12
15
  }
13
16
  export function hashParsedAst(ast) {
@@ -1,5 +1,6 @@
1
1
  import { type Position } from "./tokenizer.js";
2
2
  import { type ExportDefaultDeclaration, type ExportNamedDeclaration } from "./parse-export.js";
3
+ import { type CompileOwner } from "../interp/budget.js";
3
4
  export type { ExportDefaultDeclaration, ExportNamedDeclaration } from "./parse-export.js";
4
5
  export type SourceSpan = {
5
6
  start: Position;
@@ -349,6 +350,7 @@ export type ArrowFunctionExpression = BaseNode & {
349
350
  export type FunctionNode = ArrowFunctionExpression | FunctionDeclaration | FunctionExpression;
350
351
  export type Expression = ArrowFunctionExpression | AssignmentExpression | ArrayExpression | AwaitExpression | BinaryExpression | BooleanLiteral | CallExpression | ConditionalExpression | FunctionExpression | Identifier | LogicalExpression | MemberExpression | MetaProperty | NewExpression | NullLiteral | NumericLiteral | ObjectExpression | RegexLiteral | SequenceExpression | StringLiteral | TaggedTemplateExpression | TemplateLiteral | ThisExpression | UnaryExpression | UpdateExpression | UndefinedLiteral | YieldExpression;
351
352
  export type ParseResult = Expression | Statement;
352
- export declare function parse(source: string, filename?: string): ParseResult;
353
- export declare function parseModule(source: string, filename?: string): Module;
354
- export declare function parseExecutableModule(source: string, filename?: string): Module;
353
+ export declare function parse(source: string, filename?: string, owner?: CompileOwner): ParseResult;
354
+ export declare function parseModule(source: string, filename?: string, owner?: CompileOwner): Module;
355
+ export declare function parseExecutableModule(source: string, filename?: string, owner?: CompileOwner): Module;
356
+ export declare function findRegexLiteral(node: unknown): RegexLiteral | undefined;