@poe-platform/safe-js 0.1.720 → 0.1.722

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 (88) hide show
  1. package/README.md +50 -7
  2. package/dist/config-mutations/types.d.ts +3 -1
  3. package/dist/safe-js/chunks/{chunk-B2SQLK4O.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-OP3H3ZDN.js → chunk-55RIIUGG.js} +23 -7
  6. package/dist/safe-js/chunks/chunk-55RIIUGG.js.map +7 -0
  7. package/dist/safe-js/chunks/{chunk-NJM2Q74Z.js → chunk-7SBQ7WTV.js} +10 -11
  8. package/dist/safe-js/chunks/chunk-7SBQ7WTV.js.map +7 -0
  9. package/dist/safe-js/chunks/{chunk-KP6I45ET.js → chunk-BNIEPGDU.js} +9885 -8523
  10. package/dist/safe-js/chunks/{chunk-KP6I45ET.js.map → chunk-BNIEPGDU.js.map} +4 -4
  11. package/dist/safe-js/chunks/{chunk-HYD4K4HN.js → chunk-D3GC5QPZ.js} +63 -1
  12. package/dist/safe-js/chunks/chunk-D3GC5QPZ.js.map +7 -0
  13. package/dist/safe-js/chunks/{chunk-KK5NCR5R.js → chunk-EH5A2EQI.js} +501 -150
  14. package/dist/safe-js/chunks/chunk-EH5A2EQI.js.map +7 -0
  15. package/dist/safe-js/chunks/{chunk-APKV5Y6T.js → chunk-FA5CQO6V.js} +2 -2
  16. package/dist/safe-js/chunks/{chunk-RML35GIF.js → chunk-GCOLKRMY.js} +4 -4
  17. package/dist/safe-js/chunks/{chunk-U6KZ2DQ2.js → chunk-K3ZRBAG5.js} +3 -3
  18. package/dist/safe-js/chunks/{chunk-BGJXQN4A.js → chunk-QL2ALAE6.js} +4 -4
  19. package/dist/safe-js/chunks/{chunk-IDQ67DY2.js → chunk-RYVHEOHM.js} +1131 -101
  20. package/dist/safe-js/chunks/chunk-RYVHEOHM.js.map +7 -0
  21. package/dist/safe-js/chunks/{chunk-A7CADQAD.js → chunk-S2GFVXSI.js} +3 -3
  22. package/dist/safe-js/chunks/{chunk-XMA6FOJU.js → chunk-WF7HYDJ4.js} +4 -5
  23. package/dist/safe-js/chunks/{chunk-XMA6FOJU.js.map → chunk-WF7HYDJ4.js.map} +2 -2
  24. package/dist/safe-js/chunks/{chunk-NEJ44T4Y.js → chunk-XHSOM5CG.js} +2 -2
  25. package/dist/safe-js/chunks/{cli-runtime-PO6DQDHP.js → cli-runtime-6QPGM3TX.js} +14 -14
  26. package/dist/safe-js/chunks/{dump-TRGFSCJ7.js → dump-TU6SYRFD.js} +4 -4
  27. package/dist/safe-js/chunks/{fs-4T5MZW5J.js → fs-HTZT32MQ.js} +4 -4
  28. package/dist/safe-js/chunks/harness-RYAMI444.js +10 -0
  29. package/dist/safe-js/chunks/mcp-DO2IRFGQ.js +11 -0
  30. package/dist/safe-js/chunks/migration-file-K7UH5ZG6.js +12 -0
  31. package/dist/safe-js/chunks/restore-7LRP4BM3.js +13 -0
  32. package/dist/safe-js/chunks/run-6SETM7SV.js +17 -0
  33. package/dist/safe-js/chunks/runtime-modules-MCQS2SHQ.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/extensions.d.ts +3 -1
  37. package/dist/safe-js/index.js +14 -14
  38. package/dist/safe-js/interp/async.d.ts +4 -1
  39. package/dist/safe-js/interp/budget.d.ts +15 -6
  40. package/dist/safe-js/interp/collection-iterator.d.ts +1 -1
  41. package/dist/safe-js/interp/host-bridge.d.ts +1 -0
  42. package/dist/safe-js/interp/host-capabilities.d.ts +13 -4
  43. package/dist/safe-js/interp/interpreter.d.ts +5 -1
  44. package/dist/safe-js/interp/intl-numberformat.d.ts +2 -2
  45. package/dist/safe-js/interp/jobs.d.ts +8 -0
  46. package/dist/safe-js/interp/scope.d.ts +13 -3
  47. package/dist/safe-js/interp/typed-array.d.ts +5 -2
  48. package/dist/safe-js/interp/values.d.ts +1 -0
  49. package/dist/safe-js/modules/registry.d.ts +4 -1
  50. package/dist/safe-js/modules/source-graph.d.ts +19 -1
  51. package/dist/safe-js/parse/compact-spans.d.ts +18 -0
  52. package/dist/safe-js/parse/dynamic-source.d.ts +3 -1
  53. package/dist/safe-js/parse/function-source.d.ts +1 -1
  54. package/dist/safe-js/parse/parser.d.ts +8 -3
  55. package/dist/safe-js/parse/tokenizer.d.ts +4 -0
  56. package/dist/safe-js/realm.d.ts +17 -3
  57. package/dist/safe-js/restore.d.ts +1 -0
  58. package/dist/safe-js/run.d.ts +10 -0
  59. package/dist/safe-js/snapshot/guest-heap.d.ts +1 -0
  60. package/dist/toolcraft-design/prompts/primitives/note.d.ts +1 -1
  61. package/package.json +2 -2
  62. package/dist/safe-js/chunks/chunk-B2SQLK4O.js.map +0 -7
  63. package/dist/safe-js/chunks/chunk-HYD4K4HN.js.map +0 -7
  64. package/dist/safe-js/chunks/chunk-IDQ67DY2.js.map +0 -7
  65. package/dist/safe-js/chunks/chunk-KK5NCR5R.js.map +0 -7
  66. package/dist/safe-js/chunks/chunk-NJM2Q74Z.js.map +0 -7
  67. package/dist/safe-js/chunks/chunk-OP3H3ZDN.js.map +0 -7
  68. package/dist/safe-js/chunks/harness-DLEQ2Z3O.js +0 -10
  69. package/dist/safe-js/chunks/mcp-TNVEFJ6K.js +0 -11
  70. package/dist/safe-js/chunks/migration-file-IDIUFLKY.js +0 -12
  71. package/dist/safe-js/chunks/restore-P27AUZR2.js +0 -13
  72. package/dist/safe-js/chunks/run-KGAEHYMV.js +0 -17
  73. package/dist/safe-js/chunks/runtime-modules-KICK45EE.js +0 -10
  74. /package/dist/safe-js/chunks/{chunk-APKV5Y6T.js.map → chunk-FA5CQO6V.js.map} +0 -0
  75. /package/dist/safe-js/chunks/{chunk-RML35GIF.js.map → chunk-GCOLKRMY.js.map} +0 -0
  76. /package/dist/safe-js/chunks/{chunk-U6KZ2DQ2.js.map → chunk-K3ZRBAG5.js.map} +0 -0
  77. /package/dist/safe-js/chunks/{chunk-BGJXQN4A.js.map → chunk-QL2ALAE6.js.map} +0 -0
  78. /package/dist/safe-js/chunks/{chunk-A7CADQAD.js.map → chunk-S2GFVXSI.js.map} +0 -0
  79. /package/dist/safe-js/chunks/{chunk-NEJ44T4Y.js.map → chunk-XHSOM5CG.js.map} +0 -0
  80. /package/dist/safe-js/chunks/{cli-runtime-PO6DQDHP.js.map → cli-runtime-6QPGM3TX.js.map} +0 -0
  81. /package/dist/safe-js/chunks/{dump-TRGFSCJ7.js.map → dump-TU6SYRFD.js.map} +0 -0
  82. /package/dist/safe-js/chunks/{fs-4T5MZW5J.js.map → fs-HTZT32MQ.js.map} +0 -0
  83. /package/dist/safe-js/chunks/{harness-DLEQ2Z3O.js.map → harness-RYAMI444.js.map} +0 -0
  84. /package/dist/safe-js/chunks/{mcp-TNVEFJ6K.js.map → mcp-DO2IRFGQ.js.map} +0 -0
  85. /package/dist/safe-js/chunks/{migration-file-IDIUFLKY.js.map → migration-file-K7UH5ZG6.js.map} +0 -0
  86. /package/dist/safe-js/chunks/{restore-P27AUZR2.js.map → restore-7LRP4BM3.js.map} +0 -0
  87. /package/dist/safe-js/chunks/{run-KGAEHYMV.js.map → run-6SETM7SV.js.map} +0 -0
  88. /package/dist/safe-js/chunks/{runtime-modules-KICK45EE.js.map → runtime-modules-MCQS2SHQ.js.map} +0 -0
@@ -157,12 +157,11 @@ function releaseTemplateObjects(budget) {
157
157
  }
158
158
 
159
159
  // packages/safe-js/src/interp/budget.ts
160
- var REGEX_STEP_LIMIT = 2e3;
161
160
  var REGEX_COMPILE_LIMITS = Object.freeze({
162
- sourceLength: 4096,
163
- flagsLength: 8,
164
- depth: 64,
165
- allocations: 16384
161
+ sourceLength: Infinity,
162
+ flagsLength: Infinity,
163
+ depth: Infinity,
164
+ allocations: Infinity
166
165
  });
167
166
  var DEADLINE_CHECK_INTERVAL = 1024;
168
167
  var SandboxError = class extends Error {
@@ -204,6 +203,8 @@ var BudgetAccounting = class {
204
203
  allChecksSuspended = 0;
205
204
  deadlineChecksSuspended = 0;
206
205
  visitsUntilDeadlineCheck = DEADLINE_CHECK_INTERVAL;
206
+ pendingDeadlines;
207
+ earliestPendingDeadline;
207
208
  retainedDataSize = 0;
208
209
  retainedData = /* @__PURE__ */ new Map();
209
210
  retainedValueSources = /* @__PURE__ */ new Map();
@@ -214,6 +215,8 @@ var BudgetAccounting = class {
214
215
  provisionalScopes = 0;
215
216
  compileTickets = /* @__PURE__ */ new Map();
216
217
  completedCompileTickets = /* @__PURE__ */ new Set();
218
+ reconciliationHolds = 0;
219
+ deferredCompileDiscards = /* @__PURE__ */ new Set();
217
220
  realmViews;
218
221
  constructor(options = {}) {
219
222
  this.deadline = normalizeDeadline(options.deadline);
@@ -221,6 +224,12 @@ var BudgetAccounting = class {
221
224
  maxSteps: normalizeLimit("maxSteps", options.maxSteps),
222
225
  maxCallDepth: normalizeLimit("maxCallDepth", options.maxCallDepth),
223
226
  stringLength: normalizeLimit("stringLength", options.stringLength),
227
+ ...options.regexSourceLength === void 0 ? {} : {
228
+ regexSourceLength: normalizeRegexLimit("regexSourceLength", options.regexSourceLength)
229
+ },
230
+ ...options.regexCompileAllocations === void 0 ? {} : {
231
+ regexCompileAllocations: normalizeRegexLimit("regexCompileAllocations", options.regexCompileAllocations)
232
+ },
224
233
  arrayLength: normalizeLimit("arrayLength", options.arrayLength),
225
234
  dataSize: normalizeLimit("dataSize", options.dataSize)
226
235
  });
@@ -228,6 +237,8 @@ var BudgetAccounting = class {
228
237
  };
229
238
  var Budget = class _Budget {
230
239
  accounting;
240
+ realmView = false;
241
+ realmLease = false;
231
242
  constructor(options = {}) {
232
243
  this.accounting = new BudgetAccounting(options);
233
244
  }
@@ -267,13 +278,62 @@ var Budget = class _Budget {
267
278
  set currentCallDepth(value) {
268
279
  this.accounting.currentCallDepth = value;
269
280
  }
281
+ // Pending host operations keep their elapsed deadline active during sampled
282
+ // synchronous work. Linked private records need no observable native hooks.
283
+ acquireDeadline(deadline, onExceeded) {
284
+ if (!Number.isFinite(deadline)) throw new TypeError("Deadline must be a finite timestamp.");
285
+ const accounting = this.accounting;
286
+ const pending = {
287
+ deadline,
288
+ onExceeded,
289
+ next: accounting.pendingDeadlines,
290
+ active: true
291
+ };
292
+ if (pending.next !== void 0) pending.next.previous = pending;
293
+ accounting.pendingDeadlines = pending;
294
+ if (accounting.earliestPendingDeadline === void 0 || pending.deadline < accounting.earliestPendingDeadline.deadline)
295
+ accounting.earliestPendingDeadline = pending;
296
+ return () => {
297
+ if (!pending.active) return;
298
+ pending.active = false;
299
+ if (pending.previous === void 0) accounting.pendingDeadlines = pending.next;
300
+ else pending.previous.next = pending.next;
301
+ if (pending.next !== void 0) pending.next.previous = pending.previous;
302
+ pending.previous = pending.next = void 0;
303
+ pending.onExceeded = void 0;
304
+ if (accounting.earliestPendingDeadline !== pending) return;
305
+ accounting.earliestPendingDeadline = void 0;
306
+ for (let next = accounting.pendingDeadlines; next !== void 0; next = next.next)
307
+ if (accounting.earliestPendingDeadline === void 0 || next.deadline < accounting.earliestPendingDeadline.deadline)
308
+ accounting.earliestPendingDeadline = next;
309
+ };
310
+ }
270
311
  forkRealm() {
271
312
  const view = new _Budget();
272
313
  view.accounting = this.accounting;
314
+ view.realmView = true;
273
315
  const views = this.accounting.realmViews ??= /* @__PURE__ */ new Set([new WeakRef(this)]);
274
316
  views.add(new WeakRef(view));
275
317
  return view;
276
318
  }
319
+ acquireRealmOwner() {
320
+ if (this.realmLease) throw new SandboxError("reentry");
321
+ const lease = this.acquireCompileOwner(
322
+ !this.realmView,
323
+ this.realmView ? this.accounting.activeCompileOwner : void 0
324
+ );
325
+ this.realmLease = true;
326
+ let released = false;
327
+ return {
328
+ owner: lease.owner,
329
+ release: () => {
330
+ if (released) return;
331
+ released = true;
332
+ this.realmLease = false;
333
+ lease.release();
334
+ }
335
+ };
336
+ }
277
337
  visitNode(units = 1) {
278
338
  if (!Number.isSafeInteger(units) || units < 0) {
279
339
  throw new Error("units must be a non-negative safe integer.");
@@ -290,14 +350,17 @@ var Budget = class _Budget {
290
350
  }
291
351
  }
292
352
  allocateString(value) {
293
- if (this.accounting.allChecksSuspended === 0 && this.accounting.limits.stringLength !== void 0 && value.length > this.accounting.limits.stringLength) {
353
+ this.allocateStringLength(value.length);
354
+ return value;
355
+ }
356
+ allocateStringLength(length) {
357
+ if (this.accounting.allChecksSuspended === 0 && this.accounting.limits.stringLength !== void 0 && length > this.accounting.limits.stringLength) {
294
358
  throw new SandboxError({
295
359
  budget: "stringLength",
296
- current: value.length,
360
+ current: length,
297
361
  limit: this.accounting.limits.stringLength
298
362
  });
299
363
  }
300
- return value;
301
364
  }
302
365
  allocateArrayLength(length) {
303
366
  if (this.accounting.allChecksSuspended === 0 && this.accounting.limits.arrayLength !== void 0 && length > this.accounting.limits.arrayLength) {
@@ -312,7 +375,8 @@ var Budget = class _Budget {
312
375
  this.allocateArrayLength(count);
313
376
  }
314
377
  reconcileDataUsage(usage) {
315
- const total = usage + this.accounting.retainedDataSize;
378
+ const measured = usage + this.accounting.retainedDataSize;
379
+ const total = this.reconciliationDeferred ? Math.max(measured, this.accounting.currentDataSize) : measured;
316
380
  this.checkDataUsage(total);
317
381
  this.accounting.currentDataSize = total;
318
382
  this.accounting.peakDataSize = Math.max(this.accounting.peakDataSize, total);
@@ -334,6 +398,23 @@ var Budget = class _Budget {
334
398
  if (values === void 0) this.accounting.retainedValueSources.delete(owner);
335
399
  else this.accounting.retainedValueSources.set(owner, values);
336
400
  }
401
+ get reconciliationDeferred() {
402
+ return this.accounting.reconciliationHolds > 0;
403
+ }
404
+ deferReconciliation() {
405
+ this.accounting.reconciliationHolds++;
406
+ const generation = this.accounting.compileGeneration;
407
+ let released = false;
408
+ return () => {
409
+ if (released) return;
410
+ released = true;
411
+ if (generation !== this.accounting.compileGeneration) return;
412
+ if (--this.accounting.reconciliationHolds !== 0) return;
413
+ for (const ticket of this.accounting.deferredCompileDiscards)
414
+ this.accounting.completedCompileTickets.add(ticket);
415
+ this.accounting.deferredCompileDiscards.clear();
416
+ };
417
+ }
337
418
  *retainedValues() {
338
419
  for (const values of this.accounting.retainedValueSources.values()) {
339
420
  const retained = values();
@@ -374,6 +455,7 @@ var Budget = class _Budget {
374
455
  if (ticket.owner.generation !== this.accounting.compileGeneration || !this.accounting.compileTickets.has(ticket)) {
375
456
  throw new SandboxError("reentry");
376
457
  }
458
+ if (this.reconciliationDeferred) usage = Math.max(usage, this.compileTicketUsage(ticket));
377
459
  this.setRetainedDataUsage(ticket, usage);
378
460
  this.accounting.compileTickets.set(ticket, usage);
379
461
  }
@@ -381,6 +463,10 @@ var Budget = class _Budget {
381
463
  if (ticket.owner.generation !== this.accounting.compileGeneration) return;
382
464
  const usage = this.accounting.compileTickets.get(ticket);
383
465
  if (usage === void 0) return;
466
+ if (this.reconciliationDeferred) {
467
+ this.accounting.deferredCompileDiscards.add(ticket);
468
+ return;
469
+ }
384
470
  this.accounting.compileTickets.delete(ticket);
385
471
  this.accounting.completedCompileTickets.delete(ticket);
386
472
  this.accounting.retainedData.delete(ticket);
@@ -388,6 +474,19 @@ var Budget = class _Budget {
388
474
  this.accounting.currentDataSize -= usage;
389
475
  }
390
476
  reconcileCompileData(usage, included, transferred = included, retainedOwner, complete = false) {
477
+ if (this.reconciliationDeferred) {
478
+ if (retainedOwner !== void 0) this.setRetainedDataUsage(retainedOwner, usage);
479
+ else {
480
+ let includedUsage2 = 0;
481
+ for (const ticket of included) includedUsage2 += this.compileTicketUsage(ticket);
482
+ const measured2 = usage + this.accounting.retainedDataSize - includedUsage2;
483
+ const total2 = Math.max(measured2, this.accounting.currentDataSize);
484
+ this.checkDataUsage(total2);
485
+ this.accounting.currentDataSize = total2;
486
+ this.accounting.peakDataSize = Math.max(this.accounting.peakDataSize, total2);
487
+ }
488
+ return /* @__PURE__ */ new Set();
489
+ }
391
490
  let includedUsage = 0;
392
491
  let transferredUsage = 0;
393
492
  let discardedUsage = 0;
@@ -451,8 +550,10 @@ var Budget = class _Budget {
451
550
  released = true;
452
551
  if (generation !== this.accounting.compileGeneration) return;
453
552
  this.accounting.provisionalScopes -= 1;
454
- if (!commit)
455
- this.accounting.currentDataSize = previous + this.accounting.retainedDataSize - previousRetained;
553
+ if (!commit) {
554
+ const restored = previous + this.accounting.retainedDataSize - previousRetained;
555
+ this.accounting.currentDataSize = this.reconciliationDeferred ? Math.max(restored, this.accounting.currentDataSize) : restored;
556
+ }
456
557
  };
457
558
  }
458
559
  enterCall() {
@@ -474,6 +575,8 @@ var Budget = class _Budget {
474
575
  this.accounting.provisionalScopes = 0;
475
576
  this.accounting.compileTickets.clear();
476
577
  this.accounting.completedCompileTickets.clear();
578
+ this.accounting.reconciliationHolds = 0;
579
+ this.accounting.deferredCompileDiscards.clear();
477
580
  this.accounting.stepsUsed = 0;
478
581
  this.accounting.peakCallDepth = 0;
479
582
  this.accounting.currentCallDepth = 0;
@@ -509,18 +612,26 @@ var Budget = class _Budget {
509
612
  };
510
613
  }
511
614
  checkDeadline() {
512
- if (this.accounting.allChecksSuspended > 0 || this.accounting.deadlineChecksSuspended > 0 || this.accounting.deadline === void 0) {
615
+ const pending = this.accounting.earliestPendingDeadline;
616
+ const deadline = this.accounting.deadline === void 0 ? pending?.deadline : Math.min(this.accounting.deadline, pending?.deadline ?? Infinity);
617
+ if (this.accounting.allChecksSuspended > 0 || this.accounting.deadlineChecksSuspended > 0 || deadline === void 0) {
513
618
  return;
514
619
  }
515
620
  const now = Date.now();
516
- if (now <= this.accounting.deadline) {
621
+ if (now <= deadline) {
517
622
  return;
518
623
  }
519
- throw new SandboxError({
624
+ const reason = new SandboxError({
520
625
  budget: "deadline",
521
626
  current: now,
522
- limit: this.accounting.deadline
627
+ limit: deadline
523
628
  });
629
+ if (pending?.deadline === deadline) {
630
+ const notify = pending.onExceeded;
631
+ pending.onExceeded = void 0;
632
+ notify?.(reason);
633
+ }
634
+ throw reason;
524
635
  }
525
636
  checkDataUsage(usage) {
526
637
  if (this.accounting.allChecksSuspended === 0 && this.accounting.limits.dataSize !== void 0 && usage > this.accounting.limits.dataSize) {
@@ -532,7 +643,7 @@ var Budget = class _Budget {
532
643
  }
533
644
  }
534
645
  checkSampledDeadline(units) {
535
- if (this.accounting.allChecksSuspended > 0 || this.accounting.deadlineChecksSuspended > 0 || this.accounting.deadline === void 0) {
646
+ if (this.accounting.allChecksSuspended > 0 || this.accounting.deadlineChecksSuspended > 0 || this.accounting.deadline === void 0 && this.accounting.earliestPendingDeadline === void 0) {
536
647
  return;
537
648
  }
538
649
  this.accounting.visitsUntilDeadlineCheck -= units;
@@ -565,14 +676,6 @@ var Budget = class _Budget {
565
676
  };
566
677
  }
567
678
  };
568
- function allocateRegexSteps(steps) {
569
- if (!Number.isInteger(steps) || steps < 0) {
570
- throw new Error("steps must be a non-negative integer.");
571
- }
572
- if (steps > REGEX_STEP_LIMIT) {
573
- throw new SandboxError({ budget: "steps", current: steps, limit: REGEX_STEP_LIMIT });
574
- }
575
- }
576
679
  function normalizeDeadline(deadline) {
577
680
  if (deadline === void 0) {
578
681
  return void 0;
@@ -588,6 +691,12 @@ function normalizeLimit(name, value) {
588
691
  }
589
692
  return value;
590
693
  }
694
+ function normalizeRegexLimit(name, value) {
695
+ if (value === void 0) return void 0;
696
+ if (!Number.isSafeInteger(value) || value < 1)
697
+ throw new RangeError(`${name} must be a positive safe integer.`);
698
+ return value;
699
+ }
591
700
 
592
701
  // packages/safe-js/src/interp/regex/compile-guard.ts
593
702
  var compiledData = /* @__PURE__ */ new WeakMap();
@@ -628,7 +737,7 @@ var RegexCompileGuard = class {
628
737
  ticket;
629
738
  retained = false;
630
739
  checkLength(length, flags = false) {
631
- const hard = flags ? REGEX_COMPILE_LIMITS.flagsLength : REGEX_COMPILE_LIMITS.sourceLength;
740
+ const hard = flags ? REGEX_COMPILE_LIMITS.flagsLength : this.scope?.owner?.budget.limits.regexSourceLength ?? REGEX_COMPILE_LIMITS.sourceLength;
632
741
  const limit = Math.min(hard, this.scope?.owner?.budget.limits.stringLength ?? hard);
633
742
  if (length > limit) throw new SandboxError({ budget: "stringLength", current: length, limit });
634
743
  }
@@ -639,11 +748,12 @@ var RegexCompileGuard = class {
639
748
  }
640
749
  allocate(units) {
641
750
  const next = this.allocations + units;
642
- if (next > REGEX_COMPILE_LIMITS.allocations) {
751
+ const limit = this.scope?.owner?.budget.limits.regexCompileAllocations ?? REGEX_COMPILE_LIMITS.allocations;
752
+ if (next > limit) {
643
753
  throw new SandboxError({
644
754
  budget: "dataSize",
645
755
  current: next,
646
- limit: REGEX_COMPILE_LIMITS.allocations
756
+ limit
647
757
  });
648
758
  }
649
759
  this.work(units);
@@ -956,6 +1066,753 @@ function findNumberEnd(value, startIndex) {
956
1066
  return index;
957
1067
  }
958
1068
 
1069
+ // packages/safe-js/src/parse/compact-spans.ts
1070
+ var positionOwners = /* @__PURE__ */ new WeakMap();
1071
+ var getOwner = WeakMap.prototype.get.bind(positionOwners);
1072
+ var setOwner = WeakMap.prototype.set.bind(positionOwners);
1073
+ var cacheSize = 514;
1074
+ var writableSpan = /* @__PURE__ */ Symbol("compiler span writability");
1075
+ var CompactSourcePositions = class {
1076
+ sourceLength;
1077
+ lines = [0];
1078
+ positions = /* @__PURE__ */ new Map();
1079
+ makeSpan;
1080
+ constructor(source) {
1081
+ this.sourceLength = source.length;
1082
+ for (let offset = 0; offset < source.length; offset++) {
1083
+ const code = source.charCodeAt(offset);
1084
+ if (code === 13 && source.charCodeAt(offset + 1) === 10) offset++;
1085
+ if (code === 10 || code === 13 || code === 8232 || code === 8233)
1086
+ this.lines.push(offset + 1);
1087
+ }
1088
+ const owner = this;
1089
+ class Span {
1090
+ #start;
1091
+ #end;
1092
+ constructor(start, end) {
1093
+ this.#start = start;
1094
+ this.#end = end;
1095
+ Object.defineProperties(this, descriptors);
1096
+ }
1097
+ get start() {
1098
+ return typeof this.#start === "number" ? owner.position(this.#start) : this.#start.assigned;
1099
+ }
1100
+ set start(value) {
1101
+ if (Object.isFrozen(this)) throw new TypeError("Cannot assign to a frozen source span.");
1102
+ this.#start = getOwner(value) === owner ? value.offset : { assigned: value };
1103
+ }
1104
+ get end() {
1105
+ return typeof this.#end === "number" ? owner.position(this.#end) : this.#end.assigned;
1106
+ }
1107
+ set end(value) {
1108
+ if (Object.isFrozen(this)) throw new TypeError("Cannot assign to a frozen source span.");
1109
+ this.#end = getOwner(value) === owner ? value.offset : { assigned: value };
1110
+ }
1111
+ }
1112
+ const descriptors = {
1113
+ start: {
1114
+ ...Object.getOwnPropertyDescriptor(Span.prototype, "start"),
1115
+ enumerable: true
1116
+ },
1117
+ end: {
1118
+ ...Object.getOwnPropertyDescriptor(Span.prototype, "end"),
1119
+ enumerable: true
1120
+ },
1121
+ // Sealed accessor-only objects otherwise report as frozen. This private
1122
+ // data field preserves the distinction for the mutable endpoint setters.
1123
+ [writableSpan]: { value: true, writable: true, configurable: true }
1124
+ };
1125
+ delete Span.prototype.start;
1126
+ delete Span.prototype.end;
1127
+ this.makeSpan = (start, end) => new Span(start, end);
1128
+ }
1129
+ position(offset) {
1130
+ this.assertOffset(offset);
1131
+ const cached = this.positions.get(offset);
1132
+ if (cached !== void 0) return cached;
1133
+ let low = 0, high = this.lines.length;
1134
+ while (low + 1 < high) {
1135
+ const middle = low + Math.floor((high - low) / 2);
1136
+ if (this.lines[middle] <= offset) low = middle;
1137
+ else high = middle;
1138
+ }
1139
+ const position = Object.freeze({
1140
+ line: low + 1,
1141
+ column: offset - this.lines[low] + 1,
1142
+ offset
1143
+ });
1144
+ setOwner(position, this);
1145
+ if (this.positions.size === cacheSize)
1146
+ this.positions.delete(this.positions.keys().next().value);
1147
+ this.positions.set(offset, position);
1148
+ return position;
1149
+ }
1150
+ span(start, end) {
1151
+ this.assertOffset(start);
1152
+ this.assertOffset(end);
1153
+ return this.makeSpan(start, end);
1154
+ }
1155
+ compatiblePosition(position) {
1156
+ if (!Number.isSafeInteger(position.offset) || position.offset < 0 || position.offset > this.sourceLength)
1157
+ return void 0;
1158
+ const point = this.position(position.offset);
1159
+ return point.line === position.line && point.column === position.column ? point : void 0;
1160
+ }
1161
+ assertOffset(offset) {
1162
+ if (!Number.isSafeInteger(offset) || offset < 0 || offset > this.sourceLength)
1163
+ throw new RangeError("Source coordinate offset is outside its source.");
1164
+ }
1165
+ };
1166
+ function compactDerivedPosition(base, position) {
1167
+ return getOwner(base)?.compatiblePosition(position);
1168
+ }
1169
+ function compactSourceSpan(start, end) {
1170
+ const owner = getOwner(start);
1171
+ return owner !== void 0 && getOwner(end) === owner ? owner.span(start.offset, end.offset) : void 0;
1172
+ }
1173
+
1174
+ // packages/safe-js/src/parse/function-source.ts
1175
+ var functionSources = /* @__PURE__ */ new WeakMap();
1176
+ var templateSources = /* @__PURE__ */ new WeakMap();
1177
+ var functionStrictness = /* @__PURE__ */ new WeakMap();
1178
+ var evalFunctionDeclarations = /* @__PURE__ */ new WeakSet();
1179
+ var dynamicSourceRecords = /* @__PURE__ */ new WeakSet();
1180
+ var dynamicNodeSources = /* @__PURE__ */ new WeakMap();
1181
+ var dynamicValueSources = /* @__PURE__ */ new WeakMap();
1182
+
1183
+ // packages/safe-js/src/parse/compact-module-ast.ts
1184
+ var chunkLength = 65536;
1185
+ var scalarMask = 268435455;
1186
+ var handles = /* @__PURE__ */ new WeakMap();
1187
+ var mutableRecord = /* @__PURE__ */ Symbol("compiler record writability");
1188
+ var recordToken = /* @__PURE__ */ Symbol("compiler traversal authority");
1189
+ var defineCompilerProperty = Reflect.defineProperty;
1190
+ var NativeRowMap = Map;
1191
+ var nativeIndexMap = {
1192
+ get: Function.prototype.call.bind(Map.prototype.get),
1193
+ has: Function.prototype.call.bind(Map.prototype.has),
1194
+ set: Function.prototype.call.bind(Map.prototype.set),
1195
+ delete: Function.prototype.call.bind(Map.prototype.delete),
1196
+ clear: Function.prototype.call.bind(Map.prototype.clear),
1197
+ keys: Function.prototype.call.bind(Map.prototype.keys),
1198
+ size: Function.prototype.call.bind(
1199
+ Object.getOwnPropertyDescriptor(Map.prototype, "size").get
1200
+ )
1201
+ };
1202
+ var CodeRecord = class {
1203
+ constructor(token, owner, row) {
1204
+ this.owner = owner;
1205
+ this.row = row;
1206
+ if (token !== recordToken) throw new TypeError("Foreign compiler traversal authority.");
1207
+ Object.freeze(this);
1208
+ }
1209
+ owner;
1210
+ row;
1211
+ #brand = true;
1212
+ static authentic(value) {
1213
+ return #brand in value && value.#brand;
1214
+ }
1215
+ };
1216
+ function record(value) {
1217
+ if (value === null || typeof value !== "object") return void 0;
1218
+ return CodeRecord.authentic(value) ? value : handles.get(value);
1219
+ }
1220
+ var CompactModuleAst = class {
1221
+ constructor(source) {
1222
+ this.source = source;
1223
+ this.positions = new CompactSourcePositions(source);
1224
+ this.maxWords = Math.min(scalarMask - 1, Math.max(1024, source.length * 24 + 1024));
1225
+ }
1226
+ source;
1227
+ chunks = [];
1228
+ shapes = [];
1229
+ shapeIDs = /* @__PURE__ */ new Map();
1230
+ strings = [];
1231
+ stringIDs = /* @__PURE__ */ new Map();
1232
+ numbers = [];
1233
+ cache = /* @__PURE__ */ new Map();
1234
+ boundaries = /* @__PURE__ */ new Set();
1235
+ persistentBoundaries = /* @__PURE__ */ new Set();
1236
+ overrides = /* @__PURE__ */ new WeakMap();
1237
+ finished = false;
1238
+ positions;
1239
+ maxWords;
1240
+ used = 0;
1241
+ bodies = 0;
1242
+ dynamicSource;
1243
+ statistics() {
1244
+ return {
1245
+ words: this.used,
1246
+ bufferBytes: this.chunks.reduce((sum, c) => sum + c.byteLength, 0),
1247
+ bodies: this.bodies,
1248
+ materializedRecords: this.cache.size,
1249
+ strings: this.strings.length
1250
+ };
1251
+ }
1252
+ finish() {
1253
+ this.finished = true;
1254
+ if (this.chunks.length) {
1255
+ const last = this.chunks.length - 1;
1256
+ this.chunks[last] = this.chunks[last].slice(0, (this.used - 1) % chunkLength + 1);
1257
+ }
1258
+ this.stringIDs.clear();
1259
+ this.shapeIDs.clear();
1260
+ this.persistentBoundaries.clear();
1261
+ }
1262
+ /** Retirement consumes unobserved parser arrays after syntax validation. */
1263
+ pack(root, body = true, retire = false, persistent = true) {
1264
+ if (this.finished) throw new TypeError("Compiler body storage is finished.");
1265
+ const seen = /* @__PURE__ */ new WeakMap();
1266
+ const pending = [];
1267
+ const encode = (value) => {
1268
+ if (value === null || typeof value !== "object") return this.scalar(value);
1269
+ const existing = record(value);
1270
+ if (existing?.owner === this) {
1271
+ if (retire && !this.overrides.has(value)) {
1272
+ this.cache.delete(existing.row);
1273
+ if (!this.persistentBoundaries.has(existing.row)) this.boundaries.delete(existing.row);
1274
+ }
1275
+ return existing.row + 1;
1276
+ }
1277
+ const previous = seen.get(value);
1278
+ if (previous !== void 0) return previous + 1;
1279
+ let kind, row, values = [];
1280
+ const array = Array.isArray(value);
1281
+ const keys = array ? [] : Object.keys(value).filter((key) => key !== "nodeId");
1282
+ if (array) {
1283
+ kind = 0;
1284
+ row = this.reserve(2 + value.length);
1285
+ values = value;
1286
+ this.write(row, kind);
1287
+ this.write(row + 1, value.length);
1288
+ } else if (keys.length === 2 && keys[0] === "start" && keys[1] === "end") {
1289
+ const span = value, start = span.start, end = span.end;
1290
+ const startPoint = this.positions.compatiblePosition(start);
1291
+ const endPoint = this.positions.compatiblePosition(end);
1292
+ if (startPoint && endPoint) {
1293
+ kind = 1;
1294
+ row = this.reserve(3);
1295
+ this.write(row, kind);
1296
+ this.write(row + 1, start.offset);
1297
+ this.write(row + 2, end.offset);
1298
+ } else {
1299
+ kind = 2;
1300
+ row = this.reserve(7);
1301
+ this.write(row, kind);
1302
+ [start.line, start.column, start.offset, end.line, end.column, end.offset].forEach(
1303
+ (n, i) => this.write(row + 1 + i, n)
1304
+ );
1305
+ }
1306
+ } else {
1307
+ values = keys.map((key2) => value[key2]);
1308
+ if ("nodeId" in value || typeof value.type === "string" && "span" in value) {
1309
+ keys.push("@id");
1310
+ values.push(value.nodeId);
1311
+ const metadata = functionSources.get(value);
1312
+ if (metadata) {
1313
+ if (metadata.text !== this.source)
1314
+ throw new TypeError("Foreign compiler function source.");
1315
+ keys.push("@start", "@end", "@strict");
1316
+ values.push(
1317
+ metadata.start,
1318
+ metadata.end,
1319
+ functionStrictness.get(value)
1320
+ );
1321
+ }
1322
+ const template = templateSources.get(value);
1323
+ if (template !== void 0) {
1324
+ if (template !== this.source) throw new TypeError("Foreign compiler template source.");
1325
+ keys.push("@template");
1326
+ values.push(true);
1327
+ }
1328
+ }
1329
+ const key = JSON.stringify(keys);
1330
+ let shape = this.shapeIDs.get(key);
1331
+ if (shape === void 0) {
1332
+ shape = this.shapes.length;
1333
+ this.shapes.push(this.shape(keys));
1334
+ this.shapeIDs.set(key, shape);
1335
+ }
1336
+ kind = shape + 3;
1337
+ row = this.reserve(1 + values.length);
1338
+ this.write(row, kind);
1339
+ }
1340
+ seen.set(value, row);
1341
+ if (value.type === "VariableDeclarator") {
1342
+ this.boundaries.add(row);
1343
+ this.persistentBoundaries.add(row);
1344
+ }
1345
+ if (kind === 0 || kind >= 3) {
1346
+ const type = value.type;
1347
+ const bodyIndex = [
1348
+ "FunctionDeclaration",
1349
+ "FunctionExpression",
1350
+ "ArrowFunctionExpression"
1351
+ ].includes(String(type)) ? keys.indexOf("body") : -1;
1352
+ pending.push({
1353
+ row,
1354
+ kind,
1355
+ values,
1356
+ index: 0,
1357
+ bodyIndex: bodyIndex >= 0 ? bodyIndex : void 0
1358
+ });
1359
+ }
1360
+ return row + 1;
1361
+ };
1362
+ const reference = encode(root);
1363
+ while (pending.length) {
1364
+ const frame = pending[pending.length - 1];
1365
+ if (frame.index === frame.values.length) {
1366
+ pending.pop();
1367
+ continue;
1368
+ }
1369
+ const index = frame.index++;
1370
+ const child = encode(frame.values[index]);
1371
+ if (retire && frame.kind === 0)
1372
+ defineCompilerProperty(frame.values, index, { value: void 0 });
1373
+ this.write(frame.row + (frame.kind === 0 ? 2 : 1) + index, child);
1374
+ if (index === frame.bodyIndex && child >>> 28 === 0) {
1375
+ this.boundaries.add(child - 1);
1376
+ this.persistentBoundaries.add(child - 1);
1377
+ }
1378
+ }
1379
+ this.boundaries.add(reference - 1);
1380
+ if (persistent) this.persistentBoundaries.add(reference - 1);
1381
+ if (body) this.bodies++;
1382
+ return this.decode(reference);
1383
+ }
1384
+ /** Install source metadata and index numeric rows without publishing every node. */
1385
+ dynamicNodes(root, source) {
1386
+ if (!this.finished) throw new TypeError("Unfinished compiler storage.");
1387
+ if (source.body !== this.source) throw new TypeError("Foreign compiler dynamic source text.");
1388
+ if (this.dynamicSource && this.dynamicSource !== source)
1389
+ throw new TypeError("Foreign compiler dynamic source owner.");
1390
+ this.dynamicSource = source;
1391
+ const rows = new NativeRowMap(), seen = /* @__PURE__ */ new Set(), pending = [root];
1392
+ while (pending.length) {
1393
+ const row = pending.pop();
1394
+ if (seen.has(row)) continue;
1395
+ seen.add(row);
1396
+ const kind = this.read(row);
1397
+ if (kind === 0) {
1398
+ for (let i = 0, n = this.read(row + 1); i < n; i++) {
1399
+ const ref = this.read(row + 2 + i);
1400
+ if (ref >>> 28 === 0) pending.push((ref & scalarMask) - 1);
1401
+ }
1402
+ } else if (kind >= 3) {
1403
+ const keys = this.shapes[kind - 3].keys;
1404
+ const idIndex = keys.indexOf("@id"), typeIndex = keys.indexOf("type");
1405
+ if (idIndex >= 0 && typeIndex >= 0) {
1406
+ const id = this.decode(this.read(row + 1 + idIndex));
1407
+ const type = this.decode(this.read(row + 1 + typeIndex));
1408
+ if (typeof id === "number" && type !== "Module") nativeIndexMap.set(rows, id, row);
1409
+ }
1410
+ for (let i = 0; i < keys.length; i++) {
1411
+ if (keys[i].startsWith("@")) continue;
1412
+ const ref = this.read(row + 1 + i);
1413
+ if (ref >>> 28 === 0) pending.push((ref & scalarMask) - 1);
1414
+ }
1415
+ }
1416
+ }
1417
+ for (const node of this.cache.values()) {
1418
+ const id = node.nodeId;
1419
+ if (typeof id === "number" && nativeIndexMap.has(rows, id))
1420
+ dynamicNodeSources.set(node, source);
1421
+ }
1422
+ return new CompactNodeIndex(recordToken, rows, (row) => this.object(row));
1423
+ }
1424
+ scalar(value) {
1425
+ if (value === void 0) return 268435456;
1426
+ if (value === null) return 536870912;
1427
+ if (typeof value === "boolean") return 805306368 + (value ? 1 : 0);
1428
+ if (typeof value === "string") {
1429
+ let id = this.stringIDs.get(value);
1430
+ if (id === void 0) {
1431
+ id = this.strings.length;
1432
+ this.strings.push(value);
1433
+ this.stringIDs.set(value, id);
1434
+ }
1435
+ return 1073741824 + id;
1436
+ }
1437
+ if (typeof value === "number") {
1438
+ if (Number.isInteger(value) && !Object.is(value, -0) && Math.abs(value) <= scalarMask)
1439
+ return (value < 0 ? 1610612736 : 1342177280) + Math.abs(value);
1440
+ const id = this.numbers.length;
1441
+ this.numbers.push(value);
1442
+ return 1879048192 + id;
1443
+ }
1444
+ throw new TypeError("Unsupported compiler scalar.");
1445
+ }
1446
+ reserve(length) {
1447
+ const row = this.used, next = row + length;
1448
+ if (next > this.maxWords)
1449
+ throw new SandboxError({
1450
+ budget: "dataSize",
1451
+ current: next,
1452
+ limit: this.maxWords
1453
+ });
1454
+ this.used = next;
1455
+ this.write(next - 1, 0);
1456
+ return row;
1457
+ }
1458
+ read(index) {
1459
+ return this.chunks[Math.floor(index / chunkLength)][index % chunkLength];
1460
+ }
1461
+ write(index, value) {
1462
+ const chunk = Math.floor(index / chunkLength);
1463
+ this.chunks[chunk] ??= new Uint32Array(chunkLength);
1464
+ this.chunks[chunk][index % chunkLength] = value;
1465
+ }
1466
+ decode(reference, raw = false) {
1467
+ const tag = reference >>> 28, payload = reference & scalarMask;
1468
+ switch (tag) {
1469
+ case 0:
1470
+ return raw ? new CodeRecord(recordToken, this, payload - 1) : this.object(payload - 1);
1471
+ case 1:
1472
+ return void 0;
1473
+ case 2:
1474
+ return null;
1475
+ case 3:
1476
+ return payload !== 0;
1477
+ case 4:
1478
+ return this.strings[payload];
1479
+ case 5:
1480
+ return payload;
1481
+ case 6:
1482
+ return -payload;
1483
+ case 7:
1484
+ return this.numbers[payload];
1485
+ default:
1486
+ throw new TypeError("Invalid compiler reference.");
1487
+ }
1488
+ }
1489
+ shape(keys) {
1490
+ const descriptors = {};
1491
+ const owner = this;
1492
+ for (let i = 0; i < keys.length; i++) {
1493
+ const key = keys[i];
1494
+ if (key === "@id") {
1495
+ descriptors.nodeId = {
1496
+ value: void 0,
1497
+ writable: true,
1498
+ enumerable: false,
1499
+ configurable: true
1500
+ };
1501
+ continue;
1502
+ }
1503
+ if (key.startsWith("@")) continue;
1504
+ const name = key;
1505
+ descriptors[name] = {
1506
+ enumerable: key !== "@id",
1507
+ configurable: true,
1508
+ get() {
1509
+ const handle = handles.get(this);
1510
+ if (handle?.owner !== owner || owner.shapes[owner.read(handle.row) - 3]?.descriptors !== descriptors)
1511
+ throw new TypeError("Foreign compiler record receiver.");
1512
+ const assigned = owner.overrides.get(this);
1513
+ return assigned?.has(name) ? assigned.get(name) : owner.decode(owner.read(handle.row + 1 + i));
1514
+ },
1515
+ set(value) {
1516
+ const handle = handles.get(this);
1517
+ if (handle?.owner !== owner || owner.shapes[owner.read(handle.row) - 3]?.descriptors !== descriptors)
1518
+ throw new TypeError("Foreign compiler record receiver.");
1519
+ if (Object.isFrozen(this))
1520
+ throw new TypeError("Cannot assign to a frozen compiler record.");
1521
+ let assigned = owner.overrides.get(this);
1522
+ if (!assigned) {
1523
+ assigned = /* @__PURE__ */ new Map();
1524
+ owner.overrides.set(this, assigned);
1525
+ }
1526
+ assigned.set(name, value);
1527
+ }
1528
+ };
1529
+ }
1530
+ descriptors[mutableRecord] = {
1531
+ value: true,
1532
+ writable: true,
1533
+ configurable: true
1534
+ };
1535
+ function CompilerRecord() {
1536
+ Object.defineProperties(this, descriptors);
1537
+ }
1538
+ CompilerRecord.prototype = Object.prototype;
1539
+ return {
1540
+ keys,
1541
+ descriptors,
1542
+ create: () => Reflect.construct(CompilerRecord, [])
1543
+ };
1544
+ }
1545
+ attachMetadata(output, row, shape) {
1546
+ const metadata = (name) => {
1547
+ const index = shape.keys.indexOf(name);
1548
+ return index < 0 ? void 0 : this.decode(this.read(row + 1 + index));
1549
+ };
1550
+ const id = metadata("@id");
1551
+ if (id !== void 0 && this.dynamicSource && shape.keys.includes("type")) {
1552
+ const type = metadata("type");
1553
+ if (type !== "Module") dynamicNodeSources.set(output, this.dynamicSource);
1554
+ }
1555
+ if (id !== void 0)
1556
+ Object.defineProperty(output, "nodeId", {
1557
+ value: id,
1558
+ writable: true,
1559
+ enumerable: false,
1560
+ configurable: true
1561
+ });
1562
+ const start = metadata("@start");
1563
+ if (start !== void 0) {
1564
+ functionSources.set(output, {
1565
+ text: this.source,
1566
+ start,
1567
+ end: metadata("@end")
1568
+ });
1569
+ const strict = metadata("@strict");
1570
+ if (strict !== void 0)
1571
+ functionStrictness.set(
1572
+ output,
1573
+ strict
1574
+ );
1575
+ }
1576
+ if (metadata("@template") === true)
1577
+ templateSources.set(output, this.source);
1578
+ }
1579
+ object(row) {
1580
+ const cached = this.cache.get(row);
1581
+ if (cached !== void 0) return cached;
1582
+ const tasks = [];
1583
+ const make = (index) => {
1584
+ const cached2 = this.cache.get(index);
1585
+ if (cached2) return cached2;
1586
+ const kind = this.read(index);
1587
+ let output2;
1588
+ if (kind === 0) {
1589
+ const array = [];
1590
+ array.length = this.read(index + 1);
1591
+ output2 = array;
1592
+ tasks.push({ row: index, kind, output: output2 });
1593
+ } else if (kind === 1)
1594
+ output2 = this.positions.span(this.read(index + 1), this.read(index + 2));
1595
+ else if (kind === 2)
1596
+ output2 = {
1597
+ start: {
1598
+ line: this.read(index + 1),
1599
+ column: this.read(index + 2),
1600
+ offset: this.read(index + 3)
1601
+ },
1602
+ end: {
1603
+ line: this.read(index + 4),
1604
+ column: this.read(index + 5),
1605
+ offset: this.read(index + 6)
1606
+ }
1607
+ };
1608
+ else {
1609
+ const shape = this.shapes[kind - 3];
1610
+ if (this.boundaries.has(index)) {
1611
+ output2 = shape.create();
1612
+ handles.set(output2, new CodeRecord(recordToken, this, index));
1613
+ this.attachMetadata(output2, index, shape);
1614
+ } else {
1615
+ output2 = {};
1616
+ tasks.push({ row: index, kind, output: output2 });
1617
+ }
1618
+ }
1619
+ this.cache.set(index, output2);
1620
+ if (!this.finished) handles.set(output2, new CodeRecord(recordToken, this, index));
1621
+ return output2;
1622
+ };
1623
+ const value = (ref) => ref >>> 28 === 0 ? make((ref & scalarMask) - 1) : this.decode(ref);
1624
+ const output = make(row);
1625
+ while (tasks.length) {
1626
+ const { row: index, kind, output: target } = tasks.pop();
1627
+ if (kind === 0) {
1628
+ const array = target;
1629
+ for (let i = 0; i < array.length; i++) array[i] = value(this.read(index + 2 + i));
1630
+ } else {
1631
+ const shape = this.shapes[kind - 3];
1632
+ for (let i = 0; i < shape.keys.length; i++) {
1633
+ const key = shape.keys[i];
1634
+ if (!key.startsWith("@"))
1635
+ Object.defineProperty(target, key, {
1636
+ value: value(this.read(index + 1 + i)),
1637
+ writable: true,
1638
+ enumerable: true,
1639
+ configurable: true
1640
+ });
1641
+ }
1642
+ this.attachMetadata(target, index, shape);
1643
+ }
1644
+ }
1645
+ return output;
1646
+ }
1647
+ // Compiler passes use these private-owner records, not the decoded AST cache.
1648
+ isArray(row) {
1649
+ return this.read(row) === 0;
1650
+ }
1651
+ *entries(row) {
1652
+ const kind = this.read(row), cached = this.cache.get(row);
1653
+ if (cached) {
1654
+ for (const key of Object.keys(cached)) yield [key, this.field(row, key)];
1655
+ return;
1656
+ }
1657
+ if (kind === 0) {
1658
+ for (let i = 0, n = this.read(row + 1); i < n; i++)
1659
+ yield [String(i), this.decode(this.read(row + 2 + i), true)];
1660
+ } else if (kind === 1) {
1661
+ yield ["start", this.positions.position(this.read(row + 1))];
1662
+ yield ["end", this.positions.position(this.read(row + 2))];
1663
+ } else if (kind === 2) {
1664
+ yield [
1665
+ "start",
1666
+ {
1667
+ line: this.read(row + 1),
1668
+ column: this.read(row + 2),
1669
+ offset: this.read(row + 3)
1670
+ }
1671
+ ];
1672
+ yield [
1673
+ "end",
1674
+ {
1675
+ line: this.read(row + 4),
1676
+ column: this.read(row + 5),
1677
+ offset: this.read(row + 6)
1678
+ }
1679
+ ];
1680
+ } else if (kind >= 3) {
1681
+ const keys = this.shapes[kind - 3].keys;
1682
+ for (let i = 0; i < keys.length; i++) {
1683
+ const key = keys[i];
1684
+ if (!key.startsWith("@")) yield [key, this.decode(this.read(row + 1 + i), true)];
1685
+ }
1686
+ }
1687
+ }
1688
+ field(row, key) {
1689
+ const kind = this.read(row), cached = this.cache.get(row);
1690
+ if (cached) {
1691
+ const descriptor = Object.getOwnPropertyDescriptor(cached, key);
1692
+ if (!descriptor) return void 0;
1693
+ if ("value" in descriptor) return descriptor.value;
1694
+ const assigned = this.overrides.get(cached);
1695
+ if (assigned?.has(key)) return assigned.get(key);
1696
+ if (kind < 3) return Reflect.get(cached, key);
1697
+ if (descriptor.get !== this.shapes[kind - 3].descriptors[key]?.get)
1698
+ return Reflect.get(cached, key);
1699
+ }
1700
+ if (kind < 3) return void 0;
1701
+ const keys = this.shapes[kind - 3].keys, index = keys.indexOf(key === "nodeId" ? "@id" : key);
1702
+ return index < 0 ? void 0 : this.decode(this.read(row + 1 + index), true);
1703
+ }
1704
+ assignId(row, id) {
1705
+ const keys = this.shapes[this.read(row) - 3].keys, index = keys.indexOf("@id");
1706
+ if (index < 0) throw new TypeError("Compiler record is not an AST node.");
1707
+ this.write(row + 1 + index, this.scalar(id));
1708
+ const cached = this.cache.get(row);
1709
+ if (cached)
1710
+ Object.defineProperty(cached, "nodeId", {
1711
+ value: id,
1712
+ writable: true,
1713
+ enumerable: false,
1714
+ configurable: true
1715
+ });
1716
+ }
1717
+ offsets(row) {
1718
+ const span = this.field(row, "span");
1719
+ if (!(span instanceof CodeRecord)) return void 0;
1720
+ const kind = this.read(span.row);
1721
+ return kind === 1 ? [this.read(span.row + 1), this.read(span.row + 2)] : kind === 2 ? [this.read(span.row + 3), this.read(span.row + 6)] : void 0;
1722
+ }
1723
+ };
1724
+ function compilerField(value, key) {
1725
+ const handle = record(value);
1726
+ return handle ? handle.owner.field(handle.row, key) : value !== null && typeof value === "object" ? value[key] : void 0;
1727
+ }
1728
+ function compilerIsArray(value) {
1729
+ const handle = record(value);
1730
+ return handle ? handle.owner.isArray(handle.row) : Array.isArray(value);
1731
+ }
1732
+ function* compilerEntries(value) {
1733
+ const handle = record(value);
1734
+ if (handle) yield* handle.owner.entries(handle.row);
1735
+ else yield* Object.entries(value);
1736
+ }
1737
+ function* compilerElements(value) {
1738
+ if (value === null || typeof value !== "object") return;
1739
+ if (Array.isArray(value)) {
1740
+ yield* value;
1741
+ return;
1742
+ }
1743
+ for (const [, child] of compilerEntries(value)) yield child;
1744
+ }
1745
+ function compilerAssignId(value, id) {
1746
+ const handle = record(value);
1747
+ if (handle) handle.owner.assignId(handle.row, id);
1748
+ else
1749
+ Object.defineProperty(value, "nodeId", {
1750
+ value: id,
1751
+ writable: true,
1752
+ enumerable: false,
1753
+ configurable: true
1754
+ });
1755
+ }
1756
+ function compilerOffsets(value) {
1757
+ const handle = record(value);
1758
+ if (handle) return handle.owner.offsets(handle.row);
1759
+ const span = compilerField(value, "span");
1760
+ return span ? [span.start.offset, span.end.offset] : void 0;
1761
+ }
1762
+ var CompactNodeIndex = class extends Map {
1763
+ #rows;
1764
+ #decode;
1765
+ constructor(token, rows, decode) {
1766
+ super();
1767
+ if (token !== recordToken) throw new TypeError("Foreign compiler index authority.");
1768
+ this.#rows = rows;
1769
+ this.#decode = decode;
1770
+ }
1771
+ get size() {
1772
+ return nativeIndexMap.size(this.#rows);
1773
+ }
1774
+ has(key) {
1775
+ return nativeIndexMap.has(this.#rows, key);
1776
+ }
1777
+ get(key) {
1778
+ if (nativeIndexMap.has(this, key)) return nativeIndexMap.get(this, key);
1779
+ const row = nativeIndexMap.get(this.#rows, key);
1780
+ return row === void 0 ? void 0 : this.#decode(row);
1781
+ }
1782
+ set(key, value) {
1783
+ if (!nativeIndexMap.has(this.#rows, key)) nativeIndexMap.set(this.#rows, key, -1);
1784
+ nativeIndexMap.set(this, key, value);
1785
+ return this;
1786
+ }
1787
+ delete(key) {
1788
+ nativeIndexMap.delete(this, key);
1789
+ return nativeIndexMap.delete(this.#rows, key);
1790
+ }
1791
+ clear() {
1792
+ nativeIndexMap.clear(this);
1793
+ nativeIndexMap.clear(this.#rows);
1794
+ }
1795
+ keys() {
1796
+ return nativeIndexMap.keys(this.#rows);
1797
+ }
1798
+ *values() {
1799
+ for (const key of this.keys()) yield this.get(key);
1800
+ }
1801
+ *entries() {
1802
+ for (const key of this.keys()) yield [key, this.get(key)];
1803
+ }
1804
+ [Symbol.iterator]() {
1805
+ return this.entries();
1806
+ }
1807
+ forEach(callback, thisArg) {
1808
+ for (const [key, value] of this) callback.call(thisArg, value, key, this);
1809
+ }
1810
+ };
1811
+ function compactDynamicNodes(root, source) {
1812
+ const handle = record(root);
1813
+ return handle?.owner.dynamicNodes(handle.row, source);
1814
+ }
1815
+
959
1816
  // packages/safe-js/src/parse/bindings.ts
960
1817
  function getFunctionLength(params) {
961
1818
  const firstOptional = params.findIndex(
@@ -1051,66 +1908,169 @@ function* hoistedVarDeclarations(nodes) {
1051
1908
 
1052
1909
  // packages/safe-js/src/parse/private-names.ts
1053
1910
  function validatePrivateNames(root, inheritedNames) {
1054
- const pending = [{
1055
- value: root,
1056
- scope: inheritedNames === void 0 ? void 0 : { names: new Set(inheritedNames) }
1057
- }];
1911
+ const pending = [
1912
+ {
1913
+ value: root,
1914
+ scope: inheritedNames === void 0 ? void 0 : { names: new Set(inheritedNames) }
1915
+ }
1916
+ ];
1058
1917
  while (pending.length > 0) {
1059
1918
  const { value, scope, declaration } = pending.pop();
1060
1919
  if (value === null || typeof value !== "object") continue;
1061
- if (Array.isArray(value)) {
1062
- for (const entry of value) pending.push({ value: entry, scope });
1920
+ if (compilerIsArray(value)) {
1921
+ for (const entry of compilerElements(value)) pending.push({ value: entry, scope });
1063
1922
  continue;
1064
1923
  }
1065
- const node = value;
1066
- if (node.type === "ClassDeclaration" || node.type === "ClassExpression") {
1067
- const klass = value;
1068
- pending.push({ value: klass.superClass, scope });
1924
+ const type = compilerField(value, "type");
1925
+ if (type === "ClassDeclaration" || type === "ClassExpression") {
1926
+ pending.push({ value: compilerField(value, "superClass"), scope });
1069
1927
  const inner = { names: /* @__PURE__ */ new Set(), parent: scope };
1070
1928
  const declarations = /* @__PURE__ */ new Map();
1071
- for (const element of klass.body.body) {
1072
- if (element.type === "StaticBlock" || element.key.type !== "PrivateIdentifier") continue;
1073
- const name = element.key.name;
1929
+ const body = compilerField(value, "body");
1930
+ for (const element of compilerElements(compilerField(body, "body"))) {
1931
+ const key = compilerField(element, "key");
1932
+ if (compilerField(element, "type") === "StaticBlock" || compilerField(key, "type") !== "PrivateIdentifier")
1933
+ continue;
1934
+ const name = compilerField(key, "name");
1074
1935
  if (name === "constructor") throw new Error("Private name #constructor is forbidden.");
1075
- const kind = element.type === "MethodDefinition" ? element.kind : "field";
1936
+ const kind = compilerField(element, "type") === "MethodDefinition" ? compilerField(element, "kind") : "field";
1937
+ const isStatic = compilerField(element, "static");
1076
1938
  const previous = declarations.get(name);
1077
- if (previous !== void 0 && !(previous.static === element.static && (previous.kind === "get" && kind === "set" || previous.kind === "set" && kind === "get")))
1939
+ if (previous !== void 0 && !(previous.static === isStatic && (previous.kind === "get" && kind === "set" || previous.kind === "set" && kind === "get")))
1078
1940
  throw new Error(`Duplicate private name #${name}.`);
1079
- declarations.set(name, { kind: previous === void 0 ? kind : "pair", static: element.static });
1941
+ declarations.set(name, {
1942
+ kind: previous === void 0 ? kind : "pair",
1943
+ static: isStatic
1944
+ });
1080
1945
  inner.names.add(name);
1081
1946
  }
1082
- pending.push({ value: klass.body, scope: inner });
1947
+ pending.push({ value: body, scope: inner });
1083
1948
  continue;
1084
1949
  }
1085
- if (node.type === "PrivateIdentifier") {
1950
+ if (type === "PrivateIdentifier") {
1086
1951
  if (declaration) continue;
1952
+ const name = String(compilerField(value, "name"));
1087
1953
  let owner = scope;
1088
- while (owner !== void 0 && !owner.names.has(String(node.name))) owner = owner.parent;
1089
- if (owner === void 0) throw new Error(`Undeclared private name #${String(node.name)}.`);
1954
+ while (owner !== void 0 && !owner.names.has(name)) owner = owner.parent;
1955
+ if (owner === void 0) throw new Error(`Undeclared private name #${name}.`);
1090
1956
  continue;
1091
1957
  }
1092
- if (node.type === "MemberExpression") {
1093
- const property = node.property;
1094
- if (property.type === "PrivateIdentifier" && node.object.type === "Super")
1958
+ if (type === "MemberExpression") {
1959
+ const property = compilerField(value, "property");
1960
+ if (compilerField(property, "type") === "PrivateIdentifier" && compilerField(compilerField(value, "object"), "type") === "Super")
1095
1961
  throw new Error("super cannot access a private name.");
1096
1962
  }
1097
- if (node.type === "UnaryExpression" && node.operator === "delete") {
1098
- const argument = node.argument;
1099
- if (argument.type === "MemberExpression" && argument.property.type === "PrivateIdentifier")
1963
+ if (type === "UnaryExpression" && compilerField(value, "operator") === "delete") {
1964
+ const argument = compilerField(value, "argument");
1965
+ if (compilerField(argument, "type") === "MemberExpression" && compilerField(compilerField(argument, "property"), "type") === "PrivateIdentifier")
1100
1966
  throw new Error("Private elements cannot be deleted.");
1101
1967
  }
1102
- for (const [key, child] of Object.entries(node)) {
1968
+ for (const [key, child] of compilerEntries(value)) {
1103
1969
  if (key === "span") continue;
1104
1970
  pending.push({
1105
1971
  value: child,
1106
1972
  scope,
1107
- declaration: key === "key" && (node.type === "MethodDefinition" || node.type === "PropertyDefinition")
1973
+ declaration: key === "key" && (type === "MethodDefinition" || type === "PropertyDefinition")
1108
1974
  });
1109
1975
  }
1110
1976
  }
1111
1977
  }
1112
1978
 
1979
+ // packages/safe-js/src/parse/compact-tokens.ts
1980
+ var types = [
1981
+ "identifier",
1982
+ "private-identifier",
1983
+ "keyword",
1984
+ "escaped-keyword",
1985
+ "numeric",
1986
+ "regex",
1987
+ "string",
1988
+ "template",
1989
+ "punctuator",
1990
+ "eof"
1991
+ ];
1992
+ var stride = 3;
1993
+ var legacyEscape = 256;
1994
+ var tokenCacheSize = 256;
1995
+ var CompactTokens = class {
1996
+ constructor(sourcePositions) {
1997
+ this.sourcePositions = sourcePositions;
1998
+ }
1999
+ sourcePositions;
2000
+ rows = new Uint32Array(1024 * stride);
2001
+ values = [];
2002
+ templates = /* @__PURE__ */ new Map();
2003
+ tokens = /* @__PURE__ */ new Map();
2004
+ last;
2005
+ // Only indexing, iteration, length, push and at are used by the lexer/parser.
2006
+ // Historical tokens are materialized on demand in a bounded cache.
2007
+ indexed = new Proxy([], {
2008
+ get: (target, key, receiver) => {
2009
+ if (key === "length") return this.values.length;
2010
+ if (key === "push") return this.push;
2011
+ const index = arrayIndex(key);
2012
+ return index === void 0 ? Reflect.get(target, key, receiver) : this.get(index);
2013
+ },
2014
+ set: () => {
2015
+ throw new TypeError("Compiler token rows cannot be reassigned.");
2016
+ }
2017
+ });
2018
+ finish() {
2019
+ this.rows = this.rows.slice(0, this.values.length * stride);
2020
+ if (this.last) {
2021
+ this.last.start = this.sourcePositions.position(this.last.start.offset);
2022
+ this.last.end = this.sourcePositions.position(this.last.end.offset);
2023
+ }
2024
+ return this.indexed;
2025
+ }
2026
+ push = (token) => {
2027
+ if (this.last !== void 0) {
2028
+ const previous = this.values.length - 1;
2029
+ if (this.last.legacyEscape) this.rows[previous * stride + 2] |= legacyEscape;
2030
+ if (this.last.templateExpressions !== void 0)
2031
+ this.templates.set(previous, this.last.templateExpressions);
2032
+ }
2033
+ const index = this.values.length;
2034
+ const base = index * stride;
2035
+ if (base + stride > this.rows.length) {
2036
+ const rows = new Uint32Array(this.rows.length * 2);
2037
+ rows.set(this.rows);
2038
+ this.rows = rows;
2039
+ }
2040
+ this.rows.set([token.start.offset, token.end.offset, types.indexOf(token.type)], base);
2041
+ this.values.push(token.value);
2042
+ this.last = token;
2043
+ return index + 1;
2044
+ };
2045
+ get(index) {
2046
+ if (index >= this.values.length) return void 0;
2047
+ if (index === this.values.length - 1) return this.last;
2048
+ const cached = this.tokens.get(index);
2049
+ if (cached !== void 0) return cached;
2050
+ const base = index * stride;
2051
+ const bits = this.rows[base + 2];
2052
+ const token = {
2053
+ type: types[bits & 255],
2054
+ value: this.values[index],
2055
+ start: this.sourcePositions.position(this.rows[base]),
2056
+ end: this.sourcePositions.position(this.rows[base + 1])
2057
+ };
2058
+ if (bits & legacyEscape) token.legacyEscape = true;
2059
+ const expressions = this.templates.get(index);
2060
+ if (expressions !== void 0) token.templateExpressions = expressions;
2061
+ if (this.tokens.size === tokenCacheSize) this.tokens.delete(this.tokens.keys().next().value);
2062
+ this.tokens.set(index, token);
2063
+ return token;
2064
+ }
2065
+ };
2066
+ function arrayIndex(key) {
2067
+ if (typeof key !== "string") return void 0;
2068
+ const index = Number(key);
2069
+ return Number.isSafeInteger(index) && index >= 0 && String(index) === key ? index : void 0;
2070
+ }
2071
+
1113
2072
  // packages/safe-js/src/parse/tokenizer.ts
2073
+ var freezePosition = Object.freeze;
1114
2074
  var KEYWORDS = /* @__PURE__ */ new Set([
1115
2075
  "class",
1116
2076
  "extends",
@@ -1218,26 +2178,33 @@ function tokenize(source, options = {}) {
1218
2178
  const lexer = new Lexer(source, options);
1219
2179
  return lexer.tokenize();
1220
2180
  }
2181
+ function tokenizeCompact(source, options, positions) {
2182
+ const tokens = new CompactTokens(positions);
2183
+ new Lexer(source, { ...options, sharedPositions: true }, tokens.indexed).tokenize();
2184
+ return tokens.finish();
2185
+ }
1221
2186
  function collectComments(source) {
1222
2187
  const comments = [];
1223
2188
  tokenize(source, { allowRegexLiterals: true, statementList: true, comments });
1224
2189
  return comments;
1225
2190
  }
1226
2191
  var Lexer = class {
1227
- constructor(source, options) {
2192
+ constructor(source, options, tokens = []) {
1228
2193
  this.source = source;
1229
2194
  this.options = options;
2195
+ this.tokens = tokens;
1230
2196
  }
1231
2197
  source;
1232
2198
  options;
2199
+ tokens;
1233
2200
  index = 0;
1234
2201
  line = 1;
1235
2202
  column = 1;
1236
- tokens = [];
1237
2203
  groupingState = { stack: [], classHeads: [], tokenCount: 0 };
1238
2204
  lastClosedStatementBoundary = false;
1239
2205
  legacyStringEscape = false;
1240
2206
  lineHasToken = false;
2207
+ lastPosition;
1241
2208
  tokenize() {
1242
2209
  while (!this.isAtEnd()) {
1243
2210
  this.skipTrivia();
@@ -2011,11 +2978,15 @@ var Lexer = class {
2011
2978
  return this.index >= this.source.length;
2012
2979
  }
2013
2980
  position() {
2014
- return {
2981
+ if (this.options.sharedPositions && this.lastPosition?.offset === this.index)
2982
+ return this.lastPosition;
2983
+ const position = {
2015
2984
  line: this.line,
2016
2985
  column: this.column,
2017
2986
  offset: this.index
2018
2987
  };
2988
+ if (!this.options.sharedPositions) return position;
2989
+ return this.lastPosition = freezePosition(position);
2019
2990
  }
2020
2991
  advance() {
2021
2992
  if (this.isAtEnd()) {
@@ -2316,15 +3287,39 @@ function compareBySourceOrder(left, right) {
2316
3287
  function isAstNode(value) {
2317
3288
  return typeof value === "object" && value !== null && "type" in value && typeof value.type === "string" && "span" in value && typeof value.span?.start.offset === "number" && typeof value.span?.end.offset === "number";
2318
3289
  }
2319
-
2320
- // packages/safe-js/src/parse/function-source.ts
2321
- var functionSources = /* @__PURE__ */ new WeakMap();
2322
- var templateSources = /* @__PURE__ */ new WeakMap();
2323
- var functionStrictness = /* @__PURE__ */ new WeakMap();
2324
- var evalFunctionDeclarations = /* @__PURE__ */ new WeakSet();
2325
- var dynamicSourceRecords = /* @__PURE__ */ new WeakSet();
2326
- var dynamicNodeSources = /* @__PURE__ */ new WeakMap();
2327
- var dynamicValueSources = /* @__PURE__ */ new WeakMap();
3290
+ function assignCompactIds(root) {
3291
+ const stack = [root];
3292
+ let nextId = 0;
3293
+ const isNode = (value) => value !== null && typeof value === "object" && typeof compilerField(value, "type") === "string" && compilerOffsets(value) !== void 0;
3294
+ while (stack.length) {
3295
+ const node = stack.pop();
3296
+ if (!isNode(node) || compilerField(node, "nodeId") !== void 0) continue;
3297
+ compilerAssignId(node, nextId++);
3298
+ const type = compilerField(node, "type");
3299
+ const children = [];
3300
+ const add = (value) => {
3301
+ if (isNode(value)) children.push(value);
3302
+ else if (compilerIsArray(value)) {
3303
+ for (const child of compilerElements(value))
3304
+ if (isNode(child)) children.push(child);
3305
+ }
3306
+ };
3307
+ if (type === "UnaryExpression") add(compilerField(node, "argument"));
3308
+ else if (type === "ExpressionStatement")
3309
+ add(compilerField(node, "expression"));
3310
+ else if (type === "Module") add(compilerField(node, "body"));
3311
+ else {
3312
+ for (const [key, child] of compilerEntries(node))
3313
+ if (key !== "nodeId" && key !== "span" && key !== "type") add(child);
3314
+ children.sort((left, right) => {
3315
+ const a = compilerOffsets(left), b = compilerOffsets(right);
3316
+ return a[0] - b[0] || a[1] - b[1];
3317
+ });
3318
+ }
3319
+ for (let i = children.length - 1; i >= 0; i--) stack.push(children[i]);
3320
+ }
3321
+ return root;
3322
+ }
2328
3323
 
2329
3324
  // packages/safe-js/src/parse/syntax-diagnostic.ts
2330
3325
  var syntaxDiagnostics = /* @__PURE__ */ new WeakMap();
@@ -3121,6 +4116,7 @@ function allHexDigits(value) {
3121
4116
  }
3122
4117
 
3123
4118
  // packages/safe-js/src/parse/parser.ts
4119
+ var isFrozenPosition = Object.isFrozen;
3124
4120
  var MAX_CONDITIONAL_EXPRESSION_DEPTH = 256;
3125
4121
  var MAX_IF_STATEMENT_DEPTH = 2048;
3126
4122
  var STRICT_BINDING_NAMES = /* @__PURE__ */ new Set(["eval", "arguments", "implements", "interface", "let", "package", "private", "protected", "public", "static", "yield"]);
@@ -3211,7 +4207,7 @@ function parseModule(source, filename = "<input>", owner) {
3211
4207
  compilation.dispose();
3212
4208
  }
3213
4209
  }
3214
- function parseSourceModule(source, filename = "<input>", owner) {
4210
+ function parseSourceModule(source, filename = "<input>", owner, options = {}) {
3215
4211
  const compilation = new CompileScope(owner);
3216
4212
  const syntax = { imports: [], exports: [], moduleRequests: [] };
3217
4213
  let parser;
@@ -3220,8 +4216,10 @@ function parseSourceModule(source, filename = "<input>", owner) {
3220
4216
  if (limit !== void 0 && source.length > limit)
3221
4217
  throw new SandboxError({ budget: "stringLength", current: source.length, limit });
3222
4218
  owner?.budget.visitNode(source.length);
4219
+ const tokenOptions = { allowRegexLiterals: true, statementList: true, compilation, sharedPositions: options.sharedPositions };
4220
+ const tokens = options.compactAst ? tokenizeCompact(source, tokenOptions, new CompactSourcePositions(source)) : tokenize(source, tokenOptions);
3223
4221
  parser = new Parser(
3224
- tokenize(source, { allowRegexLiterals: true, statementList: true, compilation }),
4222
+ tokens,
3225
4223
  source,
3226
4224
  compilation,
3227
4225
  "top-level",
@@ -3235,7 +4233,10 @@ function parseSourceModule(source, filename = "<input>", owner) {
3235
4233
  true,
3236
4234
  syntax
3237
4235
  );
3238
- const module = assignIds(parser.parseModule());
4236
+ if (options.compactAst) parser.enableCompactAst();
4237
+ const parsed = parser.parseModule();
4238
+ parser.finishAstStorage();
4239
+ const module = (options.compactAst ? assignCompactIds : assignIds)(parsed);
3239
4240
  if (source.includes("#")) validatePrivateNames(module);
3240
4241
  let hasTLA = false;
3241
4242
  const pending = [module];
@@ -3243,9 +4244,10 @@ function parseSourceModule(source, filename = "<input>", owner) {
3243
4244
  const node = pending.pop();
3244
4245
  if (node === null || typeof node !== "object") continue;
3245
4246
  owner?.budget.visitNode();
3246
- if ("type" in node && ["FunctionDeclaration", "FunctionExpression", "ArrowFunctionExpression"].includes(String(node.type))) continue;
3247
- if ("type" in node && (node.type === "AwaitExpression" || "await" in node && node.await === true || node.type === "VariableDeclaration" && "disposal" in node && node.disposal === "async")) hasTLA = true;
3248
- pending.push(...Object.values(node));
4247
+ const type = compilerField(node, "type");
4248
+ if (["FunctionDeclaration", "FunctionExpression", "ArrowFunctionExpression"].includes(String(type))) continue;
4249
+ if (type !== void 0 && (type === "AwaitExpression" || compilerField(node, "await") === true || type === "VariableDeclaration" && compilerField(node, "disposal") === "async")) hasTLA = true;
4250
+ for (const child of compilerElements(node)) pending.push(child);
3249
4251
  }
3250
4252
  return { ...syntax, module, hasTLA, requests: [...new Set(syntax.moduleRequests.map((request) => request.specifier))] };
3251
4253
  } catch (error) {
@@ -3259,7 +4261,7 @@ function parseSourceModule(source, filename = "<input>", owner) {
3259
4261
  compilation.dispose();
3260
4262
  }
3261
4263
  }
3262
- function parseExecutableModule(source, filename = "<input>", owner) {
4264
+ function parseExecutableModule(source, filename = "<input>", owner, importSpecifiers) {
3263
4265
  const compilation = new CompileScope(owner);
3264
4266
  try {
3265
4267
  const result = assignIds(
@@ -3268,7 +4270,10 @@ function parseExecutableModule(source, filename = "<input>", owner) {
3268
4270
  source,
3269
4271
  compilation,
3270
4272
  "top-level",
3271
- { ...ordinaryFunctionContext, newTarget: false, requireAsyncAwait: true }
4273
+ { ...ordinaryFunctionContext, newTarget: false, requireAsyncAwait: true },
4274
+ true,
4275
+ void 0,
4276
+ new Set(importSpecifiers)
3272
4277
  ).parseModule()
3273
4278
  );
3274
4279
  throwIfImportMetaAssignment(result);
@@ -3286,7 +4291,7 @@ function parseExecutableModule(source, filename = "<input>", owner) {
3286
4291
  compilation.dispose();
3287
4292
  }
3288
4293
  }
3289
- function parseEvalScript(source, context = {}, owner) {
4294
+ function parseEvalScript(source, context = {}, owner, filename = "<eval>", options = {}) {
3290
4295
  const compilation = new CompileScope(owner);
3291
4296
  try {
3292
4297
  const limit = owner?.budget.limits.stringLength;
@@ -3294,14 +4299,16 @@ function parseEvalScript(source, context = {}, owner) {
3294
4299
  throw new SandboxError({ budget: "stringLength", current: source.length, limit });
3295
4300
  if (owner !== void 0) for (let index = 0; index < source.length; index++) owner.budget.visitNode();
3296
4301
  const grammar = { await: false, yield: false, strict: context.strict === true };
3297
- const parser = new Parser(tokenize(source, {
4302
+ const tokenOptions = {
3298
4303
  allowRegexLiterals: true,
3299
4304
  statementList: true,
3300
4305
  allowLegacyNumbers: true,
3301
4306
  allowLegacyEscapes: true,
3302
4307
  allowHtmlComments: true,
3303
4308
  compilation
3304
- }), source, compilation, "normal", {
4309
+ };
4310
+ const tokens = options.compactAst ? tokenizeCompact(source, tokenOptions, new CompactSourcePositions(source)) : tokenize(source, tokenOptions);
4311
+ const parser = new Parser(tokens, source, compilation, "normal", {
3305
4312
  grammar,
3306
4313
  newTarget: context.newTarget === true,
3307
4314
  superProperty: context.superProperty === true,
@@ -3311,7 +4318,10 @@ function parseEvalScript(source, context = {}, owner) {
3311
4318
  await: false,
3312
4319
  strictAwait: true
3313
4320
  }, false);
3314
- const node = assignIds(parser.parseScript());
4321
+ if (options.compactAst) parser.enableCompactAst();
4322
+ const parsed = parser.parseScript();
4323
+ parser.finishAstStorage();
4324
+ const node = (options.compactAst ? assignCompactIds : assignIds)(parsed);
3315
4325
  if (source.includes("#")) validatePrivateNames(node, context.privateNames);
3316
4326
  for (const statement of node.body) {
3317
4327
  if (statement.type === "FunctionDeclaration") evalFunctionDeclarations.add(statement);
@@ -3319,7 +4329,7 @@ function parseEvalScript(source, context = {}, owner) {
3319
4329
  return { node, strict: grammar.strict };
3320
4330
  } catch (error) {
3321
4331
  if (error instanceof SandboxError) throw error;
3322
- throw createSyntaxDiagnostic(source, "<eval>", error);
4332
+ throw createSyntaxDiagnostic(source, filename, error);
3323
4333
  } finally {
3324
4334
  compilation.dispose();
3325
4335
  }
@@ -3373,7 +4383,7 @@ var ordinaryFunctionContext = {
3373
4383
  await: true
3374
4384
  };
3375
4385
  var Parser = class {
3376
- constructor(tokens, source, compilation, functionContext = "top-level", lexicalContext = { ...ordinaryFunctionContext, newTarget: false }, allowImportMeta = true, moduleSyntax) {
4386
+ constructor(tokens, source, compilation, functionContext = "top-level", lexicalContext = { ...ordinaryFunctionContext, newTarget: false }, allowImportMeta = true, moduleSyntax, importSpecifiers) {
3377
4387
  this.tokens = tokens;
3378
4388
  this.source = source;
3379
4389
  this.compilation = compilation;
@@ -3381,6 +4391,7 @@ var Parser = class {
3381
4391
  this.lexicalContext = lexicalContext;
3382
4392
  this.allowImportMeta = allowImportMeta;
3383
4393
  this.moduleSyntax = moduleSyntax;
4394
+ this.importSpecifiers = importSpecifiers;
3384
4395
  this.functionScopes.add(this.scopes[0]);
3385
4396
  }
3386
4397
  tokens;
@@ -3390,6 +4401,7 @@ var Parser = class {
3390
4401
  lexicalContext;
3391
4402
  allowImportMeta;
3392
4403
  moduleSyntax;
4404
+ importSpecifiers;
3393
4405
  index = 0;
3394
4406
  allowIn = true;
3395
4407
  breakableDepth = 0;
@@ -3404,6 +4416,14 @@ var Parser = class {
3404
4416
  varNames = /* @__PURE__ */ new WeakMap();
3405
4417
  sourceExportNames = /* @__PURE__ */ new Set();
3406
4418
  sourceLocalExports = /* @__PURE__ */ new Map();
4419
+ astStorage;
4420
+ enableCompactAst() {
4421
+ if (this.astStorage) throw new TypeError("Compiler body storage is already enabled.");
4422
+ this.astStorage = new CompactModuleAst(this.source);
4423
+ }
4424
+ finishAstStorage() {
4425
+ this.astStorage?.finish();
4426
+ }
3407
4427
  get position() {
3408
4428
  return (this.tokens[this.index] ?? this.tokens[this.tokens.length - 1]).start;
3409
4429
  }
@@ -3417,6 +4437,8 @@ var Parser = class {
3417
4437
  start: node.span.start.offset,
3418
4438
  end: node.span.end.offset
3419
4439
  });
4440
+ if (this.astStorage && node.type !== "ClassDeclaration" && node.type !== "ClassExpression" && node.body.span.end.offset - node.body.span.start.offset >= 512)
4441
+ node.body = this.astStorage.pack(node.body, true, true);
3420
4442
  return node;
3421
4443
  }
3422
4444
  parseDynamicParameters() {
@@ -3454,7 +4476,8 @@ var Parser = class {
3454
4476
  }
3455
4477
  if (this.isExportToken(token)) {
3456
4478
  const statement2 = this.parseSourceExport();
3457
- if (statement2 !== void 0) body.push(statement2);
4479
+ if (statement2 !== void 0)
4480
+ body.push(this.astStorage?.pack(statement2, false, true) ?? statement2);
3458
4481
  continue;
3459
4482
  }
3460
4483
  }
@@ -3463,26 +4486,29 @@ var Parser = class {
3463
4486
  while (statement.type !== "EmptyStatement" && this.consumePunctuator(";") !== void 0) {
3464
4487
  continue;
3465
4488
  }
4489
+ if (this.astStorage) body[body.length - 1] = this.astStorage.pack(statement, false, true);
3466
4490
  }
3467
4491
  for (const [name, identifier] of this.sourceLocalExports) {
3468
4492
  if (!this.scopes[0].has(name) && !this.varNames.get(this.scopes[0])?.has(name))
3469
4493
  throw new Error(`Export '${name}' is not defined at line ${identifier.span.start.line}, column ${identifier.span.start.column}.`);
3470
4494
  }
3471
4495
  const end = this.currentToken().end;
3472
- return {
4496
+ const module = {
3473
4497
  type: "Module",
3474
4498
  body,
3475
4499
  span: createSpan2(body[0]?.span.start ?? end, body[body.length - 1]?.span.end ?? end)
3476
4500
  };
4501
+ return this.astStorage?.pack(module, false, true) ?? module;
3477
4502
  }
3478
4503
  parseScript() {
3479
4504
  const body = this.parseStatementList(void 0, true);
3480
4505
  const end = this.currentToken().end;
3481
- return {
4506
+ const module = {
3482
4507
  type: "Module",
3483
4508
  body,
3484
4509
  span: createSpan2(body[0]?.span.start ?? end, body[body.length - 1]?.span.end ?? end)
3485
4510
  };
4511
+ return this.astStorage?.pack(module, false, true) ?? module;
3486
4512
  }
3487
4513
  parseExpressionOnly() {
3488
4514
  const expression = this.parseExpression({ allowSequence: true }).node;
@@ -3765,6 +4791,8 @@ var Parser = class {
3765
4791
  while (statement.type !== "EmptyStatement" && this.consumePunctuator(";") !== void 0) {
3766
4792
  if (++semicolons > 1) directivePrologue = false;
3767
4793
  }
4794
+ if (start === void 0 && this.astStorage)
4795
+ body[body.length - 1] = this.astStorage.pack(statement, false, true);
3768
4796
  }
3769
4797
  return body;
3770
4798
  }
@@ -4457,7 +5485,7 @@ var Parser = class {
4457
5485
  }
4458
5486
  this.index += 1;
4459
5487
  const source = createStringLiteral(sourceToken);
4460
- assertBareImportSpecifier(source);
5488
+ if (!this.importSpecifiers?.has(source.value)) assertBareImportSpecifier(source);
4461
5489
  return {
4462
5490
  type: "ImportDeclaration",
4463
5491
  specifiers,
@@ -7368,14 +8396,16 @@ function positionWithinRaw(base, raw, index) {
7368
8396
  }
7369
8397
  column += 1;
7370
8398
  }
7371
- return { line, column, offset };
8399
+ const position = { line, column, offset };
8400
+ return compactDerivedPosition(base, position) ?? position;
7372
8401
  }
7373
8402
  function rebasePosition(position, base) {
7374
- return {
8403
+ const rebased = {
7375
8404
  line: base.line + position.line - 1,
7376
8405
  column: position.line === 1 ? base.column + position.column - 1 : position.column,
7377
8406
  offset: base.offset + position.offset
7378
8407
  };
8408
+ return compactDerivedPosition(base, rebased) ?? rebased;
7379
8409
  }
7380
8410
  function isLiteralPropertyKey(token) {
7381
8411
  return token.type === "numeric" || token.type === "string";
@@ -7395,9 +8425,9 @@ function assertAllowedIdentifierReference(token, strict) {
7395
8425
  }
7396
8426
  }
7397
8427
  function createSpan2(start, end) {
7398
- return {
7399
- start: { ...start },
7400
- end: { ...end }
8428
+ return compactSourceSpan(start, end) ?? {
8429
+ start: isFrozenPosition(start) ? start : { ...start },
8430
+ end: isFrozenPosition(end) ? end : { ...end }
7401
8431
  };
7402
8432
  }
7403
8433
  function hasLineBreakBetween(left, right) {
@@ -7554,10 +8584,6 @@ var numericTypedArrayConstructors = {
7554
8584
  };
7555
8585
 
7556
8586
  export {
7557
- getFunctionLength,
7558
- boundIdentifiers,
7559
- containsParameterExpression,
7560
- hoistedVarDeclarations,
7561
8587
  internalSymbols,
7562
8588
  sandboxErrorNames,
7563
8589
  sandboxErrorTypes,
@@ -7575,18 +8601,22 @@ export {
7575
8601
  SandboxError,
7576
8602
  isFatalSandboxError,
7577
8603
  Budget,
7578
- allocateRegexSteps,
7579
- CompileScope,
7580
- RegexCompileGuard,
7581
- regexCompiledData,
7582
- tokenize,
7583
- collectComments,
7584
8604
  functionSources,
7585
8605
  functionStrictness,
7586
8606
  evalFunctionDeclarations,
7587
8607
  dynamicSourceRecords,
7588
8608
  dynamicNodeSources,
7589
8609
  dynamicValueSources,
8610
+ compactDynamicNodes,
8611
+ getFunctionLength,
8612
+ boundIdentifiers,
8613
+ containsParameterExpression,
8614
+ hoistedVarDeclarations,
8615
+ CompileScope,
8616
+ RegexCompileGuard,
8617
+ regexCompiledData,
8618
+ tokenize,
8619
+ collectComments,
7590
8620
  splitSourceLines,
7591
8621
  ParseError,
7592
8622
  formatParseError,
@@ -7615,4 +8645,4 @@ export {
7615
8645
  Float16Array2 as Float16Array,
7616
8646
  numericTypedArrayConstructors
7617
8647
  };
7618
- //# sourceMappingURL=chunk-IDQ67DY2.js.map
8648
+ //# sourceMappingURL=chunk-RYVHEOHM.js.map