poe-code 14.0.28 → 14.0.30

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 (82) hide show
  1. package/dist/cli/program.js +3 -3
  2. package/dist/cli/program.js.map +1 -1
  3. package/dist/index.js +10 -547
  4. package/dist/index.js.map +4 -4
  5. package/dist/metafile.json +1 -1
  6. package/dist/package-metadata.d.ts +1 -0
  7. package/dist/package-metadata.js +5 -0
  8. package/dist/package-metadata.js.map +1 -0
  9. package/package.json +1 -1
  10. package/packages/safe-bash/dist/browser.js +584 -200
  11. package/packages/safe-bash/dist/browser.js.map +4 -4
  12. package/packages/safe-bash/dist/commands/filesystem.d.ts.map +1 -1
  13. package/packages/safe-bash/dist/commands/filesystem.js +49 -28
  14. package/packages/safe-bash/dist/commands/filesystem.js.map +1 -1
  15. package/packages/safe-bash/dist/commands/regex-execution/bounded-provider.d.ts +1 -1
  16. package/packages/safe-bash/dist/commands/regex-execution/bounded-provider.d.ts.map +1 -1
  17. package/packages/safe-bash/dist/commands/regex-execution/bounded-provider.js +135 -3
  18. package/packages/safe-bash/dist/commands/regex-execution/bounded-provider.js.map +1 -1
  19. package/packages/safe-bash/dist/commands/structured/limits.js +3 -1
  20. package/packages/safe-bash/dist/commands/structured/limits.js.map +1 -1
  21. package/packages/safe-bash/dist/commands/structured/split.d.ts.map +1 -1
  22. package/packages/safe-bash/dist/commands/structured/split.js +7 -2
  23. package/packages/safe-bash/dist/commands/structured/split.js.map +1 -1
  24. package/packages/safe-bash/dist/commands/structured/values.d.ts.map +1 -1
  25. package/packages/safe-bash/dist/commands/structured/values.js +4 -2
  26. package/packages/safe-bash/dist/commands/structured/values.js.map +1 -1
  27. package/packages/safe-bash/dist/commands/text.d.ts.map +1 -1
  28. package/packages/safe-bash/dist/commands/text.js +194 -52
  29. package/packages/safe-bash/dist/commands/text.js.map +1 -1
  30. package/packages/safe-bash/dist/shell/arithmetic-parameters.d.ts +2 -0
  31. package/packages/safe-bash/dist/shell/arithmetic-parameters.d.ts.map +1 -1
  32. package/packages/safe-bash/dist/shell/arithmetic-parameters.js +1 -1
  33. package/packages/safe-bash/dist/shell/arithmetic-parameters.js.map +1 -1
  34. package/packages/safe-bash/dist/shell/arithmetic.d.ts +4 -3
  35. package/packages/safe-bash/dist/shell/arithmetic.d.ts.map +1 -1
  36. package/packages/safe-bash/dist/shell/arithmetic.js +19 -7
  37. package/packages/safe-bash/dist/shell/arithmetic.js.map +1 -1
  38. package/packages/safe-bash/dist/shell/arrays/syntax.d.ts +6 -5
  39. package/packages/safe-bash/dist/shell/arrays/syntax.d.ts.map +1 -1
  40. package/packages/safe-bash/dist/shell/arrays/syntax.js +22 -12
  41. package/packages/safe-bash/dist/shell/arrays/syntax.js.map +1 -1
  42. package/packages/safe-bash/dist/shell/index.d.ts +1 -1
  43. package/packages/safe-bash/dist/shell/index.d.ts.map +1 -1
  44. package/packages/safe-bash/dist/shell/parse-budget.d.ts +10 -0
  45. package/packages/safe-bash/dist/shell/parse-budget.d.ts.map +1 -0
  46. package/packages/safe-bash/dist/shell/parse-budget.js +29 -0
  47. package/packages/safe-bash/dist/shell/parse-budget.js.map +1 -0
  48. package/packages/safe-bash/dist/shell/parser.d.ts +6 -4
  49. package/packages/safe-bash/dist/shell/parser.d.ts.map +1 -1
  50. package/packages/safe-bash/dist/shell/parser.js +80 -29
  51. package/packages/safe-bash/dist/shell/parser.js.map +1 -1
  52. package/packages/safe-bash/dist/shell/runtime.d.ts +3 -0
  53. package/packages/safe-bash/dist/shell/runtime.d.ts.map +1 -1
  54. package/packages/safe-bash/dist/shell/runtime.js +136 -78
  55. package/packages/safe-bash/dist/shell/runtime.js.map +1 -1
  56. package/packages/safe-bash/dist/shell/shell.js +2 -2
  57. package/packages/safe-bash/dist/shell/shell.js.map +1 -1
  58. package/packages/safe-bash/dist/shell/types.d.ts +4 -0
  59. package/packages/safe-bash/dist/shell/types.d.ts.map +1 -1
  60. package/packages/safe-bash/dist/shell/types.js.map +1 -1
  61. package/packages/safe-bash/dist/shell/worker-limits.d.ts.map +1 -1
  62. package/packages/safe-bash/dist/shell/worker-limits.js +1 -0
  63. package/packages/safe-bash/dist/shell/worker-limits.js.map +1 -1
  64. package/packages/safe-fs/dist/fs/webdav/xml.d.ts +3 -0
  65. package/packages/safe-js/dist/browser/chunks/{chunk-FX3SOWQN.js → chunk-BB4KTS26.js} +19 -9
  66. package/packages/safe-js/dist/browser/chunks/{chunk-FX3SOWQN.js.map → chunk-BB4KTS26.js.map} +3 -3
  67. package/packages/safe-js/dist/browser/safe-fs-core.js +1 -1
  68. package/packages/safe-js/dist/browser/safe-fs.js +1 -1
  69. package/packages/safe-js/dist/chunks/{chunk-EGTPUJGO.js → chunk-FOWKJRPH.js} +3 -3
  70. package/packages/safe-js/dist/chunks/{chunk-X2BJ2VDZ.js → chunk-LRWPYBOU.js} +19 -9
  71. package/packages/safe-js/dist/chunks/chunk-LRWPYBOU.js.map +7 -0
  72. package/packages/safe-js/dist/chunks/{chunk-47R6V3XW.js → chunk-OXQ2XP74.js} +2 -2
  73. package/packages/safe-js/dist/cli.js +3 -3
  74. package/packages/safe-js/dist/index.js +3 -3
  75. package/packages/safe-js/dist/safe-fs-core.js +1 -1
  76. package/packages/safe-js/dist/safe-fs-node.js +2 -2
  77. package/packages/safe-js/dist/safe-fs.js +2 -2
  78. package/packages/tiny-http-mcp-server/dist/http-transport.js +1 -1
  79. package/packages/tiny-http-mcp-server/dist/parse-body.js +12 -5
  80. package/packages/safe-js/dist/chunks/chunk-X2BJ2VDZ.js.map +0 -7
  81. /package/packages/safe-js/dist/chunks/{chunk-EGTPUJGO.js.map → chunk-FOWKJRPH.js.map} +0 -0
  82. /package/packages/safe-js/dist/chunks/{chunk-47R6V3XW.js.map → chunk-OXQ2XP74.js.map} +0 -0
@@ -3432,6 +3432,7 @@ function createDirectoryReader(maxEntries = 1e4) {
3432
3432
  }
3433
3433
 
3434
3434
  // packages/safe-bash/src/commands/filesystem.ts
3435
+ var MAX_RECURSIVE_DIRECTORY_DEPTH = 1024;
3435
3436
  async function preflightOperands(context, operands, check) {
3436
3437
  for (const operand of operands) {
3437
3438
  try {
@@ -3514,11 +3515,19 @@ async function copy(context, source, target, flags, readDirectory, top = true, a
3514
3515
  if (isPathWithin(physicalSource, physicalTarget)) throw new FsError("EINVAL", { path: target, message: "cannot copy a directory into itself" });
3515
3516
  if (ancestors.has(physicalSource)) throw new FsError("ELOOP", { path: source });
3516
3517
  if (targetStat && targetStat.type !== "directory") throw new FsError("ENOTDIR", { path: target });
3518
+ context.signal.throwIfAborted();
3519
+ if (ancestors.size > MAX_RECURSIVE_DIRECTORY_DEPTH) {
3520
+ throw new FsError("ELOOP", { path: source, message: `cp directory depth limit exceeded (${MAX_RECURSIVE_DIRECTORY_DEPTH})` });
3521
+ }
3517
3522
  await admitFilesystemModes(context, "cp", ["recursive"], [target]);
3518
- if (!targetStat && !preflight) await context.fs.mkdir(target, { mode: sourceStat.mode & 511, signal: context.signal });
3519
- const next = new Set(ancestors).add(physicalSource);
3520
- for (const entry of await readDirectory(context, source, true)) {
3521
- await copy(context, joinPath(source, entry.name), joinPath(target, entry.name), flags, readDirectory, false, next, preflight);
3523
+ ancestors.add(physicalSource);
3524
+ try {
3525
+ if (!targetStat && !preflight) await context.fs.mkdir(target, { mode: sourceStat.mode & 511, signal: context.signal });
3526
+ for (const entry of await readDirectory(context, source, true)) {
3527
+ await copy(context, joinPath(source, entry.name), joinPath(target, entry.name), flags, readDirectory, false, ancestors, preflight);
3528
+ }
3529
+ } finally {
3530
+ ancestors.delete(physicalSource);
3522
3531
  }
3523
3532
  } else if (preserveLink) {
3524
3533
  await admitFilesystemModes(context, "cp", ["symlink"], [target]);
@@ -3856,25 +3865,33 @@ function filesystemCommands(maxDirectoryEntries) {
3856
3865
  await admitFilesystemModes(context, "ls", ["directory"], [path]);
3857
3866
  const physical = await context.fs.realpath(path, { signal: context.signal });
3858
3867
  if (ancestors.has(physical)) throw new FsError("ELOOP", { path });
3859
- const next = new Set(ancestors).add(physical);
3860
- if (header) {
3861
- await output(context, `${headerWritten ? "\n" : ""}${display}:
3862
- `);
3863
- headerWritten = true;
3864
- }
3865
- const entries = await readDirectory(context, path, true);
3866
- const names = entries.map((entry) => entry.name).filter((name2) => parsed.flags.has("a") || parsed.flags.has("A") || !name2.startsWith("."));
3867
- if (parsed.flags.has("a")) for (const name2 of [".", ".."]) {
3868
- const index = names.findIndex((entry) => entry > name2);
3869
- names.splice(index < 0 ? names.length : index, 0, name2);
3868
+ context.signal.throwIfAborted();
3869
+ if (ancestors.size > MAX_RECURSIVE_DIRECTORY_DEPTH) {
3870
+ throw new FsError("ELOOP", { path, message: `ls directory depth limit exceeded (${MAX_RECURSIVE_DIRECTORY_DEPTH})` });
3870
3871
  }
3871
- if (parsed.flags.has("r")) names.reverse();
3872
- for (const name2 of names) await render(joinPath(path, name2), name2);
3873
- if (parsed.flags.has("R")) for (const name2 of names) {
3874
- if (name2 === "." || name2 === "..") continue;
3875
- const child = joinPath(path, name2);
3876
- const childStat = await context.fs[parsed.flags.has("L") ? "stat" : "lstat"](child, { signal: context.signal });
3877
- if (childStat.type === "directory") await list(child, `${display.replace(/\/$/u, "")}/${name2}`, true, next);
3872
+ ancestors.add(physical);
3873
+ try {
3874
+ if (header) {
3875
+ await output(context, `${headerWritten ? "\n" : ""}${display}:
3876
+ `);
3877
+ headerWritten = true;
3878
+ }
3879
+ const entries = await readDirectory(context, path, true);
3880
+ const names = entries.map((entry) => entry.name).filter((name2) => parsed.flags.has("a") || parsed.flags.has("A") || !name2.startsWith("."));
3881
+ if (parsed.flags.has("a")) for (const name2 of [".", ".."]) {
3882
+ const index = names.findIndex((entry) => entry > name2);
3883
+ names.splice(index < 0 ? names.length : index, 0, name2);
3884
+ }
3885
+ if (parsed.flags.has("r")) names.reverse();
3886
+ for (const name2 of names) await render(joinPath(path, name2), name2);
3887
+ if (parsed.flags.has("R")) for (const name2 of names) {
3888
+ if (name2 === "." || name2 === "..") continue;
3889
+ const child = joinPath(path, name2);
3890
+ const childStat = await context.fs[parsed.flags.has("L") ? "stat" : "lstat"](child, { signal: context.signal });
3891
+ if (childStat.type === "directory") await list(child, `${display.replace(/\/$/u, "")}/${name2}`, true, ancestors);
3892
+ }
3893
+ } finally {
3894
+ ancestors.delete(physical);
3878
3895
  }
3879
3896
  };
3880
3897
  return eachOperand(context, operands, (operand) => list(pathOf(context, operand), operand, operands.length > 1 || parsed.flags.has("R")));
@@ -4656,6 +4673,103 @@ async function sortRecords(records, compare, work) {
4656
4673
  }
4657
4674
  return source;
4658
4675
  }
4676
+ async function cutRanges(list, work) {
4677
+ const ranges = [];
4678
+ let tokenStart = 0;
4679
+ let dash = -1;
4680
+ let start = 0;
4681
+ let end = 0;
4682
+ let invalid = false;
4683
+ for (let index = 0; index <= list.length; index++) {
4684
+ const checkpoint = work.charge();
4685
+ if (checkpoint) await checkpoint;
4686
+ const character = list[index];
4687
+ if (character === "," || character === " " || index === list.length) {
4688
+ if (index === tokenStart) {
4689
+ if (index === 0 || index === list.length) throw new UsageError("invalid range ''");
4690
+ tokenStart = index + 1;
4691
+ continue;
4692
+ }
4693
+ if (invalid || dash === tokenStart && dash === index - 1) throw new UsageError(`invalid range '${list.slice(tokenStart, index)}'`);
4694
+ if (dash === tokenStart) start = 1;
4695
+ if (dash < 0) end = start;
4696
+ const openEnd = dash >= 0 && dash === index - 1;
4697
+ if (openEnd) end = Infinity;
4698
+ if (!Number.isSafeInteger(start) || start < 1) throw new UsageError(`invalid number '${list.slice(tokenStart, dash < 0 ? index : dash)}'`);
4699
+ if (!openEnd && (!Number.isSafeInteger(end) || end < 1)) throw new UsageError(`invalid number '${list.slice(dash < 0 ? tokenStart : dash + 1, index)}'`);
4700
+ if (end < start) throw new UsageError(`decreasing range '${list.slice(tokenStart, index)}'`);
4701
+ ranges.push({ start, end });
4702
+ tokenStart = index + 1;
4703
+ dash = -1;
4704
+ start = 0;
4705
+ end = 0;
4706
+ invalid = false;
4707
+ } else if (character === "-" && dash < 0) {
4708
+ dash = index;
4709
+ } else {
4710
+ const digit2 = list.charCodeAt(index) - 48;
4711
+ if (digit2 < 0 || digit2 > 9) invalid = true;
4712
+ else if (dash < 0) start = start * 10 + digit2;
4713
+ else end = end * 10 + digit2;
4714
+ }
4715
+ }
4716
+ const ordered = await sortRecords(ranges, async (left, right) => left.start - right.start, work);
4717
+ const normalized = [];
4718
+ for (const range of ordered) {
4719
+ const checkpoint = work.charge();
4720
+ if (checkpoint) await checkpoint;
4721
+ const previous = normalized.at(-1);
4722
+ if (previous && range.start <= previous.end + 1) previous.end = Math.max(previous.end, range.end);
4723
+ else normalized.push(range);
4724
+ }
4725
+ return normalized;
4726
+ }
4727
+ var CutOutput = class {
4728
+ constructor(context, work) {
4729
+ this.context = context;
4730
+ this.work = work;
4731
+ }
4732
+ context;
4733
+ work;
4734
+ #buffer = new Uint8Array(64 * 1024);
4735
+ #used = 0;
4736
+ async write(bytes2) {
4737
+ for (let offset = 0; offset < bytes2.length; ) {
4738
+ const length = Math.min(4096, bytes2.length - offset, this.#buffer.length - this.#used);
4739
+ const checkpoint = this.work.charge(length);
4740
+ if (checkpoint) await checkpoint;
4741
+ this.#buffer.set(bytes2.subarray(offset, offset + length), this.#used);
4742
+ this.#used += length;
4743
+ offset += length;
4744
+ if (this.#used === this.#buffer.length) await this.flush();
4745
+ }
4746
+ }
4747
+ async text(text) {
4748
+ for (let offset = 0; offset < text.length; ) {
4749
+ let end = Math.min(offset + 4096, text.length);
4750
+ const last = text.charCodeAt(end - 1);
4751
+ if (end < text.length && last >= 55296 && last <= 56319) end--;
4752
+ await this.write(encoder2.encode(text.slice(offset, end)));
4753
+ offset = end;
4754
+ }
4755
+ }
4756
+ async flush() {
4757
+ if (!this.#used) return;
4758
+ const bytes2 = this.#buffer.slice(0, this.#used);
4759
+ this.#used = 0;
4760
+ await output(this.context, bytes2);
4761
+ }
4762
+ };
4763
+ async function cutFieldBoundary(record4, separator, start, work) {
4764
+ for (let offset = start; offset < record4.length; offset += 4096) {
4765
+ const window = record4.subarray(offset, Math.min(record4.length, offset + 4096 + separator.length - 1));
4766
+ const found = window.indexOf(separator);
4767
+ const checkpoint = work.charge(found < 0 ? Math.min(4096, window.length) : found + separator.length);
4768
+ if (checkpoint) await checkpoint;
4769
+ if (found >= 0) return offset + found;
4770
+ }
4771
+ return -1;
4772
+ }
4659
4773
  async function compareSortBytes(left, right, work) {
4660
4774
  const length = Math.min(left.length, right.length);
4661
4775
  for (let offset = 0; offset < length; offset += 1024) {
@@ -5015,85 +5129,104 @@ function textCommands() {
5015
5129
  if (modes.length !== 1) throw new UsageError("exactly one byte, character, or field list is required");
5016
5130
  const mode = modes[0];
5017
5131
  if (mode !== "f" && (parsed.flags.has("d") || parsed.flags.has("s"))) throw new UsageError("delimiter options require field mode");
5018
- const ranges = value(parsed, mode).split(/[ ,]+/u).map((part) => {
5019
- const match = /^(?:([0-9]+)(?:-([0-9]*))?|-([0-9]+))$/u.exec(part);
5020
- if (!match) throw new UsageError(`invalid range '${part}'`);
5021
- const start = match[3] === void 0 ? integer(match[1], 1) : 1;
5022
- const end = match[3] !== void 0 ? integer(match[3], 1) : match[2] === void 0 ? start : match[2] === "" ? Infinity : integer(match[2], 1);
5023
- if (end < start) throw new UsageError(`decreasing range '${part}'`);
5024
- return { start, end };
5025
- });
5026
- const selected = (position) => ranges.some((range) => position >= range.start && position <= range.end) !== parsed.flags.has("C");
5132
+ const work = new SortWork(context.signal);
5133
+ const ranges = await cutRanges(value(parsed, mode), work);
5134
+ const complement = parsed.flags.has("C");
5027
5135
  const delimiter = value(parsed, "d") ?? " ";
5028
- if ([...delimiter].length !== 1) throw new UsageError("delimiter must be a single character");
5136
+ if (delimiter.length !== (delimiter.codePointAt(0) > 65535 ? 2 : 1)) throw new UsageError("delimiter must be a single character");
5029
5137
  const outputDelimiter = value(parsed, "o");
5030
5138
  const recordDelimiter = parsed.flags.has("z") ? 0 : 10;
5139
+ const separator = import_buffer.Buffer.from(delimiter);
5140
+ const writer = new CutOutput(context, work);
5031
5141
  let exitCode = 0;
5032
5142
  for (const name2 of parsed.operands.length ? parsed.operands : ["-"]) {
5033
5143
  try {
5034
5144
  for await (const line of lines(input(context, name2), recordDelimiter)) {
5035
5145
  context.signal.throwIfAborted();
5036
- let bytes2;
5146
+ let cursor = 0;
5147
+ const selected = (position) => {
5148
+ while (cursor < ranges.length && position > ranges[cursor].end) cursor++;
5149
+ return (cursor < ranges.length && position >= ranges[cursor].start) !== complement;
5150
+ };
5037
5151
  if (mode === "f") {
5038
5152
  const record4 = import_buffer.Buffer.from(line.bytes.buffer, line.bytes.byteOffset, line.bytes.byteLength);
5039
- const separator = import_buffer.Buffer.from(delimiter);
5040
- let boundary = record4.indexOf(separator);
5153
+ let boundary = await cutFieldBoundary(record4, separator, 0, work);
5041
5154
  if (boundary < 0) {
5042
5155
  if (parsed.flags.has("s")) continue;
5043
- bytes2 = line.bytes;
5156
+ await writer.write(line.bytes);
5044
5157
  } else {
5045
- const pieces = [];
5046
- const joiner = encoder2.encode(outputDelimiter ?? delimiter);
5047
5158
  let field = 1;
5048
5159
  let start = 0;
5049
5160
  let emitted = false;
5050
5161
  while (true) {
5162
+ const checkpoint = work.charge();
5163
+ if (checkpoint) await checkpoint;
5051
5164
  if (selected(field++)) {
5052
- if (emitted) pieces.push(joiner);
5053
- pieces.push(record4.subarray(start, boundary < 0 ? record4.length : boundary));
5165
+ if (emitted) await writer.text(outputDelimiter ?? delimiter);
5166
+ await writer.write(record4.subarray(start, boundary < 0 ? record4.length : boundary));
5054
5167
  emitted = true;
5055
5168
  }
5056
5169
  if (boundary < 0) break;
5057
5170
  start = boundary + separator.length;
5058
- boundary = record4.indexOf(separator, start);
5171
+ boundary = await cutFieldBoundary(record4, separator, start, work);
5059
5172
  }
5060
- bytes2 = concatenate(pieces);
5061
5173
  }
5062
5174
  } else if (mode === "b") {
5063
- const chunks = [];
5064
- let start = -1;
5065
5175
  let emitted = false;
5066
- for (let index = 0; index <= line.bytes.length; index++) {
5067
- const included = index < line.bytes.length && selected(index + 1);
5068
- if (included && start < 0) start = index;
5069
- if (!included && start >= 0) {
5070
- if (emitted && outputDelimiter !== void 0) chunks.push(encoder2.encode(outputDelimiter));
5071
- chunks.push(line.bytes.subarray(start, index));
5072
- emitted = true;
5073
- start = -1;
5176
+ let previousIncluded = false;
5177
+ for (let offset = 0; offset < line.bytes.length; offset += 4096) {
5178
+ const end = Math.min(line.bytes.length, offset + 4096);
5179
+ const checkpoint = work.charge(end - offset);
5180
+ if (checkpoint) await checkpoint;
5181
+ let start = -1;
5182
+ for (let index = offset; index < end; index++) {
5183
+ const included = selected(index + 1);
5184
+ if (included && start < 0) {
5185
+ if (!previousIncluded && emitted && outputDelimiter !== void 0) await writer.text(outputDelimiter);
5186
+ start = index;
5187
+ emitted = true;
5188
+ }
5189
+ if (!included && start >= 0) {
5190
+ await writer.write(line.bytes.subarray(start, index));
5191
+ start = -1;
5192
+ }
5193
+ previousIncluded = included;
5074
5194
  }
5195
+ if (start >= 0) await writer.write(line.bytes.subarray(start, end));
5075
5196
  }
5076
- bytes2 = concatenate(chunks);
5077
5197
  } else {
5078
- const text = new TextDecoder().decode(line.bytes);
5079
- const pieces = [];
5198
+ const decoder3 = new TextDecoder();
5080
5199
  let index = 0;
5081
- let offset = 0;
5082
- let start = -1;
5083
- for (const character of text) {
5084
- const included = selected(++index);
5085
- if (included && start < 0) start = offset;
5086
- if (!included && start >= 0) {
5087
- pieces.push(text.slice(start, offset));
5088
- start = -1;
5200
+ let emitted = false;
5201
+ let previousIncluded = false;
5202
+ for (let offset = 0; offset < line.bytes.length; offset += 4096) {
5203
+ const end = Math.min(line.bytes.length, offset + 4096);
5204
+ const checkpoint = work.charge(end - offset);
5205
+ if (checkpoint) await checkpoint;
5206
+ const text = decoder3.decode(line.bytes.subarray(offset, end), { stream: end < line.bytes.length });
5207
+ let start = -1;
5208
+ let position = 0;
5209
+ for (const character of text) {
5210
+ const checkpoint2 = work.charge();
5211
+ if (checkpoint2) await checkpoint2;
5212
+ const included = selected(++index);
5213
+ if (included && start < 0) {
5214
+ if (!previousIncluded && emitted && outputDelimiter !== void 0) await writer.text(outputDelimiter);
5215
+ start = position;
5216
+ emitted = true;
5217
+ }
5218
+ if (!included && start >= 0) {
5219
+ await writer.text(text.slice(start, position));
5220
+ start = -1;
5221
+ }
5222
+ position += character.length;
5223
+ previousIncluded = included;
5089
5224
  }
5090
- offset += character.length;
5225
+ if (start >= 0) await writer.text(text.slice(start));
5091
5226
  }
5092
- if (start >= 0) pieces.push(text.slice(start));
5093
- bytes2 = encoder2.encode(pieces.join(outputDelimiter ?? ""));
5094
5227
  }
5095
- await output(context, bytes2);
5096
- await output(context, Uint8Array.of(recordDelimiter));
5228
+ await writer.write(Uint8Array.of(recordDelimiter));
5229
+ await writer.flush();
5097
5230
  }
5098
5231
  } catch (error) {
5099
5232
  await diagnostic(context, error);
@@ -5152,6 +5285,33 @@ var ShellLimitError = class extends Error {
5152
5285
  limit;
5153
5286
  };
5154
5287
 
5288
+ // packages/safe-bash/src/shell/parse-budget.ts
5289
+ init_platform();
5290
+ var defaultMaxParseUnits = 262144;
5291
+ var ParseBudget = class {
5292
+ constructor(maximum = defaultMaxParseUnits, signal, onLimit) {
5293
+ this.signal = signal;
5294
+ this.onLimit = onLimit;
5295
+ if (!Number.isSafeInteger(maximum) || maximum < 0) throw new RangeError("maxParseUnits must be a nonnegative safe integer");
5296
+ this.#remaining = maximum;
5297
+ }
5298
+ signal;
5299
+ onLimit;
5300
+ #remaining;
5301
+ #failure;
5302
+ admit(units = 1) {
5303
+ this.signal?.throwIfAborted();
5304
+ if (this.#failure) throw this.#failure;
5305
+ if (!Number.isSafeInteger(units) || units < 0) throw new RangeError("Parse admission must be a nonnegative safe integer");
5306
+ if (units > this.#remaining) {
5307
+ const error = this.#failure = new ShellLimitError("maxParseUnits");
5308
+ this.onLimit?.(error);
5309
+ throw error;
5310
+ }
5311
+ this.#remaining -= units;
5312
+ }
5313
+ };
5314
+
5155
5315
  // packages/safe-bash/src/shell/arithmetic.ts
5156
5316
  init_platform();
5157
5317
  var ArithmeticFailure = class extends Error {
@@ -5161,11 +5321,13 @@ var ArithmeticFailure = class extends Error {
5161
5321
  }
5162
5322
  offset;
5163
5323
  };
5164
- function prepareArithmetic(source) {
5324
+ function prepareArithmetic(source, budget = new ParseBudget()) {
5325
+ budget.admit();
5165
5326
  try {
5166
- return { source, tree: parseArithmetic(source) };
5327
+ return { source, tree: parseArithmetic(source, 0, budget) };
5167
5328
  } catch (error) {
5168
5329
  if (!(error instanceof ShellSyntaxError) || /nesting/u.test(error.reason)) throw error;
5330
+ budget.admit();
5169
5331
  return { source, error };
5170
5332
  }
5171
5333
  }
@@ -5220,7 +5382,7 @@ function integer2(text) {
5220
5382
  }
5221
5383
  return value2;
5222
5384
  }
5223
- function parseArithmetic(source, offset = 0) {
5385
+ function parseArithmetic(source, offset = 0, budget = new ParseBudget()) {
5224
5386
  const tokens2 = [];
5225
5387
  let position = 0;
5226
5388
  while (position < source.length) {
@@ -5230,6 +5392,7 @@ function parseArithmetic(source, offset = 0) {
5230
5392
  }
5231
5393
  const value2 = /^(?:\d+#[\da-zA-Z@_]+|0[xX][\da-fA-F]+|\d+|[a-zA-Z_][a-zA-Z_0-9]*|<<=|>>=|\*\*|\+\+|--|&&|\|\||<<|>>|[+*/%&^|!<>=-]=|[()+*/%~!<>=&^|?:,\-])/u.exec(source.slice(position))?.[0];
5232
5394
  if (!value2) throw new ShellSyntaxError("Unsupported arithmetic token", offset + position);
5395
+ budget.admit();
5233
5396
  tokens2.push({ value: value2, offset: offset + position });
5234
5397
  position += value2.length;
5235
5398
  }
@@ -5246,6 +5409,7 @@ function parseArithmetic(source, offset = 0) {
5246
5409
  const token = current();
5247
5410
  cursor++;
5248
5411
  if (["+", "-", "!", "~", "++", "--"].includes(token)) {
5412
+ budget.admit();
5249
5413
  const operand = expression(15);
5250
5414
  if (["++", "--"].includes(token) && operand.kind !== "name") error("Arithmetic assignment requires a variable");
5251
5415
  left = { kind: "unary", operator: token, operand, postfix: false };
@@ -5253,8 +5417,11 @@ function parseArithmetic(source, offset = 0) {
5253
5417
  left = expression();
5254
5418
  if (current() !== ")") error("Unclosed arithmetic parenthesis");
5255
5419
  cursor++;
5256
- } else if (/^[a-zA-Z_]/u.test(token)) left = { kind: "name", name: token };
5257
- else {
5420
+ } else if (/^[a-zA-Z_]/u.test(token)) {
5421
+ budget.admit();
5422
+ left = { kind: "name", name: token };
5423
+ } else {
5424
+ budget.admit();
5258
5425
  try {
5259
5426
  left = { kind: "literal", value: BigInt.asIntN(64, integer2(token)) };
5260
5427
  } catch {
@@ -5267,12 +5434,14 @@ function parseArithmetic(source, offset = 0) {
5267
5434
  if (operator === "++" || operator === "--") {
5268
5435
  if (left.kind !== "name") error("Arithmetic assignment requires a variable");
5269
5436
  cursor++;
5437
+ budget.admit();
5270
5438
  left = { kind: "unary", operator, operand: left, postfix: true, start };
5271
5439
  continue;
5272
5440
  }
5273
5441
  const priority = Object.hasOwn(precedence, operator) ? precedence[operator] : 0;
5274
5442
  if (priority < minimum || priority === 0) break;
5275
5443
  cursor++;
5444
+ budget.admit();
5276
5445
  if (operator === "?") {
5277
5446
  const yes = expression();
5278
5447
  if (current() !== ":") error("Expected arithmetic colon");
@@ -5288,7 +5457,10 @@ function parseArithmetic(source, offset = 0) {
5288
5457
  depth--;
5289
5458
  return left;
5290
5459
  };
5291
- if (!tokens2.length) return { kind: "literal", value: 0n };
5460
+ if (!tokens2.length) {
5461
+ budget.admit();
5462
+ return { kind: "literal", value: 0n };
5463
+ }
5292
5464
  const tree = expression();
5293
5465
  if (cursor < tokens2.length) error("Unexpected arithmetic token");
5294
5466
  return tree;
@@ -5305,7 +5477,7 @@ function arithmeticEnd(source, start) {
5305
5477
  }
5306
5478
  throw new ShellSyntaxError("Unterminated arithmetic expression", start);
5307
5479
  }
5308
- function evaluateArithmetic(program, variables) {
5480
+ function evaluateArithmetic(program, variables, budget = new ParseBudget()) {
5309
5481
  const visiting = /* @__PURE__ */ new Set();
5310
5482
  let steps = 0;
5311
5483
  const binary = (operator, left, right, offset) => {
@@ -5373,7 +5545,7 @@ function evaluateArithmetic(program, variables) {
5373
5545
  else if (node.kind === "name") {
5374
5546
  if (visiting.has(node.name) || visiting.size >= 64) throw new Error("Arithmetic variable recursion");
5375
5547
  visiting.add(node.name);
5376
- pending.push({ kind: "variable", name: node.name }, { kind: "evaluate", node: parseArithmetic(variables[node.name] ?? "0") });
5548
+ pending.push({ kind: "variable", name: node.name }, { kind: "evaluate", node: parseArithmetic(variables[node.name] ?? "0", 0, budget) });
5377
5549
  } else if (node.kind === "conditional") {
5378
5550
  pending.push({ kind: "conditional", node }, { kind: "evaluate", node: node.condition });
5379
5551
  } else if (node.kind === "unary") {
@@ -5439,21 +5611,23 @@ function setQuoteMarker(part, synthetic) {
5439
5611
  function isQuoteMarker(part) {
5440
5612
  return quoteMarkers.has(part);
5441
5613
  }
5442
- function literalIndex(source, offset) {
5614
+ function literalIndex(source, offset, budget = new ParseBudget()) {
5443
5615
  let decimal = source;
5444
5616
  if (source[0] === "'" || source[0] === '"') {
5445
5617
  if (source.length < 2 || source.at(-1) !== source[0]) throw new ShellSyntaxError("Unsupported indexed-array subscript", offset);
5446
5618
  decimal = source.slice(1, -1);
5447
5619
  }
5448
5620
  if (!/^(?:0|[1-9][0-9]*)$/u.test(decimal)) throw new ShellSyntaxError("Unsupported indexed-array subscript", offset);
5621
+ budget.admit();
5449
5622
  return { decimal };
5450
5623
  }
5451
5624
  function numericIndex(index) {
5452
5625
  if (index.decimal.length > 10 || index.decimal.length === 10 && index.decimal > "2147483647") return void 0;
5453
5626
  return Number(index.decimal);
5454
5627
  }
5455
- function arraySelector(source, offset) {
5456
- return source === "@" || source === "*" ? { kind: "members", separator: source } : { kind: "element", index: literalIndex(source, offset) };
5628
+ function arraySelector(source, offset, budget = new ParseBudget()) {
5629
+ budget.admit();
5630
+ return source === "@" || source === "*" ? { kind: "members", separator: source } : { kind: "element", index: literalIndex(source, offset, budget) };
5457
5631
  }
5458
5632
  function setArraySelector(part, selector) {
5459
5633
  selectors.set(part, selector);
@@ -5473,7 +5647,8 @@ function setArrayAssignment(word, assignment) {
5473
5647
  function getArrayAssignment(word) {
5474
5648
  return assignments.get(word);
5475
5649
  }
5476
- function removePrefix(word, length) {
5650
+ function removePrefix(word, length, budget) {
5651
+ budget.admit();
5477
5652
  const parts = [];
5478
5653
  for (const part of word.parts) {
5479
5654
  if (length === 0) parts.push(part);
@@ -5481,6 +5656,7 @@ function removePrefix(word, length) {
5481
5656
  if (part.kind !== "text") throw new ShellSyntaxError("Unsupported indexed-array subscript", word.offset);
5482
5657
  if (part.value.length <= length) length -= part.value.length;
5483
5658
  else {
5659
+ budget.admit();
5484
5660
  parts.push({ ...part, value: part.value.slice(length) });
5485
5661
  length = 0;
5486
5662
  }
@@ -5489,7 +5665,7 @@ function removePrefix(word, length) {
5489
5665
  if (length !== 0) throw new ShellSyntaxError("Invalid indexed-array assignment", word.offset);
5490
5666
  return { offset: word.offset, parts };
5491
5667
  }
5492
- function elementAssignment(word) {
5668
+ function elementAssignment(word, budget = new ParseBudget()) {
5493
5669
  const source = word.spelling;
5494
5670
  const first = word.parts[0];
5495
5671
  if (source === void 0 || first?.kind !== "text" || first.quoted) return void 0;
@@ -5500,19 +5676,23 @@ function elementAssignment(word) {
5500
5676
  if (source.includes("=")) throw new ShellSyntaxError("Invalid indexed-array assignment", word.offset);
5501
5677
  return void 0;
5502
5678
  }
5503
- const index = literalIndex(source.slice(name2.length + 1, end), word.offset + name2.length + 1);
5679
+ budget.admit();
5680
+ const index = literalIndex(source.slice(name2.length + 1, end), word.offset + name2.length + 1, budget);
5504
5681
  const append = source[end + 1] === "+";
5505
- const value2 = removePrefix(word, name2.length + index.decimal.length + (append ? 4 : 3));
5682
+ const value2 = removePrefix(word, name2.length + index.decimal.length + (append ? 4 : 3), budget);
5506
5683
  return { kind: "element", name: name2, index, append, value: value2 };
5507
5684
  }
5508
- function compoundHead(word) {
5685
+ function compoundHead(word, budget = new ParseBudget()) {
5509
5686
  if (word.parts.length !== 1) return void 0;
5510
5687
  const first = word.parts[0];
5511
5688
  if (first?.kind !== "text" || first.quoted) return void 0;
5512
5689
  const match = /^([a-zA-Z_][a-zA-Z_0-9]*)(\+?)=$/u.exec(first.value);
5513
- return match ? { name: match[1], append: match[2] === "+" } : void 0;
5690
+ if (!match) return void 0;
5691
+ budget.admit();
5692
+ return { name: match[1], append: match[2] === "+" };
5514
5693
  }
5515
- function compoundEntry(word) {
5694
+ function compoundEntry(word, budget = new ParseBudget()) {
5695
+ budget.admit();
5516
5696
  const source = word.spelling;
5517
5697
  const first = word.parts[0];
5518
5698
  if (source === void 0 || source[0] !== "[" || first?.kind !== "text" || first.quoted) return { value: word };
@@ -5521,8 +5701,8 @@ function compoundEntry(word) {
5521
5701
  if (source.includes("=")) throw new ShellSyntaxError("Invalid indexed-array entry", word.offset);
5522
5702
  return { value: word };
5523
5703
  }
5524
- const index = literalIndex(source.slice(1, end), word.offset + 1);
5525
- return { index, value: removePrefix(word, index.decimal.length + 3) };
5704
+ const index = literalIndex(source.slice(1, end), word.offset + 1, budget);
5705
+ return { index, value: removePrefix(word, index.decimal.length + 3, budget) };
5526
5706
  }
5527
5707
  function scalarAssignmentName(word) {
5528
5708
  const first = word.parts[0];
@@ -5842,7 +6022,8 @@ var HereDocumentSyntaxError = class extends Error {
5842
6022
  }
5843
6023
  diagnostic;
5844
6024
  };
5845
- function printedSimpleLines(lists, separators) {
6025
+ function printedSimpleLines(lists, separators, budget) {
6026
+ budget.admit();
5846
6027
  const printedLines = /* @__PURE__ */ new Map();
5847
6028
  let line = 1;
5848
6029
  for (let index = 0; index < lists.length; index++) {
@@ -5860,7 +6041,8 @@ function printedSimpleLines(lists, separators) {
5860
6041
  var IncompleteShellInput = class extends Error {
5861
6042
  };
5862
6043
  var Lexer = class {
5863
- constructor(source, depth, warnings = [], lineOffset = 0, byteLocale2 = false, documentLine, partial = false) {
6044
+ constructor(budget, source, depth, warnings = [], lineOffset = 0, byteLocale2 = false, documentLine, partial = false) {
6045
+ this.budget = budget;
5864
6046
  this.source = source;
5865
6047
  this.depth = depth;
5866
6048
  this.warnings = warnings;
@@ -5871,6 +6053,7 @@ var Lexer = class {
5871
6053
  if (depth > 64) throw new ShellSyntaxError("Syntax nesting exceeds 64", 0);
5872
6054
  for (let offset = source.indexOf("\n"); offset !== -1; offset = source.indexOf("\n", offset + 1)) this.newlineOffsets.push(offset);
5873
6055
  }
6056
+ budget;
5874
6057
  source;
5875
6058
  depth;
5876
6059
  warnings;
@@ -5900,6 +6083,7 @@ var Lexer = class {
5900
6083
  throw new ShellSyntaxError(message2, this.position, 2, void 0, unclosedQuote);
5901
6084
  }
5902
6085
  next() {
6086
+ this.budget.admit();
5903
6087
  while (this.position < this.source.length) {
5904
6088
  const current = this.source[this.position];
5905
6089
  if (current === " " || current === " " || this.conditional && current === "\n") {
@@ -5941,6 +6125,7 @@ var Lexer = class {
5941
6125
  if (this.position <= offset) this.error("Tokenizer made no progress");
5942
6126
  let document;
5943
6127
  if (delimiterOperator) {
6128
+ this.budget.admit();
5944
6129
  document = {
5945
6130
  delimiter: word.parts.map((part) => part.kind === "text" ? part.value : "").join(""),
5946
6131
  quoted: word.parts.some((part) => part.quoted),
@@ -5952,6 +6137,7 @@ var Lexer = class {
5952
6137
  };
5953
6138
  this.documents.push(document);
5954
6139
  }
6140
+ this.budget.admit();
5955
6141
  return { kind: "word", value: word.plain ?? "", offset, end: this.position, word: { ...word, spelling: this.source.slice(offset, this.position) }, ...document ? { document } : {} };
5956
6142
  }
5957
6143
  readDocuments() {
@@ -5986,10 +6172,12 @@ var Lexer = class {
5986
6172
  const current = this.source[this.position];
5987
6173
  if (current === "$" || current === "`") {
5988
6174
  if (text) {
6175
+ this.budget.admit(2);
5989
6176
  yield { offset: 0, parts: [{ kind: "text", value: text, quoted: true }] };
5990
6177
  text = "";
5991
6178
  }
5992
6179
  const offset = this.position;
6180
+ this.budget.admit();
5993
6181
  const parts = [];
5994
6182
  try {
5995
6183
  this.expansion(parts, true);
@@ -6013,11 +6201,15 @@ var Lexer = class {
6013
6201
  this.position++;
6014
6202
  }
6015
6203
  if (text.length >= 1024) {
6204
+ this.budget.admit(2);
6016
6205
  yield { offset: 0, parts: [{ kind: "text", value: text, quoted: true }] };
6017
6206
  text = "";
6018
6207
  }
6019
6208
  }
6020
- if (text) yield { offset: 0, parts: [{ kind: "text", value: text, quoted: true }] };
6209
+ if (text) {
6210
+ this.budget.admit(2);
6211
+ yield { offset: 0, parts: [{ kind: "text", value: text, quoted: true }] };
6212
+ }
6021
6213
  }
6022
6214
  documentSubstitutionError(source, error, backtick = false) {
6023
6215
  const line = this.documentLine + source.slice(0, error.offset).split("\n").length;
@@ -6064,6 +6256,7 @@ shell: command substitution: line ${line}: \`${sourceLine}'
6064
6256
  this.error("Unterminated delimiter expansion syntax");
6065
6257
  }
6066
6258
  word(terminator, enclosingQuoted = false, literal = false, arithmetic = false) {
6259
+ this.budget.admit();
6067
6260
  const offset = this.position;
6068
6261
  const reduction = this.printedNewlineReduction;
6069
6262
  const unprinted = this.unprintedWords;
@@ -6088,6 +6281,7 @@ shell: command substitution: line ${line}: \`${sourceLine}'
6088
6281
  previous.value += value2;
6089
6282
  if (!synthetic) setQuoteMarker(previous, false);
6090
6283
  } else {
6284
+ this.budget.admit();
6091
6285
  const part = { kind: "text", value: projection, quoted, ...typeof value2 === "string" ? {} : { byteValue: value2 } };
6092
6286
  setQuoteMarker(part, synthetic);
6093
6287
  parts.push(part);
@@ -6195,6 +6389,7 @@ shell: command substitution: line ${line}: \`${sourceLine}'
6195
6389
  return new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(raw);
6196
6390
  } catch (error) {
6197
6391
  if (!(error instanceof TypeError) || !("code" in error) || error.code !== "ERR_ENCODING_INVALID_ENCODED_DATA") throw error;
6392
+ this.budget.admit(2);
6198
6393
  return shellValueFromBytes(raw);
6199
6394
  }
6200
6395
  }
@@ -6237,6 +6432,7 @@ shell: command substitution: line ${line}: \`${sourceLine}'
6237
6432
  this.error("Unterminated ANSI-C quote", { quote: "'", line: quoteLine });
6238
6433
  }
6239
6434
  expansion(parts, quoted) {
6435
+ this.budget.admit();
6240
6436
  const line = this.documentLine ?? this.lineAt(this.position);
6241
6437
  if (this.source[this.position] === "`") {
6242
6438
  this.position++;
@@ -6248,9 +6444,10 @@ shell: command substitution: line ${line}: \`${sourceLine}'
6248
6444
  if (this.source[this.position] !== "`") this.error("Unterminated command substitution");
6249
6445
  this.position++;
6250
6446
  try {
6251
- parts.push({ kind: "substitution", script: parseSource(source, this.depth + 1, this.warnings, line - 1, this.byteLocale), line, quoted });
6447
+ parts.push({ kind: "substitution", script: parseSource(source, this.depth + 1, this.warnings, line - 1, this.byteLocale, this.budget), line, quoted });
6252
6448
  } catch (error) {
6253
6449
  if (this.documentLine === void 0 || !(error instanceof ShellSyntaxError) || /nesting|exceeds/u.test(error.reason)) throw error;
6450
+ this.budget.admit();
6254
6451
  parts.push({ kind: "failed-substitution", diagnostic: this.documentSubstitutionError(source, error, true).diagnostic, quoted });
6255
6452
  }
6256
6453
  return;
@@ -6261,14 +6458,15 @@ shell: command substitution: line ${line}: \`${sourceLine}'
6261
6458
  const start = this.position + 2;
6262
6459
  const end = arithmeticEnd(this.source, start);
6263
6460
  const source = this.source.slice(start, end);
6264
- parts.push({ kind: "arithmetic", expression: prepareArithmetic(source), source, line, quoted });
6461
+ parts.push({ kind: "arithmetic", expression: prepareArithmetic(source, this.budget), source, line, quoted });
6265
6462
  this.position = end + 2;
6266
6463
  } else if (this.source[this.position] === "(") {
6267
6464
  const start = this.position + 1;
6268
6465
  let nested;
6269
6466
  let script;
6270
6467
  try {
6271
- nested = new Parser(this.source.slice(start), this.depth + 1, this.warnings, this.lineAt(start) - 1, void 0, this.byteLocale);
6468
+ this.budget.admit();
6469
+ nested = new Parser(this.budget, this.source.slice(start), this.depth + 1, this.warnings, this.lineAt(start) - 1, void 0, this.byteLocale);
6272
6470
  script = nested.script(/* @__PURE__ */ new Set([")"]));
6273
6471
  } catch (error) {
6274
6472
  if (this.documentLine !== void 0 && error instanceof ShellSyntaxError && !/nesting|exceeds/u.test(error.reason)) throw this.documentSubstitutionError(this.source.slice(start), error);
@@ -6298,7 +6496,7 @@ shell: command substitution: line ${line}: \`${sourceLine}'
6298
6496
  const start = ++this.position;
6299
6497
  const end = this.source.indexOf("]", start);
6300
6498
  if (end < 0) this.error("Unterminated indexed-array subscript");
6301
- selector = arraySelector(this.source.slice(start, end), start);
6499
+ selector = arraySelector(this.source.slice(start, end), start, this.budget);
6302
6500
  this.position = end + 1;
6303
6501
  if (this.source[this.position] !== "}") this.error("Unsupported indexed-array operator");
6304
6502
  }
@@ -6323,6 +6521,7 @@ shell: command substitution: line ${line}: \`${sourceLine}'
6323
6521
  this.position++;
6324
6522
  substringLength = this.word("}", true, false, true);
6325
6523
  }
6524
+ this.budget.admit();
6326
6525
  substring = { offset, ...substringLength ? { length: substringLength } : {}, source: this.source.slice(parameterStart, this.position + 1) };
6327
6526
  }
6328
6527
  if (this.source[this.position] !== "}") this.error("Unterminated or unsupported parameter expansion");
@@ -6340,17 +6539,20 @@ shell: command substitution: line ${line}: \`${sourceLine}'
6340
6539
  }
6341
6540
  };
6342
6541
  var Parser = class {
6542
+ constructor(budget, source, depth, warnings = [], lineOffset = 0, position, byteLocale2 = false, partial = false) {
6543
+ this.budget = budget;
6544
+ if (position === void 0 && depth === 0 && source.includes("\0")) throw new ShellSyntaxError("NUL bytes are not valid shell source", source.indexOf("\0"));
6545
+ budget.admit();
6546
+ this.lexer = new Lexer(budget, source, depth, warnings, lineOffset, byteLocale2, void 0, partial);
6547
+ this.lexer.position = position ?? 0;
6548
+ this.current = this.lexer.next();
6549
+ }
6550
+ budget;
6343
6551
  lexer;
6344
6552
  current;
6345
6553
  lookahead;
6346
6554
  nesting = 0;
6347
6555
  openCommands = [];
6348
- constructor(source, depth, warnings = [], lineOffset = 0, position, byteLocale2 = false, partial = false) {
6349
- if (position === void 0 && depth === 0 && source.includes("\0")) throw new ShellSyntaxError("NUL bytes are not valid shell source", source.indexOf("\0"));
6350
- this.lexer = new Lexer(source, depth, warnings, lineOffset, byteLocale2, void 0, partial);
6351
- this.lexer.position = position ?? 0;
6352
- this.current = this.lexer.next();
6353
- }
6354
6556
  error(message2) {
6355
6557
  const command2 = this.current.kind === "end" ? this.openCommands.findLast((command3) => ["{", "if", "while", "until", "for", "case"].includes(command3.name)) : void 0;
6356
6558
  throw new ShellSyntaxError(message2, this.current.offset, 2, command2);
@@ -6378,11 +6580,13 @@ var Parser = class {
6378
6580
  while (this.is("\n")) this.advance();
6379
6581
  }
6380
6582
  script(stops = /* @__PURE__ */ new Set(), inputUnit = false) {
6583
+ this.budget.admit();
6381
6584
  const lists = [];
6382
6585
  const separators = [];
6383
6586
  this.newlines();
6384
6587
  const line = this.lexer.lineAt(this.current.offset);
6385
6588
  while (this.current.kind !== "end" && !stops.has(this.current.value)) {
6589
+ this.budget.admit();
6386
6590
  const pipelines = [this.pipeline()];
6387
6591
  const operators = [];
6388
6592
  while (this.is("&&") || this.is("||")) {
@@ -6399,21 +6603,25 @@ var Parser = class {
6399
6603
  this.newlines();
6400
6604
  } else if (!this.isEnd() && !this.is(")") && !(stops.has(this.current.value) && [";;", ";&", ";;&"].includes(this.current.value))) this.error("Expected command separator");
6401
6605
  }
6402
- const printed = printedSimpleLines(lists, separators);
6606
+ const printed = printedSimpleLines(lists, separators, this.budget);
6403
6607
  return { lists, line, ...printed };
6404
6608
  }
6405
6609
  pipeline() {
6610
+ this.budget.admit();
6406
6611
  const negate = this.is("!");
6407
6612
  if (negate) this.advance();
6408
6613
  const commands = [this.command()];
6409
6614
  while (this.is("|") || this.is("|&")) {
6410
6615
  const operator = this.advance();
6411
- if (operator.value === "|&") commands.at(-1).redirects.push({
6412
- descriptor: 2,
6413
- operator: ">&",
6414
- implicitPipeline: true,
6415
- target: { offset: operator.offset, plain: "1", spelling: "1", parts: [{ kind: "text", value: "1", quoted: false }] }
6416
- });
6616
+ if (operator.value === "|&") {
6617
+ this.budget.admit(3);
6618
+ commands.at(-1).redirects.push({
6619
+ descriptor: 2,
6620
+ operator: ">&",
6621
+ implicitPipeline: true,
6622
+ target: { offset: operator.offset, plain: "1", spelling: "1", parts: [{ kind: "text", value: "1", quoted: false }] }
6623
+ });
6624
+ }
6417
6625
  this.newlines();
6418
6626
  commands.push(this.command());
6419
6627
  }
@@ -6421,10 +6629,12 @@ var Parser = class {
6421
6629
  }
6422
6630
  command() {
6423
6631
  if (++this.nesting + this.lexer.depth > 64) this.error("Syntax nesting exceeds 64");
6632
+ this.budget.admit();
6424
6633
  const line = this.lexer.lineAt(this.current.offset);
6425
6634
  this.openCommands.push({ name: this.current.value, line });
6426
6635
  try {
6427
6636
  const command2 = this.commandInner();
6637
+ this.budget.admit();
6428
6638
  return { ...command2, line: command2.line ?? line };
6429
6639
  } finally {
6430
6640
  this.nesting--;
@@ -6432,6 +6642,7 @@ var Parser = class {
6432
6642
  }
6433
6643
  }
6434
6644
  commandInner() {
6645
+ this.budget.admit();
6435
6646
  let command2;
6436
6647
  if (this.is("[[")) {
6437
6648
  const start = this.current.end;
@@ -6453,7 +6664,9 @@ var Parser = class {
6453
6664
  admit2();
6454
6665
  this.advance();
6455
6666
  const expression2 = primary(depth + 1);
6456
- return expression2.kind === "not" && !grouped.has(expression2) ? expression2.operand : { kind: "not", operand: expression2 };
6667
+ if (expression2.kind === "not" && !grouped.has(expression2)) return expression2.operand;
6668
+ this.budget.admit();
6669
+ return { kind: "not", operand: expression2 };
6457
6670
  }
6458
6671
  if (this.is("(")) {
6459
6672
  this.advance();
@@ -6463,6 +6676,7 @@ var Parser = class {
6463
6676
  return expression2;
6464
6677
  }
6465
6678
  admit2();
6679
+ this.budget.admit();
6466
6680
  const first = operand();
6467
6681
  if (conditionalBinary.has(this.current.value)) {
6468
6682
  const operator = this.current.value;
@@ -6478,6 +6692,7 @@ var Parser = class {
6478
6692
  let expression2 = primary(depth);
6479
6693
  while (this.is("&&")) {
6480
6694
  admit2();
6695
+ this.budget.admit();
6481
6696
  this.advance();
6482
6697
  expression2 = { kind: "and", left: expression2, right: primary(depth) };
6483
6698
  }
@@ -6487,6 +6702,7 @@ var Parser = class {
6487
6702
  let expression2 = conjunction(depth);
6488
6703
  while (this.is("||")) {
6489
6704
  admit2();
6705
+ this.budget.admit();
6490
6706
  this.advance();
6491
6707
  expression2 = { kind: "or", left: expression2, right: conjunction(depth) };
6492
6708
  }
@@ -6502,7 +6718,7 @@ var Parser = class {
6502
6718
  const start = this.current.offset + 2;
6503
6719
  const end = arithmeticEnd(this.lexer.source, start);
6504
6720
  const source = this.lexer.source.slice(start, end);
6505
- command2 = { kind: "arithmetic", expression: prepareArithmetic(source), source, redirects: [] };
6721
+ command2 = { kind: "arithmetic", expression: prepareArithmetic(source, this.budget), source, redirects: [] };
6506
6722
  this.lexer.position = end + 2;
6507
6723
  this.lookahead = void 0;
6508
6724
  this.current = this.lexer.next();
@@ -6520,6 +6736,7 @@ var Parser = class {
6520
6736
  const condition = this.nonemptyScript(/* @__PURE__ */ new Set(["then"]));
6521
6737
  this.expect("then");
6522
6738
  const body = this.nonemptyScript(/* @__PURE__ */ new Set(["elif", "else", "fi"]));
6739
+ this.budget.admit();
6523
6740
  branches.push({ condition, body });
6524
6741
  if (!this.is("elif")) break;
6525
6742
  this.advance();
@@ -6552,6 +6769,7 @@ var Parser = class {
6552
6769
  const body = this.script(/* @__PURE__ */ new Set([";;", ";&", ";;&", "esac"]));
6553
6770
  if (![";;", ";&", ";;&", "esac"].includes(this.current.value)) this.error("Expected case terminator");
6554
6771
  const terminator = this.current.value;
6772
+ this.budget.admit();
6555
6773
  clauses.push({ patterns: patterns2, body, terminator });
6556
6774
  if (terminator === "esac") break;
6557
6775
  this.advance();
@@ -6595,7 +6813,7 @@ var Parser = class {
6595
6813
  this.newlines();
6596
6814
  if (!this.is("{")) this.error("Expected brace function body");
6597
6815
  command2 = { kind: "function", name: name2, body: this.command(), redirects: [] };
6598
- } else if (this.current.kind === "word" && !compoundHead(this.current.word) && this.peek().value === "(") {
6816
+ } else if (this.current.kind === "word" && !compoundHead(this.current.word, this.budget) && this.peek().value === "(") {
6599
6817
  const name2 = this.advance().value;
6600
6818
  if (!/^[a-zA-Z_][a-zA-Z_0-9]*$/u.test(name2)) this.error("Invalid function name");
6601
6819
  this.expect("(");
@@ -6617,21 +6835,22 @@ var Parser = class {
6617
6835
  } else if (this.current.kind === "word") {
6618
6836
  line ??= wordLine;
6619
6837
  let word = this.advance().word;
6620
- const head = compoundHead(word);
6838
+ const head = compoundHead(word, this.budget);
6621
6839
  if (head && this.is("(")) {
6622
6840
  this.advance();
6623
6841
  const entries = [];
6624
6842
  this.newlines();
6625
6843
  while (this.current.kind === "word") {
6626
- entries.push(compoundEntry(this.advance().word));
6844
+ entries.push(compoundEntry(this.advance().word, this.budget));
6627
6845
  this.newlines();
6628
6846
  }
6629
6847
  if (!this.is(")")) this.error("Unsupported indexed-array compound assignment");
6630
6848
  const end = this.advance().end;
6849
+ this.budget.admit(2);
6631
6850
  word = { offset: word.offset, parts: word.parts, spelling: this.lexer.source.slice(word.offset, end) };
6632
6851
  setArrayAssignment(word, { kind: "compound", ...head, entries });
6633
6852
  } else {
6634
- const assignment = words.every((previous) => getArrayAssignment(previous) || scalarAssignmentName(previous)) ? elementAssignment(word) : void 0;
6853
+ const assignment = words.every((previous) => getArrayAssignment(previous) || scalarAssignmentName(previous)) ? elementAssignment(word, this.budget) : void 0;
6635
6854
  if (assignment) setArrayAssignment(word, assignment);
6636
6855
  }
6637
6856
  words.push(word);
@@ -6657,6 +6876,7 @@ var Parser = class {
6657
6876
  if (/^(?:>|>>|<|<<|<<-|<<<|>&|<&|>\|)$/u.test(next.value) && this.current.end === next.offset) descriptor2 = Number(this.advance().value);
6658
6877
  }
6659
6878
  if (!/^(?:>|>>|<|<<|<<-|<<<|>&|<&|>\||&>)$/u.test(this.current.value) || this.current.kind !== "operator") return void 0;
6879
+ this.budget.admit();
6660
6880
  const operator = this.advance().value;
6661
6881
  descriptor2 ??= operator.startsWith("<") ? 0 : 1;
6662
6882
  if (!Number.isSafeInteger(descriptor2) || descriptor2 > 255) this.error("File descriptor must be between 0 and 255");
@@ -6665,29 +6885,40 @@ var Parser = class {
6665
6885
  return { descriptor: descriptor2, operator, target: target.word, line: this.lexer.lineAt(Math.max(target.offset, target.end - 1)), ...(operator === "<&" || operator === ">&") && this.lexer.source[target.end - 1] === "-" ? { move: true } : {}, ...target.document ? { document: target.document } : {} };
6666
6886
  }
6667
6887
  };
6668
- function parseShell(source, depth = 0) {
6888
+ function parseShell(source, depth = 0, options3 = {}) {
6889
+ const budget = new ParseBudget(options3.maxParseUnits);
6669
6890
  const warnings = [];
6670
- const script = parseSource(source, depth, warnings);
6891
+ const script = parseSource(source, depth, warnings, 0, false, budget);
6892
+ budget.admit();
6671
6893
  return { ...script, ...warnings.length ? { warnings } : {} };
6672
6894
  }
6673
- function* hereDocumentWords(document, line, byteLocale2, warnings) {
6674
- if (document.quoted) yield { offset: document.offset, parts: [{ kind: "text", value: document.body, quoted: true }] };
6675
- else yield* new Lexer(document.body, document.depth, warnings, line - 1, byteLocale2, line).documentWords();
6895
+ function* hereDocumentWords(document, line, byteLocale2, warnings, budget = new ParseBudget()) {
6896
+ if (document.quoted) {
6897
+ budget.admit(2);
6898
+ yield { offset: document.offset, parts: [{ kind: "text", value: document.body, quoted: true }] };
6899
+ } else {
6900
+ budget.admit();
6901
+ yield* new Lexer(budget, document.body, document.depth, warnings, line - 1, byteLocale2, line).documentWords();
6902
+ }
6676
6903
  }
6677
- function parseShellUnit(source, position = 0, byteLocale2 = false) {
6904
+ function parseShellUnit(source, position = 0, byteLocale2 = false, budget = new ParseBudget()) {
6678
6905
  const warnings = [];
6679
- const parser = new Parser(source, 0, warnings, 0, position, byteLocale2);
6906
+ budget.admit();
6907
+ const parser = new Parser(budget, source, 0, warnings, 0, position, byteLocale2);
6680
6908
  const script = parser.script(/* @__PURE__ */ new Set(), true);
6681
6909
  const next = parser.current.end;
6682
6910
  const nul = source.indexOf("\0", position);
6683
6911
  if (nul >= 0 && nul < next) throw new ShellSyntaxError("NUL bytes are not valid shell source", nul);
6912
+ budget.admit(2);
6684
6913
  return { script: { ...script, ...warnings.length ? { warnings } : {} }, next };
6685
6914
  }
6686
- function parseShellInputUnit(source, byteLocale2 = false) {
6915
+ function parseShellInputUnit(source, byteLocale2 = false, budget = new ParseBudget()) {
6687
6916
  const warnings = [];
6688
6917
  try {
6689
- const parser = new Parser(source, 0, warnings, 0, 0, byteLocale2, true);
6918
+ budget.admit();
6919
+ const parser = new Parser(budget, source, 0, warnings, 0, 0, byteLocale2, true);
6690
6920
  const script = parser.script(/* @__PURE__ */ new Set(), true);
6921
+ budget.admit(2);
6691
6922
  return { script: { ...script, ...warnings.length ? { warnings } : {} }, next: parser.current.end };
6692
6923
  } catch (error) {
6693
6924
  if (error instanceof IncompleteShellInput) return void 0;
@@ -6695,8 +6926,9 @@ function parseShellInputUnit(source, byteLocale2 = false) {
6695
6926
  throw error;
6696
6927
  }
6697
6928
  }
6698
- function parseSource(source, depth, warnings, lineOffset = 0, byteLocale2 = false) {
6699
- const parser = new Parser(source, depth, warnings, lineOffset, void 0, byteLocale2);
6929
+ function parseSource(source, depth, warnings, lineOffset, byteLocale2, budget) {
6930
+ budget.admit();
6931
+ const parser = new Parser(budget, source, depth, warnings, lineOffset, void 0, byteLocale2);
6700
6932
  const script = parser.script();
6701
6933
  if (parser.current.kind !== "end") parser.error("Unexpected token");
6702
6934
  return script;
@@ -7101,7 +7333,7 @@ function evaluatePositionalArithmetic(program, options3, evaluate) {
7101
7333
  reserve(bytes2);
7102
7334
  const expanded = chunks.join("");
7103
7335
  options3.checkpoint();
7104
- return evaluate(prepareArithmetic(expanded));
7336
+ return evaluate(prepareArithmetic(expanded, options3.parseBudget));
7105
7337
  } finally {
7106
7338
  for (let position = admissions.length - 1; position >= 0; position--) admissions[position].release();
7107
7339
  header?.release();
@@ -10063,6 +10295,7 @@ async function matchEre(program, subject, ledger, signal) {
10063
10295
 
10064
10296
  // packages/safe-bash/src/shell/runtime.ts
10065
10297
  var defaultLimits = {
10298
+ maxParseUnits: defaultMaxParseUnits,
10066
10299
  maxInputBytes: 32 * 1024 * 1024,
10067
10300
  maxOutputBytes: 16 * 1024 * 1024,
10068
10301
  maxCommands: 1e4,
@@ -10153,11 +10386,13 @@ var Budget = class {
10153
10386
  constructor(limits, signal) {
10154
10387
  this.limits = limits;
10155
10388
  this.signal = signal ? AbortSignal.any([signal, this.controller.signal]) : this.controller.signal;
10389
+ this.parsing = new ParseBudget(limits.maxParseUnits, this.signal, (error) => this.controller.abort(error));
10156
10390
  this.values = new ValueArena(limits.maxExpansionBytes, limits.maxExpansionFields, () => this.signal.throwIfAborted(), (limit) => this.fail(limit));
10157
10391
  this.#wallClockDeadline = Date.now() + limits.maxWallClockMs;
10158
10392
  this.#armWallClock();
10159
10393
  }
10160
10394
  limits;
10395
+ parsing;
10161
10396
  values;
10162
10397
  commands = 0;
10163
10398
  iterations = 0;
@@ -11297,7 +11532,7 @@ var Runtime = class _Runtime {
11297
11532
  if (shellValueByteLength(value2) > this.budget.limits.maxExpansionBytes) this.budget.fail("maxExpansionBytes");
11298
11533
  if (name2 === "OPTIND" && state.getopts?.integer && origin !== "arithmetic") {
11299
11534
  try {
11300
- value2 = String(evaluateArithmetic(prepareArithmetic(shellValueText(value2) || "0"), this.arithmeticVariables(state)));
11535
+ value2 = String(evaluateArithmetic(prepareArithmetic(shellValueText(value2) || "0", this.budget.parsing), this.arithmeticVariables(state), this.budget.parsing));
11301
11536
  } catch (error) {
11302
11537
  this.rethrowArithmeticControl(error);
11303
11538
  throw new ExpansionFailure(message(error));
@@ -12003,6 +12238,7 @@ var Runtime = class _Runtime {
12003
12238
  if (command2.kind === "arithmetic") {
12004
12239
  try {
12005
12240
  return Number(evaluatePositionalArithmetic(command2.expression, {
12241
+ parseBudget: this.budget.parsing,
12006
12242
  positional: state.positional,
12007
12243
  arg0: state.arg0 ?? "virtual-bash",
12008
12244
  owner: arrayStore(state)?.owner,
@@ -12010,7 +12246,7 @@ var Runtime = class _Runtime {
12010
12246
  checkpoint: () => this.signal.throwIfAborted(),
12011
12247
  requireParameter: (name2, value2) => this.requireParameter(value2, name2, state, io),
12012
12248
  limit: () => this.budget.fail("maxExpansionBytes")
12013
- }, (prepared) => evaluateArithmetic(prepared, this.arithmeticVariables(state, io.diagnosticLine))) === 0n);
12249
+ }, (prepared) => evaluateArithmetic(prepared, this.arithmeticVariables(state, io.diagnosticLine), this.budget.parsing)) === 0n);
12014
12250
  } catch (error) {
12015
12251
  this.rethrowArithmeticControl(error);
12016
12252
  throw new Error(`((: ${message(error)}`);
@@ -12164,7 +12400,7 @@ var Runtime = class _Runtime {
12164
12400
  let words = 0;
12165
12401
  const warnings = [];
12166
12402
  try {
12167
- for (const word of hereDocumentWords(document, line, byteLocale(state.variables), warnings)) {
12403
+ for (const word of hereDocumentWords(document, line, byteLocale(state.variables), warnings, this.budget.parsing)) {
12168
12404
  this.signal.throwIfAborted();
12169
12405
  for (const warning of warnings.splice(0)) await writeText(io.stderr, `shell: warning: ${warning}
12170
12406
  `);
@@ -13056,7 +13292,7 @@ ${prefix2} line ${offset + line}: \`${source.split("\n")[line - 1] ?? ""}'
13056
13292
  try {
13057
13293
  do {
13058
13294
  this.signal.throwIfAborted();
13059
- const unit = parseShellUnit(source, position, byteLocale(state.variables));
13295
+ const unit = parseShellUnit(source, position, byteLocale(state.variables), this.budget.parsing);
13060
13296
  for (const warning of unit.script.warnings ?? []) await writeText(io.stderr, `${io.scriptName}: warning: ${warning}
13061
13297
  `);
13062
13298
  if (unit.script.lists.length) {
@@ -13093,7 +13329,7 @@ ${prefix2} line ${offset + line}: \`${source.split("\n")[line - 1] ?? ""}'
13093
13329
  if (bytes2) source += this.sourceText(bytes2, io.scriptName ?? "shell");
13094
13330
  const unitIO = { ...io, diagnosticOffset: offset };
13095
13331
  try {
13096
- const unit = eof ? parseShellUnit(source, 0, byteLocale(state.variables)) : parseShellInputUnit(source, byteLocale(state.variables));
13332
+ const unit = eof ? parseShellUnit(source, 0, byteLocale(state.variables), this.budget.parsing) : parseShellInputUnit(source, byteLocale(state.variables), this.budget.parsing);
13097
13333
  if (unit) {
13098
13334
  for (const warning of unit.script.warnings ?? []) await writeText(io.stderr, `${io.scriptName}: warning: ${warning}
13099
13335
  `);
@@ -13357,7 +13593,7 @@ ${prefix2} line ${offset + line}: \`${source.split("\n")[line - 1] ?? ""}'
13357
13593
  let position = 0;
13358
13594
  do {
13359
13595
  this.signal.throwIfAborted();
13360
- const unit = parseShellUnit(source, position, byteLocale(context.env));
13596
+ const unit = parseShellUnit(source, position, byteLocale(context.env), this.budget.parsing);
13361
13597
  units.push(unit.script);
13362
13598
  position = unit.next;
13363
13599
  } while (position < source.length);
@@ -13390,7 +13626,7 @@ ${prefix2} line ${offset + line}: \`${source.split("\n")[line - 1] ?? ""}'
13390
13626
  try {
13391
13627
  do {
13392
13628
  this.signal.throwIfAborted();
13393
- const unit = parseShellUnit(source, position, byteLocale(state.variables));
13629
+ const unit = parseShellUnit(source, position, byteLocale(state.variables), this.budget.parsing);
13394
13630
  for (const warning of unit.script.warnings ?? []) await writeText(io.stderr, `${io.scriptName ?? "shell"}: warning: ${warning}
13395
13631
  `);
13396
13632
  if (unit.script.lists.length) {
@@ -13704,7 +13940,7 @@ ${prefix2} line ${offset + line}: \`${source.split("\n")[line - 1] ?? ""}'
13704
13940
  for (let index = offset; index < args.length; index++) {
13705
13941
  this.signal.throwIfAborted();
13706
13942
  try {
13707
- value2 = evaluateArithmetic(prepareArithmetic(args[index]), variables);
13943
+ value2 = evaluateArithmetic(prepareArithmetic(args[index], this.budget.parsing), variables, this.budget.parsing);
13708
13944
  } catch (error) {
13709
13945
  this.rethrowArithmeticControl(error);
13710
13946
  throw new Error(`let: ${message(error)}`);
@@ -14369,8 +14605,10 @@ ${prefix2} line ${offset + line}: \`${source.split("\n")[line - 1] ?? ""}'
14369
14605
  else {
14370
14606
  let index;
14371
14607
  try {
14372
- index = numericIndex(literalIndex(selector, 0));
14373
- } catch {
14608
+ index = numericIndex(literalIndex(selector, 0, this.budget.parsing));
14609
+ } catch (error) {
14610
+ this.signal.throwIfAborted();
14611
+ if (error instanceof ShellLimitError) throw error;
14374
14612
  await this.diagnostic(context, "indexed array: unsupported subscript");
14375
14613
  status = 2;
14376
14614
  continue;
@@ -14574,6 +14812,7 @@ ${prefix2} line ${offset + line}: \`${source.split("\n")[line - 1] ?? ""}'
14574
14812
  if (part.kind === "arithmetic") {
14575
14813
  try {
14576
14814
  return String(evaluatePositionalArithmetic(part.expression, {
14815
+ parseBudget: this.budget.parsing,
14577
14816
  positional: state.positional,
14578
14817
  arg0: state.arg0 ?? "virtual-bash",
14579
14818
  owner: arrayStore(state)?.owner,
@@ -14581,7 +14820,7 @@ ${prefix2} line ${offset + line}: \`${source.split("\n")[line - 1] ?? ""}'
14581
14820
  checkpoint: () => this.signal.throwIfAborted(),
14582
14821
  requireParameter: (name2, value3) => this.requireParameter(value3, name2, state, io, part.line),
14583
14822
  limit: () => this.budget.fail("maxExpansionBytes")
14584
- }, (prepared) => evaluateArithmetic(prepared, this.arithmeticVariables(state, io.diagnosticLine ?? part.line))));
14823
+ }, (prepared) => evaluateArithmetic(prepared, this.arithmeticVariables(state, io.diagnosticLine ?? part.line), this.budget.parsing)));
14585
14824
  } catch (error) {
14586
14825
  this.rethrowArithmeticControl(error);
14587
14826
  throw new ExpansionFailure(message(error), io.diagnosticLine ?? part.line);
@@ -14720,7 +14959,7 @@ ${prefix2} line ${offset + line}: \`${source.split("\n")[line - 1] ?? ""}'
14720
14959
  }
14721
14960
  this.signal.throwIfAborted();
14722
14961
  try {
14723
- return { value: evaluateArithmetic(prepareArithmetic(source), variables), source };
14962
+ return { value: evaluateArithmetic(prepareArithmetic(source, this.budget.parsing), variables, this.budget.parsing), source };
14724
14963
  } catch (error) {
14725
14964
  this.rethrowArithmeticControl(error);
14726
14965
  throw new ExpansionFailure(`${part.name}: ${message(error)}`, line);
@@ -14850,29 +15089,67 @@ ${prefix2} line ${offset + line}: \`${source.split("\n")[line - 1] ?? ""}'
14850
15089
  const arrayOwned = word.parts.some((part) => part.kind === "variable" && (getArraySelector(part) !== void 0 || arrayStore(state)?.get(part.name) !== void 0));
14851
15090
  const owner = arrayOwned ? requireArrays(state).owner : void 0;
14852
15091
  const holding = owner?.hold();
15092
+ const scratch = !owner && split && state.variables.IFS !== "" && word.parts.some((part) => !part.quoted && part.kind !== "text") ? this.budget.values.scope() : void 0;
14853
15093
  try {
14854
15094
  if (owner) await this.prepareArrayObservers(state, owner);
14855
15095
  owner?.reserve({ metadata: 128 + word.parts.length * 32, allocatedSlots: word.parts.length + 1, work: word.parts.length + 5 });
14856
- const fields = [{ value: "", fragments: [], bytes: false, pattern: "", present: false }];
15096
+ scratch?.reserve(word.parts.length * 32, 0);
15097
+ const fields = [];
15098
+ const addField = () => {
15099
+ if (fields.length >= this.budget.limits.maxExpansionFields) this.budget.fail("maxExpansionFields");
15100
+ scratch?.reserve(32, 0);
15101
+ owner?.reserve({ metadata: 32, allocatedSlots: 1, work: 3 });
15102
+ fields.push({ fragments: [], bytes: false, patterns: void 0, present: false });
15103
+ };
15104
+ addField();
14857
15105
  let expansionBytes = 0;
14858
15106
  const append = (value2, glob, present) => {
14859
15107
  const text = shellValueText(value2);
14860
15108
  const size = shellValueByteLength(value2);
14861
15109
  if (size > this.budget.limits.maxExpansionBytes - expansionBytes) this.budget.fail("maxExpansionBytes");
14862
15110
  expansionBytes += size;
14863
- regexAppend?.(text, !glob);
14864
15111
  const field = fields.at(-1);
14865
- if (owner) owner.reserve({ payload: exactSum(import_buffer.Buffer.byteLength(field.value) + size, import_buffer.Buffer.byteLength(field.pattern) + (glob ? size : size * 2)), metadata: 64, work: text.length + 8 });
15112
+ let escapes = 0;
15113
+ if (!glob) {
15114
+ const special2 = conditionalPattern ? "\\*?[]-^()|+!@" : "\\*?[]-^";
15115
+ for (const character of text) if (special2.includes(character)) escapes++;
15116
+ }
15117
+ scratch?.reserve(32, 0);
15118
+ if (owner) owner.reserve({ payload: size + (escapes ? size + escapes : 0), metadata: 64, work: text.length + 8 });
15119
+ if (escapes) {
15120
+ scratch?.reserve((field.patterns ? 32 : 32 * (field.fragments.length + 1)) + (text.length + escapes) * 2, 0);
15121
+ field.patterns ??= field.fragments.map(shellValueText);
15122
+ field.patterns.push(text.replace(conditionalPattern ? /[\\*?[\]\-^()|+!@]/gu : /[\\*?[\]\-^]/gu, "\\$&"));
15123
+ } else if (field.patterns) {
15124
+ scratch?.reserve(32, 0);
15125
+ field.patterns.push(text);
15126
+ }
14866
15127
  if (typeof value2 !== "string" || field.bytes) {
14867
- io[valueScope]?.reserve(32 * (field.bytes ? 1 : field.fragments.length + 1), field.bytes ? 1 : field.fragments.length + 1);
15128
+ if (!scratch) io[valueScope]?.reserve(32 * (field.bytes ? 1 : field.fragments.length + 1), field.bytes ? 1 : field.fragments.length + 1);
14868
15129
  if (typeof value2 !== "string") io[valueScope]?.hold(value2);
14869
15130
  field.bytes = true;
14870
15131
  }
15132
+ regexAppend?.(text, !glob);
14871
15133
  field.fragments.push(value2);
14872
- field.value += text;
14873
- field.pattern += glob ? text : text.replace(conditionalPattern ? /[\\*?[\]\-^()|+!@]/gu : /[\\*?[\]\-^]/gu, "\\$&");
14874
15134
  field.present ||= present;
14875
15135
  };
15136
+ const appendSplit = async (value2) => {
15137
+ const separators = state.variables.IFS ?? " \n";
15138
+ let boundary = false;
15139
+ for await (const piece of this.splitValue(value2, separators, io, scratch)) {
15140
+ if (typeof piece === "string" && separators.includes(piece)) {
15141
+ if (!" \n".includes(piece)) {
15142
+ fields.at(-1).present = true;
15143
+ addField();
15144
+ } else if (fields.at(-1).present) boundary = true;
15145
+ } else {
15146
+ if (boundary) addField();
15147
+ boundary = false;
15148
+ append(piece, true, true);
15149
+ }
15150
+ }
15151
+ if (boundary) addField();
15152
+ };
14876
15153
  const parts = word.parts.map((part) => ({ part, splitText: false }));
14877
15154
  for (let index = 0; index < parts.length; index++) {
14878
15155
  const { part, splitText } = parts[index];
@@ -14881,6 +15158,7 @@ ${prefix2} line ${offset + line}: \`${source.split("\n")[line - 1] ?? ""}'
14881
15158
  const value2 = this.variable(state, part.name);
14882
15159
  const missing = value2 === void 0 || part.operator.startsWith(":") && value2 === "";
14883
15160
  if (part.operator.endsWith("+") ? !missing : missing) {
15161
+ scratch?.reserve(part.alternate.parts.length * 32, 0);
14884
15162
  const alternate = part.alternate.parts.map((entry) => ({ part: copyArraySelector(entry, { ...entry, quoted: entry.quoted || part.quoted }), splitText: true }));
14885
15163
  if (!alternate.length && part.quoted) append("", false, true);
14886
15164
  parts.splice(index + 1, 0, ...alternate);
@@ -14891,37 +15169,10 @@ ${prefix2} line ${offset + line}: \`${source.split("\n")[line - 1] ?? ""}'
14891
15169
  if (part.kind === "variable" && selector?.kind === "members" && !part.length && split && (!part.quoted || selector.separator === "@")) {
14892
15170
  const members = await this.arrayMembers(part.name, state);
14893
15171
  for (let position = 0; position < members.length; position++) {
14894
- if (position > 0) {
14895
- owner?.reserve({ metadata: 32, allocatedSlots: 1, work: 3 });
14896
- fields.push({ value: "", fragments: [], bytes: false, pattern: "", present: false });
14897
- }
15172
+ if (position > 0) addField();
14898
15173
  const value2 = members[position];
14899
15174
  if (part.quoted || state.variables.IFS === "") append(value2, !part.quoted, part.quoted || value2.length > 0);
14900
- else {
14901
- const separators = state.variables.IFS ?? " \n";
14902
- let boundary = false;
14903
- for (const character of value2) {
14904
- if (separators.includes(character)) {
14905
- if (!/[ \t\n]/u.test(character)) {
14906
- fields.at(-1).present = true;
14907
- owner?.reserve({ metadata: 32, allocatedSlots: 1, work: 3 });
14908
- fields.push({ value: "", fragments: [], bytes: false, pattern: "", present: false });
14909
- } else if (fields.at(-1).present) boundary = true;
14910
- } else {
14911
- if (boundary) {
14912
- owner?.reserve({ metadata: 32, allocatedSlots: 1, work: 3 });
14913
- fields.push({ value: "", fragments: [], bytes: false, pattern: "", present: false });
14914
- }
14915
- boundary = false;
14916
- append(character, true, true);
14917
- }
14918
- await owner.ledger.checkpoint(this.signal);
14919
- }
14920
- if (boundary) {
14921
- owner?.reserve({ metadata: 32, allocatedSlots: 1, work: 3 });
14922
- fields.push({ value: "", fragments: [], bytes: false, pattern: "", present: false });
14923
- }
14924
- }
15175
+ else await appendSplit(value2);
14925
15176
  }
14926
15177
  } else if (part.kind === "text" && !splitText) {
14927
15178
  let value2 = invokedValues.get(part) ?? part.byteValue ?? part.value;
@@ -14929,32 +15180,14 @@ ${prefix2} line ${offset + line}: \`${source.split("\n")[line - 1] ?? ""}'
14929
15180
  append(value2, !part.quoted, quotedPresence || shellValueByteLength(value2) > 0);
14930
15181
  } else if (part.kind === "variable" && part.name === "@" && part.quoted && !part.operator && split) {
14931
15182
  for (let position = 0; position < state.positional.length; position++) {
14932
- if (position > 0) fields.push({ value: "", fragments: [], bytes: false, pattern: "", present: false });
15183
+ if (position > 0) addField();
14933
15184
  append(stateMonitor(state)?.positionals.get(String(position), state.positional[position]) ?? state.positional[position], false, true);
14934
15185
  }
14935
15186
  if (state.positional.length === 0 && word.parts.every((entry) => entry.kind === "text" && entry.value === "" || entry === part)) fields[0].present = false;
14936
15187
  } else {
14937
15188
  const value2 = part.kind === "text" ? part.byteValue ?? part.value : await this.valuePart(part, state, io, hereString);
14938
15189
  if (part.quoted || !split || state.variables.IFS === "") append(value2, !part.quoted, quotedPresence || !split || shellValueByteLength(value2) > 0);
14939
- else {
14940
- const separators = state.variables.IFS ?? " \n";
14941
- let boundary = false;
14942
- const pieces = this.splitValue(value2, separators, io);
14943
- for (const character of pieces) {
14944
- const text = shellValueText(character);
14945
- if (typeof character === "string" && separators.includes(character)) {
14946
- if (!/[ \t\n]/u.test(text)) {
14947
- fields.at(-1).present = true;
14948
- fields.push({ value: "", fragments: [], bytes: false, pattern: "", present: false });
14949
- } else if (fields.at(-1).present) boundary = true;
14950
- } else {
14951
- if (boundary) fields.push({ value: "", fragments: [], bytes: false, pattern: "", present: false });
14952
- boundary = false;
14953
- append(character, true, true);
14954
- }
14955
- }
14956
- if (boundary) fields.push({ value: "", fragments: [], bytes: false, pattern: "", present: false });
14957
- }
15190
+ else await appendSplit(value2);
14958
15191
  }
14959
15192
  if (fields.length > this.budget.limits.maxExpansionFields) this.budget.fail("maxExpansionFields");
14960
15193
  if (owner) await owner.ledger.checkpoint(this.signal);
@@ -14963,14 +15196,23 @@ ${prefix2} line ${offset + line}: \`${source.split("\n")[line - 1] ?? ""}'
14963
15196
  let resultBytes = 0;
14964
15197
  for (const field of fields) {
14965
15198
  if (!field.present && split) continue;
15199
+ if (scratch && field.fragments.length > 1 && !field.bytes) scratch.reserve(field.fragments.reduce((bytes2, value2) => bytes2 + shellValueText(value2).length * 2, 0), 0);
14966
15200
  const assembled = concatShellValues(field.fragments, io[valueScope]);
14967
15201
  const projection = shellValueText(assembled);
14968
- const expanded = split ? await this.glob(projection, field.pattern, state) : [pattern ? field.pattern : projection];
15202
+ if (field.bytes && field.fragments.length > 1 && !field.patterns) {
15203
+ scratch?.reserve(field.fragments.length * 32, 0);
15204
+ field.patterns = field.fragments.map(shellValueText);
15205
+ }
15206
+ if (scratch && field.patterns && field.patterns.length > 1) scratch.reserve(field.patterns.reduce((bytes2, text) => bytes2 + text.length * 2, 0), 0);
15207
+ const fieldPattern = field.patterns ? field.patterns.join("") : projection;
15208
+ const expanded = split ? await this.glob(projection, fieldPattern, state) : [pattern ? fieldPattern : projection];
14969
15209
  for (const text of expanded) {
15210
+ if (result.length >= this.budget.limits.maxExpansionFields) this.budget.fail("maxExpansionFields");
14970
15211
  const value2 = !pattern && expanded.length === 1 && text === projection ? assembled : text;
14971
15212
  const size = shellValueByteLength(value2);
14972
15213
  if (size > this.budget.limits.maxExpansionBytes - resultBytes) this.budget.fail("maxExpansionBytes");
14973
15214
  resultBytes += size;
15215
+ scratch?.reserve(32, 0);
14974
15216
  owner?.reserve({ metadata: 32, allocatedSlots: 1, work: 3 });
14975
15217
  result.push(value2);
14976
15218
  }
@@ -14978,17 +15220,50 @@ ${prefix2} line ${offset + line}: \`${source.split("\n")[line - 1] ?? ""}'
14978
15220
  }
14979
15221
  return result;
14980
15222
  } finally {
15223
+ scratch?.close();
14981
15224
  holding?.release();
14982
15225
  }
14983
15226
  }
14984
- *splitValue(value2, separators, io) {
15227
+ splitWork;
15228
+ async *splitValue(value2, separators, io, scratch) {
15229
+ this.budget.cpuCheckpoint();
15230
+ const work = this.splitWork ??= { scanned: 0 };
14985
15231
  if (typeof value2 === "string" || Array.from(separators).some((character) => character.charCodeAt(0) > 127)) {
14986
- yield* shellValueText(value2);
15232
+ const text = shellValueText(value2);
15233
+ const slice = (start3, end) => {
15234
+ if (start3 === 0 && end === text.length) return text;
15235
+ scratch?.reserve((end - start3) * 2, 0);
15236
+ return text.slice(start3, end);
15237
+ };
15238
+ let start2 = 0;
15239
+ for (let index = 0; index < text.length; ) {
15240
+ const character = String.fromCodePoint(text.codePointAt(index));
15241
+ const end = index + character.length;
15242
+ if (separators.includes(character)) {
15243
+ if (start2 < index) yield slice(start2, index);
15244
+ yield character;
15245
+ start2 = end;
15246
+ } else if (end - start2 >= 4096) {
15247
+ yield slice(start2, end);
15248
+ start2 = end;
15249
+ }
15250
+ work.scanned += character.length;
15251
+ index = end;
15252
+ if (work.scanned >= 4096) {
15253
+ work.scanned = 0;
15254
+ await yieldTurn(this.signal);
15255
+ }
15256
+ }
15257
+ if (start2 < text.length) yield slice(start2, text.length);
14987
15258
  return;
14988
15259
  }
14989
15260
  const bytes2 = shellValueBytes(value2, io[valueScope]);
14990
15261
  let start = 0;
14991
15262
  for (let index = 0; index < bytes2.length; index++) {
15263
+ if (++work.scanned >= 4096) {
15264
+ work.scanned = 0;
15265
+ await yieldTurn(this.signal);
15266
+ }
14992
15267
  const byte = bytes2[index];
14993
15268
  if (byte > 127 || !separators.includes(String.fromCharCode(byte))) continue;
14994
15269
  if (start < index) yield shellValueFromBytes(bytes2.subarray(start, index), io[valueScope]);
@@ -15679,7 +15954,7 @@ var Shell = class {
15679
15954
  let failed = false;
15680
15955
  try {
15681
15956
  try {
15682
- let unit = parseShellUnit(source, 0, byteLocale({ ...this.#options.env, ...options3.env }));
15957
+ let unit = parseShellUnit(source, 0, byteLocale({ ...this.#options.env, ...options3.env }), budget.parsing);
15683
15958
  stdin = new ShellInput(typeof options3.stdin === "string" || options3.stdin instanceof Uint8Array ? toByteSource(options3.stdin) : options3.stdin ?? toByteSource(""), budget);
15684
15959
  io.stdin = stdin;
15685
15960
  await interruptible(this.#ready, budget.signal);
@@ -15736,7 +16011,7 @@ var Shell = class {
15736
16011
  }
15737
16012
  if (unit.next >= source.length) break;
15738
16013
  budget.signal.throwIfAborted();
15739
- unit = parseShellUnit(source, unit.next, byteLocale(state.variables));
16014
+ unit = parseShellUnit(source, unit.next, byteLocale(state.variables), budget.parsing);
15740
16015
  }
15741
16016
  } catch (error) {
15742
16017
  if (!(error instanceof ShellSyntaxError)) throw error;
@@ -15813,6 +16088,7 @@ shell: -c: line ${line}: \`${source.split("\n")[line - 1] ?? ""}'
15813
16088
  // packages/safe-bash/src/shell/worker-limits.ts
15814
16089
  init_platform();
15815
16090
  var cloudflareWorkerLimits = Object.freeze({
16091
+ maxParseUnits: 65536,
15816
16092
  maxInputBytes: 4 * 1024 * 1024,
15817
16093
  maxOutputBytes: 4 * 1024 * 1024,
15818
16094
  maxCommands: 1e3,
@@ -18696,7 +18972,7 @@ function descriptor(value2, limits) {
18696
18972
  for (const flag of flags) if (typeof value2[flag] !== "boolean") fail("protocol", `invalid ${flag} flag`);
18697
18973
  if (kind.value === "rg" && !["sensitive", "insensitive", "smart"].includes(value2.case)) fail("protocol", "invalid case flag");
18698
18974
  if (value2.word || kind.value === "grep" && value2.insensitive || kind.value === "rg" && value2.case !== "sensitive") fail("unsupported", "only case-sensitive, non-word selection is supported");
18699
- if (kind.value === "rg" && !value2.fixed) fail("unsupported", "rg regex and Unicode modes are unsupported; use fixed ASCII patterns");
18975
+ if (kind.value === "rg" && !value2.fixed) fail("unsupported", "rg regex modes are unsupported; use fixed UTF-8 patterns");
18700
18976
  array(value2.patterns, limits.maxPatterns, "pattern");
18701
18977
  let bytes2 = 0;
18702
18978
  for (let index = 0; index < value2.patterns.length; index++) {
@@ -18724,7 +19000,7 @@ function admit(input3, limits, signal) {
18724
19000
  bytes2 += length;
18725
19001
  }
18726
19002
  const ledger = new EreLedger({ maxExpansionBytes: 1048576, maxExpansionFields: 8192 }, {
18727
- patternBytes: limits.maxPatternBytes + 4,
19003
+ patternBytes: limits.maxPatternBytes + (selected.fixed ? 0 : 4),
18728
19004
  subjectBytes: limits.maxInputBytes,
18729
19005
  work: limits.maxWork,
18730
19006
  allocationUnits: limits.maxAllocationUnits,
@@ -18770,8 +19046,116 @@ async function admitBre(pattern, ledger, signal) {
18770
19046
  }
18771
19047
  }
18772
19048
  }
19049
+ async function validateUtf8(input3, ledger, signal) {
19050
+ for (let index = 0; index < input3.length; ) {
19051
+ const first = typeof input3 === "string" ? input3.charCodeAt(index) : input3[index];
19052
+ const width = first < 128 ? 1 : first >= 194 && first <= 223 ? 2 : first >= 224 && first <= 239 ? 3 : first >= 240 && first <= 244 ? 4 : 0;
19053
+ ledger.charge("work", Math.max(width, 1), signal);
19054
+ if (first === 0 || width === 0 || width > input3.length - index) fail("unsupported", "literal patterns and subjects require valid non-NUL UTF-8 bytes");
19055
+ for (let continuation = 1; continuation < width; continuation++) {
19056
+ const byte = typeof input3 === "string" ? input3.charCodeAt(index + continuation) : input3[index + continuation];
19057
+ if (byte < 128 || byte > 191 || continuation === 1 && (first === 224 && byte < 160 || first === 237 && byte > 159 || first === 240 && byte < 144 || first === 244 && byte > 143)) fail("unsupported", "literal patterns and subjects require valid non-NUL UTF-8 bytes");
19058
+ }
19059
+ index += width;
19060
+ await ledger.checkpoint(signal);
19061
+ }
19062
+ }
19063
+ async function literalBytes(pattern, selected, ledger, signal) {
19064
+ const { kind } = selected;
19065
+ let length = 0;
19066
+ if (kind === "grep") {
19067
+ await validateUtf8(pattern, ledger, signal);
19068
+ length = pattern.length;
19069
+ } else {
19070
+ for (let index = 0; index < pattern.length; ) {
19071
+ const scalar = pattern.codePointAt(index);
19072
+ const units = scalar > 65535 ? 2 : 1;
19073
+ ledger.charge("work", units, signal);
19074
+ if (scalar === 0 || scalar >= 55296 && scalar <= 57343) fail("unsupported", "rg literal patterns require non-NUL Unicode scalars for UTF-8");
19075
+ if (scalar === 10 && selected.kind === "rg" && !selected.nullData) fail("unsupported", "rg multiline matching is unsupported");
19076
+ length += scalar < 128 ? 1 : scalar < 2048 ? 2 : scalar < 65536 ? 3 : 4;
19077
+ index += units;
19078
+ await ledger.checkpoint(signal);
19079
+ }
19080
+ }
19081
+ ledger.charge("patternBytes", length, signal);
19082
+ ledger.charge("allocationUnits", length + 1, signal);
19083
+ const bytes2 = new Uint8Array(length);
19084
+ let offset = 0;
19085
+ for (let index = 0; index < pattern.length; ) {
19086
+ const scalar = kind === "grep" ? pattern.charCodeAt(index) : pattern.codePointAt(index);
19087
+ const width = kind === "grep" || scalar < 128 ? 1 : scalar < 2048 ? 2 : scalar < 65536 ? 3 : 4;
19088
+ ledger.charge("work", width, signal);
19089
+ if (width === 1) bytes2[offset++] = scalar;
19090
+ else {
19091
+ bytes2[offset++] = width === 2 ? 192 | scalar >> 6 : width === 3 ? 224 | scalar >> 12 : 240 | scalar >> 18;
19092
+ if (width === 4) bytes2[offset++] = 128 | scalar >> 12 & 63;
19093
+ if (width >= 3) bytes2[offset++] = 128 | scalar >> 6 & 63;
19094
+ bytes2[offset++] = 128 | scalar & 63;
19095
+ }
19096
+ index += kind === "rg" && scalar > 65535 ? 2 : 1;
19097
+ await ledger.checkpoint(signal);
19098
+ }
19099
+ return bytes2;
19100
+ }
19101
+ async function compileLiteral(bytes2, ledger, signal) {
19102
+ ledger.charge("states", bytes2.length, signal);
19103
+ ledger.charge("allocationUnits", bytes2.length * 4 + 3, signal);
19104
+ const fallback = new Uint32Array(bytes2.length);
19105
+ for (let index = 1, prefix2 = 0; index < bytes2.length; ) {
19106
+ ledger.charge("work", 1, signal);
19107
+ if (bytes2[index] === bytes2[prefix2]) fallback[index++] = ++prefix2;
19108
+ else if (prefix2 > 0) prefix2 = fallback[prefix2 - 1];
19109
+ else index++;
19110
+ await ledger.checkpoint(signal);
19111
+ }
19112
+ return { bytes: bytes2, fallback };
19113
+ }
19114
+ async function literalStart(program, subject, whole, ledger, signal) {
19115
+ const { bytes: bytes2, fallback } = program;
19116
+ ledger.charge("work", 1, signal);
19117
+ await ledger.checkpoint(signal);
19118
+ if (whole && bytes2.length !== subject.length || bytes2.length > subject.length) return -1;
19119
+ if (bytes2.length === 0) return 0;
19120
+ for (let index = 0, prefix2 = 0; index < subject.length; ) {
19121
+ ledger.charge("work", 1, signal);
19122
+ if (subject[index] === bytes2[prefix2]) {
19123
+ index++;
19124
+ if (++prefix2 === bytes2.length) return index - prefix2;
19125
+ } else if (prefix2 > 0) prefix2 = fallback[prefix2 - 1];
19126
+ else index++;
19127
+ await ledger.checkpoint(signal);
19128
+ }
19129
+ return -1;
19130
+ }
19131
+ async function executeLiteral(input3, signal) {
19132
+ const { descriptor: selected, rows, ledger } = input3;
19133
+ const programs2 = [];
19134
+ for (const pattern of selected.patterns) {
19135
+ programs2.push(await compileLiteral(await literalBytes(pattern, selected, ledger, signal), ledger, signal));
19136
+ }
19137
+ const results = [];
19138
+ for (const row of rows) {
19139
+ await validateUtf8(row.bytes, ledger, signal);
19140
+ let start = -1;
19141
+ let end = -1;
19142
+ for (const program of programs2) {
19143
+ const candidate = await literalStart(program, row.bytes, selected.whole, ledger, signal);
19144
+ if (candidate < 0) continue;
19145
+ if (start < 0 || candidate < start) {
19146
+ start = candidate;
19147
+ end = start + program.bytes.length;
19148
+ }
19149
+ if (selected.kind === "grep") break;
19150
+ }
19151
+ signal.throwIfAborted();
19152
+ results.push(start < 0 ? new Float64Array() : new Float64Array([start, end]));
19153
+ }
19154
+ return { id: input3.id, results };
19155
+ }
18773
19156
  async function execute2(input3, signal) {
18774
19157
  const { descriptor: selected, rows, ledger } = input3;
19158
+ if (selected.fixed) return executeLiteral(input3, signal);
18775
19159
  const programs2 = [];
18776
19160
  for (const pattern of selected.patterns) {
18777
19161
  if (selected.kind === "grep" && !selected.fixed && !selected.extended) await admitBre(pattern, ledger, signal);