@poe-platform/safe-bash 0.1.756 → 0.1.757

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/dist/image-ast/index.browser.js +4 -4
  2. package/dist/image-ast/index.js +4 -4
  3. package/dist/safe-bash/chunks/{chunk-LESPJ3OG.js → chunk-WNWIBBJZ.js} +15 -2
  4. package/dist/safe-bash/command-chunks/{chunk-S4J6MSLY.js → chunk-444OTXM7.js} +3 -3
  5. package/dist/safe-bash/command-chunks/{chunk-6AIOGLZO.js → chunk-CE6Z2ULX.js} +2 -2
  6. package/dist/safe-bash/command-chunks/{chunk-5VSGEE6J.js → chunk-F4IQCXZG.js} +5 -5
  7. package/dist/safe-bash/command-chunks/{chunk-F5MORDSU.js → chunk-FDNCPZQS.js} +32 -1
  8. package/dist/safe-bash/command-chunks/{chunk-SG3ZDZCX.js → chunk-FLMLM7Y4.js} +2 -2
  9. package/dist/safe-bash/command-chunks/{chunk-CT3FB722.js → chunk-J7UMKBRK.js} +1 -1
  10. package/dist/safe-bash/command-chunks/{chunk-QQOAK7JQ.js → chunk-KKJCKPTA.js} +1 -1
  11. package/dist/safe-bash/command-chunks/{chunk-7KS6PX4U.js → chunk-KSIGF4AG.js} +3 -3
  12. package/dist/safe-bash/command-chunks/{chunk-4HQ7WYSI.js → chunk-MED2BOOW.js} +30 -5
  13. package/dist/safe-bash/command-chunks/{chunk-JRNLDPLI.js → chunk-OZSWW23M.js} +1 -1
  14. package/dist/safe-bash/command-chunks/{chunk-DKQ5REXF.js → chunk-QDPRSPHQ.js} +1 -1
  15. package/dist/safe-bash/command-chunks/{chunk-WOPU2VWF.js → chunk-RID3B5RE.js} +1 -1
  16. package/dist/safe-bash/command-chunks/{chunk-DVZCHNB5.js → chunk-S4MLIYSN.js} +7 -7
  17. package/dist/safe-bash/command-chunks/{chunk-REYTQLLB.js → chunk-TT2DMDBY.js} +1 -1
  18. package/dist/safe-bash/command-chunks/{chunk-UGX6MXDD.js → chunk-UIIT4ULR.js} +3 -3
  19. package/dist/safe-bash/command-chunks/{chunk-A6NFLWZI.js → chunk-WKPOGTZY.js} +22 -6
  20. package/dist/safe-bash/command-chunks/{chunk-3DOHQQJR.js → chunk-Y64PA6IA.js} +8 -8
  21. package/dist/safe-bash/command-chunks/{chunk-6Z7OS3TZ.js → chunk-ZP5KDHB4.js} +7 -7
  22. package/dist/safe-bash/commands/pandoc/index.d.ts +1 -1
  23. package/dist/safe-bash/commands/pandoc/index.js +5 -3
  24. package/dist/safe-bash/commands/python/index.browser.js +1 -1
  25. package/dist/safe-bash/commands/python/index.js +6 -0
  26. package/dist/safe-bash/commands/python/jspi.js +11 -2
  27. package/dist/safe-bash/core.browser.js +6 -4
  28. package/dist/safe-bash-command-csvkit/index.js +8 -8
  29. package/dist/safe-bash-command-soffice/index.js +16 -16
  30. package/dist/safe-bash-command-ssconvert/core.js +8 -8
  31. package/dist/safe-bash-command-ssconvert/formats/csv.js +9 -9
  32. package/dist/safe-bash-command-ssconvert/formats/ods.js +8 -8
  33. package/dist/safe-bash-command-ssconvert/formats/spreadsheetml.js +1 -1
  34. package/dist/safe-bash-command-ssconvert/formats/xls.js +9 -8
  35. package/dist/safe-bash-command-ssconvert/formats/xlsx.js +8 -8
  36. package/dist/safe-bash-command-ssconvert/index.js +107 -27
  37. package/dist/safe-bash-contracts/filesystem-descriptor.js +1 -1
  38. package/dist/safe-bash-contracts/filesystem-output.js +1 -1
  39. package/dist/safe-bash-mcp/index.js +4 -4
  40. package/dist/spreadsheet-engine/formulas/ast.d.ts +13 -0
  41. package/dist/spreadsheet-engine/formulas/conventions.js +1 -1
  42. package/dist/spreadsheet-engine/formulas/dependencies.js +2 -2
  43. package/dist/spreadsheet-engine/formulas/evaluator.js +2 -2
  44. package/dist/spreadsheet-engine/formulas/named-expressions.d.ts +2 -2
  45. package/dist/spreadsheet-engine/formulas/named-expressions.js +31 -5
  46. package/dist/spreadsheet-engine/formulas/parser.js +34 -0
  47. package/dist/spreadsheet-engine/formulas/rewriting.js +19 -1
  48. package/dist/spreadsheet-engine/formulas/serialization.js +6 -0
  49. package/dist/spreadsheet-format-xls/biff-write-formulas.js +2 -0
  50. package/dist/xlsx-ast/formula-semantics.js +2 -0
  51. package/package.json +2 -2
  52. package/dist/safe-bash/chunks/browser-ECQX6GMK.js +0 -6766
  53. package/dist/safe-bash/chunks/bz2-4OP3OWW3.js +0 -8
  54. package/dist/safe-bash/chunks/chunk-22XPAAOI.js +0 -173
  55. package/dist/safe-bash/chunks/chunk-44WUNUPV.js +0 -649
  56. package/dist/safe-bash/chunks/chunk-4E3SGG37.js +0 -876
  57. package/dist/safe-bash/chunks/chunk-4QTSSQKP.js +0 -898
  58. package/dist/safe-bash/chunks/chunk-5UVGPVXP.js +0 -410
  59. package/dist/safe-bash/chunks/chunk-64OE6Q6X.js +0 -3487
  60. package/dist/safe-bash/chunks/chunk-6IIFUPN2.js +0 -1526
  61. package/dist/safe-bash/chunks/chunk-BENDLTQD.js +0 -7065
  62. package/dist/safe-bash/chunks/chunk-BMO5LWEX.js +0 -534
  63. package/dist/safe-bash/chunks/chunk-BPMCPTNP.js +0 -1920
  64. package/dist/safe-bash/chunks/chunk-BRJTAWP5.js +0 -2955
  65. package/dist/safe-bash/chunks/chunk-C3GEIN7Q.js +0 -20054
  66. package/dist/safe-bash/chunks/chunk-CBVANNKK.js +0 -174814
  67. package/dist/safe-bash/chunks/chunk-CEWCRI6H.js +0 -2813
  68. package/dist/safe-bash/chunks/chunk-CGL53DQQ.js +0 -37
  69. package/dist/safe-bash/chunks/chunk-CTYJJD4O.js +0 -869
  70. package/dist/safe-bash/chunks/chunk-CWSS4MQA.js +0 -7
  71. package/dist/safe-bash/chunks/chunk-DLIGMTCP.js +0 -3266
  72. package/dist/safe-bash/chunks/chunk-EMAJ7WUW.js +0 -1031
  73. package/dist/safe-bash/chunks/chunk-FDKIZ2IJ.js +0 -1950
  74. package/dist/safe-bash/chunks/chunk-FZWJ2A7V.js +0 -3093
  75. package/dist/safe-bash/chunks/chunk-G2VCIF2J.js +0 -22
  76. package/dist/safe-bash/chunks/chunk-GVKBUQDX.js +0 -2649
  77. package/dist/safe-bash/chunks/chunk-IO2OGW3E.js +0 -28051
  78. package/dist/safe-bash/chunks/chunk-ISCZG7WA.js +0 -2648
  79. package/dist/safe-bash/chunks/chunk-JCN3FXLP.js +0 -38983
  80. package/dist/safe-bash/chunks/chunk-KJHDD4A2.js +0 -759
  81. package/dist/safe-bash/chunks/chunk-LB6HVRXS.js +0 -857
  82. package/dist/safe-bash/chunks/chunk-LLWAJOZH.js +0 -15
  83. package/dist/safe-bash/chunks/chunk-LMUKYZIJ.js +0 -708
  84. package/dist/safe-bash/chunks/chunk-M5SCNKIK.js +0 -2012
  85. package/dist/safe-bash/chunks/chunk-MJPFHANT.js +0 -3350
  86. package/dist/safe-bash/chunks/chunk-MZQW2DOL.js +0 -82
  87. package/dist/safe-bash/chunks/chunk-NB2V7EOY.js +0 -1348
  88. package/dist/safe-bash/chunks/chunk-NJVGOIYN.js +0 -17
  89. package/dist/safe-bash/chunks/chunk-OLNZHDIQ.js +0 -5141
  90. package/dist/safe-bash/chunks/chunk-OSUXMC43.js +0 -2317
  91. package/dist/safe-bash/chunks/chunk-OUUVROTM.js +0 -38
  92. package/dist/safe-bash/chunks/chunk-PA6GYKBQ.js +0 -151
  93. package/dist/safe-bash/chunks/chunk-PEFHZ226.js +0 -6957
  94. package/dist/safe-bash/chunks/chunk-QBB5P5KW.js +0 -4839
  95. package/dist/safe-bash/chunks/chunk-RSAWYAEH.js +0 -973
  96. package/dist/safe-bash/chunks/chunk-UWLYRWT4.js +0 -739
  97. package/dist/safe-bash/chunks/chunk-V463NBQH.js +0 -13
  98. package/dist/safe-bash/chunks/chunk-WPUALYUS.js +0 -843
  99. package/dist/safe-bash/chunks/chunk-XHKFSRGU.js +0 -5747
  100. package/dist/safe-bash/chunks/chunk-XHRCLZ3N.js +0 -13391
  101. package/dist/safe-bash/chunks/chunk-XPF5XHLY.js +0 -15
  102. package/dist/safe-bash/chunks/chunk-ZBFOWARG.js +0 -305
  103. package/dist/safe-bash/chunks/chunk-ZG7GWQ5P.js +0 -1859
  104. package/dist/safe-bash/chunks/model-store-EIY3ZQLZ.js +0 -10
  105. package/dist/safe-bash/chunks/pdf-renderer-CMF3CSVU.js +0 -22
  106. package/dist/safe-bash/chunks/pptx-ZL6L7KP2-O425BFIU.js +0 -30658
  107. package/dist/safe-bash/chunks/src-HHEPGR7U.js +0 -18969
  108. package/dist/safe-bash/chunks/src-MXIKQCIC.js +0 -187
  109. package/dist/safe-bash/chunks/usage-6TGVA2OM.js +0 -35
  110. package/dist/safe-bash/chunks/xz-EGQGQUEI.js +0 -8
  111. package/dist/safe-bash/chunks/xz-K2E2WLM6.js +0 -9
  112. package/dist/safe-bash/chunks/zstd-6JBLP5EF.js +0 -8
  113. package/dist/safe-bash/git_rust-C3QYOAZL.wasm +0 -0
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  openCommandFile
3
- } from "../safe-bash/command-chunks/chunk-7KS6PX4U.js";
3
+ } from "../safe-bash/command-chunks/chunk-KSIGF4AG.js";
4
4
  import "../safe-bash/command-chunks/chunk-PTLDMI4O.js";
5
5
  import "../safe-bash/command-chunks/chunk-U6CJOXRY.js";
6
6
  import "../safe-bash/command-chunks/chunk-WYJTWJZ5.js";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  openCommandFile
3
- } from "../safe-bash/command-chunks/chunk-7KS6PX4U.js";
3
+ } from "../safe-bash/command-chunks/chunk-KSIGF4AG.js";
4
4
  import {
5
5
  assertCountedFileOutput,
6
6
  bindFileOutputBudget,
@@ -14397,7 +14397,7 @@ function forwardFileDescriptor(descriptor, operation, capabilities, snapshot = (
14397
14397
  write: (retained, buffer, position, options) => operation("write", options, () => retained.write(buffer, position, options)),
14398
14398
  truncate: (retained, length, options) => operation("ftruncate", options, () => retained.truncate(length, options)),
14399
14399
  sync: (retained, dataOnly, options) => operation(dataOnly ? "fdatasync" : "fsync", options, () => retained.sync(dataOnly, options)),
14400
- close: (retained) => operation("close", {}, () => retained.close())
14400
+ close: (retained, options) => operation("close", {}, () => retained.close(options))
14401
14401
  };
14402
14402
  }
14403
14403
  function integer(value, syscall, path6) {
@@ -14565,10 +14565,10 @@ var ManagedFileDescriptor = class {
14565
14565
  await backend.sync(backend.resource, dataOnly, forwarded);
14566
14566
  });
14567
14567
  }
14568
- close() {
14568
+ close(options = {}) {
14569
14569
  this.#closing ??= this.#pending.then(async () => {
14570
14570
  try {
14571
- await this.#backend.close(this.#backend.resource);
14571
+ await this.#backend.close(this.#backend.resource, options);
14572
14572
  } finally {
14573
14573
  this.#backend = void 0;
14574
14574
  this.#getPosition = void 0;
@@ -14626,7 +14626,7 @@ async function openFileDescriptor(path6, options, capabilities, acquire) {
14626
14626
  signal?.throwIfAborted();
14627
14627
  return descriptor;
14628
14628
  } catch (error) {
14629
- await finishCleanup(() => backend.close(backend.resource), true);
14629
+ await finishCleanup(() => backend.close(backend.resource, {}), true);
14630
14630
  signal?.throwIfAborted();
14631
14631
  throw error;
14632
14632
  }
@@ -13,6 +13,17 @@ export interface Axis {
13
13
  readonly value: number;
14
14
  readonly relative: boolean;
15
15
  }
16
+ /** A binary-format name use retains identity while copies displace its definition.
17
+ * Offsets apply to references resolved at the current definition's declaration
18
+ * anchor. Copy adds row/column deltas only in relative mode and always adds tab
19
+ * displacement unless sheetRelative is false; moving leaves offsets intact. */
20
+ export interface NameRelocation {
21
+ readonly relative: boolean;
22
+ readonly row: number;
23
+ readonly column: number;
24
+ readonly sheet: number;
25
+ readonly sheetRelative?: boolean;
26
+ }
16
27
  export interface ReferenceEndpoint {
17
28
  readonly row?: Axis;
18
29
  readonly column?: Axis;
@@ -62,6 +73,7 @@ export type FormulaNode = Span & ({
62
73
  readonly kind: "name";
63
74
  readonly name: string;
64
75
  readonly sheet?: string;
76
+ readonly relocation?: NameRelocation;
65
77
  /** Empty means the current workbook; without a sheet it selects only global names. */
66
78
  readonly workbook?: string;
67
79
  } | {
@@ -117,6 +129,7 @@ export interface FormulaGrammar {
117
129
  readonly quotedLabels?: "openformula" | "calc";
118
130
  /** Permit ssconvert's live label anchors in internal workbook expressions. */
119
131
  readonly internalLabels?: boolean;
132
+ readonly internalNames?: boolean;
120
133
  readonly quotedErrors?: boolean;
121
134
  readonly rangeSeparator?: string;
122
135
  readonly sheetSpans?: boolean;
@@ -25,7 +25,7 @@ export const odfGrammar = Object.freeze({ ...gnumericGrammar,
25
25
  prefixes: Object.freeze(["of:=", "oooc:=", "="]), functionPrefixes: Object.freeze(["ORG.GNUMERIC.", "COM.MICROSOFT."])
26
26
  });
27
27
  /** Internal OpenFormula preserves relative sheets alongside live label anchors. */
28
- export const internalOdfGrammar = Object.freeze({ ...odfGrammar, internalLabels: true });
28
+ export const internalOdfGrammar = Object.freeze({ ...odfGrammar, internalLabels: true, internalNames: true });
29
29
  export const sylkGrammar = Object.freeze({ ...gnumericGrammar, id: "sylk", address: "r1c1" });
30
30
  /** Native SYLK output quotes strings without escaping; its reader does not invert that operation. */
31
31
  export const sylkWriterGrammar = Object.freeze({ ...sylkGrammar, id: "sylk-writer", stringEscape: "raw" });
@@ -80,7 +80,7 @@ export function buildDependencyGraph(book, roots, resolve, parse, tick, onRange,
80
80
  const name = named(node, position);
81
81
  if (!name || names.has(name))
82
82
  return [];
83
- return staticRanges(parseNamedExpression(name, book, parse, tick, maximumDepth), position, new Set([...names, name]), depth + 1);
83
+ return staticRanges(parseNamedExpression(name, book, parse, tick, maximumDepth, node.relocation), position, new Set([...names, name]), depth + 1);
84
84
  }
85
85
  if (node.kind === "call" && (node.name === "IF" || node.name === "CHOOSE")) {
86
86
  const ranges = [];
@@ -188,7 +188,7 @@ export function buildDependencyGraph(book, roots, resolve, parse, tick, onRange,
188
188
  if (node.kind === "name" && (node.workbook === undefined || node.workbook === "")) {
189
189
  const name = named(node, position);
190
190
  if (name && !names.has(name)) {
191
- pending.push({ node: parseNamedExpression(name, book, parse, tick, maximumDepth), position, names: new Set([...names, name]), depth: depth + 1 });
191
+ pending.push({ node: parseNamedExpression(name, book, parse, tick, maximumDepth, node.relocation), position, names: new Set([...names, name]), depth: depth + 1 });
192
192
  }
193
193
  }
194
194
  const children = node.kind === "unary" || node.kind === "parentheses" ? [node.child] : node.kind === "binary" ? [node.left, node.right] : node.kind === "call" ? node.args : node.kind === "array" ? node.rows.flat() : [];
@@ -175,7 +175,7 @@ export function* recalculateWorkbookSteps(input, context, options = false, onDia
175
175
  const name = book.names?.find(entry => entry.sheet === sheet && matches(entry)) ?? book.names?.find(entry => entry.sheet === undefined && matches(entry));
176
176
  if (!name || names.has(name))
177
177
  return error("#REF!");
178
- return indirectRange(parseNamedExpression(name, book, parse, tick, context.limits.formulaDependencyDepth ?? Infinity), position, new Set([...names, name]), depth + 1);
178
+ return indirectRange(parseNamedExpression(name, book, parse, tick, context.limits.formulaDependencyDepth ?? Infinity, node.relocation), position, new Set([...names, name]), depth + 1);
179
179
  }
180
180
  function scalar(value, position) {
181
181
  // OpenFormula §5.10.4 selects at the formula position when the consumer
@@ -264,7 +264,7 @@ export function* recalculateWorkbookSteps(input, context, options = false, onDia
264
264
  const name = book.names?.find(entry => entry.sheet === sheet && matches(entry)) ?? book.names?.find(entry => entry.sheet === undefined && matches(entry));
265
265
  if (!name || names.has(name))
266
266
  return error("#NAME?");
267
- return evaluate(parseNamedExpression(name, book, parse, tick, context.limits.formulaDependencyDepth ?? Infinity), position, array, new Set([...names, name]), wantReference);
267
+ return evaluate(parseNamedExpression(name, book, parse, tick, context.limits.formulaDependencyDepth ?? Infinity, node.relocation), position, array, new Set([...names, name]), wantReference);
268
268
  }
269
269
  if (node.kind === "unary") {
270
270
  const apply = (value) => {
@@ -1,6 +1,6 @@
1
1
  import { type NamedExpression, type Workbook } from "../../spreadsheet-ast/index.js";
2
- import type { FormulaNode, ParsePosition } from "./ast.js";
2
+ import type { FormulaNode, NameRelocation, ParsePosition } from "./ast.js";
3
3
  /** Parse relative offsets at the declaration anchor and bind nested names there.
4
4
  * References and functions in the resulting expression still use the caller's
5
5
  * evaluation position, as in native expr_name_eval. */
6
- export declare function parseNamedExpression(name: NamedExpression, book: Workbook, parse: (source: string, position: ParsePosition, arrayStringLiterals?: boolean) => FormulaNode, tick: () => void, maximumDepth?: number): FormulaNode;
6
+ export declare function parseNamedExpression(name: NamedExpression, book: Workbook, parse: (source: string, position: ParsePosition, arrayStringLiterals?: boolean) => FormulaNode, tick: () => void, maximumDepth?: number, relocation?: NameRelocation): FormulaNode;
@@ -4,23 +4,49 @@ import { foldSheetName } from "../../spreadsheet-ast/case-fold.js";
4
4
  /** Parse relative offsets at the declaration anchor and bind nested names there.
5
5
  * References and functions in the resulting expression still use the caller's
6
6
  * evaluation position, as in native expr_name_eval. */
7
- export function parseNamedExpression(name, book, parse, tick, maximumDepth = Infinity) {
7
+ export function parseNamedExpression(name, book, parse, tick, maximumDepth = Infinity, relocation) {
8
8
  if (name.expression === "" || name.expression === "=") {
9
9
  tick();
10
10
  return { kind: "literal", value: { kind: "blank" }, start: 0, end: 0 };
11
11
  }
12
12
  const position = name.position ?? { sheet: name.sheet ?? book.sheets[0]?.id ?? "", row: 0, column: 0 };
13
+ function displace(ref, fallback) {
14
+ if (!relocation || ref.workbook !== undefined)
15
+ return ref;
16
+ const origin = book.sheets.findIndex(sheet => sheet.id === position.sheet);
17
+ const index = ref.sheetRelative ? origin + (ref.sheetOffset ?? 0) : ref.sheet !== undefined || fallback !== undefined
18
+ ? book.sheets.findIndex(sheet => foldSheetName(sheet.name) === foldSheetName(ref.sheet ?? fallback)) : origin;
19
+ const target = index >= 0 ? book.sheets[index + relocation.sheet] : undefined;
20
+ if (!target)
21
+ return undefined;
22
+ const axis = (value, key) => ({ value: value.value + (value.relative ? position[key] : 0) + relocation[key], relative: false });
23
+ return { ...ref, sheet: target.name, sheetRelative: false, ...(ref.row ? { row: axis(ref.row, "row") } : {}), ...(ref.column ? { column: axis(ref.column, "column") } : {}) };
24
+ }
13
25
  function bind(node, depth = 0) {
14
26
  tick();
15
27
  if (depth > maximumDepth)
16
28
  throw new SsconvertError("resource-limit", "ssconvert formula dependency depth limit exceeded");
17
- if (node.kind === "name" && (node.workbook === undefined || node.workbook === "" && node.sheet !== undefined)) {
29
+ if (node.kind === "reference" && relocation) {
30
+ const first = displace(node.first), last = node.last ? displace(node.last, node.first.sheet) : undefined;
31
+ if (!first || node.last && !last)
32
+ return { kind: "literal", value: { kind: "error", value: "#REF!" }, start: node.start, end: node.end };
33
+ return { ...node, first, ...(last ? { last } : {}) };
34
+ }
35
+ if (node.kind === "name" && (node.workbook === undefined || node.workbook === "")) {
36
+ const inherited = relocation ? { ...relocation,
37
+ row: relocation.row + (node.relocation?.row ?? 0), column: relocation.column + (node.relocation?.column ?? 0),
38
+ sheet: relocation.sheet + (node.relocation?.sheet ?? 0) } : node.relocation;
39
+ const bound = inherited ? { ...node, relocation: inherited } : node;
40
+ if (node.workbook === "" && node.sheet === undefined)
41
+ return bound;
18
42
  const scope = node.sheet === undefined ? name.sheet : book.sheets.find(sheet => foldSheetName(sheet.name) === foldSheetName(node.sheet))?.id;
19
43
  const target = resolveName(book, node.name, scope);
20
- if (target?.sheet === undefined)
21
- return { kind: "name", name: node.name, workbook: "", start: node.start, end: node.end };
44
+ if (target?.sheet === undefined) {
45
+ const { sheet: ignoredSheet, ...global } = bound;
46
+ return { ...global, workbook: "" };
47
+ }
22
48
  const sheet = book.sheets.find(sheet => sheet.id === target.sheet);
23
- return sheet ? { ...node, sheet: sheet.name } : node;
49
+ return sheet ? { ...bound, sheet: sheet.name } : bound;
24
50
  }
25
51
  if (node.kind === "parentheses" || node.kind === "unary")
26
52
  return { ...node, child: bind(node.child, depth + 1) };
@@ -335,6 +335,40 @@ export function parseExpression(source, options) {
335
335
  throw new SsconvertError("resource-limit", "ssconvert formula node limit exceeded");
336
336
  try {
337
337
  const c = source[offset];
338
+ if (source.startsWith("@name.", offset)) {
339
+ if (grammar.id !== "gnumeric" && !grammar.internalNames)
340
+ fail("Internal live name in native formula");
341
+ offset += 6;
342
+ const relative = source.startsWith("relative", offset);
343
+ if (!relative && !source.startsWith("absolute", offset))
344
+ fail("Invalid live name mode");
345
+ offset += 8;
346
+ const fixedSheet = source.startsWith(".fixed-sheet", offset);
347
+ if (fixedSheet)
348
+ offset += 12;
349
+ if (source[offset++] !== "[")
350
+ fail("Invalid live name mode");
351
+ const displacement = (separator) => {
352
+ const start = offset;
353
+ if (source[offset] === "-")
354
+ offset++;
355
+ const begin = offset;
356
+ while (digit(source[offset]))
357
+ offset++;
358
+ const value = Number(source.slice(start, offset));
359
+ if (offset === begin || !Number.isSafeInteger(value) || source[offset++] !== separator)
360
+ fail("Invalid live name displacement");
361
+ return value;
362
+ };
363
+ const row = displacement(","), column = displacement(","), sheet = displacement("]");
364
+ if (!relative && (row !== 0 || column !== 0) || source[offset++] !== ":" || source[offset] !== '"')
365
+ fail("Invalid live name");
366
+ const name = quoted('"', grammar.stringEscape);
367
+ if (!name)
368
+ fail("Empty live name");
369
+ options.onName?.(name);
370
+ return node({ kind: "name", name, workbook: "", relocation: { relative, row, column, sheet, ...(fixedSheet ? { sheetRelative: false } : {}) }, start, end: offset });
371
+ }
338
372
  if (c === "@" && !internalLabels)
339
373
  fail("Internal label reference in native formula");
340
374
  if (c === "@" && internalLabels) {
@@ -1,6 +1,6 @@
1
1
  import { SsconvertError } from "../contracts.js";
2
2
  import { foldSheetName } from "../../spreadsheet-ast/case-fold.js";
3
- import { serializeReference, serializeLabelReference, quoteFormulaString } from "./serialization.js";
3
+ import { serializeExpression, serializeReference, serializeLabelReference, quoteFormulaString } from "./serialization.js";
4
4
  export function visitFormula(node, visitor) {
5
5
  visitor(node);
6
6
  if (node.kind === "unary" || node.kind === "parentheses")
@@ -106,6 +106,24 @@ export function rewriteReferences(document, edit) {
106
106
  : serializeReference(first, last, document.grammar, position) });
107
107
  }
108
108
  }
109
+ else if (node.kind === "name" && node.relocation && edit.translation === "copy") {
110
+ const relocation = { ...node.relocation };
111
+ if (relocation.relative) {
112
+ relocation.row += target.row - document.position.row;
113
+ relocation.column += target.column - document.position.column;
114
+ }
115
+ if (relocation.sheetRelative !== false && target.sheet !== document.position.sheet) {
116
+ const origin = document.sheetOrder?.indexOf(document.position.sheet) ?? -1;
117
+ const destination = document.sheetOrder?.indexOf(target.sheet) ?? -1;
118
+ if (origin < 0 || destination < 0)
119
+ throw new SsconvertError("invalid-request", "Cross-sheet formula copy requires workbook tab order");
120
+ relocation.sheet += destination - origin;
121
+ }
122
+ if (![relocation.row, relocation.column, relocation.sheet].every(Number.isSafeInteger))
123
+ throw new SsconvertError("invalid-request", "Invalid live name displacement");
124
+ const text = serializeExpression({ ...document, root: { ...node, relocation } }, document.grammar, false).slice(document.grammar.prefixes[0]?.length ?? 0);
125
+ changes.push({ start: node.start, end: node.end, text });
126
+ }
109
127
  else if (node.kind === "name" && (node.workbook === undefined || node.workbook === "") && node.sheet && renamedSheet(node.sheet) !== undefined) {
110
128
  const text = quoteFormulaString(renamedSheet(node.sheet), "'", document.grammar) + document.grammar.sheetSeparator + node.name;
111
129
  changes.push({ start: node.start, end: node.end, text: document.grammar.bracketReferences ? "[" + text + "]" : text });
@@ -161,6 +161,12 @@ export function serializeExpression(document, grammar = document.grammar, preser
161
161
  case "reference": return value.label ? grammar.quotedLabels === "openformula" && options.quotedLabel
162
162
  ? quoteFormulaString(options.quotedLabel(value), "'", grammar) : serializeLabelReference(value, grammar, position) : serializeReference(options.relativeSheets === "fixed" ? { ...value.first, sheetRelative: false } : value.first, options.relativeSheets === "fixed" && value.last ? { ...value.last, sheetRelative: false } : value.last, grammar, position);
163
163
  case "name": {
164
+ if (value.relocation) {
165
+ if (grammar.id !== "gnumeric" && !grammar.internalNames)
166
+ throw new SsconvertError("unsupported-feature", "Unsupported ssconvert feature: live name relocation in target grammar");
167
+ const { relative, row, column, sheet } = value.relocation;
168
+ return `@name.${relative ? "relative" : "absolute"}${value.relocation.sheetRelative === false ? ".fixed-sheet" : ""}[${row},${column},${sheet}]:` + quoteFormulaString(value.name, '"', grammar);
169
+ }
164
170
  if (grammar.qualifiedNames === false && (value.sheet !== undefined || value.workbook !== undefined))
165
171
  throw new SsconvertError("unsupported-feature", "Unsupported ssconvert feature: qualified formula name in target grammar");
166
172
  if (value.workbook === "" && grammar.bracketReferences && value.sheet === undefined)
@@ -366,6 +366,8 @@ export class BiffFormulaWriter {
366
366
  push(first);
367
367
  }
368
368
  else if (node.kind === "name") {
369
+ if (node.relocation)
370
+ throw new SsconvertError("unsupported-feature", "Unsupported ssconvert feature: live name relocation in BIFF output");
369
371
  if (node.workbook) {
370
372
  const bookIndex = this.externalBook(node.workbook), book = this.externalBooks[bookIndex];
371
373
  const scope = node.sheet === undefined ? undefined : this.externalScope(bookIndex, node.sheet);
@@ -19,6 +19,8 @@ export function nativeOpenFormula(source, position, context, arrayStringLiterals
19
19
  throw new SsconvertError("unsupported-feature", "Unsupported ssconvert feature: automatic label intersection in Gnumeric output");
20
20
  if (node.kind === "reference" && node.label)
21
21
  throw new SsconvertError("unsupported-feature", "Unsupported ssconvert feature: live label reference in Gnumeric output");
22
+ if (node.kind === "name" && node.relocation)
23
+ throw new SsconvertError("unsupported-feature", "Unsupported ssconvert feature: live name relocation in Gnumeric output");
22
24
  });
23
25
  if (!source.startsWith("of:="))
24
26
  return source;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@poe-platform/safe-bash",
3
- "version": "0.1.756",
3
+ "version": "0.1.757",
4
4
  "description": "Extensible virtual shell with pluggable filesystems and streaming commands",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -837,7 +837,7 @@
837
837
  "access": "public"
838
838
  },
839
839
  "dependencies": {
840
- "@poe-platform/safe-fs": "0.1.756",
840
+ "@poe-platform/safe-fs": "0.1.757",
841
841
  "pdf-lib": "1.17.1",
842
842
  "@noble/hashes": "2.4.0",
843
843
  "pako": "3.0.1",