@poe-platform/safe-bash 0.1.434 → 0.1.436

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 (40) hide show
  1. package/dist/safe-bash/browser.js +350 -113
  2. package/dist/safe-bash/browser.js.map +3 -3
  3. package/dist/safe-bash/commands/stream-format/nl.js +1 -1
  4. package/dist/safe-bash/commands/stream-format/nl.js.map +1 -1
  5. package/dist/safe-bash/commands/structured/interpreter.js +10 -10
  6. package/dist/safe-bash/commands/structured/interpreter.js.map +1 -1
  7. package/dist/safe-bash/commands/structured/limits.d.ts +3 -1
  8. package/dist/safe-bash/commands/structured/limits.d.ts.map +1 -1
  9. package/dist/safe-bash/commands/structured/limits.js +16 -5
  10. package/dist/safe-bash/commands/structured/limits.js.map +1 -1
  11. package/dist/safe-bash/commands/structured/values.d.ts +5 -3
  12. package/dist/safe-bash/commands/structured/values.d.ts.map +1 -1
  13. package/dist/safe-bash/commands/structured/values.js +73 -21
  14. package/dist/safe-bash/commands/structured/values.js.map +1 -1
  15. package/dist/safe-bash/commands/text-programs/awk-runtime.js +4 -4
  16. package/dist/safe-bash/commands/text-programs/awk-runtime.js.map +1 -1
  17. package/dist/safe-bash/commands/text-programs/regex.d.ts +2 -1
  18. package/dist/safe-bash/commands/text-programs/regex.d.ts.map +1 -1
  19. package/dist/safe-bash/commands/text-programs/regex.js +230 -93
  20. package/dist/safe-bash/commands/text-programs/regex.js.map +1 -1
  21. package/dist/safe-bash/commands/text-programs/sed.js +1 -1
  22. package/dist/safe-bash/commands/text-programs/sed.js.map +1 -1
  23. package/dist/safe-bash/portable.js +439 -148
  24. package/dist/safe-bash/portable.js.map +3 -3
  25. package/dist/safe-bash/shell/arrays/state.d.ts +6 -1
  26. package/dist/safe-bash/shell/arrays/state.d.ts.map +1 -1
  27. package/dist/safe-bash/shell/arrays/state.js +30 -3
  28. package/dist/safe-bash/shell/arrays/state.js.map +1 -1
  29. package/dist/safe-bash/shell/cleanup.d.ts +1 -1
  30. package/dist/safe-bash/shell/cleanup.d.ts.map +1 -1
  31. package/dist/safe-bash/shell/cleanup.js +7 -3
  32. package/dist/safe-bash/shell/cleanup.js.map +1 -1
  33. package/dist/safe-bash/shell/getopts.d.ts +10 -1
  34. package/dist/safe-bash/shell/getopts.d.ts.map +1 -1
  35. package/dist/safe-bash/shell/getopts.js +55 -6
  36. package/dist/safe-bash/shell/getopts.js.map +1 -1
  37. package/dist/safe-bash/shell/runtime.d.ts.map +1 -1
  38. package/dist/safe-bash/shell/runtime.js +69 -27
  39. package/dist/safe-bash/shell/runtime.js.map +1 -1
  40. package/package.json +2 -2
@@ -9219,7 +9219,7 @@ var InvocationScope = class _InvocationScope {
9219
9219
  failures;
9220
9220
  parent;
9221
9221
  #children = /* @__PURE__ */ new Set();
9222
- #callbacks = [];
9222
+ #callbacks = /* @__PURE__ */ new Map();
9223
9223
  #finalizers = [];
9224
9224
  #work = /* @__PURE__ */ new Set();
9225
9225
  #controller = new AbortController();
@@ -9246,7 +9246,11 @@ var InvocationScope = class _InvocationScope {
9246
9246
  register(cleanup) {
9247
9247
  this.assertOpen();
9248
9248
  if (typeof cleanup !== "function") throw new TypeError("Cleanup must be callable");
9249
- this.#callbacks.push(cleanup);
9249
+ const registration = /* @__PURE__ */ Symbol();
9250
+ this.#callbacks.set(registration, cleanup);
9251
+ return () => {
9252
+ this.#callbacks.delete(registration);
9253
+ };
9250
9254
  }
9251
9255
  run(operation) {
9252
9256
  this.assertOpen();
@@ -9279,9 +9283,11 @@ var InvocationScope = class _InvocationScope {
9279
9283
  close() {
9280
9284
  if (!this.#drain) {
9281
9285
  this.#drain = Promise.resolve().then(async () => {
9286
+ const callbacks = [...this.#callbacks.values()];
9287
+ this.#callbacks.clear();
9282
9288
  try {
9283
9289
  await Promise.all([
9284
- ...this.#callbacks.splice(0).map((cleanup) => this.cleanup(cleanup)),
9290
+ ...callbacks.map((cleanup) => this.cleanup(cleanup)),
9285
9291
  ...[...this.#children].map((child) => child.close()),
9286
9292
  ...this.#work
9287
9293
  ]);
@@ -9308,6 +9314,39 @@ function throwCleanupFailures(failures) {
9308
9314
 
9309
9315
  // packages/safe-bash/src/shell/getopts.ts
9310
9316
  init_platform();
9317
+ var ownedInputs = /* @__PURE__ */ new WeakMap();
9318
+ function getoptsInputAllocationSize(argumentsCount) {
9319
+ return { bytes: 256 + argumentsCount * 8, slots: argumentsCount + 2 };
9320
+ }
9321
+ async function createGetoptsInput(args, allocation, controls2) {
9322
+ const work = new ScanWork(controls2);
9323
+ if (!Array.isArray(args)) throw new GetoptsError("INVALID_INPUT", "Getopts requires an argument array");
9324
+ const length = args.length;
9325
+ if (length > work.maxArguments) throw new GetoptsError("ARGUMENT_LIMIT", "Getopts argument limit exceeded");
9326
+ const size = getoptsInputAllocationSize(length);
9327
+ const reservation = allocation?.reserve(size.bytes, size.slots);
9328
+ try {
9329
+ const snapshot = [];
9330
+ for (let index = 0; index < length; index++) {
9331
+ const waiting = work.step();
9332
+ if (waiting) await waiting;
9333
+ snapshot.push(args[index]);
9334
+ }
9335
+ await work.flush();
9336
+ if (args.length !== length) throw new GetoptsError("INVALID_INPUT", "Getopts argument extent changed during admission");
9337
+ const input3 = Object.freeze({ args: Object.freeze(snapshot) });
9338
+ reservation?.commit(input3);
9339
+ ownedInputs.set(input3, {});
9340
+ return input3;
9341
+ } catch (error) {
9342
+ try {
9343
+ reservation?.release();
9344
+ } catch (cleanup) {
9345
+ throw new AggregateError([error, cleanup], "Getopts input allocation and release failed");
9346
+ }
9347
+ throw error;
9348
+ }
9349
+ }
9311
9350
  var GetoptsError = class extends Error {
9312
9351
  constructor(code, message2) {
9313
9352
  super(message2);
@@ -9372,6 +9411,9 @@ var ScanWork = class {
9372
9411
  check() {
9373
9412
  this.signal?.throwIfAborted();
9374
9413
  }
9414
+ get inputBytes() {
9415
+ return this.bytes;
9416
+ }
9375
9417
  step() {
9376
9418
  this.check();
9377
9419
  if (this.steps === this.maxSteps) throw new GetoptsError("STEP_LIMIT", "Getopts work step limit exceeded");
@@ -9428,19 +9470,25 @@ async function validateString(value2, work, optstring = false) {
9428
9470
  else work.addBytes(3);
9429
9471
  }
9430
9472
  }
9431
- async function scanGetopts(state, optstring, args, options3) {
9473
+ async function scanGetopts(state, optstring, input3, options3) {
9432
9474
  if (!record2(options3) || typeof options3.reportErrors !== "boolean") throw new GetoptsError("INVALID_INPUT", "Getopts requires explicit diagnostic policy and work controls");
9433
9475
  const work = new ScanWork(options3.work);
9434
9476
  const original = cloneGetoptsState(state);
9477
+ const owned = ownedInputs.get(input3);
9478
+ const args = owned ? input3.args : input3;
9435
9479
  if (!Array.isArray(args)) throw new GetoptsError("INVALID_INPUT", "Getopts requires an argument array");
9436
9480
  if (args.length > work.maxArguments) throw new GetoptsError("ARGUMENT_LIMIT", "Getopts argument limit exceeded");
9437
9481
  const starting = work.step();
9438
9482
  if (starting) await starting;
9439
9483
  await validateString(optstring, work, true);
9440
- for (let argument = 0; argument < args.length; argument++) {
9441
- const waiting = work.step();
9442
- if (waiting) await waiting;
9443
- await validateString(args[argument], work);
9484
+ const specificationBytes = work.inputBytes;
9485
+ if (owned?.bytes !== void 0) work.addBytes(owned.bytes);
9486
+ else {
9487
+ for (let argument = 0; argument < args.length; argument++) {
9488
+ const waiting = work.step();
9489
+ if (waiting) await waiting;
9490
+ await validateString(args[argument], work);
9491
+ }
9444
9492
  }
9445
9493
  const silent = optstring.startsWith(":");
9446
9494
  const specification = new Int8Array(128).fill(-1);
@@ -9456,6 +9504,7 @@ async function scanGetopts(state, optstring, args, options3) {
9456
9504
  const waiting = work.step();
9457
9505
  if (waiting) await waiting;
9458
9506
  await work.flush();
9507
+ if (owned) owned.bytes = work.inputBytes - specificationBytes;
9459
9508
  return { state: active === void 0 ? { index } : { index, active: { ...active } }, kind, status: kind === "end" ? 1 : 0, option: option2, optind: index, argument, diagnostic: diagnostic3 };
9460
9509
  };
9461
9510
  const end = () => {
@@ -10603,7 +10652,7 @@ var StateMonitor = class {
10603
10652
  monitors.set(raw, this);
10604
10653
  monitors.set(this.proxy, this);
10605
10654
  if (session.monitors) session.monitors.add(this);
10606
- else session.scope.register(async () => {
10655
+ else this.#retireCleanup = session.scope.register(async () => {
10607
10656
  await session.scope.drainWork();
10608
10657
  this.closeValues();
10609
10658
  });
@@ -10622,18 +10671,46 @@ var StateMonitor = class {
10622
10671
  #internalEnrollment;
10623
10672
  #restorations;
10624
10673
  #overlays;
10674
+ #retireCleanup;
10675
+ #positionalRevision = {};
10676
+ #getoptsInput;
10625
10677
  closeValues() {
10626
10678
  this.values.close();
10627
10679
  this.positionals.close();
10680
+ this.invalidateGetoptsInput();
10681
+ this.#retireCleanup?.();
10682
+ this.#retireCleanup = void 0;
10683
+ }
10684
+ get positionalRevision() {
10685
+ return this.#positionalRevision;
10686
+ }
10687
+ get getoptsInput() {
10688
+ return this.#getoptsInput?.input;
10689
+ }
10690
+ retainGetoptsInput(revision, input3, allocation) {
10691
+ this.session.scope.assertOpen();
10692
+ if (revision !== this.#positionalRevision) return false;
10693
+ this.#getoptsInput?.allocation.close();
10694
+ this.#getoptsInput = { input: input3, allocation };
10695
+ return true;
10696
+ }
10697
+ invalidateGetoptsInput() {
10698
+ this.#getoptsInput?.allocation.close();
10699
+ this.#getoptsInput = void 0;
10700
+ this.#positionalRevision = {};
10628
10701
  }
10629
10702
  changedValue(target, field, key) {
10630
10703
  if (field === "state") {
10631
10704
  if (key === "variables") this.values.invalidate();
10632
- if (key === "positional") this.positionals.invalidate();
10705
+ if (key === "positional") {
10706
+ this.positionals.invalidate();
10707
+ this.invalidateGetoptsInput();
10708
+ }
10633
10709
  } else if (field === "variables" && (this.raw.variables === target || this.raw.variables === this.#wrapped.get(target))) {
10634
10710
  this.values.invalidate(String(key));
10635
10711
  } else if (field === "positional" && (this.raw.positional === target || this.raw.positional === this.#wrapped.get(target))) {
10636
10712
  this.positionals.invalidate();
10713
+ this.invalidateGetoptsInput();
10637
10714
  }
10638
10715
  }
10639
10716
  internalOwner() {
@@ -12592,7 +12669,7 @@ var InvocationCancellationOwner = class {
12592
12669
  this.finalized = new Promise((resolve) => {
12593
12670
  this.#resolveFinalized = resolve;
12594
12671
  });
12595
- parent.register(async () => {
12672
+ this.#retireCleanup = parent.register(async () => {
12596
12673
  this.requestClose();
12597
12674
  await this.finalized;
12598
12675
  });
@@ -12602,6 +12679,7 @@ var InvocationCancellationOwner = class {
12602
12679
  #failures;
12603
12680
  #outcomes;
12604
12681
  #publicPromise;
12682
+ #retireCleanup;
12605
12683
  #resolveFinalized;
12606
12684
  #admissionOpen = true;
12607
12685
  #boundary;
@@ -12678,6 +12756,7 @@ var InvocationCancellationOwner = class {
12678
12756
  this.#outcomes.discard(this.#record);
12679
12757
  this.#closeBoundary();
12680
12758
  this.#resolveFinalized();
12759
+ this.#retireCleanup();
12681
12760
  }
12682
12761
  }
12683
12762
  async #finishOnce(barrier, captured) {
@@ -12690,6 +12769,7 @@ var InvocationCancellationOwner = class {
12690
12769
  return selection;
12691
12770
  } finally {
12692
12771
  this.#resolveFinalized();
12772
+ this.#retireCleanup();
12693
12773
  }
12694
12774
  }
12695
12775
  #closeBoundary() {
@@ -14044,10 +14124,18 @@ var Runtime = class _Runtime {
14044
14124
  if (--file.references === 0 && this.outputFiles.get(path) === file) this.outputFiles.delete(path);
14045
14125
  };
14046
14126
  let target2;
14127
+ let outputScope;
14047
14128
  try {
14048
- target2 = await openFileOutput({ fs: this.fs, signal: this.signal, registerCleanup: (cleanup) => io[invocationScope].register(cleanup) }, path, append ? "a" : "w", random ? incremental : void 0);
14129
+ outputScope = io[invocationScope].child();
14130
+ target2 = await openFileOutput({ fs: this.fs, signal: this.signal, registerCleanup: (cleanup) => {
14131
+ outputScope.register(cleanup);
14132
+ } }, path, append ? "a" : "w", random ? incremental : void 0);
14049
14133
  } catch (error) {
14050
- release();
14134
+ try {
14135
+ await outputScope?.close();
14136
+ } finally {
14137
+ release();
14138
+ }
14051
14139
  throw error;
14052
14140
  }
14053
14141
  outputs.add(async (completion) => {
@@ -14063,7 +14151,11 @@ var Runtime = class _Runtime {
14063
14151
  }
14064
14152
  }
14065
14153
  } finally {
14066
- release();
14154
+ try {
14155
+ await outputScope?.close();
14156
+ } finally {
14157
+ release();
14158
+ }
14067
14159
  }
14068
14160
  });
14069
14161
  const output2 = this.budget.sink(target2.sink, this.signal);
@@ -14304,7 +14396,9 @@ var Runtime = class _Runtime {
14304
14396
  fs: this.fs,
14305
14397
  signal: this.commandSignal,
14306
14398
  executionScope: this.budget.executionScope,
14307
- registerCleanup: (cleanup) => scope.register(cleanup),
14399
+ registerCleanup: (cleanup) => {
14400
+ scope.register(cleanup);
14401
+ },
14308
14402
  invoke: (name3, args, options3) => {
14309
14403
  const invocation = this.invoke(name3, args, options3, context, state, scope);
14310
14404
  void invocation.catch(() => void 0);
@@ -14911,7 +15005,9 @@ ${prefix2} line ${offset + line}: \`${source.split("\n")[line - 1] ?? ""}'
14911
15005
  stdout: this.budget.sink(incoming.stdout, runtime.signal),
14912
15006
  stderr: this.budget.sink(incoming.stderr, runtime.signal),
14913
15007
  signal: this.commandSignal,
14914
- registerCleanup: (cleanup) => scope.register(cleanup),
15008
+ registerCleanup: (cleanup) => {
15009
+ scope.register(cleanup);
15010
+ },
14915
15011
  invoke: (name2, args, options3) => {
14916
15012
  const invocation = invocationOverride.current ? invocationOverride.current(name2, args, options3) : runtime.invoke(name2, args, options3, context, child, scope);
14917
15013
  void invocation.catch(() => void 0);
@@ -15489,26 +15585,49 @@ ${prefix2} line ${offset + line}: \`${source.split("\n")[line - 1] ?? ""}'
15489
15585
  }
15490
15586
  const optstring = context.args[offset];
15491
15587
  const name2 = context.args[offset + 1];
15492
- const args = context.args.length > offset + 2 ? context.args.slice(offset + 2) : state.positional;
15493
- if (args.length > fields) this.budget.fail("maxExpansionFields");
15494
- for (let index = 0; index < args.length; index++) {
15495
- this.signal.throwIfAborted();
15496
- admit2(args[index]);
15497
- if ((index + 1) % 128 === 0) await checkpoint();
15498
- }
15499
- const maxBytes = saturatedProduct(bytes2, saturatedSum(args.length, 1));
15500
- const maxSteps = saturatedSum(saturatedProduct(maxBytes, 2), saturatedSum(args.length, 2));
15501
- state.getopts ??= cloneGetoptsBinding(state);
15588
+ const explicit = context.args.length > offset + 2;
15589
+ const monitor = explicit ? void 0 : stateMonitor(state);
15590
+ const revision = monitor?.positionalRevision;
15591
+ const supplied = explicit ? context.args.slice(offset + 2) : state.positional;
15592
+ if (supplied.length > fields) this.budget.fail("maxExpansionFields");
15593
+ const maxBytes = saturatedProduct(bytes2, saturatedSum(supplied.length, 1));
15594
+ const maxSteps = saturatedSum(saturatedProduct(maxBytes, 2), saturatedSum(supplied.length, 2));
15595
+ const work = { maxArguments: fields, maxBytes, maxSteps, yieldEvery: 128, signal: this.signal, checkpoint };
15596
+ let input3 = monitor?.getoptsInput;
15597
+ let allocation;
15502
15598
  let result;
15503
15599
  try {
15504
- result = await scanGetopts(state.getopts.cursor, optstring, args, {
15505
- reportErrors: state.variables.OPTERR === void 0 || state.variables.OPTERR === "" || decimalIndex(state.variables.OPTERR) !== 0,
15506
- work: { maxArguments: fields, maxBytes, maxSteps, yieldEvery: 128, signal: this.signal, checkpoint }
15507
- });
15508
- } catch (error) {
15600
+ if (!input3 && monitor) {
15601
+ const size = getoptsInputAllocationSize(supplied.length);
15602
+ const arena = this.budget.values;
15603
+ const usage = arena.usage;
15604
+ if (size.bytes + 64 <= arena.maximumBytes - usage.bytes && size.slots + 1 <= arena.maximumSlots - usage.slots) {
15605
+ allocation = arena.scope();
15606
+ input3 = await createGetoptsInput(supplied, allocation, work);
15607
+ }
15608
+ }
15609
+ const args = input3?.args ?? supplied;
15610
+ if (!input3 || allocation) for (let index = 0; index < args.length; index++) {
15611
+ this.signal.throwIfAborted();
15612
+ admit2(args[index]);
15613
+ if ((index + 1) % 128 === 0) await checkpoint();
15614
+ }
15615
+ state.getopts ??= cloneGetoptsBinding(state);
15616
+ try {
15617
+ result = await scanGetopts(state.getopts.cursor, optstring, input3 ?? args, {
15618
+ reportErrors: state.variables.OPTERR === void 0 || state.variables.OPTERR === "" || decimalIndex(state.variables.OPTERR) !== 0,
15619
+ work
15620
+ });
15621
+ } catch (error) {
15622
+ this.signal.throwIfAborted();
15623
+ if (error instanceof GetoptsError && (error.code === "NON_ASCII_OPTION" || error.code === "INVALID_INPUT")) throw new CommandFailure(`getopts: ${error.message}`, 2);
15624
+ throw error;
15625
+ }
15509
15626
  this.signal.throwIfAborted();
15510
- if (error instanceof GetoptsError && (error.code === "NON_ASCII_OPTION" || error.code === "INVALID_INPUT")) throw new CommandFailure(`getopts: ${error.message}`, 2);
15511
- throw error;
15627
+ if (monitor && monitor.positionalRevision !== revision) throw new CommandFailure("getopts: positional arguments changed during validation", 2);
15628
+ if (allocation && monitor.retainGetoptsInput(revision, input3, allocation)) allocation = void 0;
15629
+ } finally {
15630
+ allocation?.close();
15512
15631
  }
15513
15632
  this.signal.throwIfAborted();
15514
15633
  state.getopts.cursor = result.state;
@@ -18829,10 +18948,32 @@ function extendedSource(source) {
18829
18948
  }
18830
18949
  return result;
18831
18950
  }
18951
+ var NfaStorage = class {
18952
+ constructor(budget) {
18953
+ this.budget = budget;
18954
+ }
18955
+ budget;
18956
+ used = 0;
18957
+ reserve(bytes2) {
18958
+ this.budget.step(0);
18959
+ if (!Number.isSafeInteger(bytes2) || bytes2 < 0 || bytes2 > this.budget.maxBufferBytes - this.used) {
18960
+ throw new ProgramError("regular expression state buffer limit exceeded");
18961
+ }
18962
+ this.used += bytes2;
18963
+ }
18964
+ release(bytes2) {
18965
+ if (!Number.isSafeInteger(bytes2) || bytes2 < 0 || bytes2 > this.used) throw new ProgramError("invalid regular expression state accounting");
18966
+ this.used -= bytes2;
18967
+ }
18968
+ clear() {
18969
+ this.used = 0;
18970
+ }
18971
+ };
18832
18972
  var Pattern = class {
18833
18973
  groupCount;
18834
18974
  code = [];
18835
18975
  anchored;
18976
+ linear;
18836
18977
  constructor(source, extended = true, ignoreCase = false) {
18837
18978
  if (source.length > 8192) throw new ProgramError("regular expression exceeds 8192 bytes");
18838
18979
  if (!extended) source = extendedSource(source);
@@ -18999,92 +19140,188 @@ var Pattern = class {
18999
19140
  };
19000
19141
  compile(root);
19001
19142
  emit({ kind: "match" });
19143
+ this.linear = this.code.every((instruction) => instruction.kind === "character" || instruction.kind === "begin" || instruction.kind === "end" || instruction.kind === "match");
19002
19144
  }
19003
- find(text, budget, from = 0) {
19004
- for (let start = from; start <= text.length && (!this.anchored || start === 0); start++) {
19005
- const positions = /* @__PURE__ */ new Map([[start, [{ pc: 0, captures: [] }]]]);
19006
- let best;
19007
- let bestCaptures = [];
19008
- let queued = 1;
19009
- const preferCaptures = (captures) => {
19010
- for (let index = 1; index <= this.groupCount; index++) {
19011
- const begin = captures[index * 2];
19012
- const end = captures[index * 2 + 1];
19013
- const priorBegin = bestCaptures[index * 2];
19014
- const priorEnd = bestCaptures[index * 2 + 1];
19015
- const length = begin === void 0 || end === void 0 ? -1 : end - begin;
19016
- const priorLength = priorBegin === void 0 || priorEnd === void 0 ? -1 : priorEnd - priorBegin;
19017
- if (length !== priorLength) return length > priorLength;
19018
- }
19019
- return false;
19020
- };
19021
- for (let position = start; positions.size && position <= text.length; position++) {
19022
- budget.step();
19023
- const pending = positions.get(position)?.reverse() ?? [];
19024
- positions.delete(position);
19025
- queued -= pending.length;
19026
- const visited = /* @__PURE__ */ new Set();
19027
- let stateBytes = 0;
19028
- const enqueue = (destination, thread) => {
19029
- if (destination === position) pending.push(thread);
19030
- else {
19031
- const waiting = positions.get(destination);
19032
- if (waiting) waiting.push(thread);
19033
- else positions.set(destination, [thread]);
19034
- queued++;
19035
- }
19036
- if ((queued + pending.length) * (32 + this.groupCount * 16) > budget.maxBufferBytes) throw new ProgramError("regular expression state buffer limit exceeded");
19037
- };
19038
- while (pending.length) {
19039
- budget.step();
19040
- const thread = pending.pop();
19041
- const state = this.groupCount ? `${thread.pc}:${thread.captures.join(",")}` : thread.pc;
19042
- if (visited.has(state)) continue;
19043
- visited.add(state);
19044
- stateBytes += typeof state === "string" ? state.length * 2 + 32 : 16;
19045
- if (stateBytes > budget.maxBufferBytes) throw new ProgramError("regular expression state buffer limit exceeded");
19046
- const instruction = this.code[thread.pc];
19145
+ async find(text, budget, from = 0) {
19146
+ await budget.checkpoint();
19147
+ let units = 0;
19148
+ const work = (count2 = 1) => {
19149
+ budget.step(count2);
19150
+ units += count2;
19151
+ if (units < 64) return void 0;
19152
+ units %= 64;
19153
+ return budget.checkpoint();
19154
+ };
19155
+ if (this.linear) {
19156
+ for (let start = from; start <= text.length && (!this.anchored || start === 0); start++) {
19157
+ let position = start;
19158
+ for (const instruction of this.code) {
19159
+ const paused = work(2);
19160
+ if (paused) await paused;
19047
19161
  if (instruction.kind === "character") {
19048
- if (position < text.length && instruction.accepts(text[position])) enqueue(position + 1, { pc: thread.pc + 1, captures: thread.captures });
19049
- } else if (instruction.kind === "backreference") {
19050
- const begin = thread.captures[instruction.index * 2];
19051
- const end = thread.captures[instruction.index * 2 + 1];
19052
- if (begin === void 0 || end === void 0 || position + end - begin > text.length) continue;
19053
- let matches = true;
19054
- for (let offset = 0; offset < end - begin; offset++) {
19055
- budget.step();
19056
- const expected = text[begin + offset];
19057
- const actual = text[position + offset];
19058
- if (instruction.ignoreCase ? expected.toLowerCase() !== actual.toLowerCase() : expected !== actual) {
19059
- matches = false;
19060
- break;
19162
+ if (position >= text.length || !instruction.accepts(text[position])) break;
19163
+ position++;
19164
+ } else if (instruction.kind === "begin" && position !== 0 || instruction.kind === "end" && position !== text.length) break;
19165
+ else if (instruction.kind === "match") {
19166
+ if (position - start > budget.maxBufferBytes) throw new ProgramError("text buffer limit exceeded");
19167
+ const copied = work(position - start);
19168
+ if (copied) await copied;
19169
+ const match = { start, end: position, groups: [text.slice(start, position)] };
19170
+ await budget.checkpoint();
19171
+ return match;
19172
+ }
19173
+ }
19174
+ }
19175
+ return void 0;
19176
+ }
19177
+ const storage = new NfaStorage(budget);
19178
+ const positionDigits = String(text.length).length;
19179
+ const instructionDigits = String(this.code.length).length;
19180
+ try {
19181
+ for (let start = from; start <= text.length && (!this.anchored || start === 0); start++) {
19182
+ storage.reserve(256);
19183
+ const positions = /* @__PURE__ */ new Map([[start, [{ pc: 0, captures: [], bytes: 64 }]]]);
19184
+ let bestEnd;
19185
+ let bestCaptures = [];
19186
+ let bestBytes = 0;
19187
+ try {
19188
+ for (let position = start; positions.size && position <= text.length; position++) {
19189
+ const advanced = work();
19190
+ if (advanced) await advanced;
19191
+ const pending = positions.get(position);
19192
+ if (!pending) continue;
19193
+ const reversed = work(pending.length);
19194
+ if (reversed) await reversed;
19195
+ pending.reverse();
19196
+ positions.delete(position);
19197
+ const visited = /* @__PURE__ */ new Set();
19198
+ let stateBytes = 0;
19199
+ const enqueue = (destination, pc, captures, saveSlot) => {
19200
+ const length = saveSlot === void 0 ? captures.length : Math.max(captures.length, saveSlot + 1);
19201
+ const bytes2 = 64 + length * 8;
19202
+ const waiting = destination === position ? pending : positions.get(destination);
19203
+ storage.reserve(bytes2 + (waiting ? 0 : 64));
19204
+ const saved = saveSlot === void 0 ? captures : [...captures];
19205
+ if (saveSlot !== void 0) saved[saveSlot] = position;
19206
+ const thread = { pc, captures: saved, bytes: bytes2 };
19207
+ if (waiting) waiting.push(thread);
19208
+ else positions.set(destination, [thread]);
19209
+ };
19210
+ while (pending.length) {
19211
+ const paused = work();
19212
+ if (paused) await paused;
19213
+ const thread = pending.pop();
19214
+ try {
19215
+ let state = thread.pc;
19216
+ let bytes2 = 32;
19217
+ if (this.groupCount) {
19218
+ const joinedLength = Math.max(0, thread.captures.length - 1) + thread.captures.length * positionDigits;
19219
+ const stateLength = instructionDigits + 1 + joinedLength;
19220
+ const serialized = work(stateLength);
19221
+ if (serialized) await serialized;
19222
+ const reserved = 32 + joinedLength * 2 + 64 + stateLength * 2;
19223
+ storage.reserve(reserved);
19224
+ state = `${thread.pc}:${thread.captures.join(",")}`;
19225
+ bytes2 = 64 + state.length * 2;
19226
+ storage.release(reserved - bytes2);
19227
+ } else storage.reserve(bytes2);
19228
+ if (visited.has(state)) {
19229
+ storage.release(bytes2);
19230
+ continue;
19231
+ }
19232
+ visited.add(state);
19233
+ stateBytes += bytes2;
19234
+ const instruction = this.code[thread.pc];
19235
+ if (instruction.kind === "character") {
19236
+ if (position < text.length && instruction.accepts(text[position])) enqueue(position + 1, thread.pc + 1, thread.captures);
19237
+ } else if (instruction.kind === "backreference") {
19238
+ const begin = thread.captures[instruction.index * 2];
19239
+ const end = thread.captures[instruction.index * 2 + 1];
19240
+ if (begin === void 0 || end === void 0 || position + end - begin > text.length) continue;
19241
+ let matches = true;
19242
+ for (let offset = 0; offset < end - begin; offset++) {
19243
+ const compared = work();
19244
+ if (compared) await compared;
19245
+ const expected = text[begin + offset];
19246
+ const actual = text[position + offset];
19247
+ if (instruction.ignoreCase ? expected.toLowerCase() !== actual.toLowerCase() : expected !== actual) {
19248
+ matches = false;
19249
+ break;
19250
+ }
19251
+ }
19252
+ if (matches) enqueue(position + end - begin, thread.pc + 1, thread.captures);
19253
+ } else if (instruction.kind === "match") {
19254
+ let preferred2 = bestEnd === void 0 || position > bestEnd;
19255
+ if (position === bestEnd) for (let index = 1; index <= this.groupCount; index++) {
19256
+ const compared = work();
19257
+ if (compared) await compared;
19258
+ const begin = thread.captures[index * 2];
19259
+ const end = thread.captures[index * 2 + 1];
19260
+ const priorBegin = bestCaptures[index * 2];
19261
+ const priorEnd = bestCaptures[index * 2 + 1];
19262
+ const length = begin === void 0 || end === void 0 ? -1 : end - begin;
19263
+ const priorLength = priorBegin === void 0 || priorEnd === void 0 ? -1 : priorEnd - priorBegin;
19264
+ if (length !== priorLength) {
19265
+ preferred2 = length > priorLength;
19266
+ break;
19267
+ }
19268
+ }
19269
+ if (preferred2) {
19270
+ const retained = 32 + thread.captures.length * 8;
19271
+ storage.reserve(retained);
19272
+ bestEnd = position;
19273
+ bestCaptures = thread.captures;
19274
+ storage.release(bestBytes);
19275
+ bestBytes = retained;
19276
+ }
19277
+ } else if (instruction.kind === "split") {
19278
+ enqueue(position, instruction.second, thread.captures);
19279
+ enqueue(position, instruction.first, thread.captures);
19280
+ } else if (instruction.kind === "jump") enqueue(position, instruction.target, thread.captures);
19281
+ else if (instruction.kind === "save") {
19282
+ const copied = work(Math.max(thread.captures.length, instruction.slot + 1));
19283
+ if (copied) await copied;
19284
+ enqueue(position, thread.pc + 1, thread.captures, instruction.slot);
19285
+ } else if (instruction.kind === "begin" ? position === 0 : position === text.length) enqueue(position, thread.pc + 1, thread.captures);
19286
+ } finally {
19287
+ storage.release(thread.bytes);
19061
19288
  }
19062
19289
  }
19063
- if (matches) enqueue(position + end - begin, { pc: thread.pc + 1, captures: thread.captures });
19064
- } else if (instruction.kind === "match") {
19065
- if (!best || position > best.end || position === best.end && preferCaptures(thread.captures)) {
19066
- const groups = [text.slice(start, position)];
19067
- for (let index = 1; index <= this.groupCount; index++) {
19068
- const begin = thread.captures[index * 2];
19069
- const end = thread.captures[index * 2 + 1];
19070
- groups.push(begin === void 0 || end === void 0 ? void 0 : text.slice(begin, end));
19071
- }
19072
- best = { start, end: position, groups };
19073
- bestCaptures = thread.captures;
19290
+ visited.clear();
19291
+ storage.release(stateBytes + 64);
19292
+ }
19293
+ if (bestEnd !== void 0) {
19294
+ let characters = bestEnd - start;
19295
+ for (let index = 1; index <= this.groupCount; index++) {
19296
+ const counted = work();
19297
+ if (counted) await counted;
19298
+ const begin = bestCaptures[index * 2];
19299
+ const end = bestCaptures[index * 2 + 1];
19300
+ if (begin !== void 0 && end !== void 0) characters += end - begin;
19074
19301
  }
19075
- } else if (instruction.kind === "split") {
19076
- pending.push({ pc: instruction.second, captures: thread.captures }, { pc: instruction.first, captures: thread.captures });
19077
- } else if (instruction.kind === "jump") pending.push({ pc: instruction.target, captures: thread.captures });
19078
- else if (instruction.kind === "save") {
19079
- const captures = [...thread.captures];
19080
- captures[instruction.slot] = position;
19081
- pending.push({ pc: thread.pc + 1, captures });
19082
- } else if (instruction.kind === "begin" ? position === 0 : position === text.length) pending.push({ pc: thread.pc + 1, captures: thread.captures });
19302
+ const copied = work(characters);
19303
+ if (copied) await copied;
19304
+ storage.reserve(64 + (this.groupCount + 1) * 40 + characters * 2);
19305
+ const groups = [text.slice(start, bestEnd)];
19306
+ for (let index = 1; index <= this.groupCount; index++) {
19307
+ const materialized = work();
19308
+ if (materialized) await materialized;
19309
+ const begin = bestCaptures[index * 2];
19310
+ const end = bestCaptures[index * 2 + 1];
19311
+ groups.push(begin === void 0 || end === void 0 ? void 0 : text.slice(begin, end));
19312
+ }
19313
+ await budget.checkpoint();
19314
+ return { start, end: bestEnd, groups };
19315
+ }
19316
+ } finally {
19317
+ positions.clear();
19318
+ storage.clear();
19083
19319
  }
19084
19320
  }
19085
- if (best) return best;
19321
+ return void 0;
19322
+ } finally {
19323
+ storage.clear();
19086
19324
  }
19087
- return void 0;
19088
19325
  }
19089
19326
  };
19090
19327
  async function replacementLength(replacement, match, budget, available) {
@@ -19146,7 +19383,7 @@ async function substitute(text, pattern, replacement, budget, global, occurrence
19146
19383
  while (search <= text.length) {
19147
19384
  await budget.checkpoint();
19148
19385
  budget.step();
19149
- const match = pattern.find(text, budget, search);
19386
+ const match = await pattern.find(text, budget, search);
19150
19387
  if (!match) break;
19151
19388
  if (match.start === match.end && match.start === previousEnd) {
19152
19389
  search = match.end + 1;
@@ -19479,7 +19716,7 @@ async function execute(program, context, files, quiet, budget, separator, output
19479
19716
  if (terminated) outputState.unterminatedFiles.delete(path);
19480
19717
  else outputState.unterminatedFiles.add(path);
19481
19718
  };
19482
- const matches = async (address) => address.kind === "number" ? number === address.number : address.kind === "last" ? (await peekNext()).done === true : getPattern(address.pattern).find(pattern, budget) !== void 0;
19719
+ const matches = async (address) => address.kind === "number" ? number === address.number : address.kind === "last" ? (await peekNext()).done === true : await getPattern(address.pattern).find(pattern, budget) !== void 0;
19483
19720
  for (let pc = 0; pc < program.length; ) {
19484
19721
  budget.step();
19485
19722
  await budget.checkpoint();