@poe-platform/safe-bash 0.1.750 → 0.1.751

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.
@@ -9796,10 +9796,19 @@ export class Runtime {
9796
9796
  // Matching publishes BASH_REMATCH. A sibling may still require fallback,
9797
9797
  // so only a standalone match can be attempted without replaying writes.
9798
9798
  if (expr.operator === "=~")
9799
- return depth === 0 && this.extractSimpleErePattern(expr.right, rawState) !== undefined;
9799
+ return !requireTotal && depth === 0 && this.extractSimpleErePattern(expr.right, rawState) !== undefined;
9800
9800
  if (expr.operator === "==" || expr.operator === "=" || expr.operator === "!=") {
9801
9801
  if (expr.right.parts.length > 0 && expr.right.parts.every(p => p.quoted))
9802
9802
  return true;
9803
+ if (requireTotal) {
9804
+ // Variable patterns can change during the loop. Only admit a static
9805
+ // pattern whose synchronous matcher is guaranteed to return a result.
9806
+ if (!expr.right.parts.every((p, idx) => p.kind === "text" && !p.byteValue && (p.quoted || idx > 0 || !p.value.startsWith("~"))))
9807
+ return false;
9808
+ const pattern = expr.right.parts.map(p => p.kind === "text" ? (p.quoted ? p.value.replace(/[\\*?[\]()]/g, "\\$&") : p.value) : "").join("");
9809
+ const work = { remaining: 4096, signal: this.signal, exhausted: () => this.budget.fail("maxExpansionBytes") };
9810
+ return tryMatchesPatternSync(pattern, "", work, false, true) !== undefined;
9811
+ }
9803
9812
  if (expr.right.parts.length > 0 && expr.right.parts.every((p, idx) => (p.kind === "text" && !p.byteValue && (p.quoted || idx > 0 || !p.value.startsWith("~"))) || (p.kind === "variable" && !p.indirect && !p.prefixNames && !p.length && !p.substring && !p.transform && p.operator === undefined && getArraySelector(p) === undefined)))
9804
9813
  return true;
9805
9814
  return false;
@@ -9988,7 +9997,10 @@ export class Runtime {
9988
9997
  return undefined;
9989
9998
  const syncTouched = this._syncArithRawWriteOnly ? this._syncArithTouched : undefined;
9990
9999
  const lTrim = left.trim() || "0";
9991
- let lNum = resolveSimpleArithOperand(lTrim, rawState, monitor, store, syncTouched);
10000
+ // Preserve all signed 64-bit literal digits instead of rounding through Number.
10001
+ let lNum = lTrim.length > 13 && /^-?[0-9]{1,19}$/.test(lTrim)
10002
+ ? BigInt.asIntN(64, BigInt(lTrim))
10003
+ : resolveSimpleArithOperand(lTrim, rawState, monitor, store, syncTouched);
9992
10004
  if (lNum === undefined) {
9993
10005
  const lExpr = tryEvalSimpleExpandedArith(lTrim, rawState, monitor, store, syncTouched);
9994
10006
  if (lExpr !== undefined)
@@ -9997,7 +10009,9 @@ export class Runtime {
9997
10009
  if (lNum === undefined)
9998
10010
  return undefined;
9999
10011
  const rTrim = right.trim() || "0";
10000
- let rNum = resolveSimpleArithOperand(rTrim, rawState, monitor, store, syncTouched);
10012
+ let rNum = rTrim.length > 13 && /^-?[0-9]{1,19}$/.test(rTrim)
10013
+ ? BigInt.asIntN(64, BigInt(rTrim))
10014
+ : resolveSimpleArithOperand(rTrim, rawState, monitor, store, syncTouched);
10001
10015
  if (rNum === undefined) {
10002
10016
  const rExpr = tryEvalSimpleExpandedArith(rTrim, rawState, monitor, store, syncTouched);
10003
10017
  if (rExpr !== undefined)
@@ -10005,6 +10019,10 @@ export class Runtime {
10005
10019
  }
10006
10020
  if (rNum === undefined)
10007
10021
  return undefined;
10022
+ if (typeof lNum !== typeof rNum) {
10023
+ lNum = BigInt(lNum);
10024
+ rNum = BigInt(rNum);
10025
+ }
10008
10026
  let ok;
10009
10027
  switch (expr.operator) {
10010
10028
  case "-eq":
@@ -13026,7 +13044,6 @@ export class Runtime {
13026
13044
  rawState.substitutionStatus = 0;
13027
13045
  if (rawState.variables._ !== undefined)
13028
13046
  delete rawState.variables._;
13029
- rawState.lastArgument = fnArgs.length > 0 ? fnArgs[fnArgs.length - 1] : w0Plain;
13030
13047
  const positional = rawState.positional;
13031
13048
  let fnArgsBytes = 0;
13032
13049
  for (let i = 0; i < fnArgs.length; i++)
@@ -13240,7 +13257,6 @@ export class Runtime {
13240
13257
  rawState.substitutionStatus = 0;
13241
13258
  if (rawState.variables._ !== undefined)
13242
13259
  delete rawState.variables._;
13243
- rawState.lastArgument = lastCmdArg;
13244
13260
  rawState.depth++;
13245
13261
  let evalStatus;
13246
13262
  try {
@@ -13250,6 +13266,7 @@ export class Runtime {
13250
13266
  rawState.depth--;
13251
13267
  }
13252
13268
  if (typeof evalStatus === "number") {
13269
+ rawState.lastArgument = lastCmdArg;
13253
13270
  if (cachedUnit.script.lists.length === 0) {
13254
13271
  const activeStore = monitor.store;
13255
13272
  const activeExisting = activeStore?.get("PIPESTATUS");
@@ -13589,6 +13606,11 @@ export class Runtime {
13589
13606
  continue;
13590
13607
  }
13591
13608
  if (part.kind === "variable") {
13609
+ // Unsubscripted array references expand element zero through the normal
13610
+ // evaluator. fastValueWord cannot guarantee that expansion throughout
13611
+ // a loop, so reject it before executing any loop effects.
13612
+ if (stateMonitor(rawState)?.store?.get(part.name))
13613
+ return false;
13592
13614
  // These expansions can require multiple fields or asynchronous quoting
13593
13615
  // as loop state changes. Decide before the loop produces any effects.
13594
13616
  if (this._syncLoopFnCheckDepth > 0 && (part.substring || getArraySelector(part)?.kind === "element"))
@@ -13887,11 +13909,11 @@ export class Runtime {
13887
13909
  if (isNumOp && checkIntVars) {
13888
13910
  const isGuaranteedIntWord = (w) => {
13889
13911
  if (w.plain !== undefined)
13890
- return /^-?[0-9]+$/.test(w.plain);
13912
+ return /^-?[0-9]{1,16}$/.test(w.plain);
13891
13913
  if (w.parts.length === 1) {
13892
13914
  const p0 = w.parts[0];
13893
13915
  if (p0.kind === "arithmetic")
13894
- return true;
13916
+ return false;
13895
13917
  if (p0.kind === "variable" && !p0.indirect && !p0.prefixNames && !p0.substring && !p0.transform && p0.operator === undefined && getArraySelector(p0) === undefined) {
13896
13918
  if (script) {
13897
13919
  for (const lst of script.lists) {
@@ -13899,14 +13921,14 @@ export class Runtime {
13899
13921
  const c0 = pl.commands[0];
13900
13922
  if (c0?.kind === "simple" && c0.words.length === 1) {
13901
13923
  const asg = this.assignment(c0.words[0]);
13902
- if (asg?.name === p0.name && (!asg.value.plain || !/^-?[0-9]+$/.test(asg.value.plain)))
13924
+ if (asg?.name === p0.name && (!asg.value.plain || !/^-?[0-9]{1,16}$/.test(asg.value.plain)))
13903
13925
  return false;
13904
13926
  }
13905
13927
  }
13906
13928
  }
13907
13929
  }
13908
13930
  const cur = rawState.variables[p0.name];
13909
- return cur === undefined || /^-?[0-9]+$/.test(cur);
13931
+ return cur === undefined || /^-?[0-9]{1,16}$/.test(cur);
13910
13932
  }
13911
13933
  }
13912
13934
  return false;
@@ -14577,11 +14599,16 @@ export class Runtime {
14577
14599
  (w0DevPlain === "pwd" || this.commands.has(w0DevPlain))) {
14578
14600
  const devWordOk = (w) => this.isPureSyncValueWord(w, rawState) &&
14579
14601
  w.parts.every(p => p.quoted || (p.kind === "text" && p.value.length > 0 && !p.value.includes(" ") && !p.value.includes("\t") && !p.value.includes("\n") && !p.value.includes("{") && !hasGlobOrEscape(p.value, true)));
14602
+ // Only a nonempty literal prefix proves an expansion cannot become an option.
14603
+ const devOperandOk = (w) => {
14604
+ const first = w.parts[0];
14605
+ return first?.kind === "text" && first.value.length > 0 && !first.value.startsWith("-") && devWordOk(w);
14606
+ };
14580
14607
  const devShapeOk = (w0DevPlain === "pwd" && cmd.words.length === 1) ||
14581
- ((w0DevPlain === "dirname" || w0DevPlain === "echo") && cmd.words.length === 2 && !cmd.words[1].plain?.startsWith("-") && devWordOk(cmd.words[1])) ||
14582
- (w0DevPlain === "basename" && ((cmd.words.length === 2 && !cmd.words[1].plain?.startsWith("-") && devWordOk(cmd.words[1])) ||
14583
- (cmd.words.length === 3 && !cmd.words[1].plain?.startsWith("-") && devWordOk(cmd.words[1]) && devWordOk(cmd.words[2])) ||
14584
- (cmd.words.length === 4 && cmd.words[1].plain === "-s" && cmd.words[2].plain !== undefined && !cmd.words[3].plain?.startsWith("-") && devWordOk(cmd.words[3]))));
14608
+ ((w0DevPlain === "dirname" || w0DevPlain === "echo") && cmd.words.length === 2 && devOperandOk(cmd.words[1])) ||
14609
+ (w0DevPlain === "basename" && ((cmd.words.length === 2 && devOperandOk(cmd.words[1])) ||
14610
+ (cmd.words.length === 3 && devOperandOk(cmd.words[1]) && devWordOk(cmd.words[2])) ||
14611
+ (cmd.words.length === 4 && cmd.words[1].plain === "-s" && cmd.words[2].plain !== undefined && devOperandOk(cmd.words[3]))));
14585
14612
  if (devShapeOk)
14586
14613
  continue;
14587
14614
  }
@@ -16686,7 +16713,24 @@ export class Runtime {
16686
16713
  }
16687
16714
  if (command.kind !== "simple" || hasShellFunction(rawState, command.words[0]?.plain ?? "") || command.words.some(word => getArrayAssignment(word) !== undefined))
16688
16715
  return false;
16689
- const w0 = command.words[0]?.plain;
16716
+ let commandIndex = 0;
16717
+ while (commandIndex < command.words.length && this.assignment(command.words[commandIndex]))
16718
+ commandIndex++;
16719
+ const commandWord = command.words[commandIndex];
16720
+ if (!commandWord?.parts.every(part => part.kind === "text"))
16721
+ return false;
16722
+ const w0 = commandWord.parts.map(part => part.kind === "text" ? part.value : "").join("");
16723
+ // read targets and printf -v targets may be quoted or expanded. Both can
16724
+ // invalidate the integer assumption used by induction formatting.
16725
+ if (w0 === "read")
16726
+ return false;
16727
+ if (w0 === "printf") {
16728
+ const option = command.words[commandIndex + 1];
16729
+ if (!option?.parts.every(part => part.kind === "text"))
16730
+ return false;
16731
+ if (option.parts.map(part => part.kind === "text" ? part.value : "").join("") === "-v")
16732
+ return false;
16733
+ }
16690
16734
  if (w0 === "let" || w0 === "declare" || w0 === "typeset" || w0 === "local" || w0 === "export" || w0 === "unset" || w0 === "mapfile" || w0 === "readarray")
16691
16735
  return false;
16692
16736
  if (inductionName !== undefined) {
@@ -16695,10 +16739,6 @@ export class Runtime {
16695
16739
  return p0?.kind === "text" && !p0.quoted && (p0.value.startsWith(inductionName + "=") || p0.value.startsWith(inductionName + "+="));
16696
16740
  }))
16697
16741
  return false;
16698
- if (w0 === "read" && (inductionName === "REPLY" || command.words.slice(1).some(w => w.plain === inductionName || w.plain === "-a" || w.plain === "-ra" || w.plain === "-ar")))
16699
- return false;
16700
- if (w0 === "printf" && command.words[1]?.plain === "-v" && command.words[2]?.plain === inductionName)
16701
- return false;
16702
16742
  }
16703
16743
  for (const word of [...command.words, ...command.redirects.map(redirect => redirect.target)])
16704
16744
  for (const part of word.parts) {
@@ -16977,6 +17017,7 @@ export class Runtime {
16977
17017
  execSyncLocalDeclStep(decl, rawState, io, monitor, touched, line) {
16978
17018
  const locals = (decl.kind === "local" || decl.kind === "declare") && rawState.locals.length > 0 ? rawState.locals[rawState.locals.length - 1] : undefined;
16979
17019
  let lastArg = decl.kind;
17020
+ const values = decl.items.map(item => item.value === undefined ? undefined : this.fastValueWord(item.value, rawState, io, false, false, false, rawState.braceexpand !== false, 0, line));
16980
17021
  for (let i = 0; i < decl.items.length; i++) {
16981
17022
  const item = decl.items[i];
16982
17023
  const isNewLocal = locals ? !locals.has(item.name) : false;
@@ -16994,7 +17035,7 @@ export class Runtime {
16994
17035
  touched.add(item.name);
16995
17036
  }
16996
17037
  if (item.value !== undefined) {
16997
- const val = this.fastValueWord(item.value, rawState, io, false, false, false, rawState.braceexpand !== false, 0, line);
17038
+ const val = values[i];
16998
17039
  if (item.append) {
16999
17040
  const curRaw = rawState.variables[item.name];
17000
17041
  const curStr = curRaw === undefined ? "" : (touched.has(item.name) ? curRaw : (monitor.values.get(item.name, curRaw) ?? curRaw));
@@ -17004,7 +17045,7 @@ export class Runtime {
17004
17045
  rawState.variables[item.name] = val;
17005
17046
  }
17006
17047
  touched.add(item.name);
17007
- lastArg = `${item.name}=${rawState.variables[item.name]}`;
17048
+ lastArg = `${item.name}${item.append ? "+=" : "="}${val}`;
17008
17049
  }
17009
17050
  else {
17010
17051
  lastArg = item.name;
@@ -17103,7 +17144,28 @@ export class Runtime {
17103
17144
  if (this._syncStdoutBatch.length >= 8192)
17104
17145
  this.flushSyncStdoutBatch(io);
17105
17146
  }
17147
+ runSyncDiscardStep(step, rawState, io) {
17148
+ this.budget.fileSystemOperation();
17149
+ const args = step.discardWords.map(word => this.fastValueWord(word, rawState, io, false, false, false, false, 0, step.line));
17150
+ const cmdName = step.cmd.words[0].plain;
17151
+ let output;
17152
+ if (cmdName === "pwd")
17153
+ output = rawState.variables.PWD ?? rawState.cwd;
17154
+ else if (cmdName === "echo")
17155
+ output = args[0];
17156
+ else if (cmdName === "dirname")
17157
+ output = this.evalSyncDirname(args);
17158
+ else
17159
+ output = this.evalSyncBasename(step.discardSuffix === undefined ? args : ["-s", step.discardSuffix, ...args]);
17160
+ const bytes = shellValueByteLength(output) + 1;
17161
+ if (this.budget.bytes + bytes > this.budget.maxOutputBytesSmi && bytes > this.budget.limits.maxOutputBytes - this.budget.bytes)
17162
+ this.budget.fail("maxOutputBytes");
17163
+ this.budget.bytes += bytes;
17164
+ return args[args.length - 1] ?? cmdName;
17165
+ }
17106
17166
  writeSyncRedirectStep(step, encoded, append, mode, rawState, io) {
17167
+ if (this.budget.bytes + encoded.byteLength > this.budget.maxOutputBytesSmi && encoded.byteLength > this.budget.limits.maxOutputBytes - this.budget.bytes)
17168
+ this.budget.fail("maxOutputBytes");
17107
17169
  // The memory fast writer may retain its input; the encoder scratch is reused.
17108
17170
  encoded = encoded.slice();
17109
17171
  if (step.targetDirPrefix !== undefined && step.targetNamePrefix !== undefined) {
@@ -17267,31 +17329,7 @@ export class Runtime {
17267
17329
  lastArg = pLastArg;
17268
17330
  }
17269
17331
  else if (step.isDiscardDevNull && step.discardWords !== undefined) {
17270
- this.budget.fileSystemOperation();
17271
- const dw = step.discardWords;
17272
- const cmdName = step.cmd.words[0].plain;
17273
- let evalFirst = "";
17274
- let evalLast = cmdName;
17275
- for (let wi = 0; wi < dw.length; wi++) {
17276
- const v = this.fastValueWord(dw[wi], rawState, io, false, false, false, false, 0, step.line);
17277
- if (wi === 0)
17278
- evalFirst = v;
17279
- evalLast = v;
17280
- }
17281
- let outChars = 0;
17282
- if (cmdName === "pwd")
17283
- outChars = (rawState.variables.PWD ?? rawState.cwd).length + 1;
17284
- else if (cmdName === "echo")
17285
- outChars = evalFirst.length + 1;
17286
- else if (cmdName === "dirname")
17287
- outChars = dirname(evalFirst).length + 1;
17288
- else if (cmdName === "basename") {
17289
- const sfx = step.discardSuffix !== undefined ? step.discardSuffix : (dw.length === 2 ? evalLast : undefined);
17290
- const baseStr = /^\/+$/u.test(evalFirst) ? "/" : basename(evalFirst);
17291
- outChars = (sfx && baseStr !== sfx && baseStr.endsWith(sfx) ? baseStr.length - sfx.length : baseStr.length) + 1;
17292
- }
17293
- this.budget.bytes += outChars;
17294
- lastArg = step.discardSuffix !== undefined ? evalLast : (dw.length > 0 ? evalLast : cmdName);
17332
+ lastArg = this.runSyncDiscardStep(step, rawState, io);
17295
17333
  }
17296
17334
  else if (step.name !== undefined && step.value !== undefined) {
17297
17335
  rawState.substitutionStatus = 0;
@@ -17851,31 +17889,7 @@ export class Runtime {
17851
17889
  lastArg = pLastArg;
17852
17890
  }
17853
17891
  else if (step.isDiscardDevNull && step.discardWords !== undefined) {
17854
- this.budget.fileSystemOperation();
17855
- const dw = step.discardWords;
17856
- const cmdName = step.cmd.words[0].plain;
17857
- let evalFirst = "";
17858
- let evalLast = cmdName;
17859
- for (let wi = 0; wi < dw.length; wi++) {
17860
- const v = this.fastValueWord(dw[wi], rawState, io, false, false, false, false, 0, step.line);
17861
- if (wi === 0)
17862
- evalFirst = v;
17863
- evalLast = v;
17864
- }
17865
- let outChars = 0;
17866
- if (cmdName === "pwd")
17867
- outChars = (rawState.variables.PWD ?? rawState.cwd).length + 1;
17868
- else if (cmdName === "echo")
17869
- outChars = evalFirst.length + 1;
17870
- else if (cmdName === "dirname")
17871
- outChars = dirname(evalFirst).length + 1;
17872
- else if (cmdName === "basename") {
17873
- const sfx = step.discardSuffix !== undefined ? step.discardSuffix : (dw.length === 2 ? evalLast : undefined);
17874
- const baseStr = /^\/+$/u.test(evalFirst) ? "/" : basename(evalFirst);
17875
- outChars = (sfx && baseStr !== sfx && baseStr.endsWith(sfx) ? baseStr.length - sfx.length : baseStr.length) + 1;
17876
- }
17877
- this.budget.bytes += outChars;
17878
- lastArg = step.discardSuffix !== undefined ? evalLast : (dw.length > 0 ? evalLast : cmdName);
17892
+ lastArg = this.runSyncDiscardStep(step, rawState, io);
17879
17893
  }
17880
17894
  else if (step.name !== undefined && step.value !== undefined) {
17881
17895
  rawState.substitutionStatus = 0;
@@ -18031,31 +18045,7 @@ export class Runtime {
18031
18045
  lastArg = pLastArg;
18032
18046
  }
18033
18047
  else if (step.isDiscardDevNull && step.discardWords !== undefined) {
18034
- this.budget.fileSystemOperation();
18035
- const dw = step.discardWords;
18036
- const cmdName = step.cmd.words[0].plain;
18037
- let evalFirst = "";
18038
- let evalLast = cmdName;
18039
- for (let wi = 0; wi < dw.length; wi++) {
18040
- const v = this.fastValueWord(dw[wi], rawState, io, false, false, false, false, 0, step.line);
18041
- if (wi === 0)
18042
- evalFirst = v;
18043
- evalLast = v;
18044
- }
18045
- let outChars = 0;
18046
- if (cmdName === "pwd")
18047
- outChars = (rawState.variables.PWD ?? rawState.cwd).length + 1;
18048
- else if (cmdName === "echo")
18049
- outChars = evalFirst.length + 1;
18050
- else if (cmdName === "dirname")
18051
- outChars = dirname(evalFirst).length + 1;
18052
- else if (cmdName === "basename") {
18053
- const sfx = step.discardSuffix !== undefined ? step.discardSuffix : (dw.length === 2 ? evalLast : undefined);
18054
- const baseStr = /^\/+$/u.test(evalFirst) ? "/" : basename(evalFirst);
18055
- outChars = (sfx && baseStr !== sfx && baseStr.endsWith(sfx) ? baseStr.length - sfx.length : baseStr.length) + 1;
18056
- }
18057
- this.budget.bytes += outChars;
18058
- lastArg = step.discardSuffix !== undefined ? evalLast : (dw.length > 0 ? evalLast : cmdName);
18048
+ lastArg = this.runSyncDiscardStep(step, rawState, io);
18059
18049
  }
18060
18050
  else if (step.name !== undefined && step.value !== undefined) {
18061
18051
  rawState.substitutionStatus = 0;
@@ -18990,8 +18980,6 @@ export class Runtime {
18990
18980
  const rawState = stateMonitor(state)?.raw ?? state;
18991
18981
  if (rawState.variables._ !== undefined)
18992
18982
  delete rawState.variables._;
18993
- rawState.lastArgument = wordValues[wordValues.length - 1] ?? w0Plain;
18994
- state.lastArgument = rawState.lastArgument;
18995
18983
  return await this.dispatchFastFunction(w0Plain, state.functions.get(w0Plain), wordValues.slice(1), state, originalIO);
18996
18984
  }
18997
18985
  }
@@ -20265,8 +20253,10 @@ export class Runtime {
20265
20253
  const rawState = stateMonitor(state)?.raw ?? state;
20266
20254
  if (!state._readOnlyStage && rawState.variables._ !== undefined)
20267
20255
  delete rawState.variables._;
20268
- rawState.lastArgument = words.length > 0 ? words[words.length - 1] : "";
20269
- state.lastArgument = rawState.lastArgument;
20256
+ if (!state.functions.has(words[0] ?? "") && !["eval", ".", "source"].includes(words[declarationIndex] ?? "")) {
20257
+ rawState.lastArgument = words.at(-1) ?? "";
20258
+ state.lastArgument = rawState.lastArgument;
20259
+ }
20270
20260
  if (!words.length)
20271
20261
  return state.substitutionStatus;
20272
20262
  const args = words.slice(1);
@@ -20530,8 +20520,10 @@ export class Runtime {
20530
20520
  const rawState = stateMonitor(state)?.raw ?? state;
20531
20521
  if (rawState.variables._ !== undefined && !assignments.some(assignment => assignment.name === "_"))
20532
20522
  delete rawState.variables._;
20533
- rawState.lastArgument = words.length > 0 ? words[words.length - 1] : "";
20534
- state.lastArgument = rawState.lastArgument;
20523
+ if (!functionCommand && !["eval", ".", "source"].includes(words[declarationIndex] ?? "")) {
20524
+ rawState.lastArgument = words.at(-1) ?? "";
20525
+ state.lastArgument = rawState.lastArgument;
20526
+ }
20535
20527
  }
20536
20528
  if (!words.length)
20537
20529
  return state.substitutionStatus;
@@ -21402,10 +21394,16 @@ export class Runtime {
21402
21394
  }
21403
21395
  if (context.command === "command" || context.command === "builtin" || context.command === "type")
21404
21396
  return { exitCode: await this.discoveryBuiltin(context, state, io, assignments, defaultPath) };
21405
- if (context.command === "." || context.command === "source")
21406
- return { exitCode: await this.sourceBuiltin(context, state, { ...io, ...context }, special) };
21407
- if (context.command === "eval")
21408
- return { exitCode: await this.evalBuiltin(context, state, { ...io, ...context }, special) };
21397
+ if (context.command === "." || context.command === "source") {
21398
+ const exitCode = await this.sourceBuiltin(context, state, { ...io, ...context }, special);
21399
+ state.lastArgument = context.args.at(-1) ?? context.command;
21400
+ return { exitCode };
21401
+ }
21402
+ if (context.command === "eval") {
21403
+ const exitCode = await this.evalBuiltin(context, state, { ...io, ...context }, special);
21404
+ state.lastArgument = context.args.at(-1) ?? context.command;
21405
+ return { exitCode };
21406
+ }
21409
21407
  const builtinWork = this.builtin({ ...context, [declarationArrays]: io[declarationArrays] }, state, assignments, (error, diagnostic) => { builtinFailure = { error, diagnostic }; }, bypassFunctions);
21410
21408
  const builtin = await interruptible(builtinWork, this.signal);
21411
21409
  if (builtin !== undefined) {
@@ -27908,6 +27906,10 @@ export class Runtime {
27908
27906
  const core = anchorEnd ? core1.slice(0, -1) : core1;
27909
27907
  if (core.length === 0 && !anchorStart && !anchorEnd)
27910
27908
  return undefined;
27909
+ // POSIX-only brackets and leftmost-longest alternatives belong to sed's matcher.
27910
+ if (core.includes("[:") || core.includes("[.") || core.includes("[=") ||
27911
+ core.includes("[]") || core.includes("[^]") || isExtended && core.includes("|"))
27912
+ return undefined;
27911
27913
  let reSrc;
27912
27914
  if (/^[a-zA-Z0-9_ :;,=-]*$/.test(core)) {
27913
27915
  if (/\\[1-9]/.test(rep))
@@ -28121,7 +28123,9 @@ export class Runtime {
28121
28123
  const isOnlyMatching = mode.includes("o");
28122
28124
  // POSIX bracket classes, collating symbols and equivalence classes have
28123
28125
  // different meanings in JS RegExp. Let the command matcher parse them.
28124
- if (isExtended && !isFixed && rawPatterns.some(pattern => pattern.includes("[:") || pattern.includes("[.") || pattern.includes("[=")))
28126
+ if (!isFixed && rawPatterns.some(pattern => pattern.includes("[:") || pattern.includes("[.") || pattern.includes("[=") ||
28127
+ pattern.includes("[]") || pattern.includes("[^]") ||
28128
+ isExtended && pattern.includes("[") && pattern.includes("|")))
28125
28129
  return undefined;
28126
28130
  if (isCount && (isLineNumber || isOnlyMatching || beforeCtx > 0 || afterCtx > 0))
28127
28131
  return undefined;
@@ -28154,15 +28158,19 @@ export class Runtime {
28154
28158
  if (!re)
28155
28159
  return undefined;
28156
28160
  const out = [];
28161
+ let matched = false;
28157
28162
  for (let li = 0; li < rawLines.length; li++) {
28158
28163
  const matches = rawLines[li].match(re);
28159
28164
  if (matches) {
28165
+ matched = true;
28160
28166
  for (let mi = 0; mi < matches.length; mi++) {
28167
+ if (matches[mi].length === 0)
28168
+ continue;
28161
28169
  out.push(isLineNumber ? `${li + 1}:${matches[mi]}` : matches[mi]);
28162
28170
  }
28163
28171
  }
28164
28172
  }
28165
- return { lines: out, status: out.length > 0 ? 0 : 1 };
28173
+ return { lines: out, status: matched ? 0 : 1 };
28166
28174
  }
28167
28175
  if (isExtended && isFixed)
28168
28176
  return undefined;
@@ -29455,6 +29463,11 @@ export class Runtime {
29455
29463
  return this.evalSyncSort(rawLines, flag !== undefined ? [flag] : [], false) ?? [...rawLines];
29456
29464
  }
29457
29465
  translateSyncTr(input, set1Spec, set2Spec, truncateSet1 = false) {
29466
+ // Other string2 classes require the tr parser's class and alignment validation.
29467
+ if (set2Spec.includes("[:") &&
29468
+ !(set1Spec === "[:lower:]" && set2Spec === "[:upper:]") &&
29469
+ !(set1Spec === "[:upper:]" && set2Spec === "[:lower:]"))
29470
+ return undefined;
29458
29471
  if ((set1Spec === "a-z" || set1Spec === "[:lower:]") && (set2Spec === "A-Z" || set2Spec === "[:upper:]")) {
29459
29472
  return input.replace(/[a-z]/g, ch => String.fromCharCode(ch.charCodeAt(0) - 32));
29460
29473
  }
@@ -29830,7 +29843,7 @@ export class Runtime {
29830
29843
  const firstName = stageNames[sIdx];
29831
29844
  const extDef = stageDefs[sIdx];
29832
29845
  const stageArgs = stageArgsList[sIdx];
29833
- const nextBuf = (index & 1) === 0 ? sharedSyncPipeBuf0 : sharedSyncPipeBuf1;
29846
+ let nextBuf = (index & 1) === 0 ? sharedSyncPipeBuf0 : sharedSyncPipeBuf1;
29834
29847
  const isInlineCutField = firstName === "cut";
29835
29848
  const isInlineTr = firstName === "tr";
29836
29849
  const isInlineAwk = firstName === "awk";
@@ -29879,6 +29892,8 @@ export class Runtime {
29879
29892
  if (nextTotalBytes > this.budget.maxOutputBytesSmi && sub.byteLength > this.budget.limits.maxOutputBytes - this.budget.bytes)
29880
29893
  this.budget.fail("maxOutputBytes");
29881
29894
  this.budget.bytes = nextTotalBytes;
29895
+ if (sub.byteLength > nextBuf.byteLength)
29896
+ nextBuf = new Uint8Array(sub.byteLength);
29882
29897
  nextBuf.set(sub, 0);
29883
29898
  prevBuf = nextBuf;
29884
29899
  prevLen = sub.byteLength;
@@ -29928,12 +29943,12 @@ export class Runtime {
29928
29943
  if (transformed === undefined)
29929
29944
  return undefined;
29930
29945
  const outByteLen = shellValueByteLength(transformed);
29931
- if (outByteLen > nextBuf.byteLength)
29932
- return undefined;
29933
29946
  const nextTotalBytes = this.budget.bytes + outByteLen;
29934
29947
  if (nextTotalBytes > this.budget.maxOutputBytesSmi && outByteLen > this.budget.limits.maxOutputBytes - this.budget.bytes)
29935
29948
  this.budget.fail("maxOutputBytes");
29936
29949
  this.budget.bytes = nextTotalBytes;
29950
+ if (outByteLen > nextBuf.byteLength)
29951
+ nextBuf = new Uint8Array(outByteLen);
29937
29952
  const written = transformed.length === 0 ? 0 : fastSharedTextEncoder.encodeInto(transformed, nextBuf).written;
29938
29953
  prevBuf = nextBuf;
29939
29954
  prevLen = written;
@@ -29971,9 +29986,10 @@ export class Runtime {
29971
29986
  outLines = grepRes.lines;
29972
29987
  }
29973
29988
  else if (isInlineTac) {
29974
- if (inStr.length > 0 && !inStr.endsWith("\n"))
29975
- return undefined;
29976
29989
  outLines = [...rawLines].reverse();
29990
+ if (!inStr.endsWith("\n") && outLines.length > 1) {
29991
+ outLines.splice(0, 2, outLines[0] + outLines[1]);
29992
+ }
29977
29993
  }
29978
29994
  else if (isInlineNl) {
29979
29995
  const nlRes = this.evalSyncNl(rawLines, stageArgs);
@@ -30003,12 +30019,14 @@ export class Runtime {
30003
30019
  if (cleaned.length % 4 !== 0 || (cleaned.length > 0 && !/^[A-Za-z0-9+/]+={0,2}$/.test(cleaned)))
30004
30020
  return undefined;
30005
30021
  const decoded = this.syncBase64DecodeBytes(cleaned);
30006
- if (decoded.some(byte => byte === 0 || byte >= 128) || decoded.byteLength > nextBuf.byteLength)
30022
+ if (decoded.some(byte => byte === 0 || byte >= 128))
30007
30023
  return undefined;
30008
30024
  const nextTotalBytes = this.budget.bytes + decoded.byteLength;
30009
30025
  if (nextTotalBytes > this.budget.maxOutputBytesSmi && decoded.byteLength > this.budget.limits.maxOutputBytes - this.budget.bytes)
30010
30026
  this.budget.fail("maxOutputBytes");
30011
30027
  this.budget.bytes = nextTotalBytes;
30028
+ if (decoded.byteLength > nextBuf.byteLength)
30029
+ nextBuf = new Uint8Array(decoded.byteLength);
30012
30030
  nextBuf.set(decoded, 0);
30013
30031
  prevBuf = nextBuf;
30014
30032
  prevLen = decoded.byteLength;
@@ -30023,15 +30041,15 @@ export class Runtime {
30023
30041
  }
30024
30042
  // These filters preserve the terminator of the final selected input line.
30025
30043
  const preservesTerminator = firstName === "head" || firstName === "tail" || firstName === "rev" || firstName === "sed";
30026
- const terminated = sedTerminated || !preservesTerminator || inStr.endsWith("\n") || firstName === "head" && outLines.length < rawLines.length;
30044
+ const terminated = isInlineTac ? inStr.includes("\n") : sedTerminated || !preservesTerminator || inStr.endsWith("\n") || firstName === "head" && outLines.length < rawLines.length;
30027
30045
  const outStr = outLines.length > 0 ? outLines.join("\n") + (terminated ? "\n" : "") : "";
30028
30046
  const outByteLen = shellValueByteLength(outStr);
30029
- if (outByteLen > nextBuf.byteLength)
30030
- return undefined;
30031
30047
  const nextTotalBytes = this.budget.bytes + outByteLen;
30032
30048
  if (nextTotalBytes > this.budget.maxOutputBytesSmi && outByteLen > this.budget.limits.maxOutputBytes - this.budget.bytes)
30033
30049
  this.budget.fail("maxOutputBytes");
30034
30050
  this.budget.bytes = nextTotalBytes;
30051
+ if (outByteLen > nextBuf.byteLength)
30052
+ nextBuf = new Uint8Array(outByteLen);
30035
30053
  const written = outStr.length === 0 ? 0 : fastSharedTextEncoder.encodeInto(outStr, nextBuf).written;
30036
30054
  prevBuf = nextBuf;
30037
30055
  prevLen = written;
@@ -100951,6 +100951,18 @@ function metadataNode(value3, charge) {
100951
100951
  }
100952
100952
  return visit(value3);
100953
100953
  }
100954
+ var richUnderlines = {
100955
+ none: "none",
100956
+ single: "single",
100957
+ double: "double",
100958
+ low: "singleAccounting",
100959
+ error: "single",
100960
+ "single-line": "single",
100961
+ "double-line": "double",
100962
+ "error-line": "single",
100963
+ singleAccounting: "singleAccounting",
100964
+ doubleAccounting: "doubleAccounting"
100965
+ };
100954
100966
  function writeRichString(value3, runs, xml2, charge, target = "cell") {
100955
100967
  charge?.(value3.length);
100956
100968
  const t = (text2) => xml2("t", text2.trim() !== text2 ? { "xml:space": "preserve" } : {}, escapeXlsx(encodeXlsxString(text2)));
@@ -100973,7 +100985,9 @@ function writeRichString(value3, runs, xml2, charge, target = "cell") {
100973
100985
  if (attrs.size !== void 0) properties += xml2("sz", { val: Number(attrs.size) / 1024 });
100974
100986
  if (typeof attrs.family === "string") properties += xml2("rFont", { val: encodeXlsxString(attrs.family) });
100975
100987
  if (typeof attrs.color === "string") properties += xml2("color", { rgb: "FF" + attrs.color.split("x").join("").toUpperCase() });
100976
- if (typeof attrs.underline === "string") properties += xml2("u", { val: attrs.underline === "low" ? "singleAccounting" : attrs.underline === "error" ? "single" : attrs.underline });
100988
+ if (typeof attrs.underline === "string") properties += xml2("u", {
100989
+ val: Object.hasOwn(richUnderlines, attrs.underline) ? richUnderlines[attrs.underline] : "none"
100990
+ });
100977
100991
  if (attrs.subscript || attrs.superscript) properties += xml2("vertAlign", { val: attrs.subscript ? "subscript" : "superscript" });
100978
100992
  result2 += xml2("r", {}, (properties || target === "comment" ? xml2("rPr", {}, properties) : "") + t(decoder.decode(bytes.subarray(start, end2))));
100979
100993
  }
@@ -144460,7 +144474,11 @@ function odfCellStyle(node3, parent, charge) {
144460
144474
  const italic = attr4(p2, "font-style", "fo");
144461
144475
  if (italic !== void 0) font2.Italic = italic === "italic" || italic === "oblique" ? 1 : 0;
144462
144476
  const underline = attr4(p2, "text-underline-style", "style");
144463
- if (underline !== void 0) font2.Underline = underline === "none" ? 0 : attr4(p2, "text-underline-type", "style") === "double" ? 2 : 1;
144477
+ if (underline !== void 0) {
144478
+ const type = attr4(p2, "text-underline-type", "style");
144479
+ const double = type === "double" || attr4(p2, "text-underline-width", "style") === "bold";
144480
+ font2.Underline = underline === "none" || type === "none" ? 0 : (double ? 2 : 1) + (attr4(p2, "text-underline-placement", "gnm") === "low" ? 2 : 0);
144481
+ }
144464
144482
  const strike = attr4(p2, "text-line-through-style", "style");
144465
144483
  if (strike !== void 0) font2.StrikeThrough = strike === "none" ? 0 : 1;
144466
144484
  family = attr4(p2, "font-family", "fo") ?? attr4(p2, "font-name", "style") ?? family;
@@ -144964,8 +144982,13 @@ function createOdfStyles(xml2, extended, book, context) {
144964
144982
  if (f2.Bold !== void 0) text2["fo:font-weight"] = Number(f2.Bold) ? "bold" : "normal";
144965
144983
  if (f2.Italic !== void 0) text2["fo:font-style"] = Number(f2.Italic) ? "italic" : "normal";
144966
144984
  if (f2.Underline !== void 0) {
144967
- text2["style:text-underline-style"] = Number(f2.Underline) ? "solid" : "none";
144968
- text2["style:text-underline-type"] = Number(f2.Underline) === 2 ? "double" : "single";
144985
+ const underline = Number(f2.Underline), low = underline === 3 || underline === 4;
144986
+ text2["style:text-underline-style"] = underline ? "solid" : "none";
144987
+ text2["style:text-underline-type"] = underline === 2 || underline === 4 ? "double" : underline ? "single" : "none";
144988
+ text2["style:text-underline-width"] = "auto";
144989
+ text2["style:text-underline-color"] = "font-color";
144990
+ text2["style:text-underline-mode"] = "continuous";
144991
+ if (extended && low) text2["gnm:text-underline-placement"] = "low";
144969
144992
  }
144970
144993
  if (f2.StrikeThrough !== void 0) text2["style:text-line-through-style"] = Number(f2.StrikeThrough) ? "solid" : "none";
144971
144994
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@poe-platform/safe-bash",
3
- "version": "0.1.750",
3
+ "version": "0.1.751",
4
4
  "description": "Extensible virtual shell with pluggable filesystems and streaming commands",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -645,7 +645,7 @@
645
645
  "access": "public"
646
646
  },
647
647
  "dependencies": {
648
- "@poe-platform/safe-fs": "0.1.750",
648
+ "@poe-platform/safe-fs": "0.1.751",
649
649
  "pdf-lib": "1.17.1",
650
650
  "@types/node": "^25.2.2",
651
651
  "pako": "3.0.1",