@poe-platform/safe-bash 0.1.751 → 0.1.752

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.
@@ -52139,8 +52139,18 @@ function writeRichString(value3, runs, xml2, charge, target = "cell") {
52139
52139
  const t2 = (text2) => xml2("t", text2.trim() !== text2 ? { "xml:space": "preserve" } : {}, escapeXlsx(encodeXlsxString(text2)));
52140
52140
  if (!runs?.length) return t2(value3);
52141
52141
  charge?.(runs.length);
52142
- const bytes = new TextEncoder().encode(value3), decoder = new TextDecoder("UTF-8", { fatal: true });
52143
- const points = [.../* @__PURE__ */ new Set([0, bytes.length, ...runs.flatMap((run) => [run.start, run.end])])].sort((a3, b3) => a3 - b3);
52142
+ const length = new TextEncoder().encode(value3).length;
52143
+ const points = [.../* @__PURE__ */ new Set([0, length, ...runs.flatMap((run) => [run.start, run.end])])].sort((a3, b3) => a3 - b3);
52144
+ let byteOffset = 0, characterOffset = 0;
52145
+ const characters = points.map((point) => {
52146
+ while (byteOffset < point && characterOffset < value3.length) {
52147
+ const code2 = value3.codePointAt(characterOffset);
52148
+ byteOffset += code2 < 128 ? 1 : code2 < 2048 ? 2 : code2 < 65536 ? 3 : 4;
52149
+ characterOffset += code2 > 65535 ? 2 : 1;
52150
+ }
52151
+ if (byteOffset !== point) throw new SsconvertError("invalid-request", "Invalid rich text UTF-8 boundary");
52152
+ return characterOffset;
52153
+ });
52144
52154
  let result2 = target === "comment" ? xml2("r", {}, t2("")) : "";
52145
52155
  for (let i4 = 0; i4 + 1 < points.length; i4++) {
52146
52156
  charge?.(runs.length);
@@ -52160,7 +52170,7 @@ function writeRichString(value3, runs, xml2, charge, target = "cell") {
52160
52170
  val: Object.hasOwn(richUnderlines, attrs.underline) ? richUnderlines[attrs.underline] : "none"
52161
52171
  });
52162
52172
  if (attrs.subscript || attrs.superscript) properties += xml2("vertAlign", { val: attrs.subscript ? "subscript" : "superscript" });
52163
- result2 += xml2("r", {}, (properties || target === "comment" ? xml2("rPr", {}, properties) : "") + t2(decoder.decode(bytes.subarray(start, end2))));
52173
+ result2 += xml2("r", {}, (properties || target === "comment" ? xml2("rPr", {}, properties) : "") + t2(value3.slice(characters[i4], characters[i4 + 1])));
52164
52174
  }
52165
52175
  return result2;
52166
52176
  }
@@ -83189,7 +83199,7 @@ function createHtmlWriter(profile) {
83189
83199
  closures.unshift(close);
83190
83200
  }
83191
83201
  }
83192
- put(encoded2(new TextDecoder().decode(bytes.subarray(from, to))));
83202
+ put(encoded2(new TextDecoder("utf-8", { ignoreBOM: true }).decode(bytes.subarray(from, to))));
83193
83203
  for (const closure of closures) put(closure);
83194
83204
  }
83195
83205
  } else if (cell) put(encoded2(await renderCellText(cell, book, context2, "preserve")));
@@ -164631,4 +164641,4 @@ tslib/tslib.js:
164631
164641
  @noble/ciphers/utils.js:
164632
164642
  (*! noble-ciphers - MIT License (c) 2023 Paul Miller (paulmillr.com) *)
164633
164643
  */
164634
- //# sourceMappingURL=chunk-ILHNFAZE.js.map
164644
+ //# sourceMappingURL=chunk-W7QHCGDH.js.map
@@ -81,7 +81,7 @@ import {
81
81
  updateWorkbook,
82
82
  validSheetSize,
83
83
  visitFormula
84
- } from "./chunk-ILHNFAZE.js";
84
+ } from "./chunk-W7QHCGDH.js";
85
85
  import "./chunk-CVAVQZSF.js";
86
86
  import "./chunk-RCM5PZBN.js";
87
87
  import "./chunk-LSRHLNCR.js";
@@ -171,4 +171,4 @@ export {
171
171
  validSheetSize,
172
172
  visitFormula
173
173
  };
174
- //# sourceMappingURL=src-XXKXVO7T.js.map
174
+ //# sourceMappingURL=src-XCSZZ35R.js.map
@@ -2363,6 +2363,8 @@ async function executeUniqGeneral(context, preReadSource) {
2363
2363
  const onlyRepeated = parsed.flags.has("d");
2364
2364
  const onlyUnique = parsed.flags.has("u");
2365
2365
  const identityKey = skipFields === 0 && skipCharacters === 0 && width === Infinity && !ignoreCase;
2366
+ const locale = context.env.LC_ALL || context.env.LC_CTYPE || context.env.LANG;
2367
+ const byteLocale = locale === "C" || locale === "POSIX";
2366
2368
  const uniqDecoder = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true });
2367
2369
  const key = (bytes) => {
2368
2370
  if (identityKey)
@@ -2376,8 +2378,14 @@ async function executeUniqGeneral(context, preReadSource) {
2376
2378
  }
2377
2379
  let result;
2378
2380
  try {
2379
- const chars = Array.from(uniqDecoder.decode(bytes.subarray(offset)));
2380
- result = encoder.encode((width === Infinity ? chars.slice(skipCharacters) : chars.slice(skipCharacters, skipCharacters + width)).join(""));
2381
+ if (byteLocale) {
2382
+ offset += skipCharacters;
2383
+ result = bytes.subarray(offset, width === Infinity ? undefined : offset + width);
2384
+ }
2385
+ else {
2386
+ const chars = Array.from(uniqDecoder.decode(bytes.subarray(offset)));
2387
+ result = encoder.encode((width === Infinity ? chars.slice(skipCharacters) : chars.slice(skipCharacters, skipCharacters + width)).join(""));
2388
+ }
2381
2389
  }
2382
2390
  catch (error) {
2383
2391
  if (!(error instanceof TypeError))
@@ -2898,7 +2906,7 @@ export function textCommands() {
2898
2906
  else {
2899
2907
  const f1 = Number(part.slice(0, dashIdx));
2900
2908
  const f2 = Number(part.slice(dashIdx + 1));
2901
- if (f1 < 1 || f2 < f1 || f2 > 30) {
2909
+ if (!Number.isFinite(f2) || f1 < 1 || f2 < f1 || f2 > 30) {
2902
2910
  okMask = false;
2903
2911
  break;
2904
2912
  }
@@ -10852,6 +10852,8 @@ async function executeUniqGeneral(context2, preReadSource) {
10852
10852
  const onlyRepeated = parsed.flags.has("d");
10853
10853
  const onlyUnique = parsed.flags.has("u");
10854
10854
  const identityKey = skipFields === 0 && skipCharacters === 0 && width === Infinity && !ignoreCase;
10855
+ const locale = context2.env.LC_ALL || context2.env.LC_CTYPE || context2.env.LANG;
10856
+ const byteLocale2 = locale === "C" || locale === "POSIX";
10855
10857
  const uniqDecoder = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true });
10856
10858
  const key = (bytes2) => {
10857
10859
  if (identityKey) return bytes2;
@@ -10862,8 +10864,13 @@ async function executeUniqGeneral(context2, preReadSource) {
10862
10864
  }
10863
10865
  let result;
10864
10866
  try {
10865
- const chars = Array.from(uniqDecoder.decode(bytes2.subarray(offset)));
10866
- result = encoder.encode((width === Infinity ? chars.slice(skipCharacters) : chars.slice(skipCharacters, skipCharacters + width)).join(""));
10867
+ if (byteLocale2) {
10868
+ offset += skipCharacters;
10869
+ result = bytes2.subarray(offset, width === Infinity ? void 0 : offset + width);
10870
+ } else {
10871
+ const chars = Array.from(uniqDecoder.decode(bytes2.subarray(offset)));
10872
+ result = encoder.encode((width === Infinity ? chars.slice(skipCharacters) : chars.slice(skipCharacters, skipCharacters + width)).join(""));
10873
+ }
10867
10874
  } catch (error) {
10868
10875
  if (!(error instanceof TypeError)) throw error;
10869
10876
  offset += skipCharacters;
@@ -11320,7 +11327,7 @@ function textCommands() {
11320
11327
  } else {
11321
11328
  const f1 = Number(part.slice(0, dashIdx));
11322
11329
  const f2 = Number(part.slice(dashIdx + 1));
11323
- if (f1 < 1 || f2 < f1 || f2 > 30) {
11330
+ if (!Number.isFinite(f2) || f1 < 1 || f2 < f1 || f2 > 30) {
11324
11331
  okMask = false;
11325
11332
  break;
11326
11333
  }
@@ -68481,8 +68488,6 @@ var DiscardCommandFailure = class extends CommandFailure2 {
68481
68488
  };
68482
68489
  var ParameterExpansionFailure = class extends ExpansionFailure {
68483
68490
  };
68484
- var SubstringRangeFailure = class extends ExpansionFailure {
68485
- };
68486
68491
  var NounsetFailure = class extends ExpansionFailure {
68487
68492
  constructor(message3, line, status2 = 1) {
68488
68493
  super(message3, line);
@@ -79025,40 +79030,16 @@ var Runtime = class _Runtime {
79025
79030
  if (getArraySelector(part) !== void 0) {
79026
79031
  return false;
79027
79032
  }
79028
- if (part.length || part.substring || part.operator !== void 0) {
79033
+ if (part.substring) return false;
79034
+ if (part.length || part.operator !== void 0) {
79029
79035
  if (this.budget.limits.maxExpansionBytes !== Infinity || rawState.depth >= 32 || rawState.nounset || rawState.nocasematch || byteLocale(rawState.variables) !== false) {
79030
79036
  return false;
79031
79037
  }
79032
79038
  if (part.length) {
79033
- if (part.operator !== void 0 || part.substring) return false;
79034
- } else if (part.substring) {
79035
- if (part.operator !== void 0 || !part.substring.offset.parts.every((p) => p.kind === "text" || p.kind === "variable" && !p.operator && !p.substring && !p.length) || part.substring.length && !part.substring.length.parts.every((p) => p.kind === "text" || p.kind === "variable" && !p.operator && !p.substring && !p.length)) {
79036
- return false;
79037
- }
79038
- } else if (part.operator === "^" || part.operator === "^^" || part.operator === "," || part.operator === ",,") {
79039
- if (part.alternate && part.alternate.parts.length > 0) return false;
79039
+ if (part.operator !== void 0) return false;
79040
79040
  } else if (part.operator === "-" || part.operator === ":-" || part.operator === "+" || part.operator === ":+") {
79041
79041
  if (!part.alternate || !part.alternate.parts.every((p) => p.kind === "text" || p.kind === "arithmetic" || p.kind === "variable" && !p.operator && !p.substring && p.prefixNames !== "@")) return false;
79042
79042
  if (!this.isPureSyncValueWord(part.alternate, rawState, allowAt)) return false;
79043
- } else if (part.operator === "#" || part.operator === "##" || part.operator === "%" || part.operator === "%%") {
79044
- if (!part.alternate || part.alternate.parts.length === 0) return false;
79045
- const q0 = part.alternate.parts[0].quoted;
79046
- const trimOp = part.operator;
79047
- if (!part.alternate.parts.every((p, idx) => p.quoted === q0 && (p.kind === "text" && !p.byteValue && (p.quoted || !hasGlobOrEscape(p.value, !!rawState.extglob) || idx === 0 && (trimOp === "#" || trimOp === "##") && p.value === "*" || idx === part.alternate.parts.length - 1 && (trimOp === "%" || trimOp === "%%") && p.value === "*" || part.alternate.parts.length === 1 && Boolean(tryCompileTrimGlobToRegex(p.value, trimOp, !!rawState.extglob))) || p.kind === "variable" && !p.operator && !p.substring && !p.length && !p.indirect && !p.prefixNames && !p.specialParameter && !p.transform && (getArraySelector(p) === void 0 || p.name === "BASH_REMATCH" && getArraySelector(p)?.kind === "element" && /^[0-9]{1,2}$/.test(getArraySelector(p).index.decimal)) && isShellIdentifier(p.name)))) {
79048
- return false;
79049
- }
79050
- } else if (part.operator === "/" || part.operator === "//" || part.operator === "/#" || part.operator === "/%") {
79051
- if (!part.alternate || part.alternate.parts.length === 0) return false;
79052
- const q0 = part.alternate.parts[0].quoted;
79053
- const repOp = part.operator;
79054
- if (!part.alternate.parts.every((p) => p.quoted === q0 && (p.kind === "text" && !p.byteValue && (p.quoted || !hasGlobOrEscape(p.value, !!rawState.extglob) || part.alternate.parts.length === 1 && Boolean(tryCompileFixedGlobToRegex(p.value, repOp, !!rawState.extglob))) || p.kind === "variable" && !p.operator && !p.substring && !p.length && !p.indirect && !p.prefixNames && !p.specialParameter && !p.transform && getArraySelector(p) === void 0 && isShellIdentifier(p.name)))) {
79055
- return false;
79056
- }
79057
- if (part.replacement && part.replacement.parts.length > 0) {
79058
- if (!part.replacement.parts.every((p) => p.kind === "text" && !p.byteValue && !p.value.includes("&") && !p.value.includes("\\") && (p.quoted || !p.value.startsWith("~")) || p.kind === "variable" && !p.operator && !p.substring && !p.length && !p.indirect && !p.prefixNames && !p.specialParameter && !p.transform && getArraySelector(p) === void 0 && isShellIdentifier(p.name))) {
79059
- return false;
79060
- }
79061
- }
79062
79043
  } else return false;
79063
79044
  }
79064
79045
  continue;
@@ -79127,11 +79108,7 @@ var Runtime = class _Runtime {
79127
79108
  if (w0Plain === "printf") {
79128
79109
  if (!this.isSyncPrintfCallOk(cmd, 1, rawState, this._activePrintfInductionName)) return false;
79129
79110
  } else if (w0Plain === "echo") {
79130
- const firstArg = cmd.words[1];
79131
- if (firstArg) {
79132
- const prefix3 = firstArg.parts.find((part2) => part2.kind !== "text" || part2.value.length > 0);
79133
- if (prefix3?.kind !== "text" || prefix3.value.startsWith("-")) return false;
79134
- }
79111
+ if (!this.isSyncSubEchoCallOk(cmd, rawState)) return false;
79135
79112
  } else if (w0Plain === "dirname" || w0Plain === "basename") {
79136
79113
  const simArgs = [];
79137
79114
  let seenDoubleDash = false;
@@ -79291,6 +79268,7 @@ var Runtime = class _Runtime {
79291
79268
  canSyncNestedForWords(words, rawState, io, line) {
79292
79269
  if (words === void 0) return true;
79293
79270
  if (words.length === 0 || words.length > 64) return false;
79271
+ if (words.some((w) => w.parts.some((p) => p.kind === "variable" && (p.substring || p.operator !== void 0 || p.transform)))) return false;
79294
79272
  if (words.length === 1 && this.isQuotedAtWord(words[0])) return true;
79295
79273
  if (words.length === 1) {
79296
79274
  const w0 = words[0];
@@ -79321,8 +79299,9 @@ var Runtime = class _Runtime {
79321
79299
  }
79322
79300
  if (words.length === 1) {
79323
79301
  const w0 = words[0];
79324
- const arrMembers = this.tryExpandSyncArrayMembersWord(w0, rawState, io, line);
79325
- if (arrMembers !== void 0) return arrMembers;
79302
+ if (this.canSyncArrayMembersWord(w0, rawState)) {
79303
+ return this.tryExpandSyncArrayMembersWord(w0, rawState, io, line);
79304
+ }
79326
79305
  const p02 = w0.parts.length === 1 ? w0.parts[0] : w0.parts.length === 2 && w0.parts[0].kind === "text" && w0.parts[0].value === "" ? w0.parts[1] : void 0;
79327
79306
  if (p02 && !p02.quoted && (rawState.variables.IFS === void 0 || rawState.variables.IFS === " \n") && !rawState.noglob) {
79328
79307
  if (p02.kind === "variable" && !p02.indirect && !p02.prefixNames && !p02.length && !p02.transform && getArraySelector(p02) === void 0) {
@@ -79497,6 +79476,35 @@ var Runtime = class _Runtime {
79497
79476
  }
79498
79477
  return { ifsWord, varNames, isBareReply, inputWord: r0.target, ...delimChar !== void 0 ? { delimChar } : {}, ...maxChars !== void 0 ? { maxChars } : {}, ...exactChars !== void 0 ? { exactChars } : {} };
79499
79478
  }
79479
+ canSyncLoopConditionalOperands(expression, rawState, allowRegex = true) {
79480
+ const checkLeaf = (e) => {
79481
+ if (e.kind === "nonempty") return this.isPureSyncValueWord(e.operand, rawState);
79482
+ if (e.kind === "unary") return (e.operator === "-n" || e.operator === "-z" || e.operator === "-v" || syncConditionalFileUnaryOps.has(e.operator) && this._isMemoryBackingFs && this.canFastMemoryRedirect && this.budget.limits.maxFileSystemOperations >= 1e3 && (this._fileWrites === void 0 || this._fileWrites.size === 0) && (this._outputFiles === void 0 || this._outputFiles.size === 0)) && this.isPureSyncValueWord(e.operand, rawState);
79483
+ if (e.kind === "binary") {
79484
+ if (e.operator === "=~") {
79485
+ if (!allowRegex) return false;
79486
+ const patInfo = this.extractSimpleErePattern(e.right, rawState);
79487
+ if (patInfo?.isVar) return false;
79488
+ return this.isPureSyncValueWord(e.left, rawState) && this.getFastAnchoredEreRegex(e.right, rawState) !== void 0 && !rawState.readonlyVariables?.has("BASH_REMATCH") && !rawState.exported.has("BASH_REMATCH") && !stateMonitor(rawState)?.hasOverlay("BASH_REMATCH") && !(stateMonitor(rawState)?.store ?? requireArrays(rawState))?.watches.has("BASH_REMATCH");
79489
+ }
79490
+ return this.isPureSyncValueWord(e.left, rawState) && this.isPureSyncValueWord(e.right, rawState) && (!(e.operator === "==" || e.operator === "=" || e.operator === "!=") || e.right.parts.length > 0 && e.right.parts.every((p) => p.quoted));
79491
+ }
79492
+ if (e.kind === "not") return checkLeaf(e.operand);
79493
+ if (e.kind === "and" || e.kind === "or") return checkLeaf(e.left) && checkLeaf(e.right);
79494
+ return false;
79495
+ };
79496
+ return checkLeaf(expression);
79497
+ }
79498
+ isSyncSubEchoCallOk(cmd, rawState) {
79499
+ if (!this.isSyncEchoCallOk(cmd, rawState)) return false;
79500
+ if (!cmd.words.slice(1).every((w) => w.parts.length > 0 && w.parts.every(
79501
+ (part) => part.quoted || part.kind === "text" && part.value.length > 0 && !part.value.includes(" ") && !part.value.includes(" ") && !part.value.includes("\n") && !part.value.includes("{") && !hasGlobOrEscape(part.value, true)
79502
+ ))) return false;
79503
+ const firstArg = cmd.words[1];
79504
+ if (!firstArg) return true;
79505
+ const prefix3 = firstArg.parts.find((part) => part.kind !== "text" || part.value.length > 0);
79506
+ return prefix3?.kind === "text" && !prefix3.value.startsWith("-");
79507
+ }
79500
79508
  isPureSyncSubIfOrCase(cmd, rawState) {
79501
79509
  if (cmd.redirects.length !== 0) return false;
79502
79510
  const isSimpleEchoOrPrintf = (s) => {
@@ -79506,7 +79514,7 @@ var Runtime = class _Runtime {
79506
79514
  const sc = pl.commands[0];
79507
79515
  if (sc.kind !== "simple" || sc.redirects.length !== 0 || sc.words.length === 0) return false;
79508
79516
  const w0 = sc.words[0]?.plain;
79509
- if (w0 === "echo") return this.isSyncEchoCallOk(sc, rawState);
79517
+ if (w0 === "echo") return this.isSyncSubEchoCallOk(sc, rawState);
79510
79518
  if (w0 === "printf" && sc.words[1]?.plain !== "-v") return this.isSyncPrintfCallOk(sc, 1, rawState);
79511
79519
  return false;
79512
79520
  };
@@ -79520,9 +79528,10 @@ var Runtime = class _Runtime {
79520
79528
  if (bc.redirects.length !== 0 || bc.expression.error || bc.expression.hasMutation || bc.expression.hasSubscript || !isSafeSmiProgram(bc.expression) || !this.canSyncArithmeticWithoutFault(bc.expression.tree, rawState, bc.line ?? 1)) return false;
79521
79529
  } else if (bc.kind === "conditional") {
79522
79530
  if (bc.redirects.length !== 0 || rawState.nocasematch || byteLocale(rawState.variables) || !this.canSyncConditional(bc.expression, 0, rawState, true)) return false;
79523
- if (bc.expression.kind === "binary" && bc.expression.operator === "=~") return false;
79524
- } else if (!this.extractPosixBracketCondExpr(bc, rawState, true, br.body)) {
79525
- return false;
79531
+ if (!this.canSyncLoopConditionalOperands(bc.expression, rawState, false)) return false;
79532
+ } else {
79533
+ const expression = this.extractPosixBracketCondExpr(bc, rawState, true, br.body);
79534
+ if (!expression || !this.canSyncLoopConditionalOperands(expression, rawState, false)) return false;
79526
79535
  }
79527
79536
  if (!isSimpleEchoOrPrintf(br.body)) return false;
79528
79537
  }
@@ -79550,22 +79559,7 @@ var Runtime = class _Runtime {
79550
79559
  const cmd = pipeline.commands[0];
79551
79560
  const canSyncLoopCondCmd = (c) => {
79552
79561
  if (c.redirects.length !== 0 || rawState.nocasematch || byteLocale(rawState.variables) || !this.canSyncConditional(c.expression, 0, rawState, true)) return false;
79553
- const checkLeaf = (e) => {
79554
- if (e.kind === "nonempty") return this.isPureSyncValueWord(e.operand, rawState);
79555
- if (e.kind === "unary") return (e.operator === "-n" || e.operator === "-z" || e.operator === "-v" || syncConditionalFileUnaryOps.has(e.operator) && this._isMemoryBackingFs && this.canFastMemoryRedirect && this.budget.limits.maxFileSystemOperations >= 1e3 && (this._fileWrites === void 0 || this._fileWrites.size === 0) && (this._outputFiles === void 0 || this._outputFiles.size === 0)) && this.isPureSyncValueWord(e.operand, rawState);
79556
- if (e.kind === "binary") {
79557
- if (e.operator === "=~") {
79558
- const patInfo = this.extractSimpleErePattern(e.right, rawState);
79559
- if (patInfo?.isVar) return false;
79560
- return this.isPureSyncValueWord(e.left, rawState) && this.getFastAnchoredEreRegex(e.right, rawState) !== void 0 && !rawState.readonlyVariables?.has("BASH_REMATCH") && !rawState.exported.has("BASH_REMATCH") && !stateMonitor(rawState)?.hasOverlay("BASH_REMATCH") && !(store ?? requireArrays(rawState))?.watches.has("BASH_REMATCH");
79561
- }
79562
- return this.isPureSyncValueWord(e.left, rawState) && this.isPureSyncValueWord(e.right, rawState) && (!(e.operator === "==" || e.operator === "=" || e.operator === "!=") || e.right.parts.length > 0 && e.right.parts.every((p2) => p2.quoted));
79563
- }
79564
- if (e.kind === "not") return checkLeaf(e.operand);
79565
- if (e.kind === "and" || e.kind === "or") return checkLeaf(e.left) && checkLeaf(e.right);
79566
- return false;
79567
- };
79568
- return checkLeaf(c.expression);
79562
+ return this.canSyncLoopConditionalOperands(c.expression, rawState);
79569
79563
  };
79570
79564
  if (cmd.kind === "arithmetic") {
79571
79565
  if (cmd.redirects.length === 0) {
@@ -79797,7 +79791,7 @@ var Runtime = class _Runtime {
79797
79791
  return false;
79798
79792
  }
79799
79793
  const assignment = this.assignment(w0);
79800
- if (assignment && (assignment.name === "LC_ALL" || assignment.name === "LC_CTYPE" || assignment.name === "LC_COLLATE" || assignment.name === "LANG")) return false;
79794
+ if (assignment && (assignment.name === "IFS" || assignment.name === "LC_ALL" || assignment.name === "LC_CTYPE" || assignment.name === "LC_COLLATE" || assignment.name === "LANG")) return false;
79801
79795
  if (!assignment || assignment.append && this.budget.limits.maxExpansionBytes !== Infinity || assignment.name === "OPTIND" || assignment.name === "PIPESTATUS" || assignment.name.includes("[") || rawState.readonlyVariables?.has(assignment.name) || rawState.variableAttributes?.get(assignment.name) || store?.get(assignment.name) || !this.isPureSyncValueWord(assignment.value, rawState)) {
79802
79796
  return false;
79803
79797
  }
@@ -80695,7 +80689,7 @@ var Runtime = class _Runtime {
80695
80689
  if (rawState.allexport) monitor.proxy.exported.add(varName);
80696
80690
  } else {
80697
80691
  monitor.values.invalidate(varName);
80698
- monitor.proxy.exported.delete(varName);
80692
+ if (!rawState.exported.has(varName)) monitor.proxy.exported.delete(varName);
80699
80693
  }
80700
80694
  }
80701
80695
  }
@@ -81034,7 +81028,7 @@ var Runtime = class _Runtime {
81034
81028
  if (rawState.allexport) monitor.proxy.exported.add(varName);
81035
81029
  } else {
81036
81030
  monitor.values.invalidate(varName);
81037
- monitor.proxy.exported.delete(varName);
81031
+ if (!rawState.exported.has(varName)) monitor.proxy.exported.delete(varName);
81038
81032
  }
81039
81033
  }
81040
81034
  }
@@ -84167,7 +84161,7 @@ ${s1}
84167
84161
  (0, diagnostics_exports.publicDiagnosticMessage)(failure, this.budget.onInternalError);
84168
84162
  }
84169
84163
  }
84170
- if (error instanceof ExpansionFailure && !(error instanceof SubstringRangeFailure && state.loopDepth > 0) || error instanceof BraceExpansionFailure) throw completedExit(error instanceof ParameterExpansionFailure && !state.isolated ? 127 : 1);
84164
+ if (error instanceof ExpansionFailure || error instanceof BraceExpansionFailure) throw completedExit(error instanceof ParameterExpansionFailure && !state.isolated ? 127 : 1);
84171
84165
  if (error instanceof FatalCommandFailure) throw completedExit(error.status);
84172
84166
  if (error instanceof DiscardCommandFailure) throw completedExit(error.status, "discard");
84173
84167
  const status2 = outputTracker.outputStatus ?? (error instanceof CommandFailure2 ? error.status : 1);
@@ -89251,7 +89245,7 @@ ${prefix3} line ${offset + line}: \`${source2.split("\n")[line - 1] ?? ""}'
89251
89245
  if (expression.length) {
89252
89246
  const length = await arithmetic2(expression.length);
89253
89247
  end = length.value < 0n ? size + length.value : offset + length.value;
89254
- if (end < offset) throw new SubstringRangeFailure(`${length.source}: substring expression < 0`, line);
89248
+ if (end < offset) throw new ExpansionFailure(`${length.source}: substring expression < 0`, line);
89255
89249
  if (end > size) end = size;
89256
89250
  }
89257
89251
  this.signal.throwIfAborted();
@@ -90236,6 +90230,11 @@ ${prefix3} line ${offset + line}: \`${source2.split("\n")[line - 1] ?? ""}'
90236
90230
  return out;
90237
90231
  }
90238
90232
  evalSyncTr(input3, opArgs) {
90233
+ for (const value2 of opArgs) {
90234
+ for (let i = 0; i < value2.length; i++) {
90235
+ if (value2.charCodeAt(i) >= 128) return void 0;
90236
+ }
90237
+ }
90239
90238
  let idx = 0;
90240
90239
  let flagC = false;
90241
90240
  let flagD = false;
@@ -91863,7 +91862,7 @@ ${prefix3} line ${offset + line}: \`${source2.split("\n")[line - 1] ?? ""}'
91863
91862
  const out = [];
91864
91863
  let curStyle = bodyStyle;
91865
91864
  let num = start;
91866
- const unnumberedPrefix = " ".repeat(width + sep.length);
91865
+ const unnumberedPrefix = " ".repeat(width + shellValueByteLength(sep));
91867
91866
  for (let i = 0; i < rawLines.length; i++) {
91868
91867
  const l = rawLines[i];
91869
91868
  if (l === "\\:" || l === "\\:\\:" || l === "\\:\\:\\:") {
@@ -92001,7 +92000,9 @@ ${prefix3} line ${offset + line}: \`${source2.split("\n")[line - 1] ?? ""}'
92001
92000
  const units = "KMGTPEZY";
92002
92001
  const out = [];
92003
92002
  for (let i = 0; i < rawLines.length; i++) {
92004
- const raw = rawLines[i].trim();
92003
+ const line = rawLines[i];
92004
+ const raw = line.trim();
92005
+ if (line !== raw) return void 0;
92005
92006
  if (raw.length === 0) {
92006
92007
  out.push("");
92007
92008
  continue;
@@ -92269,7 +92270,7 @@ ${prefix3} line ${offset + line}: \`${source2.split("\n")[line - 1] ?? ""}'
92269
92270
  if (arg === "-L" || arg === "--max-line-length") return "-L";
92270
92271
  return void 0;
92271
92272
  }
92272
- evalSyncUniq(rawLines, opArgs) {
92273
+ evalSyncUniq(rawLines, opArgs, isByteLocale = false) {
92273
92274
  let ignoreCase = false;
92274
92275
  let hasCount = false;
92275
92276
  let onlyRepeated = false;
@@ -92308,6 +92309,18 @@ ${prefix3} line ${offset + line}: \`${source2.split("\n")[line - 1] ?? ""}'
92308
92309
  return void 0;
92309
92310
  }
92310
92311
  }
92312
+ if (isByteLocale && (skipChars !== 0 || checkChars !== Infinity) && rawLines.some((line) => {
92313
+ for (let i = 0; i < line.length; i++) if (line.charCodeAt(i) > 127) return true;
92314
+ return false;
92315
+ })) return void 0;
92316
+ const foldAscii = (value2) => {
92317
+ let folded = "";
92318
+ for (let i = 0; i < value2.length; i++) {
92319
+ const code = value2.charCodeAt(i);
92320
+ folded += code >= 97 && code <= 122 ? String.fromCharCode(code - 32) : value2[i];
92321
+ }
92322
+ return folded;
92323
+ };
92311
92324
  const keyOf = (l) => {
92312
92325
  let offset = 0;
92313
92326
  for (let f = 0; f < skipFields; f++) {
@@ -92316,11 +92329,11 @@ ${prefix3} line ${offset + line}: \`${source2.split("\n")[line - 1] ?? ""}'
92316
92329
  }
92317
92330
  if (skipChars === 0 && checkChars === Infinity) {
92318
92331
  const sub2 = l.slice(offset);
92319
- return ignoreCase ? sub2.toLowerCase() : sub2;
92332
+ return ignoreCase ? foldAscii(sub2) : sub2;
92320
92333
  }
92321
92334
  const chars = Array.from(l.slice(offset));
92322
92335
  const sub = (checkChars === Infinity ? chars.slice(skipChars) : chars.slice(skipChars, skipChars + checkChars)).join("");
92323
- return ignoreCase ? sub.toLowerCase() : sub;
92336
+ return ignoreCase ? foldAscii(sub) : sub;
92324
92337
  };
92325
92338
  const outLines = [];
92326
92339
  let uIdx = 0;
@@ -92664,8 +92677,8 @@ ${prefix3} line ${offset + line}: \`${source2.split("\n")[line - 1] ?? ""}'
92664
92677
  for (let k = 0; k < inStr.length; k++) if (inStr.charCodeAt(k) === 10) nl++;
92665
92678
  outLines = [String(nl)];
92666
92679
  } else if (stageArgs[0] === "-w") {
92667
- const trimmed = inStr.trim();
92668
- outLines = [String(trimmed.length === 0 ? 0 : trimmed.split(/[ \t\n\r\f\v]+/).length)];
92680
+ if (!byteLocale(rawState.variables) && prevBuf.subarray(0, prevLen).some((byte) => byte > 127)) return void 0;
92681
+ outLines = [String(inStr.split(/[ \t\n\r\f\v]+/).filter(Boolean).length)];
92669
92682
  } else if (stageArgs[0] === "-m") {
92670
92683
  outLines = [String(byteLocale(rawState.variables) ? prevLen : Array.from(inStr).length)];
92671
92684
  } else if (stageArgs[0] === "-L") {
@@ -92692,7 +92705,7 @@ ${prefix3} line ${offset + line}: \`${source2.split("\n")[line - 1] ?? ""}'
92692
92705
  } else if (isInlineSort) {
92693
92706
  outLines = this.evalSyncSort(rawLines, stageArgs, false) ?? [];
92694
92707
  } else if (isInlineUniq) {
92695
- const uniqRes = this.evalSyncUniq(rawLines, stageArgs);
92708
+ const uniqRes = this.evalSyncUniq(rawLines, stageArgs, byteLocale(rawState.variables));
92696
92709
  if (uniqRes === void 0) return void 0;
92697
92710
  outLines = uniqRes;
92698
92711
  } else if (inlineSedMatch) {
@@ -92981,14 +92994,16 @@ ${prefix3} line ${offset + line}: \`${source2.split("\n")[line - 1] ?? ""}'
92981
92994
  const sortRes = this.evalSyncSort(rawLines, opArgs, false);
92982
92995
  if (sortRes !== void 0) fileRes = renderLines(sortRes);
92983
92996
  } else if (w0Plain === "uniq") {
92984
- const uniqRes = this.evalSyncUniq(rawLines, opArgs);
92997
+ const uniqRes = this.evalSyncUniq(rawLines, opArgs, byteLocale(rawState.variables));
92985
92998
  if (uniqRes !== void 0) fileRes = renderLines(uniqRes);
92986
92999
  } else if (w0Plain === "tr" && (hasSingleStdinRedir || hasSingleHereStringRedir)) {
92987
93000
  fileRes = this.evalSyncTr(fileStr, opArgs);
92988
93001
  } else if (hasSingleHereStringRedir && w0Plain === "base64" && (opArgs.length === 0 || opArgs.length === 1 && (opArgs[0] === "-d" || opArgs[0] === "--decode"))) {
92989
93002
  if (opArgs.length === 0) fileRes = this.syncBase64Encode(view) + "\n";
92990
93003
  else {
92991
- const decoded = this.syncBase64DecodeBytes(fileStr);
93004
+ const cleaned = fileStr.replace(/[ \t\r\n]+/g, "");
93005
+ if (cleaned.length % 4 !== 0 || cleaned.length > 0 && !/^[A-Za-z0-9+/]+={0,2}$/.test(cleaned)) return void 0;
93006
+ const decoded = this.syncBase64DecodeBytes(cleaned);
92992
93007
  if (decoded.some((byte) => byte === 0 || byte >= 128)) return void 0;
92993
93008
  fileRes = sharedSyncPipeDecoder.decode(decoded);
92994
93009
  }
@@ -514,6 +514,8 @@ export declare class Runtime {
514
514
  private evalSyncNestedForWords;
515
515
  private isSyncEchoCallOk;
516
516
  private extractSyncReadHereStringSpec;
517
+ private canSyncLoopConditionalOperands;
518
+ private isSyncSubEchoCallOk;
517
519
  private isPureSyncSubIfOrCase;
518
520
  private canSyncLoopBody;
519
521
  private extractIntLoopStep;
@@ -1245,8 +1245,6 @@ class DiscardCommandFailure extends CommandFailure {
1245
1245
  }
1246
1246
  class ParameterExpansionFailure extends ExpansionFailure {
1247
1247
  }
1248
- class SubstringRangeFailure extends ExpansionFailure {
1249
- }
1250
1248
  class NounsetFailure extends ExpansionFailure {
1251
1249
  status;
1252
1250
  constructor(message, line, status = 1) {
@@ -13675,21 +13673,17 @@ export class Runtime {
13675
13673
  // their alternates rather than risking corruption or replay.
13676
13674
  return false;
13677
13675
  }
13678
- if (part.length || part.substring || part.operator !== undefined) {
13676
+ // Substrings and pattern/case operators can decline as loop values
13677
+ // change to Unicode or lengths become invalid. Loop steps cannot resume
13678
+ // after that decline without corrupting assignments or replaying effects.
13679
+ if (part.substring)
13680
+ return false;
13681
+ if (part.length || part.operator !== undefined) {
13679
13682
  if (this.budget.limits.maxExpansionBytes !== Infinity || rawState.depth >= 32 || rawState.nounset || rawState.nocasematch || byteLocale(rawState.variables) !== false) {
13680
13683
  return false;
13681
13684
  }
13682
13685
  if (part.length) {
13683
- if (part.operator !== undefined || part.substring)
13684
- return false;
13685
- }
13686
- else if (part.substring) {
13687
- if (part.operator !== undefined || !part.substring.offset.parts.every(p => p.kind === "text" || (p.kind === "variable" && !p.operator && !p.substring && !p.length)) || (part.substring.length && !part.substring.length.parts.every(p => p.kind === "text" || (p.kind === "variable" && !p.operator && !p.substring && !p.length)))) {
13688
- return false;
13689
- }
13690
- }
13691
- else if (part.operator === "^" || part.operator === "^^" || part.operator === "," || part.operator === ",,") {
13692
- if (part.alternate && part.alternate.parts.length > 0)
13686
+ if (part.operator !== undefined)
13693
13687
  return false;
13694
13688
  }
13695
13689
  else if (part.operator === "-" || part.operator === ":-" || part.operator === "+" || part.operator === ":+") {
@@ -13698,29 +13692,6 @@ export class Runtime {
13698
13692
  if (!this.isPureSyncValueWord(part.alternate, rawState, allowAt))
13699
13693
  return false;
13700
13694
  }
13701
- else if (part.operator === "#" || part.operator === "##" || part.operator === "%" || part.operator === "%%") {
13702
- if (!part.alternate || part.alternate.parts.length === 0)
13703
- return false;
13704
- const q0 = part.alternate.parts[0].quoted;
13705
- const trimOp = part.operator;
13706
- if (!part.alternate.parts.every((p, idx) => p.quoted === q0 && ((p.kind === "text" && !p.byteValue && (p.quoted || !hasGlobOrEscape(p.value, !!rawState.extglob) || (idx === 0 && (trimOp === "#" || trimOp === "##") && p.value === "*") || (idx === part.alternate.parts.length - 1 && (trimOp === "%" || trimOp === "%%") && p.value === "*") || (part.alternate.parts.length === 1 && Boolean(tryCompileTrimGlobToRegex(p.value, trimOp, !!rawState.extglob))))) || (p.kind === "variable" && !p.operator && !p.substring && !p.length && !p.indirect && !p.prefixNames && !p.specialParameter && !p.transform && (getArraySelector(p) === undefined || (p.name === "BASH_REMATCH" && getArraySelector(p)?.kind === "element" && /^[0-9]{1,2}$/.test(getArraySelector(p).index.decimal))) && isShellIdentifier(p.name))))) {
13707
- return false;
13708
- }
13709
- }
13710
- else if (part.operator === "/" || part.operator === "//" || part.operator === "/#" || part.operator === "/%") {
13711
- if (!part.alternate || part.alternate.parts.length === 0)
13712
- return false;
13713
- const q0 = part.alternate.parts[0].quoted;
13714
- const repOp = part.operator;
13715
- if (!part.alternate.parts.every(p => p.quoted === q0 && ((p.kind === "text" && !p.byteValue && (p.quoted || !hasGlobOrEscape(p.value, !!rawState.extglob) || (part.alternate.parts.length === 1 && Boolean(tryCompileFixedGlobToRegex(p.value, repOp, !!rawState.extglob))))) || (p.kind === "variable" && !p.operator && !p.substring && !p.length && !p.indirect && !p.prefixNames && !p.specialParameter && !p.transform && getArraySelector(p) === undefined && isShellIdentifier(p.name))))) {
13716
- return false;
13717
- }
13718
- if (part.replacement && part.replacement.parts.length > 0) {
13719
- if (!part.replacement.parts.every(p => ((p.kind === "text" && !p.byteValue && !p.value.includes("&") && !p.value.includes("\\") && (p.quoted || !p.value.startsWith("~"))) || (p.kind === "variable" && !p.operator && !p.substring && !p.length && !p.indirect && !p.prefixNames && !p.specialParameter && !p.transform && getArraySelector(p) === undefined && isShellIdentifier(p.name))))) {
13720
- return false;
13721
- }
13722
- }
13723
- }
13724
13695
  else
13725
13696
  return false;
13726
13697
  }
@@ -13830,14 +13801,8 @@ export class Runtime {
13830
13801
  return false;
13831
13802
  }
13832
13803
  else if (w0Plain === "echo") {
13833
- const firstArg = cmd.words[1];
13834
- if (firstArg) {
13835
- const prefix = firstArg.parts.find(part => part.kind !== "text" || part.value.length > 0);
13836
- // Substitution execution rejects options, including those that appear
13837
- // only on later iterations. Require a stable non-option prefix.
13838
- if (prefix?.kind !== "text" || prefix.value.startsWith("-"))
13839
- return false;
13840
- }
13804
+ if (!this.isSyncSubEchoCallOk(cmd, rawState))
13805
+ return false;
13841
13806
  }
13842
13807
  else if (w0Plain === "dirname" || w0Plain === "basename") {
13843
13808
  const simArgs = [];
@@ -14056,6 +14021,10 @@ export class Runtime {
14056
14021
  return true;
14057
14022
  if (words.length === 0 || words.length > 64)
14058
14023
  return false;
14024
+ // Slice expressions and member transformations may stop being synchronous
14025
+ // after body mutations. Preserve member expansion through the normal path.
14026
+ if (words.some(w => w.parts.some(p => p.kind === "variable" && (p.substring || p.operator !== undefined || p.transform))))
14027
+ return false;
14059
14028
  if (words.length === 1 && this.isQuotedAtWord(words[0]))
14060
14029
  return true;
14061
14030
  // Unquoted expansions need splitting and globbing against the state at
@@ -14097,9 +14066,10 @@ export class Runtime {
14097
14066
  }
14098
14067
  if (words.length === 1) {
14099
14068
  const w0 = words[0];
14100
- const arrMembers = this.tryExpandSyncArrayMembersWord(w0, rawState, io, line);
14101
- if (arrMembers !== undefined)
14102
- return arrMembers;
14069
+ if (this.canSyncArrayMembersWord(w0, rawState)) {
14070
+ // Member words must never degrade into a single joined scalar word.
14071
+ return this.tryExpandSyncArrayMembersWord(w0, rawState, io, line);
14072
+ }
14103
14073
  const p0 = w0.parts.length === 1 ? w0.parts[0] : (w0.parts.length === 2 && w0.parts[0].kind === "text" && w0.parts[0].value === "" ? w0.parts[1] : undefined);
14104
14074
  if (p0 && !p0.quoted && (rawState.variables.IFS === undefined || rawState.variables.IFS === " \t\n") && !rawState.noglob) {
14105
14075
  if (p0.kind === "variable" && !p0.indirect && !p0.prefixNames && !p0.length && !p0.transform && getArraySelector(p0) === undefined) {
@@ -14329,6 +14299,45 @@ export class Runtime {
14329
14299
  }
14330
14300
  return { ifsWord, varNames, isBareReply, inputWord: r0.target, ...(delimChar !== undefined ? { delimChar } : {}), ...(maxChars !== undefined ? { maxChars } : {}), ...(exactChars !== undefined ? { exactChars } : {}) };
14331
14301
  }
14302
+ canSyncLoopConditionalOperands(expression, rawState, allowRegex = true) {
14303
+ const checkLeaf = (e) => {
14304
+ if (e.kind === "nonempty")
14305
+ return this.isPureSyncValueWord(e.operand, rawState);
14306
+ if (e.kind === "unary")
14307
+ return (e.operator === "-n" || e.operator === "-z" || e.operator === "-v" || (syncConditionalFileUnaryOps.has(e.operator) && this._isMemoryBackingFs && this.canFastMemoryRedirect && this.budget.limits.maxFileSystemOperations >= 1000 && (this._fileWrites === undefined || this._fileWrites.size === 0) && (this._outputFiles === undefined || this._outputFiles.size === 0))) && this.isPureSyncValueWord(e.operand, rawState);
14308
+ if (e.kind === "binary") {
14309
+ if (e.operator === "=~") {
14310
+ if (!allowRegex)
14311
+ return false;
14312
+ const patInfo = this.extractSimpleErePattern(e.right, rawState);
14313
+ // A variable pattern can change through expansions or called functions.
14314
+ if (patInfo?.isVar)
14315
+ return false;
14316
+ return this.isPureSyncValueWord(e.left, rawState) && this.getFastAnchoredEreRegex(e.right, rawState) !== undefined && !rawState.readonlyVariables?.has("BASH_REMATCH") && !rawState.exported.has("BASH_REMATCH") && !stateMonitor(rawState)?.hasOverlay("BASH_REMATCH") && !(stateMonitor(rawState)?.store ?? requireArrays(rawState))?.watches.has("BASH_REMATCH");
14317
+ }
14318
+ // A synchronous loop cannot hand off an unsupported pattern mid-iteration.
14319
+ return this.isPureSyncValueWord(e.left, rawState) && this.isPureSyncValueWord(e.right, rawState) && (!(e.operator === "==" || e.operator === "=" || e.operator === "!=") || (e.right.parts.length > 0 && e.right.parts.every(p => p.quoted)));
14320
+ }
14321
+ if (e.kind === "not")
14322
+ return checkLeaf(e.operand);
14323
+ if (e.kind === "and" || e.kind === "or")
14324
+ return checkLeaf(e.left) && checkLeaf(e.right);
14325
+ return false;
14326
+ };
14327
+ return checkLeaf(expression);
14328
+ }
14329
+ isSyncSubEchoCallOk(cmd, rawState) {
14330
+ if (!this.isSyncEchoCallOk(cmd, rawState))
14331
+ return false;
14332
+ if (!cmd.words.slice(1).every(w => w.parts.length > 0 && w.parts.every(part => part.quoted || (part.kind === "text" && part.value.length > 0 && !part.value.includes(" ") && !part.value.includes("\t") && !part.value.includes("\n") && !part.value.includes("{") && !hasGlobOrEscape(part.value, true)))))
14333
+ return false;
14334
+ const firstArg = cmd.words[1];
14335
+ if (!firstArg)
14336
+ return true;
14337
+ const prefix = firstArg.parts.find(part => part.kind !== "text" || part.value.length > 0);
14338
+ // Substitution execution cannot resume when an option appears mid-loop.
14339
+ return prefix?.kind === "text" && !prefix.value.startsWith("-");
14340
+ }
14332
14341
  isPureSyncSubIfOrCase(cmd, rawState) {
14333
14342
  if (cmd.redirects.length !== 0)
14334
14343
  return false;
@@ -14343,7 +14352,7 @@ export class Runtime {
14343
14352
  return false;
14344
14353
  const w0 = sc.words[0]?.plain;
14345
14354
  if (w0 === "echo")
14346
- return this.isSyncEchoCallOk(sc, rawState);
14355
+ return this.isSyncSubEchoCallOk(sc, rawState);
14347
14356
  if (w0 === "printf" && sc.words[1]?.plain !== "-v")
14348
14357
  return this.isSyncPrintfCallOk(sc, 1, rawState);
14349
14358
  return false;
@@ -14363,11 +14372,13 @@ export class Runtime {
14363
14372
  else if (bc.kind === "conditional") {
14364
14373
  if (bc.redirects.length !== 0 || rawState.nocasematch || byteLocale(rawState.variables) || !this.canSyncConditional(bc.expression, 0, rawState, true))
14365
14374
  return false;
14366
- if (bc.expression.kind === "binary" && bc.expression.operator === "=~")
14375
+ if (!this.canSyncLoopConditionalOperands(bc.expression, rawState, false))
14367
14376
  return false;
14368
14377
  }
14369
- else if (!this.extractPosixBracketCondExpr(bc, rawState, true, br.body)) {
14370
- return false;
14378
+ else {
14379
+ const expression = this.extractPosixBracketCondExpr(bc, rawState, true, br.body);
14380
+ if (!expression || !this.canSyncLoopConditionalOperands(expression, rawState, false))
14381
+ return false;
14371
14382
  }
14372
14383
  if (!isSimpleEchoOrPrintf(br.body))
14373
14384
  return false;
@@ -14405,29 +14416,7 @@ export class Runtime {
14405
14416
  const canSyncLoopCondCmd = (c) => {
14406
14417
  if (c.redirects.length !== 0 || rawState.nocasematch || byteLocale(rawState.variables) || !this.canSyncConditional(c.expression, 0, rawState, true))
14407
14418
  return false;
14408
- const checkLeaf = (e) => {
14409
- if (e.kind === "nonempty")
14410
- return this.isPureSyncValueWord(e.operand, rawState);
14411
- if (e.kind === "unary")
14412
- return (e.operator === "-n" || e.operator === "-z" || e.operator === "-v" || (syncConditionalFileUnaryOps.has(e.operator) && this._isMemoryBackingFs && this.canFastMemoryRedirect && this.budget.limits.maxFileSystemOperations >= 1000 && (this._fileWrites === undefined || this._fileWrites.size === 0) && (this._outputFiles === undefined || this._outputFiles.size === 0))) && this.isPureSyncValueWord(e.operand, rawState);
14413
- if (e.kind === "binary") {
14414
- if (e.operator === "=~") {
14415
- const patInfo = this.extractSimpleErePattern(e.right, rawState);
14416
- // A variable pattern can change through expansions or called functions.
14417
- if (patInfo?.isVar)
14418
- return false;
14419
- return this.isPureSyncValueWord(e.left, rawState) && this.getFastAnchoredEreRegex(e.right, rawState) !== undefined && !rawState.readonlyVariables?.has("BASH_REMATCH") && !rawState.exported.has("BASH_REMATCH") && !stateMonitor(rawState)?.hasOverlay("BASH_REMATCH") && !(store ?? requireArrays(rawState))?.watches.has("BASH_REMATCH");
14420
- }
14421
- // A synchronous loop cannot hand off an unsupported pattern mid-iteration.
14422
- return this.isPureSyncValueWord(e.left, rawState) && this.isPureSyncValueWord(e.right, rawState) && (!(e.operator === "==" || e.operator === "=" || e.operator === "!=") || (e.right.parts.length > 0 && e.right.parts.every(p => p.quoted)));
14423
- }
14424
- if (e.kind === "not")
14425
- return checkLeaf(e.operand);
14426
- if (e.kind === "and" || e.kind === "or")
14427
- return checkLeaf(e.left) && checkLeaf(e.right);
14428
- return false;
14429
- };
14430
- return checkLeaf(c.expression);
14419
+ return this.canSyncLoopConditionalOperands(c.expression, rawState);
14431
14420
  };
14432
14421
  if (cmd.kind === "arithmetic") {
14433
14422
  if (cmd.redirects.length === 0) {
@@ -14749,9 +14738,9 @@ export class Runtime {
14749
14738
  return false;
14750
14739
  }
14751
14740
  const assignment = this.assignment(w0);
14752
- // Eligibility is checked against the entry locale. Changing it can make
14753
- // otherwise pure expansions require the asynchronous byte-aware path.
14754
- if (assignment && (assignment.name === "LC_ALL" || assignment.name === "LC_CTYPE" || assignment.name === "LC_COLLATE" || assignment.name === "LANG"))
14741
+ // Eligibility uses the entry locale and IFS. Mutations can require
14742
+ // field splitting or byte-aware expansion during a later iteration.
14743
+ if (assignment && (assignment.name === "IFS" || assignment.name === "LC_ALL" || assignment.name === "LC_CTYPE" || assignment.name === "LC_COLLATE" || assignment.name === "LANG"))
14755
14744
  return false;
14756
14745
  if (!assignment || (assignment.append && this.budget.limits.maxExpansionBytes !== Infinity) || assignment.name === "OPTIND" || assignment.name === "PIPESTATUS" || assignment.name.includes("[") || rawState.readonlyVariables?.has(assignment.name) || rawState.variableAttributes?.get(assignment.name) || store?.get(assignment.name) || !this.isPureSyncValueWord(assignment.value, rawState)) {
14757
14746
  return false;
@@ -15732,7 +15721,8 @@ export class Runtime {
15732
15721
  }
15733
15722
  else {
15734
15723
  monitor.values.invalidate(varName);
15735
- monitor.proxy.exported.delete(varName);
15724
+ if (!rawState.exported.has(varName))
15725
+ monitor.proxy.exported.delete(varName);
15736
15726
  }
15737
15727
  }
15738
15728
  }
@@ -16173,7 +16163,8 @@ export class Runtime {
16173
16163
  }
16174
16164
  else {
16175
16165
  monitor.values.invalidate(varName);
16176
- monitor.proxy.exported.delete(varName);
16166
+ if (!rawState.exported.has(varName))
16167
+ monitor.proxy.exported.delete(varName);
16177
16168
  }
16178
16169
  }
16179
16170
  }
@@ -19784,7 +19775,7 @@ export class Runtime {
19784
19775
  publicDiagnosticMessage(failure, this.budget.onInternalError);
19785
19776
  }
19786
19777
  }
19787
- if ((error instanceof ExpansionFailure && !(error instanceof SubstringRangeFailure && state.loopDepth > 0)) || error instanceof BraceExpansionFailure)
19778
+ if (error instanceof ExpansionFailure || error instanceof BraceExpansionFailure)
19788
19779
  throw completedExit(error instanceof ParameterExpansionFailure && !state.isolated ? 127 : 1);
19789
19780
  if (error instanceof FatalCommandFailure)
19790
19781
  throw completedExit(error.status);
@@ -25515,7 +25506,7 @@ export class Runtime {
25515
25506
  const length = await arithmetic(expression.length);
25516
25507
  end = length.value < 0n ? size + length.value : offset + length.value;
25517
25508
  if (end < offset)
25518
- throw new SubstringRangeFailure(`${length.source}: substring expression < 0`, line);
25509
+ throw new ExpansionFailure(`${length.source}: substring expression < 0`, line);
25519
25510
  if (end > size)
25520
25511
  end = size;
25521
25512
  }
@@ -26822,6 +26813,13 @@ export class Runtime {
26822
26813
  return out;
26823
26814
  }
26824
26815
  evalSyncTr(input, opArgs) {
26816
+ // Non-ASCII sets require the byte parser; reject them before running producers.
26817
+ for (const value of opArgs) {
26818
+ for (let i = 0; i < value.length; i++) {
26819
+ if (value.charCodeAt(i) >= 128)
26820
+ return undefined;
26821
+ }
26822
+ }
26825
26823
  let idx = 0;
26826
26824
  let flagC = false;
26827
26825
  let flagD = false;
@@ -28862,7 +28860,7 @@ export class Runtime {
28862
28860
  const out = [];
28863
28861
  let curStyle = bodyStyle;
28864
28862
  let num = start;
28865
- const unnumberedPrefix = " ".repeat(width + sep.length);
28863
+ const unnumberedPrefix = " ".repeat(width + shellValueByteLength(sep));
28866
28864
  for (let i = 0; i < rawLines.length; i++) {
28867
28865
  const l = rawLines[i];
28868
28866
  if (l === "\\:" || l === "\\:\\:" || l === "\\:\\:\\:") {
@@ -29027,7 +29025,11 @@ export class Runtime {
29027
29025
  const units = "KMGTPEZY";
29028
29026
  const out = [];
29029
29027
  for (let i = 0; i < rawLines.length; i++) {
29030
- const raw = rawLines[i].trim();
29028
+ const line = rawLines[i];
29029
+ const raw = line.trim();
29030
+ // Preserve field whitespace and padding through the command implementation.
29031
+ if (line !== raw)
29032
+ return undefined;
29031
29033
  if (raw.length === 0) {
29032
29034
  out.push("");
29033
29035
  continue;
@@ -29369,7 +29371,7 @@ export class Runtime {
29369
29371
  return "-L";
29370
29372
  return undefined;
29371
29373
  }
29372
- evalSyncUniq(rawLines, opArgs) {
29374
+ evalSyncUniq(rawLines, opArgs, isByteLocale = false) {
29373
29375
  let ignoreCase = false;
29374
29376
  let hasCount = false;
29375
29377
  let onlyRepeated = false;
@@ -29428,6 +29430,21 @@ export class Runtime {
29428
29430
  return undefined;
29429
29431
  }
29430
29432
  }
29433
+ if (isByteLocale && (skipChars !== 0 || checkChars !== Infinity) && rawLines.some(line => {
29434
+ for (let i = 0; i < line.length; i++)
29435
+ if (line.charCodeAt(i) > 127)
29436
+ return true;
29437
+ return false;
29438
+ }))
29439
+ return undefined;
29440
+ const foldAscii = (value) => {
29441
+ let folded = "";
29442
+ for (let i = 0; i < value.length; i++) {
29443
+ const code = value.charCodeAt(i);
29444
+ folded += code >= 97 && code <= 122 ? String.fromCharCode(code - 32) : value[i];
29445
+ }
29446
+ return folded;
29447
+ };
29431
29448
  const keyOf = (l) => {
29432
29449
  let offset = 0;
29433
29450
  for (let f = 0; f < skipFields; f++) {
@@ -29438,11 +29455,11 @@ export class Runtime {
29438
29455
  }
29439
29456
  if (skipChars === 0 && checkChars === Infinity) {
29440
29457
  const sub = l.slice(offset);
29441
- return ignoreCase ? sub.toLowerCase() : sub;
29458
+ return ignoreCase ? foldAscii(sub) : sub;
29442
29459
  }
29443
29460
  const chars = Array.from(l.slice(offset));
29444
29461
  const sub = (checkChars === Infinity ? chars.slice(skipChars) : chars.slice(skipChars, skipChars + checkChars)).join("");
29445
- return ignoreCase ? sub.toLowerCase() : sub;
29462
+ return ignoreCase ? foldAscii(sub) : sub;
29446
29463
  };
29447
29464
  const outLines = [];
29448
29465
  let uIdx = 0;
@@ -29919,8 +29936,9 @@ export class Runtime {
29919
29936
  outLines = [String(nl)];
29920
29937
  }
29921
29938
  else if (stageArgs[0] === "-w") {
29922
- const trimmed = inStr.trim();
29923
- outLines = [String(trimmed.length === 0 ? 0 : trimmed.split(/[ \t\n\r\f\v]+/).length)];
29939
+ if (!byteLocale(rawState.variables) && prevBuf.subarray(0, prevLen).some(byte => byte > 127))
29940
+ return undefined;
29941
+ outLines = [String(inStr.split(/[ \t\n\r\f\v]+/).filter(Boolean).length)];
29924
29942
  }
29925
29943
  else if (stageArgs[0] === "-m") {
29926
29944
  outLines = [String(byteLocale(rawState.variables) ? prevLen : Array.from(inStr).length)];
@@ -29958,7 +29976,7 @@ export class Runtime {
29958
29976
  outLines = this.evalSyncSort(rawLines, stageArgs, false) ?? [];
29959
29977
  }
29960
29978
  else if (isInlineUniq) {
29961
- const uniqRes = this.evalSyncUniq(rawLines, stageArgs);
29979
+ const uniqRes = this.evalSyncUniq(rawLines, stageArgs, byteLocale(rawState.variables));
29962
29980
  if (uniqRes === undefined)
29963
29981
  return undefined;
29964
29982
  outLines = uniqRes;
@@ -30359,7 +30377,7 @@ export class Runtime {
30359
30377
  fileRes = renderLines(sortRes);
30360
30378
  }
30361
30379
  else if (w0Plain === "uniq") {
30362
- const uniqRes = this.evalSyncUniq(rawLines, opArgs);
30380
+ const uniqRes = this.evalSyncUniq(rawLines, opArgs, byteLocale(rawState.variables));
30363
30381
  if (uniqRes !== undefined)
30364
30382
  fileRes = renderLines(uniqRes);
30365
30383
  }
@@ -30370,7 +30388,10 @@ export class Runtime {
30370
30388
  if (opArgs.length === 0)
30371
30389
  fileRes = this.syncBase64Encode(view) + "\n";
30372
30390
  else {
30373
- const decoded = this.syncBase64DecodeBytes(fileStr);
30391
+ const cleaned = fileStr.replace(/[ \t\r\n]+/g, "");
30392
+ if (cleaned.length % 4 !== 0 || (cleaned.length > 0 && !/^[A-Za-z0-9+/]+={0,2}$/.test(cleaned)))
30393
+ return undefined;
30394
+ const decoded = this.syncBase64DecodeBytes(cleaned);
30374
30395
  // Text-only substitutions cannot retain binary byte provenance.
30375
30396
  if (decoded.some(byte => byte === 0 || byte >= 128))
30376
30397
  return undefined;
@@ -100968,8 +100968,18 @@ function writeRichString(value3, runs, xml2, charge, target = "cell") {
100968
100968
  const t = (text2) => xml2("t", text2.trim() !== text2 ? { "xml:space": "preserve" } : {}, escapeXlsx(encodeXlsxString(text2)));
100969
100969
  if (!runs?.length) return t(value3);
100970
100970
  charge?.(runs.length);
100971
- const bytes = new TextEncoder().encode(value3), decoder = new TextDecoder("UTF-8", { fatal: true });
100972
- const points = [.../* @__PURE__ */ new Set([0, bytes.length, ...runs.flatMap((run) => [run.start, run.end])])].sort((a3, b3) => a3 - b3);
100971
+ const length = new TextEncoder().encode(value3).length;
100972
+ const points = [.../* @__PURE__ */ new Set([0, length, ...runs.flatMap((run) => [run.start, run.end])])].sort((a3, b3) => a3 - b3);
100973
+ let byteOffset = 0, characterOffset = 0;
100974
+ const characters = points.map((point) => {
100975
+ while (byteOffset < point && characterOffset < value3.length) {
100976
+ const code = value3.codePointAt(characterOffset);
100977
+ byteOffset += code < 128 ? 1 : code < 2048 ? 2 : code < 65536 ? 3 : 4;
100978
+ characterOffset += code > 65535 ? 2 : 1;
100979
+ }
100980
+ if (byteOffset !== point) throw new SsconvertError("invalid-request", "Invalid rich text UTF-8 boundary");
100981
+ return characterOffset;
100982
+ });
100973
100983
  let result2 = target === "comment" ? xml2("r", {}, t("")) : "";
100974
100984
  for (let i = 0; i + 1 < points.length; i++) {
100975
100985
  charge?.(runs.length);
@@ -100989,7 +100999,7 @@ function writeRichString(value3, runs, xml2, charge, target = "cell") {
100989
100999
  val: Object.hasOwn(richUnderlines, attrs.underline) ? richUnderlines[attrs.underline] : "none"
100990
101000
  });
100991
101001
  if (attrs.subscript || attrs.superscript) properties += xml2("vertAlign", { val: attrs.subscript ? "subscript" : "superscript" });
100992
- result2 += xml2("r", {}, (properties || target === "comment" ? xml2("rPr", {}, properties) : "") + t(decoder.decode(bytes.subarray(start, end2))));
101002
+ result2 += xml2("r", {}, (properties || target === "comment" ? xml2("rPr", {}, properties) : "") + t(value3.slice(characters[i], characters[i + 1])));
100993
101003
  }
100994
101004
  return result2;
100995
101005
  }
@@ -136315,7 +136325,7 @@ function createHtmlWriter(profile) {
136315
136325
  closures.unshift(close);
136316
136326
  }
136317
136327
  }
136318
- put(encoded2(new TextDecoder().decode(bytes.subarray(from, to))));
136328
+ put(encoded2(new TextDecoder("utf-8", { ignoreBOM: true }).decode(bytes.subarray(from, to))));
136319
136329
  for (const closure of closures) put(closure);
136320
136330
  }
136321
136331
  } else if (cell) put(encoded2(await renderCellText(cell, book, context, "preserve")));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@poe-platform/safe-bash",
3
- "version": "0.1.751",
3
+ "version": "0.1.752",
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.751",
648
+ "@poe-platform/safe-fs": "0.1.752",
649
649
  "pdf-lib": "1.17.1",
650
650
  "@types/node": "^25.2.2",
651
651
  "pako": "3.0.1",