@poe-platform/safe-bash 0.1.601 → 0.1.603

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 (100) hide show
  1. package/dist/safe-bash/chunks/{chunk-IYFB6VED.js → chunk-4BYUV5PA.js} +6 -4
  2. package/dist/safe-bash/chunks/{chunk-IYFB6VED.js.map → chunk-4BYUV5PA.js.map} +1 -1
  3. package/dist/safe-bash/chunks/{chunk-PE5NZIA2.js → chunk-6LSSEOG7.js} +3 -3
  4. package/dist/safe-bash/chunks/{chunk-3Y5PKSHX.js → chunk-AD7UAP5B.js} +10 -8
  5. package/dist/safe-bash/chunks/{chunk-3Y5PKSHX.js.map → chunk-AD7UAP5B.js.map} +1 -1
  6. package/dist/safe-bash/chunks/{chunk-XBLVOMEJ.js → chunk-AFH57PPL.js} +2 -2
  7. package/dist/safe-bash/chunks/{chunk-FMTBJ7SG.js → chunk-CEPNDODE.js} +3 -3
  8. package/dist/safe-bash/chunks/chunk-CXMIIXJA.js +178 -0
  9. package/dist/safe-bash/chunks/chunk-CXMIIXJA.js.map +7 -0
  10. package/dist/safe-bash/chunks/{chunk-E3NZKSTA.js → chunk-F5TCC4UG.js} +12 -10
  11. package/dist/safe-bash/chunks/{chunk-E3NZKSTA.js.map → chunk-F5TCC4UG.js.map} +1 -1
  12. package/dist/safe-bash/chunks/{chunk-TJVKGNKA.js → chunk-FWDMJTQO.js} +9 -7
  13. package/dist/safe-bash/chunks/{chunk-TJVKGNKA.js.map → chunk-FWDMJTQO.js.map} +1 -1
  14. package/dist/safe-bash/chunks/{chunk-LHKAA57T.js → chunk-GFZKQIPL.js} +8 -6
  15. package/dist/safe-bash/chunks/{chunk-LHKAA57T.js.map → chunk-GFZKQIPL.js.map} +1 -1
  16. package/dist/safe-bash/chunks/{chunk-W3TLNFDB.js → chunk-HHJSJWUD.js} +9 -7
  17. package/dist/safe-bash/chunks/{chunk-W3TLNFDB.js.map → chunk-HHJSJWUD.js.map} +1 -1
  18. package/dist/safe-bash/chunks/{chunk-XYVCXUGM.js → chunk-KZ7QCYNT.js} +10 -8
  19. package/dist/safe-bash/chunks/{chunk-XYVCXUGM.js.map → chunk-KZ7QCYNT.js.map} +1 -1
  20. package/dist/safe-bash/chunks/{chunk-HQCBEAFK.js → chunk-N5CYTGBN.js} +10 -8
  21. package/dist/safe-bash/chunks/{chunk-HQCBEAFK.js.map → chunk-N5CYTGBN.js.map} +1 -1
  22. package/dist/safe-bash/chunks/{chunk-H6E6F5KJ.js → chunk-PCLOK32J.js} +9 -7
  23. package/dist/safe-bash/chunks/{chunk-H6E6F5KJ.js.map → chunk-PCLOK32J.js.map} +1 -1
  24. package/dist/safe-bash/chunks/{chunk-BXVGCJXW.js → chunk-PFMSKAFQ.js} +12 -10
  25. package/dist/safe-bash/chunks/{chunk-BXVGCJXW.js.map → chunk-PFMSKAFQ.js.map} +1 -1
  26. package/dist/safe-bash/chunks/{chunk-PMBBUNNN.js → chunk-PW4WQSCS.js} +11 -9
  27. package/dist/safe-bash/chunks/{chunk-PMBBUNNN.js.map → chunk-PW4WQSCS.js.map} +1 -1
  28. package/dist/safe-bash/chunks/{chunk-OECKXHVJ.js → chunk-RNKTWFEM.js} +2 -2
  29. package/dist/safe-bash/chunks/{chunk-2PQ3XOCQ.js → chunk-UYX72YHY.js} +4 -172
  30. package/dist/safe-bash/chunks/chunk-UYX72YHY.js.map +7 -0
  31. package/dist/safe-bash/chunks/{chunk-QAZN7SAN.js → chunk-WPEMUXV4.js} +7 -5
  32. package/dist/safe-bash/chunks/{chunk-QAZN7SAN.js.map → chunk-WPEMUXV4.js.map} +1 -1
  33. package/dist/safe-bash/commands/csplit/index.browser.js +5 -4
  34. package/dist/safe-bash/commands/factor/index.browser.js +4 -3
  35. package/dist/safe-bash/commands/getopt/index.browser.js +4 -3
  36. package/dist/safe-bash/commands/hexdump/index.browser.js +4 -3
  37. package/dist/safe-bash/commands/iconv/index.browser.js +4 -3
  38. package/dist/safe-bash/commands/line-endings/index.browser.js +5 -4
  39. package/dist/safe-bash/commands/llm/index.browser.js +5 -4
  40. package/dist/safe-bash/commands/network/index.browser.js +6 -5
  41. package/dist/safe-bash/commands/op/index.browser.js +5664 -0
  42. package/dist/safe-bash/commands/op/index.browser.js.map +7 -0
  43. package/dist/safe-bash/commands/op/index.d.ts +14 -0
  44. package/dist/safe-bash/commands/op/index.d.ts.map +1 -0
  45. package/dist/safe-bash/commands/op/index.js +4684 -0
  46. package/dist/safe-bash/commands/op/index.js.map +7 -0
  47. package/dist/safe-bash/commands/op/op.test.d.ts +2 -0
  48. package/dist/safe-bash/commands/op/op.test.d.ts.map +1 -0
  49. package/dist/safe-bash/commands/op/op.test.js +159 -0
  50. package/dist/safe-bash/commands/op/op.test.js.map +1 -0
  51. package/dist/safe-bash/commands/pr/index.browser.js +4 -3
  52. package/dist/safe-bash/commands/tsort/index.browser.js +4 -3
  53. package/dist/safe-bash/commands/xml/index.browser.js +5 -4
  54. package/dist/safe-bash/commands/yq/index.browser.js +6 -5
  55. package/dist/safe-bash/core.browser.js +455 -101
  56. package/dist/safe-bash/core.browser.js.map +4 -4
  57. package/dist/safe-bash/index.d.ts +1 -0
  58. package/dist/safe-bash/index.d.ts.map +1 -1
  59. package/dist/safe-bash/index.js +1 -0
  60. package/dist/safe-bash/index.js.map +1 -1
  61. package/dist/safe-bash/internal/op/admin.d.ts +31 -0
  62. package/dist/safe-bash/internal/op/backend.d.ts +8 -0
  63. package/dist/safe-bash/internal/op/cli.d.ts +80 -0
  64. package/dist/safe-bash/internal/op/documents.d.ts +6 -0
  65. package/dist/safe-bash/internal/op/encoding.d.ts +15 -0
  66. package/dist/safe-bash/internal/op/environment-commands.d.ts +11 -0
  67. package/dist/safe-bash/internal/op/environment.d.ts +9 -0
  68. package/dist/safe-bash/internal/op/file-mode.d.ts +1 -0
  69. package/dist/safe-bash/internal/op/host-contracts.d.ts +12 -0
  70. package/dist/safe-bash/internal/op/index.d.ts +19 -0
  71. package/dist/safe-bash/internal/op/items.d.ts +3 -0
  72. package/dist/safe-bash/internal/op/references.d.ts +4 -0
  73. package/dist/safe-bash/internal/op/secrets.d.ts +13 -0
  74. package/dist/safe-bash/internal/op/templates.d.ts +10 -0
  75. package/dist/safe-bash/internal/op/types.d.ts +202 -0
  76. package/dist/safe-bash/shell/arrays/state.js +1 -1
  77. package/dist/safe-bash/shell/arrays/state.js.map +1 -1
  78. package/dist/safe-bash/shell/background-jobs.d.ts +39 -0
  79. package/dist/safe-bash/shell/background-jobs.d.ts.map +1 -0
  80. package/dist/safe-bash/shell/background-jobs.js +85 -0
  81. package/dist/safe-bash/shell/background-jobs.js.map +1 -0
  82. package/dist/safe-bash/shell/display.js +7 -7
  83. package/dist/safe-bash/shell/display.js.map +1 -1
  84. package/dist/safe-bash/shell/parser.d.ts +1 -0
  85. package/dist/safe-bash/shell/parser.d.ts.map +1 -1
  86. package/dist/safe-bash/shell/parser.js +8 -8
  87. package/dist/safe-bash/shell/parser.js.map +1 -1
  88. package/dist/safe-bash/shell/runtime.d.ts +7 -1
  89. package/dist/safe-bash/shell/runtime.d.ts.map +1 -1
  90. package/dist/safe-bash/shell/runtime.js +295 -46
  91. package/dist/safe-bash/shell/runtime.js.map +1 -1
  92. package/dist/safe-bash/shell/shell.d.ts.map +1 -1
  93. package/dist/safe-bash/shell/shell.js +11 -2
  94. package/dist/safe-bash/shell/shell.js.map +1 -1
  95. package/package.json +9 -2
  96. package/dist/safe-bash/chunks/chunk-2PQ3XOCQ.js.map +0 -7
  97. /package/dist/safe-bash/chunks/{chunk-PE5NZIA2.js.map → chunk-6LSSEOG7.js.map} +0 -0
  98. /package/dist/safe-bash/chunks/{chunk-XBLVOMEJ.js.map → chunk-AFH57PPL.js.map} +0 -0
  99. /package/dist/safe-bash/chunks/{chunk-FMTBJ7SG.js.map → chunk-CEPNDODE.js.map} +0 -0
  100. /package/dist/safe-bash/chunks/{chunk-OECKXHVJ.js.map → chunk-RNKTWFEM.js.map} +0 -0
@@ -1,39 +1,44 @@
1
+ import {
2
+ createPrCommand,
3
+ createPrCommands,
4
+ prCommands
5
+ } from "./chunks/chunk-PCLOK32J.js";
1
6
  import {
2
7
  createTsortCommand,
3
8
  createTsortCommands,
4
9
  tsortCommands
5
- } from "./chunks/chunk-TJVKGNKA.js";
10
+ } from "./chunks/chunk-FWDMJTQO.js";
6
11
  import {
7
12
  createFactorCommand,
8
13
  createFactorCommands,
9
14
  factorCommands
10
- } from "./chunks/chunk-IYFB6VED.js";
15
+ } from "./chunks/chunk-4BYUV5PA.js";
11
16
  import {
12
17
  createGetoptCommand,
13
18
  createGetoptCommands,
14
19
  getoptCommands
15
- } from "./chunks/chunk-PE5NZIA2.js";
20
+ } from "./chunks/chunk-6LSSEOG7.js";
16
21
  import {
17
22
  createHdCommand,
18
23
  createHexdumpCommand,
19
24
  createHexdumpCommands,
20
25
  hexdumpCommands
21
- } from "./chunks/chunk-HQCBEAFK.js";
26
+ } from "./chunks/chunk-N5CYTGBN.js";
22
27
  import {
23
28
  createIconvCommand,
24
29
  createIconvCommands,
25
30
  iconvCommands
26
- } from "./chunks/chunk-XYVCXUGM.js";
31
+ } from "./chunks/chunk-KZ7QCYNT.js";
27
32
  import {
28
33
  createDos2unixCommand,
29
34
  createLineEndingCommands,
30
35
  createUnix2dosCommand,
31
36
  lineEndingCommands
32
- } from "./chunks/chunk-E3NZKSTA.js";
37
+ } from "./chunks/chunk-F5TCC4UG.js";
33
38
  import {
34
39
  createLlmCommands,
35
40
  llmCommands
36
- } from "./chunks/chunk-QAZN7SAN.js";
41
+ } from "./chunks/chunk-WPEMUXV4.js";
37
42
  import "./chunks/chunk-C7QNJ3D4.js";
38
43
  import {
39
44
  classify,
@@ -44,7 +49,7 @@ import {
44
49
  createXmlCommands,
45
50
  defaultXmlQueryLimits,
46
51
  xmlCommands
47
- } from "./chunks/chunk-3Y5PKSHX.js";
52
+ } from "./chunks/chunk-AD7UAP5B.js";
48
53
  import {
49
54
  Interpreter,
50
55
  createYqCommand,
@@ -55,7 +60,7 @@ import {
55
60
  rawValues,
56
61
  stringify,
57
62
  yqCommands
58
- } from "./chunks/chunk-W3TLNFDB.js";
63
+ } from "./chunks/chunk-HHJSJWUD.js";
59
64
  import {
60
65
  Budget,
61
66
  JqError,
@@ -68,7 +73,7 @@ import {
68
73
  resolveJqLimits,
69
74
  truth,
70
75
  wellFormed
71
- } from "./chunks/chunk-XBLVOMEJ.js";
76
+ } from "./chunks/chunk-AFH57PPL.js";
72
77
  import {
73
78
  CurlError,
74
79
  cloudflareWorkerNetworkLimits,
@@ -81,7 +86,7 @@ import {
81
86
  curlCommands,
82
87
  defaultNetworkLimits,
83
88
  networkCommands
84
- } from "./chunks/chunk-BXVGCJXW.js";
89
+ } from "./chunks/chunk-PFMSKAFQ.js";
85
90
  import {
86
91
  RecordBuffer,
87
92
  UsageError,
@@ -113,7 +118,7 @@ import {
113
118
  textOutputRequirements,
114
119
  value,
115
120
  writeDiagnostic
116
- } from "./chunks/chunk-LHKAA57T.js";
121
+ } from "./chunks/chunk-GFZKQIPL.js";
117
122
  import {
118
123
  AvailableRecords,
119
124
  EreLedger,
@@ -133,20 +138,36 @@ import {
133
138
  exprMatchCeilings,
134
139
  matchEre,
135
140
  withRegexSession
136
- } from "./chunks/chunk-PMBBUNNN.js";
141
+ } from "./chunks/chunk-PW4WQSCS.js";
137
142
  import {
138
143
  bindFileOutputBudget,
139
144
  openFileOutput,
140
145
  writeFileOutput
141
- } from "./chunks/chunk-FMTBJ7SG.js";
142
- import {
143
- createPrCommand,
144
- createPrCommands,
145
- prCommands
146
- } from "./chunks/chunk-H6E6F5KJ.js";
146
+ } from "./chunks/chunk-CEPNDODE.js";
147
147
  import {
148
148
  createOutputOperation
149
- } from "./chunks/chunk-OECKXHVJ.js";
149
+ } from "./chunks/chunk-RNKTWFEM.js";
150
+ import {
151
+ CommandArgumentIdentityError,
152
+ CommandRegistry,
153
+ PublicDiagnostic,
154
+ cancelTurn,
155
+ concatShellValues,
156
+ createCommandArguments,
157
+ getCommandArguments,
158
+ inheritYieldCheckpoint,
159
+ monotonicNow,
160
+ publicDiagnosticMessage,
161
+ registerYieldCheckpoint,
162
+ scheduleTurn,
163
+ shellValueByteLength,
164
+ shellValueBytes,
165
+ shellValueFromBytes,
166
+ shellValueRetainedBytes,
167
+ shellValueText,
168
+ validateExitCode,
169
+ yieldTurn
170
+ } from "./chunks/chunk-UYX72YHY.js";
150
171
  import {
151
172
  basename,
152
173
  dirname,
@@ -158,39 +179,20 @@ import {
158
179
  validatePath
159
180
  } from "./chunks/chunk-DPJWEGIX.js";
160
181
  import {
161
- CommandArgumentIdentityError,
162
- CommandRegistry,
163
182
  FsError,
164
- PublicDiagnostic,
165
- cancelTurn,
166
183
  collectBytes,
167
184
  collectText,
168
- concatShellValues,
169
185
  createBytePipe,
170
- createCommandArguments,
171
- getCommandArguments,
172
- inheritYieldCheckpoint,
173
186
  isErrnoCode,
174
187
  isFsError,
175
- monotonicNow,
176
188
  outputFailure,
177
189
  pipeBytes,
178
- publicDiagnosticMessage,
179
190
  readBytes,
180
- registerYieldCheckpoint,
181
- scheduleTurn,
182
- shellValueByteLength,
183
- shellValueBytes,
184
- shellValueFromBytes,
185
- shellValueRetainedBytes,
186
- shellValueText,
187
191
  toByteSource,
188
192
  toFsError,
189
- validateExitCode,
190
193
  writeBytes,
191
- writeText,
192
- yieldTurn
193
- } from "./chunks/chunk-2PQ3XOCQ.js";
194
+ writeText
195
+ } from "./chunks/chunk-CXMIIXJA.js";
194
196
  import {
195
197
  import_buffer,
196
198
  init_platform
@@ -21200,15 +21202,15 @@ async function collectOperands(context2, options2) {
21200
21202
  }
21201
21203
  plans.push({ source: source2, sourceStat, realSource, destination: destination2, ...destinationStat ? { destinationStat } : {} });
21202
21204
  }
21203
- const destinations2 = /* @__PURE__ */ new Set();
21205
+ const destinations3 = /* @__PURE__ */ new Set();
21204
21206
  for (const plan of plans) {
21205
21207
  if (!plan.destination) continue;
21206
21208
  const parent = await context2.fs.realpath(dirname(plan.destination), { signal: context2.signal });
21207
21209
  const realDestination = joinPath(parent, plan.destination.slice(plan.destination.lastIndexOf("/") + 1));
21208
- if (destinations2.has(realDestination) || plans.some((other) => other.realSource === realDestination || plan.destinationStat && other.sourceStat && sameIdentity(plan.destinationStat, other.sourceStat))) {
21210
+ if (destinations3.has(realDestination) || plans.some((other) => other.realSource === realDestination || plan.destinationStat && other.sourceStat && sameIdentity(plan.destinationStat, other.sourceStat))) {
21209
21211
  throw new FsError("EINVAL", { path: plan.destination, message: "overlapping input/output operands" });
21210
21212
  }
21211
- destinations2.add(realDestination);
21213
+ destinations3.add(realDestination);
21212
21214
  }
21213
21215
  return plans;
21214
21216
  }
@@ -36798,6 +36800,105 @@ init_platform();
36798
36800
 
36799
36801
  // packages/safe-bash/src/shell/shell.ts
36800
36802
  init_platform();
36803
+
36804
+ // packages/safe-bash/src/shell/background-jobs.ts
36805
+ init_platform();
36806
+ var backgroundPrune = /* @__PURE__ */ Symbol("background initial redirections");
36807
+ var destinations2 = /* @__PURE__ */ new WeakMap();
36808
+ function backgroundDestination(sink) {
36809
+ return destinations2.get(sink) ?? sink;
36810
+ }
36811
+ function forwardBackgroundDestination(output2, source2) {
36812
+ destinations2.set(output2, backgroundDestination(source2));
36813
+ }
36814
+ var backgroundResources = /* @__PURE__ */ Symbol("background descriptor resources");
36815
+ var BackgroundResources = class {
36816
+ destination;
36817
+ #users = 0;
36818
+ #resolve;
36819
+ #pending;
36820
+ retain() {
36821
+ this.#users++;
36822
+ let released = false;
36823
+ return () => {
36824
+ if (released) return;
36825
+ released = true;
36826
+ if (--this.#users === 0) {
36827
+ this.#resolve?.();
36828
+ this.#resolve = void 0;
36829
+ this.#pending = void 0;
36830
+ }
36831
+ };
36832
+ }
36833
+ get busy() {
36834
+ return this.#users !== 0;
36835
+ }
36836
+ drain() {
36837
+ return this.#users ? this.#pending ??= new Promise((resolve) => {
36838
+ this.#resolve = resolve;
36839
+ }) : Promise.resolve();
36840
+ }
36841
+ };
36842
+ var BackgroundExecution = class {
36843
+ constructor(scope, admit2) {
36844
+ this.scope = scope;
36845
+ this.admit = admit2;
36846
+ }
36847
+ scope;
36848
+ admit;
36849
+ #next = 1;
36850
+ #pending = /* @__PURE__ */ new Set();
36851
+ #failures = [];
36852
+ identifier() {
36853
+ this.admit();
36854
+ return String(this.#next++);
36855
+ }
36856
+ track(promise) {
36857
+ this.#pending.add(promise);
36858
+ void promise.then(() => {
36859
+ this.#pending.delete(promise);
36860
+ }, (error) => {
36861
+ this.#pending.delete(promise);
36862
+ this.#failures.push(error);
36863
+ });
36864
+ return promise;
36865
+ }
36866
+ async drain() {
36867
+ while (this.#pending.size) await Promise.allSettled([...this.#pending]);
36868
+ if (this.#failures.length) throw this.#failures[0];
36869
+ }
36870
+ };
36871
+ var BackgroundJobs = class {
36872
+ constructor(execution) {
36873
+ this.execution = execution;
36874
+ }
36875
+ execution;
36876
+ jobs = /* @__PURE__ */ new Map();
36877
+ launch(id, work) {
36878
+ const promise = this.execution.track(Promise.resolve().then(work));
36879
+ let number6 = 1;
36880
+ for (const existing2 of this.jobs.values()) if (!existing2.consumed) number6 = Math.max(number6, existing2.number + 1);
36881
+ const job = { id, number: number6, promise, done: false, consumed: false, waited: false };
36882
+ this.jobs.set(id, job);
36883
+ void promise.then(() => {
36884
+ job.done = true;
36885
+ }, () => {
36886
+ job.done = true;
36887
+ });
36888
+ return id;
36889
+ }
36890
+ resolve(value2) {
36891
+ if (!value2.startsWith("%")) return /^[0-9]+$/u.test(value2) ? this.jobs.get(String(Number(value2))) : void 0;
36892
+ const active = [...this.jobs.values()].filter((job) => !job.consumed);
36893
+ if (["%", "%%", "%+"].includes(value2)) return active.at(-1);
36894
+ if (value2 === "%-") return active.at(-2);
36895
+ if (!/^%[0-9]+$/u.test(value2)) return void 0;
36896
+ const number6 = Number(value2.slice(1));
36897
+ return active.find((job) => job.number === number6);
36898
+ }
36899
+ };
36900
+
36901
+ // packages/safe-bash/src/shell/shell.ts
36801
36902
  import { createDeviceFileSystem } from "@poe-platform/safe-fs/core";
36802
36903
 
36803
36904
  // packages/safe-bash/src/shell/env-warning.ts
@@ -37611,7 +37712,7 @@ var Lexer2 = class {
37611
37712
  }
37612
37713
  const operator = this.conditionalPattern ? void 0 : /^(?:;;&|<<<|<<-|&>>|;&|&&|\|\||\|&|>>|>&|<&|>\||<<|;;|&>|[;\n|&()<>])/u.exec(logical)?.[0];
37613
37714
  if (operator) {
37614
- if (["&", "&>>"].includes(operator)) this.error(`Unsupported operator ${operator}`);
37715
+ if (operator === "&>>") this.error(`Unsupported operator ${operator}`);
37615
37716
  this.position = ends[operator.length - 1];
37616
37717
  if (operator === "<<" || operator === "<<-") this.delimiterOperator = operator;
37617
37718
  if (operator === "\n") this.readDocuments();
@@ -38099,7 +38200,7 @@ shell: command substitution: line ${line}: \`${sourceLine}'
38099
38200
  return;
38100
38201
  }
38101
38202
  this.position++;
38102
- if (["$", "!"].includes(this.source[this.position] ?? "") || !quoted2 && ["'", '"'].includes(this.source[this.position] ?? "")) this.error("Unsupported shell quoting or special parameter");
38203
+ if (this.source[this.position] === "$" || !quoted2 && ["'", '"'].includes(this.source[this.position] ?? "")) this.error("Unsupported shell quoting or special parameter");
38103
38204
  if (this.source.startsWith("((", this.position)) {
38104
38205
  const start = this.position + 2;
38105
38206
  const end = arithmeticEnd(this.source, start);
@@ -38131,11 +38232,11 @@ shell: command substitution: line ${line}: \`${sourceLine}'
38131
38232
  } else if (this.source[this.position] === "{") {
38132
38233
  this.position++;
38133
38234
  const parameterStart = this.position - 2;
38134
- const listing = this.source[this.position] === "!";
38235
+ const listing = this.source[this.position] === "!" && /[a-zA-Z_]/u.test(this.source[this.position + 1] ?? "");
38135
38236
  if (listing) this.position++;
38136
- const length = !listing && this.source[this.position] === "#" && /[a-zA-Z_]/u.test(this.source[this.position + 1] ?? "");
38237
+ const length = !listing && this.source[this.position] === "#" && /[a-zA-Z_!]/u.test(this.source[this.position + 1] ?? "");
38137
38238
  if (length) this.position++;
38138
- const name2 = /^(?:[a-zA-Z_][a-zA-Z_0-9]*|[0-9]+|[?@*#-])/u.exec(this.source.slice(this.position))?.[0];
38239
+ const name2 = /^(?:[a-zA-Z_][a-zA-Z_0-9]*|[0-9]+|[?@*#!-])/u.exec(this.source.slice(this.position))?.[0];
38139
38240
  if (!name2) this.error("Unsupported parameter expansion");
38140
38241
  this.position += name2.length;
38141
38242
  let prefixNames;
@@ -38212,7 +38313,7 @@ shell: command substitution: line ${line}: \`${sourceLine}'
38212
38313
  parts.push(part);
38213
38314
  }
38214
38315
  } else {
38215
- const name2 = /^(?:[a-zA-Z_][a-zA-Z_0-9]*|[?@*#0-9-])/u.exec(this.source.slice(this.position))?.[0];
38316
+ const name2 = /^(?:[a-zA-Z_][a-zA-Z_0-9]*|[?@*#!0-9-])/u.exec(this.source.slice(this.position))?.[0];
38216
38317
  if (name2) {
38217
38318
  this.position += name2.length;
38218
38319
  parts.push({ kind: "variable", name: name2, quoted: quoted2, line });
@@ -38283,11 +38384,11 @@ var Parser2 = class {
38283
38384
  this.newlines();
38284
38385
  pipelines.push(this.pipeline());
38285
38386
  }
38286
- lists.push({ pipelines, operators });
38387
+ lists.push({ pipelines, operators, ...this.is("&") ? { background: true } : {} });
38287
38388
  separators.push(this.is("\n"));
38288
38389
  if (captureInputUnits && this.is("\n")) this.completedInput.count = lists.length;
38289
38390
  if (inputUnit && this.is("\n")) break;
38290
- if (this.is(";") || this.is("\n")) {
38391
+ if (this.is(";") || this.is("&") || this.is("\n")) {
38291
38392
  this.advance();
38292
38393
  if (captureInputUnits && this.is("\n")) this.completedInput.count = lists.length;
38293
38394
  if (inputUnit && this.is("\n")) break;
@@ -39665,10 +39766,10 @@ function ownedShellSource(value2, budget, allocation) {
39665
39766
  // packages/safe-bash/src/shell/display.ts
39666
39767
  init_platform();
39667
39768
  var spelling = (word) => word.spelling ?? word.plain ?? "''";
39668
- function scriptText(script, indent) {
39669
- return script.lists.map((list) => list.pipelines.map(
39769
+ function scriptText(script, indent, terminate = false) {
39770
+ return script.lists.map((list, listIndex) => list.pipelines.map(
39670
39771
  (pipeline, index) => `${index ? `${list.operators[index - 1]} ` : ""}${pipeline.negate ? "! " : ""}${pipeline.commands.map((command7, commandIndex) => `${commandIndex ? pipeline.commands[commandIndex - 1].redirects.some((redirect) => redirect.implicitPipeline) ? " |& " : " | " : ""}${commandText(command7, indent)}`).join("")}`
39671
- ).join(" ")).join(`;
39772
+ ).join(" ") + (list.background ? " &" : terminate || listIndex < script.lists.length - 1 ? ";" : "")).join(`
39672
39773
  ${indent}`);
39673
39774
  }
39674
39775
  function commandText(command7, indent) {
@@ -39684,7 +39785,7 @@ function commandText(command7, indent) {
39684
39785
  case "arithmetic-for":
39685
39786
  text3 = `for (( ${command7.expressions.map((expression) => expression?.source ?? "").join("; ")} ));
39686
39787
  do
39687
- ${nested}${scriptText(command7.body, nested)};
39788
+ ${nested}${scriptText(command7.body, nested, true)}
39688
39789
  ${indent}done`;
39689
39790
  break;
39690
39791
  case "conditional":
@@ -39703,26 +39804,26 @@ ${indent}}`;
39703
39804
  ${indent}${commandText(command7.body, indent)}`;
39704
39805
  break;
39705
39806
  case "if":
39706
- text3 = command7.branches.map((branch, index) => `${index ? "elif" : "if"} ${scriptText(branch.condition, indent)}; then
39707
- ${nested}${scriptText(branch.body, nested)};`).join(`
39807
+ text3 = command7.branches.map((branch, index) => `${index ? "elif" : "if"} ${scriptText(branch.condition, indent, true)} then
39808
+ ${nested}${scriptText(branch.body, nested, true)}`).join(`
39708
39809
  ${indent}`);
39709
39810
  if (command7.otherwise) text3 += `
39710
39811
  ${indent}else
39711
- ${nested}${scriptText(command7.otherwise, nested)};`;
39812
+ ${nested}${scriptText(command7.otherwise, nested, true)}`;
39712
39813
  text3 += `
39713
39814
  ${indent}fi`;
39714
39815
  break;
39715
39816
  case "while":
39716
39817
  case "until":
39717
- text3 = `${command7.kind} ${scriptText(command7.condition, indent)}; do
39718
- ${nested}${scriptText(command7.body, nested)};
39818
+ text3 = `${command7.kind} ${scriptText(command7.condition, indent, true)} do
39819
+ ${nested}${scriptText(command7.body, nested, true)}
39719
39820
  ${indent}done`;
39720
39821
  break;
39721
39822
  case "for":
39722
39823
  case "select":
39723
39824
  text3 = `${command7.kind} ${command7.name}${command7.words ? ` in ${command7.words.map(spelling).join(" ")}` : ""};
39724
39825
  do
39725
- ${nested}${scriptText(command7.body, nested)};
39826
+ ${nested}${scriptText(command7.body, nested, true)}
39726
39827
  ${indent}done`;
39727
39828
  break;
39728
39829
  case "case":
@@ -41626,7 +41727,7 @@ var StateMonitor = class {
41626
41727
  proxy = new Proxy(value2, {
41627
41728
  get(target, key, receiver) {
41628
41729
  const entry = Reflect.get(target, key, receiver);
41629
- if (entry && typeof entry === "object" && key !== "redirectAssignments") {
41730
+ if (entry && typeof entry === "object" && key !== "redirectAssignments" && key !== "backgroundJobs") {
41630
41731
  if (field2 === "state" && key === "functions") return monitor.wrap(entry, "functions");
41631
41732
  if (field2 !== "functions") return monitor.wrap(entry, field2 === "state" ? String(key) : field2);
41632
41733
  }
@@ -42117,6 +42218,7 @@ var shellBuiltinNames = /* @__PURE__ */ new Set([
42117
42218
  "declare",
42118
42219
  "mapfile",
42119
42220
  "readarray",
42221
+ "wait",
42120
42222
  "exit",
42121
42223
  "return",
42122
42224
  "break",
@@ -42176,6 +42278,8 @@ function resolveLimits(...limits) {
42176
42278
  }
42177
42279
  return result;
42178
42280
  }
42281
+ var runtimeFileSystems = /* @__PURE__ */ new WeakMap();
42282
+ var inputScopes = /* @__PURE__ */ new WeakMap();
42179
42283
  var budgetedSinks = /* @__PURE__ */ new WeakMap();
42180
42284
  async function sortExpansionStrings(values, work) {
42181
42285
  const compare6 = async (left, right) => {
@@ -42324,6 +42428,7 @@ var Budget25 = class {
42324
42428
  await interruptible3(sink.write(chunk), signal);
42325
42429
  }
42326
42430
  };
42431
+ forwardBackgroundDestination(output2, sink);
42327
42432
  budgetedSinks.set(output2, { budget: this, write: output2.write, ...ownership?.write === sink.write && ownership.file ? { file: ownership.file } : {} });
42328
42433
  return output2;
42329
42434
  }
@@ -42498,6 +42603,7 @@ function signalSink(sink, signal) {
42498
42603
  await interruptible3(write2(chunk), signal);
42499
42604
  }
42500
42605
  };
42606
+ forwardBackgroundDestination(output2, sink);
42501
42607
  if (owned) budgetedSinks.set(output2, { ...owned, write: output2.write });
42502
42608
  return output2;
42503
42609
  }
@@ -42513,6 +42619,7 @@ function bindCommandIO(context2) {
42513
42619
  async function cloneState(state, signal, scope, inheritLocals = true) {
42514
42620
  const destination2 = await snapshotState(state, () => ({
42515
42621
  ...state,
42622
+ ...state.backgroundJobs ? { backgroundJobs: new BackgroundJobs(state.backgroundJobs.execution) } : {},
42516
42623
  variables: Object.assign(/* @__PURE__ */ Object.create(null), state.variables),
42517
42624
  exported: new Set(state.exported),
42518
42625
  functions: new Map(state.functions),
@@ -43157,6 +43264,7 @@ var Runtime = class _Runtime {
43157
43264
  this.outcomeFrame = outcomeFrame;
43158
43265
  this.inputProfile = inputProfile;
43159
43266
  this.fs = scopeFileSystem(fs, () => budget.fileSystemOperation(), signal, () => budget.fileSystemCleanupOperation());
43267
+ runtimeFileSystems.set(this.fs, runtimeFileSystems.get(fs) ?? fs);
43160
43268
  const checkpoint = () => budget.cpuCheckpoint();
43161
43269
  registerYieldCheckpoint(signal, checkpoint);
43162
43270
  registerYieldCheckpoint(commandSignal, checkpoint);
@@ -43877,6 +43985,81 @@ var Runtime = class _Runtime {
43877
43985
  }
43878
43986
  async script(script, state, io) {
43879
43987
  for (const list of script.lists) {
43988
+ if (list.background) {
43989
+ const jobs = state.backgroundJobs;
43990
+ const id = jobs.execution.identifier();
43991
+ const scope = jobs.execution.scope.child();
43992
+ const inheritedDestinations = new Set([io.stdout, io.stderr, ...[...io.descriptors?.values() ?? []].flatMap((descriptor) => descriptor.output && !descriptor.closed ? [descriptor.output] : [])].map(backgroundDestination));
43993
+ const leases = (io[backgroundResources] ?? []).filter((resource) => !resource.destination || inheritedDestinations.has(resource.destination)).map((resource) => ({ resource, release: resource.retain() }));
43994
+ const releases = leases.map((lease) => lease.release);
43995
+ let child;
43996
+ try {
43997
+ child = await cloneState(state, this.signal, scope);
43998
+ } catch (error) {
43999
+ releases.forEach((release) => release());
44000
+ await scope.close();
44001
+ throw error;
44002
+ }
44003
+ child.isolated = true;
44004
+ child.loopDepth = 0;
44005
+ const inherited = isolateIO(activeIO(io));
44006
+ const redirectsInput = list.pipelines[0].commands[0].redirects.some((redirect) => redirect.descriptor === 0);
44007
+ const stdin = redirectsInput ? inherited.stdin : toByteSource("");
44008
+ const descriptors = new Map(inherited.descriptors);
44009
+ descriptors.set(0, { input: stdin, stdinIsDefault: false });
44010
+ let initialRedirection = true;
44011
+ const backgroundIO = {
44012
+ ...inherited,
44013
+ [invocationScope]: scope,
44014
+ stdin,
44015
+ stdinIsDefault: false,
44016
+ descriptors,
44017
+ [backgroundPrune]: list.pipelines.length === 1 ? (descriptors2) => {
44018
+ if (!initialRedirection) return;
44019
+ initialRedirection = false;
44020
+ const destinations3 = new Set([...descriptors2.values()].flatMap((descriptor) => descriptor.output && !descriptor.closed ? [backgroundDestination(descriptor.output)] : []));
44021
+ for (const lease of leases) if (lease.resource.destination && !destinations3.has(lease.resource.destination)) lease.release();
44022
+ } : void 0
44023
+ };
44024
+ const runtime = new _Runtime(
44025
+ runtimeFileSystems.get(this.fs) ?? this.fs,
44026
+ this.commands,
44027
+ this.middleware,
44028
+ this.budget,
44029
+ AbortSignal.any([this.commandSignal, scope.signal]),
44030
+ this.fileWrites,
44031
+ this.outputFiles,
44032
+ this.commandSignal,
44033
+ this.cancellation,
44034
+ this.cancellationState,
44035
+ this.cancellationOwner,
44036
+ this.cancellationDepth,
44037
+ this.cancellationMaxDepth,
44038
+ void 0,
44039
+ this.inputProfile
44040
+ );
44041
+ try {
44042
+ state.lastBackgroundJob = jobs.launch(id, async () => {
44043
+ try {
44044
+ try {
44045
+ return (await runtime.runUnit({ lists: [{ ...list, background: false }] }, child, backgroundIO)).exitCode;
44046
+ } catch (error) {
44047
+ if (error instanceof Flow2 && error.kind === "return") return error.status;
44048
+ throw error;
44049
+ }
44050
+ } finally {
44051
+ releases.forEach((release) => release());
44052
+ await scope.close();
44053
+ }
44054
+ });
44055
+ } catch (error) {
44056
+ releases.forEach((release) => release());
44057
+ await scope.close();
44058
+ throw error;
44059
+ }
44060
+ state.status = 0;
44061
+ continue;
44062
+ }
43880
44063
  for (let index = 0; index < list.pipelines.length; index++) {
43881
44064
  const operator = list.operators[index - 1];
43882
44065
  if (operator === "&&" && state.status !== 0 || operator === "||" && state.status === 0) continue;
@@ -43918,13 +44101,18 @@ var Runtime = class _Runtime {
43918
44101
  const completed = /* @__PURE__ */ new Set();
43919
44102
  const closing = /* @__PURE__ */ new Set();
43920
44103
  let statuses;
44104
+ const stageLeases = [];
43921
44105
  try {
43922
44106
  for (let index = 1; index < pipeline.commands.length; index++) pipes.push(createBytePipe({
43923
44107
  highWaterMark: this.budget.limits.pipeHighWaterMark,
43924
44108
  signal: this.signal
43925
44109
  }));
43926
44110
  for (let index = 0; index < pipeline.commands.length; index++) controllers.push(new AbortController());
44111
+ stageLeases.push(...pipeline.commands.map(() => (io[backgroundResources] ?? []).filter((resource) => resource.destination).map((resource) => ({ resource, release: resource.retain() }))));
44112
+ io[backgroundPrune]?.(/* @__PURE__ */ new Map());
43927
44113
  const tasks = pipeline.commands.map(async (command7, index) => {
44114
+ let initialRedirection = true;
44115
+ const inheritedLeases = stageLeases[index];
43928
44116
  const incoming = pipes[index - 1];
43929
44117
  const outgoing = pipes[index];
43930
44118
  const childDepth = this.cancellationDepth + 1;
@@ -43978,6 +44166,8 @@ var Runtime = class _Runtime {
43978
44166
  throw error;
43979
44167
  }
43980
44168
  } };
44169
+ const resources = new BackgroundResources();
44170
+ resources.destination = backgroundDestination(pipeOutput ?? io.stdout);
43981
44171
  const executeStage = async () => {
43982
44172
  try {
43983
44173
  let exitCode;
@@ -43986,6 +44176,13 @@ var Runtime = class _Runtime {
43986
44176
  child.isolated = true;
43987
44177
  const work = runtime.runCommandIsolated(command7, child, {
43988
44178
  ...isolateIO(io),
44179
+ [backgroundResources]: [...io[backgroundResources] ?? [], resources],
44180
+ [backgroundPrune]: (descriptors) => {
44181
+ if (!initialRedirection) return;
44182
+ initialRedirection = false;
44183
+ const destinations3 = new Set([...descriptors.values()].flatMap((descriptor) => descriptor.output && !descriptor.closed ? [backgroundDestination(descriptor.output)] : []));
44184
+ for (const lease of inheritedLeases) if (!destinations3.has(lease.resource.destination)) lease.release();
44185
+ },
43989
44186
  stdin: input3,
43990
44187
  ...incoming ? { stdinIsDefault: false } : {},
43991
44188
  stdout: pipeOutput ? this.budget.sink(pipeOutput, signal) : signalSink(io.stdout, signal),
@@ -43993,12 +44190,14 @@ var Runtime = class _Runtime {
43993
44190
  }).finally(() => stateMonitor(child)?.closeValues());
43994
44191
  retain(work);
43995
44192
  exitCode = await interruptible3(work, signal);
44193
+ if (resources.busy) await interruptible3(resources.drain(), signal);
43996
44194
  } catch (error) {
43997
44195
  if (!(error instanceof PipelineClosed)) throw error;
43998
44196
  exitCode = 141;
43999
44197
  }
44000
44198
  return { exitCode };
44001
44199
  } finally {
44200
+ inheritedLeases.forEach((lease) => lease.release());
44002
44201
  completed.add(index);
44003
44202
  if (incoming) {
44004
44203
  const upstream = index - 1;
@@ -44029,6 +44228,7 @@ var Runtime = class _Runtime {
44029
44228
  statuses = await interruptible3(Promise.all(tasks), this.signal);
44030
44229
  } finally {
44031
44230
  try {
44231
+ for (const leases of stageLeases) for (const lease of leases) lease.release();
44032
44232
  for (const close of closing) cancelTurn(close);
44033
44233
  for (const [index, controller] of controllers.entries()) if (!completed.has(index) || written.has(index)) controller.abort(new PipelineClosed());
44034
44234
  const aborts = pipes.map((pipe) => pipe.abort());
@@ -44102,13 +44302,20 @@ var Runtime = class _Runtime {
44102
44302
  this.budget.tick();
44103
44303
  if (this.budget.commands % 128 === 0) await yieldTurn(this.signal);
44104
44304
  this.signal.throwIfAborted();
44305
+ const resources = new BackgroundResources();
44306
+ originalIO = { ...originalIO, [backgroundResources]: [...originalIO[backgroundResources] ?? [], resources] };
44105
44307
  const inputs2 = /* @__PURE__ */ new Set();
44106
44308
  const outputs = /* @__PURE__ */ new Set();
44107
44309
  const finishOutputs = async (status2) => {
44108
44310
  const pending = [...outputs];
44109
44311
  outputs.clear();
44110
- const settled = await Promise.allSettled(pending.map((close) => close({ status: status2 })));
44111
- throwCleanupFailures(settled.filter((result) => result.status === "rejected").map((result) => result.reason));
44312
+ const finish = async () => {
44313
+ await resources.drain();
44314
+ const settled = await Promise.allSettled(pending.map((close) => close({ status: status2 })));
44315
+ throwCleanupFailures(settled.filter((result) => result.status === "rejected").map((result) => result.reason));
44316
+ };
44317
+ if (resources.busy) state.backgroundJobs.execution.track(finish());
44318
+ else await finish();
44112
44319
  };
44113
44320
  const allocation = this.budget.values.scope();
44114
44321
  originalIO = { ...originalIO, [valueScope]: allocation };
@@ -44403,14 +44610,25 @@ var Runtime = class _Runtime {
44403
44610
  return status2;
44404
44611
  } finally {
44405
44612
  try {
44406
- await Promise.allSettled([
44407
- ...[...outputs].map(async (close) => close({ reason: new FsError("ECANCELED", { syscall: "redirect" }) })),
44408
- ...[...inputs2].map(async (input3) => input3.close())
44409
- ]).then((results) => {
44410
- const failures = results.filter((result) => result.status === "rejected").map((result) => result.reason);
44411
- if (diagnosticFailure) io[invocationScope].failures.push(...failures);
44412
- else throwCleanupFailures(failures);
44413
- }).finally(() => allocation.close());
44613
+ const close = async () => {
44614
+ await resources.drain();
44615
+ await Promise.allSettled([
44616
+ ...[...outputs].map(async (close2) => close2({ reason: new FsError("ECANCELED", { syscall: "redirect" }) })),
44617
+ ...[...inputs2].map(async (input3) => {
44618
+ try {
44619
+ await input3.close();
44620
+ } finally {
44621
+ await inputScopes.get(input3)?.close();
44622
+ }
44623
+ })
44624
+ ]).then((results) => {
44625
+ const failures = results.filter((result) => result.status === "rejected").map((result) => result.reason);
44626
+ if (diagnosticFailure) io[invocationScope].failures.push(...failures);
44627
+ else throwCleanupFailures(failures);
44628
+ }).finally(() => allocation.close());
44629
+ };
44630
+ if (resources.busy) state.backgroundJobs.execution.track(close());
44631
+ else await close();
44414
44632
  } finally {
44415
44633
  finishSnapshot?.();
44416
44634
  await snapshotScope?.close();
@@ -44486,6 +44704,8 @@ var Runtime = class _Runtime {
44486
44704
  const stdinIsDefault = descriptor?.input ? descriptor.stdinIsDefault : false;
44487
44705
  return {
44488
44706
  [invocationScope]: io[invocationScope],
44707
+ [backgroundResources]: io[backgroundResources],
44708
+ [backgroundPrune]: io[backgroundPrune],
44489
44709
  ...io[valueScope] === void 0 ? {} : { [valueScope]: io[valueScope] },
44490
44710
  ...io.execution === void 0 ? {} : { execution: io.execution },
44491
44711
  ...io.diagnosticLine === void 0 ? {} : { diagnosticLine: io.diagnosticLine },
@@ -44550,15 +44770,28 @@ var Runtime = class _Runtime {
44550
44770
  await interruptible3(this.fs.access(path, 4, options2), this.signal);
44551
44771
  const stat = await interruptible3(this.fs.stat(path, options2), this.signal);
44552
44772
  if (stat.type === "directory" && !fileShortcut) throw new PublicDiagnostic(`${target}: Is a directory`);
44553
- const source2 = stat.type === "directory" ? toByteSource("") : await fileInput(this.fs, path, this.budget.limits.maxInputBytes, this.signal, this.inputProfile, { stat, registerCleanup: (cleanup) => {
44554
- io[invocationScope].register(cleanup);
44555
- } });
44556
- const input3 = new ShellInput(source2, this.budget, this.signal, { stat, ...source2 });
44557
- inputs2.add(input3);
44773
+ const inputScope = (state.backgroundJobs?.execution.scope ?? io[invocationScope]).child();
44774
+ let input3;
44775
+ try {
44776
+ const inputSignal = this.commandSignal;
44777
+ const inputFs = scopeFileSystem(runtimeFileSystems.get(this.fs) ?? this.fs, () => this.budget.fileSystemOperation(), inputSignal, () => this.budget.fileSystemCleanupOperation());
44778
+ const source2 = stat.type === "directory" ? toByteSource("") : await fileInput(inputFs, path, this.budget.limits.maxInputBytes, inputSignal, this.inputProfile, { stat, registerCleanup: (cleanup) => {
44779
+ inputScope.register(cleanup);
44780
+ } });
44781
+ input3 = new ShellInput(source2, this.budget, inputSignal, { stat, ...source2 });
44782
+ inputScopes.set(input3, inputScope);
44783
+ inputs2.add(input3);
44784
+ } catch (error) {
44785
+ await inputScope.close();
44786
+ throw error;
44787
+ }
44558
44788
  descriptors.set(redirect.descriptor, { input: input3, stdinIsDefault: false });
44559
44789
  } else {
44790
+ const outputSignal = this.commandSignal;
44791
+ const outputFs = scopeFileSystem(runtimeFileSystems.get(this.fs) ?? this.fs, () => this.budget.fileSystemOperation(), outputSignal, () => this.budget.fileSystemCleanupOperation());
44792
+ const options3 = { signal: outputSignal };
44560
44793
  const append = redirect.operator === ">>";
44561
- const capabilities = await this.fs.capabilitiesFor?.(path, options2) ?? this.fs.capabilities;
44794
+ const capabilities = await outputFs.capabilitiesFor?.(path, options3) ?? outputFs.capabilities;
44562
44795
  const random = capabilities.randomAccessWrite === true;
44563
44796
  const key = resolvePath(state.cwd, path);
44564
44797
  let file;
@@ -44567,15 +44800,15 @@ var Runtime = class _Runtime {
44567
44800
  if (!random && file.references) throw new FsError("ENOTSUP", { path, message: "Conflicting sequential output descriptors" });
44568
44801
  file.references++;
44569
44802
  this.outputFiles.set(key, file);
44570
- });
44803
+ }, outputSignal);
44571
44804
  let closed = false;
44572
44805
  let offset = 0;
44573
44806
  const incremental = async () => {
44574
44807
  await this.fileOperation(key, async () => {
44575
- if (append) await this.fs.appendFile(path, new Uint8Array(), options2);
44576
- else await this.fs.writeFile(path, new Uint8Array(), { ...options2, flag: "w" });
44808
+ if (append) await outputFs.appendFile(path, new Uint8Array(), options3);
44809
+ else await outputFs.writeFile(path, new Uint8Array(), { ...options3, flag: "w" });
44577
44810
  if (!append) file.data = new Uint8Array();
44578
- });
44811
+ }, outputSignal);
44579
44812
  return { write: (chunk) => {
44580
44813
  const copy2 = new Uint8Array(chunk);
44581
44814
  return this.fileOperation(key, async () => {
@@ -44584,25 +44817,25 @@ var Runtime = class _Runtime {
44584
44817
  let atEOF = false;
44585
44818
  if (!append && current && offset === current.length && capabilities.append === true && capabilities.stat !== false) {
44586
44819
  try {
44587
- atEOF = (await interruptible3(this.fs.stat(path, options2), this.signal)).size === offset;
44820
+ atEOF = (await interruptible3(outputFs.stat(path, options3), outputSignal)).size === offset;
44588
44821
  } catch {
44589
- this.signal.throwIfAborted();
44822
+ outputSignal.throwIfAborted();
44590
44823
  }
44591
- this.signal.throwIfAborted();
44824
+ outputSignal.throwIfAborted();
44592
44825
  }
44593
44826
  if (append || atEOF) {
44594
44827
  const bytes3 = current ? appendOutputBytes(current, copy2) : void 0;
44595
- await this.fs.appendFile(path, copy2, options2);
44828
+ await outputFs.appendFile(path, copy2, options3);
44596
44829
  file.data = bytes3;
44597
44830
  } else {
44598
44831
  const bytes3 = new Uint8Array(Math.max(current?.length ?? 0, offset + copy2.length));
44599
44832
  if (current) bytes3.set(current);
44600
44833
  bytes3.set(copy2, offset);
44601
- await this.fs.writeFile(path, bytes3, options2);
44834
+ await outputFs.writeFile(path, bytes3, options3);
44602
44835
  file.data = bytes3;
44603
44836
  }
44604
44837
  if (!append) offset += copy2.length;
44605
- });
44838
+ }, outputSignal);
44606
44839
  } };
44607
44840
  };
44608
44841
  const resumePathCache = this.budget.pathLookup.suspend();
@@ -44615,8 +44848,8 @@ var Runtime = class _Runtime {
44615
44848
  let target2;
44616
44849
  let outputScope;
44617
44850
  try {
44618
- outputScope = io[invocationScope].child();
44619
- target2 = await openFileOutput({ fs: this.fs, signal: this.signal, registerCleanup: (cleanup) => {
44851
+ outputScope = (state.backgroundJobs?.execution.scope ?? io[invocationScope]).child();
44852
+ target2 = await openFileOutput({ fs: outputFs, signal: outputSignal, registerCleanup: (cleanup) => {
44620
44853
  outputScope.register(cleanup);
44621
44854
  } }, path, append ? "a" : "w", random ? incremental : void 0);
44622
44855
  } catch (error) {
@@ -44629,13 +44862,13 @@ var Runtime = class _Runtime {
44629
44862
  }
44630
44863
  outputs.add(async (completion) => {
44631
44864
  try {
44632
- if (this.signal.aborted) await target2.abort(this.signal.reason);
44865
+ if (outputSignal.aborted) await target2.abort(outputSignal.reason);
44633
44866
  else if ("reason" in completion) await target2.abort(completion.reason);
44634
44867
  else {
44635
44868
  try {
44636
44869
  await target2.finish();
44637
44870
  } catch (error) {
44638
- this.signal.throwIfAborted();
44871
+ outputSignal.throwIfAborted();
44639
44872
  if (completion.status === 0) throw error;
44640
44873
  }
44641
44874
  }
@@ -44647,7 +44880,7 @@ var Runtime = class _Runtime {
44647
44880
  }
44648
44881
  }
44649
44882
  });
44650
- const output2 = this.budget.sink(target2.sink, this.signal);
44883
+ const output2 = this.budget.sink(target2.sink, outputSignal);
44651
44884
  budgetedSinks.get(output2).file = Object.freeze({ path });
44652
44885
  descriptors.set(redirect.descriptor, { output: output2 });
44653
44886
  if (redirect.operator === "&>") {
@@ -44661,17 +44894,18 @@ var Runtime = class _Runtime {
44661
44894
  const diagnostic4 = errorTarget === void 0 ? void 0 : filesystemDiagnostic(error, errorTarget);
44662
44895
  throw new ExecutionFailure(error, currentIO(), diagnostic4);
44663
44896
  }
44897
+ io[backgroundPrune]?.(descriptors);
44664
44898
  return currentIO();
44665
44899
  }
44666
- async fileOperation(path, operation2) {
44900
+ async fileOperation(path, operation2, signal = this.signal) {
44667
44901
  const previous = this.fileWrites.get(path) ?? Promise.resolve();
44668
44902
  const pending = previous.catch(() => void 0).then(() => {
44669
- this.signal.throwIfAborted();
44903
+ signal.throwIfAborted();
44670
44904
  return operation2();
44671
44905
  });
44672
44906
  this.fileWrites.set(path, pending);
44673
44907
  try {
44674
- await interruptible3(pending, this.signal);
44908
+ await interruptible3(pending, signal);
44675
44909
  } finally {
44676
44910
  if (this.fileWrites.get(path) === pending) this.fileWrites.delete(path);
44677
44911
  }
@@ -44742,7 +44976,11 @@ var Runtime = class _Runtime {
44742
44976
  };
44743
44977
  let overlayOpen = false;
44744
44978
  try {
44745
- if (snapshotScope) state = await cloneState(state, this.signal, snapshotScope);
44979
+ if (snapshotScope) {
44980
+ const jobs = state.backgroundJobs;
44981
+ state = await cloneState(state, this.signal, snapshotScope);
44982
+ state.backgroundJobs = jobs;
44983
+ }
44746
44984
  if (words.length) {
44747
44985
  stateMonitor(state)?.openOverlay(previous);
44748
44986
  overlayOpen = true;
@@ -44867,6 +45105,8 @@ var Runtime = class _Runtime {
44867
45105
  async dispatchScoped(name2, values, state, io, assignments2, bypassFunctions) {
44868
45106
  const { [invocationScope]: scope, ...publicIO } = io;
44869
45107
  Reflect.deleteProperty(publicIO, valueScope);
45108
+ Reflect.deleteProperty(publicIO, backgroundResources);
45109
+ Reflect.deleteProperty(publicIO, backgroundPrune);
44870
45110
  const allocation = this.budget.values.scope();
44871
45111
  scope.register(() => allocation.close());
44872
45112
  const argumentValues = this.admitArguments(values, allocation);
@@ -44928,6 +45168,8 @@ var Runtime = class _Runtime {
44928
45168
  args: admitted.args,
44929
45169
  argumentValues: admitted,
44930
45170
  [invocationScope]: scope,
45171
+ [backgroundResources]: io[backgroundResources],
45172
+ [backgroundPrune]: io[backgroundPrune],
44931
45173
  ...io[valueScope] === void 0 ? {} : { [valueScope]: io[valueScope] }
44932
45174
  };
44933
45175
  const previous = /* @__PURE__ */ new Map();
@@ -45311,6 +45553,7 @@ ${functionDisplay(name2, state.functions.get(name2))}`;
45311
45553
  directoryStack: { entries: [], bytes: 0 },
45312
45554
  dotglob: false,
45313
45555
  globstar: false,
45556
+ backgroundJobs: new BackgroundJobs(state.backgroundJobs.execution),
45314
45557
  positional: [],
45315
45558
  arg0: shellValueText(arg0),
45316
45559
  profile: context2.command === "sh" ? "sh" : "bash",
@@ -45535,6 +45778,8 @@ ${prefix3} line ${offset + line}: \`${source2.split("\n")[line - 1] ?? ""}'
45535
45778
  };
45536
45779
  Reflect.deleteProperty(context2, invocationScope);
45537
45780
  Reflect.deleteProperty(context2, valueScope);
45781
+ Reflect.deleteProperty(context2, backgroundResources);
45782
+ Reflect.deleteProperty(context2, backgroundPrune);
45538
45783
  bindCommandIO(context2);
45539
45784
  bindFileOutputBudget(context2, (sink) => this.budget.sink(sink, runtime.signal));
45540
45785
  if (argumentValues.values.every((value2) => typeof value2 === "string")) Reflect.deleteProperty(context2, "argumentValues");
@@ -46659,10 +46904,108 @@ ${prefix3} line ${offset + line}: \`${source2.split("\n")[line - 1] ?? ""}'
46659
46904
  }
46660
46905
  }
46661
46906
  }
46907
+ async waitBuiltin(context2, state) {
46908
+ let next = false;
46909
+ let variable;
46910
+ let index = 0;
46911
+ for (; index < context2.args.length; index++) {
46912
+ const option = context2.args[index];
46913
+ if (option === "--") {
46914
+ index++;
46915
+ break;
46916
+ }
46917
+ if (!option.startsWith("-") || option === "-") break;
46918
+ for (let flag = 1; flag < option.length; flag++) {
46919
+ const letter = option[flag];
46920
+ if (letter === "n") next = true;
46921
+ else if (letter === "f") {
46922
+ } else if (letter === "p") {
46923
+ variable = option.slice(flag + 1) || context2.args[++index];
46924
+ if (variable === void 0) {
46925
+ await writeDiagnostic(context2.stderr, "wait: -p: option requires an argument\n");
46926
+ return 2;
46927
+ }
46928
+ if (!/^[a-zA-Z_][a-zA-Z_0-9]*(?:\[.+\])?$/su.test(variable)) {
46929
+ await writeDiagnostic(context2.stderr, `wait: ${variable}: not a valid identifier
46930
+ `);
46931
+ return 1;
46932
+ }
46933
+ break;
46934
+ } else {
46935
+ await writeDiagnostic(context2.stderr, `wait: -${letter}: invalid option
46936
+ `);
46937
+ return 2;
46938
+ }
46939
+ }
46940
+ }
46941
+ const target = variable === void 0 ? void 0 : /^([a-zA-Z_][a-zA-Z_0-9]*)(?:\[(.+)\])?$/su.exec(variable);
46942
+ if (target) {
46943
+ const name2 = target[1];
46944
+ if (state.readonlyVariables?.has(name2)) {
46945
+ await writeDiagnostic(context2.stderr, `wait: ${name2}: readonly variable
46946
+ `);
46947
+ return 1;
46948
+ }
46949
+ if (target[2] === void 0) {
46950
+ if (arrayStore(state)?.get(name2)) await this.unsetIndexed(state, name2);
46951
+ else this.unsetVariable(state, name2);
46952
+ }
46953
+ }
46954
+ const publish4 = async (id) => {
46955
+ if (!target) return;
46956
+ if (target[2] === void 0) await this.assignVariable(state, target[1], id);
46957
+ else await this.arrayAssignment({ kind: "element", name: target[1], index: { decimal: target[2], source: target[2] }, append: false, value: { offset: 0, parts: [{ kind: "text", value: id, quoted: true }] } }, state, context2);
46958
+ };
46959
+ const table = state.backgroundJobs;
46960
+ const ids = context2.args.slice(index);
46961
+ const selected = [];
46962
+ let invalid3 = false;
46963
+ for (const id of ids) {
46964
+ if (!id.startsWith("%") && !/^[0-9]+$/u.test(id)) {
46965
+ await writeDiagnostic(context2.stderr, `wait: ${id}: not a pid or valid job spec
46966
+ `);
46967
+ if (next) continue;
46968
+ return 1;
46969
+ }
46970
+ const job = table.resolve(id);
46971
+ if (!job) {
46972
+ await writeDiagnostic(context2.stderr, `wait: ${id}: not a child of this shell
46973
+ `);
46974
+ invalid3 = true;
46975
+ } else selected.push(job);
46976
+ }
46977
+ if (next) {
46978
+ const eligible = ids.length ? selected.filter((job) => !job.waited) : [...table.jobs.values()].filter((job) => !job.done && !job.consumed);
46979
+ if (!eligible.length) return 127;
46980
+ const result = await interruptible3(Promise.race(eligible.map(async (job) => ({ job, status: await job.promise }))), this.signal);
46981
+ result.job.consumed = true;
46982
+ await publish4(result.job.id);
46983
+ return result.status;
46984
+ }
46985
+ if (!ids.length) {
46986
+ await interruptible3(Promise.all([...table.jobs.values()].map((job) => job.promise)), this.signal);
46987
+ table.jobs.clear();
46988
+ return 0;
46989
+ }
46990
+ let status2 = invalid3 ? 127 : 0;
46991
+ for (const id of ids) {
46992
+ const job = table.resolve(id);
46993
+ if (!job) {
46994
+ status2 = 127;
46995
+ continue;
46996
+ }
46997
+ status2 = await interruptible3(job.promise, this.signal);
46998
+ job.consumed = true;
46999
+ job.waited = true;
47000
+ await publish4(job.id);
47001
+ }
47002
+ return status2;
47003
+ }
46662
47004
  async builtin(context2, state, assignments2, diagnose, suppressSpecial = false) {
46663
47005
  const { command: command7, args, stdout, stderr } = context2;
46664
47006
  if (command7 === ":" || command7 === "true") return 0;
46665
47007
  if (command7 === "false") return 1;
47008
+ if (command7 === "wait") return this.waitBuiltin(context2, state);
46666
47009
  if (command7 === "shopt") return this.shoptBuiltin(context2, state);
46667
47010
  if (command7 === "let") return this.letBuiltin(context2, state);
46668
47011
  if (command7 === "mapfile" || command7 === "readarray") return this.mapfileBuiltin(context2, state);
@@ -47375,10 +47718,13 @@ ${prefix3} line ${offset + line}: \`${source2.split("\n")[line - 1] ?? ""}'
47375
47718
  command8,
47376
47719
  part.sourceLine === void 0 ? warningLine + (command8.line ?? part.line) - part.line : warningLine + line - 1
47377
47720
  );
47378
- const captureIO = { ...isolateIO(io), substitutionDiagnosticLines, diagnosticOffset: (io.diagnosticLine ?? part.line) - (part.sourceLine ?? part.line), stdout: this.budget.sink(capture, this.signal) };
47721
+ const resources = new BackgroundResources();
47722
+ const captureIO = { ...isolateIO(io), [backgroundResources]: [...io[backgroundResources] ?? [], resources], substitutionDiagnosticLines, diagnosticOffset: (io.diagnosticLine ?? part.line) - (part.sourceLine ?? part.line), stdout: this.budget.sink(capture, this.signal) };
47723
+ resources.destination = backgroundDestination(captureIO.stdout);
47379
47724
  try {
47380
47725
  state.substitutionStatus = fileShortcut ? await this.runCommandIsolated(command7, child, captureIO, true) : await this.run(part.script, child, captureIO);
47381
47726
  } finally {
47727
+ await interruptible3(resources.drain(), this.signal);
47382
47728
  stateMonitor(child)?.closeValues();
47383
47729
  }
47384
47730
  state.status = state.substitutionStatus;
@@ -47416,7 +47762,7 @@ ${prefix3} line ${offset + line}: \`${source2.split("\n")[line - 1] ?? ""}'
47416
47762
  io[valueScope]?.reserve(values.length * 32 + 64, 0);
47417
47763
  return concatShellValues(values.flatMap((value3, index) => index ? [separator, value3] : [value3]), io[valueScope]);
47418
47764
  }
47419
- let value2 = part.name === "?" ? String(state.status) : part.name === "-" ? `${state.errexit ? "e" : ""}${state.nounset ? "u" : ""}${state.braceexpand !== false ? "B" : ""}` : part.name === "#" ? String(state.positional.length) : part.name === "@" || part.name === "*" ? state.positional.join(hereString && (part.name === "@" || !part.quoted) ? " " : Array.from(state.variables.IFS ?? " ")[0] ?? "") : /^0+$/u.test(part.name) ? state.arg0 ?? "virtual-bash" : /^\d+$/u.test(part.name) ? state.positional[Number(part.name) - 1] : this.variable(state, part.name);
47765
+ let value2 = part.name === "!" ? state.lastBackgroundJob : part.name === "?" ? String(state.status) : part.name === "-" ? `${state.errexit ? "e" : ""}${state.nounset ? "u" : ""}${state.braceexpand !== false ? "B" : ""}` : part.name === "#" ? String(state.positional.length) : part.name === "@" || part.name === "*" ? state.positional.join(hereString && (part.name === "@" || !part.quoted) ? " " : Array.from(state.variables.IFS ?? " ")[0] ?? "") : /^0+$/u.test(part.name) ? state.arg0 ?? "virtual-bash" : /^\d+$/u.test(part.name) ? state.positional[Number(part.name) - 1] : this.variable(state, part.name);
47420
47766
  let retained = value2;
47421
47767
  if (!part.length && value2 !== void 0) {
47422
47768
  if (/^[a-zA-Z_][a-zA-Z_0-9]*$/u.test(part.name) && !arrayStore(state)?.get(part.name)) retained = stateMonitor(state)?.values.get(part.name, value2) ?? value2;
@@ -49064,6 +49410,7 @@ var Shell = class {
49064
49410
  stdout: sink(stdout, options2.stdout),
49065
49411
  stderr: sink(stderr, options2.stderr)
49066
49412
  };
49413
+ const background = new BackgroundExecution(scope, () => budget.tick());
49067
49414
  let exitCode;
49068
49415
  let failed = false;
49069
49416
  try {
@@ -49082,6 +49429,7 @@ var Shell = class {
49082
49429
  variables.OPTIND = "1";
49083
49430
  variables.OPTERR = "1";
49084
49431
  const state = {
49432
+ backgroundJobs: new BackgroundJobs(background),
49085
49433
  cwd,
49086
49434
  variables,
49087
49435
  exported,
@@ -49157,7 +49505,13 @@ shell: -c: line ${line}: \`${source2.split("\n")[line - 1] ?? ""}'
49157
49505
  } finally {
49158
49506
  if (failed) await stdin?.close().catch(() => {
49159
49507
  });
49160
- else await stdin?.close();
49508
+ else {
49509
+ try {
49510
+ await interruptible3(background.drain(), budget.signal);
49511
+ } finally {
49512
+ await stdin?.close();
49513
+ }
49514
+ }
49161
49515
  }
49162
49516
  const stdoutBytes = stdout.takeBytes();
49163
49517
  const stderrBytes = stderr.takeBytes();