@bilig/core 0.1.28 → 0.1.30
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/dist/cell-mutations-at.d.ts +26 -0
- package/dist/cell-mutations-at.js +70 -0
- package/dist/cell-mutations-at.js.map +1 -0
- package/dist/cell-store.d.ts +1 -0
- package/dist/cell-store.js +2 -0
- package/dist/cell-store.js.map +1 -1
- package/dist/engine/live.d.ts +1 -1
- package/dist/engine/live.js +64 -58
- package/dist/engine/live.js.map +1 -1
- package/dist/engine/runtime-state.d.ts +6 -0
- package/dist/engine/runtime-state.js.map +1 -1
- package/dist/engine/services/cell-state-service.d.ts +4 -0
- package/dist/engine/services/cell-state-service.js +20 -7
- package/dist/engine/services/cell-state-service.js.map +1 -1
- package/dist/engine/services/formula-binding-service.d.ts +11 -2
- package/dist/engine/services/formula-binding-service.js +284 -7
- package/dist/engine/services/formula-binding-service.js.map +1 -1
- package/dist/engine/services/formula-evaluation-service.d.ts +3 -1
- package/dist/engine/services/formula-evaluation-service.js +30 -2
- package/dist/engine/services/formula-evaluation-service.js.map +1 -1
- package/dist/engine/services/formula-graph-service.d.ts +5 -1
- package/dist/engine/services/formula-graph-service.js +21 -4
- package/dist/engine/services/formula-graph-service.js.map +1 -1
- package/dist/engine/services/lookup-service.d.ts +51 -0
- package/dist/engine/services/lookup-service.js +370 -0
- package/dist/engine/services/lookup-service.js.map +1 -0
- package/dist/engine/services/mutation-history-fast-path.d.ts +17 -0
- package/dist/engine/services/mutation-history-fast-path.js +119 -0
- package/dist/engine/services/mutation-history-fast-path.js.map +1 -0
- package/dist/engine/services/mutation-service.d.ts +26 -2
- package/dist/engine/services/mutation-service.js +169 -36
- package/dist/engine/services/mutation-service.js.map +1 -1
- package/dist/engine/services/mutation-support-service.d.ts +27 -0
- package/dist/engine/services/mutation-support-service.js +114 -0
- package/dist/engine/services/mutation-support-service.js.map +1 -1
- package/dist/engine/services/operation-service.d.ts +3 -1
- package/dist/engine/services/operation-service.js +351 -75
- package/dist/engine/services/operation-service.js.map +1 -1
- package/dist/engine/services/pivot-service.d.ts +7 -0
- package/dist/engine/services/pivot-service.js +9 -1
- package/dist/engine/services/pivot-service.js.map +1 -1
- package/dist/engine/services/recalc-service.d.ts +2 -0
- package/dist/engine/services/recalc-service.js +15 -6
- package/dist/engine/services/recalc-service.js.map +1 -1
- package/dist/engine/services/runtime-scratch-service.d.ts +1 -0
- package/dist/engine/services/runtime-scratch-service.js +1 -0
- package/dist/engine/services/runtime-scratch-service.js.map +1 -1
- package/dist/engine-value-utils.d.ts +2 -0
- package/dist/engine-value-utils.js +28 -0
- package/dist/engine-value-utils.js.map +1 -1
- package/dist/engine.d.ts +12 -0
- package/dist/engine.js +35 -3
- package/dist/engine.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/literal-sheet-loader.d.ts +4 -0
- package/dist/literal-sheet-loader.js +69 -0
- package/dist/literal-sheet-loader.js.map +1 -0
- package/dist/wasm-facade.d.ts +2 -0
- package/dist/wasm-facade.js +34 -5
- package/dist/wasm-facade.js.map +1 -1
- package/dist/workbook-store.d.ts +6 -0
- package/dist/workbook-store.js +48 -0
- package/dist/workbook-store.js.map +1 -1
- package/package.json +5 -5
|
@@ -1,11 +1,24 @@
|
|
|
1
1
|
import { Effect } from "effect";
|
|
2
|
-
import {
|
|
2
|
+
import { formatAddress } from "@bilig/formula";
|
|
3
3
|
import { makeCellEntity } from "../../entity-ids.js";
|
|
4
4
|
import { batchOpOrder, compareOpOrder, createBatch, markBatchApplied, } from "../../replica-state.js";
|
|
5
|
-
import { emptyValue,
|
|
5
|
+
import { emptyValue, writeLiteralToCellStore } from "../../engine-value-utils.js";
|
|
6
6
|
import { spillDependencyKey, tableDependencyKey } from "../../engine-metadata-utils.js";
|
|
7
|
-
import { normalizeDefinedName, pivotKey } from "../../workbook-store.js";
|
|
7
|
+
import { makeCellKey, normalizeDefinedName, pivotKey, } from "../../workbook-store.js";
|
|
8
8
|
import { EngineMutationError } from "../errors.js";
|
|
9
|
+
const noopVersionStore = {
|
|
10
|
+
get() {
|
|
11
|
+
return undefined;
|
|
12
|
+
},
|
|
13
|
+
set() {
|
|
14
|
+
return;
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
const FAST_LITERAL_OVERWRITE_FLAGS = 2 /* CellFlags.HasFormula */ |
|
|
18
|
+
4 /* CellFlags.JsOnly */ |
|
|
19
|
+
8 /* CellFlags.InCycle */ |
|
|
20
|
+
64 /* CellFlags.SpillChild */ |
|
|
21
|
+
128 /* CellFlags.PivotOutput */;
|
|
9
22
|
function mutationErrorMessage(message, cause) {
|
|
10
23
|
return cause instanceof Error && cause.message.length > 0 ? cause.message : message;
|
|
11
24
|
}
|
|
@@ -25,6 +38,30 @@ export function createEngineOperationService(args) {
|
|
|
25
38
|
const emitBatch = (batch) => {
|
|
26
39
|
args.state.batchListeners.forEach((listener) => listener(batch));
|
|
27
40
|
};
|
|
41
|
+
const entityVersions = args.state.trackReplicaVersions
|
|
42
|
+
? args.state.entityVersions
|
|
43
|
+
: noopVersionStore;
|
|
44
|
+
const sheetDeleteVersions = args.state.trackReplicaVersions
|
|
45
|
+
? args.state.sheetDeleteVersions
|
|
46
|
+
: noopVersionStore;
|
|
47
|
+
const setEntityVersionForOp = (op, order) => {
|
|
48
|
+
if (!args.state.trackReplicaVersions) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
entityVersions.set(entityKeyForOp(op), order);
|
|
52
|
+
};
|
|
53
|
+
const setCellEntityVersion = (sheetName, address, order) => {
|
|
54
|
+
if (!args.state.trackReplicaVersions) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
entityVersions.set(`cell:${sheetName}!${address}`, order);
|
|
58
|
+
};
|
|
59
|
+
const setSheetDeleteVersion = (sheetName, order) => {
|
|
60
|
+
if (!args.state.trackReplicaVersions) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
sheetDeleteVersions.set(sheetName, order);
|
|
64
|
+
};
|
|
28
65
|
const pruneCellIfOrphaned = (cellIndex) => {
|
|
29
66
|
if (args.collectFormulaDependents(makeCellEntity(cellIndex)).length > 0) {
|
|
30
67
|
return;
|
|
@@ -98,6 +135,11 @@ export function createEngineOperationService(args) {
|
|
|
98
135
|
return assertNever(op);
|
|
99
136
|
}
|
|
100
137
|
};
|
|
138
|
+
const canFastPathLiteralOverwrite = (cellIndex) => {
|
|
139
|
+
const flags = args.state.workbook.cellStore.flags[cellIndex] ?? 0;
|
|
140
|
+
return ((flags & FAST_LITERAL_OVERWRITE_FLAGS) === 0 &&
|
|
141
|
+
args.state.formulas.get(cellIndex) === undefined);
|
|
142
|
+
};
|
|
101
143
|
const sheetDeleteBarrierForOp = (op) => {
|
|
102
144
|
switch (op.kind) {
|
|
103
145
|
case "upsertWorkbook":
|
|
@@ -131,20 +173,19 @@ export function createEngineOperationService(args) {
|
|
|
131
173
|
case "upsertSpillRange":
|
|
132
174
|
case "deleteSpillRange":
|
|
133
175
|
case "deletePivotTable":
|
|
134
|
-
return
|
|
176
|
+
return sheetDeleteVersions.get(op.sheetName);
|
|
135
177
|
case "setStyleRange":
|
|
136
178
|
case "setFormatRange":
|
|
137
|
-
return
|
|
179
|
+
return sheetDeleteVersions.get(op.range.sheetName);
|
|
138
180
|
case "upsertCellNumberFormat":
|
|
139
181
|
case "upsertCellStyle":
|
|
140
182
|
return undefined;
|
|
141
183
|
case "upsertSheet":
|
|
142
|
-
return
|
|
184
|
+
return sheetDeleteVersions.get(op.name);
|
|
143
185
|
case "renameSheet":
|
|
144
|
-
return
|
|
186
|
+
return sheetDeleteVersions.get(op.oldName);
|
|
145
187
|
case "upsertPivotTable":
|
|
146
|
-
return (
|
|
147
|
-
args.state.sheetDeleteVersions.get(op.source.sheetName));
|
|
188
|
+
return (sheetDeleteVersions.get(op.sheetName) ?? sheetDeleteVersions.get(op.source.sheetName));
|
|
148
189
|
default:
|
|
149
190
|
return assertNever(op);
|
|
150
191
|
}
|
|
@@ -154,7 +195,7 @@ export function createEngineOperationService(args) {
|
|
|
154
195
|
if (sheetDeleteOrder && compareOpOrder(order, sheetDeleteOrder) <= 0) {
|
|
155
196
|
return false;
|
|
156
197
|
}
|
|
157
|
-
const existingOrder =
|
|
198
|
+
const existingOrder = entityVersions.get(entityKeyForOp(op));
|
|
158
199
|
if (existingOrder && compareOpOrder(order, existingOrder) <= 0) {
|
|
159
200
|
return false;
|
|
160
201
|
}
|
|
@@ -167,7 +208,7 @@ export function createEngineOperationService(args) {
|
|
|
167
208
|
else {
|
|
168
209
|
args.state.workbook.deleteSpill(op.sheetName, op.address);
|
|
169
210
|
}
|
|
170
|
-
|
|
211
|
+
setEntityVersionForOp(op, order);
|
|
171
212
|
return collectTrackedDependents(args.reverseState.reverseSpillEdges, [
|
|
172
213
|
spillDependencyKey(op.sheetName, op.address),
|
|
173
214
|
]);
|
|
@@ -183,17 +224,17 @@ export function createEngineOperationService(args) {
|
|
|
183
224
|
rows: op.rows,
|
|
184
225
|
cols: op.cols,
|
|
185
226
|
});
|
|
186
|
-
|
|
227
|
+
setEntityVersionForOp(op, order);
|
|
187
228
|
};
|
|
188
229
|
const applyPivotDeleteOp = (op, order) => {
|
|
189
230
|
const pivot = args.state.workbook.getPivot(op.sheetName, op.address);
|
|
190
231
|
if (!pivot) {
|
|
191
|
-
|
|
232
|
+
setEntityVersionForOp(op, order);
|
|
192
233
|
return [];
|
|
193
234
|
}
|
|
194
235
|
const changedPivotOutputs = args.clearOwnedPivot(pivot);
|
|
195
236
|
args.state.workbook.deletePivot(op.sheetName, op.address);
|
|
196
|
-
|
|
237
|
+
setEntityVersionForOp(op, order);
|
|
197
238
|
return changedPivotOutputs;
|
|
198
239
|
};
|
|
199
240
|
const applyDerivedOpNow = (op) => {
|
|
@@ -216,6 +257,7 @@ export function createEngineOperationService(args) {
|
|
|
216
257
|
}
|
|
217
258
|
};
|
|
218
259
|
const applyBatchNow = (batch, source, potentialNewCells) => {
|
|
260
|
+
const isRestore = source === "restore";
|
|
219
261
|
args.beginMutationCollection();
|
|
220
262
|
let changedInputCount = 0;
|
|
221
263
|
let formulaChangedCount = 0;
|
|
@@ -223,6 +265,7 @@ export function createEngineOperationService(args) {
|
|
|
223
265
|
let topologyChanged = false;
|
|
224
266
|
let sheetDeleted = false;
|
|
225
267
|
let structuralInvalidation = false;
|
|
268
|
+
let compileMs = 0;
|
|
226
269
|
const invalidatedRanges = [];
|
|
227
270
|
const invalidatedRows = [];
|
|
228
271
|
const invalidatedColumns = [];
|
|
@@ -242,24 +285,24 @@ export function createEngineOperationService(args) {
|
|
|
242
285
|
switch (op.kind) {
|
|
243
286
|
case "upsertWorkbook":
|
|
244
287
|
args.state.workbook.workbookName = op.name;
|
|
245
|
-
|
|
288
|
+
setEntityVersionForOp(op, order);
|
|
246
289
|
break;
|
|
247
290
|
case "setWorkbookMetadata":
|
|
248
291
|
args.state.workbook.setWorkbookProperty(op.key, op.value);
|
|
249
|
-
|
|
292
|
+
setEntityVersionForOp(op, order);
|
|
250
293
|
break;
|
|
251
294
|
case "setCalculationSettings":
|
|
252
295
|
args.state.workbook.setCalculationSettings(op.settings);
|
|
253
|
-
|
|
296
|
+
setEntityVersionForOp(op, order);
|
|
254
297
|
break;
|
|
255
298
|
case "setVolatileContext":
|
|
256
299
|
args.state.workbook.setVolatileContext(op.context);
|
|
257
|
-
|
|
300
|
+
setEntityVersionForOp(op, order);
|
|
258
301
|
break;
|
|
259
302
|
case "upsertSheet": {
|
|
260
303
|
args.state.workbook.createSheet(op.name, op.order, op.id);
|
|
261
|
-
|
|
262
|
-
const tombstone =
|
|
304
|
+
setEntityVersionForOp(op, order);
|
|
305
|
+
const tombstone = sheetDeleteVersions.get(op.name);
|
|
263
306
|
if (!tombstone || compareOpOrder(order, tombstone) > 0) {
|
|
264
307
|
args.state.sheetDeleteVersions.delete(op.name);
|
|
265
308
|
}
|
|
@@ -271,10 +314,12 @@ export function createEngineOperationService(args) {
|
|
|
271
314
|
}
|
|
272
315
|
case "renameSheet": {
|
|
273
316
|
const renamedSheet = args.state.workbook.renameSheet(op.oldName, op.newName);
|
|
274
|
-
args.state.
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
317
|
+
if (args.state.trackReplicaVersions) {
|
|
318
|
+
entityVersions.set(`sheet:${op.oldName}`, order);
|
|
319
|
+
entityVersions.set(`sheet:${op.newName}`, order);
|
|
320
|
+
}
|
|
321
|
+
setSheetDeleteVersion(op.oldName, order);
|
|
322
|
+
const renamedTombstone = sheetDeleteVersions.get(op.newName);
|
|
278
323
|
if (!renamedTombstone || compareOpOrder(order, renamedTombstone) > 0) {
|
|
279
324
|
args.state.sheetDeleteVersions.delete(op.newName);
|
|
280
325
|
}
|
|
@@ -298,8 +343,8 @@ export function createEngineOperationService(args) {
|
|
|
298
343
|
changedInputCount += removal.changedInputCount;
|
|
299
344
|
formulaChangedCount += removal.formulaChangedCount;
|
|
300
345
|
explicitChangedCount = removal.explicitChangedCount;
|
|
301
|
-
|
|
302
|
-
|
|
346
|
+
setEntityVersionForOp(op, order);
|
|
347
|
+
setSheetDeleteVersion(op.name, order);
|
|
303
348
|
topologyChanged = true;
|
|
304
349
|
sheetDeleted = true;
|
|
305
350
|
structuralInvalidation = true;
|
|
@@ -322,7 +367,7 @@ export function createEngineOperationService(args) {
|
|
|
322
367
|
topologyChanged = true;
|
|
323
368
|
structuralInvalidation = true;
|
|
324
369
|
refreshAllPivots = true;
|
|
325
|
-
|
|
370
|
+
setEntityVersionForOp(op, order);
|
|
326
371
|
break;
|
|
327
372
|
}
|
|
328
373
|
case "updateRowMetadata":
|
|
@@ -332,7 +377,7 @@ export function createEngineOperationService(args) {
|
|
|
332
377
|
startIndex: op.start,
|
|
333
378
|
endIndex: op.start + op.count - 1,
|
|
334
379
|
});
|
|
335
|
-
|
|
380
|
+
setEntityVersionForOp(op, order);
|
|
336
381
|
break;
|
|
337
382
|
case "updateColumnMetadata":
|
|
338
383
|
args.state.workbook.setColumnMetadata(op.sheetName, op.start, op.count, op.size, op.hidden);
|
|
@@ -341,44 +386,44 @@ export function createEngineOperationService(args) {
|
|
|
341
386
|
startIndex: op.start,
|
|
342
387
|
endIndex: op.start + op.count - 1,
|
|
343
388
|
});
|
|
344
|
-
|
|
389
|
+
setEntityVersionForOp(op, order);
|
|
345
390
|
break;
|
|
346
391
|
case "setFreezePane":
|
|
347
392
|
args.state.workbook.setFreezePane(op.sheetName, op.rows, op.cols);
|
|
348
393
|
structuralInvalidation = true;
|
|
349
|
-
|
|
394
|
+
setEntityVersionForOp(op, order);
|
|
350
395
|
break;
|
|
351
396
|
case "clearFreezePane":
|
|
352
397
|
args.state.workbook.clearFreezePane(op.sheetName);
|
|
353
398
|
structuralInvalidation = true;
|
|
354
|
-
|
|
399
|
+
setEntityVersionForOp(op, order);
|
|
355
400
|
break;
|
|
356
401
|
case "setFilter":
|
|
357
402
|
args.state.workbook.setFilter(op.sheetName, op.range);
|
|
358
403
|
structuralInvalidation = true;
|
|
359
|
-
|
|
404
|
+
setEntityVersionForOp(op, order);
|
|
360
405
|
break;
|
|
361
406
|
case "clearFilter":
|
|
362
407
|
args.state.workbook.deleteFilter(op.sheetName, op.range);
|
|
363
408
|
structuralInvalidation = true;
|
|
364
|
-
|
|
409
|
+
setEntityVersionForOp(op, order);
|
|
365
410
|
break;
|
|
366
411
|
case "setSort":
|
|
367
412
|
args.state.workbook.setSort(op.sheetName, op.range, op.keys);
|
|
368
413
|
structuralInvalidation = true;
|
|
369
|
-
|
|
414
|
+
setEntityVersionForOp(op, order);
|
|
370
415
|
break;
|
|
371
416
|
case "clearSort":
|
|
372
417
|
args.state.workbook.deleteSort(op.sheetName, op.range);
|
|
373
418
|
structuralInvalidation = true;
|
|
374
|
-
|
|
419
|
+
setEntityVersionForOp(op, order);
|
|
375
420
|
break;
|
|
376
421
|
case "upsertTable": {
|
|
377
422
|
args.state.workbook.setTable(op.table);
|
|
378
423
|
const reboundCount = formulaChangedCount;
|
|
379
424
|
formulaChangedCount = args.rebindTableDependents([tableDependencyKey(op.table.name)], formulaChangedCount);
|
|
380
425
|
topologyChanged = topologyChanged || formulaChangedCount !== reboundCount;
|
|
381
|
-
|
|
426
|
+
setEntityVersionForOp(op, order);
|
|
382
427
|
break;
|
|
383
428
|
}
|
|
384
429
|
case "deleteTable": {
|
|
@@ -386,7 +431,7 @@ export function createEngineOperationService(args) {
|
|
|
386
431
|
const reboundCount = formulaChangedCount;
|
|
387
432
|
formulaChangedCount = args.rebindTableDependents([tableDependencyKey(op.name)], formulaChangedCount);
|
|
388
433
|
topologyChanged = topologyChanged || formulaChangedCount !== reboundCount;
|
|
389
|
-
|
|
434
|
+
setEntityVersionForOp(op, order);
|
|
390
435
|
break;
|
|
391
436
|
}
|
|
392
437
|
case "upsertSpillRange":
|
|
@@ -397,15 +442,18 @@ export function createEngineOperationService(args) {
|
|
|
397
442
|
break;
|
|
398
443
|
}
|
|
399
444
|
case "setCellValue": {
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
445
|
+
if (!isRestore) {
|
|
446
|
+
const existingIndex = args.state.workbook.getCellIndex(op.sheetName, op.address);
|
|
447
|
+
if (existingIndex !== undefined) {
|
|
448
|
+
changedInputCount = args.markPivotRootsChanged(args.clearPivotForCell(existingIndex), changedInputCount);
|
|
449
|
+
}
|
|
403
450
|
}
|
|
404
451
|
const cellIndex = args.ensureCellTracked(op.sheetName, op.address);
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
452
|
+
if (!isRestore) {
|
|
453
|
+
changedInputCount = args.markSpillRootsChanged(args.clearOwnedSpill(cellIndex), changedInputCount);
|
|
454
|
+
topologyChanged = args.removeFormula(cellIndex) || topologyChanged;
|
|
455
|
+
}
|
|
456
|
+
writeLiteralToCellStore(args.state.workbook.cellStore, cellIndex, op.value, args.state.strings);
|
|
409
457
|
args.state.workbook.cellStore.flags[cellIndex] =
|
|
410
458
|
(args.state.workbook.cellStore.flags[cellIndex] ?? 0) &
|
|
411
459
|
~(2 /* CellFlags.HasFormula */ |
|
|
@@ -413,72 +461,82 @@ export function createEngineOperationService(args) {
|
|
|
413
461
|
8 /* CellFlags.InCycle */ |
|
|
414
462
|
64 /* CellFlags.SpillChild */ |
|
|
415
463
|
128 /* CellFlags.PivotOutput */);
|
|
416
|
-
|
|
464
|
+
if (!isRestore) {
|
|
465
|
+
pruneCellIfOrphaned(cellIndex);
|
|
466
|
+
}
|
|
417
467
|
changedInputCount = args.markInputChanged(cellIndex, changedInputCount);
|
|
418
|
-
|
|
419
|
-
|
|
468
|
+
if (!isRestore) {
|
|
469
|
+
explicitChangedCount = args.markExplicitChanged(cellIndex, explicitChangedCount);
|
|
470
|
+
setEntityVersionForOp(op, order);
|
|
471
|
+
}
|
|
420
472
|
break;
|
|
421
473
|
}
|
|
422
474
|
case "setCellFormula": {
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
475
|
+
if (!isRestore) {
|
|
476
|
+
const existingIndex = args.state.workbook.getCellIndex(op.sheetName, op.address);
|
|
477
|
+
if (existingIndex !== undefined) {
|
|
478
|
+
changedInputCount = args.markPivotRootsChanged(args.clearPivotForCell(existingIndex), changedInputCount);
|
|
479
|
+
}
|
|
426
480
|
}
|
|
427
481
|
const cellIndex = args.ensureCellTracked(op.sheetName, op.address);
|
|
428
|
-
|
|
429
|
-
|
|
482
|
+
if (!isRestore) {
|
|
483
|
+
changedInputCount = args.markSpillRootsChanged(args.clearOwnedSpill(cellIndex), changedInputCount);
|
|
484
|
+
}
|
|
485
|
+
const compileStarted = isRestore ? 0 : performance.now();
|
|
430
486
|
try {
|
|
431
487
|
args.bindFormula(cellIndex, op.sheetName, op.formula);
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
});
|
|
488
|
+
if (!isRestore) {
|
|
489
|
+
compileMs += performance.now() - compileStarted;
|
|
490
|
+
}
|
|
436
491
|
formulaChangedCount = args.markFormulaChanged(cellIndex, formulaChangedCount);
|
|
437
492
|
topologyChanged = true;
|
|
438
493
|
}
|
|
439
494
|
catch {
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
});
|
|
495
|
+
if (!isRestore) {
|
|
496
|
+
compileMs += performance.now() - compileStarted;
|
|
497
|
+
}
|
|
444
498
|
topologyChanged = args.removeFormula(cellIndex) || topologyChanged;
|
|
445
499
|
args.setInvalidFormulaValue(cellIndex);
|
|
446
500
|
changedInputCount = args.markInputChanged(cellIndex, changedInputCount);
|
|
447
501
|
}
|
|
448
|
-
|
|
449
|
-
|
|
502
|
+
if (!isRestore) {
|
|
503
|
+
explicitChangedCount = args.markExplicitChanged(cellIndex, explicitChangedCount);
|
|
504
|
+
setEntityVersionForOp(op, order);
|
|
505
|
+
}
|
|
450
506
|
break;
|
|
451
507
|
}
|
|
452
508
|
case "setCellFormat": {
|
|
453
509
|
const cellIndex = args.ensureCellTracked(op.sheetName, op.address);
|
|
454
510
|
args.state.workbook.setCellFormat(cellIndex, op.format);
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
511
|
+
if (!isRestore) {
|
|
512
|
+
pruneCellIfOrphaned(cellIndex);
|
|
513
|
+
explicitChangedCount = args.markExplicitChanged(cellIndex, explicitChangedCount);
|
|
514
|
+
setEntityVersionForOp(op, order);
|
|
515
|
+
}
|
|
458
516
|
break;
|
|
459
517
|
}
|
|
460
518
|
case "upsertCellStyle":
|
|
461
519
|
args.state.workbook.upsertCellStyle(op.style);
|
|
462
|
-
|
|
520
|
+
setEntityVersionForOp(op, order);
|
|
463
521
|
break;
|
|
464
522
|
case "upsertCellNumberFormat":
|
|
465
523
|
args.state.workbook.upsertCellNumberFormat(op.format);
|
|
466
|
-
|
|
524
|
+
setEntityVersionForOp(op, order);
|
|
467
525
|
break;
|
|
468
526
|
case "setStyleRange":
|
|
469
527
|
args.state.workbook.setStyleRange(op.range, op.styleId);
|
|
470
528
|
invalidatedRanges.push(op.range);
|
|
471
|
-
|
|
529
|
+
setEntityVersionForOp(op, order);
|
|
472
530
|
break;
|
|
473
531
|
case "setFormatRange":
|
|
474
532
|
args.state.workbook.setFormatRange(op.range, op.formatId);
|
|
475
533
|
invalidatedRanges.push(op.range);
|
|
476
|
-
|
|
534
|
+
setEntityVersionForOp(op, order);
|
|
477
535
|
break;
|
|
478
536
|
case "clearCell": {
|
|
479
537
|
const cellIndex = args.state.workbook.getCellIndex(op.sheetName, op.address);
|
|
480
538
|
if (cellIndex === undefined) {
|
|
481
|
-
|
|
539
|
+
setEntityVersionForOp(op, order);
|
|
482
540
|
break;
|
|
483
541
|
}
|
|
484
542
|
changedInputCount = args.markPivotRootsChanged(args.clearPivotForCell(cellIndex), changedInputCount);
|
|
@@ -495,7 +553,7 @@ export function createEngineOperationService(args) {
|
|
|
495
553
|
pruneCellIfOrphaned(cellIndex);
|
|
496
554
|
changedInputCount = args.markInputChanged(cellIndex, changedInputCount);
|
|
497
555
|
explicitChangedCount = args.markExplicitChanged(cellIndex, explicitChangedCount);
|
|
498
|
-
|
|
556
|
+
setEntityVersionForOp(op, order);
|
|
499
557
|
break;
|
|
500
558
|
}
|
|
501
559
|
case "upsertDefinedName": {
|
|
@@ -504,7 +562,7 @@ export function createEngineOperationService(args) {
|
|
|
504
562
|
const reboundCount = formulaChangedCount;
|
|
505
563
|
formulaChangedCount = args.rebindDefinedNameDependents([normalizedName], formulaChangedCount);
|
|
506
564
|
topologyChanged = topologyChanged || formulaChangedCount !== reboundCount;
|
|
507
|
-
|
|
565
|
+
setEntityVersionForOp(op, order);
|
|
508
566
|
break;
|
|
509
567
|
}
|
|
510
568
|
case "deleteDefinedName": {
|
|
@@ -513,7 +571,7 @@ export function createEngineOperationService(args) {
|
|
|
513
571
|
const reboundCount = formulaChangedCount;
|
|
514
572
|
formulaChangedCount = args.rebindDefinedNameDependents([normalizedName], formulaChangedCount);
|
|
515
573
|
topologyChanged = topologyChanged || formulaChangedCount !== reboundCount;
|
|
516
|
-
|
|
574
|
+
setEntityVersionForOp(op, order);
|
|
517
575
|
break;
|
|
518
576
|
}
|
|
519
577
|
case "upsertPivotTable":
|
|
@@ -557,21 +615,25 @@ export function createEngineOperationService(args) {
|
|
|
557
615
|
const changedInputArray = args.getChangedInputBuffer().subarray(0, changedInputCount);
|
|
558
616
|
let recalculated = args.recalculate(args.composeMutationRoots(changedInputCount, formulaChangedCount), changedInputArray);
|
|
559
617
|
recalculated = args.reconcilePivotOutputs(recalculated, refreshAllPivots);
|
|
560
|
-
const changed =
|
|
618
|
+
const changed = isRestore
|
|
619
|
+
? new Uint32Array()
|
|
620
|
+
: args.composeEventChanges(recalculated, explicitChangedCount);
|
|
561
621
|
const lastMetrics = {
|
|
562
622
|
...args.state.getLastMetrics(),
|
|
563
623
|
batchId: args.state.getLastMetrics().batchId + 1,
|
|
564
624
|
changedInputCount: changedInputCount + formulaChangedCount,
|
|
625
|
+
compileMs,
|
|
565
626
|
};
|
|
566
627
|
args.state.setLastMetrics(lastMetrics);
|
|
567
628
|
const event = {
|
|
568
629
|
kind: "batch",
|
|
569
|
-
invalidation: sheetDeleted || structuralInvalidation ? "full" : "cells",
|
|
630
|
+
invalidation: isRestore || sheetDeleted || structuralInvalidation ? "full" : "cells",
|
|
570
631
|
changedCellIndices: changed,
|
|
571
632
|
invalidatedRanges,
|
|
572
633
|
invalidatedRows,
|
|
573
634
|
invalidatedColumns,
|
|
574
635
|
metrics: lastMetrics,
|
|
636
|
+
explicitChangedCount,
|
|
575
637
|
};
|
|
576
638
|
if (event.invalidation === "full") {
|
|
577
639
|
args.state.events.emitAllWatched(event);
|
|
@@ -587,6 +649,209 @@ export function createEngineOperationService(args) {
|
|
|
587
649
|
args.state.redoStack.length = 0;
|
|
588
650
|
}
|
|
589
651
|
};
|
|
652
|
+
const applyCellMutationsAtNow = (refs, batch, source, potentialNewCells) => {
|
|
653
|
+
const isRestore = source === "restore";
|
|
654
|
+
args.beginMutationCollection();
|
|
655
|
+
let changedInputCount = 0;
|
|
656
|
+
let formulaChangedCount = 0;
|
|
657
|
+
let explicitChangedCount = 0;
|
|
658
|
+
let topologyChanged = false;
|
|
659
|
+
let compileMs = 0;
|
|
660
|
+
const reservedNewCells = potentialNewCells ?? refs.length;
|
|
661
|
+
args.state.workbook.cellStore.ensureCapacity(args.state.workbook.cellStore.size + reservedNewCells);
|
|
662
|
+
args.resetMaterializedCellScratch(reservedNewCells);
|
|
663
|
+
const sheetNameById = new Map();
|
|
664
|
+
const resolveSheetName = (sheetId) => {
|
|
665
|
+
const cached = sheetNameById.get(sheetId);
|
|
666
|
+
if (cached !== undefined) {
|
|
667
|
+
return cached;
|
|
668
|
+
}
|
|
669
|
+
const sheet = args.state.workbook.getSheetById(sheetId);
|
|
670
|
+
if (!sheet) {
|
|
671
|
+
throw new Error(`Unknown sheet id: ${sheetId}`);
|
|
672
|
+
}
|
|
673
|
+
sheetNameById.set(sheetId, sheet.name);
|
|
674
|
+
return sheet.name;
|
|
675
|
+
};
|
|
676
|
+
args.setBatchMutationDepth(args.getBatchMutationDepth() + 1);
|
|
677
|
+
try {
|
|
678
|
+
args.state.workbook.withBatchedColumnVersionUpdates(() => {
|
|
679
|
+
refs.forEach((ref, refIndex) => {
|
|
680
|
+
const { sheetId, mutation } = ref;
|
|
681
|
+
const order = args.state.trackReplicaVersions ? batchOpOrder(batch, refIndex) : undefined;
|
|
682
|
+
const existingIndex = args.state.workbook.cellKeyToIndex.get(makeCellKey(sheetId, mutation.row, mutation.col));
|
|
683
|
+
switch (mutation.kind) {
|
|
684
|
+
case "setCellValue": {
|
|
685
|
+
if (existingIndex !== undefined && canFastPathLiteralOverwrite(existingIndex)) {
|
|
686
|
+
writeLiteralToCellStore(args.state.workbook.cellStore, existingIndex, mutation.value, args.state.strings);
|
|
687
|
+
changedInputCount = args.markInputChanged(existingIndex, changedInputCount);
|
|
688
|
+
if (!isRestore) {
|
|
689
|
+
explicitChangedCount = args.markExplicitChanged(existingIndex, explicitChangedCount);
|
|
690
|
+
}
|
|
691
|
+
if (!isRestore && args.state.trackReplicaVersions) {
|
|
692
|
+
setCellEntityVersion(resolveSheetName(sheetId), formatAddress(mutation.row, mutation.col), order);
|
|
693
|
+
}
|
|
694
|
+
break;
|
|
695
|
+
}
|
|
696
|
+
if (existingIndex !== undefined) {
|
|
697
|
+
changedInputCount = args.markPivotRootsChanged(args.clearPivotForCell(existingIndex), changedInputCount);
|
|
698
|
+
}
|
|
699
|
+
const cellIndex = args.state.workbook.ensureCellAt(sheetId, mutation.row, mutation.col).cellIndex;
|
|
700
|
+
if (!isRestore) {
|
|
701
|
+
changedInputCount = args.markSpillRootsChanged(args.clearOwnedSpill(cellIndex), changedInputCount);
|
|
702
|
+
topologyChanged = args.removeFormula(cellIndex) || topologyChanged;
|
|
703
|
+
}
|
|
704
|
+
writeLiteralToCellStore(args.state.workbook.cellStore, cellIndex, mutation.value, args.state.strings);
|
|
705
|
+
args.state.workbook.cellStore.flags[cellIndex] =
|
|
706
|
+
(args.state.workbook.cellStore.flags[cellIndex] ?? 0) &
|
|
707
|
+
~(2 /* CellFlags.HasFormula */ |
|
|
708
|
+
4 /* CellFlags.JsOnly */ |
|
|
709
|
+
8 /* CellFlags.InCycle */ |
|
|
710
|
+
64 /* CellFlags.SpillChild */ |
|
|
711
|
+
128 /* CellFlags.PivotOutput */);
|
|
712
|
+
if (!isRestore) {
|
|
713
|
+
pruneCellIfOrphaned(cellIndex);
|
|
714
|
+
}
|
|
715
|
+
changedInputCount = args.markInputChanged(cellIndex, changedInputCount);
|
|
716
|
+
if (!isRestore) {
|
|
717
|
+
explicitChangedCount = args.markExplicitChanged(cellIndex, explicitChangedCount);
|
|
718
|
+
}
|
|
719
|
+
if (!isRestore && args.state.trackReplicaVersions) {
|
|
720
|
+
setCellEntityVersion(resolveSheetName(sheetId), formatAddress(mutation.row, mutation.col), order);
|
|
721
|
+
}
|
|
722
|
+
break;
|
|
723
|
+
}
|
|
724
|
+
case "setCellFormula": {
|
|
725
|
+
const sheetName = resolveSheetName(sheetId);
|
|
726
|
+
if (!isRestore && existingIndex !== undefined) {
|
|
727
|
+
changedInputCount = args.markPivotRootsChanged(args.clearPivotForCell(existingIndex), changedInputCount);
|
|
728
|
+
}
|
|
729
|
+
const cellIndex = args.state.workbook.ensureCellAt(sheetId, mutation.row, mutation.col).cellIndex;
|
|
730
|
+
if (!isRestore) {
|
|
731
|
+
changedInputCount = args.markSpillRootsChanged(args.clearOwnedSpill(cellIndex), changedInputCount);
|
|
732
|
+
}
|
|
733
|
+
const compileStarted = isRestore ? 0 : performance.now();
|
|
734
|
+
try {
|
|
735
|
+
args.bindFormula(cellIndex, sheetName, mutation.formula);
|
|
736
|
+
if (!isRestore) {
|
|
737
|
+
compileMs += performance.now() - compileStarted;
|
|
738
|
+
}
|
|
739
|
+
formulaChangedCount = args.markFormulaChanged(cellIndex, formulaChangedCount);
|
|
740
|
+
topologyChanged = true;
|
|
741
|
+
}
|
|
742
|
+
catch {
|
|
743
|
+
if (!isRestore) {
|
|
744
|
+
compileMs += performance.now() - compileStarted;
|
|
745
|
+
}
|
|
746
|
+
topologyChanged = args.removeFormula(cellIndex) || topologyChanged;
|
|
747
|
+
args.setInvalidFormulaValue(cellIndex);
|
|
748
|
+
changedInputCount = args.markInputChanged(cellIndex, changedInputCount);
|
|
749
|
+
}
|
|
750
|
+
if (!isRestore) {
|
|
751
|
+
explicitChangedCount = args.markExplicitChanged(cellIndex, explicitChangedCount);
|
|
752
|
+
}
|
|
753
|
+
if (!isRestore && args.state.trackReplicaVersions) {
|
|
754
|
+
setCellEntityVersion(sheetName, formatAddress(mutation.row, mutation.col), order);
|
|
755
|
+
}
|
|
756
|
+
break;
|
|
757
|
+
}
|
|
758
|
+
case "clearCell": {
|
|
759
|
+
if (existingIndex !== undefined && canFastPathLiteralOverwrite(existingIndex)) {
|
|
760
|
+
args.state.workbook.cellStore.setValue(existingIndex, emptyValue());
|
|
761
|
+
changedInputCount = args.markInputChanged(existingIndex, changedInputCount);
|
|
762
|
+
if (!isRestore) {
|
|
763
|
+
explicitChangedCount = args.markExplicitChanged(existingIndex, explicitChangedCount);
|
|
764
|
+
}
|
|
765
|
+
if (!isRestore && args.state.trackReplicaVersions) {
|
|
766
|
+
setCellEntityVersion(resolveSheetName(sheetId), formatAddress(mutation.row, mutation.col), order);
|
|
767
|
+
}
|
|
768
|
+
break;
|
|
769
|
+
}
|
|
770
|
+
if (existingIndex === undefined) {
|
|
771
|
+
if (!isRestore && args.state.trackReplicaVersions) {
|
|
772
|
+
setCellEntityVersion(resolveSheetName(sheetId), formatAddress(mutation.row, mutation.col), order);
|
|
773
|
+
}
|
|
774
|
+
break;
|
|
775
|
+
}
|
|
776
|
+
changedInputCount = args.markPivotRootsChanged(args.clearPivotForCell(existingIndex), changedInputCount);
|
|
777
|
+
changedInputCount = args.markSpillRootsChanged(args.clearOwnedSpill(existingIndex), changedInputCount);
|
|
778
|
+
topologyChanged = args.removeFormula(existingIndex) || topologyChanged;
|
|
779
|
+
args.state.workbook.cellStore.setValue(existingIndex, emptyValue());
|
|
780
|
+
args.state.workbook.cellStore.flags[existingIndex] =
|
|
781
|
+
(args.state.workbook.cellStore.flags[existingIndex] ?? 0) &
|
|
782
|
+
~(2 /* CellFlags.HasFormula */ |
|
|
783
|
+
4 /* CellFlags.JsOnly */ |
|
|
784
|
+
8 /* CellFlags.InCycle */ |
|
|
785
|
+
64 /* CellFlags.SpillChild */ |
|
|
786
|
+
128 /* CellFlags.PivotOutput */);
|
|
787
|
+
if (!isRestore) {
|
|
788
|
+
pruneCellIfOrphaned(existingIndex);
|
|
789
|
+
}
|
|
790
|
+
changedInputCount = args.markInputChanged(existingIndex, changedInputCount);
|
|
791
|
+
if (!isRestore) {
|
|
792
|
+
explicitChangedCount = args.markExplicitChanged(existingIndex, explicitChangedCount);
|
|
793
|
+
}
|
|
794
|
+
if (!isRestore && args.state.trackReplicaVersions) {
|
|
795
|
+
setCellEntityVersion(resolveSheetName(sheetId), formatAddress(mutation.row, mutation.col), order);
|
|
796
|
+
}
|
|
797
|
+
break;
|
|
798
|
+
}
|
|
799
|
+
default:
|
|
800
|
+
assertNever(mutation);
|
|
801
|
+
}
|
|
802
|
+
});
|
|
803
|
+
});
|
|
804
|
+
const reboundCount = formulaChangedCount;
|
|
805
|
+
formulaChangedCount = args.syncDynamicRanges(formulaChangedCount);
|
|
806
|
+
topologyChanged = topologyChanged || formulaChangedCount !== reboundCount;
|
|
807
|
+
}
|
|
808
|
+
finally {
|
|
809
|
+
args.setBatchMutationDepth(args.getBatchMutationDepth() - 1);
|
|
810
|
+
args.flushWasmProgramSync();
|
|
811
|
+
}
|
|
812
|
+
markBatchApplied(args.state.replicaState, batch);
|
|
813
|
+
if (refs.length === 0) {
|
|
814
|
+
if (!isRestore) {
|
|
815
|
+
emitBatch(batch);
|
|
816
|
+
}
|
|
817
|
+
return;
|
|
818
|
+
}
|
|
819
|
+
if (topologyChanged) {
|
|
820
|
+
args.rebuildTopoRanks();
|
|
821
|
+
args.detectCycles();
|
|
822
|
+
}
|
|
823
|
+
formulaChangedCount = args.markVolatileFormulasChanged(formulaChangedCount);
|
|
824
|
+
const changedInputArray = args.getChangedInputBuffer().subarray(0, changedInputCount);
|
|
825
|
+
let recalculated = args.recalculate(args.composeMutationRoots(changedInputCount, formulaChangedCount), changedInputArray);
|
|
826
|
+
recalculated = args.reconcilePivotOutputs(recalculated, false);
|
|
827
|
+
const changed = isRestore
|
|
828
|
+
? new Uint32Array()
|
|
829
|
+
: args.composeEventChanges(recalculated, explicitChangedCount);
|
|
830
|
+
const lastMetrics = {
|
|
831
|
+
...args.state.getLastMetrics(),
|
|
832
|
+
batchId: args.state.getLastMetrics().batchId + 1,
|
|
833
|
+
changedInputCount: changedInputCount + formulaChangedCount,
|
|
834
|
+
compileMs,
|
|
835
|
+
};
|
|
836
|
+
args.state.setLastMetrics(lastMetrics);
|
|
837
|
+
const event = {
|
|
838
|
+
kind: "batch",
|
|
839
|
+
invalidation: isRestore ? "full" : "cells",
|
|
840
|
+
changedCellIndices: changed,
|
|
841
|
+
invalidatedRanges: [],
|
|
842
|
+
invalidatedRows: [],
|
|
843
|
+
invalidatedColumns: [],
|
|
844
|
+
metrics: lastMetrics,
|
|
845
|
+
explicitChangedCount,
|
|
846
|
+
};
|
|
847
|
+
if (isRestore) {
|
|
848
|
+
args.state.events.emitAllWatched(event);
|
|
849
|
+
return;
|
|
850
|
+
}
|
|
851
|
+
args.state.events.emit(event, changed, (cellIndex) => args.state.workbook.getQualifiedAddress(cellIndex));
|
|
852
|
+
void args.state.getSyncClientConnection()?.send(batch);
|
|
853
|
+
emitBatch(batch);
|
|
854
|
+
};
|
|
590
855
|
return {
|
|
591
856
|
applyBatch(batch, source, potentialNewCells) {
|
|
592
857
|
return Effect.try({
|
|
@@ -599,6 +864,17 @@ export function createEngineOperationService(args) {
|
|
|
599
864
|
}),
|
|
600
865
|
});
|
|
601
866
|
},
|
|
867
|
+
applyCellMutationsAt(refs, batch, source, potentialNewCells) {
|
|
868
|
+
return Effect.try({
|
|
869
|
+
try: () => {
|
|
870
|
+
applyCellMutationsAtNow(refs, batch, source, potentialNewCells);
|
|
871
|
+
},
|
|
872
|
+
catch: (cause) => new EngineMutationError({
|
|
873
|
+
message: mutationErrorMessage(`Failed to apply ${source} cell mutations`, cause),
|
|
874
|
+
cause,
|
|
875
|
+
}),
|
|
876
|
+
});
|
|
877
|
+
},
|
|
602
878
|
applyDerivedOp(op) {
|
|
603
879
|
return Effect.try({
|
|
604
880
|
try: () => applyDerivedOpNow(op),
|