poe-code 14.0.21 → 14.0.23

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 (161) hide show
  1. package/dist/index.js +1 -1
  2. package/dist/index.js.map +1 -1
  3. package/dist/metafile.json +1 -1
  4. package/package.json +1 -1
  5. package/packages/safe-bash/dist/browser.d.ts +5 -0
  6. package/packages/safe-bash/dist/browser.d.ts.map +1 -1
  7. package/packages/safe-bash/dist/browser.js +981 -134
  8. package/packages/safe-bash/dist/browser.js.map +4 -4
  9. package/packages/safe-bash/dist/commands/apply-patch/shared.js +2 -2
  10. package/packages/safe-bash/dist/commands/apply-patch/shared.js.map +1 -1
  11. package/packages/safe-bash/dist/commands/archive/internal.d.ts.map +1 -1
  12. package/packages/safe-bash/dist/commands/archive/internal.js +3 -2
  13. package/packages/safe-bash/dist/commands/archive/internal.js.map +1 -1
  14. package/packages/safe-bash/dist/commands/bytes/checksums/index.d.ts.map +1 -1
  15. package/packages/safe-bash/dist/commands/bytes/checksums/index.js +3 -2
  16. package/packages/safe-bash/dist/commands/bytes/checksums/index.js.map +1 -1
  17. package/packages/safe-bash/dist/commands/bytes/compression/gunzip.js +2 -2
  18. package/packages/safe-bash/dist/commands/bytes/compression/gunzip.js.map +1 -1
  19. package/packages/safe-bash/dist/commands/bytes/compression/stream.js +2 -2
  20. package/packages/safe-bash/dist/commands/bytes/compression/stream.js.map +1 -1
  21. package/packages/safe-bash/dist/commands/bytes/encoding/shared.js +3 -3
  22. package/packages/safe-bash/dist/commands/bytes/encoding/shared.js.map +1 -1
  23. package/packages/safe-bash/dist/commands/column/internal.d.ts.map +1 -1
  24. package/packages/safe-bash/dist/commands/column/internal.js +2 -1
  25. package/packages/safe-bash/dist/commands/column/internal.js.map +1 -1
  26. package/packages/safe-bash/dist/commands/diff-patch/shared.js +2 -2
  27. package/packages/safe-bash/dist/commands/diff-patch/shared.js.map +1 -1
  28. package/packages/safe-bash/dist/commands/du/budget.d.ts.map +1 -1
  29. package/packages/safe-bash/dist/commands/du/budget.js +3 -2
  30. package/packages/safe-bash/dist/commands/du/budget.js.map +1 -1
  31. package/packages/safe-bash/dist/commands/env-split.d.ts.map +1 -1
  32. package/packages/safe-bash/dist/commands/env-split.js +2 -1
  33. package/packages/safe-bash/dist/commands/env-split.js.map +1 -1
  34. package/packages/safe-bash/dist/commands/expr/internal.d.ts.map +1 -1
  35. package/packages/safe-bash/dist/commands/expr/internal.js +2 -1
  36. package/packages/safe-bash/dist/commands/expr/internal.js.map +1 -1
  37. package/packages/safe-bash/dist/commands/file/shared.d.ts.map +1 -1
  38. package/packages/safe-bash/dist/commands/file/shared.js +3 -2
  39. package/packages/safe-bash/dist/commands/file/shared.js.map +1 -1
  40. package/packages/safe-bash/dist/commands/find.d.ts.map +1 -1
  41. package/packages/safe-bash/dist/commands/find.js +12 -31
  42. package/packages/safe-bash/dist/commands/find.js.map +1 -1
  43. package/packages/safe-bash/dist/commands/html-to-markdown/budget.js +2 -2
  44. package/packages/safe-bash/dist/commands/html-to-markdown/budget.js.map +1 -1
  45. package/packages/safe-bash/dist/commands/metadata/internal.d.ts.map +1 -1
  46. package/packages/safe-bash/dist/commands/metadata/internal.js +2 -1
  47. package/packages/safe-bash/dist/commands/metadata/internal.js.map +1 -1
  48. package/packages/safe-bash/dist/commands/move.d.ts.map +1 -1
  49. package/packages/safe-bash/dist/commands/move.js +2 -1
  50. package/packages/safe-bash/dist/commands/move.js.map +1 -1
  51. package/packages/safe-bash/dist/commands/network/authorizer.d.ts +7 -0
  52. package/packages/safe-bash/dist/commands/network/authorizer.d.ts.map +1 -0
  53. package/packages/safe-bash/dist/commands/network/authorizer.js +48 -0
  54. package/packages/safe-bash/dist/commands/network/authorizer.js.map +1 -0
  55. package/packages/safe-bash/dist/commands/network/body.js +2 -2
  56. package/packages/safe-bash/dist/commands/network/body.js.map +1 -1
  57. package/packages/safe-bash/dist/commands/network/curl.js +2 -2
  58. package/packages/safe-bash/dist/commands/network/curl.js.map +1 -1
  59. package/packages/safe-bash/dist/commands/network/fetch-transport.d.ts +6 -0
  60. package/packages/safe-bash/dist/commands/network/fetch-transport.d.ts.map +1 -0
  61. package/packages/safe-bash/dist/commands/network/fetch-transport.js +82 -0
  62. package/packages/safe-bash/dist/commands/network/fetch-transport.js.map +1 -0
  63. package/packages/safe-bash/dist/commands/network/index.d.ts +2 -0
  64. package/packages/safe-bash/dist/commands/network/index.d.ts.map +1 -1
  65. package/packages/safe-bash/dist/commands/network/index.js +2 -0
  66. package/packages/safe-bash/dist/commands/network/index.js.map +1 -1
  67. package/packages/safe-bash/dist/commands/network/types.d.ts +1 -0
  68. package/packages/safe-bash/dist/commands/network/types.d.ts.map +1 -1
  69. package/packages/safe-bash/dist/commands/network/types.js +10 -0
  70. package/packages/safe-bash/dist/commands/network/types.js.map +1 -1
  71. package/packages/safe-bash/dist/commands/regex-execution/ere/limits.js +2 -2
  72. package/packages/safe-bash/dist/commands/regex-execution/ere/limits.js.map +1 -1
  73. package/packages/safe-bash/dist/commands/safejs/io.js +2 -2
  74. package/packages/safe-bash/dist/commands/safejs/io.js.map +1 -1
  75. package/packages/safe-bash/dist/commands/search/shared.js +2 -2
  76. package/packages/safe-bash/dist/commands/search/shared.js.map +1 -1
  77. package/packages/safe-bash/dist/commands/split/io.js +2 -2
  78. package/packages/safe-bash/dist/commands/split/io.js.map +1 -1
  79. package/packages/safe-bash/dist/commands/stream-format/shared.d.ts.map +1 -1
  80. package/packages/safe-bash/dist/commands/stream-format/shared.js +2 -1
  81. package/packages/safe-bash/dist/commands/stream-format/shared.js.map +1 -1
  82. package/packages/safe-bash/dist/commands/stream-inspection/shared.d.ts.map +1 -1
  83. package/packages/safe-bash/dist/commands/stream-inspection/shared.js +2 -1
  84. package/packages/safe-bash/dist/commands/stream-inspection/shared.js.map +1 -1
  85. package/packages/safe-bash/dist/commands/streams.d.ts.map +1 -1
  86. package/packages/safe-bash/dist/commands/streams.js +114 -13
  87. package/packages/safe-bash/dist/commands/streams.js.map +1 -1
  88. package/packages/safe-bash/dist/commands/structured/limits.js +2 -2
  89. package/packages/safe-bash/dist/commands/structured/limits.js.map +1 -1
  90. package/packages/safe-bash/dist/commands/table-text/internal.d.ts.map +1 -1
  91. package/packages/safe-bash/dist/commands/table-text/internal.js +2 -1
  92. package/packages/safe-bash/dist/commands/table-text/internal.js.map +1 -1
  93. package/packages/safe-bash/dist/commands/text-programs/shared.js +2 -2
  94. package/packages/safe-bash/dist/commands/text-programs/shared.js.map +1 -1
  95. package/packages/safe-bash/dist/commands/tree/io.js +2 -2
  96. package/packages/safe-bash/dist/commands/tree/io.js.map +1 -1
  97. package/packages/safe-bash/dist/contracts/yield.d.ts +13 -0
  98. package/packages/safe-bash/dist/contracts/yield.d.ts.map +1 -0
  99. package/packages/safe-bash/dist/contracts/yield.js +41 -0
  100. package/packages/safe-bash/dist/contracts/yield.js.map +1 -0
  101. package/packages/safe-bash/dist/shell/arithmetic.js +1 -1
  102. package/packages/safe-bash/dist/shell/arithmetic.js.map +1 -1
  103. package/packages/safe-bash/dist/shell/arrays/ledger.d.ts.map +1 -1
  104. package/packages/safe-bash/dist/shell/arrays/ledger.js +2 -9
  105. package/packages/safe-bash/dist/shell/arrays/ledger.js.map +1 -1
  106. package/packages/safe-bash/dist/shell/conditional.d.ts.map +1 -1
  107. package/packages/safe-bash/dist/shell/conditional.js +2 -1
  108. package/packages/safe-bash/dist/shell/conditional.js.map +1 -1
  109. package/packages/safe-bash/dist/shell/env-warning.d.ts +2 -0
  110. package/packages/safe-bash/dist/shell/env-warning.d.ts.map +1 -0
  111. package/packages/safe-bash/dist/shell/env-warning.js +12 -0
  112. package/packages/safe-bash/dist/shell/env-warning.js.map +1 -0
  113. package/packages/safe-bash/dist/shell/index.d.ts +1 -0
  114. package/packages/safe-bash/dist/shell/index.d.ts.map +1 -1
  115. package/packages/safe-bash/dist/shell/index.js +1 -0
  116. package/packages/safe-bash/dist/shell/index.js.map +1 -1
  117. package/packages/safe-bash/dist/shell/input.d.ts.map +1 -1
  118. package/packages/safe-bash/dist/shell/input.js +4 -3
  119. package/packages/safe-bash/dist/shell/input.js.map +1 -1
  120. package/packages/safe-bash/dist/shell/parser.d.ts.map +1 -1
  121. package/packages/safe-bash/dist/shell/parser.js +15 -1
  122. package/packages/safe-bash/dist/shell/parser.js.map +1 -1
  123. package/packages/safe-bash/dist/shell/pattern.d.ts.map +1 -1
  124. package/packages/safe-bash/dist/shell/pattern.js +3 -2
  125. package/packages/safe-bash/dist/shell/pattern.js.map +1 -1
  126. package/packages/safe-bash/dist/shell/runtime.d.ts +3 -2
  127. package/packages/safe-bash/dist/shell/runtime.d.ts.map +1 -1
  128. package/packages/safe-bash/dist/shell/runtime.js +57 -55
  129. package/packages/safe-bash/dist/shell/runtime.js.map +1 -1
  130. package/packages/safe-bash/dist/shell/shell.d.ts.map +1 -1
  131. package/packages/safe-bash/dist/shell/shell.js +4 -1
  132. package/packages/safe-bash/dist/shell/shell.js.map +1 -1
  133. package/packages/safe-bash/dist/shell/types.d.ts +2 -0
  134. package/packages/safe-bash/dist/shell/types.d.ts.map +1 -1
  135. package/packages/safe-bash/dist/shell/types.js.map +1 -1
  136. package/packages/safe-bash/dist/shell/worker-limits.d.ts +3 -0
  137. package/packages/safe-bash/dist/shell/worker-limits.d.ts.map +1 -0
  138. package/packages/safe-bash/dist/shell/worker-limits.js +14 -0
  139. package/packages/safe-bash/dist/shell/worker-limits.js.map +1 -0
  140. package/packages/safe-fs/dist/contracts/filesystem.d.ts +1 -0
  141. package/packages/safe-fs/dist/core.d.ts +1 -0
  142. package/packages/safe-fs/dist/fs/quota/index.d.ts +9 -0
  143. package/packages/safe-fs/dist/index.d.ts +1 -0
  144. package/packages/safe-js/dist/browser/chunks/{chunk-QGN6DKT5.js → chunk-EMJUKJ6C.js} +111 -1
  145. package/packages/safe-js/dist/browser/chunks/chunk-EMJUKJ6C.js.map +7 -0
  146. package/packages/safe-js/dist/browser/safe-fs-core.js +7 -3
  147. package/packages/safe-js/dist/browser/safe-fs.js +7 -3
  148. package/packages/safe-js/dist/chunks/{chunk-7M2SJPSZ.js → chunk-AFVEPD3R.js} +2 -2
  149. package/packages/safe-js/dist/chunks/{chunk-5XRQ3GNN.js → chunk-WGUFH7HF.js} +3 -3
  150. package/packages/safe-js/dist/chunks/{chunk-2QRYPKQQ.js → chunk-WMZQYBLB.js} +111 -1
  151. package/packages/safe-js/dist/chunks/chunk-WMZQYBLB.js.map +7 -0
  152. package/packages/safe-js/dist/cli.js +3 -3
  153. package/packages/safe-js/dist/index.js +3 -3
  154. package/packages/safe-js/dist/safe-fs-core.js +7 -3
  155. package/packages/safe-js/dist/safe-fs-core.js.map +1 -1
  156. package/packages/safe-js/dist/safe-fs-node.js +8 -4
  157. package/packages/safe-js/dist/safe-fs.js +8 -4
  158. package/packages/safe-js/dist/browser/chunks/chunk-QGN6DKT5.js.map +0 -7
  159. package/packages/safe-js/dist/chunks/chunk-2QRYPKQQ.js.map +0 -7
  160. /package/packages/safe-js/dist/chunks/{chunk-7M2SJPSZ.js.map → chunk-AFVEPD3R.js.map} +0 -0
  161. /package/packages/safe-js/dist/chunks/{chunk-5XRQ3GNN.js.map → chunk-WGUFH7HF.js.map} +0 -0
@@ -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;
@@ -3992,32 +4025,128 @@ function streamCommands() {
3992
4025
  }),
3993
4026
  define("tee", async (context) => {
3994
4027
  const parsed = options(context.args, "a", { append: "a" });
3995
- const targets = [];
4028
+ const paths = [];
4029
+ const streams = [];
3996
4030
  let exitCode = 0;
4031
+ let closed = false;
4032
+ let cleanup;
4033
+ const closeStreams = () => {
4034
+ closed = true;
4035
+ return cleanup ??= Promise.allSettled(streams.map(async (target) => {
4036
+ await target.pipe.abort(context.signal.aborted ? context.signal.reason : void 0);
4037
+ await target.task;
4038
+ })).then(() => {
4039
+ });
4040
+ };
4041
+ context.registerCleanup?.(closeStreams);
4042
+ const report = async (target, error) => {
4043
+ target.active = false;
4044
+ if (target.reported) return;
4045
+ target.reported = true;
4046
+ await diagnostic(context, error);
4047
+ exitCode = 1;
4048
+ };
4049
+ const fallback = async (target) => {
4050
+ if (context.fs.capabilities.append === false || target.accepted || !(target.failure instanceof FsError) || target.failure.code !== "ENOTSUP") return false;
4051
+ await context.fs.writeFile(target.path, new Uint8Array(), { flag: target.flag, signal: context.signal });
4052
+ target.active = false;
4053
+ paths.push(target.path);
4054
+ return true;
4055
+ };
4056
+ const recover = async (target, error) => {
4057
+ try {
4058
+ if (await fallback(target)) return;
4059
+ } catch (fallbackError) {
4060
+ context.signal.throwIfAborted();
4061
+ await report(target, fallbackError);
4062
+ return;
4063
+ }
4064
+ await report(target, error);
4065
+ };
4066
+ const streaming = context.fs.capabilities.streamingWrite !== false && typeof context.fs.writeStream === "function";
3997
4067
  for (const operand of parsed.operands) {
3998
4068
  try {
3999
4069
  const path = pathOf(context, operand);
4000
- await context.fs.writeFile(path, new Uint8Array(), { flag: parsed.flags.has("a") ? "a" : "w", signal: context.signal });
4001
- targets.push(path);
4070
+ if (closed) throw new FsError("ECANCELED", { syscall: "tee", path });
4071
+ const flag = parsed.flags.has("a") ? "a" : "w";
4072
+ if (streaming) {
4073
+ const pipe = createBytePipe({ signal: context.signal });
4074
+ const target = {
4075
+ path,
4076
+ flag,
4077
+ pipe,
4078
+ task: Promise.resolve(),
4079
+ failed: false,
4080
+ accepted: false,
4081
+ reported: false,
4082
+ active: true
4083
+ };
4084
+ const source = (async function* () {
4085
+ for await (const chunk of pipe.readable) {
4086
+ target.accepted = true;
4087
+ yield chunk;
4088
+ }
4089
+ })();
4090
+ target.task = context.fs.writeStream(path, source, { flag, signal: context.signal }).catch(async (error) => {
4091
+ target.failed = true;
4092
+ target.failure = error;
4093
+ await pipe.abort(error);
4094
+ });
4095
+ streams.push(target);
4096
+ } else {
4097
+ if (context.fs.capabilities.append === false) {
4098
+ throw new FsError(context.fs.capabilities.readOnly === true ? "EROFS" : "ENOTSUP", { syscall: "tee", path });
4099
+ }
4100
+ await context.fs.writeFile(path, new Uint8Array(), { flag, signal: context.signal });
4101
+ paths.push(path);
4102
+ }
4002
4103
  } catch (error) {
4104
+ context.signal.throwIfAborted();
4003
4105
  await diagnostic(context, error);
4004
4106
  exitCode = 1;
4005
4107
  }
4006
4108
  }
4007
- for await (const chunk of input(context)) {
4008
- await output(context, chunk);
4009
- for (let index = 0; index < targets.length; ) {
4109
+ try {
4110
+ for await (const chunk of input(context)) {
4111
+ await output(context, chunk);
4112
+ for (const target of streams) {
4113
+ if (!target.active) continue;
4114
+ try {
4115
+ await target.pipe.writable.write(chunk);
4116
+ } catch (error) {
4117
+ context.signal.throwIfAborted();
4118
+ await target.task;
4119
+ await recover(target, target.failed ? target.failure : error);
4120
+ }
4121
+ }
4122
+ for (let index = 0; index < paths.length; ) {
4123
+ try {
4124
+ await context.fs.appendFile(paths[index], chunk, { signal: context.signal });
4125
+ index++;
4126
+ } catch (error) {
4127
+ context.signal.throwIfAborted();
4128
+ await diagnostic(context, error);
4129
+ paths.splice(index, 1);
4130
+ exitCode = 1;
4131
+ }
4132
+ }
4133
+ }
4134
+ for (const target of streams) {
4135
+ if (!target.active) continue;
4010
4136
  try {
4011
- await context.fs.appendFile(targets[index], chunk, { signal: context.signal });
4012
- index++;
4137
+ await target.pipe.close();
4138
+ await target.task;
4139
+ if (target.failed) await recover(target, target.failure);
4013
4140
  } catch (error) {
4014
- await diagnostic(context, error);
4015
- targets.splice(index, 1);
4016
- exitCode = 1;
4141
+ context.signal.throwIfAborted();
4142
+ await target.task;
4143
+ await recover(target, target.failed ? target.failure : error);
4017
4144
  }
4018
4145
  }
4146
+ return { exitCode };
4147
+ } finally {
4148
+ await closeStreams();
4019
4149
  }
4020
- return { exitCode };
4021
4150
  }),
4022
4151
  define("tr", async (context) => {
4023
4152
  const parsed = options(context.args, "dscC", { delete: "d", "squeeze-repeats": "s", complement: "c" });
@@ -4444,6 +4573,17 @@ init_platform();
4444
4573
  // packages/safe-bash/src/shell/shell.ts
4445
4574
  init_platform();
4446
4575
 
4576
+ // packages/safe-bash/src/shell/env-warning.ts
4577
+ init_platform();
4578
+ var warned = /* @__PURE__ */ new WeakSet();
4579
+ function warnIfHostProcessEnv(env) {
4580
+ if (!env || typeof process === "undefined" || !process.env) return;
4581
+ if (warned.has(env)) return;
4582
+ if (env !== process.env) return;
4583
+ warned.add(env);
4584
+ 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).");
4585
+ }
4586
+
4447
4587
  // packages/safe-bash/src/shell/parser.ts
4448
4588
  init_platform();
4449
4589
 
@@ -4538,7 +4678,7 @@ function integer2(text) {
4538
4678
  for (const character of match[2]) {
4539
4679
  const digit2 = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ@_".indexOf(base <= 36 ? character.toLowerCase() : character);
4540
4680
  if (digit2 >= base || digit2 < 0) throw new Error("Digit exceeds arithmetic base");
4541
- value2 = value2 * BigInt(base) + BigInt(digit2);
4681
+ value2 = BigInt.asIntN(64, value2 * BigInt(base) + BigInt(digit2));
4542
4682
  }
4543
4683
  return value2;
4544
4684
  }
@@ -4864,7 +5004,7 @@ async function tokens(pattern, work) {
4864
5004
  let steps = 0;
4865
5005
  const tick = () => {
4866
5006
  if (--work.remaining < 0) work.exhausted();
4867
- if (++steps % 1024 === 0) return new Promise((resolve) => setImmediate(resolve)).then(() => work.signal.throwIfAborted());
5007
+ if (++steps % 1024 === 0) return yieldTurn(work.signal);
4868
5008
  return void 0;
4869
5009
  };
4870
5010
  for (let index = 0; index < characters.length; index++) {
@@ -4941,7 +5081,7 @@ async function matchTokens(patternTokens, value2, work) {
4941
5081
  while (position < characters.length) {
4942
5082
  if (--work.remaining < 0) work.exhausted();
4943
5083
  if (++steps % 1024 === 0) {
4944
- await new Promise((resolve) => setImmediate(resolve));
5084
+ await yieldTurn(work.signal);
4945
5085
  work.signal.throwIfAborted();
4946
5086
  }
4947
5087
  const token = patternTokens[tokenIndex];
@@ -4974,7 +5114,7 @@ async function charge(context, amount = 1) {
4974
5114
  const previous = context.work.remaining;
4975
5115
  context.work.remaining -= amount;
4976
5116
  if (previous >>> 10 !== context.work.remaining >>> 10) {
4977
- await new Promise((resolve) => setImmediate(resolve));
5117
+ await yieldTurn();
4978
5118
  context.signal.throwIfAborted();
4979
5119
  }
4980
5120
  }
@@ -5176,6 +5316,7 @@ var Lexer = class {
5176
5316
  this.documentLine = documentLine;
5177
5317
  this.partial = partial;
5178
5318
  if (depth > 64) throw new ShellSyntaxError("Syntax nesting exceeds 64", 0);
5319
+ for (let offset = source.indexOf("\n"); offset !== -1; offset = source.indexOf("\n", offset + 1)) this.newlineOffsets.push(offset);
5179
5320
  }
5180
5321
  source;
5181
5322
  depth;
@@ -5191,8 +5332,16 @@ var Lexer = class {
5191
5332
  conditional = false;
5192
5333
  conditionalPattern;
5193
5334
  documents = [];
5335
+ newlineOffsets = [];
5194
5336
  lineAt(position) {
5195
- return this.lineOffset + this.source.slice(0, position).split("\n").length;
5337
+ let low = 0;
5338
+ let high = this.newlineOffsets.length;
5339
+ while (low < high) {
5340
+ const middle = low + Math.floor((high - low) / 2);
5341
+ if (this.newlineOffsets[middle] < position) low = middle + 1;
5342
+ else high = middle;
5343
+ }
5344
+ return this.lineOffset + low + 1;
5196
5345
  }
5197
5346
  error(message2, unclosedQuote) {
5198
5347
  throw new ShellSyntaxError(message2, this.position, 2, void 0, unclosedQuote);
@@ -6614,7 +6763,7 @@ var SplitWork = class {
6614
6763
  return this.work >= this.nextYield;
6615
6764
  }
6616
6765
  async pause() {
6617
- await new Promise((resolve) => setImmediate(resolve));
6766
+ await yieldTurn();
6618
6767
  this.signal.throwIfAborted();
6619
6768
  this.nextYield = this.work + 4096;
6620
6769
  }
@@ -7884,14 +8033,7 @@ var ArrayLedger = class _ArrayLedger {
7884
8033
  this.#checkpoint += units;
7885
8034
  if (this.#checkpoint >= 128) {
7886
8035
  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
- }));
8036
+ return yieldTurn(signal);
7895
8037
  }
7896
8038
  }
7897
8039
  };
@@ -8768,43 +8910,17 @@ async function publishPipelineStatus(state, statuses, signal, scope) {
8768
8910
  throwCleanupFailures(scope.failures);
8769
8911
  }
8770
8912
 
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
8913
+ // packages/safe-bash/src/commands/regex-execution/ere/errors.ts
8780
8914
  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;
8915
+ var EreSyntaxError = class extends Error {
8916
+ constructor(message2, offset) {
8917
+ super(`invalid ERE at ${offset}: ${message2}`);
8798
8918
  this.offset = offset;
8799
- this.name = "EreTransportSemanticError";
8919
+ this.name = "EreSyntaxError";
8800
8920
  }
8801
- category;
8802
8921
  offset;
8803
8922
  status = 2;
8804
8923
  };
8805
-
8806
- // packages/safe-bash/src/commands/regex-execution/ere/errors.ts
8807
- init_platform();
8808
8924
  var EreUnsupportedError = class extends Error {
8809
8925
  constructor(message2, offset) {
8810
8926
  super(`unsupported ERE profile at ${offset}: ${message2}`);
@@ -8825,6 +8941,564 @@ var EreProfileLimitError = class extends Error {
8825
8941
  limit;
8826
8942
  status = 3;
8827
8943
  };
8944
+ var EreUsageUnknownError = class extends Error {
8945
+ status = 3;
8946
+ constructor(reason) {
8947
+ super("ERE invocation usage is unknown; further work is refused", { cause: reason });
8948
+ this.name = "EreUsageUnknownError";
8949
+ }
8950
+ };
8951
+
8952
+ // packages/safe-bash/src/commands/regex-execution/ere/limits.ts
8953
+ init_platform();
8954
+ var resources = Object.freeze([
8955
+ "patternBytes",
8956
+ "subjectBytes",
8957
+ "work",
8958
+ "states",
8959
+ "allocationUnits",
8960
+ "captureBytes",
8961
+ "captureSlots"
8962
+ ]);
8963
+ function integer5(value2) {
8964
+ if (!Number.isSafeInteger(value2) || value2 < 0) throw new TypeError("ERE bounds must be nonnegative safe integers");
8965
+ }
8966
+ function multiply(value2, factor, ceiling) {
8967
+ return value2 > Math.floor(ceiling / factor) ? ceiling : value2 * factor;
8968
+ }
8969
+ function deriveEreLimits(bounds) {
8970
+ integer5(bounds.maxExpansionBytes);
8971
+ integer5(bounds.maxExpansionFields);
8972
+ const bytes = bounds.maxExpansionBytes;
8973
+ const fields = bounds.maxExpansionFields;
8974
+ const byteUnits = multiply(bytes, 8, 4e6);
8975
+ const fieldUnits = multiply(fields, 128, 4e6);
8976
+ return Object.freeze({
8977
+ patternBytes: Math.min(bytes, 65536),
8978
+ subjectBytes: Math.min(bytes, 1048576),
8979
+ work: multiply(bytes, 32, 5e7),
8980
+ states: multiply(fields, 8, 65536),
8981
+ allocationUnits: byteUnits >= 4e6 - fieldUnits ? 4e6 : byteUnits + fieldUnits,
8982
+ captureBytes: bytes,
8983
+ captureSlots: fields
8984
+ });
8985
+ }
8986
+ var EreLedger = class {
8987
+ limits;
8988
+ #usage = {
8989
+ patternBytes: 0,
8990
+ subjectBytes: 0,
8991
+ work: 0,
8992
+ states: 0,
8993
+ allocationUnits: 0,
8994
+ captureBytes: 0,
8995
+ captureSlots: 0
8996
+ };
8997
+ #poison;
8998
+ #lastYield = 0;
8999
+ constructor(bounds, lowering = {}) {
9000
+ const limits = { ...deriveEreLimits(bounds) };
9001
+ for (const resource of Object.keys(lowering)) {
9002
+ if (!resources.includes(resource)) throw new TypeError("unknown ERE limit");
9003
+ const key = resource;
9004
+ const value2 = lowering[key];
9005
+ if (value2 === void 0) throw new TypeError("undefined ERE limit");
9006
+ integer5(value2);
9007
+ if (value2 > limits[key]) throw new RangeError("ERE limits may only be lowered");
9008
+ limits[key] = value2;
9009
+ }
9010
+ this.limits = Object.freeze(limits);
9011
+ }
9012
+ get usage() {
9013
+ return Object.freeze({ ...this.#usage });
9014
+ }
9015
+ check(signal) {
9016
+ if (signal?.aborted) throw signal.reason;
9017
+ if (this.#poison) throw this.#poison;
9018
+ }
9019
+ charge(resource, amount, signal) {
9020
+ this.check(signal);
9021
+ integer5(amount);
9022
+ if (amount > this.limits[resource] - this.#usage[resource]) {
9023
+ throw new EreProfileLimitError(resource, this.limits[resource]);
9024
+ }
9025
+ this.#usage[resource] += amount;
9026
+ }
9027
+ admitInput(resource, length, signal) {
9028
+ this.check(signal);
9029
+ integer5(length);
9030
+ if (length > this.limits[resource]) throw new EreProfileLimitError(resource, this.limits[resource]);
9031
+ this.#usage[resource] = Math.max(this.#usage[resource], length);
9032
+ }
9033
+ async checkpoint(signal) {
9034
+ this.check(signal);
9035
+ if (this.#usage.work - this.#lastYield >= 256) {
9036
+ this.#lastYield = this.#usage.work;
9037
+ await yieldTurn(signal);
9038
+ this.check(signal);
9039
+ }
9040
+ }
9041
+ markUnknownUsage(reason) {
9042
+ this.#poison ??= new EreUsageUnknownError(reason);
9043
+ }
9044
+ };
9045
+
9046
+ // packages/safe-bash/src/commands/regex-execution/ere/syntax.ts
9047
+ init_platform();
9048
+ var programs = /* @__PURE__ */ new WeakMap();
9049
+ var special = "\\.^$[]()|*+?{}";
9050
+ var classes = /* @__PURE__ */ new Set(["alnum", "alpha", "blank", "cntrl", "digit", "graph", "lower", "print", "punct", "space", "upper", "xdigit"]);
9051
+ function classMember(name2, code) {
9052
+ const upper = code >= 65 && code <= 90;
9053
+ const lower = code >= 97 && code <= 122;
9054
+ const digit2 = code >= 48 && code <= 57;
9055
+ switch (name2) {
9056
+ case "alnum":
9057
+ return upper || lower || digit2;
9058
+ case "alpha":
9059
+ return upper || lower;
9060
+ case "blank":
9061
+ return code === 9 || code === 32;
9062
+ case "cntrl":
9063
+ return code < 32 || code === 127;
9064
+ case "digit":
9065
+ return digit2;
9066
+ case "graph":
9067
+ return code >= 33 && code <= 126;
9068
+ case "lower":
9069
+ return lower;
9070
+ case "print":
9071
+ return code >= 32 && code <= 126;
9072
+ case "punct":
9073
+ return code >= 33 && code <= 126 && !upper && !lower && !digit2;
9074
+ case "space":
9075
+ return code === 32 || code >= 9 && code <= 13;
9076
+ case "upper":
9077
+ return upper;
9078
+ case "xdigit":
9079
+ return digit2 || code >= 65 && code <= 70 || code >= 97 && code <= 102;
9080
+ default:
9081
+ return false;
9082
+ }
9083
+ }
9084
+ async function admitAscii(text, ledger, signal) {
9085
+ for (let offset = 0; offset < text.length; offset++) {
9086
+ ledger.charge("work", 1, signal);
9087
+ const code = text.charCodeAt(offset);
9088
+ if (code === 0 || code > 127) throw new EreUnsupportedError("only non-NUL ASCII in the C/POSIX profile", offset);
9089
+ await ledger.checkpoint(signal);
9090
+ }
9091
+ }
9092
+ async function flatten(input2, ledger, signal) {
9093
+ if (typeof input2 === "string") {
9094
+ ledger.admitInput("patternBytes", input2.length, signal);
9095
+ await admitAscii(input2, ledger, signal);
9096
+ return { pattern: input2, quoted: null };
9097
+ }
9098
+ let size = 0;
9099
+ for (const fragment of input2) {
9100
+ if (typeof fragment.text !== "string" || typeof fragment.literal !== "boolean") throw new TypeError("invalid ERE fragment");
9101
+ ledger.charge("work", 1, signal);
9102
+ await ledger.checkpoint(signal);
9103
+ ledger.admitInput("patternBytes", fragment.text.length, signal);
9104
+ await admitAscii(fragment.text, ledger, signal);
9105
+ if (fragment.text.length > ledger.limits.patternBytes - size) ledger.admitInput("patternBytes", ledger.limits.patternBytes + 1, signal);
9106
+ size += fragment.text.length;
9107
+ ledger.admitInput("patternBytes", size, signal);
9108
+ }
9109
+ ledger.charge("allocationUnits", size * 2 + input2.length + 2, signal);
9110
+ const output2 = [];
9111
+ const quoted = [];
9112
+ for (const fragment of input2) {
9113
+ for (let offset = 0; offset < fragment.text.length; offset++) {
9114
+ ledger.charge("work", 1, signal);
9115
+ await ledger.checkpoint(signal);
9116
+ quoted.push(fragment.literal);
9117
+ }
9118
+ output2.push(fragment.text);
9119
+ await ledger.checkpoint(signal);
9120
+ }
9121
+ return { pattern: output2.join(""), quoted: Object.freeze(quoted) };
9122
+ }
9123
+ var Parser2 = class {
9124
+ constructor(pattern, quoted, ledger, signal) {
9125
+ this.pattern = pattern;
9126
+ this.quoted = quoted;
9127
+ this.ledger = ledger;
9128
+ this.signal = signal;
9129
+ }
9130
+ pattern;
9131
+ quoted;
9132
+ ledger;
9133
+ signal;
9134
+ offset = 0;
9135
+ groups = 0;
9136
+ nodes = 0;
9137
+ at(character, offset = this.offset) {
9138
+ return !this.quoted?.[offset] && this.pattern[offset] === character;
9139
+ }
9140
+ node(create) {
9141
+ if (this.nodes >= 4096) throw new EreUnsupportedError("4096-node grammar ceiling", this.offset);
9142
+ this.ledger.charge("allocationUnits", 8, this.signal);
9143
+ this.nodes++;
9144
+ return Object.freeze(create());
9145
+ }
9146
+ async expression(depth) {
9147
+ if (depth > 64) throw new EreUnsupportedError("64-level grammar ceiling", this.offset);
9148
+ this.ledger.charge("allocationUnits", 1, this.signal);
9149
+ const alternatives = [];
9150
+ while (true) {
9151
+ const child = await this.sequence(depth);
9152
+ this.ledger.charge("allocationUnits", 1, this.signal);
9153
+ alternatives.push(child);
9154
+ if (!this.at("|")) break;
9155
+ this.offset++;
9156
+ }
9157
+ if (alternatives.length === 1) return alternatives[0];
9158
+ this.ledger.charge("work", alternatives.length * 2, this.signal);
9159
+ await this.ledger.checkpoint(this.signal);
9160
+ return this.node(() => ({ kind: "alternative", children: Object.freeze(alternatives), nullable: alternatives.some((value2) => value2.nullable), captured: alternatives.some((value2) => value2.captured) }));
9161
+ }
9162
+ async sequence(depth) {
9163
+ this.ledger.charge("allocationUnits", 1, this.signal);
9164
+ const children = [];
9165
+ while (this.offset < this.pattern.length && !this.at("|") && !this.at(")")) {
9166
+ this.ledger.charge("work", 1, this.signal);
9167
+ await this.ledger.checkpoint(this.signal);
9168
+ let child = await this.atom(depth);
9169
+ const operator = this.quoted?.[this.offset] ? void 0 : this.pattern[this.offset];
9170
+ if (operator === "*" || operator === "+" || operator === "?" || operator === "{") {
9171
+ const begin = this.offset++;
9172
+ let min = operator === "+" ? 1 : 0;
9173
+ let max = operator === "?" ? 1 : Infinity;
9174
+ if (operator === "{") {
9175
+ min = await this.count();
9176
+ max = min;
9177
+ if (this.at(",")) {
9178
+ this.offset++;
9179
+ max = this.at("}") ? Infinity : await this.count();
9180
+ }
9181
+ if (!this.at("}") || max < min) throw new EreSyntaxError("invalid interval", begin);
9182
+ this.offset++;
9183
+ }
9184
+ if (child.kind === "start" || child.kind === "end") throw new EreUnsupportedError("repeated anchor", begin);
9185
+ if (child.nullable && child.captured && max > 1) throw new EreUnsupportedError("nullable captured repetition", begin);
9186
+ const repeated = child;
9187
+ child = this.node(() => ({ kind: "repeat", child: repeated, min, max, nullable: min === 0 || repeated.nullable, captured: repeated.captured }));
9188
+ const next = this.quoted?.[this.offset] ? void 0 : this.pattern[this.offset];
9189
+ if (next === "*" || next === "+" || next === "?" || next === "{") throw new EreUnsupportedError("stacked repetition", this.offset);
9190
+ }
9191
+ this.ledger.charge("allocationUnits", 1, this.signal);
9192
+ children.push(child);
9193
+ }
9194
+ if (children.length === 0) return this.node(() => ({ kind: "empty", nullable: true, captured: false }));
9195
+ if (children.length === 1) return children[0];
9196
+ this.ledger.charge("work", children.length * 2, this.signal);
9197
+ await this.ledger.checkpoint(this.signal);
9198
+ return this.node(() => ({ kind: "sequence", children: Object.freeze(children), nullable: children.every((value2) => value2.nullable), captured: children.some((value2) => value2.captured) }));
9199
+ }
9200
+ async count() {
9201
+ const begin = this.offset;
9202
+ let value2 = 0;
9203
+ while (!this.quoted?.[this.offset] && this.pattern[this.offset] !== void 0 && this.pattern[this.offset] >= "0" && this.pattern[this.offset] <= "9") {
9204
+ this.ledger.charge("work", 1, this.signal);
9205
+ await this.ledger.checkpoint(this.signal);
9206
+ value2 = value2 * 10 + this.pattern.charCodeAt(this.offset++) - 48;
9207
+ if (value2 > 255) throw new EreUnsupportedError("interval counts exceed 255", begin);
9208
+ }
9209
+ if (begin === this.offset) throw new EreSyntaxError("missing interval count", begin);
9210
+ return value2;
9211
+ }
9212
+ async atom(depth) {
9213
+ const begin = this.offset;
9214
+ const character = this.pattern[this.offset++];
9215
+ if (this.quoted?.[begin]) return this.node(() => ({ kind: "literal", code: character.charCodeAt(0), nullable: false, captured: false }));
9216
+ if (character === "(") {
9217
+ if (this.at("?")) throw new EreUnsupportedError("extended group syntax", begin);
9218
+ if (this.groups >= 32) throw new EreUnsupportedError("32-group grammar ceiling", begin);
9219
+ const index = ++this.groups;
9220
+ const child = await this.expression(depth + 1);
9221
+ if (!this.at(")")) throw new EreSyntaxError("unclosed group", begin);
9222
+ this.offset++;
9223
+ return this.node(() => ({ kind: "group", index, child, nullable: child.nullable, captured: true }));
9224
+ }
9225
+ if (character === "[") return this.set(begin);
9226
+ if (character === "\\") {
9227
+ const escaped = this.pattern[this.offset++];
9228
+ if (escaped === void 0) throw new EreSyntaxError("trailing escape", begin);
9229
+ if (!special.includes(escaped)) throw new EreUnsupportedError("backreference or escape extension", begin);
9230
+ return this.node(() => ({ kind: "literal", code: escaped.charCodeAt(0), nullable: false, captured: false }));
9231
+ }
9232
+ if (character === "*" || character === "+" || character === "?" || character === "{") throw new EreSyntaxError("repetition without operand", begin);
9233
+ if (character === ".") return this.node(() => ({ kind: "dot", nullable: false, captured: false }));
9234
+ if (character === "^") return this.node(() => ({ kind: "start", nullable: true, captured: false }));
9235
+ if (character === "$") return this.node(() => ({ kind: "end", nullable: true, captured: false }));
9236
+ return this.node(() => ({ kind: "literal", code: character.charCodeAt(0), nullable: false, captured: false }));
9237
+ }
9238
+ async set(begin) {
9239
+ this.ledger.charge("allocationUnits", 128, this.signal);
9240
+ const members = new Array(128).fill(false);
9241
+ const negate = this.at("^");
9242
+ if (negate) this.offset++;
9243
+ let first = true;
9244
+ while (this.offset < this.pattern.length) {
9245
+ this.ledger.charge("work", 1, this.signal);
9246
+ await this.ledger.checkpoint(this.signal);
9247
+ if (this.at("]") && !first) {
9248
+ this.offset++;
9249
+ if (negate) for (let code = 1; code < 128; code++) {
9250
+ this.ledger.charge("work", 1, this.signal);
9251
+ await this.ledger.checkpoint(this.signal);
9252
+ members[code] = !members[code];
9253
+ }
9254
+ return this.node(() => ({ kind: "set", members: Object.freeze(members), nullable: false, captured: false }));
9255
+ }
9256
+ first = false;
9257
+ if (this.at("[") && (this.at(".", this.offset + 1) || this.at("=", this.offset + 1))) {
9258
+ throw new EreUnsupportedError("collating or equivalence element", this.offset);
9259
+ }
9260
+ if (this.at("[") && this.at(":", this.offset + 1)) {
9261
+ const classBegin = this.offset;
9262
+ this.offset += 2;
9263
+ let name2 = "";
9264
+ while (this.offset < this.pattern.length && !this.at(":")) {
9265
+ this.ledger.charge("work", 1, this.signal);
9266
+ await this.ledger.checkpoint(this.signal);
9267
+ if (name2.length >= 6) throw new EreSyntaxError("unknown character class", classBegin);
9268
+ name2 += this.pattern[this.offset++];
9269
+ }
9270
+ if (!this.at(":") || !this.at("]", this.offset + 1) || !classes.has(name2)) throw new EreSyntaxError("unknown character class", classBegin);
9271
+ this.offset += 2;
9272
+ for (let code = 1; code < 128; code++) {
9273
+ this.ledger.charge("work", 1, this.signal);
9274
+ await this.ledger.checkpoint(this.signal);
9275
+ if (classMember(name2, code)) members[code] = true;
9276
+ }
9277
+ if (this.at("-") && !this.at("]", this.offset + 1)) throw new EreSyntaxError("class cannot be a range endpoint", this.offset);
9278
+ } else {
9279
+ const lower = this.pattern.charCodeAt(this.offset++);
9280
+ if (this.at("-") && !this.at("]", this.offset + 1) && this.pattern[this.offset + 1] !== void 0) {
9281
+ this.offset++;
9282
+ if (this.at("[")) throw new EreSyntaxError("nonliteral range endpoint", this.offset);
9283
+ const upper = this.pattern.charCodeAt(this.offset++);
9284
+ if (lower > upper) throw new EreSyntaxError("descending range", this.offset - 3);
9285
+ for (let code = lower; code <= upper; code++) {
9286
+ this.ledger.charge("work", 1, this.signal);
9287
+ await this.ledger.checkpoint(this.signal);
9288
+ members[code] = true;
9289
+ }
9290
+ } else members[lower] = true;
9291
+ }
9292
+ }
9293
+ throw new EreSyntaxError("unclosed bracket expression", begin);
9294
+ }
9295
+ };
9296
+ async function compileEre(input2, ledger, signal) {
9297
+ ledger.check(signal);
9298
+ const { pattern, quoted } = await flatten(input2, ledger, signal);
9299
+ const parser = new Parser2(pattern, quoted, ledger, signal);
9300
+ const root = await parser.expression(0);
9301
+ if (parser.offset !== pattern.length) throw new EreSyntaxError("unmatched closing group", parser.offset);
9302
+ ledger.charge("allocationUnits", 3, signal);
9303
+ const program = Object.freeze({ pattern, groups: parser.groups });
9304
+ programs.set(program, { root, ledger });
9305
+ return program;
9306
+ }
9307
+ function resolveEreProgram(program, ledger) {
9308
+ const entry = programs.get(program);
9309
+ if (!entry || entry.ledger !== ledger) throw new TypeError("ERE program is not bound to this invocation ledger");
9310
+ return entry.root;
9311
+ }
9312
+
9313
+ // packages/safe-bash/src/commands/regex-execution/ere/matcher.ts
9314
+ init_platform();
9315
+ function spanOrder(left, right) {
9316
+ if (left === null) return right === null ? 0 : -1;
9317
+ if (right === null) return 1;
9318
+ const length = left.end - left.start - (right.end - right.start);
9319
+ return length === 0 ? right.start - left.start : length;
9320
+ }
9321
+ async function historyOrder(left, right, ledger, signal) {
9322
+ const leftCount = left?.count ?? 0;
9323
+ const rightCount = right?.count ?? 0;
9324
+ for (let ordinal = 1; ordinal <= Math.min(leftCount, rightCount); ordinal++) {
9325
+ ledger.charge("work", 1, signal);
9326
+ await ledger.checkpoint(signal);
9327
+ let leftEntry = left;
9328
+ let rightEntry = right;
9329
+ for (let remaining = leftCount; remaining > ordinal; remaining--) {
9330
+ ledger.charge("work", 1, signal);
9331
+ await ledger.checkpoint(signal);
9332
+ leftEntry = leftEntry.previous;
9333
+ }
9334
+ for (let remaining = rightCount; remaining > ordinal; remaining--) {
9335
+ ledger.charge("work", 1, signal);
9336
+ await ledger.checkpoint(signal);
9337
+ rightEntry = rightEntry.previous;
9338
+ }
9339
+ const compared = spanOrder(leftEntry.span, rightEntry.span);
9340
+ if (compared !== 0) return compared;
9341
+ }
9342
+ return leftCount - rightCount;
9343
+ }
9344
+ async function preferred(candidate, incumbent, ledger, signal) {
9345
+ if (candidate.position !== incumbent.position) return candidate.position > incumbent.position;
9346
+ for (let group = 1; group < candidate.captures.length; group++) {
9347
+ ledger.charge("work", 1, signal);
9348
+ const compared = await historyOrder(candidate.histories[group], incumbent.histories[group], ledger, signal);
9349
+ if (compared !== 0) return compared > 0;
9350
+ }
9351
+ return false;
9352
+ }
9353
+ async function resetDescendants(node, previous, ledger, signal) {
9354
+ ledger.charge("allocationUnits", previous.length + 3, signal);
9355
+ const captures = previous.slice();
9356
+ const pending = [node];
9357
+ while (pending.length > 0) {
9358
+ ledger.charge("work", 1, signal);
9359
+ await ledger.checkpoint(signal);
9360
+ const current = pending.pop();
9361
+ if (current.kind === "group") captures[current.index] = null;
9362
+ if (current.kind === "group" || current.kind === "repeat") {
9363
+ if (current.child.captured) {
9364
+ ledger.charge("allocationUnits", 1, signal);
9365
+ pending.push(current.child);
9366
+ }
9367
+ } else if (current.kind === "sequence" || current.kind === "alternative") {
9368
+ for (const child of current.children) {
9369
+ ledger.charge("work", 1, signal);
9370
+ await ledger.checkpoint(signal);
9371
+ if (child.captured) {
9372
+ ledger.charge("allocationUnits", 1, signal);
9373
+ pending.push(child);
9374
+ }
9375
+ }
9376
+ }
9377
+ }
9378
+ return captures;
9379
+ }
9380
+ async function matchEre(program, subject, ledger, signal) {
9381
+ ledger.check(signal);
9382
+ const root = resolveEreProgram(program, ledger);
9383
+ ledger.admitInput("subjectBytes", subject.length, signal);
9384
+ await admitAscii(subject, ledger, signal);
9385
+ const width = program.groups + 1;
9386
+ ledger.charge("allocationUnits", width * 2 + 1, signal);
9387
+ const emptyCaptures = Object.freeze(new Array(width).fill(null));
9388
+ const emptyHistories = Object.freeze(new Array(width).fill(null));
9389
+ const pending = [];
9390
+ const task = (create) => {
9391
+ ledger.charge("allocationUnits", 5, signal);
9392
+ return create();
9393
+ };
9394
+ const push = (position, next, captures, histories) => {
9395
+ ledger.charge("states", 1, signal);
9396
+ ledger.charge("allocationUnits", 5, signal);
9397
+ pending.push({ position, task: next, captures, histories });
9398
+ };
9399
+ for (let start = 0; start <= subject.length; start++) {
9400
+ push(start, task(() => ({ kind: "node", node: root, next: null })), emptyCaptures, emptyHistories);
9401
+ let best;
9402
+ while (pending.length > 0) {
9403
+ ledger.charge("work", 1, signal);
9404
+ await ledger.checkpoint(signal);
9405
+ const state = pending.pop();
9406
+ const current = state.task;
9407
+ if (current === null) {
9408
+ if (!best || await preferred(state, best, ledger, signal)) best = state;
9409
+ continue;
9410
+ }
9411
+ if (current.kind === "close") {
9412
+ ledger.charge("allocationUnits", width * 2 + 6, signal);
9413
+ const span = Object.freeze({ start: current.start, end: state.position });
9414
+ const captures = state.captures.slice();
9415
+ captures[current.group] = span;
9416
+ const histories = state.histories.slice();
9417
+ const previous = histories[current.group];
9418
+ histories[current.group] = { span, previous, count: (previous?.count ?? 0) + 1 };
9419
+ push(state.position, current.next, captures, histories);
9420
+ continue;
9421
+ }
9422
+ if (current.kind === "repeat") {
9423
+ const { node: node2, count } = current;
9424
+ if (count >= node2.min) push(state.position, current.next, state.captures, state.histories);
9425
+ const noProgress = count > 0 && state.position === current.previous;
9426
+ if (count < node2.max && (!noProgress || count < node2.min)) {
9427
+ const repeat = task(() => ({ kind: "repeat", node: node2, count: count + 1, previous: state.position, next: current.next }));
9428
+ push(state.position, task(() => ({ kind: "node", node: node2.child, next: repeat })), state.captures, state.histories);
9429
+ }
9430
+ continue;
9431
+ }
9432
+ const node = current.node;
9433
+ switch (node.kind) {
9434
+ case "empty":
9435
+ push(state.position, current.next, state.captures, state.histories);
9436
+ break;
9437
+ case "start":
9438
+ if (state.position === 0) push(state.position, current.next, state.captures, state.histories);
9439
+ break;
9440
+ case "end":
9441
+ if (state.position === subject.length) push(state.position, current.next, state.captures, state.histories);
9442
+ break;
9443
+ case "dot":
9444
+ case "literal":
9445
+ case "set": {
9446
+ const code = subject.charCodeAt(state.position);
9447
+ if (state.position < subject.length && (node.kind === "dot" || node.kind === "literal" && node.code === code || node.kind === "set" && node.members[code])) {
9448
+ push(state.position + 1, current.next, state.captures, state.histories);
9449
+ }
9450
+ break;
9451
+ }
9452
+ case "sequence": {
9453
+ let next = current.next;
9454
+ for (let index = node.children.length - 1; index >= 0; index--) {
9455
+ ledger.charge("work", 1, signal);
9456
+ await ledger.checkpoint(signal);
9457
+ const following = next;
9458
+ next = task(() => ({ kind: "node", node: node.children[index], next: following }));
9459
+ }
9460
+ push(state.position, next, state.captures, state.histories);
9461
+ break;
9462
+ }
9463
+ case "alternative":
9464
+ for (let index = node.children.length - 1; index >= 0; index--) {
9465
+ ledger.charge("work", 1, signal);
9466
+ await ledger.checkpoint(signal);
9467
+ push(state.position, task(() => ({ kind: "node", node: node.children[index], next: current.next })), state.captures, state.histories);
9468
+ }
9469
+ break;
9470
+ case "group": {
9471
+ const captures = node.child.captured ? await resetDescendants(node.child, state.captures, ledger, signal) : state.captures;
9472
+ const close = task(() => ({ kind: "close", group: node.index, start: state.position, next: current.next }));
9473
+ push(state.position, task(() => ({ kind: "node", node: node.child, next: close })), captures, state.histories);
9474
+ break;
9475
+ }
9476
+ case "repeat":
9477
+ push(state.position, task(() => ({ kind: "repeat", node, count: 0, previous: -1, next: current.next })), state.captures, state.histories);
9478
+ break;
9479
+ }
9480
+ }
9481
+ if (best) {
9482
+ ledger.charge("captureSlots", width, signal);
9483
+ let bytes = best.position - start;
9484
+ for (let group = 1; group < width; group++) {
9485
+ ledger.charge("work", 1, signal);
9486
+ const span = best.captures[group];
9487
+ if (span) bytes += span.end - span.start;
9488
+ }
9489
+ ledger.charge("captureBytes", bytes, signal);
9490
+ ledger.charge("allocationUnits", width * 2 + bytes + 4, signal);
9491
+ const captures = best.captures.slice();
9492
+ captures[0] = Object.freeze({ start, end: best.position });
9493
+ const values = captures.map((span) => span === null ? "" : subject.slice(span.start, span.end));
9494
+ ledger.check(signal);
9495
+ return Object.freeze({ matched: true, captures: Object.freeze(captures), values: Object.freeze(values) });
9496
+ }
9497
+ }
9498
+ ledger.charge("allocationUnits", 3, signal);
9499
+ ledger.check(signal);
9500
+ return Object.freeze({ matched: false, captures: Object.freeze([]), values: Object.freeze([]) });
9501
+ }
8828
9502
 
8829
9503
  // packages/safe-bash/src/shell/runtime.ts
8830
9504
  var defaultLimits = {
@@ -8836,6 +9510,8 @@ var defaultLimits = {
8836
9510
  maxSourceBytes: 1024 * 1024,
8837
9511
  maxExpansionFields: 1e4,
8838
9512
  maxExpansionBytes: 16 * 1024 * 1024,
9513
+ maxWallClockMs: 3e4,
9514
+ maxCpuMs: 3e4,
8839
9515
  pipeHighWaterMark: 64 * 1024
8840
9516
  };
8841
9517
  var shellBuiltinNames = /* @__PURE__ */ new Set([
@@ -8915,6 +9591,8 @@ var Budget = class {
8915
9591
  this.limits = limits;
8916
9592
  this.signal = signal ? AbortSignal.any([signal, this.controller.signal]) : this.controller.signal;
8917
9593
  this.values = new ValueArena(limits.maxExpansionBytes, limits.maxExpansionFields, () => this.signal.throwIfAborted(), (limit) => this.fail(limit));
9594
+ this.#wallClockDeadline = Date.now() + limits.maxWallClockMs;
9595
+ this.#armWallClock();
8918
9596
  }
8919
9597
  limits;
8920
9598
  values;
@@ -8924,12 +9602,34 @@ var Budget = class {
8924
9602
  sourceBytes = 0;
8925
9603
  controller = new AbortController();
8926
9604
  signal;
9605
+ #wallClockTimer;
9606
+ #wallClockDeadline = 0;
9607
+ #cpuStarted = monotonicNow();
9608
+ #armWallClock() {
9609
+ const remaining = this.#wallClockDeadline - Date.now();
9610
+ if (remaining <= 0) {
9611
+ this.controller.abort(new ShellLimitError("maxWallClockMs"));
9612
+ return;
9613
+ }
9614
+ this.#wallClockTimer = setTimeout(() => this.#armWallClock(), Math.min(remaining, 2147483647));
9615
+ const timer = this.#wallClockTimer;
9616
+ timer.unref?.();
9617
+ }
9618
+ close() {
9619
+ if (this.#wallClockTimer !== void 0) clearTimeout(this.#wallClockTimer);
9620
+ this.#wallClockTimer = void 0;
9621
+ }
8927
9622
  fail(limit) {
8928
9623
  const error = new ShellLimitError(limit);
8929
9624
  this.controller.abort(error);
8930
9625
  throw error;
8931
9626
  }
9627
+ cpuCheckpoint() {
9628
+ this.signal.throwIfAborted();
9629
+ if (monotonicNow() - this.#cpuStarted > this.limits.maxCpuMs) this.fail("maxCpuMs");
9630
+ }
8932
9631
  tick() {
9632
+ this.cpuCheckpoint();
8933
9633
  this.signal.throwIfAborted();
8934
9634
  if (++this.commands > this.limits.maxCommands) this.fail("maxCommands");
8935
9635
  }
@@ -9328,7 +10028,7 @@ var CdLookup = class {
9328
10028
  amount -= step;
9329
10029
  if (this.spent % 128 === 0) {
9330
10030
  this.signal.throwIfAborted();
9331
- await interruptible(new Promise((resolve) => setImmediate(resolve)), this.signal);
10031
+ await yieldTurn(this.signal);
9332
10032
  this.signal.throwIfAborted();
9333
10033
  }
9334
10034
  }
@@ -9424,7 +10124,7 @@ var DirectoryStackWork = class {
9424
10124
  async flushWork() {
9425
10125
  this.signal.throwIfAborted();
9426
10126
  if (this.flushed === this.spent) return;
9427
- await interruptible(new Promise((resolve) => setImmediate(resolve)), this.signal);
10127
+ await yieldTurn(this.signal);
9428
10128
  this.signal.throwIfAborted();
9429
10129
  this.flushed = this.spent;
9430
10130
  }
@@ -9745,6 +10445,7 @@ var Runtime = class _Runtime {
9745
10445
  this.cancellationDepth = cancellationDepth;
9746
10446
  this.cancellationMaxDepth = cancellationMaxDepth;
9747
10447
  this.outcomeFrame = outcomeFrame;
10448
+ registerYieldCheckpoint(this.signal, () => this.budget.cpuCheckpoint());
9748
10449
  }
9749
10450
  fs;
9750
10451
  commands;
@@ -9760,19 +10461,6 @@ var Runtime = class _Runtime {
9760
10461
  cancellationDepth;
9761
10462
  cancellationMaxDepth;
9762
10463
  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
10464
  async ereDiagnostic(io, detail) {
9777
10465
  try {
9778
10466
  await this.diagnostic(io, detail);
@@ -9821,32 +10509,21 @@ var Runtime = class _Runtime {
9821
10509
  await textToken(operation, subject, this.signal);
9822
10510
  const name2 = "BASH_REMATCH";
9823
10511
  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;
10512
+ const ledger = new EreLedger({
10513
+ maxExpansionBytes: this.budget.limits.maxExpansionBytes,
10514
+ maxExpansionFields: this.budget.limits.maxExpansionFields
9830
10515
  });
9831
- const session = entry.root.openSession((cleanup) => scope.register(cleanup));
9832
10516
  let result;
9833
- let sessionEscaping = false;
9834
10517
  try {
9835
- result = await session.execute({ pattern: fragments, subject }, this.signal);
10518
+ const program = await compileEre(fragments, ledger, this.signal);
10519
+ result = await matchEre(program, subject, ledger, this.signal);
9836
10520
  } catch (error) {
9837
- sessionEscaping = true;
9838
10521
  this.signal.throwIfAborted();
9839
- if (error instanceof EreTransportSemanticError || error instanceof EreUnsupportedError) {
10522
+ if (error instanceof EreSyntaxError || error instanceof EreUnsupportedError) {
9840
10523
  await this.ereDiagnostic(io, `[[ ${error.message}`);
9841
- sessionEscaping = false;
9842
10524
  return 2;
9843
10525
  }
9844
10526
  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
10527
  }
9851
10528
  const status = result.matched ? 0 : 1;
9852
10529
  this.signal.throwIfAborted();
@@ -9861,8 +10538,8 @@ var Runtime = class _Runtime {
9861
10538
  const tickets = operation.reserve({ generation: true, version: true, epoch: true, work: 8 });
9862
10539
  const prepared = await store.prepareName(name2, operation, this.signal);
9863
10540
  staged = IndexedBinding.create(store.owner);
9864
- if (result.matched) for (let index = 0; index < result.spans.length; index++) {
9865
- const span = result.spans[index];
10541
+ if (result.matched) for (let index = 0; index < result.captures.length; index++) {
10542
+ const span = result.captures[index];
9866
10543
  const size = span === null ? 0 : span.end - span.start;
9867
10544
  operation.reserve({ payload: size, metadata: 32, work: size + 2 });
9868
10545
  const value2 = span === null ? "" : subject.slice(span.start, span.end);
@@ -10024,6 +10701,7 @@ var Runtime = class _Runtime {
10024
10701
  writeVariable(state, name2, value2, origin = "assignment") {
10025
10702
  if (arrayStore(state)?.get(name2)) throw new ArrayFailure(origin === "arithmetic" ? "indexed arithmetic is unsupported" : "indexed write requires prepared publication");
10026
10703
  if (state.readonlyVariables?.has(name2)) throw new Error(`${name2}: readonly variable`);
10704
+ if (shellValueByteLength(value2) > this.budget.limits.maxExpansionBytes) this.budget.fail("maxExpansionBytes");
10027
10705
  if (name2 === "OPTIND" && state.getopts?.integer && origin !== "arithmetic") {
10028
10706
  try {
10029
10707
  value2 = String(evaluateArithmetic(prepareArithmetic(shellValueText(value2) || "0"), this.arithmeticVariables(state)));
@@ -10533,7 +11211,7 @@ var Runtime = class _Runtime {
10533
11211
  completed.add(index);
10534
11212
  if (incoming) {
10535
11213
  const upstream = index - 1;
10536
- const close = setImmediate(() => {
11214
+ const close = scheduleTurn(() => {
10537
11215
  closing.delete(close);
10538
11216
  if (written.has(upstream) && !completed.has(upstream)) controllers[upstream].abort(new PipelineClosed());
10539
11217
  });
@@ -10560,7 +11238,7 @@ var Runtime = class _Runtime {
10560
11238
  try {
10561
11239
  statuses = await interruptible(Promise.all(tasks), this.signal);
10562
11240
  } finally {
10563
- for (const close of closing) clearImmediate(close);
11241
+ for (const close of closing) cancelTurn(close);
10564
11242
  for (const [index, controller] of controllers.entries()) if (!completed.has(index) || written.has(index)) controller.abort(new PipelineClosed());
10565
11243
  await Promise.all(pipes.map((pipe) => pipe.abort()));
10566
11244
  }
@@ -10625,7 +11303,7 @@ var Runtime = class _Runtime {
10625
11303
  originalIO = { ...originalIO, diagnosticLine, substitutionDiagnosticLine: originalIO.substitutionDiagnosticLines?.get(command) ?? diagnosticLine };
10626
11304
  if (command.kind === "subshell") originalIO = isolateIO(originalIO);
10627
11305
  this.budget.tick();
10628
- if (this.budget.commands % 128 === 0) await interruptible(new Promise((resolve) => setImmediate(resolve)), this.signal);
11306
+ if (this.budget.commands % 128 === 0) await yieldTurn(this.signal);
10629
11307
  this.signal.throwIfAborted();
10630
11308
  const inputs = /* @__PURE__ */ new Set();
10631
11309
  const outputs = /* @__PURE__ */ new Set();
@@ -10672,7 +11350,7 @@ var Runtime = class _Runtime {
10672
11350
  diagnosticFailure = error;
10673
11351
  throw error;
10674
11352
  }
10675
- if (error instanceof EreProfileLimitError || error instanceof EreTransportProfileLimitError) {
11353
+ if (error instanceof EreProfileLimitError) {
10676
11354
  try {
10677
11355
  await this.diagnostic(io, `[[ ${error.message}`);
10678
11356
  } catch (reason) {
@@ -10741,7 +11419,7 @@ var Runtime = class _Runtime {
10741
11419
  for (const clause of command.clauses) {
10742
11420
  let matched = fallthrough;
10743
11421
  if (!matched) for (const word of clause.patterns) {
10744
- if (++patterns % 128 === 0) await interruptible(new Promise((resolve) => setImmediate(resolve)), this.signal);
11422
+ if (++patterns % 128 === 0) await yieldTurn(this.signal);
10745
11423
  const pattern = (await this.word(word, state, io, false, true)).join("");
10746
11424
  if (await matchesPattern(pattern, subject, work)) {
10747
11425
  matched = true;
@@ -10862,7 +11540,7 @@ var Runtime = class _Runtime {
10862
11540
  this.signal.throwIfAborted();
10863
11541
  for (const warning of warnings.splice(0)) await writeText(io.stderr, `shell: warning: ${warning}
10864
11542
  `);
10865
- if (++words % 128 === 0) await interruptible(new Promise((resolve) => setImmediate(resolve)), this.signal);
11543
+ if (++words % 128 === 0) await yieldTurn(this.signal);
10866
11544
  const part = (await this.word(word, state, io, false)).join("");
10867
11545
  size += import_buffer.Buffer.byteLength(part);
10868
11546
  if (size > this.budget.limits.maxExpansionBytes) this.budget.fail("maxExpansionBytes");
@@ -11056,7 +11734,7 @@ var Runtime = class _Runtime {
11056
11734
  }
11057
11735
  const wordValues = await this.valueWords(commandWords, state, originalIO, ["export", "local", "readonly"].includes(commandWords[declarationIndex]?.plain ?? ""));
11058
11736
  const words = wordValues.map(shellValueText);
11059
- const special = state.profile === "sh" && specialBuiltinNames.has(words[0] ?? "");
11737
+ const special2 = state.profile === "sh" && specialBuiltinNames.has(words[0] ?? "");
11060
11738
  const inlineInput = command.redirects.some((redirect) => redirect.document || redirect.operator === "<<<");
11061
11739
  const functionCommand = words.length > 0 && state.functions.has(words[0]);
11062
11740
  const isolatedInlineInput = inlineInput && words.length > 0 && !shellBuiltinNames.has(words[0]) && !functionCommand;
@@ -11084,7 +11762,7 @@ var Runtime = class _Runtime {
11084
11762
  if (assignment.append) value2 = concatShellValues([stateMonitor(state)?.values.get(assignment.name, state.variables[assignment.name] ?? "") ?? state.variables[assignment.name] ?? "", value2], io[valueScope]);
11085
11763
  if (state.readonlyVariables?.has(assignment.name)) {
11086
11764
  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);
11765
+ if (state.profile === "sh" || !words.length) throw new Flow("exit", state.profile === "sh" && (special2 || !words.length) ? 127 : 1);
11088
11766
  continue;
11089
11767
  }
11090
11768
  if (!previous.has(assignment.name)) {
@@ -11175,7 +11853,7 @@ var Runtime = class _Runtime {
11175
11853
  if (error instanceof Flow) throw error;
11176
11854
  this.signal.throwIfAborted();
11177
11855
  const original = error instanceof ExecutionFailure ? error.original : error;
11178
- if (special && !(original instanceof ShellLimitError) && !(original instanceof ExpansionFailure) && !(original instanceof Flow) && !(original instanceof ShellSyntaxError)) {
11856
+ if (special2 && !(original instanceof ShellLimitError) && !(original instanceof ExpansionFailure) && !(original instanceof Flow) && !(original instanceof ShellSyntaxError)) {
11179
11857
  throw new ExecutionFailure(new FatalCommandFailure(message(original), 1), error instanceof ExecutionFailure ? error.io : io, error instanceof ExecutionFailure ? error.diagnostic : void 0);
11180
11858
  }
11181
11859
  if (error instanceof ExecutionFailure) throw error;
@@ -11415,16 +12093,16 @@ var Runtime = class _Runtime {
11415
12093
  }
11416
12094
  if (selected?.kind === "builtin") {
11417
12095
  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) };
12096
+ const special2 = state.profile === "sh" && !bypassFunctions && specialBuiltinNames.has(context2.command);
12097
+ if (special2) assignments2.clear();
12098
+ if (context2.command === "." || context2.command === "source") return { exitCode: await this.sourceBuiltin(context2, state, { ...io, ...context2 }, special2) };
12099
+ if (context2.command === "eval") return { exitCode: await this.evalBuiltin(context2, state, { ...io, ...context2 }, special2) };
11422
12100
  const builtinWork = this.builtin(context2, state, assignments2, (error, diagnostic2) => {
11423
12101
  builtinFailure = { error, diagnostic: diagnostic2 };
11424
12102
  }, bypassFunctions);
11425
12103
  const builtin = arrayStore(state) ? await interruptible(builtinWork, this.signal) : await builtinWork;
11426
12104
  if (builtin !== void 0) {
11427
- if (special && builtin !== 0 && context2.command !== "shift") throw new Flow("exit", builtin);
12105
+ if (special2 && builtin !== 0 && context2.command !== "shift") throw new Flow("exit", builtin);
11428
12106
  return { exitCode: builtin };
11429
12107
  }
11430
12108
  }
@@ -11736,7 +12414,7 @@ ${prefix2} line ${offset + line}: \`${source.split("\n")[line - 1] ?? ""}'
11736
12414
  let status = 0;
11737
12415
  let lines2 = 0;
11738
12416
  while (true) {
11739
- if (++lines2 % 32 === 0) await interruptible(new Promise((resolve) => setImmediate(resolve)), this.signal);
12417
+ if (++lines2 % 32 === 0) await yieldTurn(this.signal);
11740
12418
  this.signal.throwIfAborted();
11741
12419
  const bytes = await input2.sourceLine();
11742
12420
  const eof = bytes === void 0;
@@ -12056,13 +12734,13 @@ ${prefix2} line ${offset + line}: \`${source.split("\n")[line - 1] ?? ""}'
12056
12734
  return status2;
12057
12735
  }
12058
12736
  }
12059
- async evalBuiltin(context, state, io, special) {
12737
+ async evalBuiltin(context, state, io, special2) {
12060
12738
  const args = [...context.args];
12061
12739
  if (args[0] === "--") args.shift();
12062
12740
  else if (args[0]?.startsWith("-") && args[0] !== "-") {
12063
12741
  await this.diagnostic(io, `eval: -${args[0][1]}: invalid option`);
12064
12742
  await writeText(io.stderr, "eval: usage: eval [arg ...]\n");
12065
- if (special) throw new Flow("exit", 2);
12743
+ if (special2) throw new Flow("exit", 2);
12066
12744
  return 2;
12067
12745
  }
12068
12746
  if (!args.length) return 0;
@@ -12078,7 +12756,7 @@ ${prefix2} line ${offset + line}: \`${source.split("\n")[line - 1] ?? ""}'
12078
12756
  throw error;
12079
12757
  }
12080
12758
  try {
12081
- return await this.runCurrentText(source, state, { ...io, diagnosticOffset: (io.diagnosticLine ?? 1) - 1 }, special, `${io.scriptName ?? "shell"}: eval`);
12759
+ return await this.runCurrentText(source, state, { ...io, diagnosticOffset: (io.diagnosticLine ?? 1) - 1 }, special2, `${io.scriptName ?? "shell"}: eval`);
12082
12760
  } finally {
12083
12761
  const restore = () => {
12084
12762
  state.depth--;
@@ -12087,12 +12765,12 @@ ${prefix2} line ${offset + line}: \`${source.split("\n")[line - 1] ?? ""}'
12087
12765
  else restore();
12088
12766
  }
12089
12767
  }
12090
- async sourceBuiltin(context, state, io, special) {
12768
+ async sourceBuiltin(context, state, io, special2) {
12091
12769
  const args = [...context.args];
12092
12770
  if (args[0] === "--") args.shift();
12093
12771
  else if (args[0]?.startsWith("-") && args[0] !== "-") {
12094
12772
  await this.diagnostic(io, `${context.command}: ${args[0]}: unsupported option`);
12095
- if (special) throw new Flow("exit", 2);
12773
+ if (special2) throw new Flow("exit", 2);
12096
12774
  return 2;
12097
12775
  }
12098
12776
  const filename = args.shift();
@@ -12100,7 +12778,7 @@ ${prefix2} line ${offset + line}: \`${source.split("\n")[line - 1] ?? ""}'
12100
12778
  await this.diagnostic(io, `${context.command}: filename argument required`);
12101
12779
  await writeText(io.stderr, `${context.command}: usage: ${context.command} [-p path] filename [arguments]
12102
12780
  `);
12103
- if (special) throw new Flow("exit", 2);
12781
+ if (special2) throw new Flow("exit", 2);
12104
12782
  return 2;
12105
12783
  }
12106
12784
  if (state.depth >= this.budget.limits.maxSubstitutionDepth) this.budget.fail("maxSubstitutionDepth");
@@ -12146,7 +12824,7 @@ ${prefix2} line ${offset + line}: \`${source.split("\n")[line - 1] ?? ""}'
12146
12824
  if (error instanceof ShellLimitError) throw error;
12147
12825
  if (errorCode(error) === "EFBIG") this.budget.fail("maxSourceBytes");
12148
12826
  const diagnostic2 = error instanceof CommandFailure ? error.message : filesystemDiagnostic(error, target) ?? `${target}: ${message(error)}`;
12149
- if (special) throw new FatalCommandFailure(diagnostic2, 1);
12827
+ if (special2) throw new FatalCommandFailure(diagnostic2, 1);
12150
12828
  throw new CommandFailure(diagnostic2, error instanceof CommandFailure ? error.status : 1);
12151
12829
  }
12152
12830
  const positional = state.positional;
@@ -12175,7 +12853,7 @@ ${prefix2} line ${offset + line}: \`${source.split("\n")[line - 1] ?? ""}'
12175
12853
  throw error;
12176
12854
  }
12177
12855
  try {
12178
- return await this.runCurrentText(source, state, { ...io, scriptName: target, diagnosticOffset: 0, diagnosticLine: 1 }, special);
12856
+ return await this.runCurrentText(source, state, { ...io, scriptName: target, diagnosticOffset: 0, diagnosticLine: 1 }, special2);
12179
12857
  } catch (error) {
12180
12858
  if (error instanceof Flow && error.kind === "return") return error.status;
12181
12859
  throw error;
@@ -12317,7 +12995,7 @@ ${prefix2} line ${offset + line}: \`${source.split("\n")[line - 1] ?? ""}'
12317
12995
  };
12318
12996
  const checkpoint = async () => {
12319
12997
  this.signal.throwIfAborted();
12320
- await interruptible(new Promise((resolve) => setImmediate(resolve)), this.signal);
12998
+ await yieldTurn(this.signal);
12321
12999
  this.signal.throwIfAborted();
12322
13000
  };
12323
13001
  const { args } = context;
@@ -12362,7 +13040,7 @@ ${prefix2} line ${offset + line}: \`${source.split("\n")[line - 1] ?? ""}'
12362
13040
  };
12363
13041
  const checkpoint = async () => {
12364
13042
  this.signal.throwIfAborted();
12365
- await interruptible(new Promise((resolve) => setImmediate(resolve)), this.signal);
13043
+ await yieldTurn(this.signal);
12366
13044
  this.signal.throwIfAborted();
12367
13045
  };
12368
13046
  if (!Array.isArray(context.args)) throw new CommandFailure("getopts: argument array required", 2);
@@ -13395,7 +14073,7 @@ ${prefix2} line ${offset + line}: \`${source.split("\n")[line - 1] ?? ""}'
13395
14073
  const match = async (start, end) => {
13396
14074
  work.remaining -= end - start + 1;
13397
14075
  if (work.remaining < 0) work.exhausted();
13398
- if (++attempts % 256 === 0) await new Promise((resolve) => setImmediate(resolve));
14076
+ if (++attempts % 256 === 0) await yieldTurn(this.signal);
13399
14077
  this.signal.throwIfAborted();
13400
14078
  if (owner) await owner.ledger.checkpoint(this.signal, end - start + 1);
13401
14079
  return matches(slice(start, end));
@@ -13904,7 +14582,7 @@ var ShellInput = class _ShellInput {
13904
14582
  let length = 0;
13905
14583
  let pulls = 0;
13906
14584
  while (true) {
13907
- if (++pulls % 128 === 0) await interruptible(new Promise((resolve) => setImmediate(resolve)), this.signal);
14585
+ if (++pulls % 128 === 0) await yieldTurn(this.signal);
13908
14586
  const result = await this.#cursor.take(this.signal);
13909
14587
  if (result.done) {
13910
14588
  if (!length) return void 0;
@@ -13946,7 +14624,7 @@ var ShellInput = class _ShellInput {
13946
14624
  try {
13947
14625
  while (!terminated) {
13948
14626
  if (offset === chunk.length) {
13949
- if (++pulls % 128 === 0) await interruptible(new Promise((resolve) => setImmediate(resolve)), this.signal);
14627
+ if (++pulls % 128 === 0) await yieldTurn(this.signal);
13950
14628
  const result = await this.#cursor.take(this.signal);
13951
14629
  if (result.done) {
13952
14630
  break;
@@ -13962,7 +14640,7 @@ var ShellInput = class _ShellInput {
13962
14640
  }
13963
14641
  if (++length > this.budget.limits.maxOutputBytes) this.budget.fail("maxOutputBytes");
13964
14642
  if (length % 1024 === 0) {
13965
- await interruptible(new Promise((resolve) => setImmediate(resolve)), this.signal);
14643
+ await yieldTurn(this.signal);
13966
14644
  this.signal.throwIfAborted();
13967
14645
  }
13968
14646
  if (byte === 0) continue;
@@ -14155,6 +14833,7 @@ var Shell = class {
14155
14833
  #active = /* @__PURE__ */ new Set();
14156
14834
  constructor(options2) {
14157
14835
  if (!options2?.fs) throw new TypeError("Shell requires an explicit filesystem");
14836
+ warnIfHostProcessEnv(options2.env);
14158
14837
  resolveLimits(options2.limits);
14159
14838
  this.#options = { ...options2, cwd: resolvePath("/", options2.cwd ?? "/"), env: { ...options2.env }, limits: { ...options2.limits } };
14160
14839
  this.commands = options2.commands ?? new CommandRegistry();
@@ -14217,6 +14896,7 @@ var Shell = class {
14217
14896
  }
14218
14897
  async exec(source, options2 = {}) {
14219
14898
  if (this.#disposed) throw new Error("Shell is disposed");
14899
+ warnIfHostProcessEnv(options2.env);
14220
14900
  const budget = new Budget(resolveLimits(this.#options.limits, options2.limits), options2.signal);
14221
14901
  const scope = new InvocationScope(options2.signal);
14222
14902
  const cancellationState = new RuntimeCancellationState();
@@ -14227,7 +14907,6 @@ var Shell = class {
14227
14907
  controls: [{ role: "budget-control", signal: budget.controller.signal }]
14228
14908
  });
14229
14909
  try {
14230
- Runtime.registerEreRoot(budget, scope);
14231
14910
  owner.activate(boundary);
14232
14911
  } catch (error) {
14233
14912
  scope.failures.push(...boundary.close().failures);
@@ -14241,6 +14920,7 @@ var Shell = class {
14241
14920
  try {
14242
14921
  captured = await owner.capture(() => this.#execute(source, options2, scope, budget, boundary, cancellationState, owner));
14243
14922
  } finally {
14923
+ budget.close();
14244
14924
  await scope.close();
14245
14925
  }
14246
14926
  const selection = owner.finish(captured);
@@ -14413,6 +15093,22 @@ shell: -c: line ${line}: \`${source.split("\n")[line - 1] ?? ""}'
14413
15093
  }
14414
15094
  };
14415
15095
 
15096
+ // packages/safe-bash/src/shell/worker-limits.ts
15097
+ init_platform();
15098
+ var cloudflareWorkerLimits = Object.freeze({
15099
+ maxInputBytes: 4 * 1024 * 1024,
15100
+ maxOutputBytes: 4 * 1024 * 1024,
15101
+ maxCommands: 1e3,
15102
+ maxLoopIterations: 1e3,
15103
+ maxSubstitutionDepth: 16,
15104
+ maxSourceBytes: 256 * 1024,
15105
+ maxExpansionFields: 1e3,
15106
+ maxExpansionBytes: 4 * 1024 * 1024,
15107
+ maxWallClockMs: 1e4,
15108
+ maxCpuMs: 1e4,
15109
+ pipeHighWaterMark: 16 * 1024
15110
+ });
15111
+
14416
15112
  // packages/safe-bash/src/fs/memory/index.ts
14417
15113
  init_platform();
14418
15114
  import { createMemoryFileSystem, MemoryFileSystem } from "poe-code/safe-fs/core";
@@ -14429,6 +15125,153 @@ import { createMountFileSystem, MountFileSystem } from "poe-code/safe-fs/core";
14429
15125
  init_platform();
14430
15126
  import { createOverlayFileSystem, OverlayFileSystem } from "poe-code/safe-fs/core";
14431
15127
 
15128
+ // packages/safe-bash/src/commands/network/fetch-transport.ts
15129
+ init_platform();
15130
+
15131
+ // packages/safe-bash/src/commands/network/types.ts
15132
+ init_platform();
15133
+ var defaultNetworkLimits = Object.freeze({
15134
+ maxUploadBytes: 64 * 1024 * 1024,
15135
+ maxDownloadBytes: 64 * 1024 * 1024,
15136
+ maxBufferBytes: 8 * 1024 * 1024,
15137
+ maxHeaderBytes: 64 * 1024,
15138
+ maxRedirects: 10,
15139
+ maxRetries: 5,
15140
+ maxUrls: 32,
15141
+ maxTimeMs: 12e4
15142
+ });
15143
+ var cloudflareWorkerNetworkLimits = Object.freeze({
15144
+ maxUploadBytes: 4 * 1024 * 1024,
15145
+ maxDownloadBytes: 4 * 1024 * 1024,
15146
+ maxBufferBytes: 1024 * 1024,
15147
+ maxHeaderBytes: 32 * 1024,
15148
+ maxRedirects: 2,
15149
+ maxRetries: 1,
15150
+ maxUrls: 8,
15151
+ maxTimeMs: 1e4
15152
+ });
15153
+ var CurlError = class extends Error {
15154
+ constructor(exitCode, message2) {
15155
+ super(message2);
15156
+ this.exitCode = exitCode;
15157
+ this.name = "CurlError";
15158
+ }
15159
+ exitCode;
15160
+ };
15161
+
15162
+ // packages/safe-bash/src/commands/network/fetch-transport.ts
15163
+ function requestBody(source, signal) {
15164
+ if (!source) return void 0;
15165
+ const iterator = source[Symbol.asyncIterator]();
15166
+ return new ReadableStream({
15167
+ async pull(controller) {
15168
+ try {
15169
+ signal.throwIfAborted();
15170
+ const next = await iterator.next();
15171
+ if (next.done) controller.close();
15172
+ else controller.enqueue(next.value);
15173
+ } catch (error) {
15174
+ controller.error(error);
15175
+ }
15176
+ },
15177
+ async cancel(reason) {
15178
+ await iterator.return?.(reason);
15179
+ }
15180
+ });
15181
+ }
15182
+ function createFetchTransport(options2 = {}) {
15183
+ const fetchRequest = options2.fetch ?? globalThis.fetch;
15184
+ if (typeof fetchRequest !== "function") throw new TypeError("Fetch is unavailable");
15185
+ return async (input2) => {
15186
+ input2.signal.throwIfAborted();
15187
+ const url = new URL(input2.url);
15188
+ if (url.protocol !== "http:" && url.protocol !== "https:") throw new CurlError(1, "Unsupported protocol");
15189
+ const stopped = new AbortController();
15190
+ const signal = AbortSignal.any([input2.signal, stopped.signal]);
15191
+ const body = requestBody(input2.body, signal);
15192
+ const headers = new Headers();
15193
+ for (const [name2, value2] of input2.headers) headers.append(name2, value2);
15194
+ const request = new Request(url, {
15195
+ method: input2.method,
15196
+ headers,
15197
+ body,
15198
+ credentials: "omit",
15199
+ redirect: "manual",
15200
+ signal,
15201
+ ...body ? { duplex: "half" } : {}
15202
+ });
15203
+ let response;
15204
+ const dispose = async () => {
15205
+ stopped.abort();
15206
+ if (response?.body) await response.body.cancel().catch(() => void 0);
15207
+ };
15208
+ input2.registerCleanup?.(dispose);
15209
+ response = await fetchRequest(request);
15210
+ const reader = response.body?.getReader();
15211
+ const responseBody = reader ? (async function* () {
15212
+ try {
15213
+ while (true) {
15214
+ input2.signal.throwIfAborted();
15215
+ const next = await reader.read();
15216
+ if (next.done) return;
15217
+ yield next.value;
15218
+ }
15219
+ } finally {
15220
+ reader.releaseLock();
15221
+ }
15222
+ })() : (async function* () {
15223
+ })();
15224
+ return {
15225
+ status: response.status,
15226
+ statusText: response.statusText,
15227
+ headers: [...response.headers.entries()],
15228
+ body: responseBody,
15229
+ dispose
15230
+ };
15231
+ };
15232
+ }
15233
+
15234
+ // packages/safe-bash/src/commands/network/authorizer.ts
15235
+ init_platform();
15236
+ function privateHostname(input2) {
15237
+ const hostname = input2.toLowerCase().replace(/\.$/u, "").replace(/^\[|\]$/gu, "");
15238
+ if (hostname === "localhost" || hostname.endsWith(".localhost") || hostname === "::1") return true;
15239
+ if (hostname.includes(":")) {
15240
+ const first2 = hostname.split(":", 1)[0] ?? "";
15241
+ return first2.startsWith("fc") || first2.startsWith("fd") || ["fe8", "fe9", "fea", "feb"].some((prefix2) => first2.startsWith(prefix2));
15242
+ }
15243
+ const parts = hostname.split(".");
15244
+ if (parts.length !== 4 || parts.some((part) => !part || Array.from(part).some((character) => character < "0" || character > "9"))) return false;
15245
+ const octets = parts.map(Number);
15246
+ if (octets.some((value2) => value2 < 0 || value2 > 255)) return false;
15247
+ const [first, second] = octets;
15248
+ return first === 10 || first === 127 || first === 0 || first === 169 && second === 254 || first === 192 && second === 168 || first === 172 && second >= 16 && second <= 31;
15249
+ }
15250
+ function createOriginAuthorizer(allowlist = "*", options2 = {}) {
15251
+ const origins = /* @__PURE__ */ new Set();
15252
+ const hosts = /* @__PURE__ */ new Set();
15253
+ for (const entry of allowlist === "*" ? [] : allowlist) {
15254
+ const value2 = entry.trim();
15255
+ if (!value2) throw new TypeError("Origin allowlist entries must not be empty");
15256
+ if (value2.includes("://")) {
15257
+ const url = new URL(value2);
15258
+ if (url.pathname !== "/" || url.search || url.hash || url.username || url.password) {
15259
+ throw new TypeError(`Origin allowlist entry must be an origin: ${entry}`);
15260
+ }
15261
+ origins.add(url.origin);
15262
+ } else {
15263
+ if (value2.includes("/") || value2.includes("@")) throw new TypeError(`Invalid hostname allowlist entry: ${entry}`);
15264
+ hosts.add(value2.toLowerCase().replace(/\.$/u, ""));
15265
+ }
15266
+ }
15267
+ return (request) => {
15268
+ const url = new URL(request.url);
15269
+ const hostname = url.hostname.toLowerCase().replace(/\.$/u, "");
15270
+ if (options2.denyPrivateNetworks && privateHostname(hostname)) return false;
15271
+ return allowlist === "*" || origins.has(url.origin) || hosts.has(hostname);
15272
+ };
15273
+ }
15274
+
14432
15275
  // packages/safe-bash/src/browser.ts
14433
15276
  function createBrowserCommands() {
14434
15277
  return [
@@ -14467,14 +15310,18 @@ export {
14467
15310
  assertPathWithin,
14468
15311
  basename,
14469
15312
  browserCommands,
15313
+ cloudflareWorkerLimits,
15314
+ cloudflareWorkerNetworkLimits,
14470
15315
  collectBytes2 as collectBytes,
14471
15316
  collectText,
14472
15317
  composeMiddleware,
14473
15318
  createBrowserCommands,
14474
15319
  createBytePipe,
14475
15320
  createCommandArguments,
15321
+ createFetchTransport,
14476
15322
  createMemoryFileSystem,
14477
15323
  createMountFileSystem,
15324
+ createOriginAuthorizer,
14478
15325
  createOutputOperation,
14479
15326
  createOverlayFileSystem,
14480
15327
  createReadOnlyFileSystem,