@poe-platform/safe-bash 0.1.41 → 0.1.43

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 (134) hide show
  1. package/README.md +11 -2
  2. package/dist/safe-bash/browser.d.ts +5 -0
  3. package/dist/safe-bash/browser.d.ts.map +1 -1
  4. package/dist/safe-bash/browser.js +873 -122
  5. package/dist/safe-bash/browser.js.map +4 -4
  6. package/dist/safe-bash/commands/apply-patch/shared.js +2 -2
  7. package/dist/safe-bash/commands/apply-patch/shared.js.map +1 -1
  8. package/dist/safe-bash/commands/archive/internal.d.ts.map +1 -1
  9. package/dist/safe-bash/commands/archive/internal.js +3 -2
  10. package/dist/safe-bash/commands/archive/internal.js.map +1 -1
  11. package/dist/safe-bash/commands/bytes/checksums/index.d.ts.map +1 -1
  12. package/dist/safe-bash/commands/bytes/checksums/index.js +3 -2
  13. package/dist/safe-bash/commands/bytes/checksums/index.js.map +1 -1
  14. package/dist/safe-bash/commands/bytes/compression/gunzip.js +2 -2
  15. package/dist/safe-bash/commands/bytes/compression/gunzip.js.map +1 -1
  16. package/dist/safe-bash/commands/bytes/compression/stream.js +2 -2
  17. package/dist/safe-bash/commands/bytes/compression/stream.js.map +1 -1
  18. package/dist/safe-bash/commands/bytes/encoding/shared.js +3 -3
  19. package/dist/safe-bash/commands/bytes/encoding/shared.js.map +1 -1
  20. package/dist/safe-bash/commands/column/internal.d.ts.map +1 -1
  21. package/dist/safe-bash/commands/column/internal.js +2 -1
  22. package/dist/safe-bash/commands/column/internal.js.map +1 -1
  23. package/dist/safe-bash/commands/diff-patch/shared.js +2 -2
  24. package/dist/safe-bash/commands/diff-patch/shared.js.map +1 -1
  25. package/dist/safe-bash/commands/du/budget.d.ts.map +1 -1
  26. package/dist/safe-bash/commands/du/budget.js +3 -2
  27. package/dist/safe-bash/commands/du/budget.js.map +1 -1
  28. package/dist/safe-bash/commands/env-split.d.ts.map +1 -1
  29. package/dist/safe-bash/commands/env-split.js +2 -1
  30. package/dist/safe-bash/commands/env-split.js.map +1 -1
  31. package/dist/safe-bash/commands/expr/internal.d.ts.map +1 -1
  32. package/dist/safe-bash/commands/expr/internal.js +2 -1
  33. package/dist/safe-bash/commands/expr/internal.js.map +1 -1
  34. package/dist/safe-bash/commands/file/shared.d.ts.map +1 -1
  35. package/dist/safe-bash/commands/file/shared.js +3 -2
  36. package/dist/safe-bash/commands/file/shared.js.map +1 -1
  37. package/dist/safe-bash/commands/find.d.ts.map +1 -1
  38. package/dist/safe-bash/commands/find.js +12 -31
  39. package/dist/safe-bash/commands/find.js.map +1 -1
  40. package/dist/safe-bash/commands/html-to-markdown/budget.js +2 -2
  41. package/dist/safe-bash/commands/html-to-markdown/budget.js.map +1 -1
  42. package/dist/safe-bash/commands/metadata/internal.d.ts.map +1 -1
  43. package/dist/safe-bash/commands/metadata/internal.js +2 -1
  44. package/dist/safe-bash/commands/metadata/internal.js.map +1 -1
  45. package/dist/safe-bash/commands/move.d.ts.map +1 -1
  46. package/dist/safe-bash/commands/move.js +2 -1
  47. package/dist/safe-bash/commands/move.js.map +1 -1
  48. package/dist/safe-bash/commands/network/authorizer.d.ts +7 -0
  49. package/dist/safe-bash/commands/network/authorizer.d.ts.map +1 -0
  50. package/dist/safe-bash/commands/network/authorizer.js +48 -0
  51. package/dist/safe-bash/commands/network/authorizer.js.map +1 -0
  52. package/dist/safe-bash/commands/network/body.js +2 -2
  53. package/dist/safe-bash/commands/network/body.js.map +1 -1
  54. package/dist/safe-bash/commands/network/curl.js +2 -2
  55. package/dist/safe-bash/commands/network/curl.js.map +1 -1
  56. package/dist/safe-bash/commands/network/fetch-transport.d.ts +6 -0
  57. package/dist/safe-bash/commands/network/fetch-transport.d.ts.map +1 -0
  58. package/dist/safe-bash/commands/network/fetch-transport.js +80 -0
  59. package/dist/safe-bash/commands/network/fetch-transport.js.map +1 -0
  60. package/dist/safe-bash/commands/network/index.d.ts +2 -0
  61. package/dist/safe-bash/commands/network/index.d.ts.map +1 -1
  62. package/dist/safe-bash/commands/network/index.js +2 -0
  63. package/dist/safe-bash/commands/network/index.js.map +1 -1
  64. package/dist/safe-bash/commands/network/types.d.ts +1 -0
  65. package/dist/safe-bash/commands/network/types.d.ts.map +1 -1
  66. package/dist/safe-bash/commands/network/types.js +10 -0
  67. package/dist/safe-bash/commands/network/types.js.map +1 -1
  68. package/dist/safe-bash/commands/regex-execution/ere/limits.js +2 -2
  69. package/dist/safe-bash/commands/regex-execution/ere/limits.js.map +1 -1
  70. package/dist/safe-bash/commands/safejs/io.js +2 -2
  71. package/dist/safe-bash/commands/safejs/io.js.map +1 -1
  72. package/dist/safe-bash/commands/search/shared.js +2 -2
  73. package/dist/safe-bash/commands/search/shared.js.map +1 -1
  74. package/dist/safe-bash/commands/split/io.js +2 -2
  75. package/dist/safe-bash/commands/split/io.js.map +1 -1
  76. package/dist/safe-bash/commands/stream-format/shared.d.ts.map +1 -1
  77. package/dist/safe-bash/commands/stream-format/shared.js +2 -1
  78. package/dist/safe-bash/commands/stream-format/shared.js.map +1 -1
  79. package/dist/safe-bash/commands/stream-inspection/shared.d.ts.map +1 -1
  80. package/dist/safe-bash/commands/stream-inspection/shared.js +2 -1
  81. package/dist/safe-bash/commands/stream-inspection/shared.js.map +1 -1
  82. package/dist/safe-bash/commands/structured/limits.js +2 -2
  83. package/dist/safe-bash/commands/structured/limits.js.map +1 -1
  84. package/dist/safe-bash/commands/table-text/internal.d.ts.map +1 -1
  85. package/dist/safe-bash/commands/table-text/internal.js +2 -1
  86. package/dist/safe-bash/commands/table-text/internal.js.map +1 -1
  87. package/dist/safe-bash/commands/text-programs/shared.js +2 -2
  88. package/dist/safe-bash/commands/text-programs/shared.js.map +1 -1
  89. package/dist/safe-bash/commands/tree/io.js +2 -2
  90. package/dist/safe-bash/commands/tree/io.js.map +1 -1
  91. package/dist/safe-bash/contracts/yield.d.ts +13 -0
  92. package/dist/safe-bash/contracts/yield.d.ts.map +1 -0
  93. package/dist/safe-bash/contracts/yield.js +41 -0
  94. package/dist/safe-bash/contracts/yield.js.map +1 -0
  95. package/dist/safe-bash/shell/arithmetic.js +1 -1
  96. package/dist/safe-bash/shell/arithmetic.js.map +1 -1
  97. package/dist/safe-bash/shell/arrays/ledger.d.ts.map +1 -1
  98. package/dist/safe-bash/shell/arrays/ledger.js +2 -9
  99. package/dist/safe-bash/shell/arrays/ledger.js.map +1 -1
  100. package/dist/safe-bash/shell/conditional.d.ts.map +1 -1
  101. package/dist/safe-bash/shell/conditional.js +2 -1
  102. package/dist/safe-bash/shell/conditional.js.map +1 -1
  103. package/dist/safe-bash/shell/env-warning.d.ts +2 -0
  104. package/dist/safe-bash/shell/env-warning.d.ts.map +1 -0
  105. package/dist/safe-bash/shell/env-warning.js +12 -0
  106. package/dist/safe-bash/shell/env-warning.js.map +1 -0
  107. package/dist/safe-bash/shell/index.d.ts +1 -0
  108. package/dist/safe-bash/shell/index.d.ts.map +1 -1
  109. package/dist/safe-bash/shell/index.js +1 -0
  110. package/dist/safe-bash/shell/index.js.map +1 -1
  111. package/dist/safe-bash/shell/input.d.ts.map +1 -1
  112. package/dist/safe-bash/shell/input.js +4 -3
  113. package/dist/safe-bash/shell/input.js.map +1 -1
  114. package/dist/safe-bash/shell/parser.d.ts.map +1 -1
  115. package/dist/safe-bash/shell/parser.js +15 -1
  116. package/dist/safe-bash/shell/parser.js.map +1 -1
  117. package/dist/safe-bash/shell/pattern.d.ts.map +1 -1
  118. package/dist/safe-bash/shell/pattern.js +3 -2
  119. package/dist/safe-bash/shell/pattern.js.map +1 -1
  120. package/dist/safe-bash/shell/runtime.d.ts +3 -2
  121. package/dist/safe-bash/shell/runtime.d.ts.map +1 -1
  122. package/dist/safe-bash/shell/runtime.js +57 -55
  123. package/dist/safe-bash/shell/runtime.js.map +1 -1
  124. package/dist/safe-bash/shell/shell.d.ts.map +1 -1
  125. package/dist/safe-bash/shell/shell.js +4 -1
  126. package/dist/safe-bash/shell/shell.js.map +1 -1
  127. package/dist/safe-bash/shell/types.d.ts +2 -0
  128. package/dist/safe-bash/shell/types.d.ts.map +1 -1
  129. package/dist/safe-bash/shell/types.js.map +1 -1
  130. package/dist/safe-bash/shell/worker-limits.d.ts +3 -0
  131. package/dist/safe-bash/shell/worker-limits.d.ts.map +1 -0
  132. package/dist/safe-bash/shell/worker-limits.js +14 -0
  133. package/dist/safe-bash/shell/worker-limits.js.map +1 -0
  134. package/package.json +2 -2
@@ -1816,14 +1816,12 @@ var require_buffer = __commonJS({
1816
1816
  });
1817
1817
 
1818
1818
  // packages/safe-bash/browser/platform.mjs
1819
- var import_buffer, TransformStream, setImmediate, clearImmediate;
1819
+ var import_buffer, TransformStream;
1820
1820
  var init_platform = __esm({
1821
1821
  "packages/safe-bash/browser/platform.mjs"() {
1822
1822
  "use strict";
1823
1823
  import_buffer = __toESM(require_buffer(), 1);
1824
1824
  TransformStream = globalThis.TransformStream;
1825
- setImmediate = (callback, ...args) => setTimeout(callback, 0, ...args);
1826
- clearImmediate = clearTimeout;
1827
1825
  }
1828
1826
  });
1829
1827
 
@@ -1833,9 +1831,6 @@ init_platform();
1833
1831
  // packages/safe-bash/src/commands/basic.ts
1834
1832
  init_platform();
1835
1833
 
1836
- // packages/safe-bash/src/contracts/index.ts
1837
- init_platform();
1838
-
1839
1834
  // packages/safe-bash/src/contracts/command.ts
1840
1835
  init_platform();
1841
1836
 
@@ -3054,6 +3049,44 @@ async function compareObservedEntries(fs, path, stat, peer, peerPath, peerStat,
3054
3049
 
3055
3050
  // packages/safe-bash/src/commands/move.ts
3056
3051
  init_platform();
3052
+
3053
+ // packages/safe-bash/src/contracts/yield.ts
3054
+ init_platform();
3055
+ var checkpoints = /* @__PURE__ */ new WeakMap();
3056
+ function monotonicNow() {
3057
+ return globalThis.performance?.now() ?? Date.now();
3058
+ }
3059
+ function registerYieldCheckpoint(signal, checkpoint) {
3060
+ checkpoints.set(signal, checkpoint);
3061
+ }
3062
+ function scheduleTurn(callback) {
3063
+ const host = globalThis;
3064
+ return host.setImmediate ? { kind: "immediate", value: host.setImmediate(callback) } : { kind: "timeout", value: setTimeout(callback, 0) };
3065
+ }
3066
+ function cancelTurn(handle) {
3067
+ if (handle?.kind === "immediate") globalThis.clearImmediate?.(handle.value);
3068
+ else if (handle) clearTimeout(handle.value);
3069
+ }
3070
+ function yieldTurn(signal) {
3071
+ signal?.throwIfAborted();
3072
+ if (signal) checkpoints.get(signal)?.();
3073
+ return new Promise((resolve, reject) => {
3074
+ let handle;
3075
+ const finish = () => {
3076
+ signal?.removeEventListener("abort", abort);
3077
+ resolve();
3078
+ };
3079
+ const abort = () => {
3080
+ cancelTurn(handle);
3081
+ reject(signal.reason);
3082
+ };
3083
+ signal?.addEventListener("abort", abort, { once: true });
3084
+ if (signal?.aborted) abort();
3085
+ else handle = scheduleTurn(finish);
3086
+ });
3087
+ }
3088
+
3089
+ // packages/safe-bash/src/commands/move.ts
3057
3090
  var MoveBudget = class {
3058
3091
  constructor(signal) {
3059
3092
  this.signal = signal;
@@ -3066,7 +3099,7 @@ var MoveBudget = class {
3066
3099
  async step() {
3067
3100
  this.signal.throwIfAborted();
3068
3101
  if (++this.steps > 1e5) throw new FsError("EFBIG", { message: "cross-device move entry limit exceeded" });
3069
- if (this.steps % 128 === 0) await new Promise((resolve) => setImmediate(resolve));
3102
+ if (this.steps % 128 === 0) await yieldTurn();
3070
3103
  this.signal.throwIfAborted();
3071
3104
  }
3072
3105
  };
@@ -3801,7 +3834,7 @@ function headTail(name2) {
3801
3834
  });
3802
3835
  }
3803
3836
  function characterSet(specification) {
3804
- const classes = {
3837
+ const classes2 = {
3805
3838
  lower: Array.from({ length: 26 }, (_, offset) => 97 + offset),
3806
3839
  upper: Array.from({ length: 26 }, (_, offset) => 65 + offset),
3807
3840
  digit: Array.from({ length: 10 }, (_, offset) => 48 + offset),
@@ -3811,15 +3844,15 @@ function characterSet(specification) {
3811
3844
  graph: Array.from({ length: 94 }, (_, offset) => 33 + offset),
3812
3845
  print: Array.from({ length: 95 }, (_, offset) => 32 + offset)
3813
3846
  };
3814
- classes.alpha = [...classes.upper, ...classes.lower];
3815
- classes.alnum = [...classes.digit, ...classes.alpha];
3816
- classes.xdigit = [...classes.digit, ...classes.upper.slice(0, 6), ...classes.lower.slice(0, 6)];
3817
- classes.punct = classes.graph.filter((byte) => !classes.alnum.includes(byte));
3847
+ classes2.alpha = [...classes2.upper, ...classes2.lower];
3848
+ classes2.alnum = [...classes2.digit, ...classes2.alpha];
3849
+ classes2.xdigit = [...classes2.digit, ...classes2.upper.slice(0, 6), ...classes2.lower.slice(0, 6)];
3850
+ classes2.punct = classes2.graph.filter((byte) => !classes2.alnum.includes(byte));
3818
3851
  const tokens2 = [];
3819
3852
  for (let offset = 0; offset < specification.length; ) {
3820
3853
  const classMatch = /^\[:([a-z]+):\]/u.exec(specification.slice(offset));
3821
3854
  if (classMatch) {
3822
- const bytes = classes[classMatch[1]];
3855
+ const bytes = classes2[classMatch[1]];
3823
3856
  if (!bytes) throw new UsageError(`unknown character class '${classMatch[1]}'`);
3824
3857
  tokens2.push({ bytes, literal: false });
3825
3858
  offset += classMatch[0].length;
@@ -4444,6 +4477,17 @@ init_platform();
4444
4477
  // packages/safe-bash/src/shell/shell.ts
4445
4478
  init_platform();
4446
4479
 
4480
+ // packages/safe-bash/src/shell/env-warning.ts
4481
+ init_platform();
4482
+ var warned = /* @__PURE__ */ new WeakSet();
4483
+ function warnIfHostProcessEnv(env) {
4484
+ if (!env || typeof process === "undefined" || !process.env) return;
4485
+ if (warned.has(env)) return;
4486
+ if (env !== process.env) return;
4487
+ warned.add(env);
4488
+ console.warn("[safe-bash] warning: the shell `env` is the host `process.env`. Scripts executed in this shell can read every value in it, including secrets; on Cloudflare Workers with nodejs_compat, process.env contains the Worker's secret bindings. Pass only the variables your scripts need. See docs/issues/safe-bash-cloudflare-security-audit.md (finding 1).");
4489
+ }
4490
+
4447
4491
  // packages/safe-bash/src/shell/parser.ts
4448
4492
  init_platform();
4449
4493
 
@@ -4538,7 +4582,7 @@ function integer2(text) {
4538
4582
  for (const character of match[2]) {
4539
4583
  const digit2 = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ@_".indexOf(base <= 36 ? character.toLowerCase() : character);
4540
4584
  if (digit2 >= base || digit2 < 0) throw new Error("Digit exceeds arithmetic base");
4541
- value2 = value2 * BigInt(base) + BigInt(digit2);
4585
+ value2 = BigInt.asIntN(64, value2 * BigInt(base) + BigInt(digit2));
4542
4586
  }
4543
4587
  return value2;
4544
4588
  }
@@ -4864,7 +4908,7 @@ async function tokens(pattern, work) {
4864
4908
  let steps = 0;
4865
4909
  const tick = () => {
4866
4910
  if (--work.remaining < 0) work.exhausted();
4867
- if (++steps % 1024 === 0) return new Promise((resolve) => setImmediate(resolve)).then(() => work.signal.throwIfAborted());
4911
+ if (++steps % 1024 === 0) return yieldTurn(work.signal);
4868
4912
  return void 0;
4869
4913
  };
4870
4914
  for (let index = 0; index < characters.length; index++) {
@@ -4941,7 +4985,7 @@ async function matchTokens(patternTokens, value2, work) {
4941
4985
  while (position < characters.length) {
4942
4986
  if (--work.remaining < 0) work.exhausted();
4943
4987
  if (++steps % 1024 === 0) {
4944
- await new Promise((resolve) => setImmediate(resolve));
4988
+ await yieldTurn(work.signal);
4945
4989
  work.signal.throwIfAborted();
4946
4990
  }
4947
4991
  const token = patternTokens[tokenIndex];
@@ -4974,7 +5018,7 @@ async function charge(context, amount = 1) {
4974
5018
  const previous = context.work.remaining;
4975
5019
  context.work.remaining -= amount;
4976
5020
  if (previous >>> 10 !== context.work.remaining >>> 10) {
4977
- await new Promise((resolve) => setImmediate(resolve));
5021
+ await yieldTurn();
4978
5022
  context.signal.throwIfAborted();
4979
5023
  }
4980
5024
  }
@@ -5176,6 +5220,7 @@ var Lexer = class {
5176
5220
  this.documentLine = documentLine;
5177
5221
  this.partial = partial;
5178
5222
  if (depth > 64) throw new ShellSyntaxError("Syntax nesting exceeds 64", 0);
5223
+ for (let offset = source.indexOf("\n"); offset !== -1; offset = source.indexOf("\n", offset + 1)) this.newlineOffsets.push(offset);
5179
5224
  }
5180
5225
  source;
5181
5226
  depth;
@@ -5191,8 +5236,16 @@ var Lexer = class {
5191
5236
  conditional = false;
5192
5237
  conditionalPattern;
5193
5238
  documents = [];
5239
+ newlineOffsets = [];
5194
5240
  lineAt(position) {
5195
- return this.lineOffset + this.source.slice(0, position).split("\n").length;
5241
+ let low = 0;
5242
+ let high = this.newlineOffsets.length;
5243
+ while (low < high) {
5244
+ const middle = low + Math.floor((high - low) / 2);
5245
+ if (this.newlineOffsets[middle] < position) low = middle + 1;
5246
+ else high = middle;
5247
+ }
5248
+ return this.lineOffset + low + 1;
5196
5249
  }
5197
5250
  error(message2, unclosedQuote) {
5198
5251
  throw new ShellSyntaxError(message2, this.position, 2, void 0, unclosedQuote);
@@ -6614,7 +6667,7 @@ var SplitWork = class {
6614
6667
  return this.work >= this.nextYield;
6615
6668
  }
6616
6669
  async pause() {
6617
- await new Promise((resolve) => setImmediate(resolve));
6670
+ await yieldTurn();
6618
6671
  this.signal.throwIfAborted();
6619
6672
  this.nextYield = this.work + 4096;
6620
6673
  }
@@ -7884,14 +7937,7 @@ var ArrayLedger = class _ArrayLedger {
7884
7937
  this.#checkpoint += units;
7885
7938
  if (this.#checkpoint >= 128) {
7886
7939
  this.#checkpoint %= 128;
7887
- return new Promise((resolve, reject) => setImmediate(() => {
7888
- try {
7889
- signal?.throwIfAborted();
7890
- resolve();
7891
- } catch (error) {
7892
- reject(error);
7893
- }
7894
- }));
7940
+ return yieldTurn(signal);
7895
7941
  }
7896
7942
  }
7897
7943
  };
@@ -8768,43 +8814,17 @@ async function publishPipelineStatus(state, statuses, signal, scope) {
8768
8814
  throwCleanupFailures(scope.failures);
8769
8815
  }
8770
8816
 
8771
- // packages/safe-bash/browser/regex.mjs
8772
- init_platform();
8773
- var EreTransportRoot = class {
8774
- constructor() {
8775
- throw new ConditionalUnsupported("[[ =~ ]] is not supported by the browser shell entry");
8776
- }
8777
- };
8778
-
8779
- // packages/safe-bash/src/commands/regex-execution/ere/transport/protocol.ts
8817
+ // packages/safe-bash/src/commands/regex-execution/ere/errors.ts
8780
8818
  init_platform();
8781
- var resources = Object.freeze(["patternBytes", "subjectBytes", "work", "states", "allocationUnits", "captureBytes", "captureSlots"]);
8782
- var cumulative = Object.freeze(["work", "states", "allocationUnits", "captureBytes", "captureSlots"]);
8783
- var EreTransportProfileLimitError = class extends Error {
8784
- constructor(resource, limit) {
8785
- super(`ERE transport profile limit exceeded: ${resource} (${limit})`);
8786
- this.resource = resource;
8787
- this.limit = limit;
8788
- this.name = "EreTransportProfileLimitError";
8789
- }
8790
- resource;
8791
- limit;
8792
- status = 3;
8793
- };
8794
- var EreTransportSemanticError = class extends Error {
8795
- constructor(category, offset) {
8796
- super(category === "syntax" ? "invalid ERE" : "unsupported ERE profile");
8797
- this.category = category;
8819
+ var EreSyntaxError = class extends Error {
8820
+ constructor(message2, offset) {
8821
+ super(`invalid ERE at ${offset}: ${message2}`);
8798
8822
  this.offset = offset;
8799
- this.name = "EreTransportSemanticError";
8823
+ this.name = "EreSyntaxError";
8800
8824
  }
8801
- category;
8802
8825
  offset;
8803
8826
  status = 2;
8804
8827
  };
8805
-
8806
- // packages/safe-bash/src/commands/regex-execution/ere/errors.ts
8807
- init_platform();
8808
8828
  var EreUnsupportedError = class extends Error {
8809
8829
  constructor(message2, offset) {
8810
8830
  super(`unsupported ERE profile at ${offset}: ${message2}`);
@@ -8825,6 +8845,564 @@ var EreProfileLimitError = class extends Error {
8825
8845
  limit;
8826
8846
  status = 3;
8827
8847
  };
8848
+ var EreUsageUnknownError = class extends Error {
8849
+ status = 3;
8850
+ constructor(reason) {
8851
+ super("ERE invocation usage is unknown; further work is refused", { cause: reason });
8852
+ this.name = "EreUsageUnknownError";
8853
+ }
8854
+ };
8855
+
8856
+ // packages/safe-bash/src/commands/regex-execution/ere/limits.ts
8857
+ init_platform();
8858
+ var resources = Object.freeze([
8859
+ "patternBytes",
8860
+ "subjectBytes",
8861
+ "work",
8862
+ "states",
8863
+ "allocationUnits",
8864
+ "captureBytes",
8865
+ "captureSlots"
8866
+ ]);
8867
+ function integer5(value2) {
8868
+ if (!Number.isSafeInteger(value2) || value2 < 0) throw new TypeError("ERE bounds must be nonnegative safe integers");
8869
+ }
8870
+ function multiply(value2, factor, ceiling) {
8871
+ return value2 > Math.floor(ceiling / factor) ? ceiling : value2 * factor;
8872
+ }
8873
+ function deriveEreLimits(bounds) {
8874
+ integer5(bounds.maxExpansionBytes);
8875
+ integer5(bounds.maxExpansionFields);
8876
+ const bytes = bounds.maxExpansionBytes;
8877
+ const fields = bounds.maxExpansionFields;
8878
+ const byteUnits = multiply(bytes, 8, 4e6);
8879
+ const fieldUnits = multiply(fields, 128, 4e6);
8880
+ return Object.freeze({
8881
+ patternBytes: Math.min(bytes, 65536),
8882
+ subjectBytes: Math.min(bytes, 1048576),
8883
+ work: multiply(bytes, 32, 5e7),
8884
+ states: multiply(fields, 8, 65536),
8885
+ allocationUnits: byteUnits >= 4e6 - fieldUnits ? 4e6 : byteUnits + fieldUnits,
8886
+ captureBytes: bytes,
8887
+ captureSlots: fields
8888
+ });
8889
+ }
8890
+ var EreLedger = class {
8891
+ limits;
8892
+ #usage = {
8893
+ patternBytes: 0,
8894
+ subjectBytes: 0,
8895
+ work: 0,
8896
+ states: 0,
8897
+ allocationUnits: 0,
8898
+ captureBytes: 0,
8899
+ captureSlots: 0
8900
+ };
8901
+ #poison;
8902
+ #lastYield = 0;
8903
+ constructor(bounds, lowering = {}) {
8904
+ const limits = { ...deriveEreLimits(bounds) };
8905
+ for (const resource of Object.keys(lowering)) {
8906
+ if (!resources.includes(resource)) throw new TypeError("unknown ERE limit");
8907
+ const key = resource;
8908
+ const value2 = lowering[key];
8909
+ if (value2 === void 0) throw new TypeError("undefined ERE limit");
8910
+ integer5(value2);
8911
+ if (value2 > limits[key]) throw new RangeError("ERE limits may only be lowered");
8912
+ limits[key] = value2;
8913
+ }
8914
+ this.limits = Object.freeze(limits);
8915
+ }
8916
+ get usage() {
8917
+ return Object.freeze({ ...this.#usage });
8918
+ }
8919
+ check(signal) {
8920
+ if (signal?.aborted) throw signal.reason;
8921
+ if (this.#poison) throw this.#poison;
8922
+ }
8923
+ charge(resource, amount, signal) {
8924
+ this.check(signal);
8925
+ integer5(amount);
8926
+ if (amount > this.limits[resource] - this.#usage[resource]) {
8927
+ throw new EreProfileLimitError(resource, this.limits[resource]);
8928
+ }
8929
+ this.#usage[resource] += amount;
8930
+ }
8931
+ admitInput(resource, length, signal) {
8932
+ this.check(signal);
8933
+ integer5(length);
8934
+ if (length > this.limits[resource]) throw new EreProfileLimitError(resource, this.limits[resource]);
8935
+ this.#usage[resource] = Math.max(this.#usage[resource], length);
8936
+ }
8937
+ async checkpoint(signal) {
8938
+ this.check(signal);
8939
+ if (this.#usage.work - this.#lastYield >= 256) {
8940
+ this.#lastYield = this.#usage.work;
8941
+ await yieldTurn(signal);
8942
+ this.check(signal);
8943
+ }
8944
+ }
8945
+ markUnknownUsage(reason) {
8946
+ this.#poison ??= new EreUsageUnknownError(reason);
8947
+ }
8948
+ };
8949
+
8950
+ // packages/safe-bash/src/commands/regex-execution/ere/syntax.ts
8951
+ init_platform();
8952
+ var programs = /* @__PURE__ */ new WeakMap();
8953
+ var special = "\\.^$[]()|*+?{}";
8954
+ var classes = /* @__PURE__ */ new Set(["alnum", "alpha", "blank", "cntrl", "digit", "graph", "lower", "print", "punct", "space", "upper", "xdigit"]);
8955
+ function classMember(name2, code) {
8956
+ const upper = code >= 65 && code <= 90;
8957
+ const lower = code >= 97 && code <= 122;
8958
+ const digit2 = code >= 48 && code <= 57;
8959
+ switch (name2) {
8960
+ case "alnum":
8961
+ return upper || lower || digit2;
8962
+ case "alpha":
8963
+ return upper || lower;
8964
+ case "blank":
8965
+ return code === 9 || code === 32;
8966
+ case "cntrl":
8967
+ return code < 32 || code === 127;
8968
+ case "digit":
8969
+ return digit2;
8970
+ case "graph":
8971
+ return code >= 33 && code <= 126;
8972
+ case "lower":
8973
+ return lower;
8974
+ case "print":
8975
+ return code >= 32 && code <= 126;
8976
+ case "punct":
8977
+ return code >= 33 && code <= 126 && !upper && !lower && !digit2;
8978
+ case "space":
8979
+ return code === 32 || code >= 9 && code <= 13;
8980
+ case "upper":
8981
+ return upper;
8982
+ case "xdigit":
8983
+ return digit2 || code >= 65 && code <= 70 || code >= 97 && code <= 102;
8984
+ default:
8985
+ return false;
8986
+ }
8987
+ }
8988
+ async function admitAscii(text, ledger, signal) {
8989
+ for (let offset = 0; offset < text.length; offset++) {
8990
+ ledger.charge("work", 1, signal);
8991
+ const code = text.charCodeAt(offset);
8992
+ if (code === 0 || code > 127) throw new EreUnsupportedError("only non-NUL ASCII in the C/POSIX profile", offset);
8993
+ await ledger.checkpoint(signal);
8994
+ }
8995
+ }
8996
+ async function flatten(input2, ledger, signal) {
8997
+ if (typeof input2 === "string") {
8998
+ ledger.admitInput("patternBytes", input2.length, signal);
8999
+ await admitAscii(input2, ledger, signal);
9000
+ return { pattern: input2, quoted: null };
9001
+ }
9002
+ let size = 0;
9003
+ for (const fragment of input2) {
9004
+ if (typeof fragment.text !== "string" || typeof fragment.literal !== "boolean") throw new TypeError("invalid ERE fragment");
9005
+ ledger.charge("work", 1, signal);
9006
+ await ledger.checkpoint(signal);
9007
+ ledger.admitInput("patternBytes", fragment.text.length, signal);
9008
+ await admitAscii(fragment.text, ledger, signal);
9009
+ if (fragment.text.length > ledger.limits.patternBytes - size) ledger.admitInput("patternBytes", ledger.limits.patternBytes + 1, signal);
9010
+ size += fragment.text.length;
9011
+ ledger.admitInput("patternBytes", size, signal);
9012
+ }
9013
+ ledger.charge("allocationUnits", size * 2 + input2.length + 2, signal);
9014
+ const output2 = [];
9015
+ const quoted = [];
9016
+ for (const fragment of input2) {
9017
+ for (let offset = 0; offset < fragment.text.length; offset++) {
9018
+ ledger.charge("work", 1, signal);
9019
+ await ledger.checkpoint(signal);
9020
+ quoted.push(fragment.literal);
9021
+ }
9022
+ output2.push(fragment.text);
9023
+ await ledger.checkpoint(signal);
9024
+ }
9025
+ return { pattern: output2.join(""), quoted: Object.freeze(quoted) };
9026
+ }
9027
+ var Parser2 = class {
9028
+ constructor(pattern, quoted, ledger, signal) {
9029
+ this.pattern = pattern;
9030
+ this.quoted = quoted;
9031
+ this.ledger = ledger;
9032
+ this.signal = signal;
9033
+ }
9034
+ pattern;
9035
+ quoted;
9036
+ ledger;
9037
+ signal;
9038
+ offset = 0;
9039
+ groups = 0;
9040
+ nodes = 0;
9041
+ at(character, offset = this.offset) {
9042
+ return !this.quoted?.[offset] && this.pattern[offset] === character;
9043
+ }
9044
+ node(create) {
9045
+ if (this.nodes >= 4096) throw new EreUnsupportedError("4096-node grammar ceiling", this.offset);
9046
+ this.ledger.charge("allocationUnits", 8, this.signal);
9047
+ this.nodes++;
9048
+ return Object.freeze(create());
9049
+ }
9050
+ async expression(depth) {
9051
+ if (depth > 64) throw new EreUnsupportedError("64-level grammar ceiling", this.offset);
9052
+ this.ledger.charge("allocationUnits", 1, this.signal);
9053
+ const alternatives = [];
9054
+ while (true) {
9055
+ const child = await this.sequence(depth);
9056
+ this.ledger.charge("allocationUnits", 1, this.signal);
9057
+ alternatives.push(child);
9058
+ if (!this.at("|")) break;
9059
+ this.offset++;
9060
+ }
9061
+ if (alternatives.length === 1) return alternatives[0];
9062
+ this.ledger.charge("work", alternatives.length * 2, this.signal);
9063
+ await this.ledger.checkpoint(this.signal);
9064
+ return this.node(() => ({ kind: "alternative", children: Object.freeze(alternatives), nullable: alternatives.some((value2) => value2.nullable), captured: alternatives.some((value2) => value2.captured) }));
9065
+ }
9066
+ async sequence(depth) {
9067
+ this.ledger.charge("allocationUnits", 1, this.signal);
9068
+ const children = [];
9069
+ while (this.offset < this.pattern.length && !this.at("|") && !this.at(")")) {
9070
+ this.ledger.charge("work", 1, this.signal);
9071
+ await this.ledger.checkpoint(this.signal);
9072
+ let child = await this.atom(depth);
9073
+ const operator = this.quoted?.[this.offset] ? void 0 : this.pattern[this.offset];
9074
+ if (operator === "*" || operator === "+" || operator === "?" || operator === "{") {
9075
+ const begin = this.offset++;
9076
+ let min = operator === "+" ? 1 : 0;
9077
+ let max = operator === "?" ? 1 : Infinity;
9078
+ if (operator === "{") {
9079
+ min = await this.count();
9080
+ max = min;
9081
+ if (this.at(",")) {
9082
+ this.offset++;
9083
+ max = this.at("}") ? Infinity : await this.count();
9084
+ }
9085
+ if (!this.at("}") || max < min) throw new EreSyntaxError("invalid interval", begin);
9086
+ this.offset++;
9087
+ }
9088
+ if (child.kind === "start" || child.kind === "end") throw new EreUnsupportedError("repeated anchor", begin);
9089
+ if (child.nullable && child.captured && max > 1) throw new EreUnsupportedError("nullable captured repetition", begin);
9090
+ const repeated = child;
9091
+ child = this.node(() => ({ kind: "repeat", child: repeated, min, max, nullable: min === 0 || repeated.nullable, captured: repeated.captured }));
9092
+ const next = this.quoted?.[this.offset] ? void 0 : this.pattern[this.offset];
9093
+ if (next === "*" || next === "+" || next === "?" || next === "{") throw new EreUnsupportedError("stacked repetition", this.offset);
9094
+ }
9095
+ this.ledger.charge("allocationUnits", 1, this.signal);
9096
+ children.push(child);
9097
+ }
9098
+ if (children.length === 0) return this.node(() => ({ kind: "empty", nullable: true, captured: false }));
9099
+ if (children.length === 1) return children[0];
9100
+ this.ledger.charge("work", children.length * 2, this.signal);
9101
+ await this.ledger.checkpoint(this.signal);
9102
+ return this.node(() => ({ kind: "sequence", children: Object.freeze(children), nullable: children.every((value2) => value2.nullable), captured: children.some((value2) => value2.captured) }));
9103
+ }
9104
+ async count() {
9105
+ const begin = this.offset;
9106
+ let value2 = 0;
9107
+ while (!this.quoted?.[this.offset] && this.pattern[this.offset] !== void 0 && this.pattern[this.offset] >= "0" && this.pattern[this.offset] <= "9") {
9108
+ this.ledger.charge("work", 1, this.signal);
9109
+ await this.ledger.checkpoint(this.signal);
9110
+ value2 = value2 * 10 + this.pattern.charCodeAt(this.offset++) - 48;
9111
+ if (value2 > 255) throw new EreUnsupportedError("interval counts exceed 255", begin);
9112
+ }
9113
+ if (begin === this.offset) throw new EreSyntaxError("missing interval count", begin);
9114
+ return value2;
9115
+ }
9116
+ async atom(depth) {
9117
+ const begin = this.offset;
9118
+ const character = this.pattern[this.offset++];
9119
+ if (this.quoted?.[begin]) return this.node(() => ({ kind: "literal", code: character.charCodeAt(0), nullable: false, captured: false }));
9120
+ if (character === "(") {
9121
+ if (this.at("?")) throw new EreUnsupportedError("extended group syntax", begin);
9122
+ if (this.groups >= 32) throw new EreUnsupportedError("32-group grammar ceiling", begin);
9123
+ const index = ++this.groups;
9124
+ const child = await this.expression(depth + 1);
9125
+ if (!this.at(")")) throw new EreSyntaxError("unclosed group", begin);
9126
+ this.offset++;
9127
+ return this.node(() => ({ kind: "group", index, child, nullable: child.nullable, captured: true }));
9128
+ }
9129
+ if (character === "[") return this.set(begin);
9130
+ if (character === "\\") {
9131
+ const escaped = this.pattern[this.offset++];
9132
+ if (escaped === void 0) throw new EreSyntaxError("trailing escape", begin);
9133
+ if (!special.includes(escaped)) throw new EreUnsupportedError("backreference or escape extension", begin);
9134
+ return this.node(() => ({ kind: "literal", code: escaped.charCodeAt(0), nullable: false, captured: false }));
9135
+ }
9136
+ if (character === "*" || character === "+" || character === "?" || character === "{") throw new EreSyntaxError("repetition without operand", begin);
9137
+ if (character === ".") return this.node(() => ({ kind: "dot", nullable: false, captured: false }));
9138
+ if (character === "^") return this.node(() => ({ kind: "start", nullable: true, captured: false }));
9139
+ if (character === "$") return this.node(() => ({ kind: "end", nullable: true, captured: false }));
9140
+ return this.node(() => ({ kind: "literal", code: character.charCodeAt(0), nullable: false, captured: false }));
9141
+ }
9142
+ async set(begin) {
9143
+ this.ledger.charge("allocationUnits", 128, this.signal);
9144
+ const members = new Array(128).fill(false);
9145
+ const negate = this.at("^");
9146
+ if (negate) this.offset++;
9147
+ let first = true;
9148
+ while (this.offset < this.pattern.length) {
9149
+ this.ledger.charge("work", 1, this.signal);
9150
+ await this.ledger.checkpoint(this.signal);
9151
+ if (this.at("]") && !first) {
9152
+ this.offset++;
9153
+ if (negate) for (let code = 1; code < 128; code++) {
9154
+ this.ledger.charge("work", 1, this.signal);
9155
+ await this.ledger.checkpoint(this.signal);
9156
+ members[code] = !members[code];
9157
+ }
9158
+ return this.node(() => ({ kind: "set", members: Object.freeze(members), nullable: false, captured: false }));
9159
+ }
9160
+ first = false;
9161
+ if (this.at("[") && (this.at(".", this.offset + 1) || this.at("=", this.offset + 1))) {
9162
+ throw new EreUnsupportedError("collating or equivalence element", this.offset);
9163
+ }
9164
+ if (this.at("[") && this.at(":", this.offset + 1)) {
9165
+ const classBegin = this.offset;
9166
+ this.offset += 2;
9167
+ let name2 = "";
9168
+ while (this.offset < this.pattern.length && !this.at(":")) {
9169
+ this.ledger.charge("work", 1, this.signal);
9170
+ await this.ledger.checkpoint(this.signal);
9171
+ if (name2.length >= 6) throw new EreSyntaxError("unknown character class", classBegin);
9172
+ name2 += this.pattern[this.offset++];
9173
+ }
9174
+ if (!this.at(":") || !this.at("]", this.offset + 1) || !classes.has(name2)) throw new EreSyntaxError("unknown character class", classBegin);
9175
+ this.offset += 2;
9176
+ for (let code = 1; code < 128; code++) {
9177
+ this.ledger.charge("work", 1, this.signal);
9178
+ await this.ledger.checkpoint(this.signal);
9179
+ if (classMember(name2, code)) members[code] = true;
9180
+ }
9181
+ if (this.at("-") && !this.at("]", this.offset + 1)) throw new EreSyntaxError("class cannot be a range endpoint", this.offset);
9182
+ } else {
9183
+ const lower = this.pattern.charCodeAt(this.offset++);
9184
+ if (this.at("-") && !this.at("]", this.offset + 1) && this.pattern[this.offset + 1] !== void 0) {
9185
+ this.offset++;
9186
+ if (this.at("[")) throw new EreSyntaxError("nonliteral range endpoint", this.offset);
9187
+ const upper = this.pattern.charCodeAt(this.offset++);
9188
+ if (lower > upper) throw new EreSyntaxError("descending range", this.offset - 3);
9189
+ for (let code = lower; code <= upper; code++) {
9190
+ this.ledger.charge("work", 1, this.signal);
9191
+ await this.ledger.checkpoint(this.signal);
9192
+ members[code] = true;
9193
+ }
9194
+ } else members[lower] = true;
9195
+ }
9196
+ }
9197
+ throw new EreSyntaxError("unclosed bracket expression", begin);
9198
+ }
9199
+ };
9200
+ async function compileEre(input2, ledger, signal) {
9201
+ ledger.check(signal);
9202
+ const { pattern, quoted } = await flatten(input2, ledger, signal);
9203
+ const parser = new Parser2(pattern, quoted, ledger, signal);
9204
+ const root = await parser.expression(0);
9205
+ if (parser.offset !== pattern.length) throw new EreSyntaxError("unmatched closing group", parser.offset);
9206
+ ledger.charge("allocationUnits", 3, signal);
9207
+ const program = Object.freeze({ pattern, groups: parser.groups });
9208
+ programs.set(program, { root, ledger });
9209
+ return program;
9210
+ }
9211
+ function resolveEreProgram(program, ledger) {
9212
+ const entry = programs.get(program);
9213
+ if (!entry || entry.ledger !== ledger) throw new TypeError("ERE program is not bound to this invocation ledger");
9214
+ return entry.root;
9215
+ }
9216
+
9217
+ // packages/safe-bash/src/commands/regex-execution/ere/matcher.ts
9218
+ init_platform();
9219
+ function spanOrder(left, right) {
9220
+ if (left === null) return right === null ? 0 : -1;
9221
+ if (right === null) return 1;
9222
+ const length = left.end - left.start - (right.end - right.start);
9223
+ return length === 0 ? right.start - left.start : length;
9224
+ }
9225
+ async function historyOrder(left, right, ledger, signal) {
9226
+ const leftCount = left?.count ?? 0;
9227
+ const rightCount = right?.count ?? 0;
9228
+ for (let ordinal = 1; ordinal <= Math.min(leftCount, rightCount); ordinal++) {
9229
+ ledger.charge("work", 1, signal);
9230
+ await ledger.checkpoint(signal);
9231
+ let leftEntry = left;
9232
+ let rightEntry = right;
9233
+ for (let remaining = leftCount; remaining > ordinal; remaining--) {
9234
+ ledger.charge("work", 1, signal);
9235
+ await ledger.checkpoint(signal);
9236
+ leftEntry = leftEntry.previous;
9237
+ }
9238
+ for (let remaining = rightCount; remaining > ordinal; remaining--) {
9239
+ ledger.charge("work", 1, signal);
9240
+ await ledger.checkpoint(signal);
9241
+ rightEntry = rightEntry.previous;
9242
+ }
9243
+ const compared = spanOrder(leftEntry.span, rightEntry.span);
9244
+ if (compared !== 0) return compared;
9245
+ }
9246
+ return leftCount - rightCount;
9247
+ }
9248
+ async function preferred(candidate, incumbent, ledger, signal) {
9249
+ if (candidate.position !== incumbent.position) return candidate.position > incumbent.position;
9250
+ for (let group = 1; group < candidate.captures.length; group++) {
9251
+ ledger.charge("work", 1, signal);
9252
+ const compared = await historyOrder(candidate.histories[group], incumbent.histories[group], ledger, signal);
9253
+ if (compared !== 0) return compared > 0;
9254
+ }
9255
+ return false;
9256
+ }
9257
+ async function resetDescendants(node, previous, ledger, signal) {
9258
+ ledger.charge("allocationUnits", previous.length + 3, signal);
9259
+ const captures = previous.slice();
9260
+ const pending = [node];
9261
+ while (pending.length > 0) {
9262
+ ledger.charge("work", 1, signal);
9263
+ await ledger.checkpoint(signal);
9264
+ const current = pending.pop();
9265
+ if (current.kind === "group") captures[current.index] = null;
9266
+ if (current.kind === "group" || current.kind === "repeat") {
9267
+ if (current.child.captured) {
9268
+ ledger.charge("allocationUnits", 1, signal);
9269
+ pending.push(current.child);
9270
+ }
9271
+ } else if (current.kind === "sequence" || current.kind === "alternative") {
9272
+ for (const child of current.children) {
9273
+ ledger.charge("work", 1, signal);
9274
+ await ledger.checkpoint(signal);
9275
+ if (child.captured) {
9276
+ ledger.charge("allocationUnits", 1, signal);
9277
+ pending.push(child);
9278
+ }
9279
+ }
9280
+ }
9281
+ }
9282
+ return captures;
9283
+ }
9284
+ async function matchEre(program, subject, ledger, signal) {
9285
+ ledger.check(signal);
9286
+ const root = resolveEreProgram(program, ledger);
9287
+ ledger.admitInput("subjectBytes", subject.length, signal);
9288
+ await admitAscii(subject, ledger, signal);
9289
+ const width = program.groups + 1;
9290
+ ledger.charge("allocationUnits", width * 2 + 1, signal);
9291
+ const emptyCaptures = Object.freeze(new Array(width).fill(null));
9292
+ const emptyHistories = Object.freeze(new Array(width).fill(null));
9293
+ const pending = [];
9294
+ const task = (create) => {
9295
+ ledger.charge("allocationUnits", 5, signal);
9296
+ return create();
9297
+ };
9298
+ const push = (position, next, captures, histories) => {
9299
+ ledger.charge("states", 1, signal);
9300
+ ledger.charge("allocationUnits", 5, signal);
9301
+ pending.push({ position, task: next, captures, histories });
9302
+ };
9303
+ for (let start = 0; start <= subject.length; start++) {
9304
+ push(start, task(() => ({ kind: "node", node: root, next: null })), emptyCaptures, emptyHistories);
9305
+ let best;
9306
+ while (pending.length > 0) {
9307
+ ledger.charge("work", 1, signal);
9308
+ await ledger.checkpoint(signal);
9309
+ const state = pending.pop();
9310
+ const current = state.task;
9311
+ if (current === null) {
9312
+ if (!best || await preferred(state, best, ledger, signal)) best = state;
9313
+ continue;
9314
+ }
9315
+ if (current.kind === "close") {
9316
+ ledger.charge("allocationUnits", width * 2 + 6, signal);
9317
+ const span = Object.freeze({ start: current.start, end: state.position });
9318
+ const captures = state.captures.slice();
9319
+ captures[current.group] = span;
9320
+ const histories = state.histories.slice();
9321
+ const previous = histories[current.group];
9322
+ histories[current.group] = { span, previous, count: (previous?.count ?? 0) + 1 };
9323
+ push(state.position, current.next, captures, histories);
9324
+ continue;
9325
+ }
9326
+ if (current.kind === "repeat") {
9327
+ const { node: node2, count } = current;
9328
+ if (count >= node2.min) push(state.position, current.next, state.captures, state.histories);
9329
+ const noProgress = count > 0 && state.position === current.previous;
9330
+ if (count < node2.max && (!noProgress || count < node2.min)) {
9331
+ const repeat = task(() => ({ kind: "repeat", node: node2, count: count + 1, previous: state.position, next: current.next }));
9332
+ push(state.position, task(() => ({ kind: "node", node: node2.child, next: repeat })), state.captures, state.histories);
9333
+ }
9334
+ continue;
9335
+ }
9336
+ const node = current.node;
9337
+ switch (node.kind) {
9338
+ case "empty":
9339
+ push(state.position, current.next, state.captures, state.histories);
9340
+ break;
9341
+ case "start":
9342
+ if (state.position === 0) push(state.position, current.next, state.captures, state.histories);
9343
+ break;
9344
+ case "end":
9345
+ if (state.position === subject.length) push(state.position, current.next, state.captures, state.histories);
9346
+ break;
9347
+ case "dot":
9348
+ case "literal":
9349
+ case "set": {
9350
+ const code = subject.charCodeAt(state.position);
9351
+ if (state.position < subject.length && (node.kind === "dot" || node.kind === "literal" && node.code === code || node.kind === "set" && node.members[code])) {
9352
+ push(state.position + 1, current.next, state.captures, state.histories);
9353
+ }
9354
+ break;
9355
+ }
9356
+ case "sequence": {
9357
+ let next = current.next;
9358
+ for (let index = node.children.length - 1; index >= 0; index--) {
9359
+ ledger.charge("work", 1, signal);
9360
+ await ledger.checkpoint(signal);
9361
+ const following = next;
9362
+ next = task(() => ({ kind: "node", node: node.children[index], next: following }));
9363
+ }
9364
+ push(state.position, next, state.captures, state.histories);
9365
+ break;
9366
+ }
9367
+ case "alternative":
9368
+ for (let index = node.children.length - 1; index >= 0; index--) {
9369
+ ledger.charge("work", 1, signal);
9370
+ await ledger.checkpoint(signal);
9371
+ push(state.position, task(() => ({ kind: "node", node: node.children[index], next: current.next })), state.captures, state.histories);
9372
+ }
9373
+ break;
9374
+ case "group": {
9375
+ const captures = node.child.captured ? await resetDescendants(node.child, state.captures, ledger, signal) : state.captures;
9376
+ const close = task(() => ({ kind: "close", group: node.index, start: state.position, next: current.next }));
9377
+ push(state.position, task(() => ({ kind: "node", node: node.child, next: close })), captures, state.histories);
9378
+ break;
9379
+ }
9380
+ case "repeat":
9381
+ push(state.position, task(() => ({ kind: "repeat", node, count: 0, previous: -1, next: current.next })), state.captures, state.histories);
9382
+ break;
9383
+ }
9384
+ }
9385
+ if (best) {
9386
+ ledger.charge("captureSlots", width, signal);
9387
+ let bytes = best.position - start;
9388
+ for (let group = 1; group < width; group++) {
9389
+ ledger.charge("work", 1, signal);
9390
+ const span = best.captures[group];
9391
+ if (span) bytes += span.end - span.start;
9392
+ }
9393
+ ledger.charge("captureBytes", bytes, signal);
9394
+ ledger.charge("allocationUnits", width * 2 + bytes + 4, signal);
9395
+ const captures = best.captures.slice();
9396
+ captures[0] = Object.freeze({ start, end: best.position });
9397
+ const values = captures.map((span) => span === null ? "" : subject.slice(span.start, span.end));
9398
+ ledger.check(signal);
9399
+ return Object.freeze({ matched: true, captures: Object.freeze(captures), values: Object.freeze(values) });
9400
+ }
9401
+ }
9402
+ ledger.charge("allocationUnits", 3, signal);
9403
+ ledger.check(signal);
9404
+ return Object.freeze({ matched: false, captures: Object.freeze([]), values: Object.freeze([]) });
9405
+ }
8828
9406
 
8829
9407
  // packages/safe-bash/src/shell/runtime.ts
8830
9408
  var defaultLimits = {
@@ -8836,6 +9414,8 @@ var defaultLimits = {
8836
9414
  maxSourceBytes: 1024 * 1024,
8837
9415
  maxExpansionFields: 1e4,
8838
9416
  maxExpansionBytes: 16 * 1024 * 1024,
9417
+ maxWallClockMs: 3e4,
9418
+ maxCpuMs: 3e4,
8839
9419
  pipeHighWaterMark: 64 * 1024
8840
9420
  };
8841
9421
  var shellBuiltinNames = /* @__PURE__ */ new Set([
@@ -8915,6 +9495,8 @@ var Budget = class {
8915
9495
  this.limits = limits;
8916
9496
  this.signal = signal ? AbortSignal.any([signal, this.controller.signal]) : this.controller.signal;
8917
9497
  this.values = new ValueArena(limits.maxExpansionBytes, limits.maxExpansionFields, () => this.signal.throwIfAborted(), (limit) => this.fail(limit));
9498
+ this.#wallClockDeadline = Date.now() + limits.maxWallClockMs;
9499
+ this.#armWallClock();
8918
9500
  }
8919
9501
  limits;
8920
9502
  values;
@@ -8924,12 +9506,34 @@ var Budget = class {
8924
9506
  sourceBytes = 0;
8925
9507
  controller = new AbortController();
8926
9508
  signal;
9509
+ #wallClockTimer;
9510
+ #wallClockDeadline = 0;
9511
+ #cpuStarted = monotonicNow();
9512
+ #armWallClock() {
9513
+ const remaining = this.#wallClockDeadline - Date.now();
9514
+ if (remaining <= 0) {
9515
+ this.controller.abort(new ShellLimitError("maxWallClockMs"));
9516
+ return;
9517
+ }
9518
+ this.#wallClockTimer = setTimeout(() => this.#armWallClock(), Math.min(remaining, 2147483647));
9519
+ const timer = this.#wallClockTimer;
9520
+ timer.unref?.();
9521
+ }
9522
+ close() {
9523
+ if (this.#wallClockTimer !== void 0) clearTimeout(this.#wallClockTimer);
9524
+ this.#wallClockTimer = void 0;
9525
+ }
8927
9526
  fail(limit) {
8928
9527
  const error = new ShellLimitError(limit);
8929
9528
  this.controller.abort(error);
8930
9529
  throw error;
8931
9530
  }
9531
+ cpuCheckpoint() {
9532
+ this.signal.throwIfAborted();
9533
+ if (monotonicNow() - this.#cpuStarted > this.limits.maxCpuMs) this.fail("maxCpuMs");
9534
+ }
8932
9535
  tick() {
9536
+ this.cpuCheckpoint();
8933
9537
  this.signal.throwIfAborted();
8934
9538
  if (++this.commands > this.limits.maxCommands) this.fail("maxCommands");
8935
9539
  }
@@ -9328,7 +9932,7 @@ var CdLookup = class {
9328
9932
  amount -= step;
9329
9933
  if (this.spent % 128 === 0) {
9330
9934
  this.signal.throwIfAborted();
9331
- await interruptible(new Promise((resolve) => setImmediate(resolve)), this.signal);
9935
+ await yieldTurn(this.signal);
9332
9936
  this.signal.throwIfAborted();
9333
9937
  }
9334
9938
  }
@@ -9424,7 +10028,7 @@ var DirectoryStackWork = class {
9424
10028
  async flushWork() {
9425
10029
  this.signal.throwIfAborted();
9426
10030
  if (this.flushed === this.spent) return;
9427
- await interruptible(new Promise((resolve) => setImmediate(resolve)), this.signal);
10031
+ await yieldTurn(this.signal);
9428
10032
  this.signal.throwIfAborted();
9429
10033
  this.flushed = this.spent;
9430
10034
  }
@@ -9745,6 +10349,7 @@ var Runtime = class _Runtime {
9745
10349
  this.cancellationDepth = cancellationDepth;
9746
10350
  this.cancellationMaxDepth = cancellationMaxDepth;
9747
10351
  this.outcomeFrame = outcomeFrame;
10352
+ registerYieldCheckpoint(this.signal, () => this.budget.cpuCheckpoint());
9748
10353
  }
9749
10354
  fs;
9750
10355
  commands;
@@ -9760,19 +10365,6 @@ var Runtime = class _Runtime {
9760
10365
  cancellationDepth;
9761
10366
  cancellationMaxDepth;
9762
10367
  outcomeFrame;
9763
- static #ereRoots = /* @__PURE__ */ new WeakMap();
9764
- static registerEreRoot(budget, scope) {
9765
- const entry = { closed: false };
9766
- scope.register(async () => {
9767
- entry.closed = true;
9768
- try {
9769
- await entry.cleanup?.();
9770
- } finally {
9771
- _Runtime.#ereRoots.delete(budget);
9772
- }
9773
- });
9774
- _Runtime.#ereRoots.set(budget, entry);
9775
- }
9776
10368
  async ereDiagnostic(io, detail) {
9777
10369
  try {
9778
10370
  await this.diagnostic(io, detail);
@@ -9821,32 +10413,21 @@ var Runtime = class _Runtime {
9821
10413
  await textToken(operation, subject, this.signal);
9822
10414
  const name2 = "BASH_REMATCH";
9823
10415
  const watch = await store.watch(name2, operation, this.signal);
9824
- const entry = _Runtime.#ereRoots.get(this.budget);
9825
- if (!entry || entry.closed) throw new Error("ERE invocation owner is closed");
9826
- if (!entry.root) entry.root = new EreTransportRoot({ maxExpansionBytes: this.budget.limits.maxExpansionBytes, maxExpansionFields: this.budget.limits.maxExpansionFields }, (cleanup) => {
9827
- scope.assertOpen();
9828
- if (entry.closed) throw new Error("ERE invocation owner is closed");
9829
- entry.cleanup = cleanup;
10416
+ const ledger = new EreLedger({
10417
+ maxExpansionBytes: this.budget.limits.maxExpansionBytes,
10418
+ maxExpansionFields: this.budget.limits.maxExpansionFields
9830
10419
  });
9831
- const session = entry.root.openSession((cleanup) => scope.register(cleanup));
9832
10420
  let result;
9833
- let sessionEscaping = false;
9834
10421
  try {
9835
- result = await session.execute({ pattern: fragments, subject }, this.signal);
10422
+ const program = await compileEre(fragments, ledger, this.signal);
10423
+ result = await matchEre(program, subject, ledger, this.signal);
9836
10424
  } catch (error) {
9837
- sessionEscaping = true;
9838
10425
  this.signal.throwIfAborted();
9839
- if (error instanceof EreTransportSemanticError || error instanceof EreUnsupportedError) {
10426
+ if (error instanceof EreSyntaxError || error instanceof EreUnsupportedError) {
9840
10427
  await this.ereDiagnostic(io, `[[ ${error.message}`);
9841
- sessionEscaping = false;
9842
10428
  return 2;
9843
10429
  }
9844
10430
  throw error;
9845
- } finally {
9846
- await session.close().catch((error) => {
9847
- if (sessionEscaping) scope.failures.push(error);
9848
- else throw new NounsetDiagnosticFailure(error);
9849
- });
9850
10431
  }
9851
10432
  const status = result.matched ? 0 : 1;
9852
10433
  this.signal.throwIfAborted();
@@ -9861,8 +10442,8 @@ var Runtime = class _Runtime {
9861
10442
  const tickets = operation.reserve({ generation: true, version: true, epoch: true, work: 8 });
9862
10443
  const prepared = await store.prepareName(name2, operation, this.signal);
9863
10444
  staged = IndexedBinding.create(store.owner);
9864
- if (result.matched) for (let index = 0; index < result.spans.length; index++) {
9865
- const span = result.spans[index];
10445
+ if (result.matched) for (let index = 0; index < result.captures.length; index++) {
10446
+ const span = result.captures[index];
9866
10447
  const size = span === null ? 0 : span.end - span.start;
9867
10448
  operation.reserve({ payload: size, metadata: 32, work: size + 2 });
9868
10449
  const value2 = span === null ? "" : subject.slice(span.start, span.end);
@@ -10024,6 +10605,7 @@ var Runtime = class _Runtime {
10024
10605
  writeVariable(state, name2, value2, origin = "assignment") {
10025
10606
  if (arrayStore(state)?.get(name2)) throw new ArrayFailure(origin === "arithmetic" ? "indexed arithmetic is unsupported" : "indexed write requires prepared publication");
10026
10607
  if (state.readonlyVariables?.has(name2)) throw new Error(`${name2}: readonly variable`);
10608
+ if (shellValueByteLength(value2) > this.budget.limits.maxExpansionBytes) this.budget.fail("maxExpansionBytes");
10027
10609
  if (name2 === "OPTIND" && state.getopts?.integer && origin !== "arithmetic") {
10028
10610
  try {
10029
10611
  value2 = String(evaluateArithmetic(prepareArithmetic(shellValueText(value2) || "0"), this.arithmeticVariables(state)));
@@ -10533,7 +11115,7 @@ var Runtime = class _Runtime {
10533
11115
  completed.add(index);
10534
11116
  if (incoming) {
10535
11117
  const upstream = index - 1;
10536
- const close = setImmediate(() => {
11118
+ const close = scheduleTurn(() => {
10537
11119
  closing.delete(close);
10538
11120
  if (written.has(upstream) && !completed.has(upstream)) controllers[upstream].abort(new PipelineClosed());
10539
11121
  });
@@ -10560,7 +11142,7 @@ var Runtime = class _Runtime {
10560
11142
  try {
10561
11143
  statuses = await interruptible(Promise.all(tasks), this.signal);
10562
11144
  } finally {
10563
- for (const close of closing) clearImmediate(close);
11145
+ for (const close of closing) cancelTurn(close);
10564
11146
  for (const [index, controller] of controllers.entries()) if (!completed.has(index) || written.has(index)) controller.abort(new PipelineClosed());
10565
11147
  await Promise.all(pipes.map((pipe) => pipe.abort()));
10566
11148
  }
@@ -10625,7 +11207,7 @@ var Runtime = class _Runtime {
10625
11207
  originalIO = { ...originalIO, diagnosticLine, substitutionDiagnosticLine: originalIO.substitutionDiagnosticLines?.get(command) ?? diagnosticLine };
10626
11208
  if (command.kind === "subshell") originalIO = isolateIO(originalIO);
10627
11209
  this.budget.tick();
10628
- if (this.budget.commands % 128 === 0) await interruptible(new Promise((resolve) => setImmediate(resolve)), this.signal);
11210
+ if (this.budget.commands % 128 === 0) await yieldTurn(this.signal);
10629
11211
  this.signal.throwIfAborted();
10630
11212
  const inputs = /* @__PURE__ */ new Set();
10631
11213
  const outputs = /* @__PURE__ */ new Set();
@@ -10672,7 +11254,7 @@ var Runtime = class _Runtime {
10672
11254
  diagnosticFailure = error;
10673
11255
  throw error;
10674
11256
  }
10675
- if (error instanceof EreProfileLimitError || error instanceof EreTransportProfileLimitError) {
11257
+ if (error instanceof EreProfileLimitError) {
10676
11258
  try {
10677
11259
  await this.diagnostic(io, `[[ ${error.message}`);
10678
11260
  } catch (reason) {
@@ -10741,7 +11323,7 @@ var Runtime = class _Runtime {
10741
11323
  for (const clause of command.clauses) {
10742
11324
  let matched = fallthrough;
10743
11325
  if (!matched) for (const word of clause.patterns) {
10744
- if (++patterns % 128 === 0) await interruptible(new Promise((resolve) => setImmediate(resolve)), this.signal);
11326
+ if (++patterns % 128 === 0) await yieldTurn(this.signal);
10745
11327
  const pattern = (await this.word(word, state, io, false, true)).join("");
10746
11328
  if (await matchesPattern(pattern, subject, work)) {
10747
11329
  matched = true;
@@ -10862,7 +11444,7 @@ var Runtime = class _Runtime {
10862
11444
  this.signal.throwIfAborted();
10863
11445
  for (const warning of warnings.splice(0)) await writeText(io.stderr, `shell: warning: ${warning}
10864
11446
  `);
10865
- if (++words % 128 === 0) await interruptible(new Promise((resolve) => setImmediate(resolve)), this.signal);
11447
+ if (++words % 128 === 0) await yieldTurn(this.signal);
10866
11448
  const part = (await this.word(word, state, io, false)).join("");
10867
11449
  size += import_buffer.Buffer.byteLength(part);
10868
11450
  if (size > this.budget.limits.maxExpansionBytes) this.budget.fail("maxExpansionBytes");
@@ -11056,7 +11638,7 @@ var Runtime = class _Runtime {
11056
11638
  }
11057
11639
  const wordValues = await this.valueWords(commandWords, state, originalIO, ["export", "local", "readonly"].includes(commandWords[declarationIndex]?.plain ?? ""));
11058
11640
  const words = wordValues.map(shellValueText);
11059
- const special = state.profile === "sh" && specialBuiltinNames.has(words[0] ?? "");
11641
+ const special2 = state.profile === "sh" && specialBuiltinNames.has(words[0] ?? "");
11060
11642
  const inlineInput = command.redirects.some((redirect) => redirect.document || redirect.operator === "<<<");
11061
11643
  const functionCommand = words.length > 0 && state.functions.has(words[0]);
11062
11644
  const isolatedInlineInput = inlineInput && words.length > 0 && !shellBuiltinNames.has(words[0]) && !functionCommand;
@@ -11084,7 +11666,7 @@ var Runtime = class _Runtime {
11084
11666
  if (assignment.append) value2 = concatShellValues([stateMonitor(state)?.values.get(assignment.name, state.variables[assignment.name] ?? "") ?? state.variables[assignment.name] ?? "", value2], io[valueScope]);
11085
11667
  if (state.readonlyVariables?.has(assignment.name)) {
11086
11668
  await this.diagnostic(io, `${assignment.name}: readonly variable`);
11087
- if (state.profile === "sh" || !words.length) throw new Flow("exit", state.profile === "sh" && (special || !words.length) ? 127 : 1);
11669
+ if (state.profile === "sh" || !words.length) throw new Flow("exit", state.profile === "sh" && (special2 || !words.length) ? 127 : 1);
11088
11670
  continue;
11089
11671
  }
11090
11672
  if (!previous.has(assignment.name)) {
@@ -11175,7 +11757,7 @@ var Runtime = class _Runtime {
11175
11757
  if (error instanceof Flow) throw error;
11176
11758
  this.signal.throwIfAborted();
11177
11759
  const original = error instanceof ExecutionFailure ? error.original : error;
11178
- if (special && !(original instanceof ShellLimitError) && !(original instanceof ExpansionFailure) && !(original instanceof Flow) && !(original instanceof ShellSyntaxError)) {
11760
+ if (special2 && !(original instanceof ShellLimitError) && !(original instanceof ExpansionFailure) && !(original instanceof Flow) && !(original instanceof ShellSyntaxError)) {
11179
11761
  throw new ExecutionFailure(new FatalCommandFailure(message(original), 1), error instanceof ExecutionFailure ? error.io : io, error instanceof ExecutionFailure ? error.diagnostic : void 0);
11180
11762
  }
11181
11763
  if (error instanceof ExecutionFailure) throw error;
@@ -11415,16 +11997,16 @@ var Runtime = class _Runtime {
11415
11997
  }
11416
11998
  if (selected?.kind === "builtin") {
11417
11999
  if (context2.command === "command" || context2.command === "builtin" || context2.command === "type") return { exitCode: await this.discoveryBuiltin(context2, state, io, assignments2) };
11418
- const special = state.profile === "sh" && !bypassFunctions && specialBuiltinNames.has(context2.command);
11419
- if (special) assignments2.clear();
11420
- if (context2.command === "." || context2.command === "source") return { exitCode: await this.sourceBuiltin(context2, state, { ...io, ...context2 }, special) };
11421
- if (context2.command === "eval") return { exitCode: await this.evalBuiltin(context2, state, { ...io, ...context2 }, special) };
12000
+ const special2 = state.profile === "sh" && !bypassFunctions && specialBuiltinNames.has(context2.command);
12001
+ if (special2) assignments2.clear();
12002
+ if (context2.command === "." || context2.command === "source") return { exitCode: await this.sourceBuiltin(context2, state, { ...io, ...context2 }, special2) };
12003
+ if (context2.command === "eval") return { exitCode: await this.evalBuiltin(context2, state, { ...io, ...context2 }, special2) };
11422
12004
  const builtinWork = this.builtin(context2, state, assignments2, (error, diagnostic2) => {
11423
12005
  builtinFailure = { error, diagnostic: diagnostic2 };
11424
12006
  }, bypassFunctions);
11425
12007
  const builtin = arrayStore(state) ? await interruptible(builtinWork, this.signal) : await builtinWork;
11426
12008
  if (builtin !== void 0) {
11427
- if (special && builtin !== 0 && context2.command !== "shift") throw new Flow("exit", builtin);
12009
+ if (special2 && builtin !== 0 && context2.command !== "shift") throw new Flow("exit", builtin);
11428
12010
  return { exitCode: builtin };
11429
12011
  }
11430
12012
  }
@@ -11736,7 +12318,7 @@ ${prefix2} line ${offset + line}: \`${source.split("\n")[line - 1] ?? ""}'
11736
12318
  let status = 0;
11737
12319
  let lines2 = 0;
11738
12320
  while (true) {
11739
- if (++lines2 % 32 === 0) await interruptible(new Promise((resolve) => setImmediate(resolve)), this.signal);
12321
+ if (++lines2 % 32 === 0) await yieldTurn(this.signal);
11740
12322
  this.signal.throwIfAborted();
11741
12323
  const bytes = await input2.sourceLine();
11742
12324
  const eof = bytes === void 0;
@@ -12056,13 +12638,13 @@ ${prefix2} line ${offset + line}: \`${source.split("\n")[line - 1] ?? ""}'
12056
12638
  return status2;
12057
12639
  }
12058
12640
  }
12059
- async evalBuiltin(context, state, io, special) {
12641
+ async evalBuiltin(context, state, io, special2) {
12060
12642
  const args = [...context.args];
12061
12643
  if (args[0] === "--") args.shift();
12062
12644
  else if (args[0]?.startsWith("-") && args[0] !== "-") {
12063
12645
  await this.diagnostic(io, `eval: -${args[0][1]}: invalid option`);
12064
12646
  await writeText(io.stderr, "eval: usage: eval [arg ...]\n");
12065
- if (special) throw new Flow("exit", 2);
12647
+ if (special2) throw new Flow("exit", 2);
12066
12648
  return 2;
12067
12649
  }
12068
12650
  if (!args.length) return 0;
@@ -12078,7 +12660,7 @@ ${prefix2} line ${offset + line}: \`${source.split("\n")[line - 1] ?? ""}'
12078
12660
  throw error;
12079
12661
  }
12080
12662
  try {
12081
- return await this.runCurrentText(source, state, { ...io, diagnosticOffset: (io.diagnosticLine ?? 1) - 1 }, special, `${io.scriptName ?? "shell"}: eval`);
12663
+ return await this.runCurrentText(source, state, { ...io, diagnosticOffset: (io.diagnosticLine ?? 1) - 1 }, special2, `${io.scriptName ?? "shell"}: eval`);
12082
12664
  } finally {
12083
12665
  const restore = () => {
12084
12666
  state.depth--;
@@ -12087,12 +12669,12 @@ ${prefix2} line ${offset + line}: \`${source.split("\n")[line - 1] ?? ""}'
12087
12669
  else restore();
12088
12670
  }
12089
12671
  }
12090
- async sourceBuiltin(context, state, io, special) {
12672
+ async sourceBuiltin(context, state, io, special2) {
12091
12673
  const args = [...context.args];
12092
12674
  if (args[0] === "--") args.shift();
12093
12675
  else if (args[0]?.startsWith("-") && args[0] !== "-") {
12094
12676
  await this.diagnostic(io, `${context.command}: ${args[0]}: unsupported option`);
12095
- if (special) throw new Flow("exit", 2);
12677
+ if (special2) throw new Flow("exit", 2);
12096
12678
  return 2;
12097
12679
  }
12098
12680
  const filename = args.shift();
@@ -12100,7 +12682,7 @@ ${prefix2} line ${offset + line}: \`${source.split("\n")[line - 1] ?? ""}'
12100
12682
  await this.diagnostic(io, `${context.command}: filename argument required`);
12101
12683
  await writeText(io.stderr, `${context.command}: usage: ${context.command} [-p path] filename [arguments]
12102
12684
  `);
12103
- if (special) throw new Flow("exit", 2);
12685
+ if (special2) throw new Flow("exit", 2);
12104
12686
  return 2;
12105
12687
  }
12106
12688
  if (state.depth >= this.budget.limits.maxSubstitutionDepth) this.budget.fail("maxSubstitutionDepth");
@@ -12146,7 +12728,7 @@ ${prefix2} line ${offset + line}: \`${source.split("\n")[line - 1] ?? ""}'
12146
12728
  if (error instanceof ShellLimitError) throw error;
12147
12729
  if (errorCode(error) === "EFBIG") this.budget.fail("maxSourceBytes");
12148
12730
  const diagnostic2 = error instanceof CommandFailure ? error.message : filesystemDiagnostic(error, target) ?? `${target}: ${message(error)}`;
12149
- if (special) throw new FatalCommandFailure(diagnostic2, 1);
12731
+ if (special2) throw new FatalCommandFailure(diagnostic2, 1);
12150
12732
  throw new CommandFailure(diagnostic2, error instanceof CommandFailure ? error.status : 1);
12151
12733
  }
12152
12734
  const positional = state.positional;
@@ -12175,7 +12757,7 @@ ${prefix2} line ${offset + line}: \`${source.split("\n")[line - 1] ?? ""}'
12175
12757
  throw error;
12176
12758
  }
12177
12759
  try {
12178
- return await this.runCurrentText(source, state, { ...io, scriptName: target, diagnosticOffset: 0, diagnosticLine: 1 }, special);
12760
+ return await this.runCurrentText(source, state, { ...io, scriptName: target, diagnosticOffset: 0, diagnosticLine: 1 }, special2);
12179
12761
  } catch (error) {
12180
12762
  if (error instanceof Flow && error.kind === "return") return error.status;
12181
12763
  throw error;
@@ -12317,7 +12899,7 @@ ${prefix2} line ${offset + line}: \`${source.split("\n")[line - 1] ?? ""}'
12317
12899
  };
12318
12900
  const checkpoint = async () => {
12319
12901
  this.signal.throwIfAborted();
12320
- await interruptible(new Promise((resolve) => setImmediate(resolve)), this.signal);
12902
+ await yieldTurn(this.signal);
12321
12903
  this.signal.throwIfAborted();
12322
12904
  };
12323
12905
  const { args } = context;
@@ -12362,7 +12944,7 @@ ${prefix2} line ${offset + line}: \`${source.split("\n")[line - 1] ?? ""}'
12362
12944
  };
12363
12945
  const checkpoint = async () => {
12364
12946
  this.signal.throwIfAborted();
12365
- await interruptible(new Promise((resolve) => setImmediate(resolve)), this.signal);
12947
+ await yieldTurn(this.signal);
12366
12948
  this.signal.throwIfAborted();
12367
12949
  };
12368
12950
  if (!Array.isArray(context.args)) throw new CommandFailure("getopts: argument array required", 2);
@@ -13395,7 +13977,7 @@ ${prefix2} line ${offset + line}: \`${source.split("\n")[line - 1] ?? ""}'
13395
13977
  const match = async (start, end) => {
13396
13978
  work.remaining -= end - start + 1;
13397
13979
  if (work.remaining < 0) work.exhausted();
13398
- if (++attempts % 256 === 0) await new Promise((resolve) => setImmediate(resolve));
13980
+ if (++attempts % 256 === 0) await yieldTurn(this.signal);
13399
13981
  this.signal.throwIfAborted();
13400
13982
  if (owner) await owner.ledger.checkpoint(this.signal, end - start + 1);
13401
13983
  return matches(slice(start, end));
@@ -13904,7 +14486,7 @@ var ShellInput = class _ShellInput {
13904
14486
  let length = 0;
13905
14487
  let pulls = 0;
13906
14488
  while (true) {
13907
- if (++pulls % 128 === 0) await interruptible(new Promise((resolve) => setImmediate(resolve)), this.signal);
14489
+ if (++pulls % 128 === 0) await yieldTurn(this.signal);
13908
14490
  const result = await this.#cursor.take(this.signal);
13909
14491
  if (result.done) {
13910
14492
  if (!length) return void 0;
@@ -13946,7 +14528,7 @@ var ShellInput = class _ShellInput {
13946
14528
  try {
13947
14529
  while (!terminated) {
13948
14530
  if (offset === chunk.length) {
13949
- if (++pulls % 128 === 0) await interruptible(new Promise((resolve) => setImmediate(resolve)), this.signal);
14531
+ if (++pulls % 128 === 0) await yieldTurn(this.signal);
13950
14532
  const result = await this.#cursor.take(this.signal);
13951
14533
  if (result.done) {
13952
14534
  break;
@@ -13962,7 +14544,7 @@ var ShellInput = class _ShellInput {
13962
14544
  }
13963
14545
  if (++length > this.budget.limits.maxOutputBytes) this.budget.fail("maxOutputBytes");
13964
14546
  if (length % 1024 === 0) {
13965
- await interruptible(new Promise((resolve) => setImmediate(resolve)), this.signal);
14547
+ await yieldTurn(this.signal);
13966
14548
  this.signal.throwIfAborted();
13967
14549
  }
13968
14550
  if (byte === 0) continue;
@@ -14155,6 +14737,7 @@ var Shell = class {
14155
14737
  #active = /* @__PURE__ */ new Set();
14156
14738
  constructor(options2) {
14157
14739
  if (!options2?.fs) throw new TypeError("Shell requires an explicit filesystem");
14740
+ warnIfHostProcessEnv(options2.env);
14158
14741
  resolveLimits(options2.limits);
14159
14742
  this.#options = { ...options2, cwd: resolvePath("/", options2.cwd ?? "/"), env: { ...options2.env }, limits: { ...options2.limits } };
14160
14743
  this.commands = options2.commands ?? new CommandRegistry();
@@ -14217,6 +14800,7 @@ var Shell = class {
14217
14800
  }
14218
14801
  async exec(source, options2 = {}) {
14219
14802
  if (this.#disposed) throw new Error("Shell is disposed");
14803
+ warnIfHostProcessEnv(options2.env);
14220
14804
  const budget = new Budget(resolveLimits(this.#options.limits, options2.limits), options2.signal);
14221
14805
  const scope = new InvocationScope(options2.signal);
14222
14806
  const cancellationState = new RuntimeCancellationState();
@@ -14227,7 +14811,6 @@ var Shell = class {
14227
14811
  controls: [{ role: "budget-control", signal: budget.controller.signal }]
14228
14812
  });
14229
14813
  try {
14230
- Runtime.registerEreRoot(budget, scope);
14231
14814
  owner.activate(boundary);
14232
14815
  } catch (error) {
14233
14816
  scope.failures.push(...boundary.close().failures);
@@ -14241,6 +14824,7 @@ var Shell = class {
14241
14824
  try {
14242
14825
  captured = await owner.capture(() => this.#execute(source, options2, scope, budget, boundary, cancellationState, owner));
14243
14826
  } finally {
14827
+ budget.close();
14244
14828
  await scope.close();
14245
14829
  }
14246
14830
  const selection = owner.finish(captured);
@@ -14413,6 +14997,22 @@ shell: -c: line ${line}: \`${source.split("\n")[line - 1] ?? ""}'
14413
14997
  }
14414
14998
  };
14415
14999
 
15000
+ // packages/safe-bash/src/shell/worker-limits.ts
15001
+ init_platform();
15002
+ var cloudflareWorkerLimits = Object.freeze({
15003
+ maxInputBytes: 4 * 1024 * 1024,
15004
+ maxOutputBytes: 4 * 1024 * 1024,
15005
+ maxCommands: 1e3,
15006
+ maxLoopIterations: 1e3,
15007
+ maxSubstitutionDepth: 16,
15008
+ maxSourceBytes: 256 * 1024,
15009
+ maxExpansionFields: 1e3,
15010
+ maxExpansionBytes: 4 * 1024 * 1024,
15011
+ maxWallClockMs: 1e4,
15012
+ maxCpuMs: 1e4,
15013
+ pipeHighWaterMark: 16 * 1024
15014
+ });
15015
+
14416
15016
  // packages/safe-bash/src/fs/memory/index.ts
14417
15017
  init_platform();
14418
15018
  import { createMemoryFileSystem, MemoryFileSystem } from "@poe-platform/safe-fs/core";
@@ -14429,6 +15029,153 @@ import { createMountFileSystem, MountFileSystem } from "@poe-platform/safe-fs/co
14429
15029
  init_platform();
14430
15030
  import { createOverlayFileSystem, OverlayFileSystem } from "@poe-platform/safe-fs/core";
14431
15031
 
15032
+ // packages/safe-bash/src/commands/network/fetch-transport.ts
15033
+ init_platform();
15034
+
15035
+ // packages/safe-bash/src/commands/network/types.ts
15036
+ init_platform();
15037
+ var defaultNetworkLimits = Object.freeze({
15038
+ maxUploadBytes: 64 * 1024 * 1024,
15039
+ maxDownloadBytes: 64 * 1024 * 1024,
15040
+ maxBufferBytes: 8 * 1024 * 1024,
15041
+ maxHeaderBytes: 64 * 1024,
15042
+ maxRedirects: 10,
15043
+ maxRetries: 5,
15044
+ maxUrls: 32,
15045
+ maxTimeMs: 12e4
15046
+ });
15047
+ var cloudflareWorkerNetworkLimits = Object.freeze({
15048
+ maxUploadBytes: 4 * 1024 * 1024,
15049
+ maxDownloadBytes: 4 * 1024 * 1024,
15050
+ maxBufferBytes: 1024 * 1024,
15051
+ maxHeaderBytes: 32 * 1024,
15052
+ maxRedirects: 2,
15053
+ maxRetries: 1,
15054
+ maxUrls: 8,
15055
+ maxTimeMs: 1e4
15056
+ });
15057
+ var CurlError = class extends Error {
15058
+ constructor(exitCode, message2) {
15059
+ super(message2);
15060
+ this.exitCode = exitCode;
15061
+ this.name = "CurlError";
15062
+ }
15063
+ exitCode;
15064
+ };
15065
+
15066
+ // packages/safe-bash/src/commands/network/fetch-transport.ts
15067
+ function requestBody(source, signal) {
15068
+ if (!source) return void 0;
15069
+ const iterator = source[Symbol.asyncIterator]();
15070
+ return new ReadableStream({
15071
+ async pull(controller) {
15072
+ try {
15073
+ signal.throwIfAborted();
15074
+ const next = await iterator.next();
15075
+ if (next.done) controller.close();
15076
+ else controller.enqueue(next.value);
15077
+ } catch (error) {
15078
+ controller.error(error);
15079
+ }
15080
+ },
15081
+ async cancel(reason) {
15082
+ await iterator.return?.(reason);
15083
+ }
15084
+ });
15085
+ }
15086
+ function createFetchTransport(options2 = {}) {
15087
+ const fetchRequest = options2.fetch ?? globalThis.fetch;
15088
+ if (typeof fetchRequest !== "function") throw new TypeError("Fetch is unavailable");
15089
+ return async (input2) => {
15090
+ input2.signal.throwIfAborted();
15091
+ const url = new URL(input2.url);
15092
+ if (url.protocol !== "http:" && url.protocol !== "https:") throw new CurlError(1, "Unsupported protocol");
15093
+ const stopped = new AbortController();
15094
+ const signal = AbortSignal.any([input2.signal, stopped.signal]);
15095
+ const body = requestBody(input2.body, signal);
15096
+ const headers = new Headers();
15097
+ for (const [name2, value2] of input2.headers) headers.append(name2, value2);
15098
+ const request = new Request(url, {
15099
+ method: input2.method,
15100
+ headers,
15101
+ body,
15102
+ credentials: "omit",
15103
+ redirect: "manual",
15104
+ signal,
15105
+ ...body ? { duplex: "half" } : {}
15106
+ });
15107
+ let response;
15108
+ const dispose = async () => {
15109
+ stopped.abort();
15110
+ if (response?.body) await response.body.cancel().catch(() => void 0);
15111
+ };
15112
+ input2.registerCleanup?.(dispose);
15113
+ response = await fetchRequest(request);
15114
+ const reader = response.body?.getReader();
15115
+ const responseBody = reader ? (async function* () {
15116
+ try {
15117
+ while (true) {
15118
+ input2.signal.throwIfAborted();
15119
+ const next = await reader.read();
15120
+ if (next.done) return;
15121
+ yield next.value;
15122
+ }
15123
+ } finally {
15124
+ reader.releaseLock();
15125
+ }
15126
+ })() : (async function* () {
15127
+ })();
15128
+ return {
15129
+ status: response.status,
15130
+ statusText: response.statusText,
15131
+ headers: [...response.headers.entries()],
15132
+ body: responseBody,
15133
+ dispose
15134
+ };
15135
+ };
15136
+ }
15137
+
15138
+ // packages/safe-bash/src/commands/network/authorizer.ts
15139
+ init_platform();
15140
+ function privateHostname(input2) {
15141
+ const hostname = input2.toLowerCase().replace(/\.$/u, "").replace(/^\[|\]$/gu, "");
15142
+ if (hostname === "localhost" || hostname.endsWith(".localhost") || hostname === "::1") return true;
15143
+ if (hostname.includes(":")) {
15144
+ const first2 = hostname.split(":", 1)[0] ?? "";
15145
+ return first2.startsWith("fc") || first2.startsWith("fd") || ["fe8", "fe9", "fea", "feb"].some((prefix2) => first2.startsWith(prefix2));
15146
+ }
15147
+ const parts = hostname.split(".");
15148
+ if (parts.length !== 4 || parts.some((part) => !part || Array.from(part).some((character) => character < "0" || character > "9"))) return false;
15149
+ const octets = parts.map(Number);
15150
+ if (octets.some((value2) => value2 < 0 || value2 > 255)) return false;
15151
+ const [first, second] = octets;
15152
+ return first === 10 || first === 127 || first === 0 || first === 169 && second === 254 || first === 192 && second === 168 || first === 172 && second >= 16 && second <= 31;
15153
+ }
15154
+ function createOriginAuthorizer(allowlist = "*", options2 = {}) {
15155
+ const origins = /* @__PURE__ */ new Set();
15156
+ const hosts = /* @__PURE__ */ new Set();
15157
+ for (const entry of allowlist === "*" ? [] : allowlist) {
15158
+ const value2 = entry.trim();
15159
+ if (!value2) throw new TypeError("Origin allowlist entries must not be empty");
15160
+ if (value2.includes("://")) {
15161
+ const url = new URL(value2);
15162
+ if (url.pathname !== "/" || url.search || url.hash || url.username || url.password) {
15163
+ throw new TypeError(`Origin allowlist entry must be an origin: ${entry}`);
15164
+ }
15165
+ origins.add(url.origin);
15166
+ } else {
15167
+ if (value2.includes("/") || value2.includes("@")) throw new TypeError(`Invalid hostname allowlist entry: ${entry}`);
15168
+ hosts.add(value2.toLowerCase().replace(/\.$/u, ""));
15169
+ }
15170
+ }
15171
+ return (request) => {
15172
+ const url = new URL(request.url);
15173
+ const hostname = url.hostname.toLowerCase().replace(/\.$/u, "");
15174
+ if (options2.denyPrivateNetworks && privateHostname(hostname)) return false;
15175
+ return allowlist === "*" || origins.has(url.origin) || hosts.has(hostname);
15176
+ };
15177
+ }
15178
+
14432
15179
  // packages/safe-bash/src/browser.ts
14433
15180
  function createBrowserCommands() {
14434
15181
  return [
@@ -14467,14 +15214,18 @@ export {
14467
15214
  assertPathWithin,
14468
15215
  basename,
14469
15216
  browserCommands,
15217
+ cloudflareWorkerLimits,
15218
+ cloudflareWorkerNetworkLimits,
14470
15219
  collectBytes2 as collectBytes,
14471
15220
  collectText,
14472
15221
  composeMiddleware,
14473
15222
  createBrowserCommands,
14474
15223
  createBytePipe,
14475
15224
  createCommandArguments,
15225
+ createFetchTransport,
14476
15226
  createMemoryFileSystem,
14477
15227
  createMountFileSystem,
15228
+ createOriginAuthorizer,
14478
15229
  createOutputOperation,
14479
15230
  createOverlayFileSystem,
14480
15231
  createReadOnlyFileSystem,