@poe-platform/safe-bash 0.1.748 → 0.1.749

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -67,7 +67,8 @@ All private command plugins are available from `@poe-platform/safe-bash/core` in
67
67
  - `if`/`elif`/`else`, `case`, `for name in …`, `while`, `until`, functions,
68
68
  groups `{ …; }`, subshells `( … )` (including adjacent nested subshells),
69
69
  `[[ … ]]` (including file age/identity, special-file, mode and nameref
70
- predicates; literal/glob and ASCII ERE comparisons also accept UTF-8 locales,
70
+ predicates; ordering uses byte comparison in C/POSIX and host Intl collation
71
+ in supported UTF-8 locales. Literal/glob and ASCII ERE comparisons also accept UTF-8 locales,
71
72
  with ranges limited to C/POSIX collation, including C.UTF-8), arithmetic commands `(( … ))`, and indexed arrays with arithmetic
72
73
  and relative negative element indices, including `declare -a`, `local -a`,
73
74
  and `readonly -a` array literals. Arithmetic array operands such as `a[i]`
@@ -48712,6 +48712,9 @@ var lookupSpecialForms = {
48712
48712
  if (offsets[2] < 0 || offsets[2] >= areas.length) return error("#REF!");
48713
48713
  value3 = areas[offsets[2]];
48714
48714
  if (value3.kind === "error") return value3;
48715
+ } else if (value3.kind === "range" && value3.sheets.length > 1) {
48716
+ if (offsets[2] < 0 || offsets[2] >= value3.sheets.length) return error("#REF!");
48717
+ value3 = { ...value3, sheets: [value3.sheets[offsets[2]]] };
48715
48718
  } else if (offsets[2] !== 0) return error("#REF!");
48716
48719
  const row = offsets[0], column = offsets[1], dims = dimensions(value3);
48717
48720
  if (row < 0 || column < 0 || row >= dims.height || column >= dims.width) return error("#REF!");
@@ -52500,6 +52503,73 @@ init_platform();
52500
52503
 
52501
52504
  // packages/safe-bash-command-ssconvert/src/codecs/xlsx.ts
52502
52505
  init_platform();
52506
+
52507
+ // packages/safe-bash-command-ssconvert/src/formulas/index-sheet-areas.ts
52508
+ init_platform();
52509
+ function expandIndexSheetAreas(document6, onWork) {
52510
+ const order = document6.sheetOrder;
52511
+ if (!order) return document6;
52512
+ let ids, names2;
52513
+ const sheetIndex = (endpoint) => {
52514
+ if (!ids || !names2) {
52515
+ ids = /* @__PURE__ */ new Map();
52516
+ names2 = /* @__PURE__ */ new Map();
52517
+ for (let index2 = 0; index2 < order.length; index2++) {
52518
+ onWork();
52519
+ const id = order[index2];
52520
+ ids.set(id, index2);
52521
+ names2.set(foldSheetName(document6.sheetNames?.[id] ?? id), index2);
52522
+ }
52523
+ }
52524
+ const name5 = endpoint.sheet ?? document6.position.sheet;
52525
+ return ids.get(name5) ?? names2.get(foldSheetName(name5)) ?? -1;
52526
+ };
52527
+ const visit = (node3) => {
52528
+ onWork();
52529
+ if (node3.kind === "parentheses" || node3.kind === "unary") return { ...node3, child: visit(node3.child) };
52530
+ if (node3.kind === "binary") return { ...node3, left: visit(node3.left), right: visit(node3.right) };
52531
+ if (node3.kind === "array") return { ...node3, rows: node3.rows.map((row) => row.map(visit)) };
52532
+ if (node3.kind !== "call") return node3;
52533
+ const args = node3.args.map(visit);
52534
+ let source = args[0];
52535
+ while (source?.kind === "parentheses") source = source.child;
52536
+ if (node3.name === "INDEX" && args.length >= 1 && args.length <= 4 && source?.kind === "reference" && source.last && source.first.workbook === void 0 && source.last.workbook === void 0 && !source.label) {
52537
+ const first = sheetIndex(source.first), last2 = sheetIndex(source.last);
52538
+ if (first >= 0 && last2 >= 0 && first !== last2) {
52539
+ let areas = [];
52540
+ for (let index2 = Math.min(first, last2); index2 <= Math.max(first, last2); index2++) {
52541
+ onWork();
52542
+ const sheet = document6.sheetNames?.[order[index2]] ?? order[index2];
52543
+ const first2 = { ...source.first, sheet };
52544
+ const last3 = { ...source.last, sheet };
52545
+ delete first2.sheetOffset;
52546
+ delete last3.sheetOffset;
52547
+ areas.push({ ...source, first: first2, last: last3 });
52548
+ }
52549
+ while (areas.length > 1) {
52550
+ const next = [];
52551
+ for (let index2 = 0; index2 < areas.length; index2 += 2) {
52552
+ onWork();
52553
+ next.push(areas[index2 + 1] ? {
52554
+ kind: "binary",
52555
+ op: "union",
52556
+ start: source.start,
52557
+ end: source.end,
52558
+ left: areas[index2],
52559
+ right: areas[index2 + 1]
52560
+ } : areas[index2]);
52561
+ }
52562
+ areas = next;
52563
+ }
52564
+ args[0] = { kind: "parentheses", start: source.start, end: source.end, child: areas[0] };
52565
+ }
52566
+ }
52567
+ return { ...node3, args };
52568
+ };
52569
+ return { ...document6, root: visit(document6.root) };
52570
+ }
52571
+
52572
+ // packages/safe-bash-command-ssconvert/src/codecs/xlsx.ts
52503
52573
  import { parseXmlSteps as parseXmlSteps3, XmlLimitError } from "@poe-platform/safe-fs/core";
52504
52574
 
52505
52575
  // packages/safe-bash-command-ssconvert/src/codecs/xlsx-schema.ts
@@ -56406,7 +56476,7 @@ function createXlsxWriter(edition) {
56406
56476
  ...array ? { t: "array", ref: rangeText(array.range) } : {},
56407
56477
  ...formulaSemanticsAttributes(array?.arrayStringLiterals ?? cell.arrayStringLiterals, true, cell.formula)
56408
56478
  },
56409
- escapeXlsx(exportXlsxFormula(cell.formula, sheet, cell.row, cell.column, context2, array?.arrayStringLiterals ?? cell.arrayStringLiterals))
56479
+ escapeXlsx(exportXlsxFormula(book, cell.formula, sheet, cell.row, cell.column, context2, array?.arrayStringLiterals ?? cell.arrayStringLiterals))
56410
56480
  );
56411
56481
  if (value3.kind === "string") {
56412
56482
  if ((stringCounts.get(stringKey) ?? 0) > 1) {
@@ -56450,7 +56520,7 @@ function createXlsxWriter(edition) {
56450
56520
  };
56451
56521
  for (const [gnm, xlsx, invert] of [["DisplayFormulas", "showFormulas", false], ["HideZero", "showZeros", true], ["HideGrid", "showGridLines", true], ["HideColHeader", "showRowColHeaders", true], ["DisplayOutlines", "showOutlineSymbols", false], ["RTL_Layout", "rightToLeft", false]])
56452
56522
  if (view2[gnm] !== void 0) viewAttrs[xlsx] = (invert ? !Number(view2[gnm]) : !!Number(view2[gnm])) ? 1 : 0;
56453
- const metadata3 = await writeXlsxSheetMetadata(sheet, index2 + 1, xml2, context2, namespace5, exportXlsxFormula, styles, charge);
56523
+ const metadata3 = await writeXlsxSheetMetadata(sheet, index2 + 1, xml2, context2, namespace5, exportXlsxFormula.bind(null, book), styles, charge);
56454
56524
  let cols = "", nextColumn = 0;
56455
56525
  for (const c4 of [...sheet.columns ?? []].sort((a3, b3) => a3.index - b3.index)) {
56456
56526
  const importedColumn = metadataNode(c4.style?.gnumeric, charge);
@@ -56531,7 +56601,7 @@ function createXlsxWriter(edition) {
56531
56601
  localSheetId: index2 < 0 ? void 0 : index2,
56532
56602
  ...formulaSemanticsAttributes(name5.arrayStringLiterals, true, name5.expression, name5.position ? { ...name5.position, sheet: book.sheets.find((s) => s.id === name5.position.sheet)?.name ?? name5.position.sheet } : void 0)
56533
56603
  },
56534
- escapeXlsx(exportXlsxFormula(name5.expression, sheet, name5.position?.row ?? 0, name5.position?.column ?? 0, context2, name5.arrayStringLiterals))
56604
+ escapeXlsx(exportXlsxFormula(book, name5.expression, sheet, name5.position?.row ?? 0, name5.position?.column ?? 0, context2, name5.arrayStringLiterals))
56535
56605
  );
56536
56606
  }
56537
56607
  for (const [index2, sheet] of book.sheets.entries()) {
@@ -56608,17 +56678,24 @@ function createXlsxWriter(edition) {
56608
56678
  }
56609
56679
  };
56610
56680
  }
56611
- function exportXlsxFormula(source, sheet, row, column, context2, arrayStringLiterals = false) {
56681
+ function exportXlsxFormula(book, source, sheet, row, column, context2, arrayStringLiterals = false) {
56612
56682
  const position = { sheet: sheet.id, row, column };
56683
+ let work = 0;
56684
+ const onWork = () => {
56685
+ context2.signal.throwIfAborted();
56686
+ if (++work > (context2.limits.workbookNodes ?? Infinity))
56687
+ throw new SsconvertError("resource-limit", "ssconvert XLSX formula node limit exceeded");
56688
+ };
56613
56689
  const parsed = parseExpression(source.startsWith("=") || source.startsWith("of:=") ? source : "=" + source, {
56614
56690
  position,
56615
56691
  arrayStringLiterals,
56692
+ workbook: book,
56616
56693
  signal: context2.signal,
56617
56694
  maximumLength: context2.limits.workbookTextBytes ?? context2.limits.outputBytes,
56618
56695
  maximumNodes: context2.limits.workbookNodes ?? Infinity
56619
56696
  });
56620
56697
  if (!parsed.ok) throw new SsconvertError("unsupported-feature", "Unsupported ssconvert feature: unparsed XLSX formula");
56621
- const result2 = serializeExpression(parsed.document, excelGrammar, false, true, { relativeSheets: "fixed" });
56698
+ const result2 = serializeExpression(expandIndexSheetAreas(parsed.document, onWork), excelGrammar, false, true, { relativeSheets: "fixed" });
56622
56699
  return result2.startsWith("=") ? result2.slice(1) : result2;
56623
56700
  }
56624
56701
 
@@ -74924,8 +75001,8 @@ var operators = {
74924
75001
  ">=": 12,
74925
75002
  ">": 13,
74926
75003
  "<>": 14,
74927
- " ": 15,
74928
- ",": 16,
75004
+ intersection: 15,
75005
+ union: 16,
74929
75006
  ":": 17
74930
75007
  };
74931
75008
  var functions = new Map(Object.entries(biffFunctions).map(([id, spec]) => [spec[0], { id: Number(id), min: spec[1], max: spec[2] }]));
@@ -75069,7 +75146,12 @@ var BiffFormulaWriter = class {
75069
75146
  maximumLength: this.context.limits.workbookTextBytes ?? this.context.limits.outputBytes
75070
75147
  });
75071
75148
  if (!parsed.ok) throw new SsconvertError("unsupported-feature", `Cannot export Excel formula: ${expression2}`);
75072
- return parsed.document.root;
75149
+ let work = 0;
75150
+ return expandIndexSheetAreas(parsed.document, () => {
75151
+ this.context.signal.throwIfAborted();
75152
+ if (++work > (this.context.limits.workbookNodes ?? this.context.limits.cells))
75153
+ throw new SsconvertError("resource-limit", "ssconvert BIFF formula node limit exceeded");
75154
+ }).root;
75073
75155
  };
75074
75156
  const relative = definition !== void 0;
75075
75157
  const root = definition ? parseNamedExpression(definition, this.book, parse3, () => this.context.signal.throwIfAborted()) : parse3(source, { sheet, row, column });
@@ -164396,4 +164478,4 @@ tslib/tslib.js:
164396
164478
  @noble/ciphers/utils.js:
164397
164479
  (*! noble-ciphers - MIT License (c) 2023 Paul Miller (paulmillr.com) *)
164398
164480
  */
164399
- //# sourceMappingURL=chunk-MSEZFB2F.js.map
164481
+ //# sourceMappingURL=chunk-BQEH4DKJ.js.map
@@ -81,7 +81,7 @@ import {
81
81
  updateWorkbook,
82
82
  validSheetSize,
83
83
  visitFormula
84
- } from "./chunk-MSEZFB2F.js";
84
+ } from "./chunk-BQEH4DKJ.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-2MBSXQOA.js.map
174
+ //# sourceMappingURL=src-YBSQFQZO.js.map
@@ -56443,9 +56443,20 @@ async function leaf(node, context2) {
56443
56443
  return node.operator === "!=" ? !match : match;
56444
56444
  }
56445
56445
  if (node.operator === "<" || node.operator === ">") {
56446
- cLocale(context2);
56447
56446
  await charge(context2, shellValueByteLength(left) + shellValueByteLength(right));
56448
- const order = compareUtf8(left, right);
56447
+ let order;
56448
+ if (cCollation(context2.locale)) {
56449
+ order = compareUtf8(left, right);
56450
+ } else {
56451
+ if (!utf8Locale(context2.locale)) unsupported("collation locale");
56452
+ const locale = context2.locale.split(".")[0].split("_").join("-");
56453
+ try {
56454
+ if (Intl.Collator.supportedLocalesOf([locale]).length === 0) unsupported("collation locale");
56455
+ order = new Intl.Collator(locale).compare(left, right);
56456
+ } catch {
56457
+ unsupported("collation locale");
56458
+ }
56459
+ }
56449
56460
  return node.operator === "<" ? order < 0 : order > 0;
56450
56461
  }
56451
56462
  await charge(context2, left.length + right.length);
@@ -75593,7 +75604,7 @@ var Runtime = class _Runtime {
75593
75604
  }
75594
75605
  }
75595
75606
  if (command7.kind === "conditional") {
75596
- if (rawState.nocasematch || byteLocale(rawState.variables) || !this.canSyncConditional(command7.expression, 0, rawState)) return false;
75607
+ if (rawState.nocasematch || byteLocale(rawState.variables) || !this.canSyncConditional(command7.expression, 0, rawState, true)) return false;
75597
75608
  const checkCondNoFile = (e) => {
75598
75609
  if (e.kind === "nonempty") return this.isPureSyncValueWord(e.operand, rawState);
75599
75610
  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);
@@ -75869,8 +75880,9 @@ var Runtime = class _Runtime {
75869
75880
  }
75870
75881
  return false;
75871
75882
  }
75872
- canSyncConditional(expr, depth = 0, rawState) {
75883
+ canSyncConditional(expr, depth = 0, rawState, requireTotal = false) {
75873
75884
  if (depth > 8) return false;
75885
+ if (requireTotal && (expr.kind === "unary" && syncConditionalFileUnaryOps.has(expr.operator) || expr.kind === "binary" && expr.operator !== "==" && expr.operator !== "=" && expr.operator !== "!=" && expr.operator !== "=~")) return false;
75874
75886
  const okWord = (w) => !_Runtime.wordMayMutate(w) || rawState !== void 0 && this.isPureSyncValueWord(w, rawState);
75875
75887
  if (expr.kind === "nonempty") return okWord(expr.operand);
75876
75888
  if (expr.kind === "unary") return (expr.operator === "-n" || expr.operator === "-z" || expr.operator === "-v" || syncConditionalFileUnaryOps.has(expr.operator)) && okWord(expr.operand);
@@ -75886,8 +75898,8 @@ var Runtime = class _Runtime {
75886
75898
  }
75887
75899
  return expr.operator === "<" || expr.operator === ">" || expr.operator === "-eq" || expr.operator === "-ne" || expr.operator === "-lt" || expr.operator === "-le" || expr.operator === "-gt" || expr.operator === "-ge";
75888
75900
  }
75889
- if (expr.kind === "not") return this.canSyncConditional(expr.operand, depth + 1, rawState);
75890
- if (expr.kind === "and" || expr.kind === "or") return this.canSyncConditional(expr.left, depth + 1, rawState) && this.canSyncConditional(expr.right, depth + 1, rawState);
75901
+ if (expr.kind === "not") return this.canSyncConditional(expr.operand, depth + 1, rawState, requireTotal);
75902
+ if (expr.kind === "and" || expr.kind === "or") return this.canSyncConditional(expr.left, depth + 1, rawState, requireTotal) && this.canSyncConditional(expr.right, depth + 1, rawState, requireTotal);
75891
75903
  return false;
75892
75904
  }
75893
75905
  tryEvalFastMemoryFileUnary(op, rawVal, rawState) {
@@ -79632,7 +79644,7 @@ var Runtime = class _Runtime {
79632
79644
  if (bc.kind === "arithmetic") {
79633
79645
  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;
79634
79646
  } else if (bc.kind === "conditional") {
79635
- if (bc.redirects.length !== 0 || rawState.nocasematch || byteLocale(rawState.variables) || !this.canSyncConditional(bc.expression, 0, rawState)) return false;
79647
+ if (bc.redirects.length !== 0 || rawState.nocasematch || byteLocale(rawState.variables) || !this.canSyncConditional(bc.expression, 0, rawState, true)) return false;
79636
79648
  if (bc.expression.kind === "binary" && bc.expression.operator === "=~") return false;
79637
79649
  } else if (!this.extractPosixBracketCondExpr(bc, rawState, true, br.body)) {
79638
79650
  return false;
@@ -79662,7 +79674,7 @@ var Runtime = class _Runtime {
79662
79674
  if (pipeline.negate || pipeline.commands.length !== 1) return false;
79663
79675
  const cmd = pipeline.commands[0];
79664
79676
  const canSyncLoopCondCmd = (c) => {
79665
- if (c.redirects.length !== 0 || rawState.nocasematch || byteLocale(rawState.variables) || !this.canSyncConditional(c.expression, 0, rawState)) return false;
79677
+ if (c.redirects.length !== 0 || rawState.nocasematch || byteLocale(rawState.variables) || !this.canSyncConditional(c.expression, 0, rawState, true)) return false;
79666
79678
  const checkLeaf = (e) => {
79667
79679
  if (e.kind === "nonempty") return this.isPureSyncValueWord(e.operand, rawState);
79668
79680
  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);
@@ -80907,7 +80919,7 @@ var Runtime = class _Runtime {
80907
80919
  }
80908
80920
  whileArithProg = condCmd.expression;
80909
80921
  } else if (condCmd.kind === "conditional") {
80910
- if (condCmd.redirects.length !== 0 || rawState.nocasematch || byteLocale(rawState.variables) || !this.canSyncConditional(condCmd.expression, 0, rawState)) return void 0;
80922
+ if (condCmd.redirects.length !== 0 || rawState.nocasematch || byteLocale(rawState.variables) || !this.canSyncConditional(condCmd.expression, 0, rawState, true)) return void 0;
80911
80923
  const checkWhileCond = (e, depth) => {
80912
80924
  if (e.kind === "nonempty") return this.isPureSyncValueWord(e.operand, rawState);
80913
80925
  if (e.kind === "unary") return (e.operator === "-n" || e.operator === "-z" || e.operator === "-v") && this.isPureSyncValueWord(e.operand, rawState);
@@ -169,9 +169,24 @@ async function leaf(node, context) {
169
169
  return node.operator === "!=" ? !match : match;
170
170
  }
171
171
  if (node.operator === "<" || node.operator === ">") {
172
- cLocale(context);
173
172
  await charge(context, shellValueByteLength(left) + shellValueByteLength(right));
174
- const order = compareUtf8(left, right);
173
+ let order;
174
+ if (cCollation(context.locale)) {
175
+ order = compareUtf8(left, right);
176
+ }
177
+ else {
178
+ if (!utf8Locale(context.locale))
179
+ unsupported("collation locale");
180
+ const locale = context.locale.split(".")[0].split("_").join("-");
181
+ try {
182
+ if (Intl.Collator.supportedLocalesOf([locale]).length === 0)
183
+ unsupported("collation locale");
184
+ order = new Intl.Collator(locale).compare(left, right);
185
+ }
186
+ catch {
187
+ unsupported("collation locale");
188
+ }
189
+ }
175
190
  return node.operator === "<" ? order < 0 : order > 0;
176
191
  }
177
192
  await charge(context, left.length + right.length);
@@ -9405,7 +9405,7 @@ export class Runtime {
9405
9405
  }
9406
9406
  }
9407
9407
  if (command.kind === "conditional") {
9408
- if (rawState.nocasematch || byteLocale(rawState.variables) || !this.canSyncConditional(command.expression, 0, rawState))
9408
+ if (rawState.nocasematch || byteLocale(rawState.variables) || !this.canSyncConditional(command.expression, 0, rawState, true))
9409
9409
  return false;
9410
9410
  const checkCondNoFile = (e) => {
9411
9411
  if (e.kind === "nonempty")
@@ -9775,9 +9775,15 @@ export class Runtime {
9775
9775
  }
9776
9776
  return false;
9777
9777
  }
9778
- canSyncConditional(expr, depth = 0, rawState) {
9778
+ canSyncConditional(expr, depth = 0, rawState, requireTotal = false) {
9779
9779
  if (depth > 8)
9780
9780
  return false;
9781
+ // Loops and pure substitutions cannot replay after an unsupported result.
9782
+ // These operators depend on arithmetic, filesystem or collation state that
9783
+ // the speculative synchronous evaluator does not fully support.
9784
+ if (requireTotal && ((expr.kind === "unary" && syncConditionalFileUnaryOps.has(expr.operator)) ||
9785
+ (expr.kind === "binary" && expr.operator !== "==" && expr.operator !== "=" && expr.operator !== "!=" && expr.operator !== "=~")))
9786
+ return false;
9781
9787
  const okWord = (w) => !Runtime.wordMayMutate(w) || (rawState !== undefined && this.isPureSyncValueWord(w, rawState));
9782
9788
  if (expr.kind === "nonempty")
9783
9789
  return okWord(expr.operand);
@@ -9800,9 +9806,9 @@ export class Runtime {
9800
9806
  return (expr.operator === "<" || expr.operator === ">" || expr.operator === "-eq" || expr.operator === "-ne" || expr.operator === "-lt" || expr.operator === "-le" || expr.operator === "-gt" || expr.operator === "-ge");
9801
9807
  }
9802
9808
  if (expr.kind === "not")
9803
- return this.canSyncConditional(expr.operand, depth + 1, rawState);
9809
+ return this.canSyncConditional(expr.operand, depth + 1, rawState, requireTotal);
9804
9810
  if (expr.kind === "and" || expr.kind === "or")
9805
- return this.canSyncConditional(expr.left, depth + 1, rawState) && this.canSyncConditional(expr.right, depth + 1, rawState);
9811
+ return this.canSyncConditional(expr.left, depth + 1, rawState, requireTotal) && this.canSyncConditional(expr.right, depth + 1, rawState, requireTotal);
9806
9812
  return false;
9807
9813
  }
9808
9814
  tryEvalFastMemoryFileUnary(op, rawVal, rawState) {
@@ -14583,7 +14589,7 @@ export class Runtime {
14583
14589
  return false;
14584
14590
  }
14585
14591
  else if (bc.kind === "conditional") {
14586
- if (bc.redirects.length !== 0 || rawState.nocasematch || byteLocale(rawState.variables) || !this.canSyncConditional(bc.expression, 0, rawState))
14592
+ if (bc.redirects.length !== 0 || rawState.nocasematch || byteLocale(rawState.variables) || !this.canSyncConditional(bc.expression, 0, rawState, true))
14587
14593
  return false;
14588
14594
  if (bc.expression.kind === "binary" && bc.expression.operator === "=~")
14589
14595
  return false;
@@ -14625,7 +14631,7 @@ export class Runtime {
14625
14631
  return false;
14626
14632
  const cmd = pipeline.commands[0];
14627
14633
  const canSyncLoopCondCmd = (c) => {
14628
- if (c.redirects.length !== 0 || rawState.nocasematch || byteLocale(rawState.variables) || !this.canSyncConditional(c.expression, 0, rawState))
14634
+ if (c.redirects.length !== 0 || rawState.nocasematch || byteLocale(rawState.variables) || !this.canSyncConditional(c.expression, 0, rawState, true))
14629
14635
  return false;
14630
14636
  const checkLeaf = (e) => {
14631
14637
  if (e.kind === "nonempty")
@@ -16074,7 +16080,7 @@ export class Runtime {
16074
16080
  whileArithProg = condCmd.expression;
16075
16081
  }
16076
16082
  else if (condCmd.kind === "conditional") {
16077
- if (condCmd.redirects.length !== 0 || rawState.nocasematch || byteLocale(rawState.variables) || !this.canSyncConditional(condCmd.expression, 0, rawState))
16083
+ if (condCmd.redirects.length !== 0 || rawState.nocasematch || byteLocale(rawState.variables) || !this.canSyncConditional(condCmd.expression, 0, rawState, true))
16078
16084
  return undefined;
16079
16085
  const checkWhileCond = (e, depth) => {
16080
16086
  if (e.kind === "nonempty")
@@ -97628,6 +97628,9 @@ var lookupSpecialForms = {
97628
97628
  if (offsets[2] < 0 || offsets[2] >= areas.length) return error("#REF!");
97629
97629
  value3 = areas[offsets[2]];
97630
97630
  if (value3.kind === "error") return value3;
97631
+ } else if (value3.kind === "range" && value3.sheets.length > 1) {
97632
+ if (offsets[2] < 0 || offsets[2] >= value3.sheets.length) return error("#REF!");
97633
+ value3 = { ...value3, sheets: [value3.sheets[offsets[2]]] };
97631
97634
  } else if (offsets[2] !== 0) return error("#REF!");
97632
97635
  const row = offsets[0], column = offsets[1], dims = dimensions(value3);
97633
97636
  if (row < 0 || column < 0 || row >= dims.height || column >= dims.width) return error("#REF!");
@@ -105519,6 +105522,68 @@ function createZipCodec(runtime = defaults, profile = {}) {
105519
105522
  }
105520
105523
  return { readZipArchive, decodeZipEntry, makeZipEntry, writeZipArchive };
105521
105524
  }
105525
+ function expandIndexSheetAreas(document6, onWork) {
105526
+ const order = document6.sheetOrder;
105527
+ if (!order) return document6;
105528
+ let ids, names2;
105529
+ const sheetIndex = (endpoint) => {
105530
+ if (!ids || !names2) {
105531
+ ids = /* @__PURE__ */ new Map();
105532
+ names2 = /* @__PURE__ */ new Map();
105533
+ for (let index2 = 0; index2 < order.length; index2++) {
105534
+ onWork();
105535
+ const id = order[index2];
105536
+ ids.set(id, index2);
105537
+ names2.set(foldSheetName(document6.sheetNames?.[id] ?? id), index2);
105538
+ }
105539
+ }
105540
+ const name = endpoint.sheet ?? document6.position.sheet;
105541
+ return ids.get(name) ?? names2.get(foldSheetName(name)) ?? -1;
105542
+ };
105543
+ const visit = (node3) => {
105544
+ onWork();
105545
+ if (node3.kind === "parentheses" || node3.kind === "unary") return { ...node3, child: visit(node3.child) };
105546
+ if (node3.kind === "binary") return { ...node3, left: visit(node3.left), right: visit(node3.right) };
105547
+ if (node3.kind === "array") return { ...node3, rows: node3.rows.map((row) => row.map(visit)) };
105548
+ if (node3.kind !== "call") return node3;
105549
+ const args = node3.args.map(visit);
105550
+ let source = args[0];
105551
+ while (source?.kind === "parentheses") source = source.child;
105552
+ if (node3.name === "INDEX" && args.length >= 1 && args.length <= 4 && source?.kind === "reference" && source.last && source.first.workbook === void 0 && source.last.workbook === void 0 && !source.label) {
105553
+ const first = sheetIndex(source.first), last = sheetIndex(source.last);
105554
+ if (first >= 0 && last >= 0 && first !== last) {
105555
+ let areas = [];
105556
+ for (let index2 = Math.min(first, last); index2 <= Math.max(first, last); index2++) {
105557
+ onWork();
105558
+ const sheet = document6.sheetNames?.[order[index2]] ?? order[index2];
105559
+ const first2 = { ...source.first, sheet };
105560
+ const last2 = { ...source.last, sheet };
105561
+ delete first2.sheetOffset;
105562
+ delete last2.sheetOffset;
105563
+ areas.push({ ...source, first: first2, last: last2 });
105564
+ }
105565
+ while (areas.length > 1) {
105566
+ const next = [];
105567
+ for (let index2 = 0; index2 < areas.length; index2 += 2) {
105568
+ onWork();
105569
+ next.push(areas[index2 + 1] ? {
105570
+ kind: "binary",
105571
+ op: "union",
105572
+ start: source.start,
105573
+ end: source.end,
105574
+ left: areas[index2],
105575
+ right: areas[index2 + 1]
105576
+ } : areas[index2]);
105577
+ }
105578
+ areas = next;
105579
+ }
105580
+ args[0] = { kind: "parentheses", start: source.start, end: source.end, child: areas[0] };
105581
+ }
105582
+ }
105583
+ return { ...node3, args };
105584
+ };
105585
+ return { ...document6, root: visit(document6.root) };
105586
+ }
105522
105587
  var xlsxNamespaceScanElements = /* @__PURE__ */ new Set([
105523
105588
  "worksheet",
105524
105589
  "extLst",
@@ -109393,7 +109458,7 @@ function createXlsxWriter(edition) {
109393
109458
  ...array ? { t: "array", ref: rangeText(array.range) } : {},
109394
109459
  ...formulaSemanticsAttributes(array?.arrayStringLiterals ?? cell.arrayStringLiterals, true, cell.formula)
109395
109460
  },
109396
- escapeXlsx(exportXlsxFormula(cell.formula, sheet, cell.row, cell.column, context, array?.arrayStringLiterals ?? cell.arrayStringLiterals))
109461
+ escapeXlsx(exportXlsxFormula(book, cell.formula, sheet, cell.row, cell.column, context, array?.arrayStringLiterals ?? cell.arrayStringLiterals))
109397
109462
  );
109398
109463
  if (value3.kind === "string") {
109399
109464
  if ((stringCounts.get(stringKey) ?? 0) > 1) {
@@ -109437,7 +109502,7 @@ function createXlsxWriter(edition) {
109437
109502
  };
109438
109503
  for (const [gnm, xlsx, invert] of [["DisplayFormulas", "showFormulas", false], ["HideZero", "showZeros", true], ["HideGrid", "showGridLines", true], ["HideColHeader", "showRowColHeaders", true], ["DisplayOutlines", "showOutlineSymbols", false], ["RTL_Layout", "rightToLeft", false]])
109439
109504
  if (view2[gnm] !== void 0) viewAttrs[xlsx] = (invert ? !Number(view2[gnm]) : !!Number(view2[gnm])) ? 1 : 0;
109440
- const metadata3 = await writeXlsxSheetMetadata(sheet, index2 + 1, xml2, context, namespace5, exportXlsxFormula, styles, charge);
109505
+ const metadata3 = await writeXlsxSheetMetadata(sheet, index2 + 1, xml2, context, namespace5, exportXlsxFormula.bind(null, book), styles, charge);
109441
109506
  let cols = "", nextColumn = 0;
109442
109507
  for (const c4 of [...sheet.columns ?? []].sort((a3, b3) => a3.index - b3.index)) {
109443
109508
  const importedColumn = metadataNode(c4.style?.gnumeric, charge);
@@ -109518,7 +109583,7 @@ function createXlsxWriter(edition) {
109518
109583
  localSheetId: index2 < 0 ? void 0 : index2,
109519
109584
  ...formulaSemanticsAttributes(name.arrayStringLiterals, true, name.expression, name.position ? { ...name.position, sheet: book.sheets.find((s) => s.id === name.position.sheet)?.name ?? name.position.sheet } : void 0)
109520
109585
  },
109521
- escapeXlsx(exportXlsxFormula(name.expression, sheet, name.position?.row ?? 0, name.position?.column ?? 0, context, name.arrayStringLiterals))
109586
+ escapeXlsx(exportXlsxFormula(book, name.expression, sheet, name.position?.row ?? 0, name.position?.column ?? 0, context, name.arrayStringLiterals))
109522
109587
  );
109523
109588
  }
109524
109589
  for (const [index2, sheet] of book.sheets.entries()) {
@@ -109595,17 +109660,24 @@ function createXlsxWriter(edition) {
109595
109660
  }
109596
109661
  };
109597
109662
  }
109598
- function exportXlsxFormula(source, sheet, row, column, context, arrayStringLiterals = false) {
109663
+ function exportXlsxFormula(book, source, sheet, row, column, context, arrayStringLiterals = false) {
109599
109664
  const position = { sheet: sheet.id, row, column };
109665
+ let work = 0;
109666
+ const onWork = () => {
109667
+ context.signal.throwIfAborted();
109668
+ if (++work > (context.limits.workbookNodes ?? Infinity))
109669
+ throw new SsconvertError("resource-limit", "ssconvert XLSX formula node limit exceeded");
109670
+ };
109600
109671
  const parsed = parseExpression(source.startsWith("=") || source.startsWith("of:=") ? source : "=" + source, {
109601
109672
  position,
109602
109673
  arrayStringLiterals,
109674
+ workbook: book,
109603
109675
  signal: context.signal,
109604
109676
  maximumLength: context.limits.workbookTextBytes ?? context.limits.outputBytes,
109605
109677
  maximumNodes: context.limits.workbookNodes ?? Infinity
109606
109678
  });
109607
109679
  if (!parsed.ok) throw new SsconvertError("unsupported-feature", "Unsupported ssconvert feature: unparsed XLSX formula");
109608
- const result2 = serializeExpression(parsed.document, excelGrammar, false, true, { relativeSheets: "fixed" });
109680
+ const result2 = serializeExpression(expandIndexSheetAreas(parsed.document, onWork), excelGrammar, false, true, { relativeSheets: "fixed" });
109609
109681
  return result2.startsWith("=") ? result2.slice(1) : result2;
109610
109682
  }
109611
109683
  function invalidBiff(message) {
@@ -128230,8 +128302,8 @@ var operators = {
128230
128302
  ">=": 12,
128231
128303
  ">": 13,
128232
128304
  "<>": 14,
128233
- " ": 15,
128234
- ",": 16,
128305
+ intersection: 15,
128306
+ union: 16,
128235
128307
  ":": 17
128236
128308
  };
128237
128309
  var functions = new Map(Object.entries(biffFunctions).map(([id, spec]) => [spec[0], { id: Number(id), min: spec[1], max: spec[2] }]));
@@ -128375,7 +128447,12 @@ var BiffFormulaWriter = class {
128375
128447
  maximumLength: this.context.limits.workbookTextBytes ?? this.context.limits.outputBytes
128376
128448
  });
128377
128449
  if (!parsed.ok) throw new SsconvertError("unsupported-feature", `Cannot export Excel formula: ${expression2}`);
128378
- return parsed.document.root;
128450
+ let work = 0;
128451
+ return expandIndexSheetAreas(parsed.document, () => {
128452
+ this.context.signal.throwIfAborted();
128453
+ if (++work > (this.context.limits.workbookNodes ?? this.context.limits.cells))
128454
+ throw new SsconvertError("resource-limit", "ssconvert BIFF formula node limit exceeded");
128455
+ }).root;
128379
128456
  };
128380
128457
  const relative = definition !== void 0;
128381
128458
  const root = definition ? parseNamedExpression(definition, this.book, parse2, () => this.context.signal.throwIfAborted()) : parse2(source, { sheet, row, column });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@poe-platform/safe-bash",
3
- "version": "0.1.748",
3
+ "version": "0.1.749",
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.748",
648
+ "@poe-platform/safe-fs": "0.1.749",
649
649
  "pdf-lib": "1.17.1",
650
650
  "@types/node": "^25.2.2",
651
651
  "pako": "3.0.1",