@univerjs-pro/engine-formula-rust 1.0.0-insiders.20260808-70eefdd → 1.0.0-rc.0

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/lib/cjs/index.cjs CHANGED
@@ -246,7 +246,7 @@ var import_core2 = require("@univerjs/core");
246
246
 
247
247
  // src/services/rust-formula-projection.service.ts
248
248
  var import_core = require("@univerjs/core");
249
- var XLSX_WORKBOOK_CALCULATION_RESOURCE = "UNIVER_CLI_XLSX_WORKBOOK_CALCULATION_PLUGIN", EXTERNAL_DATA_RESOURCE = "SHEET_EXTERNAL_DATA_PLUGIN", EXTERNAL_DATA_SIGNATURE = "external_data", RUST_UPSERT_REFERENCE_DATA_MUTATION_ID = "formula.mutation.upsert-reference-data", RUST_UPSERT_PROJECTION_WORKBOOK_MUTATION_ID = "formula.mutation.upsert-projection-workbook", RUST_FORMULA_RELEVANT_MUTATIONS = /* @__PURE__ */ new Set([
249
+ var XLSX_WORKBOOK_CALCULATION_RESOURCE = "UNIVER_CLI_XLSX_WORKBOOK_CALCULATION_PLUGIN", EXTERNAL_DATA_RESOURCE = "SHEET_EXTERNAL_DATA_PLUGIN", EXTERNAL_DATA_SIGNATURE = "external_data", RUST_UPSERT_REFERENCE_DATA_MUTATION_ID = "formula.mutation.upsert-reference-data", RUST_UPSERT_PROJECTION_WORKBOOK_MUTATION_ID = "formula.mutation.upsert-projection-workbook", RUST_SET_SUPER_TABLES_MUTATION_ID = "formula.mutation.set-super-tables", RUST_FORMULA_RELEVANT_MUTATIONS = /* @__PURE__ */ new Set([
250
250
  "sheet.mutation.set-range-values",
251
251
  "sheet.mutation.insert-row",
252
252
  "sheet.mutation.insert-col",
@@ -273,6 +273,7 @@ var XLSX_WORKBOOK_CALCULATION_RESOURCE = "UNIVER_CLI_XLSX_WORKBOOK_CALCULATION_P
273
273
  "formula.mutation.set-image-formula-data",
274
274
  "formula.mutation.set-other-formula",
275
275
  "formula.mutation.set-super-table",
276
+ RUST_SET_SUPER_TABLES_MUTATION_ID,
276
277
  "formula.mutation.set-super-table-option",
277
278
  "formula.mutation.remove-defined-name",
278
279
  "formula.mutation.remove-feature-calculation",
@@ -508,13 +509,26 @@ var RustFormulaProjectionService = class {
508
509
  }
509
510
  };
510
511
  function normalizeRustEngineMutation(mutation) {
511
- let params = mutation.id === "formula.mutation.set-super-table" ? normalizeSetSuperTableMutationParams(mutation.params) : mutation.params ?? {};
512
+ let params = mutation.id === "formula.mutation.set-super-table" ? normalizeSetSuperTableMutationParams(mutation.params) : mutation.id === RUST_SET_SUPER_TABLES_MUTATION_ID ? normalizeSetSuperTablesMutationParams(mutation.params) : mutation.params ?? {};
512
513
  return {
513
514
  id: mutation.id,
514
515
  params,
515
516
  dirtyRanges: Array.isArray(mutation.dirtyRanges) ? mutation.dirtyRanges : []
516
517
  };
517
518
  }
519
+ function normalizeSetSuperTablesMutationParams(params) {
520
+ return !isRecord(params) || typeof params.unitId != "string" || !Array.isArray(params.tables) ? params ?? {} : {
521
+ ...params,
522
+ tables: params.tables.map((entry) => {
523
+ let normalized = normalizeSetSuperTableMutationParams(
524
+ isRecord(entry) ? { ...entry, unitId: params.unitId } : entry
525
+ );
526
+ if (!isRecord(normalized)) return normalized;
527
+ let { unitId: _unitId, ...table } = normalized;
528
+ return table;
529
+ })
530
+ };
531
+ }
518
532
  function normalizeSetSuperTableMutationParams(params) {
519
533
  if (!isRecord(params) || !isRecord(params.reference))
520
534
  return params ?? {};
@@ -1082,6 +1096,7 @@ var RustEngineSyncController = class extends import_core2.Disposable {
1082
1096
  _nativeReady = !1;
1083
1097
  _snapshotLoaded = !1;
1084
1098
  _projectionLoadPromise = null;
1099
+ _projectionDeltaBarrier = Promise.resolve();
1085
1100
  _pendingMutations = [];
1086
1101
  _projectionManagedTableUnits = /* @__PURE__ */ new Set();
1087
1102
  _initialize() {
@@ -1102,6 +1117,13 @@ var RustEngineSyncController = class extends import_core2.Disposable {
1102
1117
  }
1103
1118
  async waitForProjectionLoad() {
1104
1119
  await this._projectionLoadPromise;
1120
+ let barrier;
1121
+ do
1122
+ barrier = this._projectionDeltaBarrier, await barrier;
1123
+ while (barrier !== this._projectionDeltaBarrier);
1124
+ }
1125
+ setProjectionDeltaBarrier(barrier) {
1126
+ this._projectionDeltaBarrier = barrier;
1105
1127
  }
1106
1128
  async _loadProjection(workbooks) {
1107
1129
  await loadNativeBinding(), this._nativeReady = !0;
@@ -1133,7 +1155,7 @@ var RustEngineSyncController = class extends import_core2.Disposable {
1133
1155
  }
1134
1156
  }
1135
1157
  dispose() {
1136
- if (super.dispose(), this._pendingMutations.length = 0, this._projectionManagedTableUnits.clear(), this._projectionLoadPromise = null, !this._nativeReady) {
1158
+ if (super.dispose(), this._pendingMutations.length = 0, this._projectionManagedTableUnits.clear(), this._projectionLoadPromise = null, this._projectionDeltaBarrier = Promise.resolve(), !this._nativeReady) {
1137
1159
  this._snapshotLoaded = !1;
1138
1160
  return;
1139
1161
  }
@@ -1267,7 +1289,7 @@ async function analyzeNumberFormatBatch(input) {
1267
1289
  var import_core5 = require("@univerjs/core"), import_engine_formula4 = require("@univerjs/engine-formula"), import_engine_formula5 = require("@univerjs-pro/engine-formula");
1268
1290
 
1269
1291
  // package.json
1270
- var name = "@univerjs-pro/engine-formula-rust", version = "1.0.0-insiders.20260808-70eefdd";
1292
+ var name = "@univerjs-pro/engine-formula-rust", version = "1.0.0-rc.0";
1271
1293
 
1272
1294
  // src/controllers/rust-same-runtime-projection.controller.ts
1273
1295
  var import_core3 = require("@univerjs/core"), import_engine_formula = require("@univerjs/engine-formula");
@@ -1290,11 +1312,12 @@ var RustSameRuntimeProjectionController = class extends import_core3.Disposable
1290
1312
  _activeDirtyManagerService;
1291
1313
  _initialProjectionLoad = Promise.resolve();
1292
1314
  _projectionDeltaQueue = Promise.resolve();
1315
+ _baseProjectionStructures = /* @__PURE__ */ new Map();
1293
1316
  _initialize() {
1294
1317
  for (let base of this._univerInstanceService.getAllUnitsForType(
1295
1318
  import_core3.UniverInstanceType.UNIVER_BASE
1296
1319
  ))
1297
- this._rustEngineSyncController.setProjectionManagedTableUnit(base.getUnitId());
1320
+ this._rustEngineSyncController.setProjectionManagedTableUnit(base.getUnitId()), this._rememberBaseProjectionStructure(base.getSnapshot());
1298
1321
  this._initialProjectionLoad = this._loadAllWorkbookProjections(), this.disposeWithMe(
1299
1322
  (0, import_core3.toDisposable)(
1300
1323
  this._univerInstanceService.getTypeOfUnitAdded$(import_core3.UniverInstanceType.UNIVER_SHEET).subscribe(({ unit }) => {
@@ -1309,7 +1332,7 @@ var RustSameRuntimeProjectionController = class extends import_core3.Disposable
1309
1332
  ), this.disposeWithMe(
1310
1333
  (0, import_core3.toDisposable)(
1311
1334
  this._univerInstanceService.getTypeOfUnitAdded$(import_core3.UniverInstanceType.UNIVER_BASE).subscribe(({ unit }) => {
1312
- this._rustEngineSyncController.setProjectionManagedTableUnit(unit.getUnitId()), this._enqueueProjectionDelta(() => this._upsertBaseProjection(unit.getUnitId()));
1335
+ this._rustEngineSyncController.setProjectionManagedTableUnit(unit.getUnitId()), this._rememberBaseProjectionStructure(unit.getSnapshot()), this._enqueueProjectionDelta(() => this._upsertBaseProjection(unit.getUnitId()));
1313
1336
  })
1314
1337
  )
1315
1338
  ), this.disposeWithMe(
@@ -1319,7 +1342,7 @@ var RustSameRuntimeProjectionController = class extends import_core3.Disposable
1319
1342
  ), this.disposeWithMe(
1320
1343
  (0, import_core3.toDisposable)(
1321
1344
  this._univerInstanceService.getTypeOfUnitDisposed$(import_core3.UniverInstanceType.UNIVER_BASE).subscribe((base) => {
1322
- this._rustEngineSyncController.unsetProjectionManagedTableUnit(base.getUnitId()), this._enqueueProjectionDelta(
1345
+ this._baseProjectionStructures.delete(base.getUnitId()), this._rustEngineSyncController.unsetProjectionManagedTableUnit(base.getUnitId()), this._enqueueProjectionDelta(
1323
1346
  () => this._rustEngineSyncController.disposeProjectionWorkbook(base.getUnitId())
1324
1347
  );
1325
1348
  })
@@ -1343,7 +1366,7 @@ var RustSameRuntimeProjectionController = class extends import_core3.Disposable
1343
1366
  if (!this._disposed)
1344
1367
  return task();
1345
1368
  }).catch(() => {
1346
- });
1369
+ }), this._rustEngineSyncController.setProjectionDeltaBarrier(this._projectionDeltaQueue);
1347
1370
  }
1348
1371
  async _syncBaseMutation(command) {
1349
1372
  if (!hasUnitId2(command.params)) return;
@@ -1352,7 +1375,65 @@ var RustSameRuntimeProjectionController = class extends import_core3.Disposable
1352
1375
  this._applyBaseDirtyRanges(unitId, dirtyRanges);
1353
1376
  return;
1354
1377
  }
1355
- await this._upsertBaseProjection(unitId);
1378
+ hasStructureInvalidation && this._isBaseProjectionStructureUnchanged(unitId) || hasStructureInvalidation && this._applyBaseFieldInsertion(unitId, dirtyRanges) || await this._upsertBaseProjection(unitId);
1379
+ }
1380
+ _applyBaseFieldInsertion(unitId, dirtyRanges) {
1381
+ let base = this._univerInstanceService.getUnit(unitId, import_core3.UniverInstanceType.UNIVER_BASE), previous = this._baseProjectionStructures.get(unitId);
1382
+ if (!base || !previous) return !1;
1383
+ let snapshot = base.getSnapshot(), insertion = findSingleBaseFieldInsertion(previous, snapshot);
1384
+ if (!insertion) return !1;
1385
+ let projectionData = this._getBaseProjectionData(unitId), sheet = projectionData?.calculationData.allUnitData[unitId]?.[insertion.tableId];
1386
+ if (!projectionData || !sheet) return !1;
1387
+ let formulaData = projectionData.formulaData[unitId]?.[insertion.tableId], cellValue = {};
1388
+ for (let row = 0; row < sheet.rowCount; row++) {
1389
+ let value = sheet.cellData.getValue(row, insertion.column), formula = formulaData?.[row]?.[insertion.column];
1390
+ cellValue[row] = {
1391
+ [insertion.column]: formula ? { ...value, ...formula } : value ?? null
1392
+ };
1393
+ }
1394
+ let insertedRange = {
1395
+ startRow: 0,
1396
+ endRow: Math.max(0, sheet.rowCount - 1),
1397
+ startColumn: insertion.column,
1398
+ endColumn: insertion.column
1399
+ };
1400
+ return this._rustEngineSyncController.applyProjectionMutation({
1401
+ id: "sheet.mutation.insert-col",
1402
+ params: {
1403
+ unitId,
1404
+ subUnitId: insertion.tableId,
1405
+ range: insertedRange
1406
+ },
1407
+ dirtyRanges: []
1408
+ }), this._rustEngineSyncController.applyProjectionMutation({
1409
+ id: "sheet.mutation.set-range-values",
1410
+ params: {
1411
+ unitId,
1412
+ subUnitId: insertion.tableId,
1413
+ cellValue
1414
+ },
1415
+ dirtyRanges: [
1416
+ {
1417
+ unitId,
1418
+ sheetId: insertion.tableId,
1419
+ range: insertedRange
1420
+ }
1421
+ ]
1422
+ }), this._rustEngineSyncController.applyProjectionMutation({
1423
+ id: RUST_SET_SUPER_TABLES_MUTATION_ID,
1424
+ params: {
1425
+ unitId,
1426
+ tables: this._getBaseTableEntries(
1427
+ insertion.tableId,
1428
+ snapshot,
1429
+ (0, import_core3.createBaseFormulaTableNameMap)(snapshot)
1430
+ ).map(([tableName, reference]) => ({
1431
+ tableName,
1432
+ reference
1433
+ }))
1434
+ },
1435
+ dirtyRanges: []
1436
+ }), dirtyRanges.length > 0 && this._applyBaseDirtyRanges(unitId, dirtyRanges), this._rememberBaseProjectionStructure(snapshot), !0;
1356
1437
  }
1357
1438
  _applyBaseDirtyRanges(unitId, dirtyRanges) {
1358
1439
  let projectionData = this._getBaseProjectionData(unitId);
@@ -1394,6 +1475,15 @@ var RustSameRuntimeProjectionController = class extends import_core3.Disposable
1394
1475
  formulaData
1395
1476
  );
1396
1477
  workbook && this._rustEngineSyncController.upsertProjectionWorkbook(workbook);
1478
+ let base = this._univerInstanceService.getUnit(unitId, import_core3.UniverInstanceType.UNIVER_BASE);
1479
+ base && this._rememberBaseProjectionStructure(base.getSnapshot());
1480
+ }
1481
+ _rememberBaseProjectionStructure(snapshot) {
1482
+ this._baseProjectionStructures.set(snapshot.id, createBaseProjectionStructure(snapshot));
1483
+ }
1484
+ _isBaseProjectionStructureUnchanged(unitId) {
1485
+ let base = this._univerInstanceService.getUnit(unitId, import_core3.UniverInstanceType.UNIVER_BASE), previous = this._baseProjectionStructures.get(unitId);
1486
+ return !!(base && previous && isSameBaseProjectionStructure(previous, createBaseProjectionStructure(base.getSnapshot())));
1397
1487
  }
1398
1488
  _getBaseProjectionData(unitId) {
1399
1489
  let base = this._univerInstanceService.getUnit(unitId, import_core3.UniverInstanceType.UNIVER_BASE);
@@ -1403,9 +1493,9 @@ var RustSameRuntimeProjectionController = class extends import_core3.Disposable
1403
1493
  name: snapshot.name,
1404
1494
  unitType: import_core3.UniverInstanceType.UNIVER_BASE
1405
1495
  }
1406
- }, formulaData = { [unitId]: {} };
1496
+ }, formulaData = { [unitId]: {} }, formulaTableNames = (0, import_core3.createBaseFormulaTableNameMap)(snapshot), normalizeFormulaTableReferences = (0, import_core3.createBaseFormulaTableReferenceNormalizer)(snapshot, formulaTableNames);
1407
1497
  for (let table of Object.values(snapshot.tables)) {
1408
- let fieldOrder = table.fieldOrder.filter((fieldId) => table.fields[fieldId] != null);
1498
+ let fieldOrder = table.fieldOrder;
1409
1499
  allUnitData[unitId][table.id] = {
1410
1500
  cellData: new import_core3.ObjectMatrix(
1411
1501
  buildBaseRuntimeCellData(table)
@@ -1414,18 +1504,25 @@ var RustSameRuntimeProjectionController = class extends import_core3.Disposable
1414
1504
  columnCount: fieldOrder.length,
1415
1505
  rowData: {},
1416
1506
  columnData: {}
1417
- }, unitSheetNameMap[unitId][table.name] = table.id;
1418
- let tableFormulaData = {}, recordOrder = baseRecordOrder(table);
1507
+ }, unitSheetNameMap[unitId][formulaTableNames.get(table.id)] = table.id;
1508
+ let tableFormulaData = {}, normalizedFormulaByFieldId = /* @__PURE__ */ new Map(), recordOrder = baseRecordOrder(table);
1419
1509
  for (let row = 0; row < recordOrder.length; row++)
1420
1510
  if (table.records[recordOrder[row]])
1421
1511
  for (let column = 0; column < fieldOrder.length; column++) {
1422
1512
  let field = table.fields[fieldOrder[column]];
1423
1513
  if (!field || field.type !== "formula") continue;
1424
1514
  let formula = String(field.config?.formula ?? "").trim();
1425
- formula && ((tableFormulaData[row] ??= {})[column] = {
1426
- f: normalizeBaseFormulaForEngine(formula, table, snapshot),
1515
+ if (!formula) continue;
1516
+ let normalizedFormula = normalizedFormulaByFieldId.get(field.id);
1517
+ normalizedFormula == null && (normalizedFormula = normalizeBaseFormulaForEngine(
1518
+ formula,
1519
+ table,
1520
+ formulaTableNames,
1521
+ normalizeFormulaTableReferences
1522
+ ), normalizedFormulaByFieldId.set(field.id, normalizedFormula)), (tableFormulaData[row] ??= {})[column] = {
1523
+ f: normalizedFormula,
1427
1524
  si: field.id
1428
- });
1525
+ };
1429
1526
  }
1430
1527
  formulaData[unitId][table.id] = tableFormulaData;
1431
1528
  }
@@ -1494,30 +1591,17 @@ var RustSameRuntimeProjectionController = class extends import_core3.Disposable
1494
1591
  _createBaseTableResource(unitId) {
1495
1592
  let base = this._univerInstanceService.getUnit(unitId, import_core3.UniverInstanceType.UNIVER_BASE);
1496
1593
  if (base) {
1497
- let tables = Object.values(base.getSnapshot().tables), nameCounts = /* @__PURE__ */ new Map();
1498
- for (let table of tables) {
1499
- let name2 = table.name.trim().toLowerCase();
1500
- nameCounts.set(name2, (nameCounts.get(name2) ?? 0) + 1);
1501
- }
1502
- let data2 = {};
1503
- for (let table of tables) {
1504
- let recordCount = baseRecordOrder(table).length, fieldOrder = table.fieldOrder.filter((fieldId) => table.fields[fieldId] != null), tableNames = /* @__PURE__ */ new Set([table.id]);
1505
- (nameCounts.get(table.name.trim().toLowerCase()) ?? 0) === 1 && tableNames.add(table.name);
1506
- for (let name2 of tableNames)
1594
+ let snapshot = base.getSnapshot(), tables = Object.values(snapshot.tables), formulaTableNames = (0, import_core3.createBaseFormulaTableNameMap)(snapshot), data2 = {};
1595
+ for (let table of tables)
1596
+ for (let [name2, reference] of this._getBaseTableEntries(table.id, snapshot, formulaTableNames)) {
1597
+ let columns = Array.from(reference.titleMap.entries()).toSorted((left, right) => left[1] - right[1]).map(([displayName]) => ({ displayName }));
1507
1598
  (data2[table.id] ??= { tables: [] }).tables.push({
1508
1599
  name: name2,
1509
- range: {
1510
- startRow: 0,
1511
- endRow: Math.max(0, recordCount - 1),
1512
- startColumn: 0,
1513
- endColumn: Math.max(0, fieldOrder.length - 1)
1514
- },
1515
- columns: fieldOrder.map((fieldId) => ({
1516
- displayName: table.fields[fieldId]?.name ?? fieldId
1517
- })),
1518
- options: { showHeader: !1 }
1600
+ range: reference.range,
1601
+ columns,
1602
+ options: { showHeader: reference.showHeader !== !1 }
1519
1603
  });
1520
- }
1604
+ }
1521
1605
  return [
1522
1606
  {
1523
1607
  name: "SHEET_TABLE_PLUGIN",
@@ -1544,6 +1628,22 @@ var RustSameRuntimeProjectionController = class extends import_core3.Disposable
1544
1628
  }
1545
1629
  ];
1546
1630
  }
1631
+ _getBaseTableEntries(tableId, snapshot, formulaTableNames) {
1632
+ let table = snapshot.tables[tableId];
1633
+ if (!table) return [];
1634
+ let recordCount = baseRecordOrder(table).length, fieldOrder = table.fieldOrder, reference = {
1635
+ sheetId: table.id,
1636
+ range: {
1637
+ startRow: 0,
1638
+ endRow: Math.max(0, recordCount - 1),
1639
+ startColumn: 0,
1640
+ endColumn: Math.max(0, fieldOrder.length - 1)
1641
+ },
1642
+ titleMap: new Map(fieldOrder.map((fieldId, index) => [table.fields[fieldId]?.name ?? fieldId, index])),
1643
+ showHeader: !1
1644
+ };
1645
+ return [[formulaTableNames.get(table.id), reference]];
1646
+ }
1547
1647
  _rawCellData(cellData) {
1548
1648
  let raw = cellData.getMatrix(), result = {};
1549
1649
  for (let [row, values] of Object.entries(raw ?? {})) {
@@ -1554,20 +1654,65 @@ var RustSameRuntimeProjectionController = class extends import_core3.Disposable
1554
1654
  }
1555
1655
  return result;
1556
1656
  }
1557
- }, BASE_LEGACY_FIELD_REF_PATTERN = /\{([^}]+)\}/g, BASE_TABLE_FIELD_REF_PATTERN = /\b([A-Z_]\w*)\[([^\]]+)\]/gi, BASE_BRACKET_FIELD_REF_PATTERN = /(^|[^A-Za-z0-9_[\]])\[([^\]]+)\]/g, BASE_EXTERNAL_A1_REF_PATTERN = /(?:'\[[^\]]+\](?:[^']|'')+'|\[[^\]]+\][^\s'!]+)!\$?[A-Z]{1,3}\$?\d+(?::\$?[A-Z]{1,3}\$?\d+)?/gi, BASE_EXTERNAL_STRUCTURED_REFERENCE_PREFIX = /(?:'((?:[^']|'')+)'|\[[^\]]+\]|[A-Za-z0-9_.-]+)![^\s![\]]+\[/g;
1558
- function normalizeBaseFormulaForEngine(formula, currentTable, snapshot) {
1559
- let references = [], hold = (reference) => `__BASE_FORMULA_REF_${references.push(reference) - 1}__`;
1560
- return protectBaseExternalReferences(formula, hold).replace(
1657
+ };
1658
+ function createBaseProjectionStructure(snapshot) {
1659
+ let formulaTableNames = (0, import_core3.createBaseFormulaTableNameMap)(snapshot);
1660
+ return {
1661
+ unitName: snapshot.name,
1662
+ tables: Object.fromEntries(
1663
+ Object.values(snapshot.tables).map((table) => [
1664
+ table.id,
1665
+ {
1666
+ formulaName: formulaTableNames.get(table.id),
1667
+ fieldOrder: [...table.fieldOrder],
1668
+ recordOrder: baseRecordOrder(table),
1669
+ fieldSignatures: Object.fromEntries(
1670
+ Object.entries(table.fields).map(([fieldId, field]) => [fieldId, JSON.stringify(field)])
1671
+ )
1672
+ }
1673
+ ])
1674
+ )
1675
+ };
1676
+ }
1677
+ function isSameBaseProjectionStructure(left, right) {
1678
+ return JSON.stringify(left) === JSON.stringify(right);
1679
+ }
1680
+ function findSingleBaseFieldInsertion(previous, snapshot) {
1681
+ let current = createBaseProjectionStructure(snapshot), previousTableIds = Object.keys(previous.tables).toSorted(), currentTableIds = Object.keys(current.tables).toSorted();
1682
+ if (JSON.stringify(previousTableIds) !== JSON.stringify(currentTableIds)) return null;
1683
+ let insertion = null;
1684
+ for (let tableId of currentTableIds) {
1685
+ let before = previous.tables[tableId], after = current.tables[tableId];
1686
+ if (!before || !after || before.formulaName !== after.formulaName || JSON.stringify(before.recordOrder) !== JSON.stringify(after.recordOrder)) return null;
1687
+ for (let fieldId of before.fieldOrder)
1688
+ if (before.fieldSignatures[fieldId] !== after.fieldSignatures[fieldId]) return null;
1689
+ if (JSON.stringify(before.fieldOrder) === JSON.stringify(after.fieldOrder)) continue;
1690
+ if (insertion || after.fieldOrder.length !== before.fieldOrder.length + 1) return null;
1691
+ let column = after.fieldOrder.findIndex((fieldId, index) => fieldId !== before.fieldOrder[index]);
1692
+ if (column < 0) return null;
1693
+ let withoutInserted = after.fieldOrder.filter((_, index) => index !== column);
1694
+ if (JSON.stringify(withoutInserted) !== JSON.stringify(before.fieldOrder)) return null;
1695
+ insertion = { tableId, column };
1696
+ }
1697
+ return insertion;
1698
+ }
1699
+ var BASE_LEGACY_FIELD_REF_PATTERN = /\{([^}]+)\}/g, BASE_TABLE_REFERENCE_PREFIX = /\b[A-Z_][\w.]*\[/gi, BASE_SCOPED_FIELD_REF_PATTERN = /(^|[^\w[])\[\[\s*#(This Row|Data)\s*\],\s*\[([^\]]+)\]\]/gi, BASE_CURRENT_ROW_FIELD_REF_PATTERN = /(^|[^\w[])\[@\[([^\]]+)\]\](?!\])/g, BASE_BRACKET_FIELD_REF_PATTERN = /(^|[^A-Za-z0-9_[\]])\[([^\]]+)\]/g, BASE_EXTERNAL_A1_REF_PATTERN = /(?:'\[[^\]]+\](?:[^']|'')+'|\[[^\]]+\][^\s'!]+)!\$?[A-Z]{1,3}\$?\d+(?::\$?[A-Z]{1,3}\$?\d+)?/gi, BASE_EXTERNAL_STRUCTURED_REFERENCE_PREFIX = /(?:'((?:[^']|'')+)'|\[[^\]]+\]|[A-Za-z0-9_.-]+)![^\s![\]]+\[/g;
1700
+ function normalizeBaseFormulaForEngine(formula, currentTable, formulaTableNames, normalizeFormulaTableReferences) {
1701
+ let canonicalFormula = normalizeFormulaTableReferences(formula), references = [], hold = (reference) => `__BASE_FORMULA_REF_${references.push(reference) - 1}__`;
1702
+ return protectBaseTableReferences(protectBaseExternalReferences(canonicalFormula, hold), hold).replace(
1561
1703
  BASE_LEGACY_FIELD_REF_PATTERN,
1562
- (_match, fieldName) => hold(createEngineThisRowRef(currentTable, fieldName, snapshot))
1563
- ).replace(BASE_TABLE_FIELD_REF_PATTERN, (_match, sourceTableName, fieldName) => {
1564
- let targetTable = resolveBaseFormulaTable(sourceTableName, currentTable, snapshot);
1565
- return targetTable ? hold(createEngineThisRowRef(targetTable, fieldName, snapshot)) : `${sourceTableName}[${fieldName}]`;
1566
- }).replace(BASE_BRACKET_FIELD_REF_PATTERN, (_match, prefix, fieldName) => `${prefix}${hold(createEngineThisRowRef(currentTable, fieldName, snapshot))}`).replace(
1704
+ (_match, fieldName) => hold(createEngineThisRowRef(currentTable, fieldName, formulaTableNames))
1705
+ ).replace(BASE_SCOPED_FIELD_REF_PATTERN, (_match, prefix, scope, fieldName) => {
1706
+ let tableName = formulaTableNames.get(currentTable.id) ?? currentTable.name;
1707
+ return `${prefix}${hold(`${tableName}[[#${scope}],[${fieldName}]]`)}`;
1708
+ }).replace(BASE_CURRENT_ROW_FIELD_REF_PATTERN, (_match, prefix, fieldName) => `${prefix}${hold(createEngineThisRowRef(currentTable, fieldName, formulaTableNames))}`).replace(BASE_BRACKET_FIELD_REF_PATTERN, (_match, prefix, fieldName) => `${prefix}${hold(createEngineThisRowRef(currentTable, fieldName, formulaTableNames))}`).replace(
1567
1709
  /__BASE_FORMULA_REF_(\d+)__/g,
1568
1710
  (_match, index) => references[Number(index)] ?? ""
1569
1711
  );
1570
1712
  }
1713
+ function protectBaseTableReferences(formula, replace) {
1714
+ return protectBaseStructuredReferences(formula, BASE_TABLE_REFERENCE_PREFIX, replace);
1715
+ }
1571
1716
  function protectBaseExternalReferences(formula, replace) {
1572
1717
  let withProtectedA1 = formula.replace(
1573
1718
  BASE_EXTERNAL_A1_REF_PATTERN,
@@ -1576,15 +1721,18 @@ function protectBaseExternalReferences(formula, replace) {
1576
1721
  return protectBaseExternalStructuredReferences(withProtectedA1, replace);
1577
1722
  }
1578
1723
  function protectBaseExternalStructuredReferences(formula, replace) {
1724
+ return protectBaseStructuredReferences(formula, BASE_EXTERNAL_STRUCTURED_REFERENCE_PREFIX, replace);
1725
+ }
1726
+ function protectBaseStructuredReferences(formula, prefixPattern, replace) {
1579
1727
  let spans = [];
1580
- BASE_EXTERNAL_STRUCTURED_REFERENCE_PREFIX.lastIndex = 0;
1728
+ prefixPattern.lastIndex = 0;
1581
1729
  let match;
1582
- for (; (match = BASE_EXTERNAL_STRUCTURED_REFERENCE_PREFIX.exec(formula)) != null; ) {
1730
+ for (; (match = prefixPattern.exec(formula)) != null; ) {
1583
1731
  if (isInsideBaseFormulaString(formula, match.index)) continue;
1584
- let openBracket = BASE_EXTERNAL_STRUCTURED_REFERENCE_PREFIX.lastIndex - 1, depth = 0;
1732
+ let openBracket = prefixPattern.lastIndex - 1, depth = 0;
1585
1733
  for (let index = openBracket; index < formula.length; index++)
1586
1734
  if (formula[index] === "[" && depth++, formula[index] === "]" && (depth--, depth === 0)) {
1587
- spans.push({ start: match.index, end: index + 1 }), BASE_EXTERNAL_STRUCTURED_REFERENCE_PREFIX.lastIndex = index + 1;
1735
+ spans.push({ start: match.index, end: index + 1 }), prefixPattern.lastIndex = index + 1;
1588
1736
  break;
1589
1737
  }
1590
1738
  }
@@ -1606,34 +1754,21 @@ function isInsideBaseFormulaString(formula, position) {
1606
1754
  }
1607
1755
  return inString;
1608
1756
  }
1609
- function createEngineThisRowRef(table, fieldName, snapshot) {
1610
- return `${getEngineBaseTableName(table, snapshot)}[[#This Row],[${fieldName}]]`;
1611
- }
1612
- function getEngineBaseTableName(table, snapshot) {
1613
- return Object.values(snapshot.tables).filter(
1614
- (candidate) => candidate.name === table.name
1615
- ).length === 1 ? table.name : table.id;
1616
- }
1617
- function resolveBaseFormulaTable(tableName, currentTable, snapshot) {
1618
- if (!tableName || tableName === "table" || tableName === currentTable.id || tableName === currentTable.name)
1619
- return currentTable;
1620
- let byId = snapshot.tables[tableName];
1621
- if (byId) return byId;
1622
- let matches = Object.values(snapshot.tables).filter((table) => table.name === tableName);
1623
- return matches.length === 1 ? matches[0] : void 0;
1757
+ function createEngineThisRowRef(table, fieldName, formulaTableNames) {
1758
+ return `${formulaTableNames.get(table.id) ?? table.name}[[#This Row],[${fieldName}]]`;
1624
1759
  }
1625
1760
  function baseRecordOrder(table) {
1626
1761
  return table.recordOrder ? table.recordOrder.filter((recordId) => table.records[recordId] != null) : Object.values(table.records ?? {}).toSorted((left, right) => left.orderKey.localeCompare(right.orderKey)).map((record) => record.id);
1627
1762
  }
1628
1763
  function buildBaseRuntimeCellData(table) {
1629
- let cellData = { ...table.cellData }, fieldOrder = table.fieldOrder.filter((fieldId) => table.fields[fieldId] != null), recordOrder = baseRecordOrder(table);
1764
+ let cellData = { ...table.cellData }, fieldOrder = table.fieldOrder, recordOrder = baseRecordOrder(table);
1630
1765
  for (let row = 0; row < recordOrder.length; row++) {
1631
1766
  let record = table.records[recordOrder[row]];
1632
1767
  if (record) {
1633
1768
  cellData[row] = { ...cellData[row] };
1634
1769
  for (let column = 0; column < fieldOrder.length; column++) {
1635
1770
  let fieldId = fieldOrder[column];
1636
- Object.prototype.hasOwnProperty.call(record.values ?? {}, fieldId) && (cellData[row][column] = toBaseRuntimeCellData(record.values[fieldId], table.fields[fieldId]));
1771
+ table.fields[fieldId] && Object.prototype.hasOwnProperty.call(record.values ?? {}, fieldId) && (cellData[row][column] = toBaseRuntimeCellData(record.values[fieldId], table.fields[fieldId]));
1637
1772
  }
1638
1773
  }
1639
1774
  }
package/lib/es/index.js CHANGED
@@ -179,7 +179,7 @@ import {
179
179
  CellValueType,
180
180
  ObjectMatrix
181
181
  } from "@univerjs/core";
182
- var XLSX_WORKBOOK_CALCULATION_RESOURCE = "UNIVER_CLI_XLSX_WORKBOOK_CALCULATION_PLUGIN", EXTERNAL_DATA_RESOURCE = "SHEET_EXTERNAL_DATA_PLUGIN", EXTERNAL_DATA_SIGNATURE = "external_data", RUST_UPSERT_REFERENCE_DATA_MUTATION_ID = "formula.mutation.upsert-reference-data", RUST_UPSERT_PROJECTION_WORKBOOK_MUTATION_ID = "formula.mutation.upsert-projection-workbook", RUST_FORMULA_RELEVANT_MUTATIONS = /* @__PURE__ */ new Set([
182
+ var XLSX_WORKBOOK_CALCULATION_RESOURCE = "UNIVER_CLI_XLSX_WORKBOOK_CALCULATION_PLUGIN", EXTERNAL_DATA_RESOURCE = "SHEET_EXTERNAL_DATA_PLUGIN", EXTERNAL_DATA_SIGNATURE = "external_data", RUST_UPSERT_REFERENCE_DATA_MUTATION_ID = "formula.mutation.upsert-reference-data", RUST_UPSERT_PROJECTION_WORKBOOK_MUTATION_ID = "formula.mutation.upsert-projection-workbook", RUST_SET_SUPER_TABLES_MUTATION_ID = "formula.mutation.set-super-tables", RUST_FORMULA_RELEVANT_MUTATIONS = /* @__PURE__ */ new Set([
183
183
  "sheet.mutation.set-range-values",
184
184
  "sheet.mutation.insert-row",
185
185
  "sheet.mutation.insert-col",
@@ -206,6 +206,7 @@ var XLSX_WORKBOOK_CALCULATION_RESOURCE = "UNIVER_CLI_XLSX_WORKBOOK_CALCULATION_P
206
206
  "formula.mutation.set-image-formula-data",
207
207
  "formula.mutation.set-other-formula",
208
208
  "formula.mutation.set-super-table",
209
+ RUST_SET_SUPER_TABLES_MUTATION_ID,
209
210
  "formula.mutation.set-super-table-option",
210
211
  "formula.mutation.remove-defined-name",
211
212
  "formula.mutation.remove-feature-calculation",
@@ -441,13 +442,26 @@ var RustFormulaProjectionService = class {
441
442
  }
442
443
  };
443
444
  function normalizeRustEngineMutation(mutation) {
444
- let params = mutation.id === "formula.mutation.set-super-table" ? normalizeSetSuperTableMutationParams(mutation.params) : mutation.params ?? {};
445
+ let params = mutation.id === "formula.mutation.set-super-table" ? normalizeSetSuperTableMutationParams(mutation.params) : mutation.id === RUST_SET_SUPER_TABLES_MUTATION_ID ? normalizeSetSuperTablesMutationParams(mutation.params) : mutation.params ?? {};
445
446
  return {
446
447
  id: mutation.id,
447
448
  params,
448
449
  dirtyRanges: Array.isArray(mutation.dirtyRanges) ? mutation.dirtyRanges : []
449
450
  };
450
451
  }
452
+ function normalizeSetSuperTablesMutationParams(params) {
453
+ return !isRecord(params) || typeof params.unitId != "string" || !Array.isArray(params.tables) ? params ?? {} : {
454
+ ...params,
455
+ tables: params.tables.map((entry) => {
456
+ let normalized = normalizeSetSuperTableMutationParams(
457
+ isRecord(entry) ? { ...entry, unitId: params.unitId } : entry
458
+ );
459
+ if (!isRecord(normalized)) return normalized;
460
+ let { unitId: _unitId, ...table } = normalized;
461
+ return table;
462
+ })
463
+ };
464
+ }
451
465
  function normalizeSetSuperTableMutationParams(params) {
452
466
  if (!isRecord(params) || !isRecord(params.reference))
453
467
  return params ?? {};
@@ -1015,6 +1029,7 @@ var RustEngineSyncController = class extends Disposable {
1015
1029
  _nativeReady = !1;
1016
1030
  _snapshotLoaded = !1;
1017
1031
  _projectionLoadPromise = null;
1032
+ _projectionDeltaBarrier = Promise.resolve();
1018
1033
  _pendingMutations = [];
1019
1034
  _projectionManagedTableUnits = /* @__PURE__ */ new Set();
1020
1035
  _initialize() {
@@ -1035,6 +1050,13 @@ var RustEngineSyncController = class extends Disposable {
1035
1050
  }
1036
1051
  async waitForProjectionLoad() {
1037
1052
  await this._projectionLoadPromise;
1053
+ let barrier;
1054
+ do
1055
+ barrier = this._projectionDeltaBarrier, await barrier;
1056
+ while (barrier !== this._projectionDeltaBarrier);
1057
+ }
1058
+ setProjectionDeltaBarrier(barrier) {
1059
+ this._projectionDeltaBarrier = barrier;
1038
1060
  }
1039
1061
  async _loadProjection(workbooks) {
1040
1062
  await loadNativeBinding(), this._nativeReady = !0;
@@ -1066,7 +1088,7 @@ var RustEngineSyncController = class extends Disposable {
1066
1088
  }
1067
1089
  }
1068
1090
  dispose() {
1069
- if (super.dispose(), this._pendingMutations.length = 0, this._projectionManagedTableUnits.clear(), this._projectionLoadPromise = null, !this._nativeReady) {
1091
+ if (super.dispose(), this._pendingMutations.length = 0, this._projectionManagedTableUnits.clear(), this._projectionLoadPromise = null, this._projectionDeltaBarrier = Promise.resolve(), !this._nativeReady) {
1070
1092
  this._snapshotLoaded = !1;
1071
1093
  return;
1072
1094
  }
@@ -1202,12 +1224,14 @@ import { ICalculateFormulaService } from "@univerjs/engine-formula";
1202
1224
  import { UniverProFormulaEnginePlugin } from "@univerjs-pro/engine-formula";
1203
1225
 
1204
1226
  // package.json
1205
- var name = "@univerjs-pro/engine-formula-rust", version = "1.0.0-insiders.20260808-70eefdd";
1227
+ var name = "@univerjs-pro/engine-formula-rust", version = "1.0.0-rc.0";
1206
1228
 
1207
1229
  // src/controllers/rust-same-runtime-projection.controller.ts
1208
1230
  import {
1209
1231
  CellValueType as CellValueType2,
1210
1232
  CommandType as CommandType2,
1233
+ createBaseFormulaTableReferenceNormalizer,
1234
+ createBaseFormulaTableNameMap,
1211
1235
  Disposable as Disposable2,
1212
1236
  ICommandService as ICommandService2,
1213
1237
  IUniverInstanceService,
@@ -1242,11 +1266,12 @@ var RustSameRuntimeProjectionController = class extends Disposable2 {
1242
1266
  _activeDirtyManagerService;
1243
1267
  _initialProjectionLoad = Promise.resolve();
1244
1268
  _projectionDeltaQueue = Promise.resolve();
1269
+ _baseProjectionStructures = /* @__PURE__ */ new Map();
1245
1270
  _initialize() {
1246
1271
  for (let base of this._univerInstanceService.getAllUnitsForType(
1247
1272
  UniverInstanceType.UNIVER_BASE
1248
1273
  ))
1249
- this._rustEngineSyncController.setProjectionManagedTableUnit(base.getUnitId());
1274
+ this._rustEngineSyncController.setProjectionManagedTableUnit(base.getUnitId()), this._rememberBaseProjectionStructure(base.getSnapshot());
1250
1275
  this._initialProjectionLoad = this._loadAllWorkbookProjections(), this.disposeWithMe(
1251
1276
  toDisposable(
1252
1277
  this._univerInstanceService.getTypeOfUnitAdded$(UniverInstanceType.UNIVER_SHEET).subscribe(({ unit }) => {
@@ -1261,7 +1286,7 @@ var RustSameRuntimeProjectionController = class extends Disposable2 {
1261
1286
  ), this.disposeWithMe(
1262
1287
  toDisposable(
1263
1288
  this._univerInstanceService.getTypeOfUnitAdded$(UniverInstanceType.UNIVER_BASE).subscribe(({ unit }) => {
1264
- this._rustEngineSyncController.setProjectionManagedTableUnit(unit.getUnitId()), this._enqueueProjectionDelta(() => this._upsertBaseProjection(unit.getUnitId()));
1289
+ this._rustEngineSyncController.setProjectionManagedTableUnit(unit.getUnitId()), this._rememberBaseProjectionStructure(unit.getSnapshot()), this._enqueueProjectionDelta(() => this._upsertBaseProjection(unit.getUnitId()));
1265
1290
  })
1266
1291
  )
1267
1292
  ), this.disposeWithMe(
@@ -1271,7 +1296,7 @@ var RustSameRuntimeProjectionController = class extends Disposable2 {
1271
1296
  ), this.disposeWithMe(
1272
1297
  toDisposable(
1273
1298
  this._univerInstanceService.getTypeOfUnitDisposed$(UniverInstanceType.UNIVER_BASE).subscribe((base) => {
1274
- this._rustEngineSyncController.unsetProjectionManagedTableUnit(base.getUnitId()), this._enqueueProjectionDelta(
1299
+ this._baseProjectionStructures.delete(base.getUnitId()), this._rustEngineSyncController.unsetProjectionManagedTableUnit(base.getUnitId()), this._enqueueProjectionDelta(
1275
1300
  () => this._rustEngineSyncController.disposeProjectionWorkbook(base.getUnitId())
1276
1301
  );
1277
1302
  })
@@ -1295,7 +1320,7 @@ var RustSameRuntimeProjectionController = class extends Disposable2 {
1295
1320
  if (!this._disposed)
1296
1321
  return task();
1297
1322
  }).catch(() => {
1298
- });
1323
+ }), this._rustEngineSyncController.setProjectionDeltaBarrier(this._projectionDeltaQueue);
1299
1324
  }
1300
1325
  async _syncBaseMutation(command) {
1301
1326
  if (!hasUnitId2(command.params)) return;
@@ -1304,7 +1329,65 @@ var RustSameRuntimeProjectionController = class extends Disposable2 {
1304
1329
  this._applyBaseDirtyRanges(unitId, dirtyRanges);
1305
1330
  return;
1306
1331
  }
1307
- await this._upsertBaseProjection(unitId);
1332
+ hasStructureInvalidation && this._isBaseProjectionStructureUnchanged(unitId) || hasStructureInvalidation && this._applyBaseFieldInsertion(unitId, dirtyRanges) || await this._upsertBaseProjection(unitId);
1333
+ }
1334
+ _applyBaseFieldInsertion(unitId, dirtyRanges) {
1335
+ let base = this._univerInstanceService.getUnit(unitId, UniverInstanceType.UNIVER_BASE), previous = this._baseProjectionStructures.get(unitId);
1336
+ if (!base || !previous) return !1;
1337
+ let snapshot = base.getSnapshot(), insertion = findSingleBaseFieldInsertion(previous, snapshot);
1338
+ if (!insertion) return !1;
1339
+ let projectionData = this._getBaseProjectionData(unitId), sheet = projectionData?.calculationData.allUnitData[unitId]?.[insertion.tableId];
1340
+ if (!projectionData || !sheet) return !1;
1341
+ let formulaData = projectionData.formulaData[unitId]?.[insertion.tableId], cellValue = {};
1342
+ for (let row = 0; row < sheet.rowCount; row++) {
1343
+ let value = sheet.cellData.getValue(row, insertion.column), formula = formulaData?.[row]?.[insertion.column];
1344
+ cellValue[row] = {
1345
+ [insertion.column]: formula ? { ...value, ...formula } : value ?? null
1346
+ };
1347
+ }
1348
+ let insertedRange = {
1349
+ startRow: 0,
1350
+ endRow: Math.max(0, sheet.rowCount - 1),
1351
+ startColumn: insertion.column,
1352
+ endColumn: insertion.column
1353
+ };
1354
+ return this._rustEngineSyncController.applyProjectionMutation({
1355
+ id: "sheet.mutation.insert-col",
1356
+ params: {
1357
+ unitId,
1358
+ subUnitId: insertion.tableId,
1359
+ range: insertedRange
1360
+ },
1361
+ dirtyRanges: []
1362
+ }), this._rustEngineSyncController.applyProjectionMutation({
1363
+ id: "sheet.mutation.set-range-values",
1364
+ params: {
1365
+ unitId,
1366
+ subUnitId: insertion.tableId,
1367
+ cellValue
1368
+ },
1369
+ dirtyRanges: [
1370
+ {
1371
+ unitId,
1372
+ sheetId: insertion.tableId,
1373
+ range: insertedRange
1374
+ }
1375
+ ]
1376
+ }), this._rustEngineSyncController.applyProjectionMutation({
1377
+ id: RUST_SET_SUPER_TABLES_MUTATION_ID,
1378
+ params: {
1379
+ unitId,
1380
+ tables: this._getBaseTableEntries(
1381
+ insertion.tableId,
1382
+ snapshot,
1383
+ createBaseFormulaTableNameMap(snapshot)
1384
+ ).map(([tableName, reference]) => ({
1385
+ tableName,
1386
+ reference
1387
+ }))
1388
+ },
1389
+ dirtyRanges: []
1390
+ }), dirtyRanges.length > 0 && this._applyBaseDirtyRanges(unitId, dirtyRanges), this._rememberBaseProjectionStructure(snapshot), !0;
1308
1391
  }
1309
1392
  _applyBaseDirtyRanges(unitId, dirtyRanges) {
1310
1393
  let projectionData = this._getBaseProjectionData(unitId);
@@ -1346,6 +1429,15 @@ var RustSameRuntimeProjectionController = class extends Disposable2 {
1346
1429
  formulaData
1347
1430
  );
1348
1431
  workbook && this._rustEngineSyncController.upsertProjectionWorkbook(workbook);
1432
+ let base = this._univerInstanceService.getUnit(unitId, UniverInstanceType.UNIVER_BASE);
1433
+ base && this._rememberBaseProjectionStructure(base.getSnapshot());
1434
+ }
1435
+ _rememberBaseProjectionStructure(snapshot) {
1436
+ this._baseProjectionStructures.set(snapshot.id, createBaseProjectionStructure(snapshot));
1437
+ }
1438
+ _isBaseProjectionStructureUnchanged(unitId) {
1439
+ let base = this._univerInstanceService.getUnit(unitId, UniverInstanceType.UNIVER_BASE), previous = this._baseProjectionStructures.get(unitId);
1440
+ return !!(base && previous && isSameBaseProjectionStructure(previous, createBaseProjectionStructure(base.getSnapshot())));
1349
1441
  }
1350
1442
  _getBaseProjectionData(unitId) {
1351
1443
  let base = this._univerInstanceService.getUnit(unitId, UniverInstanceType.UNIVER_BASE);
@@ -1355,9 +1447,9 @@ var RustSameRuntimeProjectionController = class extends Disposable2 {
1355
1447
  name: snapshot.name,
1356
1448
  unitType: UniverInstanceType.UNIVER_BASE
1357
1449
  }
1358
- }, formulaData = { [unitId]: {} };
1450
+ }, formulaData = { [unitId]: {} }, formulaTableNames = createBaseFormulaTableNameMap(snapshot), normalizeFormulaTableReferences = createBaseFormulaTableReferenceNormalizer(snapshot, formulaTableNames);
1359
1451
  for (let table of Object.values(snapshot.tables)) {
1360
- let fieldOrder = table.fieldOrder.filter((fieldId) => table.fields[fieldId] != null);
1452
+ let fieldOrder = table.fieldOrder;
1361
1453
  allUnitData[unitId][table.id] = {
1362
1454
  cellData: new ObjectMatrix2(
1363
1455
  buildBaseRuntimeCellData(table)
@@ -1366,18 +1458,25 @@ var RustSameRuntimeProjectionController = class extends Disposable2 {
1366
1458
  columnCount: fieldOrder.length,
1367
1459
  rowData: {},
1368
1460
  columnData: {}
1369
- }, unitSheetNameMap[unitId][table.name] = table.id;
1370
- let tableFormulaData = {}, recordOrder = baseRecordOrder(table);
1461
+ }, unitSheetNameMap[unitId][formulaTableNames.get(table.id)] = table.id;
1462
+ let tableFormulaData = {}, normalizedFormulaByFieldId = /* @__PURE__ */ new Map(), recordOrder = baseRecordOrder(table);
1371
1463
  for (let row = 0; row < recordOrder.length; row++)
1372
1464
  if (table.records[recordOrder[row]])
1373
1465
  for (let column = 0; column < fieldOrder.length; column++) {
1374
1466
  let field = table.fields[fieldOrder[column]];
1375
1467
  if (!field || field.type !== "formula") continue;
1376
1468
  let formula = String(field.config?.formula ?? "").trim();
1377
- formula && ((tableFormulaData[row] ??= {})[column] = {
1378
- f: normalizeBaseFormulaForEngine(formula, table, snapshot),
1469
+ if (!formula) continue;
1470
+ let normalizedFormula = normalizedFormulaByFieldId.get(field.id);
1471
+ normalizedFormula == null && (normalizedFormula = normalizeBaseFormulaForEngine(
1472
+ formula,
1473
+ table,
1474
+ formulaTableNames,
1475
+ normalizeFormulaTableReferences
1476
+ ), normalizedFormulaByFieldId.set(field.id, normalizedFormula)), (tableFormulaData[row] ??= {})[column] = {
1477
+ f: normalizedFormula,
1379
1478
  si: field.id
1380
- });
1479
+ };
1381
1480
  }
1382
1481
  formulaData[unitId][table.id] = tableFormulaData;
1383
1482
  }
@@ -1446,30 +1545,17 @@ var RustSameRuntimeProjectionController = class extends Disposable2 {
1446
1545
  _createBaseTableResource(unitId) {
1447
1546
  let base = this._univerInstanceService.getUnit(unitId, UniverInstanceType.UNIVER_BASE);
1448
1547
  if (base) {
1449
- let tables = Object.values(base.getSnapshot().tables), nameCounts = /* @__PURE__ */ new Map();
1450
- for (let table of tables) {
1451
- let name2 = table.name.trim().toLowerCase();
1452
- nameCounts.set(name2, (nameCounts.get(name2) ?? 0) + 1);
1453
- }
1454
- let data2 = {};
1455
- for (let table of tables) {
1456
- let recordCount = baseRecordOrder(table).length, fieldOrder = table.fieldOrder.filter((fieldId) => table.fields[fieldId] != null), tableNames = /* @__PURE__ */ new Set([table.id]);
1457
- (nameCounts.get(table.name.trim().toLowerCase()) ?? 0) === 1 && tableNames.add(table.name);
1458
- for (let name2 of tableNames)
1548
+ let snapshot = base.getSnapshot(), tables = Object.values(snapshot.tables), formulaTableNames = createBaseFormulaTableNameMap(snapshot), data2 = {};
1549
+ for (let table of tables)
1550
+ for (let [name2, reference] of this._getBaseTableEntries(table.id, snapshot, formulaTableNames)) {
1551
+ let columns = Array.from(reference.titleMap.entries()).toSorted((left, right) => left[1] - right[1]).map(([displayName]) => ({ displayName }));
1459
1552
  (data2[table.id] ??= { tables: [] }).tables.push({
1460
1553
  name: name2,
1461
- range: {
1462
- startRow: 0,
1463
- endRow: Math.max(0, recordCount - 1),
1464
- startColumn: 0,
1465
- endColumn: Math.max(0, fieldOrder.length - 1)
1466
- },
1467
- columns: fieldOrder.map((fieldId) => ({
1468
- displayName: table.fields[fieldId]?.name ?? fieldId
1469
- })),
1470
- options: { showHeader: !1 }
1554
+ range: reference.range,
1555
+ columns,
1556
+ options: { showHeader: reference.showHeader !== !1 }
1471
1557
  });
1472
- }
1558
+ }
1473
1559
  return [
1474
1560
  {
1475
1561
  name: "SHEET_TABLE_PLUGIN",
@@ -1496,6 +1582,22 @@ var RustSameRuntimeProjectionController = class extends Disposable2 {
1496
1582
  }
1497
1583
  ];
1498
1584
  }
1585
+ _getBaseTableEntries(tableId, snapshot, formulaTableNames) {
1586
+ let table = snapshot.tables[tableId];
1587
+ if (!table) return [];
1588
+ let recordCount = baseRecordOrder(table).length, fieldOrder = table.fieldOrder, reference = {
1589
+ sheetId: table.id,
1590
+ range: {
1591
+ startRow: 0,
1592
+ endRow: Math.max(0, recordCount - 1),
1593
+ startColumn: 0,
1594
+ endColumn: Math.max(0, fieldOrder.length - 1)
1595
+ },
1596
+ titleMap: new Map(fieldOrder.map((fieldId, index) => [table.fields[fieldId]?.name ?? fieldId, index])),
1597
+ showHeader: !1
1598
+ };
1599
+ return [[formulaTableNames.get(table.id), reference]];
1600
+ }
1499
1601
  _rawCellData(cellData) {
1500
1602
  let raw = cellData.getMatrix(), result = {};
1501
1603
  for (let [row, values] of Object.entries(raw ?? {})) {
@@ -1506,20 +1608,65 @@ var RustSameRuntimeProjectionController = class extends Disposable2 {
1506
1608
  }
1507
1609
  return result;
1508
1610
  }
1509
- }, BASE_LEGACY_FIELD_REF_PATTERN = /\{([^}]+)\}/g, BASE_TABLE_FIELD_REF_PATTERN = /\b([A-Z_]\w*)\[([^\]]+)\]/gi, BASE_BRACKET_FIELD_REF_PATTERN = /(^|[^A-Za-z0-9_[\]])\[([^\]]+)\]/g, BASE_EXTERNAL_A1_REF_PATTERN = /(?:'\[[^\]]+\](?:[^']|'')+'|\[[^\]]+\][^\s'!]+)!\$?[A-Z]{1,3}\$?\d+(?::\$?[A-Z]{1,3}\$?\d+)?/gi, BASE_EXTERNAL_STRUCTURED_REFERENCE_PREFIX = /(?:'((?:[^']|'')+)'|\[[^\]]+\]|[A-Za-z0-9_.-]+)![^\s![\]]+\[/g;
1510
- function normalizeBaseFormulaForEngine(formula, currentTable, snapshot) {
1511
- let references = [], hold = (reference) => `__BASE_FORMULA_REF_${references.push(reference) - 1}__`;
1512
- return protectBaseExternalReferences(formula, hold).replace(
1611
+ };
1612
+ function createBaseProjectionStructure(snapshot) {
1613
+ let formulaTableNames = createBaseFormulaTableNameMap(snapshot);
1614
+ return {
1615
+ unitName: snapshot.name,
1616
+ tables: Object.fromEntries(
1617
+ Object.values(snapshot.tables).map((table) => [
1618
+ table.id,
1619
+ {
1620
+ formulaName: formulaTableNames.get(table.id),
1621
+ fieldOrder: [...table.fieldOrder],
1622
+ recordOrder: baseRecordOrder(table),
1623
+ fieldSignatures: Object.fromEntries(
1624
+ Object.entries(table.fields).map(([fieldId, field]) => [fieldId, JSON.stringify(field)])
1625
+ )
1626
+ }
1627
+ ])
1628
+ )
1629
+ };
1630
+ }
1631
+ function isSameBaseProjectionStructure(left, right) {
1632
+ return JSON.stringify(left) === JSON.stringify(right);
1633
+ }
1634
+ function findSingleBaseFieldInsertion(previous, snapshot) {
1635
+ let current = createBaseProjectionStructure(snapshot), previousTableIds = Object.keys(previous.tables).toSorted(), currentTableIds = Object.keys(current.tables).toSorted();
1636
+ if (JSON.stringify(previousTableIds) !== JSON.stringify(currentTableIds)) return null;
1637
+ let insertion = null;
1638
+ for (let tableId of currentTableIds) {
1639
+ let before = previous.tables[tableId], after = current.tables[tableId];
1640
+ if (!before || !after || before.formulaName !== after.formulaName || JSON.stringify(before.recordOrder) !== JSON.stringify(after.recordOrder)) return null;
1641
+ for (let fieldId of before.fieldOrder)
1642
+ if (before.fieldSignatures[fieldId] !== after.fieldSignatures[fieldId]) return null;
1643
+ if (JSON.stringify(before.fieldOrder) === JSON.stringify(after.fieldOrder)) continue;
1644
+ if (insertion || after.fieldOrder.length !== before.fieldOrder.length + 1) return null;
1645
+ let column = after.fieldOrder.findIndex((fieldId, index) => fieldId !== before.fieldOrder[index]);
1646
+ if (column < 0) return null;
1647
+ let withoutInserted = after.fieldOrder.filter((_, index) => index !== column);
1648
+ if (JSON.stringify(withoutInserted) !== JSON.stringify(before.fieldOrder)) return null;
1649
+ insertion = { tableId, column };
1650
+ }
1651
+ return insertion;
1652
+ }
1653
+ var BASE_LEGACY_FIELD_REF_PATTERN = /\{([^}]+)\}/g, BASE_TABLE_REFERENCE_PREFIX = /\b[A-Z_][\w.]*\[/gi, BASE_SCOPED_FIELD_REF_PATTERN = /(^|[^\w[])\[\[\s*#(This Row|Data)\s*\],\s*\[([^\]]+)\]\]/gi, BASE_CURRENT_ROW_FIELD_REF_PATTERN = /(^|[^\w[])\[@\[([^\]]+)\]\](?!\])/g, BASE_BRACKET_FIELD_REF_PATTERN = /(^|[^A-Za-z0-9_[\]])\[([^\]]+)\]/g, BASE_EXTERNAL_A1_REF_PATTERN = /(?:'\[[^\]]+\](?:[^']|'')+'|\[[^\]]+\][^\s'!]+)!\$?[A-Z]{1,3}\$?\d+(?::\$?[A-Z]{1,3}\$?\d+)?/gi, BASE_EXTERNAL_STRUCTURED_REFERENCE_PREFIX = /(?:'((?:[^']|'')+)'|\[[^\]]+\]|[A-Za-z0-9_.-]+)![^\s![\]]+\[/g;
1654
+ function normalizeBaseFormulaForEngine(formula, currentTable, formulaTableNames, normalizeFormulaTableReferences) {
1655
+ let canonicalFormula = normalizeFormulaTableReferences(formula), references = [], hold = (reference) => `__BASE_FORMULA_REF_${references.push(reference) - 1}__`;
1656
+ return protectBaseTableReferences(protectBaseExternalReferences(canonicalFormula, hold), hold).replace(
1513
1657
  BASE_LEGACY_FIELD_REF_PATTERN,
1514
- (_match, fieldName) => hold(createEngineThisRowRef(currentTable, fieldName, snapshot))
1515
- ).replace(BASE_TABLE_FIELD_REF_PATTERN, (_match, sourceTableName, fieldName) => {
1516
- let targetTable = resolveBaseFormulaTable(sourceTableName, currentTable, snapshot);
1517
- return targetTable ? hold(createEngineThisRowRef(targetTable, fieldName, snapshot)) : `${sourceTableName}[${fieldName}]`;
1518
- }).replace(BASE_BRACKET_FIELD_REF_PATTERN, (_match, prefix, fieldName) => `${prefix}${hold(createEngineThisRowRef(currentTable, fieldName, snapshot))}`).replace(
1658
+ (_match, fieldName) => hold(createEngineThisRowRef(currentTable, fieldName, formulaTableNames))
1659
+ ).replace(BASE_SCOPED_FIELD_REF_PATTERN, (_match, prefix, scope, fieldName) => {
1660
+ let tableName = formulaTableNames.get(currentTable.id) ?? currentTable.name;
1661
+ return `${prefix}${hold(`${tableName}[[#${scope}],[${fieldName}]]`)}`;
1662
+ }).replace(BASE_CURRENT_ROW_FIELD_REF_PATTERN, (_match, prefix, fieldName) => `${prefix}${hold(createEngineThisRowRef(currentTable, fieldName, formulaTableNames))}`).replace(BASE_BRACKET_FIELD_REF_PATTERN, (_match, prefix, fieldName) => `${prefix}${hold(createEngineThisRowRef(currentTable, fieldName, formulaTableNames))}`).replace(
1519
1663
  /__BASE_FORMULA_REF_(\d+)__/g,
1520
1664
  (_match, index) => references[Number(index)] ?? ""
1521
1665
  );
1522
1666
  }
1667
+ function protectBaseTableReferences(formula, replace) {
1668
+ return protectBaseStructuredReferences(formula, BASE_TABLE_REFERENCE_PREFIX, replace);
1669
+ }
1523
1670
  function protectBaseExternalReferences(formula, replace) {
1524
1671
  let withProtectedA1 = formula.replace(
1525
1672
  BASE_EXTERNAL_A1_REF_PATTERN,
@@ -1528,15 +1675,18 @@ function protectBaseExternalReferences(formula, replace) {
1528
1675
  return protectBaseExternalStructuredReferences(withProtectedA1, replace);
1529
1676
  }
1530
1677
  function protectBaseExternalStructuredReferences(formula, replace) {
1678
+ return protectBaseStructuredReferences(formula, BASE_EXTERNAL_STRUCTURED_REFERENCE_PREFIX, replace);
1679
+ }
1680
+ function protectBaseStructuredReferences(formula, prefixPattern, replace) {
1531
1681
  let spans = [];
1532
- BASE_EXTERNAL_STRUCTURED_REFERENCE_PREFIX.lastIndex = 0;
1682
+ prefixPattern.lastIndex = 0;
1533
1683
  let match;
1534
- for (; (match = BASE_EXTERNAL_STRUCTURED_REFERENCE_PREFIX.exec(formula)) != null; ) {
1684
+ for (; (match = prefixPattern.exec(formula)) != null; ) {
1535
1685
  if (isInsideBaseFormulaString(formula, match.index)) continue;
1536
- let openBracket = BASE_EXTERNAL_STRUCTURED_REFERENCE_PREFIX.lastIndex - 1, depth = 0;
1686
+ let openBracket = prefixPattern.lastIndex - 1, depth = 0;
1537
1687
  for (let index = openBracket; index < formula.length; index++)
1538
1688
  if (formula[index] === "[" && depth++, formula[index] === "]" && (depth--, depth === 0)) {
1539
- spans.push({ start: match.index, end: index + 1 }), BASE_EXTERNAL_STRUCTURED_REFERENCE_PREFIX.lastIndex = index + 1;
1689
+ spans.push({ start: match.index, end: index + 1 }), prefixPattern.lastIndex = index + 1;
1540
1690
  break;
1541
1691
  }
1542
1692
  }
@@ -1558,34 +1708,21 @@ function isInsideBaseFormulaString(formula, position) {
1558
1708
  }
1559
1709
  return inString;
1560
1710
  }
1561
- function createEngineThisRowRef(table, fieldName, snapshot) {
1562
- return `${getEngineBaseTableName(table, snapshot)}[[#This Row],[${fieldName}]]`;
1563
- }
1564
- function getEngineBaseTableName(table, snapshot) {
1565
- return Object.values(snapshot.tables).filter(
1566
- (candidate) => candidate.name === table.name
1567
- ).length === 1 ? table.name : table.id;
1568
- }
1569
- function resolveBaseFormulaTable(tableName, currentTable, snapshot) {
1570
- if (!tableName || tableName === "table" || tableName === currentTable.id || tableName === currentTable.name)
1571
- return currentTable;
1572
- let byId = snapshot.tables[tableName];
1573
- if (byId) return byId;
1574
- let matches = Object.values(snapshot.tables).filter((table) => table.name === tableName);
1575
- return matches.length === 1 ? matches[0] : void 0;
1711
+ function createEngineThisRowRef(table, fieldName, formulaTableNames) {
1712
+ return `${formulaTableNames.get(table.id) ?? table.name}[[#This Row],[${fieldName}]]`;
1576
1713
  }
1577
1714
  function baseRecordOrder(table) {
1578
1715
  return table.recordOrder ? table.recordOrder.filter((recordId) => table.records[recordId] != null) : Object.values(table.records ?? {}).toSorted((left, right) => left.orderKey.localeCompare(right.orderKey)).map((record) => record.id);
1579
1716
  }
1580
1717
  function buildBaseRuntimeCellData(table) {
1581
- let cellData = { ...table.cellData }, fieldOrder = table.fieldOrder.filter((fieldId) => table.fields[fieldId] != null), recordOrder = baseRecordOrder(table);
1718
+ let cellData = { ...table.cellData }, fieldOrder = table.fieldOrder, recordOrder = baseRecordOrder(table);
1582
1719
  for (let row = 0; row < recordOrder.length; row++) {
1583
1720
  let record = table.records[recordOrder[row]];
1584
1721
  if (record) {
1585
1722
  cellData[row] = { ...cellData[row] };
1586
1723
  for (let column = 0; column < fieldOrder.length; column++) {
1587
1724
  let fieldId = fieldOrder[column];
1588
- Object.prototype.hasOwnProperty.call(record.values ?? {}, fieldId) && (cellData[row][column] = toBaseRuntimeCellData(record.values[fieldId], table.fields[fieldId]));
1725
+ table.fields[fieldId] && Object.prototype.hasOwnProperty.call(record.values ?? {}, fieldId) && (cellData[row][column] = toBaseRuntimeCellData(record.values[fieldId], table.fields[fieldId]));
1589
1726
  }
1590
1727
  }
1591
1728
  }
@@ -587,12 +587,14 @@ declare class RustEngineSyncController extends Disposable {
587
587
  private _nativeReady;
588
588
  private _snapshotLoaded;
589
589
  private _projectionLoadPromise;
590
+ private _projectionDeltaBarrier;
590
591
  private readonly _pendingMutations;
591
592
  private readonly _projectionManagedTableUnits;
592
593
  constructor(_commandService: ICommandService);
593
594
  private _initialize;
594
595
  loadProjection(workbooks: readonly IRustProjectionWorkbookInput[]): Promise<void>;
595
596
  waitForProjectionLoad(): Promise<void>;
597
+ setProjectionDeltaBarrier(barrier: Promise<void>): void;
596
598
  private _loadProjection;
597
599
  applyProjectionMutation(mutation: IRustEngineMutation): void;
598
600
  upsertProjectionWorkbook(workbook: IRustProjectionWorkbookInput): void;
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "author": "DreamNum Co., Ltd. <developer@univer.ai>",
3
3
  "dependencies": {
4
- "@univerjs-pro/engine-formula": "1.0.0-insiders.20260808-70eefdd",
5
- "@univerjs-pro/engine-formula-rust-binding": "1.0.0-insiders.20260805-b15e7f3",
6
- "@univerjs/core": "1.0.0-insiders.20260808-70eefdd",
7
- "@univerjs/engine-formula": "1.0.0-insiders.20260808-70eefdd",
8
- "@univerjs/protocol": "1.0.0-insiders.20260808-70eefdd"
4
+ "@univerjs-pro/engine-formula": "1.0.0-rc.0",
5
+ "@univerjs-pro/engine-formula-rust-binding": "1.0.0-insiders.20260910-22fe9c7",
6
+ "@univerjs/core": "1.0.0-rc.0",
7
+ "@univerjs/engine-formula": "1.0.0-rc.0",
8
+ "@univerjs/protocol": "1.0.0-rc.0"
9
9
  },
10
10
  "description": "Rust-backed formula engine plugin for Univer",
11
11
  "files": [
@@ -20,7 +20,7 @@
20
20
  "url": "https://github.com/dream-num/univer-rs"
21
21
  },
22
22
  "type": "module",
23
- "version": "1.0.0-insiders.20260808-70eefdd",
23
+ "version": "1.0.0-rc.0",
24
24
  "main": "./lib/cjs/index.cjs",
25
25
  "module": "./lib/es/index.js",
26
26
  "types": "./lib/types/index.d.ts",