@poe-platform/safe-bash 0.1.40 → 0.1.42

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 +45 -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 +878 -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 +18 -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,22 @@ 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 && !isShallowCopyOfProcessEnv(env)) return;
4487
+ warned.add(env);
4488
+ console.warn("[safe-bash] warning: the shell `env` is the host `process.env` (or a shallow copy of it). 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
+ function isShallowCopyOfProcessEnv(env) {
4491
+ const keys = Object.keys(env);
4492
+ if (keys.length !== Object.keys(process.env).length) return false;
4493
+ return keys.every((key) => env[key] === process.env[key]);
4494
+ }
4495
+
4447
4496
  // packages/safe-bash/src/shell/parser.ts
4448
4497
  init_platform();
4449
4498
 
@@ -4538,7 +4587,7 @@ function integer2(text) {
4538
4587
  for (const character of match[2]) {
4539
4588
  const digit2 = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ@_".indexOf(base <= 36 ? character.toLowerCase() : character);
4540
4589
  if (digit2 >= base || digit2 < 0) throw new Error("Digit exceeds arithmetic base");
4541
- value2 = value2 * BigInt(base) + BigInt(digit2);
4590
+ value2 = BigInt.asIntN(64, value2 * BigInt(base) + BigInt(digit2));
4542
4591
  }
4543
4592
  return value2;
4544
4593
  }
@@ -4864,7 +4913,7 @@ async function tokens(pattern, work) {
4864
4913
  let steps = 0;
4865
4914
  const tick = () => {
4866
4915
  if (--work.remaining < 0) work.exhausted();
4867
- if (++steps % 1024 === 0) return new Promise((resolve) => setImmediate(resolve)).then(() => work.signal.throwIfAborted());
4916
+ if (++steps % 1024 === 0) return yieldTurn(work.signal);
4868
4917
  return void 0;
4869
4918
  };
4870
4919
  for (let index = 0; index < characters.length; index++) {
@@ -4941,7 +4990,7 @@ async function matchTokens(patternTokens, value2, work) {
4941
4990
  while (position < characters.length) {
4942
4991
  if (--work.remaining < 0) work.exhausted();
4943
4992
  if (++steps % 1024 === 0) {
4944
- await new Promise((resolve) => setImmediate(resolve));
4993
+ await yieldTurn(work.signal);
4945
4994
  work.signal.throwIfAborted();
4946
4995
  }
4947
4996
  const token = patternTokens[tokenIndex];
@@ -4974,7 +5023,7 @@ async function charge(context, amount = 1) {
4974
5023
  const previous = context.work.remaining;
4975
5024
  context.work.remaining -= amount;
4976
5025
  if (previous >>> 10 !== context.work.remaining >>> 10) {
4977
- await new Promise((resolve) => setImmediate(resolve));
5026
+ await yieldTurn();
4978
5027
  context.signal.throwIfAborted();
4979
5028
  }
4980
5029
  }
@@ -5176,6 +5225,7 @@ var Lexer = class {
5176
5225
  this.documentLine = documentLine;
5177
5226
  this.partial = partial;
5178
5227
  if (depth > 64) throw new ShellSyntaxError("Syntax nesting exceeds 64", 0);
5228
+ for (let offset = source.indexOf("\n"); offset !== -1; offset = source.indexOf("\n", offset + 1)) this.newlineOffsets.push(offset);
5179
5229
  }
5180
5230
  source;
5181
5231
  depth;
@@ -5191,8 +5241,16 @@ var Lexer = class {
5191
5241
  conditional = false;
5192
5242
  conditionalPattern;
5193
5243
  documents = [];
5244
+ newlineOffsets = [];
5194
5245
  lineAt(position) {
5195
- return this.lineOffset + this.source.slice(0, position).split("\n").length;
5246
+ let low = 0;
5247
+ let high = this.newlineOffsets.length;
5248
+ while (low < high) {
5249
+ const middle = low + Math.floor((high - low) / 2);
5250
+ if (this.newlineOffsets[middle] < position) low = middle + 1;
5251
+ else high = middle;
5252
+ }
5253
+ return this.lineOffset + low + 1;
5196
5254
  }
5197
5255
  error(message2, unclosedQuote) {
5198
5256
  throw new ShellSyntaxError(message2, this.position, 2, void 0, unclosedQuote);
@@ -6614,7 +6672,7 @@ var SplitWork = class {
6614
6672
  return this.work >= this.nextYield;
6615
6673
  }
6616
6674
  async pause() {
6617
- await new Promise((resolve) => setImmediate(resolve));
6675
+ await yieldTurn();
6618
6676
  this.signal.throwIfAborted();
6619
6677
  this.nextYield = this.work + 4096;
6620
6678
  }
@@ -7884,14 +7942,7 @@ var ArrayLedger = class _ArrayLedger {
7884
7942
  this.#checkpoint += units;
7885
7943
  if (this.#checkpoint >= 128) {
7886
7944
  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
- }));
7945
+ return yieldTurn(signal);
7895
7946
  }
7896
7947
  }
7897
7948
  };
@@ -8768,43 +8819,17 @@ async function publishPipelineStatus(state, statuses, signal, scope) {
8768
8819
  throwCleanupFailures(scope.failures);
8769
8820
  }
8770
8821
 
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
8822
+ // packages/safe-bash/src/commands/regex-execution/ere/errors.ts
8780
8823
  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;
8824
+ var EreSyntaxError = class extends Error {
8825
+ constructor(message2, offset) {
8826
+ super(`invalid ERE at ${offset}: ${message2}`);
8798
8827
  this.offset = offset;
8799
- this.name = "EreTransportSemanticError";
8828
+ this.name = "EreSyntaxError";
8800
8829
  }
8801
- category;
8802
8830
  offset;
8803
8831
  status = 2;
8804
8832
  };
8805
-
8806
- // packages/safe-bash/src/commands/regex-execution/ere/errors.ts
8807
- init_platform();
8808
8833
  var EreUnsupportedError = class extends Error {
8809
8834
  constructor(message2, offset) {
8810
8835
  super(`unsupported ERE profile at ${offset}: ${message2}`);
@@ -8825,6 +8850,564 @@ var EreProfileLimitError = class extends Error {
8825
8850
  limit;
8826
8851
  status = 3;
8827
8852
  };
8853
+ var EreUsageUnknownError = class extends Error {
8854
+ status = 3;
8855
+ constructor(reason) {
8856
+ super("ERE invocation usage is unknown; further work is refused", { cause: reason });
8857
+ this.name = "EreUsageUnknownError";
8858
+ }
8859
+ };
8860
+
8861
+ // packages/safe-bash/src/commands/regex-execution/ere/limits.ts
8862
+ init_platform();
8863
+ var resources = Object.freeze([
8864
+ "patternBytes",
8865
+ "subjectBytes",
8866
+ "work",
8867
+ "states",
8868
+ "allocationUnits",
8869
+ "captureBytes",
8870
+ "captureSlots"
8871
+ ]);
8872
+ function integer5(value2) {
8873
+ if (!Number.isSafeInteger(value2) || value2 < 0) throw new TypeError("ERE bounds must be nonnegative safe integers");
8874
+ }
8875
+ function multiply(value2, factor, ceiling) {
8876
+ return value2 > Math.floor(ceiling / factor) ? ceiling : value2 * factor;
8877
+ }
8878
+ function deriveEreLimits(bounds) {
8879
+ integer5(bounds.maxExpansionBytes);
8880
+ integer5(bounds.maxExpansionFields);
8881
+ const bytes = bounds.maxExpansionBytes;
8882
+ const fields = bounds.maxExpansionFields;
8883
+ const byteUnits = multiply(bytes, 8, 4e6);
8884
+ const fieldUnits = multiply(fields, 128, 4e6);
8885
+ return Object.freeze({
8886
+ patternBytes: Math.min(bytes, 65536),
8887
+ subjectBytes: Math.min(bytes, 1048576),
8888
+ work: multiply(bytes, 32, 5e7),
8889
+ states: multiply(fields, 8, 65536),
8890
+ allocationUnits: byteUnits >= 4e6 - fieldUnits ? 4e6 : byteUnits + fieldUnits,
8891
+ captureBytes: bytes,
8892
+ captureSlots: fields
8893
+ });
8894
+ }
8895
+ var EreLedger = class {
8896
+ limits;
8897
+ #usage = {
8898
+ patternBytes: 0,
8899
+ subjectBytes: 0,
8900
+ work: 0,
8901
+ states: 0,
8902
+ allocationUnits: 0,
8903
+ captureBytes: 0,
8904
+ captureSlots: 0
8905
+ };
8906
+ #poison;
8907
+ #lastYield = 0;
8908
+ constructor(bounds, lowering = {}) {
8909
+ const limits = { ...deriveEreLimits(bounds) };
8910
+ for (const resource of Object.keys(lowering)) {
8911
+ if (!resources.includes(resource)) throw new TypeError("unknown ERE limit");
8912
+ const key = resource;
8913
+ const value2 = lowering[key];
8914
+ if (value2 === void 0) throw new TypeError("undefined ERE limit");
8915
+ integer5(value2);
8916
+ if (value2 > limits[key]) throw new RangeError("ERE limits may only be lowered");
8917
+ limits[key] = value2;
8918
+ }
8919
+ this.limits = Object.freeze(limits);
8920
+ }
8921
+ get usage() {
8922
+ return Object.freeze({ ...this.#usage });
8923
+ }
8924
+ check(signal) {
8925
+ if (signal?.aborted) throw signal.reason;
8926
+ if (this.#poison) throw this.#poison;
8927
+ }
8928
+ charge(resource, amount, signal) {
8929
+ this.check(signal);
8930
+ integer5(amount);
8931
+ if (amount > this.limits[resource] - this.#usage[resource]) {
8932
+ throw new EreProfileLimitError(resource, this.limits[resource]);
8933
+ }
8934
+ this.#usage[resource] += amount;
8935
+ }
8936
+ admitInput(resource, length, signal) {
8937
+ this.check(signal);
8938
+ integer5(length);
8939
+ if (length > this.limits[resource]) throw new EreProfileLimitError(resource, this.limits[resource]);
8940
+ this.#usage[resource] = Math.max(this.#usage[resource], length);
8941
+ }
8942
+ async checkpoint(signal) {
8943
+ this.check(signal);
8944
+ if (this.#usage.work - this.#lastYield >= 256) {
8945
+ this.#lastYield = this.#usage.work;
8946
+ await yieldTurn(signal);
8947
+ this.check(signal);
8948
+ }
8949
+ }
8950
+ markUnknownUsage(reason) {
8951
+ this.#poison ??= new EreUsageUnknownError(reason);
8952
+ }
8953
+ };
8954
+
8955
+ // packages/safe-bash/src/commands/regex-execution/ere/syntax.ts
8956
+ init_platform();
8957
+ var programs = /* @__PURE__ */ new WeakMap();
8958
+ var special = "\\.^$[]()|*+?{}";
8959
+ var classes = /* @__PURE__ */ new Set(["alnum", "alpha", "blank", "cntrl", "digit", "graph", "lower", "print", "punct", "space", "upper", "xdigit"]);
8960
+ function classMember(name2, code) {
8961
+ const upper = code >= 65 && code <= 90;
8962
+ const lower = code >= 97 && code <= 122;
8963
+ const digit2 = code >= 48 && code <= 57;
8964
+ switch (name2) {
8965
+ case "alnum":
8966
+ return upper || lower || digit2;
8967
+ case "alpha":
8968
+ return upper || lower;
8969
+ case "blank":
8970
+ return code === 9 || code === 32;
8971
+ case "cntrl":
8972
+ return code < 32 || code === 127;
8973
+ case "digit":
8974
+ return digit2;
8975
+ case "graph":
8976
+ return code >= 33 && code <= 126;
8977
+ case "lower":
8978
+ return lower;
8979
+ case "print":
8980
+ return code >= 32 && code <= 126;
8981
+ case "punct":
8982
+ return code >= 33 && code <= 126 && !upper && !lower && !digit2;
8983
+ case "space":
8984
+ return code === 32 || code >= 9 && code <= 13;
8985
+ case "upper":
8986
+ return upper;
8987
+ case "xdigit":
8988
+ return digit2 || code >= 65 && code <= 70 || code >= 97 && code <= 102;
8989
+ default:
8990
+ return false;
8991
+ }
8992
+ }
8993
+ async function admitAscii(text, ledger, signal) {
8994
+ for (let offset = 0; offset < text.length; offset++) {
8995
+ ledger.charge("work", 1, signal);
8996
+ const code = text.charCodeAt(offset);
8997
+ if (code === 0 || code > 127) throw new EreUnsupportedError("only non-NUL ASCII in the C/POSIX profile", offset);
8998
+ await ledger.checkpoint(signal);
8999
+ }
9000
+ }
9001
+ async function flatten(input2, ledger, signal) {
9002
+ if (typeof input2 === "string") {
9003
+ ledger.admitInput("patternBytes", input2.length, signal);
9004
+ await admitAscii(input2, ledger, signal);
9005
+ return { pattern: input2, quoted: null };
9006
+ }
9007
+ let size = 0;
9008
+ for (const fragment of input2) {
9009
+ if (typeof fragment.text !== "string" || typeof fragment.literal !== "boolean") throw new TypeError("invalid ERE fragment");
9010
+ ledger.charge("work", 1, signal);
9011
+ await ledger.checkpoint(signal);
9012
+ ledger.admitInput("patternBytes", fragment.text.length, signal);
9013
+ await admitAscii(fragment.text, ledger, signal);
9014
+ if (fragment.text.length > ledger.limits.patternBytes - size) ledger.admitInput("patternBytes", ledger.limits.patternBytes + 1, signal);
9015
+ size += fragment.text.length;
9016
+ ledger.admitInput("patternBytes", size, signal);
9017
+ }
9018
+ ledger.charge("allocationUnits", size * 2 + input2.length + 2, signal);
9019
+ const output2 = [];
9020
+ const quoted = [];
9021
+ for (const fragment of input2) {
9022
+ for (let offset = 0; offset < fragment.text.length; offset++) {
9023
+ ledger.charge("work", 1, signal);
9024
+ await ledger.checkpoint(signal);
9025
+ quoted.push(fragment.literal);
9026
+ }
9027
+ output2.push(fragment.text);
9028
+ await ledger.checkpoint(signal);
9029
+ }
9030
+ return { pattern: output2.join(""), quoted: Object.freeze(quoted) };
9031
+ }
9032
+ var Parser2 = class {
9033
+ constructor(pattern, quoted, ledger, signal) {
9034
+ this.pattern = pattern;
9035
+ this.quoted = quoted;
9036
+ this.ledger = ledger;
9037
+ this.signal = signal;
9038
+ }
9039
+ pattern;
9040
+ quoted;
9041
+ ledger;
9042
+ signal;
9043
+ offset = 0;
9044
+ groups = 0;
9045
+ nodes = 0;
9046
+ at(character, offset = this.offset) {
9047
+ return !this.quoted?.[offset] && this.pattern[offset] === character;
9048
+ }
9049
+ node(create) {
9050
+ if (this.nodes >= 4096) throw new EreUnsupportedError("4096-node grammar ceiling", this.offset);
9051
+ this.ledger.charge("allocationUnits", 8, this.signal);
9052
+ this.nodes++;
9053
+ return Object.freeze(create());
9054
+ }
9055
+ async expression(depth) {
9056
+ if (depth > 64) throw new EreUnsupportedError("64-level grammar ceiling", this.offset);
9057
+ this.ledger.charge("allocationUnits", 1, this.signal);
9058
+ const alternatives = [];
9059
+ while (true) {
9060
+ const child = await this.sequence(depth);
9061
+ this.ledger.charge("allocationUnits", 1, this.signal);
9062
+ alternatives.push(child);
9063
+ if (!this.at("|")) break;
9064
+ this.offset++;
9065
+ }
9066
+ if (alternatives.length === 1) return alternatives[0];
9067
+ this.ledger.charge("work", alternatives.length * 2, this.signal);
9068
+ await this.ledger.checkpoint(this.signal);
9069
+ return this.node(() => ({ kind: "alternative", children: Object.freeze(alternatives), nullable: alternatives.some((value2) => value2.nullable), captured: alternatives.some((value2) => value2.captured) }));
9070
+ }
9071
+ async sequence(depth) {
9072
+ this.ledger.charge("allocationUnits", 1, this.signal);
9073
+ const children = [];
9074
+ while (this.offset < this.pattern.length && !this.at("|") && !this.at(")")) {
9075
+ this.ledger.charge("work", 1, this.signal);
9076
+ await this.ledger.checkpoint(this.signal);
9077
+ let child = await this.atom(depth);
9078
+ const operator = this.quoted?.[this.offset] ? void 0 : this.pattern[this.offset];
9079
+ if (operator === "*" || operator === "+" || operator === "?" || operator === "{") {
9080
+ const begin = this.offset++;
9081
+ let min = operator === "+" ? 1 : 0;
9082
+ let max = operator === "?" ? 1 : Infinity;
9083
+ if (operator === "{") {
9084
+ min = await this.count();
9085
+ max = min;
9086
+ if (this.at(",")) {
9087
+ this.offset++;
9088
+ max = this.at("}") ? Infinity : await this.count();
9089
+ }
9090
+ if (!this.at("}") || max < min) throw new EreSyntaxError("invalid interval", begin);
9091
+ this.offset++;
9092
+ }
9093
+ if (child.kind === "start" || child.kind === "end") throw new EreUnsupportedError("repeated anchor", begin);
9094
+ if (child.nullable && child.captured && max > 1) throw new EreUnsupportedError("nullable captured repetition", begin);
9095
+ const repeated = child;
9096
+ child = this.node(() => ({ kind: "repeat", child: repeated, min, max, nullable: min === 0 || repeated.nullable, captured: repeated.captured }));
9097
+ const next = this.quoted?.[this.offset] ? void 0 : this.pattern[this.offset];
9098
+ if (next === "*" || next === "+" || next === "?" || next === "{") throw new EreUnsupportedError("stacked repetition", this.offset);
9099
+ }
9100
+ this.ledger.charge("allocationUnits", 1, this.signal);
9101
+ children.push(child);
9102
+ }
9103
+ if (children.length === 0) return this.node(() => ({ kind: "empty", nullable: true, captured: false }));
9104
+ if (children.length === 1) return children[0];
9105
+ this.ledger.charge("work", children.length * 2, this.signal);
9106
+ await this.ledger.checkpoint(this.signal);
9107
+ return this.node(() => ({ kind: "sequence", children: Object.freeze(children), nullable: children.every((value2) => value2.nullable), captured: children.some((value2) => value2.captured) }));
9108
+ }
9109
+ async count() {
9110
+ const begin = this.offset;
9111
+ let value2 = 0;
9112
+ while (!this.quoted?.[this.offset] && this.pattern[this.offset] !== void 0 && this.pattern[this.offset] >= "0" && this.pattern[this.offset] <= "9") {
9113
+ this.ledger.charge("work", 1, this.signal);
9114
+ await this.ledger.checkpoint(this.signal);
9115
+ value2 = value2 * 10 + this.pattern.charCodeAt(this.offset++) - 48;
9116
+ if (value2 > 255) throw new EreUnsupportedError("interval counts exceed 255", begin);
9117
+ }
9118
+ if (begin === this.offset) throw new EreSyntaxError("missing interval count", begin);
9119
+ return value2;
9120
+ }
9121
+ async atom(depth) {
9122
+ const begin = this.offset;
9123
+ const character = this.pattern[this.offset++];
9124
+ if (this.quoted?.[begin]) return this.node(() => ({ kind: "literal", code: character.charCodeAt(0), nullable: false, captured: false }));
9125
+ if (character === "(") {
9126
+ if (this.at("?")) throw new EreUnsupportedError("extended group syntax", begin);
9127
+ if (this.groups >= 32) throw new EreUnsupportedError("32-group grammar ceiling", begin);
9128
+ const index = ++this.groups;
9129
+ const child = await this.expression(depth + 1);
9130
+ if (!this.at(")")) throw new EreSyntaxError("unclosed group", begin);
9131
+ this.offset++;
9132
+ return this.node(() => ({ kind: "group", index, child, nullable: child.nullable, captured: true }));
9133
+ }
9134
+ if (character === "[") return this.set(begin);
9135
+ if (character === "\\") {
9136
+ const escaped = this.pattern[this.offset++];
9137
+ if (escaped === void 0) throw new EreSyntaxError("trailing escape", begin);
9138
+ if (!special.includes(escaped)) throw new EreUnsupportedError("backreference or escape extension", begin);
9139
+ return this.node(() => ({ kind: "literal", code: escaped.charCodeAt(0), nullable: false, captured: false }));
9140
+ }
9141
+ if (character === "*" || character === "+" || character === "?" || character === "{") throw new EreSyntaxError("repetition without operand", begin);
9142
+ if (character === ".") return this.node(() => ({ kind: "dot", nullable: false, captured: false }));
9143
+ if (character === "^") return this.node(() => ({ kind: "start", nullable: true, captured: false }));
9144
+ if (character === "$") return this.node(() => ({ kind: "end", nullable: true, captured: false }));
9145
+ return this.node(() => ({ kind: "literal", code: character.charCodeAt(0), nullable: false, captured: false }));
9146
+ }
9147
+ async set(begin) {
9148
+ this.ledger.charge("allocationUnits", 128, this.signal);
9149
+ const members = new Array(128).fill(false);
9150
+ const negate = this.at("^");
9151
+ if (negate) this.offset++;
9152
+ let first = true;
9153
+ while (this.offset < this.pattern.length) {
9154
+ this.ledger.charge("work", 1, this.signal);
9155
+ await this.ledger.checkpoint(this.signal);
9156
+ if (this.at("]") && !first) {
9157
+ this.offset++;
9158
+ if (negate) for (let code = 1; code < 128; code++) {
9159
+ this.ledger.charge("work", 1, this.signal);
9160
+ await this.ledger.checkpoint(this.signal);
9161
+ members[code] = !members[code];
9162
+ }
9163
+ return this.node(() => ({ kind: "set", members: Object.freeze(members), nullable: false, captured: false }));
9164
+ }
9165
+ first = false;
9166
+ if (this.at("[") && (this.at(".", this.offset + 1) || this.at("=", this.offset + 1))) {
9167
+ throw new EreUnsupportedError("collating or equivalence element", this.offset);
9168
+ }
9169
+ if (this.at("[") && this.at(":", this.offset + 1)) {
9170
+ const classBegin = this.offset;
9171
+ this.offset += 2;
9172
+ let name2 = "";
9173
+ while (this.offset < this.pattern.length && !this.at(":")) {
9174
+ this.ledger.charge("work", 1, this.signal);
9175
+ await this.ledger.checkpoint(this.signal);
9176
+ if (name2.length >= 6) throw new EreSyntaxError("unknown character class", classBegin);
9177
+ name2 += this.pattern[this.offset++];
9178
+ }
9179
+ if (!this.at(":") || !this.at("]", this.offset + 1) || !classes.has(name2)) throw new EreSyntaxError("unknown character class", classBegin);
9180
+ this.offset += 2;
9181
+ for (let code = 1; code < 128; code++) {
9182
+ this.ledger.charge("work", 1, this.signal);
9183
+ await this.ledger.checkpoint(this.signal);
9184
+ if (classMember(name2, code)) members[code] = true;
9185
+ }
9186
+ if (this.at("-") && !this.at("]", this.offset + 1)) throw new EreSyntaxError("class cannot be a range endpoint", this.offset);
9187
+ } else {
9188
+ const lower = this.pattern.charCodeAt(this.offset++);
9189
+ if (this.at("-") && !this.at("]", this.offset + 1) && this.pattern[this.offset + 1] !== void 0) {
9190
+ this.offset++;
9191
+ if (this.at("[")) throw new EreSyntaxError("nonliteral range endpoint", this.offset);
9192
+ const upper = this.pattern.charCodeAt(this.offset++);
9193
+ if (lower > upper) throw new EreSyntaxError("descending range", this.offset - 3);
9194
+ for (let code = lower; code <= upper; code++) {
9195
+ this.ledger.charge("work", 1, this.signal);
9196
+ await this.ledger.checkpoint(this.signal);
9197
+ members[code] = true;
9198
+ }
9199
+ } else members[lower] = true;
9200
+ }
9201
+ }
9202
+ throw new EreSyntaxError("unclosed bracket expression", begin);
9203
+ }
9204
+ };
9205
+ async function compileEre(input2, ledger, signal) {
9206
+ ledger.check(signal);
9207
+ const { pattern, quoted } = await flatten(input2, ledger, signal);
9208
+ const parser = new Parser2(pattern, quoted, ledger, signal);
9209
+ const root = await parser.expression(0);
9210
+ if (parser.offset !== pattern.length) throw new EreSyntaxError("unmatched closing group", parser.offset);
9211
+ ledger.charge("allocationUnits", 3, signal);
9212
+ const program = Object.freeze({ pattern, groups: parser.groups });
9213
+ programs.set(program, { root, ledger });
9214
+ return program;
9215
+ }
9216
+ function resolveEreProgram(program, ledger) {
9217
+ const entry = programs.get(program);
9218
+ if (!entry || entry.ledger !== ledger) throw new TypeError("ERE program is not bound to this invocation ledger");
9219
+ return entry.root;
9220
+ }
9221
+
9222
+ // packages/safe-bash/src/commands/regex-execution/ere/matcher.ts
9223
+ init_platform();
9224
+ function spanOrder(left, right) {
9225
+ if (left === null) return right === null ? 0 : -1;
9226
+ if (right === null) return 1;
9227
+ const length = left.end - left.start - (right.end - right.start);
9228
+ return length === 0 ? right.start - left.start : length;
9229
+ }
9230
+ async function historyOrder(left, right, ledger, signal) {
9231
+ const leftCount = left?.count ?? 0;
9232
+ const rightCount = right?.count ?? 0;
9233
+ for (let ordinal = 1; ordinal <= Math.min(leftCount, rightCount); ordinal++) {
9234
+ ledger.charge("work", 1, signal);
9235
+ await ledger.checkpoint(signal);
9236
+ let leftEntry = left;
9237
+ let rightEntry = right;
9238
+ for (let remaining = leftCount; remaining > ordinal; remaining--) {
9239
+ ledger.charge("work", 1, signal);
9240
+ await ledger.checkpoint(signal);
9241
+ leftEntry = leftEntry.previous;
9242
+ }
9243
+ for (let remaining = rightCount; remaining > ordinal; remaining--) {
9244
+ ledger.charge("work", 1, signal);
9245
+ await ledger.checkpoint(signal);
9246
+ rightEntry = rightEntry.previous;
9247
+ }
9248
+ const compared = spanOrder(leftEntry.span, rightEntry.span);
9249
+ if (compared !== 0) return compared;
9250
+ }
9251
+ return leftCount - rightCount;
9252
+ }
9253
+ async function preferred(candidate, incumbent, ledger, signal) {
9254
+ if (candidate.position !== incumbent.position) return candidate.position > incumbent.position;
9255
+ for (let group = 1; group < candidate.captures.length; group++) {
9256
+ ledger.charge("work", 1, signal);
9257
+ const compared = await historyOrder(candidate.histories[group], incumbent.histories[group], ledger, signal);
9258
+ if (compared !== 0) return compared > 0;
9259
+ }
9260
+ return false;
9261
+ }
9262
+ async function resetDescendants(node, previous, ledger, signal) {
9263
+ ledger.charge("allocationUnits", previous.length + 3, signal);
9264
+ const captures = previous.slice();
9265
+ const pending = [node];
9266
+ while (pending.length > 0) {
9267
+ ledger.charge("work", 1, signal);
9268
+ await ledger.checkpoint(signal);
9269
+ const current = pending.pop();
9270
+ if (current.kind === "group") captures[current.index] = null;
9271
+ if (current.kind === "group" || current.kind === "repeat") {
9272
+ if (current.child.captured) {
9273
+ ledger.charge("allocationUnits", 1, signal);
9274
+ pending.push(current.child);
9275
+ }
9276
+ } else if (current.kind === "sequence" || current.kind === "alternative") {
9277
+ for (const child of current.children) {
9278
+ ledger.charge("work", 1, signal);
9279
+ await ledger.checkpoint(signal);
9280
+ if (child.captured) {
9281
+ ledger.charge("allocationUnits", 1, signal);
9282
+ pending.push(child);
9283
+ }
9284
+ }
9285
+ }
9286
+ }
9287
+ return captures;
9288
+ }
9289
+ async function matchEre(program, subject, ledger, signal) {
9290
+ ledger.check(signal);
9291
+ const root = resolveEreProgram(program, ledger);
9292
+ ledger.admitInput("subjectBytes", subject.length, signal);
9293
+ await admitAscii(subject, ledger, signal);
9294
+ const width = program.groups + 1;
9295
+ ledger.charge("allocationUnits", width * 2 + 1, signal);
9296
+ const emptyCaptures = Object.freeze(new Array(width).fill(null));
9297
+ const emptyHistories = Object.freeze(new Array(width).fill(null));
9298
+ const pending = [];
9299
+ const task = (create) => {
9300
+ ledger.charge("allocationUnits", 5, signal);
9301
+ return create();
9302
+ };
9303
+ const push = (position, next, captures, histories) => {
9304
+ ledger.charge("states", 1, signal);
9305
+ ledger.charge("allocationUnits", 5, signal);
9306
+ pending.push({ position, task: next, captures, histories });
9307
+ };
9308
+ for (let start = 0; start <= subject.length; start++) {
9309
+ push(start, task(() => ({ kind: "node", node: root, next: null })), emptyCaptures, emptyHistories);
9310
+ let best;
9311
+ while (pending.length > 0) {
9312
+ ledger.charge("work", 1, signal);
9313
+ await ledger.checkpoint(signal);
9314
+ const state = pending.pop();
9315
+ const current = state.task;
9316
+ if (current === null) {
9317
+ if (!best || await preferred(state, best, ledger, signal)) best = state;
9318
+ continue;
9319
+ }
9320
+ if (current.kind === "close") {
9321
+ ledger.charge("allocationUnits", width * 2 + 6, signal);
9322
+ const span = Object.freeze({ start: current.start, end: state.position });
9323
+ const captures = state.captures.slice();
9324
+ captures[current.group] = span;
9325
+ const histories = state.histories.slice();
9326
+ const previous = histories[current.group];
9327
+ histories[current.group] = { span, previous, count: (previous?.count ?? 0) + 1 };
9328
+ push(state.position, current.next, captures, histories);
9329
+ continue;
9330
+ }
9331
+ if (current.kind === "repeat") {
9332
+ const { node: node2, count } = current;
9333
+ if (count >= node2.min) push(state.position, current.next, state.captures, state.histories);
9334
+ const noProgress = count > 0 && state.position === current.previous;
9335
+ if (count < node2.max && (!noProgress || count < node2.min)) {
9336
+ const repeat = task(() => ({ kind: "repeat", node: node2, count: count + 1, previous: state.position, next: current.next }));
9337
+ push(state.position, task(() => ({ kind: "node", node: node2.child, next: repeat })), state.captures, state.histories);
9338
+ }
9339
+ continue;
9340
+ }
9341
+ const node = current.node;
9342
+ switch (node.kind) {
9343
+ case "empty":
9344
+ push(state.position, current.next, state.captures, state.histories);
9345
+ break;
9346
+ case "start":
9347
+ if (state.position === 0) push(state.position, current.next, state.captures, state.histories);
9348
+ break;
9349
+ case "end":
9350
+ if (state.position === subject.length) push(state.position, current.next, state.captures, state.histories);
9351
+ break;
9352
+ case "dot":
9353
+ case "literal":
9354
+ case "set": {
9355
+ const code = subject.charCodeAt(state.position);
9356
+ if (state.position < subject.length && (node.kind === "dot" || node.kind === "literal" && node.code === code || node.kind === "set" && node.members[code])) {
9357
+ push(state.position + 1, current.next, state.captures, state.histories);
9358
+ }
9359
+ break;
9360
+ }
9361
+ case "sequence": {
9362
+ let next = current.next;
9363
+ for (let index = node.children.length - 1; index >= 0; index--) {
9364
+ ledger.charge("work", 1, signal);
9365
+ await ledger.checkpoint(signal);
9366
+ const following = next;
9367
+ next = task(() => ({ kind: "node", node: node.children[index], next: following }));
9368
+ }
9369
+ push(state.position, next, state.captures, state.histories);
9370
+ break;
9371
+ }
9372
+ case "alternative":
9373
+ for (let index = node.children.length - 1; index >= 0; index--) {
9374
+ ledger.charge("work", 1, signal);
9375
+ await ledger.checkpoint(signal);
9376
+ push(state.position, task(() => ({ kind: "node", node: node.children[index], next: current.next })), state.captures, state.histories);
9377
+ }
9378
+ break;
9379
+ case "group": {
9380
+ const captures = node.child.captured ? await resetDescendants(node.child, state.captures, ledger, signal) : state.captures;
9381
+ const close = task(() => ({ kind: "close", group: node.index, start: state.position, next: current.next }));
9382
+ push(state.position, task(() => ({ kind: "node", node: node.child, next: close })), captures, state.histories);
9383
+ break;
9384
+ }
9385
+ case "repeat":
9386
+ push(state.position, task(() => ({ kind: "repeat", node, count: 0, previous: -1, next: current.next })), state.captures, state.histories);
9387
+ break;
9388
+ }
9389
+ }
9390
+ if (best) {
9391
+ ledger.charge("captureSlots", width, signal);
9392
+ let bytes = best.position - start;
9393
+ for (let group = 1; group < width; group++) {
9394
+ ledger.charge("work", 1, signal);
9395
+ const span = best.captures[group];
9396
+ if (span) bytes += span.end - span.start;
9397
+ }
9398
+ ledger.charge("captureBytes", bytes, signal);
9399
+ ledger.charge("allocationUnits", width * 2 + bytes + 4, signal);
9400
+ const captures = best.captures.slice();
9401
+ captures[0] = Object.freeze({ start, end: best.position });
9402
+ const values = captures.map((span) => span === null ? "" : subject.slice(span.start, span.end));
9403
+ ledger.check(signal);
9404
+ return Object.freeze({ matched: true, captures: Object.freeze(captures), values: Object.freeze(values) });
9405
+ }
9406
+ }
9407
+ ledger.charge("allocationUnits", 3, signal);
9408
+ ledger.check(signal);
9409
+ return Object.freeze({ matched: false, captures: Object.freeze([]), values: Object.freeze([]) });
9410
+ }
8828
9411
 
8829
9412
  // packages/safe-bash/src/shell/runtime.ts
8830
9413
  var defaultLimits = {
@@ -8836,6 +9419,8 @@ var defaultLimits = {
8836
9419
  maxSourceBytes: 1024 * 1024,
8837
9420
  maxExpansionFields: 1e4,
8838
9421
  maxExpansionBytes: 16 * 1024 * 1024,
9422
+ maxWallClockMs: 3e4,
9423
+ maxCpuMs: 3e4,
8839
9424
  pipeHighWaterMark: 64 * 1024
8840
9425
  };
8841
9426
  var shellBuiltinNames = /* @__PURE__ */ new Set([
@@ -8915,6 +9500,8 @@ var Budget = class {
8915
9500
  this.limits = limits;
8916
9501
  this.signal = signal ? AbortSignal.any([signal, this.controller.signal]) : this.controller.signal;
8917
9502
  this.values = new ValueArena(limits.maxExpansionBytes, limits.maxExpansionFields, () => this.signal.throwIfAborted(), (limit) => this.fail(limit));
9503
+ this.#wallClockDeadline = Date.now() + limits.maxWallClockMs;
9504
+ this.#armWallClock();
8918
9505
  }
8919
9506
  limits;
8920
9507
  values;
@@ -8924,12 +9511,34 @@ var Budget = class {
8924
9511
  sourceBytes = 0;
8925
9512
  controller = new AbortController();
8926
9513
  signal;
9514
+ #wallClockTimer;
9515
+ #wallClockDeadline = 0;
9516
+ #cpuStarted = monotonicNow();
9517
+ #armWallClock() {
9518
+ const remaining = this.#wallClockDeadline - Date.now();
9519
+ if (remaining <= 0) {
9520
+ this.controller.abort(new ShellLimitError("maxWallClockMs"));
9521
+ return;
9522
+ }
9523
+ this.#wallClockTimer = setTimeout(() => this.#armWallClock(), Math.min(remaining, 2147483647));
9524
+ const timer = this.#wallClockTimer;
9525
+ timer.unref?.();
9526
+ }
9527
+ close() {
9528
+ if (this.#wallClockTimer !== void 0) clearTimeout(this.#wallClockTimer);
9529
+ this.#wallClockTimer = void 0;
9530
+ }
8927
9531
  fail(limit) {
8928
9532
  const error = new ShellLimitError(limit);
8929
9533
  this.controller.abort(error);
8930
9534
  throw error;
8931
9535
  }
9536
+ cpuCheckpoint() {
9537
+ this.signal.throwIfAborted();
9538
+ if (monotonicNow() - this.#cpuStarted > this.limits.maxCpuMs) this.fail("maxCpuMs");
9539
+ }
8932
9540
  tick() {
9541
+ this.cpuCheckpoint();
8933
9542
  this.signal.throwIfAborted();
8934
9543
  if (++this.commands > this.limits.maxCommands) this.fail("maxCommands");
8935
9544
  }
@@ -9328,7 +9937,7 @@ var CdLookup = class {
9328
9937
  amount -= step;
9329
9938
  if (this.spent % 128 === 0) {
9330
9939
  this.signal.throwIfAborted();
9331
- await interruptible(new Promise((resolve) => setImmediate(resolve)), this.signal);
9940
+ await yieldTurn(this.signal);
9332
9941
  this.signal.throwIfAborted();
9333
9942
  }
9334
9943
  }
@@ -9424,7 +10033,7 @@ var DirectoryStackWork = class {
9424
10033
  async flushWork() {
9425
10034
  this.signal.throwIfAborted();
9426
10035
  if (this.flushed === this.spent) return;
9427
- await interruptible(new Promise((resolve) => setImmediate(resolve)), this.signal);
10036
+ await yieldTurn(this.signal);
9428
10037
  this.signal.throwIfAborted();
9429
10038
  this.flushed = this.spent;
9430
10039
  }
@@ -9745,6 +10354,7 @@ var Runtime = class _Runtime {
9745
10354
  this.cancellationDepth = cancellationDepth;
9746
10355
  this.cancellationMaxDepth = cancellationMaxDepth;
9747
10356
  this.outcomeFrame = outcomeFrame;
10357
+ registerYieldCheckpoint(this.signal, () => this.budget.cpuCheckpoint());
9748
10358
  }
9749
10359
  fs;
9750
10360
  commands;
@@ -9760,19 +10370,6 @@ var Runtime = class _Runtime {
9760
10370
  cancellationDepth;
9761
10371
  cancellationMaxDepth;
9762
10372
  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
10373
  async ereDiagnostic(io, detail) {
9777
10374
  try {
9778
10375
  await this.diagnostic(io, detail);
@@ -9821,32 +10418,21 @@ var Runtime = class _Runtime {
9821
10418
  await textToken(operation, subject, this.signal);
9822
10419
  const name2 = "BASH_REMATCH";
9823
10420
  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;
10421
+ const ledger = new EreLedger({
10422
+ maxExpansionBytes: this.budget.limits.maxExpansionBytes,
10423
+ maxExpansionFields: this.budget.limits.maxExpansionFields
9830
10424
  });
9831
- const session = entry.root.openSession((cleanup) => scope.register(cleanup));
9832
10425
  let result;
9833
- let sessionEscaping = false;
9834
10426
  try {
9835
- result = await session.execute({ pattern: fragments, subject }, this.signal);
10427
+ const program = await compileEre(fragments, ledger, this.signal);
10428
+ result = await matchEre(program, subject, ledger, this.signal);
9836
10429
  } catch (error) {
9837
- sessionEscaping = true;
9838
10430
  this.signal.throwIfAborted();
9839
- if (error instanceof EreTransportSemanticError || error instanceof EreUnsupportedError) {
10431
+ if (error instanceof EreSyntaxError || error instanceof EreUnsupportedError) {
9840
10432
  await this.ereDiagnostic(io, `[[ ${error.message}`);
9841
- sessionEscaping = false;
9842
10433
  return 2;
9843
10434
  }
9844
10435
  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
10436
  }
9851
10437
  const status = result.matched ? 0 : 1;
9852
10438
  this.signal.throwIfAborted();
@@ -9861,8 +10447,8 @@ var Runtime = class _Runtime {
9861
10447
  const tickets = operation.reserve({ generation: true, version: true, epoch: true, work: 8 });
9862
10448
  const prepared = await store.prepareName(name2, operation, this.signal);
9863
10449
  staged = IndexedBinding.create(store.owner);
9864
- if (result.matched) for (let index = 0; index < result.spans.length; index++) {
9865
- const span = result.spans[index];
10450
+ if (result.matched) for (let index = 0; index < result.captures.length; index++) {
10451
+ const span = result.captures[index];
9866
10452
  const size = span === null ? 0 : span.end - span.start;
9867
10453
  operation.reserve({ payload: size, metadata: 32, work: size + 2 });
9868
10454
  const value2 = span === null ? "" : subject.slice(span.start, span.end);
@@ -10024,6 +10610,7 @@ var Runtime = class _Runtime {
10024
10610
  writeVariable(state, name2, value2, origin = "assignment") {
10025
10611
  if (arrayStore(state)?.get(name2)) throw new ArrayFailure(origin === "arithmetic" ? "indexed arithmetic is unsupported" : "indexed write requires prepared publication");
10026
10612
  if (state.readonlyVariables?.has(name2)) throw new Error(`${name2}: readonly variable`);
10613
+ if (shellValueByteLength(value2) > this.budget.limits.maxExpansionBytes) this.budget.fail("maxExpansionBytes");
10027
10614
  if (name2 === "OPTIND" && state.getopts?.integer && origin !== "arithmetic") {
10028
10615
  try {
10029
10616
  value2 = String(evaluateArithmetic(prepareArithmetic(shellValueText(value2) || "0"), this.arithmeticVariables(state)));
@@ -10533,7 +11120,7 @@ var Runtime = class _Runtime {
10533
11120
  completed.add(index);
10534
11121
  if (incoming) {
10535
11122
  const upstream = index - 1;
10536
- const close = setImmediate(() => {
11123
+ const close = scheduleTurn(() => {
10537
11124
  closing.delete(close);
10538
11125
  if (written.has(upstream) && !completed.has(upstream)) controllers[upstream].abort(new PipelineClosed());
10539
11126
  });
@@ -10560,7 +11147,7 @@ var Runtime = class _Runtime {
10560
11147
  try {
10561
11148
  statuses = await interruptible(Promise.all(tasks), this.signal);
10562
11149
  } finally {
10563
- for (const close of closing) clearImmediate(close);
11150
+ for (const close of closing) cancelTurn(close);
10564
11151
  for (const [index, controller] of controllers.entries()) if (!completed.has(index) || written.has(index)) controller.abort(new PipelineClosed());
10565
11152
  await Promise.all(pipes.map((pipe) => pipe.abort()));
10566
11153
  }
@@ -10625,7 +11212,7 @@ var Runtime = class _Runtime {
10625
11212
  originalIO = { ...originalIO, diagnosticLine, substitutionDiagnosticLine: originalIO.substitutionDiagnosticLines?.get(command) ?? diagnosticLine };
10626
11213
  if (command.kind === "subshell") originalIO = isolateIO(originalIO);
10627
11214
  this.budget.tick();
10628
- if (this.budget.commands % 128 === 0) await interruptible(new Promise((resolve) => setImmediate(resolve)), this.signal);
11215
+ if (this.budget.commands % 128 === 0) await yieldTurn(this.signal);
10629
11216
  this.signal.throwIfAborted();
10630
11217
  const inputs = /* @__PURE__ */ new Set();
10631
11218
  const outputs = /* @__PURE__ */ new Set();
@@ -10672,7 +11259,7 @@ var Runtime = class _Runtime {
10672
11259
  diagnosticFailure = error;
10673
11260
  throw error;
10674
11261
  }
10675
- if (error instanceof EreProfileLimitError || error instanceof EreTransportProfileLimitError) {
11262
+ if (error instanceof EreProfileLimitError) {
10676
11263
  try {
10677
11264
  await this.diagnostic(io, `[[ ${error.message}`);
10678
11265
  } catch (reason) {
@@ -10741,7 +11328,7 @@ var Runtime = class _Runtime {
10741
11328
  for (const clause of command.clauses) {
10742
11329
  let matched = fallthrough;
10743
11330
  if (!matched) for (const word of clause.patterns) {
10744
- if (++patterns % 128 === 0) await interruptible(new Promise((resolve) => setImmediate(resolve)), this.signal);
11331
+ if (++patterns % 128 === 0) await yieldTurn(this.signal);
10745
11332
  const pattern = (await this.word(word, state, io, false, true)).join("");
10746
11333
  if (await matchesPattern(pattern, subject, work)) {
10747
11334
  matched = true;
@@ -10862,7 +11449,7 @@ var Runtime = class _Runtime {
10862
11449
  this.signal.throwIfAborted();
10863
11450
  for (const warning of warnings.splice(0)) await writeText(io.stderr, `shell: warning: ${warning}
10864
11451
  `);
10865
- if (++words % 128 === 0) await interruptible(new Promise((resolve) => setImmediate(resolve)), this.signal);
11452
+ if (++words % 128 === 0) await yieldTurn(this.signal);
10866
11453
  const part = (await this.word(word, state, io, false)).join("");
10867
11454
  size += import_buffer.Buffer.byteLength(part);
10868
11455
  if (size > this.budget.limits.maxExpansionBytes) this.budget.fail("maxExpansionBytes");
@@ -11056,7 +11643,7 @@ var Runtime = class _Runtime {
11056
11643
  }
11057
11644
  const wordValues = await this.valueWords(commandWords, state, originalIO, ["export", "local", "readonly"].includes(commandWords[declarationIndex]?.plain ?? ""));
11058
11645
  const words = wordValues.map(shellValueText);
11059
- const special = state.profile === "sh" && specialBuiltinNames.has(words[0] ?? "");
11646
+ const special2 = state.profile === "sh" && specialBuiltinNames.has(words[0] ?? "");
11060
11647
  const inlineInput = command.redirects.some((redirect) => redirect.document || redirect.operator === "<<<");
11061
11648
  const functionCommand = words.length > 0 && state.functions.has(words[0]);
11062
11649
  const isolatedInlineInput = inlineInput && words.length > 0 && !shellBuiltinNames.has(words[0]) && !functionCommand;
@@ -11084,7 +11671,7 @@ var Runtime = class _Runtime {
11084
11671
  if (assignment.append) value2 = concatShellValues([stateMonitor(state)?.values.get(assignment.name, state.variables[assignment.name] ?? "") ?? state.variables[assignment.name] ?? "", value2], io[valueScope]);
11085
11672
  if (state.readonlyVariables?.has(assignment.name)) {
11086
11673
  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);
11674
+ if (state.profile === "sh" || !words.length) throw new Flow("exit", state.profile === "sh" && (special2 || !words.length) ? 127 : 1);
11088
11675
  continue;
11089
11676
  }
11090
11677
  if (!previous.has(assignment.name)) {
@@ -11175,7 +11762,7 @@ var Runtime = class _Runtime {
11175
11762
  if (error instanceof Flow) throw error;
11176
11763
  this.signal.throwIfAborted();
11177
11764
  const original = error instanceof ExecutionFailure ? error.original : error;
11178
- if (special && !(original instanceof ShellLimitError) && !(original instanceof ExpansionFailure) && !(original instanceof Flow) && !(original instanceof ShellSyntaxError)) {
11765
+ if (special2 && !(original instanceof ShellLimitError) && !(original instanceof ExpansionFailure) && !(original instanceof Flow) && !(original instanceof ShellSyntaxError)) {
11179
11766
  throw new ExecutionFailure(new FatalCommandFailure(message(original), 1), error instanceof ExecutionFailure ? error.io : io, error instanceof ExecutionFailure ? error.diagnostic : void 0);
11180
11767
  }
11181
11768
  if (error instanceof ExecutionFailure) throw error;
@@ -11415,16 +12002,16 @@ var Runtime = class _Runtime {
11415
12002
  }
11416
12003
  if (selected?.kind === "builtin") {
11417
12004
  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) };
12005
+ const special2 = state.profile === "sh" && !bypassFunctions && specialBuiltinNames.has(context2.command);
12006
+ if (special2) assignments2.clear();
12007
+ if (context2.command === "." || context2.command === "source") return { exitCode: await this.sourceBuiltin(context2, state, { ...io, ...context2 }, special2) };
12008
+ if (context2.command === "eval") return { exitCode: await this.evalBuiltin(context2, state, { ...io, ...context2 }, special2) };
11422
12009
  const builtinWork = this.builtin(context2, state, assignments2, (error, diagnostic2) => {
11423
12010
  builtinFailure = { error, diagnostic: diagnostic2 };
11424
12011
  }, bypassFunctions);
11425
12012
  const builtin = arrayStore(state) ? await interruptible(builtinWork, this.signal) : await builtinWork;
11426
12013
  if (builtin !== void 0) {
11427
- if (special && builtin !== 0 && context2.command !== "shift") throw new Flow("exit", builtin);
12014
+ if (special2 && builtin !== 0 && context2.command !== "shift") throw new Flow("exit", builtin);
11428
12015
  return { exitCode: builtin };
11429
12016
  }
11430
12017
  }
@@ -11736,7 +12323,7 @@ ${prefix2} line ${offset + line}: \`${source.split("\n")[line - 1] ?? ""}'
11736
12323
  let status = 0;
11737
12324
  let lines2 = 0;
11738
12325
  while (true) {
11739
- if (++lines2 % 32 === 0) await interruptible(new Promise((resolve) => setImmediate(resolve)), this.signal);
12326
+ if (++lines2 % 32 === 0) await yieldTurn(this.signal);
11740
12327
  this.signal.throwIfAborted();
11741
12328
  const bytes = await input2.sourceLine();
11742
12329
  const eof = bytes === void 0;
@@ -12056,13 +12643,13 @@ ${prefix2} line ${offset + line}: \`${source.split("\n")[line - 1] ?? ""}'
12056
12643
  return status2;
12057
12644
  }
12058
12645
  }
12059
- async evalBuiltin(context, state, io, special) {
12646
+ async evalBuiltin(context, state, io, special2) {
12060
12647
  const args = [...context.args];
12061
12648
  if (args[0] === "--") args.shift();
12062
12649
  else if (args[0]?.startsWith("-") && args[0] !== "-") {
12063
12650
  await this.diagnostic(io, `eval: -${args[0][1]}: invalid option`);
12064
12651
  await writeText(io.stderr, "eval: usage: eval [arg ...]\n");
12065
- if (special) throw new Flow("exit", 2);
12652
+ if (special2) throw new Flow("exit", 2);
12066
12653
  return 2;
12067
12654
  }
12068
12655
  if (!args.length) return 0;
@@ -12078,7 +12665,7 @@ ${prefix2} line ${offset + line}: \`${source.split("\n")[line - 1] ?? ""}'
12078
12665
  throw error;
12079
12666
  }
12080
12667
  try {
12081
- return await this.runCurrentText(source, state, { ...io, diagnosticOffset: (io.diagnosticLine ?? 1) - 1 }, special, `${io.scriptName ?? "shell"}: eval`);
12668
+ return await this.runCurrentText(source, state, { ...io, diagnosticOffset: (io.diagnosticLine ?? 1) - 1 }, special2, `${io.scriptName ?? "shell"}: eval`);
12082
12669
  } finally {
12083
12670
  const restore = () => {
12084
12671
  state.depth--;
@@ -12087,12 +12674,12 @@ ${prefix2} line ${offset + line}: \`${source.split("\n")[line - 1] ?? ""}'
12087
12674
  else restore();
12088
12675
  }
12089
12676
  }
12090
- async sourceBuiltin(context, state, io, special) {
12677
+ async sourceBuiltin(context, state, io, special2) {
12091
12678
  const args = [...context.args];
12092
12679
  if (args[0] === "--") args.shift();
12093
12680
  else if (args[0]?.startsWith("-") && args[0] !== "-") {
12094
12681
  await this.diagnostic(io, `${context.command}: ${args[0]}: unsupported option`);
12095
- if (special) throw new Flow("exit", 2);
12682
+ if (special2) throw new Flow("exit", 2);
12096
12683
  return 2;
12097
12684
  }
12098
12685
  const filename = args.shift();
@@ -12100,7 +12687,7 @@ ${prefix2} line ${offset + line}: \`${source.split("\n")[line - 1] ?? ""}'
12100
12687
  await this.diagnostic(io, `${context.command}: filename argument required`);
12101
12688
  await writeText(io.stderr, `${context.command}: usage: ${context.command} [-p path] filename [arguments]
12102
12689
  `);
12103
- if (special) throw new Flow("exit", 2);
12690
+ if (special2) throw new Flow("exit", 2);
12104
12691
  return 2;
12105
12692
  }
12106
12693
  if (state.depth >= this.budget.limits.maxSubstitutionDepth) this.budget.fail("maxSubstitutionDepth");
@@ -12146,7 +12733,7 @@ ${prefix2} line ${offset + line}: \`${source.split("\n")[line - 1] ?? ""}'
12146
12733
  if (error instanceof ShellLimitError) throw error;
12147
12734
  if (errorCode(error) === "EFBIG") this.budget.fail("maxSourceBytes");
12148
12735
  const diagnostic2 = error instanceof CommandFailure ? error.message : filesystemDiagnostic(error, target) ?? `${target}: ${message(error)}`;
12149
- if (special) throw new FatalCommandFailure(diagnostic2, 1);
12736
+ if (special2) throw new FatalCommandFailure(diagnostic2, 1);
12150
12737
  throw new CommandFailure(diagnostic2, error instanceof CommandFailure ? error.status : 1);
12151
12738
  }
12152
12739
  const positional = state.positional;
@@ -12175,7 +12762,7 @@ ${prefix2} line ${offset + line}: \`${source.split("\n")[line - 1] ?? ""}'
12175
12762
  throw error;
12176
12763
  }
12177
12764
  try {
12178
- return await this.runCurrentText(source, state, { ...io, scriptName: target, diagnosticOffset: 0, diagnosticLine: 1 }, special);
12765
+ return await this.runCurrentText(source, state, { ...io, scriptName: target, diagnosticOffset: 0, diagnosticLine: 1 }, special2);
12179
12766
  } catch (error) {
12180
12767
  if (error instanceof Flow && error.kind === "return") return error.status;
12181
12768
  throw error;
@@ -12317,7 +12904,7 @@ ${prefix2} line ${offset + line}: \`${source.split("\n")[line - 1] ?? ""}'
12317
12904
  };
12318
12905
  const checkpoint = async () => {
12319
12906
  this.signal.throwIfAborted();
12320
- await interruptible(new Promise((resolve) => setImmediate(resolve)), this.signal);
12907
+ await yieldTurn(this.signal);
12321
12908
  this.signal.throwIfAborted();
12322
12909
  };
12323
12910
  const { args } = context;
@@ -12362,7 +12949,7 @@ ${prefix2} line ${offset + line}: \`${source.split("\n")[line - 1] ?? ""}'
12362
12949
  };
12363
12950
  const checkpoint = async () => {
12364
12951
  this.signal.throwIfAborted();
12365
- await interruptible(new Promise((resolve) => setImmediate(resolve)), this.signal);
12952
+ await yieldTurn(this.signal);
12366
12953
  this.signal.throwIfAborted();
12367
12954
  };
12368
12955
  if (!Array.isArray(context.args)) throw new CommandFailure("getopts: argument array required", 2);
@@ -13395,7 +13982,7 @@ ${prefix2} line ${offset + line}: \`${source.split("\n")[line - 1] ?? ""}'
13395
13982
  const match = async (start, end) => {
13396
13983
  work.remaining -= end - start + 1;
13397
13984
  if (work.remaining < 0) work.exhausted();
13398
- if (++attempts % 256 === 0) await new Promise((resolve) => setImmediate(resolve));
13985
+ if (++attempts % 256 === 0) await yieldTurn(this.signal);
13399
13986
  this.signal.throwIfAborted();
13400
13987
  if (owner) await owner.ledger.checkpoint(this.signal, end - start + 1);
13401
13988
  return matches(slice(start, end));
@@ -13904,7 +14491,7 @@ var ShellInput = class _ShellInput {
13904
14491
  let length = 0;
13905
14492
  let pulls = 0;
13906
14493
  while (true) {
13907
- if (++pulls % 128 === 0) await interruptible(new Promise((resolve) => setImmediate(resolve)), this.signal);
14494
+ if (++pulls % 128 === 0) await yieldTurn(this.signal);
13908
14495
  const result = await this.#cursor.take(this.signal);
13909
14496
  if (result.done) {
13910
14497
  if (!length) return void 0;
@@ -13946,7 +14533,7 @@ var ShellInput = class _ShellInput {
13946
14533
  try {
13947
14534
  while (!terminated) {
13948
14535
  if (offset === chunk.length) {
13949
- if (++pulls % 128 === 0) await interruptible(new Promise((resolve) => setImmediate(resolve)), this.signal);
14536
+ if (++pulls % 128 === 0) await yieldTurn(this.signal);
13950
14537
  const result = await this.#cursor.take(this.signal);
13951
14538
  if (result.done) {
13952
14539
  break;
@@ -13962,7 +14549,7 @@ var ShellInput = class _ShellInput {
13962
14549
  }
13963
14550
  if (++length > this.budget.limits.maxOutputBytes) this.budget.fail("maxOutputBytes");
13964
14551
  if (length % 1024 === 0) {
13965
- await interruptible(new Promise((resolve) => setImmediate(resolve)), this.signal);
14552
+ await yieldTurn(this.signal);
13966
14553
  this.signal.throwIfAborted();
13967
14554
  }
13968
14555
  if (byte === 0) continue;
@@ -14155,6 +14742,7 @@ var Shell = class {
14155
14742
  #active = /* @__PURE__ */ new Set();
14156
14743
  constructor(options2) {
14157
14744
  if (!options2?.fs) throw new TypeError("Shell requires an explicit filesystem");
14745
+ warnIfHostProcessEnv(options2.env);
14158
14746
  resolveLimits(options2.limits);
14159
14747
  this.#options = { ...options2, cwd: resolvePath("/", options2.cwd ?? "/"), env: { ...options2.env }, limits: { ...options2.limits } };
14160
14748
  this.commands = options2.commands ?? new CommandRegistry();
@@ -14217,6 +14805,7 @@ var Shell = class {
14217
14805
  }
14218
14806
  async exec(source, options2 = {}) {
14219
14807
  if (this.#disposed) throw new Error("Shell is disposed");
14808
+ warnIfHostProcessEnv(options2.env);
14220
14809
  const budget = new Budget(resolveLimits(this.#options.limits, options2.limits), options2.signal);
14221
14810
  const scope = new InvocationScope(options2.signal);
14222
14811
  const cancellationState = new RuntimeCancellationState();
@@ -14227,7 +14816,6 @@ var Shell = class {
14227
14816
  controls: [{ role: "budget-control", signal: budget.controller.signal }]
14228
14817
  });
14229
14818
  try {
14230
- Runtime.registerEreRoot(budget, scope);
14231
14819
  owner.activate(boundary);
14232
14820
  } catch (error) {
14233
14821
  scope.failures.push(...boundary.close().failures);
@@ -14241,6 +14829,7 @@ var Shell = class {
14241
14829
  try {
14242
14830
  captured = await owner.capture(() => this.#execute(source, options2, scope, budget, boundary, cancellationState, owner));
14243
14831
  } finally {
14832
+ budget.close();
14244
14833
  await scope.close();
14245
14834
  }
14246
14835
  const selection = owner.finish(captured);
@@ -14413,6 +15002,22 @@ shell: -c: line ${line}: \`${source.split("\n")[line - 1] ?? ""}'
14413
15002
  }
14414
15003
  };
14415
15004
 
15005
+ // packages/safe-bash/src/shell/worker-limits.ts
15006
+ init_platform();
15007
+ var cloudflareWorkerLimits = Object.freeze({
15008
+ maxInputBytes: 4 * 1024 * 1024,
15009
+ maxOutputBytes: 4 * 1024 * 1024,
15010
+ maxCommands: 1e3,
15011
+ maxLoopIterations: 1e3,
15012
+ maxSubstitutionDepth: 16,
15013
+ maxSourceBytes: 256 * 1024,
15014
+ maxExpansionFields: 1e3,
15015
+ maxExpansionBytes: 4 * 1024 * 1024,
15016
+ maxWallClockMs: 1e4,
15017
+ maxCpuMs: 1e4,
15018
+ pipeHighWaterMark: 16 * 1024
15019
+ });
15020
+
14416
15021
  // packages/safe-bash/src/fs/memory/index.ts
14417
15022
  init_platform();
14418
15023
  import { createMemoryFileSystem, MemoryFileSystem } from "@poe-platform/safe-fs/core";
@@ -14429,6 +15034,153 @@ import { createMountFileSystem, MountFileSystem } from "@poe-platform/safe-fs/co
14429
15034
  init_platform();
14430
15035
  import { createOverlayFileSystem, OverlayFileSystem } from "@poe-platform/safe-fs/core";
14431
15036
 
15037
+ // packages/safe-bash/src/commands/network/fetch-transport.ts
15038
+ init_platform();
15039
+
15040
+ // packages/safe-bash/src/commands/network/types.ts
15041
+ init_platform();
15042
+ var defaultNetworkLimits = Object.freeze({
15043
+ maxUploadBytes: 64 * 1024 * 1024,
15044
+ maxDownloadBytes: 64 * 1024 * 1024,
15045
+ maxBufferBytes: 8 * 1024 * 1024,
15046
+ maxHeaderBytes: 64 * 1024,
15047
+ maxRedirects: 10,
15048
+ maxRetries: 5,
15049
+ maxUrls: 32,
15050
+ maxTimeMs: 12e4
15051
+ });
15052
+ var cloudflareWorkerNetworkLimits = Object.freeze({
15053
+ maxUploadBytes: 4 * 1024 * 1024,
15054
+ maxDownloadBytes: 4 * 1024 * 1024,
15055
+ maxBufferBytes: 1024 * 1024,
15056
+ maxHeaderBytes: 32 * 1024,
15057
+ maxRedirects: 2,
15058
+ maxRetries: 1,
15059
+ maxUrls: 8,
15060
+ maxTimeMs: 1e4
15061
+ });
15062
+ var CurlError = class extends Error {
15063
+ constructor(exitCode, message2) {
15064
+ super(message2);
15065
+ this.exitCode = exitCode;
15066
+ this.name = "CurlError";
15067
+ }
15068
+ exitCode;
15069
+ };
15070
+
15071
+ // packages/safe-bash/src/commands/network/fetch-transport.ts
15072
+ function requestBody(source, signal) {
15073
+ if (!source) return void 0;
15074
+ const iterator = source[Symbol.asyncIterator]();
15075
+ return new ReadableStream({
15076
+ async pull(controller) {
15077
+ try {
15078
+ signal.throwIfAborted();
15079
+ const next = await iterator.next();
15080
+ if (next.done) controller.close();
15081
+ else controller.enqueue(next.value);
15082
+ } catch (error) {
15083
+ controller.error(error);
15084
+ }
15085
+ },
15086
+ async cancel(reason) {
15087
+ await iterator.return?.(reason);
15088
+ }
15089
+ });
15090
+ }
15091
+ function createFetchTransport(options2 = {}) {
15092
+ const fetchRequest = options2.fetch ?? globalThis.fetch;
15093
+ if (typeof fetchRequest !== "function") throw new TypeError("Fetch is unavailable");
15094
+ return async (input2) => {
15095
+ input2.signal.throwIfAborted();
15096
+ const url = new URL(input2.url);
15097
+ if (url.protocol !== "http:" && url.protocol !== "https:") throw new CurlError(1, "Unsupported protocol");
15098
+ const stopped = new AbortController();
15099
+ const signal = AbortSignal.any([input2.signal, stopped.signal]);
15100
+ const body = requestBody(input2.body, signal);
15101
+ const headers = new Headers();
15102
+ for (const [name2, value2] of input2.headers) headers.append(name2, value2);
15103
+ const request = new Request(url, {
15104
+ method: input2.method,
15105
+ headers,
15106
+ body,
15107
+ credentials: "omit",
15108
+ redirect: "manual",
15109
+ signal,
15110
+ ...body ? { duplex: "half" } : {}
15111
+ });
15112
+ let response;
15113
+ const dispose = async () => {
15114
+ stopped.abort();
15115
+ if (response?.body) await response.body.cancel().catch(() => void 0);
15116
+ };
15117
+ input2.registerCleanup?.(dispose);
15118
+ response = await fetchRequest(request);
15119
+ const reader = response.body?.getReader();
15120
+ const responseBody = reader ? (async function* () {
15121
+ try {
15122
+ while (true) {
15123
+ input2.signal.throwIfAborted();
15124
+ const next = await reader.read();
15125
+ if (next.done) return;
15126
+ yield next.value;
15127
+ }
15128
+ } finally {
15129
+ reader.releaseLock();
15130
+ }
15131
+ })() : (async function* () {
15132
+ })();
15133
+ return {
15134
+ status: response.status,
15135
+ statusText: response.statusText,
15136
+ headers: [...response.headers.entries()],
15137
+ body: responseBody,
15138
+ dispose
15139
+ };
15140
+ };
15141
+ }
15142
+
15143
+ // packages/safe-bash/src/commands/network/authorizer.ts
15144
+ init_platform();
15145
+ function privateHostname(input2) {
15146
+ const hostname = input2.toLowerCase().replace(/\.$/u, "").replace(/^\[|\]$/gu, "");
15147
+ if (hostname === "localhost" || hostname.endsWith(".localhost") || hostname === "::1") return true;
15148
+ if (hostname.includes(":")) {
15149
+ const first2 = hostname.split(":", 1)[0] ?? "";
15150
+ return first2.startsWith("fc") || first2.startsWith("fd") || ["fe8", "fe9", "fea", "feb"].some((prefix2) => first2.startsWith(prefix2));
15151
+ }
15152
+ const parts = hostname.split(".");
15153
+ if (parts.length !== 4 || parts.some((part) => !part || Array.from(part).some((character) => character < "0" || character > "9"))) return false;
15154
+ const octets = parts.map(Number);
15155
+ if (octets.some((value2) => value2 < 0 || value2 > 255)) return false;
15156
+ const [first, second] = octets;
15157
+ return first === 10 || first === 127 || first === 0 || first === 169 && second === 254 || first === 192 && second === 168 || first === 172 && second >= 16 && second <= 31;
15158
+ }
15159
+ function createOriginAuthorizer(allowlist = "*", options2 = {}) {
15160
+ const origins = /* @__PURE__ */ new Set();
15161
+ const hosts = /* @__PURE__ */ new Set();
15162
+ for (const entry of allowlist === "*" ? [] : allowlist) {
15163
+ const value2 = entry.trim();
15164
+ if (!value2) throw new TypeError("Origin allowlist entries must not be empty");
15165
+ if (value2.includes("://")) {
15166
+ const url = new URL(value2);
15167
+ if (url.pathname !== "/" || url.search || url.hash || url.username || url.password) {
15168
+ throw new TypeError(`Origin allowlist entry must be an origin: ${entry}`);
15169
+ }
15170
+ origins.add(url.origin);
15171
+ } else {
15172
+ if (value2.includes("/") || value2.includes("@")) throw new TypeError(`Invalid hostname allowlist entry: ${entry}`);
15173
+ hosts.add(value2.toLowerCase().replace(/\.$/u, ""));
15174
+ }
15175
+ }
15176
+ return (request) => {
15177
+ const url = new URL(request.url);
15178
+ const hostname = url.hostname.toLowerCase().replace(/\.$/u, "");
15179
+ if (options2.denyPrivateNetworks && privateHostname(hostname)) return false;
15180
+ return allowlist === "*" || origins.has(url.origin) || hosts.has(hostname);
15181
+ };
15182
+ }
15183
+
14432
15184
  // packages/safe-bash/src/browser.ts
14433
15185
  function createBrowserCommands() {
14434
15186
  return [
@@ -14467,14 +15219,18 @@ export {
14467
15219
  assertPathWithin,
14468
15220
  basename,
14469
15221
  browserCommands,
15222
+ cloudflareWorkerLimits,
15223
+ cloudflareWorkerNetworkLimits,
14470
15224
  collectBytes2 as collectBytes,
14471
15225
  collectText,
14472
15226
  composeMiddleware,
14473
15227
  createBrowserCommands,
14474
15228
  createBytePipe,
14475
15229
  createCommandArguments,
15230
+ createFetchTransport,
14476
15231
  createMemoryFileSystem,
14477
15232
  createMountFileSystem,
15233
+ createOriginAuthorizer,
14478
15234
  createOutputOperation,
14479
15235
  createOverlayFileSystem,
14480
15236
  createReadOnlyFileSystem,