@poe-platform/safe-bash 0.1.53 → 0.1.55
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.
- package/dist/safe-bash/browser.d.ts +2 -0
- package/dist/safe-bash/browser.d.ts.map +1 -1
- package/dist/safe-bash/browser.js +3951 -874
- package/dist/safe-bash/browser.js.map +4 -4
- package/dist/safe-bash/commands/basic.d.ts.map +1 -1
- package/dist/safe-bash/commands/basic.js +7 -1
- package/dist/safe-bash/commands/basic.js.map +1 -1
- package/dist/safe-bash/commands/filesystem-requirements.d.ts +67 -0
- package/dist/safe-bash/commands/filesystem-requirements.d.ts.map +1 -0
- package/dist/safe-bash/commands/filesystem-requirements.js +81 -0
- package/dist/safe-bash/commands/filesystem-requirements.js.map +1 -0
- package/dist/safe-bash/commands/filesystem.d.ts.map +1 -1
- package/dist/safe-bash/commands/filesystem.js +106 -9
- package/dist/safe-bash/commands/filesystem.js.map +1 -1
- package/dist/safe-bash/commands/grep.d.ts.map +1 -1
- package/dist/safe-bash/commands/grep.js +3 -109
- package/dist/safe-bash/commands/grep.js.map +1 -1
- package/dist/safe-bash/commands/internal.d.ts +1 -0
- package/dist/safe-bash/commands/internal.d.ts.map +1 -1
- package/dist/safe-bash/commands/internal.js +22 -0
- package/dist/safe-bash/commands/internal.js.map +1 -1
- package/dist/safe-bash/commands/move.d.ts.map +1 -1
- package/dist/safe-bash/commands/move.js +21 -0
- package/dist/safe-bash/commands/move.js.map +1 -1
- package/dist/safe-bash/commands/network/curl.js +2 -0
- package/dist/safe-bash/commands/network/curl.js.map +1 -1
- package/dist/safe-bash/commands/network/output.d.ts.map +1 -1
- package/dist/safe-bash/commands/network/output.js +37 -33
- package/dist/safe-bash/commands/network/output.js.map +1 -1
- package/dist/safe-bash/commands/portable-requirements.d.ts +6 -0
- package/dist/safe-bash/commands/portable-requirements.d.ts.map +1 -0
- package/dist/safe-bash/commands/portable-requirements.js +18 -0
- package/dist/safe-bash/commands/portable-requirements.js.map +1 -0
- package/dist/safe-bash/commands/predicates.d.ts.map +1 -1
- package/dist/safe-bash/commands/predicates.js +9 -1
- package/dist/safe-bash/commands/predicates.js.map +1 -1
- package/dist/safe-bash/commands/regex-execution/client.d.ts +5 -67
- package/dist/safe-bash/commands/regex-execution/client.d.ts.map +1 -1
- package/dist/safe-bash/commands/regex-execution/client.js +13 -379
- package/dist/safe-bash/commands/regex-execution/client.js.map +1 -1
- package/dist/safe-bash/commands/regex-execution/portable.d.ts +71 -0
- package/dist/safe-bash/commands/regex-execution/portable.d.ts.map +1 -0
- package/dist/safe-bash/commands/regex-execution/portable.js +385 -0
- package/dist/safe-bash/commands/regex-execution/portable.js.map +1 -0
- package/dist/safe-bash/commands/regex-execution/protocol.js +1 -1
- package/dist/safe-bash/commands/regex-execution/protocol.js.map +1 -1
- package/dist/safe-bash/commands/regex-execution/provider.d.ts +24 -0
- package/dist/safe-bash/commands/regex-execution/provider.d.ts.map +1 -0
- package/dist/safe-bash/commands/regex-execution/provider.js +2 -0
- package/dist/safe-bash/commands/regex-execution/provider.js.map +1 -0
- package/dist/safe-bash/commands/regex-execution/public.d.ts +9 -0
- package/dist/safe-bash/commands/regex-execution/public.d.ts.map +1 -0
- package/dist/safe-bash/commands/regex-execution/public.js +6 -0
- package/dist/safe-bash/commands/regex-execution/public.js.map +1 -0
- package/dist/safe-bash/commands/search/glob.d.ts +1 -1
- package/dist/safe-bash/commands/search/glob.d.ts.map +1 -1
- package/dist/safe-bash/commands/search/glob.js +1 -1
- package/dist/safe-bash/commands/search/glob.js.map +1 -1
- package/dist/safe-bash/commands/search/grep.d.ts +4 -0
- package/dist/safe-bash/commands/search/grep.d.ts.map +1 -0
- package/dist/safe-bash/commands/search/grep.js +119 -0
- package/dist/safe-bash/commands/search/grep.js.map +1 -0
- package/dist/safe-bash/commands/search/matcher.d.ts +1 -1
- package/dist/safe-bash/commands/search/matcher.d.ts.map +1 -1
- package/dist/safe-bash/commands/search/matcher.js +1 -1
- package/dist/safe-bash/commands/search/matcher.js.map +1 -1
- package/dist/safe-bash/commands/search/output.d.ts +1 -1
- package/dist/safe-bash/commands/search/output.d.ts.map +1 -1
- package/dist/safe-bash/commands/search/output.js +8 -2
- package/dist/safe-bash/commands/search/output.js.map +1 -1
- package/dist/safe-bash/commands/search/portable.d.ts +14 -0
- package/dist/safe-bash/commands/search/portable.d.ts.map +1 -0
- package/dist/safe-bash/commands/search/portable.js +22 -0
- package/dist/safe-bash/commands/search/portable.js.map +1 -0
- package/dist/safe-bash/commands/search/requirements.d.ts +9 -0
- package/dist/safe-bash/commands/search/requirements.d.ts.map +1 -0
- package/dist/safe-bash/commands/search/requirements.js +72 -0
- package/dist/safe-bash/commands/search/requirements.js.map +1 -0
- package/dist/safe-bash/commands/search/rg-command.d.ts +5 -0
- package/dist/safe-bash/commands/search/rg-command.d.ts.map +1 -0
- package/dist/safe-bash/commands/search/rg-command.js +230 -0
- package/dist/safe-bash/commands/search/rg-command.js.map +1 -0
- package/dist/safe-bash/commands/search/rg.d.ts +2 -2
- package/dist/safe-bash/commands/search/rg.d.ts.map +1 -1
- package/dist/safe-bash/commands/search/rg.js +3 -216
- package/dist/safe-bash/commands/search/rg.js.map +1 -1
- package/dist/safe-bash/commands/search/shared.d.ts.map +1 -1
- package/dist/safe-bash/commands/search/shared.js +2 -0
- package/dist/safe-bash/commands/search/shared.js.map +1 -1
- package/dist/safe-bash/commands/search/walk.d.ts +1 -1
- package/dist/safe-bash/commands/search/walk.d.ts.map +1 -1
- package/dist/safe-bash/commands/search/walk.js +8 -1
- package/dist/safe-bash/commands/search/walk.js.map +1 -1
- package/dist/safe-bash/commands/streams.d.ts.map +1 -1
- package/dist/safe-bash/commands/streams.js +52 -108
- package/dist/safe-bash/commands/streams.js.map +1 -1
- package/dist/safe-bash/commands/text-programs/sed.d.ts.map +1 -1
- package/dist/safe-bash/commands/text-programs/sed.js +23 -14
- package/dist/safe-bash/commands/text-programs/sed.js.map +1 -1
- package/dist/safe-bash/commands/text-programs/shared.d.ts.map +1 -1
- package/dist/safe-bash/commands/text-programs/shared.js +3 -5
- package/dist/safe-bash/commands/text-programs/shared.js.map +1 -1
- package/dist/safe-bash/commands/text.d.ts.map +1 -1
- package/dist/safe-bash/commands/text.js +22 -4
- package/dist/safe-bash/commands/text.js.map +1 -1
- package/dist/safe-bash/contracts/command-requirements.d.ts +22 -0
- package/dist/safe-bash/contracts/command-requirements.d.ts.map +1 -0
- package/dist/safe-bash/contracts/command-requirements.js +47 -0
- package/dist/safe-bash/contracts/command-requirements.js.map +1 -0
- package/dist/safe-bash/contracts/command.d.ts +2 -0
- package/dist/safe-bash/contracts/command.d.ts.map +1 -1
- package/dist/safe-bash/contracts/command.js.map +1 -1
- package/dist/safe-bash/contracts/filesystem-output.d.ts +12 -0
- package/dist/safe-bash/contracts/filesystem-output.d.ts.map +1 -0
- package/dist/safe-bash/contracts/filesystem-output.js +160 -0
- package/dist/safe-bash/contracts/filesystem-output.js.map +1 -0
- package/dist/safe-bash/contracts/index.d.ts +1 -0
- package/dist/safe-bash/contracts/index.d.ts.map +1 -1
- package/dist/safe-bash/contracts/index.js +1 -0
- package/dist/safe-bash/contracts/index.js.map +1 -1
- package/dist/safe-bash/contracts/io.d.ts +2 -0
- package/dist/safe-bash/contracts/io.d.ts.map +1 -1
- package/dist/safe-bash/contracts/io.js +2 -0
- package/dist/safe-bash/contracts/io.js.map +1 -1
- package/dist/safe-bash/contracts/output.d.ts +1 -0
- package/dist/safe-bash/contracts/output.d.ts.map +1 -1
- package/dist/safe-bash/contracts/output.js +6 -1
- package/dist/safe-bash/contracts/output.js.map +1 -1
- package/dist/safe-bash/index.d.ts +2 -1
- package/dist/safe-bash/index.d.ts.map +1 -1
- package/dist/safe-bash/index.js +2 -0
- package/dist/safe-bash/index.js.map +1 -1
- package/dist/safe-bash/shell/runtime.d.ts +7 -2
- package/dist/safe-bash/shell/runtime.d.ts.map +1 -1
- package/dist/safe-bash/shell/runtime.js +247 -185
- package/dist/safe-bash/shell/runtime.js.map +1 -1
- package/package.json +2 -2
|
@@ -13,6 +13,8 @@ import { byteLocale } from "./locale.js";
|
|
|
13
13
|
import { functionDisplay } from "./display.js";
|
|
14
14
|
import { ConditionalUnsupported, evaluateConditional } from "./conditional.js";
|
|
15
15
|
import { invocationScope, throwCleanupFailures } from "./cleanup.js";
|
|
16
|
+
import { bindFileOutputBudget, openFileOutput } from "../contracts/filesystem-output.js";
|
|
17
|
+
import { outputFailure } from "../contracts/io.js";
|
|
16
18
|
import { executionCommands } from "../commands/execution.js";
|
|
17
19
|
import { cloneGetoptsState, createGetoptsState, GetoptsError, scanGetopts, withGetoptsIndex } from "./getopts.js";
|
|
18
20
|
import { activateChildCancellation, prepareChildCancellation, selectRuntimeCancellationOutcome, subscribeCancellation, } from "./cancellation.js";
|
|
@@ -140,6 +142,7 @@ export class Budget {
|
|
|
140
142
|
if (ownership?.budget === this && ownership.write === sink.write)
|
|
141
143
|
return signalSink(sink, signal);
|
|
142
144
|
const output = {
|
|
145
|
+
...(sink[outputFailure] ? { [outputFailure]: sink[outputFailure] } : {}),
|
|
143
146
|
...(sink.ownedOutput ? { ownedOutput: {
|
|
144
147
|
consumerClosed: sink.ownedOutput.consumerClosed,
|
|
145
148
|
write: async (chunk) => {
|
|
@@ -299,6 +302,7 @@ function signalSink(sink, signal) {
|
|
|
299
302
|
const owned = ownership?.write === sink.write ? ownership : undefined;
|
|
300
303
|
const write = owned ? owned.write.bind(sink) : (chunk) => sink.write(chunk);
|
|
301
304
|
const output = {
|
|
305
|
+
...(sink[outputFailure] ? { [outputFailure]: sink[outputFailure] } : {}),
|
|
302
306
|
...(sink.ownedOutput ? { ownedOutput: {
|
|
303
307
|
consumerClosed: sink.ownedOutput.consumerClosed,
|
|
304
308
|
async write(chunk) { signal.throwIfAborted(); await interruptible(sink.ownedOutput.write(chunk), signal); },
|
|
@@ -1796,7 +1800,7 @@ export class Runtime {
|
|
|
1796
1800
|
const frame = {};
|
|
1797
1801
|
const runtime = new Runtime(this.fs, this.commands, this.middleware, this.budget, signal, this.fileWrites, this.outputFiles, boundary.deliverySignal, boundary, this.cancellationState, owner, childDepth, this.cancellationMaxDepth, frame);
|
|
1798
1802
|
const input = new ShellInput(incoming?.readable ?? io.stdin, this.budget, signal);
|
|
1799
|
-
const pipeOutput = outgoing && { ownedOutput: outgoing.writable.ownedOutput, write: async (chunk) => {
|
|
1803
|
+
const pipeOutput = outgoing && { [outputFailure]: outgoing.abort, ownedOutput: outgoing.writable.ownedOutput, write: async (chunk) => {
|
|
1800
1804
|
try {
|
|
1801
1805
|
await outgoing.writable.write(chunk);
|
|
1802
1806
|
if (chunk.byteLength)
|
|
@@ -1962,181 +1966,192 @@ export class Runtime {
|
|
|
1962
1966
|
this.signal.throwIfAborted();
|
|
1963
1967
|
const inputs = new Set();
|
|
1964
1968
|
const outputs = new Set();
|
|
1969
|
+
const finishOutputs = async (status) => {
|
|
1970
|
+
const pending = [...outputs];
|
|
1971
|
+
outputs.clear();
|
|
1972
|
+
const settled = await Promise.allSettled(pending.map(close => close({ status })));
|
|
1973
|
+
throwCleanupFailures(settled.filter(result => result.status === "rejected").map(result => result.reason));
|
|
1974
|
+
};
|
|
1965
1975
|
const allocation = this.budget.values.scope();
|
|
1966
1976
|
originalIO = { ...originalIO, [valueScope]: allocation };
|
|
1967
1977
|
let io = originalIO;
|
|
1968
1978
|
let diagnosticFailure;
|
|
1969
1979
|
try {
|
|
1970
|
-
|
|
1971
|
-
if (
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
if (command.kind === "simple")
|
|
1979
|
-
return await this.simple(command, state, originalIO, inputs, outputs, fileShortcut);
|
|
1980
|
-
io = await this.redirect(command.redirects, state, io, inputs, outputs, command.kind === "subshell", command.kind !== "subshell");
|
|
1981
|
-
if (command.kind === "conditional") {
|
|
1982
|
-
try {
|
|
1983
|
-
return await evaluateConditional(command.expression, {
|
|
1984
|
-
fs: this.fs, cwd: state.cwd, signal: this.signal,
|
|
1985
|
-
locale: state.variables.LC_ALL || state.variables.LC_COLLATE || state.variables.LANG || "C",
|
|
1986
|
-
work: { remaining: this.budget.limits.maxExpansionBytes, signal: this.signal, exhausted: () => this.budget.fail("maxExpansionBytes") },
|
|
1987
|
-
expand: async (word, pattern = false) => (await this.word(word, state, io, false, pattern, false, pattern)).join(""),
|
|
1988
|
-
regex: (subject, pattern) => this.ere(subject, pattern, state, io),
|
|
1989
|
-
option: name => name === "errexit" ? !!state.errexit : name === "nounset" ? !!state.nounset : name === "pipefail" ? state.pipefail : false,
|
|
1990
|
-
present: name => {
|
|
1991
|
-
const match = /^([a-zA-Z_][a-zA-Z_0-9]*)(?:\[(0|[1-9][0-9]*|[@*])\])?$/u.exec(name);
|
|
1992
|
-
if (!match)
|
|
1993
|
-
throw new ConditionalUnsupported("[[ variable selector: unsupported conditional profile");
|
|
1994
|
-
const binding = arrayStore(state)?.get(match[1]);
|
|
1995
|
-
const selector = match[2];
|
|
1996
|
-
if (selector === "@" || selector === "*")
|
|
1997
|
-
return binding ? binding.values.size > 0 : this.variable(state, match[1]) !== undefined;
|
|
1998
|
-
const index = selector === undefined ? 0 : numericIndex({ decimal: selector });
|
|
1999
|
-
if (index === undefined)
|
|
2000
|
-
throw new ConditionalUnsupported("[[ variable index: unsupported conditional profile");
|
|
2001
|
-
return binding ? binding.get(index) !== undefined : index === 0 && this.variable(state, match[1]) !== undefined;
|
|
2002
|
-
},
|
|
2003
|
-
});
|
|
1980
|
+
const execute = async () => {
|
|
1981
|
+
if (command.kind === "function") {
|
|
1982
|
+
if (state.profile === "sh" && specialBuiltinNames.has(command.name)) {
|
|
1983
|
+
await this.diagnostic(io, `\`${command.name}': is a special builtin`);
|
|
1984
|
+
throw new Flow("exit", 2);
|
|
1985
|
+
}
|
|
1986
|
+
state.functions.set(command.name, { ...command.body, sourceName: io.scriptName ?? "shell" });
|
|
1987
|
+
return 0;
|
|
2004
1988
|
}
|
|
2005
|
-
|
|
2006
|
-
this.
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
1989
|
+
if (command.kind === "simple")
|
|
1990
|
+
return await this.simple(command, state, originalIO, inputs, outputs, fileShortcut);
|
|
1991
|
+
io = await this.redirect(command.redirects, state, io, inputs, outputs, command.kind === "subshell", command.kind !== "subshell");
|
|
1992
|
+
if (command.kind === "conditional") {
|
|
1993
|
+
try {
|
|
1994
|
+
return await evaluateConditional(command.expression, {
|
|
1995
|
+
fs: this.fs, cwd: state.cwd, signal: this.signal,
|
|
1996
|
+
locale: state.variables.LC_ALL || state.variables.LC_COLLATE || state.variables.LANG || "C",
|
|
1997
|
+
work: { remaining: this.budget.limits.maxExpansionBytes, signal: this.signal, exhausted: () => this.budget.fail("maxExpansionBytes") },
|
|
1998
|
+
expand: async (word, pattern = false) => (await this.word(word, state, io, false, pattern, false, pattern)).join(""),
|
|
1999
|
+
regex: (subject, pattern) => this.ere(subject, pattern, state, io),
|
|
2000
|
+
option: name => name === "errexit" ? !!state.errexit : name === "nounset" ? !!state.nounset : name === "pipefail" ? state.pipefail : false,
|
|
2001
|
+
present: name => {
|
|
2002
|
+
const match = /^([a-zA-Z_][a-zA-Z_0-9]*)(?:\[(0|[1-9][0-9]*|[@*])\])?$/u.exec(name);
|
|
2003
|
+
if (!match)
|
|
2004
|
+
throw new ConditionalUnsupported("[[ variable selector: unsupported conditional profile");
|
|
2005
|
+
const binding = arrayStore(state)?.get(match[1]);
|
|
2006
|
+
const selector = match[2];
|
|
2007
|
+
if (selector === "@" || selector === "*")
|
|
2008
|
+
return binding ? binding.values.size > 0 : this.variable(state, match[1]) !== undefined;
|
|
2009
|
+
const index = selector === undefined ? 0 : numericIndex({ decimal: selector });
|
|
2010
|
+
if (index === undefined)
|
|
2011
|
+
throw new ConditionalUnsupported("[[ variable index: unsupported conditional profile");
|
|
2012
|
+
return binding ? binding.get(index) !== undefined : index === 0 && this.variable(state, match[1]) !== undefined;
|
|
2013
|
+
},
|
|
2014
|
+
});
|
|
2010
2015
|
}
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
this.signal.throwIfAborted();
|
|
2017
|
-
if (reason instanceof ShellLimitError)
|
|
2018
|
-
throw reason;
|
|
2019
|
-
diagnosticFailure = new NounsetDiagnosticFailure(reason);
|
|
2020
|
-
throw diagnosticFailure;
|
|
2016
|
+
catch (error) {
|
|
2017
|
+
this.signal.throwIfAborted();
|
|
2018
|
+
if (error instanceof NounsetDiagnosticFailure) {
|
|
2019
|
+
diagnosticFailure = error;
|
|
2020
|
+
throw error;
|
|
2021
2021
|
}
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2022
|
+
if (error instanceof EreProfileLimitError) {
|
|
2023
|
+
try {
|
|
2024
|
+
await this.diagnostic(io, `[[ ${error.message}`);
|
|
2025
|
+
}
|
|
2026
|
+
catch (reason) {
|
|
2027
|
+
this.signal.throwIfAborted();
|
|
2028
|
+
if (reason instanceof ShellLimitError)
|
|
2029
|
+
throw reason;
|
|
2030
|
+
diagnosticFailure = new NounsetDiagnosticFailure(reason);
|
|
2031
|
+
throw diagnosticFailure;
|
|
2032
|
+
}
|
|
2033
|
+
return 3;
|
|
2027
2034
|
}
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2035
|
+
if (error instanceof ConditionalUnsupported) {
|
|
2036
|
+
try {
|
|
2037
|
+
await this.diagnostic(io, error.message);
|
|
2038
|
+
}
|
|
2039
|
+
catch (reason) {
|
|
2040
|
+
this.signal.throwIfAborted();
|
|
2041
|
+
if (reason instanceof ShellLimitError)
|
|
2042
|
+
throw reason;
|
|
2043
|
+
diagnosticFailure = new NounsetDiagnosticFailure(reason);
|
|
2044
|
+
throw diagnosticFailure;
|
|
2045
|
+
}
|
|
2046
|
+
return 2;
|
|
2034
2047
|
}
|
|
2035
|
-
|
|
2048
|
+
if (error instanceof ExpansionFailure || error instanceof Flow || error instanceof ShellLimitError || error instanceof ShellSyntaxError || error instanceof ArrayFailure)
|
|
2049
|
+
throw error;
|
|
2050
|
+
diagnosticFailure = new NounsetDiagnosticFailure(error);
|
|
2051
|
+
throw diagnosticFailure;
|
|
2036
2052
|
}
|
|
2037
|
-
if (error instanceof ExpansionFailure || error instanceof Flow || error instanceof ShellLimitError || error instanceof ShellSyntaxError || error instanceof ArrayFailure)
|
|
2038
|
-
throw error;
|
|
2039
|
-
diagnosticFailure = new NounsetDiagnosticFailure(error);
|
|
2040
|
-
throw diagnosticFailure;
|
|
2041
|
-
}
|
|
2042
|
-
}
|
|
2043
|
-
if (command.kind === "arithmetic") {
|
|
2044
|
-
try {
|
|
2045
|
-
return Number(evaluatePositionalArithmetic(command.expression, {
|
|
2046
|
-
positional: state.positional, arg0: state.arg0 ?? "virtual-bash", owner: arrayStore(state)?.owner,
|
|
2047
|
-
maximumBytes: this.budget.limits.maxExpansionBytes,
|
|
2048
|
-
checkpoint: () => this.signal.throwIfAborted(),
|
|
2049
|
-
requireParameter: (name, value) => this.requireParameter(value, name, state, io),
|
|
2050
|
-
limit: () => this.budget.fail("maxExpansionBytes"),
|
|
2051
|
-
}, (prepared) => evaluateArithmetic(prepared, this.arithmeticVariables(state, io.diagnosticLine))) === 0n);
|
|
2052
2053
|
}
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2054
|
+
if (command.kind === "arithmetic") {
|
|
2055
|
+
try {
|
|
2056
|
+
return Number(evaluatePositionalArithmetic(command.expression, {
|
|
2057
|
+
positional: state.positional, arg0: state.arg0 ?? "virtual-bash", owner: arrayStore(state)?.owner,
|
|
2058
|
+
maximumBytes: this.budget.limits.maxExpansionBytes,
|
|
2059
|
+
checkpoint: () => this.signal.throwIfAborted(),
|
|
2060
|
+
requireParameter: (name, value) => this.requireParameter(value, name, state, io),
|
|
2061
|
+
limit: () => this.budget.fail("maxExpansionBytes"),
|
|
2062
|
+
}, (prepared) => evaluateArithmetic(prepared, this.arithmeticVariables(state, io.diagnosticLine))) === 0n);
|
|
2063
|
+
}
|
|
2064
|
+
catch (error) {
|
|
2065
|
+
this.rethrowArithmeticControl(error);
|
|
2066
|
+
throw new Error(`((: ${message(error)}`);
|
|
2067
|
+
}
|
|
2056
2068
|
}
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2069
|
+
if (command.kind === "subshell") {
|
|
2070
|
+
const child = await cloneState(state, this.signal);
|
|
2071
|
+
child.isolated = true;
|
|
2072
|
+
child.loopDepth = 0;
|
|
2073
|
+
try {
|
|
2074
|
+
return await this.run(command.body, child, io);
|
|
2075
|
+
}
|
|
2076
|
+
finally {
|
|
2077
|
+
stateMonitor(child)?.closeValues();
|
|
2078
|
+
}
|
|
2064
2079
|
}
|
|
2065
|
-
|
|
2066
|
-
|
|
2080
|
+
if (command.kind === "group")
|
|
2081
|
+
return await this.script(command.body, state, io);
|
|
2082
|
+
if (command.kind === "if") {
|
|
2083
|
+
for (const branch of command.branches) {
|
|
2084
|
+
if (await this.script(branch.condition, state, { ...io, execution: { ignoreErrexit: true } }) === 0)
|
|
2085
|
+
return await this.script(branch.body, state, io);
|
|
2086
|
+
}
|
|
2087
|
+
return command.otherwise ? await this.script(command.otherwise, state, io) : 0;
|
|
2067
2088
|
}
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2089
|
+
if (command.kind === "case") {
|
|
2090
|
+
const subject = (await this.word(command.subject, state, io, false)).join("");
|
|
2091
|
+
const work = { remaining: this.budget.limits.maxExpansionBytes, signal: this.signal, exhausted: () => this.budget.fail("maxExpansionBytes") };
|
|
2092
|
+
let status = 0;
|
|
2093
|
+
let fallthrough = false;
|
|
2094
|
+
let patterns = 0;
|
|
2095
|
+
for (const clause of command.clauses) {
|
|
2096
|
+
let matched = fallthrough;
|
|
2097
|
+
if (!matched)
|
|
2098
|
+
for (const word of clause.patterns) {
|
|
2099
|
+
if (++patterns % 128 === 0)
|
|
2100
|
+
await yieldTurn(this.signal);
|
|
2101
|
+
const pattern = (await this.word(word, state, io, false, true)).join("");
|
|
2102
|
+
if (await matchesPattern(pattern, subject, work)) {
|
|
2103
|
+
matched = true;
|
|
2104
|
+
break;
|
|
2105
|
+
}
|
|
2106
|
+
}
|
|
2107
|
+
if (!matched)
|
|
2108
|
+
continue;
|
|
2109
|
+
if (clause.body.lists.length)
|
|
2110
|
+
status = await this.script(clause.body, state, io);
|
|
2111
|
+
if (clause.terminator === ";;" || clause.terminator === "esac")
|
|
2112
|
+
break;
|
|
2113
|
+
fallthrough = clause.terminator === ";&";
|
|
2114
|
+
}
|
|
2115
|
+
return status;
|
|
2075
2116
|
}
|
|
2076
|
-
return command.otherwise ? await this.script(command.otherwise, state, io) : 0;
|
|
2077
|
-
}
|
|
2078
|
-
if (command.kind === "case") {
|
|
2079
|
-
const subject = (await this.word(command.subject, state, io, false)).join("");
|
|
2080
|
-
const work = { remaining: this.budget.limits.maxExpansionBytes, signal: this.signal, exhausted: () => this.budget.fail("maxExpansionBytes") };
|
|
2081
2117
|
let status = 0;
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
for (const
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
const
|
|
2091
|
-
|
|
2092
|
-
|
|
2118
|
+
const loopRestoration = stateMonitor(state)?.restoration();
|
|
2119
|
+
state.loopDepth++;
|
|
2120
|
+
try {
|
|
2121
|
+
if (command.kind === "for") {
|
|
2122
|
+
const values = command.words ? await this.valueWords(command.words, state, io) : this.positionalValues(state);
|
|
2123
|
+
for (const value of values) {
|
|
2124
|
+
this.budget.loop();
|
|
2125
|
+
await this.assignVariable(state, command.name, value);
|
|
2126
|
+
const result = await this.loopBody(command.body, state, io);
|
|
2127
|
+
status = result.status;
|
|
2128
|
+
if (result.stop)
|
|
2093
2129
|
break;
|
|
2094
|
-
}
|
|
2095
2130
|
}
|
|
2096
|
-
if (!matched)
|
|
2097
|
-
continue;
|
|
2098
|
-
if (clause.body.lists.length)
|
|
2099
|
-
status = await this.script(clause.body, state, io);
|
|
2100
|
-
if (clause.terminator === ";;" || clause.terminator === "esac")
|
|
2101
|
-
break;
|
|
2102
|
-
fallthrough = clause.terminator === ";&";
|
|
2103
|
-
}
|
|
2104
|
-
return status;
|
|
2105
|
-
}
|
|
2106
|
-
let status = 0;
|
|
2107
|
-
const loopRestoration = stateMonitor(state)?.restoration();
|
|
2108
|
-
state.loopDepth++;
|
|
2109
|
-
try {
|
|
2110
|
-
if (command.kind === "for") {
|
|
2111
|
-
const values = command.words ? await this.valueWords(command.words, state, io) : this.positionalValues(state);
|
|
2112
|
-
for (const value of values) {
|
|
2113
|
-
this.budget.loop();
|
|
2114
|
-
await this.assignVariable(state, command.name, value);
|
|
2115
|
-
const result = await this.loopBody(command.body, state, io);
|
|
2116
|
-
status = result.status;
|
|
2117
|
-
if (result.stop)
|
|
2118
|
-
break;
|
|
2119
2131
|
}
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2132
|
+
else {
|
|
2133
|
+
while (true) {
|
|
2134
|
+
this.budget.loop();
|
|
2135
|
+
const condition = await this.script(command.condition, state, { ...io, execution: { ignoreErrexit: true } });
|
|
2136
|
+
if ((condition === 0) !== (command.kind === "while"))
|
|
2137
|
+
break;
|
|
2138
|
+
const result = await this.loopBody(command.body, state, io);
|
|
2139
|
+
status = result.status;
|
|
2140
|
+
if (result.stop)
|
|
2141
|
+
break;
|
|
2142
|
+
}
|
|
2131
2143
|
}
|
|
2132
2144
|
}
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2145
|
+
finally {
|
|
2146
|
+
if (loopRestoration)
|
|
2147
|
+
loopRestoration.apply(() => { state.loopDepth--; });
|
|
2148
|
+
else
|
|
2149
|
+
state.loopDepth--;
|
|
2150
|
+
}
|
|
2151
|
+
return status;
|
|
2152
|
+
};
|
|
2153
|
+
const status = await execute();
|
|
2154
|
+
await finishOutputs(status);
|
|
2140
2155
|
return status;
|
|
2141
2156
|
}
|
|
2142
2157
|
catch (caught) {
|
|
@@ -2147,6 +2162,8 @@ export class Runtime {
|
|
|
2147
2162
|
if (error instanceof NounsetDiagnosticFailure)
|
|
2148
2163
|
diagnosticFailure = error;
|
|
2149
2164
|
this.signal.throwIfAborted();
|
|
2165
|
+
if (error instanceof Flow)
|
|
2166
|
+
await finishOutputs(error.status);
|
|
2150
2167
|
if (error instanceof Flow || error instanceof ShellLimitError || error instanceof ShellSyntaxError)
|
|
2151
2168
|
throw error;
|
|
2152
2169
|
this.clearOutcomeReport();
|
|
@@ -2197,7 +2214,7 @@ export class Runtime {
|
|
|
2197
2214
|
}
|
|
2198
2215
|
finally {
|
|
2199
2216
|
await Promise.allSettled([
|
|
2200
|
-
...[...outputs].map(async (close) => close()),
|
|
2217
|
+
...[...outputs].map(async (close) => close({ reason: new FsError("ECANCELED", { syscall: "redirect" }) })),
|
|
2201
2218
|
...[...inputs].map(async (input) => input.close()),
|
|
2202
2219
|
]).then(results => {
|
|
2203
2220
|
const failures = results.filter(result => result.status === "rejected").map(result => result.reason);
|
|
@@ -2358,48 +2375,91 @@ export class Runtime {
|
|
|
2358
2375
|
}
|
|
2359
2376
|
else {
|
|
2360
2377
|
const append = redirect.operator === ">>";
|
|
2378
|
+
const capabilities = await this.fs.capabilitiesFor?.(path, options) ?? this.fs.capabilities;
|
|
2379
|
+
const random = capabilities.randomAccessWrite === true;
|
|
2361
2380
|
let file;
|
|
2362
2381
|
await this.fileOperation(path, async () => {
|
|
2363
|
-
await this.fs.writeFile(path, new Uint8Array(), { ...options, flag: append ? "a" : "w" });
|
|
2364
2382
|
file = this.outputFiles.get(path) ?? { data: undefined, references: 0 };
|
|
2365
|
-
if (!
|
|
2366
|
-
|
|
2383
|
+
if (!random && file.references)
|
|
2384
|
+
throw new FsError("ENOTSUP", { path, message: "Conflicting sequential output descriptors" });
|
|
2367
2385
|
file.references++;
|
|
2368
2386
|
this.outputFiles.set(path, file);
|
|
2369
2387
|
});
|
|
2370
2388
|
let closed = false;
|
|
2371
|
-
outputs.add(() => {
|
|
2372
|
-
closed = true;
|
|
2373
|
-
if (--file.references === 0 && this.outputFiles.get(path) === file)
|
|
2374
|
-
this.outputFiles.delete(path);
|
|
2375
|
-
});
|
|
2376
2389
|
let offset = 0;
|
|
2377
|
-
const
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2390
|
+
const incremental = async () => {
|
|
2391
|
+
await this.fileOperation(path, async () => {
|
|
2392
|
+
if (append)
|
|
2393
|
+
await this.fs.appendFile(path, new Uint8Array(), options);
|
|
2394
|
+
else
|
|
2395
|
+
await this.fs.writeFile(path, new Uint8Array(), { ...options, flag: "w" });
|
|
2396
|
+
if (!append)
|
|
2397
|
+
file.data = new Uint8Array();
|
|
2398
|
+
});
|
|
2399
|
+
return { write: (chunk) => {
|
|
2400
|
+
const copy = new Uint8Array(chunk);
|
|
2401
|
+
return this.fileOperation(path, async () => {
|
|
2402
|
+
if (closed)
|
|
2403
|
+
throw new Error("Output descriptor is closed");
|
|
2404
|
+
const current = file.data;
|
|
2405
|
+
if (append) {
|
|
2406
|
+
await this.fs.appendFile(path, copy, options);
|
|
2407
|
+
if (current) {
|
|
2408
|
+
const bytes = new Uint8Array(current.length + copy.length);
|
|
2409
|
+
bytes.set(current);
|
|
2410
|
+
bytes.set(copy, current.length);
|
|
2411
|
+
file.data = bytes;
|
|
2412
|
+
}
|
|
2413
|
+
}
|
|
2414
|
+
else {
|
|
2415
|
+
const bytes = new Uint8Array(Math.max(current?.length ?? 0, offset + copy.length));
|
|
2416
|
+
if (current)
|
|
2417
|
+
bytes.set(current);
|
|
2418
|
+
bytes.set(copy, offset);
|
|
2419
|
+
await this.fs.writeFile(path, bytes, options);
|
|
2389
2420
|
file.data = bytes;
|
|
2421
|
+
offset += copy.length;
|
|
2390
2422
|
}
|
|
2423
|
+
});
|
|
2424
|
+
} };
|
|
2425
|
+
};
|
|
2426
|
+
const release = () => {
|
|
2427
|
+
if (closed)
|
|
2428
|
+
return;
|
|
2429
|
+
closed = true;
|
|
2430
|
+
if (--file.references === 0 && this.outputFiles.get(path) === file)
|
|
2431
|
+
this.outputFiles.delete(path);
|
|
2432
|
+
};
|
|
2433
|
+
let target;
|
|
2434
|
+
try {
|
|
2435
|
+
target = await openFileOutput({ fs: this.fs, signal: this.signal, registerCleanup: cleanup => io[invocationScope].register(cleanup) }, path, append ? "a" : "w", random ? incremental : undefined);
|
|
2436
|
+
}
|
|
2437
|
+
catch (error) {
|
|
2438
|
+
release();
|
|
2439
|
+
throw error;
|
|
2440
|
+
}
|
|
2441
|
+
outputs.add(async (completion) => {
|
|
2442
|
+
try {
|
|
2443
|
+
if (this.signal.aborted)
|
|
2444
|
+
await target.abort(this.signal.reason);
|
|
2445
|
+
else if ("reason" in completion)
|
|
2446
|
+
await target.abort(completion.reason);
|
|
2447
|
+
else {
|
|
2448
|
+
try {
|
|
2449
|
+
await target.finish();
|
|
2391
2450
|
}
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
if (
|
|
2395
|
-
|
|
2396
|
-
bytes.set(copy, offset);
|
|
2397
|
-
await this.fs.writeFile(path, bytes, options);
|
|
2398
|
-
file.data = bytes;
|
|
2399
|
-
offset += copy.length;
|
|
2451
|
+
catch (error) {
|
|
2452
|
+
this.signal.throwIfAborted();
|
|
2453
|
+
if (completion.status === 0)
|
|
2454
|
+
throw error;
|
|
2400
2455
|
}
|
|
2401
|
-
}
|
|
2402
|
-
}
|
|
2456
|
+
}
|
|
2457
|
+
}
|
|
2458
|
+
finally {
|
|
2459
|
+
release();
|
|
2460
|
+
}
|
|
2461
|
+
});
|
|
2462
|
+
const output = this.budget.sink(target.sink, this.signal);
|
|
2403
2463
|
descriptors.set(redirect.descriptor, { output });
|
|
2404
2464
|
if (redirect.operator === "&>") {
|
|
2405
2465
|
replaced.add(2);
|
|
@@ -2657,6 +2717,7 @@ export class Runtime {
|
|
|
2657
2717
|
return invocation;
|
|
2658
2718
|
},
|
|
2659
2719
|
};
|
|
2720
|
+
bindFileOutputBudget(context, sink => this.budget.sink(sink, this.signal));
|
|
2660
2721
|
if (argumentValues.values.every(value => typeof value === "string"))
|
|
2661
2722
|
Reflect.deleteProperty(context, "argumentValues");
|
|
2662
2723
|
const middleware = this.middleware.map((handler) => (context, next) => {
|
|
@@ -3301,6 +3362,7 @@ export class Runtime {
|
|
|
3301
3362
|
return invocation;
|
|
3302
3363
|
},
|
|
3303
3364
|
};
|
|
3365
|
+
bindFileOutputBudget(context, sink => this.budget.sink(sink, runtime.signal));
|
|
3304
3366
|
if (argumentValues.values.every(value => typeof value === "string"))
|
|
3305
3367
|
Reflect.deleteProperty(context, "argumentValues");
|
|
3306
3368
|
const child = await runtime.shebangState(context, state);
|