@ai-matrx/office 0.8.12 → 0.8.13

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/CHANGELOG.md CHANGED
@@ -1,11 +1,8 @@
1
1
  # Changelog
2
2
 
3
- ## 0.8.12
3
+ ## 0.8.13
4
4
 
5
- UNVERIFIED: this release has no Unreleased entry and the source comparison could not be made (the caller supplied no source comparison). Whether source changed is unknown; consumer action is unknown.
6
- Check the package's gitHead on npm against this version's gitHead: `git diff <previous gitHead> <this gitHead> -- apps/shared/office/src`.
7
-
8
- ## 0.8.11
5
+ - **`/collab/sheets-transform` covers formulas (C5b; still inert, no consumer).** A formula typed on one seat now has its references rewritten by a concurrent row/column insert, removal or move and by a concurrent sheet rename, in both commit orders, exactly as Univer's own `@univerjs/sheets-formula` interceptor rewrites them: A1 references, ranges, absolute and mixed markers, whole rows and columns, cross-sheet references, references into a removed range (`#REF!`), array formulas (`ref` range moves; dynamic arrays are plain formulas). Defined names (`formula.mutation.set-defined-name` / `remove-defined-name`) are covered: their ranges follow structural edits and renames, a renamed name is renamed inside concurrent formulas, a removed one becomes `#REF!`. A rewrite a structural op carries for formulas that already existed (`derived`) never outranks a person's concurrent write to the same cell or name. Shared formulas (`si`) stay barriers (the failing case is in `sheet-formula-hard-cases.test.ts`). Rewrites match real sheets-formula 1.0.4 on 476 oracle cases (`fixtures/sheets-formula-oracle.json`, made by `spikes/c5b-probe/oracle.mjs`). Pairwise TP1 fuzz: 20 families (210 pairs) at 4,000 seeds each, both orders, 0 divergences, 0 throws. New exports: `annotateFormulas`, `forApply`, `SIDE_CAR_PARAMS`, `rewriteFormula`, `formulaBarrier`, `isFormulaText`, `refSheetsFor`, `refSheetsAfter`, `REF_ERROR` and the `RefChange`/`RefSheets`/`FormulaCtx` types. `CONTENT_KEYS` gains `ref`, `xf`, `ft`, `fd`; `SHEET_TRANSFORMER_VERSION` is `c5b-1`. `@univerjs/engine-formula` moves from devDependencies to dependencies (the lexer runs in shipped code). Consumer action: none (nothing imports this section yet). For C6: capture must call `annotateFormulas` on each group (sheet id → name, defined-name id → name, read before the command) and apply through `forApply`; the canonical sheet hash must sort the defined-names resource by id.
9
6
 
10
7
  - Docs co-editing (C3): a collaborator's edit now moves this seat's caret with the characters (§6.8). The view port gains an optional `selection { get, set }`; `DocumentModel` moves body ranges through each applied remote edit (`moveRangesThroughBodyEdit`). Found in the live two-browser run: with the caret left at its old offset, the next keystrokes landed scattered. Consumer action: pass `selection` from Univer's `DocSelectionManagerService` in the view port.
11
8
 
@@ -22,15 +22,24 @@ var transform_exports = {};
22
22
  __export(transform_exports, {
23
23
  CONTENT_KEYS: () => CONTENT_KEYS,
24
24
  PAIR_BARRIERS: () => PAIR_BARRIERS,
25
+ REF_ERROR: () => REF_ERROR,
25
26
  SHEET_MUTATIONS: () => SHEET_MUTATIONS,
26
27
  SHEET_TRANSFORMER_VERSION: () => SHEET_TRANSFORMER_VERSION,
28
+ SIDE_CAR_PARAMS: () => SIDE_CAR_PARAMS,
27
29
  allowParams: () => allowParams,
30
+ annotateFormulas: () => annotateFormulas,
28
31
  cellRef: () => cellRef,
29
32
  completeCellContent: () => completeCellContent,
30
33
  exactMergeRemovals: () => exactMergeRemovals,
34
+ forApply: () => forApply,
35
+ formulaBarrier: () => formulaBarrier,
31
36
  isBarrier: () => isBarrier,
37
+ isFormulaText: () => isFormulaText,
32
38
  mapIndex: () => mapIndex,
33
39
  mergeThrough: () => mergeThrough,
40
+ refSheetsAfter: () => refSheetsAfter,
41
+ refSheetsFor: () => refSheetsFor,
42
+ rewriteFormula: () => rewriteFormula,
34
43
  sheetTransformer: () => sheetTransformer,
35
44
  transformAxis: () => transformAxis,
36
45
  transformGroup: () => transformGroup,
@@ -46,12 +55,11 @@ var covered = (family, ...allow) => ({ kind: "covered", family, allow: ["subUnit
46
55
  var never = (reason) => ({ kind: "never-travels", reason });
47
56
  var barrier = (until, reason) => ({ kind: "barrier", until, reason });
48
57
  var FORMULA_RESULT = "formula engine output; every seat computes it (dropped at capture)";
49
- var FORMULA = "formula state; C5b rewrites references";
50
58
  var RESOURCE = "ranged resource keyed by id; C5c";
51
59
  var UNIVER_COMMENTS = "Univer's own thread-comment store; our comments anchor elsewhere (design \xA78)";
52
60
  var SHEET_MUTATIONS = {
53
61
  // ── covered (C5a) ────────────────────────────────────────────────────────────────────────────────
54
- "sheet.mutation.set-range-values": covered("cell", "cellValue"),
62
+ "sheet.mutation.set-range-values": covered("cell", "cellValue", "refSheets", "derived"),
55
63
  "sheet.mutation.insert-row": covered("structure", "range", "rowInfo"),
56
64
  "sheet.mutation.remove-rows": covered("structure", "range"),
57
65
  "sheet.mutation.insert-col": covered("structure", "range", "colInfo"),
@@ -128,16 +136,16 @@ var SHEET_MUTATIONS = {
128
136
  "sheet.mutation.set-worksheet-protection": barrier("X", "permissions: not in C5a"),
129
137
  "sheet.mutation.delete-worksheet-protection": barrier("X", "permissions: not in C5a"),
130
138
  "sheet.mutation.set-worksheet-permission-points": barrier("X", "permissions: not in C5a"),
131
- // ── barriers until C5b (formulas) ────────────────────────────────────────────────────────────────
132
- "formula.mutation.set-defined-name": barrier("C5b", FORMULA),
133
- "formula.mutation.remove-defined-name": barrier("C5b", FORMULA),
134
- "formula.mutation.set-feature-calculation": barrier("C5b", FORMULA),
135
- "formula.mutation.remove-feature-calculation": barrier("C5b", FORMULA),
136
- "formula.mutation.set-other-formula": barrier("C5b", FORMULA),
137
- "formula.mutation.remove-other-formula": barrier("C5b", FORMULA),
138
- "formula.mutation.set-super-table": barrier("C5b", FORMULA),
139
- "formula.mutation.remove-super-table": barrier("C5b", FORMULA),
140
- "formula.mutation.set-super-table-option": barrier("C5b", FORMULA),
139
+ // ── formulas (C5b) ───────────────────────────────────────────────────────────────────────────────
140
+ "formula.mutation.set-defined-name": covered("defname", "id", "name", "formulaOrRefString", "formulaOrRefStringWithPrefix", "comment", "localSheetId", "hidden", "refSheets", "previousName", "derived"),
141
+ "formula.mutation.remove-defined-name": covered("defname", "id", "name"),
142
+ "formula.mutation.set-feature-calculation": barrier("X", "a plugin's registered calculation (feature id + dependency ranges); no transform yet"),
143
+ "formula.mutation.remove-feature-calculation": barrier("X", "a plugin's registered calculation; no transform yet"),
144
+ "formula.mutation.set-other-formula": barrier("C5c", "formulas owned by conditional formats and data validation travel with their rule"),
145
+ "formula.mutation.remove-other-formula": barrier("C5c", "formulas owned by conditional formats and data validation travel with their rule"),
146
+ "formula.mutation.set-super-table": barrier("C5c", "table names in structured references belong to tables (C5c)"),
147
+ "formula.mutation.remove-super-table": barrier("C5c", "table names in structured references belong to tables (C5c)"),
148
+ "formula.mutation.set-super-table-option": barrier("C5c", "table names in structured references belong to tables (C5c)"),
141
149
  // ── barriers until C5c (ranged resources) ────────────────────────────────────────────────────────
142
150
  "sheet.mutation.add-conditional-rule": barrier("C5c", RESOURCE),
143
151
  "sheet.mutation.set-conditional-rule": barrier("C5c", RESOURCE),
@@ -181,7 +189,7 @@ function allowParams(id, params) {
181
189
  }
182
190
 
183
191
  // src/collab/sheets/transform/transform.ts
184
- var import_sheets = require("@univerjs/sheets");
192
+ var import_sheets2 = require("@univerjs/sheets");
185
193
 
186
194
  // src/collab/sheets/transform/axis.ts
187
195
  function isNoopMove(op) {
@@ -294,8 +302,228 @@ function transformAxis(x, y, side) {
294
302
  }
295
303
  }
296
304
 
305
+ // src/collab/sheets/transform/formula.ts
306
+ var import_core = require("@univerjs/core");
307
+ var import_engine_formula = require("@univerjs/engine-formula");
308
+ var import_sheets = require("@univerjs/sheets");
309
+ var REF_ERROR = "#REF!";
310
+ var FULL = 16383 * 64;
311
+ var lexer = null;
312
+ var lex = (body) => (lexer ??= new import_engine_formula.LexerTreeBuilder()).sequenceNodesBuilder(body);
313
+ var isFormulaText = (f) => typeof f === "string" && f.length > 1 && f.startsWith("=");
314
+ var IDENT = /^[A-Za-z_\\][A-Za-z0-9_.\\]*$/;
315
+ var ERROR_TOKEN = /^(?:[^!]+!)?#(?:REF!|NAME\?|VALUE!|DIV\/0!|N\/A|NUM!|NULL!|SPILL!|CALC!)$/i;
316
+ function parse(text) {
317
+ const prefix = text.startsWith("=") ? "=" : "";
318
+ const nodes = lex(text.slice(prefix.length));
319
+ if (!nodes) return "the formula lexer could not read the text";
320
+ for (const n of nodes) {
321
+ if (typeof n === "string") continue;
322
+ if (n.nodeType === import_engine_formula.sequenceNodeType.TABLE || n.nodeType === import_engine_formula.sequenceNodeType.FUNCTION && n.token.includes("[")) return "a structured table reference (tables are C5c)";
323
+ if (n.nodeType === import_engine_formula.sequenceNodeType.FUNCTION && !IDENT.test(n.token) && !ERROR_TOKEN.test(n.token))
324
+ return `the lexer read "${n.token}" as one name (an intersection or a reference it cannot split)`;
325
+ if (n.nodeType === import_engine_formula.sequenceNodeType.REFERENCE) {
326
+ const d = (0, import_engine_formula.deserializeRangeWithSheet)(n.token);
327
+ if (d.unitId) return "a reference into another workbook";
328
+ }
329
+ }
330
+ return { prefix, nodes };
331
+ }
332
+ function targetSheet(sheetName, ctx) {
333
+ if (!sheetName) return ctx.ownSheet;
334
+ const id = ctx.refSheets?.[sheetName];
335
+ if (id === void 0) return void 0;
336
+ return id ?? "";
337
+ }
338
+ function formulaBarrier(text, ctx) {
339
+ const p = parse(text);
340
+ if (typeof p === "string") return p;
341
+ for (const n of p.nodes) {
342
+ if (typeof n === "string" || n.nodeType !== import_engine_formula.sequenceNodeType.REFERENCE) continue;
343
+ const { sheetName } = (0, import_engine_formula.deserializeRangeWithSheet)(n.token);
344
+ if (targetSheet(sheetName, ctx) === void 0) return `sheet "${sheetName}" is not in refSheets (capture must call annotateRefSheets)`;
345
+ }
346
+ return null;
347
+ }
348
+ function referencedSheetNames(text) {
349
+ const p = parse(text);
350
+ if (typeof p === "string") return [];
351
+ const out = /* @__PURE__ */ new Set();
352
+ for (const n of p.nodes) if (typeof n !== "string" && n.nodeType === import_engine_formula.sequenceNodeType.REFERENCE) {
353
+ const { sheetName } = (0, import_engine_formula.deserializeRangeWithSheet)(n.token);
354
+ if (sheetName) out.add(sheetName);
355
+ }
356
+ return [...out];
357
+ }
358
+ var fullRowsRange = (start, count) => ({ startRow: start, endRow: start + count - 1, startColumn: 0, endColumn: FULL, rangeType: import_core.RANGE_TYPE.ROW });
359
+ var fullColsRange = (start, count) => ({ startRow: 0, endRow: FULL, startColumn: start, endColumn: start + count - 1, rangeType: import_core.RANGE_TYPE.COLUMN });
360
+ var startV = (v) => Number.isNaN(v) || v == null ? -Infinity : v;
361
+ var endV = (v) => Number.isNaN(v) || v == null ? Infinity : v;
362
+ function startEnd(r) {
363
+ return { ...r, startRow: startV(r.startRow), endRow: endV(r.endRow), startColumn: startV(r.startColumn), endColumn: endV(r.endColumn) };
364
+ }
365
+ function checkMoveEdge(origin, from) {
366
+ const startRow = startV(origin.startRow), endRow = endV(origin.endRow), startColumn = startV(origin.startColumn), endColumn = endV(origin.endColumn);
367
+ const fromStartRow = startV(from.startRow), fromEndRow = endV(from.endRow), fromStartColumn = startV(from.startColumn), fromEndColumn = endV(from.endColumn);
368
+ const rowsCover = () => origin.rangeType === import_core.RANGE_TYPE.COLUMN && from.rangeType === import_core.RANGE_TYPE.COLUMN || startRow >= fromStartRow && endRow <= fromEndRow;
369
+ const columnsCover = () => origin.rangeType === import_core.RANGE_TYPE.ROW && from.rangeType === import_core.RANGE_TYPE.ROW || startColumn >= fromStartColumn && endColumn <= fromEndColumn;
370
+ const allCover = () => origin.rangeType === import_core.RANGE_TYPE.ALL && from.rangeType === import_core.RANGE_TYPE.ALL;
371
+ if (rowsCover() && columnsCover() || allCover()) return 4;
372
+ if (columnsCover() && startRow >= fromStartRow && startRow <= fromEndRow && endRow > fromEndRow) return 0;
373
+ if (columnsCover() && endRow >= fromStartRow && endRow <= fromEndRow && startRow < fromStartRow) return 1;
374
+ if (rowsCover() && startColumn >= fromStartColumn && startColumn <= fromEndColumn && endColumn > fromEndColumn) return 2;
375
+ if (rowsCover() && endColumn >= fromStartColumn && endColumn <= fromEndColumn && startColumn < fromStartColumn) return 3;
376
+ return null;
377
+ }
378
+ function getMoveNewRange(moveEdge, result, from, to, origin, remain) {
379
+ const { startRow, endRow, startColumn, endColumn, rangeType } = startEnd(result);
380
+ const f = startEnd(from);
381
+ const fromRangeType = from.rangeType ?? import_core.RANGE_TYPE.NORMAL;
382
+ const t = startEnd(to);
383
+ const rm = startEnd(remain);
384
+ const o = startEnd(origin);
385
+ const originRangeType = origin.rangeType ?? import_core.RANGE_TYPE.NORMAL;
386
+ const newRange = { ...origin };
387
+ const rowsCover = () => rangeType === import_core.RANGE_TYPE.COLUMN && originRangeType === import_core.RANGE_TYPE.COLUMN || startColumn >= o.startColumn && endColumn <= o.endColumn;
388
+ const columnsCover = () => rangeType === import_core.RANGE_TYPE.ROW && originRangeType === import_core.RANGE_TYPE.ROW || startRow >= o.startRow && endRow <= o.endRow;
389
+ if (moveEdge === 0) {
390
+ if (!rowsCover()) return void 0;
391
+ if (startRow < o.startRow) newRange.startRow = startRow;
392
+ else if (startRow >= o.endRow) newRange.endRow -= f.endRow + 1 - o.startRow;
393
+ else return void 0;
394
+ } else if (moveEdge === 1) {
395
+ if (!rowsCover()) return void 0;
396
+ if (endRow > o.endRow) newRange.endRow = endRow;
397
+ else if (endRow <= o.startRow) newRange.startRow += o.endRow - f.startRow + 1;
398
+ else return void 0;
399
+ } else if (moveEdge === 2) {
400
+ if (!columnsCover()) return void 0;
401
+ if (startColumn < o.startColumn) newRange.startColumn = startColumn;
402
+ else if (startColumn >= o.endColumn) newRange.endColumn -= f.endColumn + 1 - o.startColumn;
403
+ else return void 0;
404
+ } else if (moveEdge === 3) {
405
+ if (!columnsCover()) return void 0;
406
+ if (endColumn > o.endColumn) newRange.endColumn = endColumn;
407
+ else if (endColumn <= o.startColumn) newRange.startColumn += o.endColumn - f.startColumn + 1;
408
+ else return void 0;
409
+ } else if (moveEdge === 4) {
410
+ Object.assign(newRange, { startRow, startColumn, endRow, endColumn });
411
+ } else if (f.startColumn <= o.startColumn && f.endColumn >= o.endColumn || fromRangeType === import_core.RANGE_TYPE.ROW && originRangeType === import_core.RANGE_TYPE.ROW) {
412
+ const n = f.endRow - f.startRow + 1;
413
+ if (from.endRow < o.startRow) {
414
+ if (t.startRow >= o.startRow) newRange.startRow -= n;
415
+ if (t.startRow >= o.endRow) newRange.endRow -= n;
416
+ } else if (from.startRow > o.endRow) {
417
+ if (t.endRow <= o.endRow) newRange.endRow += n;
418
+ if (t.endRow <= o.startRow) newRange.startRow += n;
419
+ } else if (from.startRow >= o.startRow && from.endRow <= o.endRow) {
420
+ if (t.startRow <= o.startRow) newRange.startRow += n;
421
+ else if (t.startRow >= o.endRow) newRange.endRow -= n;
422
+ }
423
+ } else if (f.startRow <= o.startRow && f.endRow >= o.endRow || fromRangeType === import_core.RANGE_TYPE.COLUMN && originRangeType === import_core.RANGE_TYPE.COLUMN) {
424
+ const n = f.endColumn - f.startColumn + 1;
425
+ if (from.endColumn < o.startColumn) {
426
+ if (t.startColumn >= o.startColumn) newRange.startColumn -= n;
427
+ if (t.startColumn >= o.endColumn) newRange.endColumn -= n;
428
+ } else if (from.startColumn > o.endColumn) {
429
+ if (t.endColumn <= o.endColumn) newRange.endColumn += n;
430
+ if (t.endColumn <= o.startColumn) newRange.startColumn += n;
431
+ } else if (from.startColumn >= o.startColumn && from.endColumn <= o.endColumn) {
432
+ if (t.startColumn <= o.startColumn) newRange.startColumn += n;
433
+ else if (t.startColumn >= o.endColumn) newRange.endColumn -= n;
434
+ }
435
+ } else {
436
+ Object.assign(newRange, { startRow, startColumn, endRow, endColumn });
437
+ void rm;
438
+ }
439
+ return newRange;
440
+ }
441
+ function moveRange(range, axis, op) {
442
+ const isRow = axis === "row";
443
+ if (op.kind === "insert") {
444
+ const r = isRow ? fullRowsRange(op.at, op.count) : fullColsRange(op.at, op.count);
445
+ const ops2 = isRow ? (0, import_sheets.handleInsertRow)({ id: import_sheets.EffectRefRangId.InsertRowCommandId, params: { range: r, unitId: "", subUnitId: "", direction: import_core.Direction.DOWN } }, range) : (0, import_sheets.handleInsertCol)({ id: import_sheets.EffectRefRangId.InsertColCommandId, params: { range: r, unitId: "", subUnitId: "", direction: import_core.Direction.RIGHT } }, range);
446
+ const res2 = (0, import_sheets.runRefRangeMutations)(ops2, range);
447
+ return res2 == null ? void 0 : { ...range, ...res2 };
448
+ }
449
+ if (op.kind === "remove") {
450
+ const r = isRow ? fullRowsRange(op.start, op.count) : fullColsRange(op.start, op.count);
451
+ const ops2 = isRow ? (0, import_sheets.handleIRemoveRow)({ id: import_sheets.EffectRefRangId.RemoveRowCommandId, params: { range: r } }, range, []) : (0, import_sheets.handleIRemoveCol)({ id: import_sheets.EffectRefRangId.RemoveColCommandId, params: { range: r } }, range);
452
+ const res2 = (0, import_sheets.runRefRangeMutations)(ops2, range);
453
+ return res2 == null ? REF_ERROR : { ...range, ...res2 };
454
+ }
455
+ const from = isRow ? fullRowsRange(op.from, op.count) : fullColsRange(op.from, op.count);
456
+ const to = isRow ? fullRowsRange(op.to, op.count) : fullColsRange(op.to, op.count);
457
+ const moveEdge = checkMoveEdge(range, from);
458
+ let remain = (0, import_core.getIntersectRange)(range, from);
459
+ if (isRow) {
460
+ if (remain == null && (from.endRow < range.startRow && to.endRow <= range.startRow || from.startRow > range.endRow && to.startRow > range.endRow)) return void 0;
461
+ } else if (remain == null && (from.endColumn < range.startColumn && to.endColumn <= range.startColumn || from.startColumn > range.endColumn && to.startColumn > range.endColumn)) return void 0;
462
+ remain ??= { startRow: range.startRow, endRow: range.endRow, startColumn: range.startColumn, endColumn: range.endColumn, rangeType: import_core.RANGE_TYPE.NORMAL };
463
+ const ops = isRow ? (0, import_sheets.handleMoveRows)({ id: import_sheets.EffectRefRangId.MoveRowsCommandId, params: { toRange: to, fromRange: from } }, remain) : (0, import_sheets.handleMoveCols)({ id: import_sheets.EffectRefRangId.MoveColsCommandId, params: { toRange: to, fromRange: from } }, remain);
464
+ const res = (0, import_sheets.runRefRangeMutations)(ops, remain);
465
+ if (res == null) return REF_ERROR;
466
+ return getMoveNewRange(moveEdge, res, from, to, range, remain);
467
+ }
468
+ function rewriteFormula(text, ctx, change) {
469
+ const p = parse(text);
470
+ if (typeof p === "string") return p;
471
+ const nodes = p.nodes.slice();
472
+ const changedIds = [];
473
+ for (let i = 0; i < nodes.length; i++) {
474
+ const n = nodes[i];
475
+ if (typeof n === "string") continue;
476
+ if (change.kind === "defined-name") {
477
+ if ((n.nodeType === import_engine_formula.sequenceNodeType.DEFINED_NAME || n.nodeType === import_engine_formula.sequenceNodeType.FUNCTION) && n.token === change.from) {
478
+ nodes[i] = { ...n, token: change.to ?? REF_ERROR };
479
+ changedIds.push(i);
480
+ }
481
+ continue;
482
+ }
483
+ if (n.nodeType !== import_engine_formula.sequenceNodeType.REFERENCE) continue;
484
+ const { range, sheetName, unitId } = (0, import_engine_formula.deserializeRangeWithSheet)(n.token);
485
+ const target = targetSheet(sheetName, ctx);
486
+ if (target === void 0) return `sheet "${sheetName}" is not in refSheets (capture must call annotateRefSheets)`;
487
+ if (target !== change.sheet) continue;
488
+ let token;
489
+ if (change.kind === "rename") {
490
+ if (!sheetName) continue;
491
+ token = (0, import_engine_formula.serializeRangeToRefString)({ range, sheetName: change.name, unitId });
492
+ } else {
493
+ const moved = moveRange(import_core.Rectangle.moveOffset(range, 0, 0), change.axis, change.op);
494
+ if (moved === void 0) continue;
495
+ token = moved === REF_ERROR ? REF_ERROR : (0, import_engine_formula.serializeRangeToRefString)({ range: moved, sheetName: target === ctx.ownSheet && !ctx.keepQualifier ? "" : sheetName, unitId });
496
+ }
497
+ nodes[i] = { ...n, token };
498
+ changedIds.push(i);
499
+ }
500
+ if (!changedIds.length) return { text, changed: false };
501
+ for (let i = 0; i < nodes.length; i++) {
502
+ const n = nodes[i];
503
+ if (typeof n === "string" || n.nodeType !== import_engine_formula.sequenceNodeType.REFERENCE || changedIds.includes(i)) continue;
504
+ nodes[i] = { ...n, token: (0, import_engine_formula.serializeRangeToRefString)((0, import_engine_formula.deserializeRangeWithSheet)(n.token)) };
505
+ }
506
+ const out = p.prefix + (0, import_engine_formula.generateStringWithSequence)(nodes);
507
+ return { text: out, changed: out !== text };
508
+ }
509
+ function refSheetsAfter(refSheets, change) {
510
+ if (change.kind !== "rename" || !refSheets) return refSheets;
511
+ const entries = Object.entries(refSheets);
512
+ if (!entries.some(([name, id]) => id === change.sheet || name === change.name)) return refSheets;
513
+ const next = {};
514
+ for (const [name, id] of entries) if (id !== change.sheet && name !== change.name) next[name] = id;
515
+ next[change.name] = change.sheet;
516
+ return next;
517
+ }
518
+ function refSheetsFor(texts, sheets) {
519
+ const byName = new Map(Object.entries(sheets).map(([id, name]) => [name, id]));
520
+ const out = {};
521
+ for (const t of texts) for (const name of referencedSheetNames(t)) out[name] = byName.get(name) ?? null;
522
+ return Object.keys(out).length ? out : void 0;
523
+ }
524
+
297
525
  // src/collab/sheets/transform/transform.ts
298
- var SHEET_TRANSFORMER_VERSION = "c5a-1";
526
+ var SHEET_TRANSFORMER_VERSION = "c5b-1";
299
527
  var ID = {
300
528
  values: "sheet.mutation.set-range-values",
301
529
  insertRow: "sheet.mutation.insert-row",
@@ -310,7 +538,9 @@ var ID = {
310
538
  rowIsAuto: "sheet.mutation.set-worksheet-row-is-auto-height",
311
539
  rowAutoHeight: "sheet.mutation.set-worksheet-row-auto-height",
312
540
  colWidth: "sheet.mutation.set-worksheet-col-width",
313
- name: "sheet.mutation.set-worksheet-name"
541
+ name: "sheet.mutation.set-worksheet-name",
542
+ setName: "formula.mutation.set-defined-name",
543
+ removeName: "formula.mutation.remove-defined-name"
314
544
  };
315
545
  var PAIR_BARRIERS = {};
316
546
  var pairKey = (a, b) => a < b ? `${a}|${b}` : `${b}|${a}`;
@@ -375,9 +605,9 @@ function buildStructural(template, axis, op) {
375
605
  return { id: template.id, params: { ...p, range: { ...p.range, [a0]: start, [a1]: start + op.count - 1 } } };
376
606
  }
377
607
  function univerMutationInfo(axis, op) {
378
- const FULL = { startRow: 0, endRow: Number.MAX_SAFE_INTEGER, startColumn: 0, endColumn: Number.MAX_SAFE_INTEGER };
608
+ const FULL2 = { startRow: 0, endRow: Number.MAX_SAFE_INTEGER, startColumn: 0, endColumn: Number.MAX_SAFE_INTEGER };
379
609
  const start = op.kind === "insert" ? op.at : op.start;
380
- const range = axis === "row" ? { ...FULL, startRow: start, endRow: start + op.count - 1 } : { ...FULL, startColumn: start, endColumn: start + op.count - 1 };
610
+ const range = axis === "row" ? { ...FULL2, startRow: start, endRow: start + op.count - 1 } : { ...FULL2, startColumn: start, endColumn: start + op.count - 1 };
381
611
  const id = op.kind === "insert" ? axis === "row" ? ID.insertRow : ID.insertCol : axis === "row" ? ID.removeRows : ID.removeCol;
382
612
  return { id, params: { unitId: "", subUnitId: "", range } };
383
613
  }
@@ -390,11 +620,11 @@ function mergeThrough(s, m) {
390
620
  const op = s.op;
391
621
  if (op.kind === "insert") {
392
622
  const fixup = lo < op.at && op.at <= hi;
393
- return { range: (0, import_sheets.adjustRangeOnMutation)(m, univerMutationInfo(s.axis, op)), fixup };
623
+ return { range: (0, import_sheets2.adjustRangeOnMutation)(m, univerMutationInfo(s.axis, op)), fixup };
394
624
  }
395
625
  if (op.kind === "remove") {
396
626
  const e = op.start + op.count - 1;
397
- if (e < lo || op.start > hi) return { range: (0, import_sheets.adjustRangeOnMutation)(m, univerMutationInfo(s.axis, op)), fixup: false };
627
+ if (e < lo || op.start > hi) return { range: (0, import_sheets2.adjustRangeOnMutation)(m, univerMutationInfo(s.axis, op)), fixup: false };
398
628
  if (op.start <= lo && e >= hi) return { range: null, fixup: true };
399
629
  const r = { ...m };
400
630
  if (op.start >= lo && e <= hi) r[a1] = hi - op.count;
@@ -444,8 +674,8 @@ function exactMergeRemovals(group, merges) {
444
674
  var mergeRanges = (m) => m.params.ranges ?? [];
445
675
  var withRanges = (m, ranges) => ({ id: m.id, params: { ...m.params, ranges } });
446
676
  var mergeMutation = (id, sheet, ranges) => ({ id, params: { subUnitId: sheet, ranges } });
447
- var CONTENT_KEYS = ["v", "t", "p", "f", "si", "custom"];
448
- var CONTENT_TOUCH = ["v", "p", "f", "si", "custom"];
677
+ var CONTENT_KEYS = ["v", "t", "p", "f", "si", "custom", "ref", "xf", "ft", "fd"];
678
+ var CONTENT_TOUCH = ["v", "p", "f", "si", "custom", "ref", "xf", "ft", "fd"];
449
679
  var KNOWN_CELL_KEYS = /* @__PURE__ */ new Set([...CONTENT_KEYS, "s"]);
450
680
  function completeCellContent(entry, post) {
451
681
  if (entry === null) return null;
@@ -475,8 +705,10 @@ function cellBarrier(m) {
475
705
  forEachCell(cellsOf(m), (_r, _c, v) => {
476
706
  if (why || v === null) return;
477
707
  for (const k of Object.keys(v)) {
708
+ if (why) break;
478
709
  if (!KNOWN_CELL_KEYS.has(k)) why = `cell key "${k}" is not covered`;
479
- else if ((k === "f" || k === "si") && v[k] != null) why = "formula content (f/si) belongs to C5b";
710
+ else if (k === "si" && v.si != null) why = "shared formula (si): its text lives in a master cell this op does not carry";
711
+ else if (k === "f" && isFormulaText(v.f) || k === "ref" && typeof v.ref === "string") why = formulaBarrier(v[k], formulaCtx(m));
480
712
  }
481
713
  if (typeof v.s === "string") why = "a style id travels (style ids are seat-local; capture must send the style object)";
482
714
  if (!why && CONTENT_TOUCH.some((k) => k in v) && !CONTENT_KEYS.every((k) => k in v || k === "t" && v.v == null))
@@ -549,6 +781,9 @@ function transformCell(X, Y, side) {
549
781
  function transformCellsOverCells(x, y, side) {
550
782
  if (sheetOf(x) !== sheetOf(y)) return ok([x]);
551
783
  const ycells = cellsOf(y);
784
+ const xd = isDerived(x);
785
+ const yd = isDerived(y);
786
+ const eff = derivedSide(x, y, side);
552
787
  const passes = [{}];
553
788
  const lost = [];
554
789
  const dropped = [];
@@ -560,29 +795,218 @@ function transformCellsOverCells(x, y, side) {
560
795
  putCell(passes[0], r, c, X);
561
796
  return;
562
797
  }
563
- const res = transformCell(X, yRow[c] ?? null, side);
798
+ const res = transformCell(X, yRow[c] ?? null, eff);
564
799
  if (typeof res === "string") {
565
800
  why = res;
566
801
  return;
567
802
  }
568
803
  res.entries.forEach((e, i) => putCell(passes[i] ??= {}, r, c, e));
569
- if (res.xLost) dropped.push(cellRef(sheetOf(x), r, c));
570
- if (res.yLost) lost.push(cellRef(sheetOf(x), r, c));
804
+ if (res.xLost && !xd) dropped.push(cellRef(sheetOf(x), r, c));
805
+ if (res.yLost && !yd) lost.push(cellRef(sheetOf(x), r, c));
571
806
  });
572
807
  if (why) return barrier2(why);
573
808
  const op = passes.filter((p) => !isEmptyMatrix(p)).map((p) => cellsMutation(x, p));
574
809
  return ok(op, lost, dropped);
575
810
  }
576
- function transformCellsOverStructural(x, s) {
811
+ var SIDE_CAR_PARAMS = ["refSheets", "previousName", "derived"];
812
+ var isDerived = (m) => m.params.derived === true;
813
+ function derivedSide(x, y, side) {
814
+ const xd = isDerived(x);
815
+ const yd = isDerived(y);
816
+ return xd === yd ? side : xd ? "right" : "left";
817
+ }
818
+ function forApply(m) {
819
+ if (!SIDE_CAR_PARAMS.some((k) => k in m.params)) return m;
820
+ const params = { ...m.params };
821
+ for (const k of SIDE_CAR_PARAMS) delete params[k];
822
+ return { id: m.id, params };
823
+ }
824
+ var refSheetsOf = (m) => m.params.refSheets;
825
+ var formulaCtx = (m) => ({ ownSheet: sheetOf(m), refSheets: refSheetsOf(m) });
826
+ function withRefSheets(params, rs) {
827
+ const out = { ...params };
828
+ if (rs) out.refSheets = rs;
829
+ else delete out.refSheets;
830
+ return out;
831
+ }
832
+ var hasFormulaText = (v) => !!v && (isFormulaText(v.f) || typeof v.ref === "string");
833
+ function hasFormula(m) {
834
+ let any = false;
835
+ forEachCell(cellsOf(m), (_r, _c, v) => {
836
+ any ||= hasFormulaText(v);
837
+ });
838
+ return any;
839
+ }
840
+ var setNameWithoutPrevious = (m) => m.id === ID.setName && !("previousName" in m.params);
841
+ function refChangeOf(m) {
842
+ const st = structuralOf(m);
843
+ if (st) return { kind: "axis", sheet: sheetOf(m), axis: st.axis, op: st.op };
844
+ if (m.id === ID.name) return { kind: "rename", sheet: sheetOf(m), name: String(m.params.name) };
845
+ if (m.id === ID.removeName) return { kind: "defined-name", from: String(m.params.name), to: null };
846
+ if (m.id === ID.setName && typeof m.params.previousName === "string" && m.params.previousName !== m.params.name)
847
+ return { kind: "defined-name", from: m.params.previousName, to: String(m.params.name) };
848
+ return null;
849
+ }
850
+ function rewriteEntry(v, ctx, change) {
851
+ let entry = v;
852
+ let changed = false;
853
+ for (const k of ["f", "ref"]) {
854
+ const t = v[k];
855
+ if (k === "f" ? !isFormulaText(t) : typeof t !== "string") continue;
856
+ const r = rewriteFormula(t, ctx, change);
857
+ if (typeof r === "string") return r;
858
+ if (!r.changed) continue;
859
+ if (k === "ref" && r.text.includes(REF_ERROR)) return "an array formula's range was removed under it";
860
+ entry = { ...entry, [k]: r.text };
861
+ changed = true;
862
+ }
863
+ return { entry, changed };
864
+ }
865
+ function cellThrough(sheet, change, r, c) {
866
+ if (change.kind !== "axis" || change.sheet !== sheet) return [r, c];
867
+ const nr = change.axis === "row" ? mapIndex(change.op, r) : r;
868
+ const nc = change.axis === "col" ? mapIndex(change.op, c) : c;
869
+ return nr === null || nc === null ? null : [nr, nc];
870
+ }
871
+ function cellsOverChange(x, change) {
872
+ const ctx = formulaCtx(x);
577
873
  const out = {};
578
874
  const dropped = [];
875
+ let why = null;
579
876
  forEachCell(cellsOf(x), (r, c, v) => {
580
- const nr = s.axis === "row" ? mapIndex(s.op, r) : r;
581
- const nc = s.axis === "col" ? mapIndex(s.op, c) : c;
582
- if (nr === null || nc === null) dropped.push(cellRef(sheetOf(x), r, c));
583
- else putCell(out, nr, nc, v);
877
+ if (why) return;
878
+ const at = cellThrough(sheetOf(x), change, r, c);
879
+ if (!at) {
880
+ dropped.push(cellRef(sheetOf(x), r, c));
881
+ return;
882
+ }
883
+ let e = v;
884
+ if (hasFormulaText(v)) {
885
+ const res = rewriteEntry(v, ctx, change);
886
+ if (typeof res === "string") {
887
+ why = res;
888
+ return;
889
+ }
890
+ e = res.entry;
891
+ }
892
+ putCell(out, at[0], at[1], e);
893
+ });
894
+ if (why) return barrier2(why);
895
+ const params = { ...withRefSheets(x.params, refSheetsAfter(ctx.refSheets, change)), cellValue: out };
896
+ return ok(isEmptyMatrix(out) ? [] : [{ id: x.id, params }], [], dropped);
897
+ }
898
+ function rewritesOf(y, change) {
899
+ const ctx = formulaCtx(y);
900
+ const out = {};
901
+ let why = null;
902
+ forEachCell(cellsOf(y), (r, c, v) => {
903
+ if (why || !hasFormulaText(v)) return;
904
+ const at = cellThrough(sheetOf(y), change, r, c);
905
+ if (!at) return;
906
+ const res = rewriteEntry(v, ctx, change);
907
+ if (typeof res === "string") {
908
+ why = res;
909
+ return;
910
+ }
911
+ if (res.changed) putCell(out, at[0], at[1], contentOf(res.entry));
912
+ });
913
+ if (why) return why;
914
+ if (isEmptyMatrix(out)) return null;
915
+ return { id: ID.values, params: withRefSheets({ subUnitId: sheetOf(y), cellValue: out, derived: true }, refSheetsAfter(ctx.refSheets, change)) };
916
+ }
917
+ var NAME_TEXT_KEYS = ["formulaOrRefString", "formulaOrRefStringWithPrefix"];
918
+ function definedNameThrough(m, change) {
919
+ if (change.kind === "defined-name" || m.id !== ID.setName) return { op: m, changed: false };
920
+ const ctx = { ownSheet: change.sheet, refSheets: refSheetsOf(m), keepQualifier: true };
921
+ const params = { ...m.params };
922
+ let changed = false;
923
+ for (const k of NAME_TEXT_KEYS) {
924
+ if (typeof params[k] !== "string" || !params[k]) continue;
925
+ const r = rewriteFormula(params[k], ctx, change);
926
+ if (typeof r === "string") return r;
927
+ if (r.changed) {
928
+ params[k] = r.text;
929
+ changed = true;
930
+ }
931
+ }
932
+ return { op: { id: m.id, params: withRefSheets(params, refSheetsAfter(ctx.refSheets, change)) }, changed };
933
+ }
934
+ function asDerived(m) {
935
+ return { id: m.id, params: { ...m.params, derived: true } };
936
+ }
937
+ function formulaPair(x, y, side) {
938
+ const fx = familyOf(x.id);
939
+ const fy = familyOf(y.id);
940
+ const SET_NAME_BARRIER = "set-defined-name without previousName (capture must record the name it replaces) against formula text";
941
+ if (fx === "defname" && fy === "defname") {
942
+ if (String(x.params.id) !== String(y.params.id)) return ok([x]);
943
+ const eff = derivedSide(x, y, side);
944
+ if (eff === "left") return ok([x], isDerived(y) ? [] : [`defined name "${String(y.params.name)}"`]);
945
+ return ok([], [], isDerived(x) ? [] : [`defined name "${String(x.params.name)}"`]);
946
+ }
947
+ if (fx === "defname") {
948
+ if (fy === "cell") {
949
+ if (setNameWithoutPrevious(x) && hasFormula(y)) return barrier2(SET_NAME_BARRIER);
950
+ const ch2 = refChangeOf(x);
951
+ if (!ch2) return ok([x]);
952
+ const add = rewritesOf(y, ch2);
953
+ if (typeof add === "string") return barrier2(add);
954
+ return ok(add ? [x, add] : [x]);
955
+ }
956
+ const ch = refChangeOf(y);
957
+ if (!ch) return ok([x]);
958
+ const r = definedNameThrough(x, ch);
959
+ return typeof r === "string" ? barrier2(r) : ok([r.op]);
960
+ }
961
+ if (fy === "defname") {
962
+ if (fx === "cell") {
963
+ if (setNameWithoutPrevious(y) && hasFormula(x)) return barrier2(SET_NAME_BARRIER);
964
+ const ch2 = refChangeOf(y);
965
+ return ch2 ? cellsOverChange(x, ch2) : ok([x]);
966
+ }
967
+ const ch = refChangeOf(x);
968
+ if (!ch) return ok([x]);
969
+ const r = definedNameThrough(y, ch);
970
+ if (typeof r === "string") return barrier2(r);
971
+ return ok(r.changed ? [x, asDerived(r.op)] : [x]);
972
+ }
973
+ if (fx === "cell") {
974
+ const ch = refChangeOf(y);
975
+ return ch ? cellsOverChange(x, ch) : null;
976
+ }
977
+ if (fy === "cell") {
978
+ const ch = refChangeOf(x);
979
+ if (!ch) return null;
980
+ const lost = [];
981
+ forEachCell(cellsOf(y), (r, c) => {
982
+ if (!cellThrough(sheetOf(y), ch, r, c)) lost.push(cellRef(sheetOf(y), r, c));
983
+ });
984
+ const add = rewritesOf(y, ch);
985
+ if (typeof add === "string") return barrier2(add);
986
+ return ok(add ? [x, add] : [x], lost);
987
+ }
988
+ return null;
989
+ }
990
+ var withPrevious = (m, definedNames) => m.id === ID.setName && !("previousName" in m.params) ? { id: m.id, params: { ...m.params, previousName: definedNames[String(m.params.id)] ?? null } } : m;
991
+ function annotateFormulas(group, sheets, definedNames) {
992
+ let changed = false;
993
+ return group.map((m0) => {
994
+ const m = changed && (m0.id === ID.values || m0.id === ID.setName) ? asDerived(m0) : m0;
995
+ if (!changed && refChangeOf(withPrevious(m0, definedNames))) changed = true;
996
+ if (m.id === ID.values) {
997
+ const texts = [];
998
+ forEachCell(cellsOf(m), (_r, _c, v) => {
999
+ if (v && isFormulaText(v.f)) texts.push(v.f);
1000
+ if (v && typeof v.ref === "string") texts.push(v.ref);
1001
+ });
1002
+ return { id: m.id, params: withRefSheets(m.params, refSheetsFor(texts, sheets)) };
1003
+ }
1004
+ if (m.id === ID.setName) {
1005
+ const texts = NAME_TEXT_KEYS.map((k) => m.params[k]).filter((t) => typeof t === "string");
1006
+ return { id: m.id, params: { ...withRefSheets(m.params, refSheetsFor(texts, sheets)), previousName: withPrevious(m, definedNames).params.previousName } };
1007
+ }
1008
+ return m;
584
1009
  });
585
- return ok(isEmptyMatrix(out) ? [] : [cellsMutation(x, out)], [], dropped);
586
1010
  }
587
1011
  function sizeOf(m) {
588
1012
  const p = m.params;
@@ -633,6 +1057,8 @@ function transformMutation(x, y, side) {
633
1057
  }
634
1058
  const pb = PAIR_BARRIERS[pairKey(x.id, y.id)];
635
1059
  if (pb) return barrier2(`${x.id} \xD7 ${y.id}: ${pb.reason}${pb.seed === null ? "" : ` (fuzz seed ${pb.seed})`}`);
1060
+ const fp = formulaPair(x, y, side);
1061
+ if (fp) return fp;
636
1062
  if (sheetOf(x) !== sheetOf(y)) return ok([x]);
637
1063
  const fx = familyOf(x.id);
638
1064
  const fy = familyOf(y.id);
@@ -646,7 +1072,6 @@ function transformMutation(x, y, side) {
646
1072
  return ok(out.map((o) => buildStructural(x, sx.axis, o)), [], dropped);
647
1073
  }
648
1074
  if (sy) {
649
- if (fx === "cell") return transformCellsOverStructural(x, sy);
650
1075
  if (fx === "merge") {
651
1076
  const kept = [];
652
1077
  const dropped = [];
@@ -671,13 +1096,6 @@ function transformMutation(x, y, side) {
671
1096
  return ok([x]);
672
1097
  }
673
1098
  if (sx) {
674
- if (fy === "cell") {
675
- const lost = [];
676
- forEachCell(cellsOf(y), (r, c) => {
677
- if ((sx.axis === "row" ? mapIndex(sx.op, r) : mapIndex(sx.op, c)) === null) lost.push(cellRef(sheetOf(y), r, c));
678
- });
679
- return ok([x], lost);
680
- }
681
1099
  if (fy !== "merge" || y.id !== ID.addMerge) return ok([x]);
682
1100
  const remove = [];
683
1101
  const add = [];