@bilig/headless 0.1.102 → 0.3.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.
@@ -0,0 +1,842 @@
1
+ import { indexToColumn } from '@bilig/formula';
2
+ import { makeCellKey } from '@bilig/core';
3
+ import { ErrorCode, ValueTag } from '@bilig/protocol';
4
+ const COLUMN_LABEL_CACHE = [];
5
+ const DEFERRED_TRACKED_INDEX_CHANGES = new WeakMap();
6
+ const LAZY_PUBLIC_CHANGE_SOURCE_THRESHOLD = 256;
7
+ export function materializeTrackedIndexChangesWithMetadata(engine, changedCellIndices, options = {}) {
8
+ if (changedCellIndices.length === 0) {
9
+ return { changes: [], ordered: true };
10
+ }
11
+ const workbook = engine.workbook;
12
+ const cellStore = workbook.cellStore;
13
+ const formatAddressCached = (row, col) => {
14
+ let label = COLUMN_LABEL_CACHE[col];
15
+ if (label === undefined) {
16
+ label = indexToColumn(col);
17
+ COLUMN_LABEL_CACHE[col] = label;
18
+ }
19
+ return `${label}${row + 1}`;
20
+ };
21
+ if (options.lazy && options.trustedPhysicalSheetId !== undefined) {
22
+ const sheet = workbook.getSheetById(options.trustedPhysicalSheetId);
23
+ if (!sheet || sheet.structureVersion === 1) {
24
+ const split = options.trustedSortedSliceSplit !== undefined &&
25
+ options.trustedSortedSliceSplit > 0 &&
26
+ options.trustedSortedSliceSplit < changedCellIndices.length
27
+ ? options.trustedSortedSliceSplit
28
+ : undefined;
29
+ return {
30
+ changes: createLazyPhysicalTrackedIndexChanges(options.trustedPhysicalSheetId, sheet?.name ?? workbook.getSheetNameById(options.trustedPhysicalSheetId), cellStore, engine, changedCellIndices, formatAddressCached, split),
31
+ ordered: true,
32
+ };
33
+ }
34
+ }
35
+ let firstSheetId;
36
+ for (let index = 0; index < changedCellIndices.length; index += 1) {
37
+ const sheetId = cellStore.sheetIds[changedCellIndices[index]];
38
+ if (sheetId !== undefined) {
39
+ firstSheetId = sheetId;
40
+ break;
41
+ }
42
+ }
43
+ if (firstSheetId === undefined) {
44
+ return { changes: [], ordered: true };
45
+ }
46
+ if (options.lazy) {
47
+ const lazyPhysicalChanges = tryCreateLazyPhysicalTrackedIndexChanges(engine, changedCellIndices, firstSheetId, formatAddressCached, {
48
+ sortedSliceSplit: options.explicitChangedCount,
49
+ });
50
+ if (lazyPhysicalChanges) {
51
+ return { changes: lazyPhysicalChanges, ordered: true };
52
+ }
53
+ }
54
+ let isSingleSheetChangeSet = true;
55
+ for (let index = 0; index < changedCellIndices.length; index += 1) {
56
+ const sheetId = cellStore.sheetIds[changedCellIndices[index]];
57
+ if (sheetId !== undefined && sheetId !== firstSheetId) {
58
+ isSingleSheetChangeSet = false;
59
+ break;
60
+ }
61
+ }
62
+ const changes = [];
63
+ if (isSingleSheetChangeSet) {
64
+ const sheet = workbook.getSheetById(firstSheetId);
65
+ const sheetName = sheet?.name ?? workbook.getSheetNameById(firstSheetId);
66
+ const isPhysicalSheet = !sheet || sheet.structureVersion === 1;
67
+ const split = options.explicitChangedCount;
68
+ if (isPhysicalSheet &&
69
+ split !== undefined &&
70
+ split > 0 &&
71
+ split < changedCellIndices.length &&
72
+ isTrackedIndexSliceSorted(cellStore, changedCellIndices, 0, split) &&
73
+ isTrackedIndexSliceSorted(cellStore, changedCellIndices, split, changedCellIndices.length)) {
74
+ if (options.lazy && trackedIndicesAllBelongToSheet(cellStore, changedCellIndices, firstSheetId)) {
75
+ return {
76
+ changes: createLazyPhysicalTrackedIndexChanges(firstSheetId, sheetName, cellStore, engine, changedCellIndices, formatAddressCached, split),
77
+ ordered: true,
78
+ };
79
+ }
80
+ let explicitIndex = 0;
81
+ let recalculatedIndex = split;
82
+ while (explicitIndex < split && recalculatedIndex < changedCellIndices.length) {
83
+ const explicitCellIndex = changedCellIndices[explicitIndex];
84
+ const recalculatedCellIndex = changedCellIndices[recalculatedIndex];
85
+ if (compareTrackedPhysicalCellIndices(cellStore, explicitCellIndex, recalculatedCellIndex) <= 0) {
86
+ changes.push(readPhysicalTrackedIndexChange(explicitCellIndex, firstSheetId, sheetName, cellStore, engine, formatAddressCached));
87
+ explicitIndex += 1;
88
+ }
89
+ else {
90
+ changes.push(readPhysicalTrackedIndexChange(recalculatedCellIndex, firstSheetId, sheetName, cellStore, engine, formatAddressCached));
91
+ recalculatedIndex += 1;
92
+ }
93
+ }
94
+ while (explicitIndex < split) {
95
+ changes.push(readPhysicalTrackedIndexChange(changedCellIndices[explicitIndex], firstSheetId, sheetName, cellStore, engine, formatAddressCached));
96
+ explicitIndex += 1;
97
+ }
98
+ while (recalculatedIndex < changedCellIndices.length) {
99
+ changes.push(readPhysicalTrackedIndexChange(changedCellIndices[recalculatedIndex], firstSheetId, sheetName, cellStore, engine, formatAddressCached));
100
+ recalculatedIndex += 1;
101
+ }
102
+ return { changes, ordered: true };
103
+ }
104
+ if (options.lazy &&
105
+ isPhysicalSheet &&
106
+ trackedIndicesAllBelongToSheet(cellStore, changedCellIndices, firstSheetId) &&
107
+ isTrackedIndexSliceSorted(cellStore, changedCellIndices, 0, changedCellIndices.length)) {
108
+ return {
109
+ changes: createLazyPhysicalTrackedIndexChanges(firstSheetId, sheetName, cellStore, engine, changedCellIndices, formatAddressCached),
110
+ ordered: true,
111
+ };
112
+ }
113
+ let ordered = true;
114
+ let previousRow = -1;
115
+ let previousCol = -1;
116
+ for (let index = 0; index < changedCellIndices.length; index += 1) {
117
+ const cellIndex = changedCellIndices[index];
118
+ if (cellStore.sheetIds[cellIndex] !== firstSheetId) {
119
+ continue;
120
+ }
121
+ let row;
122
+ let col;
123
+ if (isPhysicalSheet) {
124
+ row = cellStore.rows[cellIndex];
125
+ col = cellStore.cols[cellIndex];
126
+ }
127
+ else {
128
+ const position = workbook.getCellPosition(cellIndex);
129
+ /* v8 ignore next -- defensive guard for stale logical indices without visible positions. */
130
+ if (!position) {
131
+ continue;
132
+ }
133
+ row = position.row;
134
+ col = position.col;
135
+ }
136
+ if (row < previousRow || (row === previousRow && col < previousCol)) {
137
+ ordered = false;
138
+ }
139
+ changes.push({
140
+ kind: 'cell',
141
+ address: { sheet: firstSheetId, row, col },
142
+ sheetName,
143
+ a1: formatAddressCached(row, col),
144
+ newValue: readTrackedCellValue(cellStore, cellIndex, engine),
145
+ });
146
+ previousRow = row;
147
+ previousCol = col;
148
+ }
149
+ return { changes, ordered };
150
+ }
151
+ const sheetNames = new Map();
152
+ const physicalSheetIds = new Set();
153
+ let ordered = true;
154
+ let previousSheetOrder = -1;
155
+ let previousRow = -1;
156
+ let previousCol = -1;
157
+ for (let index = 0; index < changedCellIndices.length; index += 1) {
158
+ const cellIndex = changedCellIndices[index];
159
+ const sheetId = cellStore.sheetIds[cellIndex];
160
+ if (sheetId === undefined) {
161
+ continue;
162
+ }
163
+ let sheetName = sheetNames.get(sheetId);
164
+ let sheetOrder = 0;
165
+ if (sheetName === undefined) {
166
+ const sheet = workbook.getSheetById(sheetId);
167
+ sheetName = sheet?.name ?? workbook.getSheetNameById(sheetId);
168
+ sheetOrder = sheet?.order ?? 0;
169
+ sheetNames.set(sheetId, sheetName);
170
+ if (!sheet || sheet.structureVersion === 1) {
171
+ physicalSheetIds.add(sheetId);
172
+ }
173
+ }
174
+ else {
175
+ sheetOrder = workbook.getSheetById(sheetId)?.order ?? 0;
176
+ }
177
+ let row;
178
+ let col;
179
+ if (physicalSheetIds.has(sheetId)) {
180
+ row = cellStore.rows[cellIndex];
181
+ col = cellStore.cols[cellIndex];
182
+ }
183
+ else {
184
+ const position = workbook.getCellPosition(cellIndex);
185
+ /* v8 ignore next -- defensive guard for stale logical indices without visible positions. */
186
+ if (!position) {
187
+ continue;
188
+ }
189
+ row = position.row;
190
+ col = position.col;
191
+ }
192
+ if (sheetOrder < previousSheetOrder ||
193
+ (sheetOrder === previousSheetOrder && (row < previousRow || (row === previousRow && col < previousCol)))) {
194
+ ordered = false;
195
+ }
196
+ changes.push({
197
+ kind: 'cell',
198
+ address: { sheet: sheetId, row, col },
199
+ sheetName,
200
+ a1: formatAddressCached(row, col),
201
+ newValue: readTrackedCellValue(cellStore, cellIndex, engine),
202
+ });
203
+ previousSheetOrder = sheetOrder;
204
+ previousRow = row;
205
+ previousCol = col;
206
+ }
207
+ return { changes, ordered };
208
+ }
209
+ export function materializeTrackedIndexChanges(engine, changedCellIndices, options = {}) {
210
+ return materializeTrackedIndexChangesWithMetadata(engine, changedCellIndices, options).changes;
211
+ }
212
+ export function materializeTrackedIndexChangeSourcesWithMetadata(engine, sources, options = {}) {
213
+ if (sources.length === 0) {
214
+ return { changes: [], ordered: true, usedSortedDisjointFastPath: true };
215
+ }
216
+ for (const source of sources) {
217
+ if (source.invalidation === 'full' ||
218
+ (source.patches !== undefined && source.patches.length > 0) ||
219
+ source.hasInvalidatedRanges ||
220
+ source.hasInvalidatedRows ||
221
+ source.hasInvalidatedColumns) {
222
+ return null;
223
+ }
224
+ }
225
+ const lazySameSheetChanges = tryCreateLazySameSheetTrackedSourceChanges(engine, sources, {
226
+ deferLazyDetach: options.deferLazyDetach === true,
227
+ preferLazyPublicChanges: options.lazy === true,
228
+ });
229
+ if (lazySameSheetChanges) {
230
+ return lazySameSheetChanges;
231
+ }
232
+ if (sources.length === 1) {
233
+ const materialized = materializeTrackedIndexChangesWithMetadata(engine, sources[0].changedCellIndices, {
234
+ ...options,
235
+ explicitChangedCount: sources[0].explicitChangedCount,
236
+ });
237
+ return {
238
+ changes: materialized.changes,
239
+ ordered: materialized.ordered,
240
+ usedSortedDisjointFastPath: materialized.ordered && trackedSourceHasSortedDisjointIndices(sources[0]),
241
+ };
242
+ }
243
+ const materializedSources = Array.from({ length: sources.length }, () => undefined);
244
+ const sheetOrders = sheetOrderLookup(engine);
245
+ const fastChanges = [];
246
+ let previousNumericCellIndex = -1;
247
+ let previousPublicChange;
248
+ let canUseSortedDisjointFastPath = true;
249
+ for (let sourceIndex = 0; sourceIndex < sources.length; sourceIndex += 1) {
250
+ const source = sources[sourceIndex];
251
+ if (!trackedSourceHasSortedDisjointIndices(source)) {
252
+ canUseSortedDisjointFastPath = false;
253
+ break;
254
+ }
255
+ if (source.changedCellIndices.length > 0) {
256
+ const first = source.firstChangedCellIndex ?? source.changedCellIndices[0];
257
+ const last = source.lastChangedCellIndex ?? source.changedCellIndices[source.changedCellIndices.length - 1];
258
+ if (first <= previousNumericCellIndex) {
259
+ canUseSortedDisjointFastPath = false;
260
+ break;
261
+ }
262
+ previousNumericCellIndex = last;
263
+ }
264
+ const materialized = materializeTrackedIndexChangesWithMetadata(engine, source.changedCellIndices, {
265
+ ...options,
266
+ explicitChangedCount: source.explicitChangedCount,
267
+ });
268
+ materializedSources[sourceIndex] = materialized;
269
+ if (!materialized.ordered) {
270
+ canUseSortedDisjointFastPath = false;
271
+ break;
272
+ }
273
+ for (let changeIndex = 0; changeIndex < materialized.changes.length; changeIndex += 1) {
274
+ const change = materialized.changes[changeIndex];
275
+ if (previousPublicChange !== undefined) {
276
+ const comparison = compareTrackedCellChanges(previousPublicChange, change, sheetOrders);
277
+ if (comparison >= 0) {
278
+ canUseSortedDisjointFastPath = false;
279
+ break;
280
+ }
281
+ }
282
+ fastChanges.push(change);
283
+ previousPublicChange = change;
284
+ }
285
+ if (!canUseSortedDisjointFastPath) {
286
+ break;
287
+ }
288
+ }
289
+ if (canUseSortedDisjointFastPath) {
290
+ return { changes: fastChanges, ordered: true, usedSortedDisjointFastPath: true };
291
+ }
292
+ return materializeTrackedIndexChangeSourcesGeneric(engine, sources, materializedSources, options, sheetOrders);
293
+ }
294
+ export function forceMaterializeTrackedIndexChanges(changes) {
295
+ const deferred = DEFERRED_TRACKED_INDEX_CHANGES.get(changes);
296
+ if (deferred === undefined) {
297
+ return false;
298
+ }
299
+ deferred.forceMaterialize();
300
+ DEFERRED_TRACKED_INDEX_CHANGES.delete(changes);
301
+ return true;
302
+ }
303
+ export function detachTrackedIndexChanges(changes, options = {}) {
304
+ const deferred = DEFERRED_TRACKED_INDEX_CHANGES.get(changes);
305
+ if (deferred === undefined) {
306
+ return false;
307
+ }
308
+ deferred.detach(options);
309
+ return true;
310
+ }
311
+ export function hasDeferredTrackedIndexChanges(changes) {
312
+ return DEFERRED_TRACKED_INDEX_CHANGES.has(changes);
313
+ }
314
+ function readTrackedCellValue(cellStore, cellIndex, engine) {
315
+ const tag = cellStore.tags[cellIndex] ?? ValueTag.Empty;
316
+ switch (tag) {
317
+ case ValueTag.Number:
318
+ return { tag: ValueTag.Number, value: cellStore.numbers[cellIndex] ?? 0 };
319
+ case ValueTag.Boolean:
320
+ return { tag: ValueTag.Boolean, value: (cellStore.numbers[cellIndex] ?? 0) !== 0 };
321
+ case ValueTag.String:
322
+ return cellStore.getValue(cellIndex, (stringId) => engine.strings.get(stringId));
323
+ case ValueTag.Error:
324
+ return { tag: ValueTag.Error, code: cellStore.errors[cellIndex] };
325
+ case ValueTag.Empty:
326
+ default:
327
+ return { tag: ValueTag.Empty };
328
+ }
329
+ }
330
+ function materializeTrackedIndexChangeSourcesGeneric(engine, sources, materializedSources, options, sheetOrders) {
331
+ const latestChangesByKey = new Map();
332
+ for (let sourceIndex = 0; sourceIndex < sources.length; sourceIndex += 1) {
333
+ const source = sources[sourceIndex];
334
+ const materialized = materializedSources[sourceIndex] ??
335
+ materializeTrackedIndexChangesWithMetadata(engine, source.changedCellIndices, {
336
+ ...options,
337
+ explicitChangedCount: source.explicitChangedCount,
338
+ });
339
+ for (let changeIndex = 0; changeIndex < materialized.changes.length; changeIndex += 1) {
340
+ const change = materialized.changes[changeIndex];
341
+ const key = makeCellKey(change.address.sheet, change.address.row, change.address.col);
342
+ latestChangesByKey.delete(key);
343
+ latestChangesByKey.set(key, change);
344
+ }
345
+ }
346
+ return {
347
+ changes: orderTrackedCellChanges([...latestChangesByKey.values()], sheetOrders),
348
+ ordered: true,
349
+ usedSortedDisjointFastPath: false,
350
+ };
351
+ }
352
+ function tryCreateLazySameSheetTrackedSourceChanges(engine, sources, options) {
353
+ let totalLength = 0;
354
+ for (let sourceIndex = 0; sourceIndex < sources.length; sourceIndex += 1) {
355
+ totalLength += sources[sourceIndex].changedCellIndices.length;
356
+ }
357
+ if (!options.preferLazyPublicChanges && totalLength < LAZY_PUBLIC_CHANGE_SOURCE_THRESHOLD) {
358
+ return null;
359
+ }
360
+ const workbook = engine.workbook;
361
+ const cellStore = workbook.cellStore;
362
+ const orderedSources = [];
363
+ let sheetId;
364
+ let previousNumericCellIndex = -1;
365
+ let previousLastCellIndex;
366
+ for (let sourceIndex = 0; sourceIndex < sources.length; sourceIndex += 1) {
367
+ const source = sources[sourceIndex];
368
+ if (!trackedSourceHasSortedDisjointIndices(source)) {
369
+ return null;
370
+ }
371
+ if (source.changedCellIndices.length === 0) {
372
+ continue;
373
+ }
374
+ const firstNumericCellIndex = source.firstChangedCellIndex ?? source.changedCellIndices[0];
375
+ const lastNumericCellIndex = source.lastChangedCellIndex ?? source.changedCellIndices[source.changedCellIndices.length - 1];
376
+ if (firstNumericCellIndex <= previousNumericCellIndex) {
377
+ return null;
378
+ }
379
+ previousNumericCellIndex = lastNumericCellIndex;
380
+ const sourceSheetId = cellStore.sheetIds[source.changedCellIndices[0]];
381
+ if (sourceSheetId === undefined) {
382
+ return null;
383
+ }
384
+ if (sheetId === undefined) {
385
+ sheetId = sourceSheetId;
386
+ }
387
+ else if (sourceSheetId !== sheetId) {
388
+ return null;
389
+ }
390
+ const sheet = workbook.getSheetById(sourceSheetId);
391
+ if (sheet && sheet.structureVersion !== 1) {
392
+ return null;
393
+ }
394
+ if (!trackedIndicesAllBelongToSheet(cellStore, source.changedCellIndices, sourceSheetId)) {
395
+ return null;
396
+ }
397
+ const orderedSource = orderedTrackedIndexSource(cellStore, source);
398
+ if (orderedSource === null) {
399
+ return null;
400
+ }
401
+ if (previousLastCellIndex !== undefined &&
402
+ compareTrackedPhysicalCellIndices(cellStore, previousLastCellIndex, orderedSource.firstCellIndex) >= 0) {
403
+ return null;
404
+ }
405
+ previousLastCellIndex = orderedSource.lastCellIndex;
406
+ orderedSources.push(orderedSource);
407
+ }
408
+ if (sheetId === undefined) {
409
+ return { changes: [], ordered: true, usedSortedDisjointFastPath: true };
410
+ }
411
+ const orderedCellIndices = new Uint32Array(totalLength);
412
+ let offset = 0;
413
+ for (let sourceIndex = 0; sourceIndex < orderedSources.length; sourceIndex += 1) {
414
+ const source = orderedSources[sourceIndex];
415
+ offset = copyOrderedTrackedCellIndices(cellStore, source.changedCellIndices, orderedCellIndices, offset, source.sortedSliceSplit);
416
+ }
417
+ const sheet = workbook.getSheetById(sheetId);
418
+ const sheetName = sheet?.name ?? workbook.getSheetNameById(sheetId);
419
+ const changes = createLazyPhysicalTrackedIndexChanges(sheetId, sheetName, cellStore, engine, orderedCellIndices, formatTrackedAddress);
420
+ if (!options.deferLazyDetach) {
421
+ detachTrackedIndexChanges(changes);
422
+ }
423
+ return { changes, ordered: true, usedSortedDisjointFastPath: true };
424
+ }
425
+ function orderedTrackedIndexSource(cellStore, source) {
426
+ const length = source.changedCellIndices.length;
427
+ const split = source.explicitChangedCount;
428
+ if (split !== undefined && split > 0 && split < length) {
429
+ if (!isTrackedIndexSliceSorted(cellStore, source.changedCellIndices, 0, split) ||
430
+ !isTrackedIndexSliceSorted(cellStore, source.changedCellIndices, split, length)) {
431
+ return null;
432
+ }
433
+ const firstLeft = source.changedCellIndices[0];
434
+ const firstRight = source.changedCellIndices[split];
435
+ const lastLeft = source.changedCellIndices[split - 1];
436
+ const lastRight = source.changedCellIndices[length - 1];
437
+ return {
438
+ changedCellIndices: source.changedCellIndices,
439
+ sortedSliceSplit: split,
440
+ firstCellIndex: compareTrackedPhysicalCellIndices(cellStore, firstLeft, firstRight) <= 0 ? firstLeft : firstRight,
441
+ lastCellIndex: compareTrackedPhysicalCellIndices(cellStore, lastLeft, lastRight) >= 0 ? lastLeft : lastRight,
442
+ };
443
+ }
444
+ if (!isTrackedIndexSliceSorted(cellStore, source.changedCellIndices, 0, length)) {
445
+ return null;
446
+ }
447
+ return {
448
+ changedCellIndices: source.changedCellIndices,
449
+ firstCellIndex: source.changedCellIndices[0],
450
+ lastCellIndex: source.changedCellIndices[length - 1],
451
+ };
452
+ }
453
+ function trackedSourceHasSortedDisjointIndices(source) {
454
+ if (source.changedCellIndicesSortedDisjoint !== undefined) {
455
+ return source.changedCellIndicesSortedDisjoint;
456
+ }
457
+ let previous = -1;
458
+ for (let index = 0; index < source.changedCellIndices.length; index += 1) {
459
+ const cellIndex = source.changedCellIndices[index];
460
+ if (!Number.isInteger(cellIndex) || cellIndex < 0 || cellIndex <= previous) {
461
+ return false;
462
+ }
463
+ previous = cellIndex;
464
+ }
465
+ return true;
466
+ }
467
+ function sheetOrderLookup(engine) {
468
+ const sheetOrders = new Map();
469
+ engine.workbook.sheetsById.forEach((sheet, sheetId) => {
470
+ sheetOrders.set(sheetId, sheet.order);
471
+ });
472
+ return sheetOrders;
473
+ }
474
+ function compareTrackedCellChanges(left, right, sheetOrders) {
475
+ return ((sheetOrders.get(left.address.sheet) ?? 0) - (sheetOrders.get(right.address.sheet) ?? 0) ||
476
+ left.address.row - right.address.row ||
477
+ left.address.col - right.address.col);
478
+ }
479
+ function orderTrackedCellChanges(changes, sheetOrders) {
480
+ if (changes.length < 2) {
481
+ return changes;
482
+ }
483
+ if (isTrackedCellChangeSliceSorted(changes, sheetOrders, 0, changes.length)) {
484
+ return changes;
485
+ }
486
+ if (isTrackedCellChangeSliceReverseSorted(changes, sheetOrders, 0, changes.length)) {
487
+ return changes.toReversed();
488
+ }
489
+ return changes.toSorted((left, right) => compareTrackedCellChanges(left, right, sheetOrders));
490
+ }
491
+ function isTrackedCellChangeSliceSorted(changes, sheetOrders, start, end) {
492
+ for (let index = start + 1; index < end; index += 1) {
493
+ if (compareTrackedCellChanges(changes[index - 1], changes[index], sheetOrders) > 0) {
494
+ return false;
495
+ }
496
+ }
497
+ return true;
498
+ }
499
+ function isTrackedCellChangeSliceReverseSorted(changes, sheetOrders, start, end) {
500
+ for (let index = start + 1; index < end; index += 1) {
501
+ if (compareTrackedCellChanges(changes[index - 1], changes[index], sheetOrders) < 0) {
502
+ return false;
503
+ }
504
+ }
505
+ return true;
506
+ }
507
+ function compareTrackedPhysicalCellIndices(cellStore, leftCellIndex, rightCellIndex) {
508
+ return ((cellStore.rows[leftCellIndex] ?? 0) - (cellStore.rows[rightCellIndex] ?? 0) ||
509
+ (cellStore.cols[leftCellIndex] ?? 0) - (cellStore.cols[rightCellIndex] ?? 0));
510
+ }
511
+ function isTrackedIndexSliceSorted(cellStore, changedCellIndices, start, end) {
512
+ for (let index = start + 1; index < end; index += 1) {
513
+ if (compareTrackedPhysicalCellIndices(cellStore, changedCellIndices[index - 1], changedCellIndices[index]) > 0) {
514
+ return false;
515
+ }
516
+ }
517
+ return true;
518
+ }
519
+ function formatTrackedAddress(row, col) {
520
+ let label = COLUMN_LABEL_CACHE[col];
521
+ if (label === undefined) {
522
+ label = indexToColumn(col);
523
+ COLUMN_LABEL_CACHE[col] = label;
524
+ }
525
+ return `${label}${row + 1}`;
526
+ }
527
+ function readPhysicalTrackedIndexChange(cellIndex, sheetId, sheetName, cellStore, engine, formatAddressCached) {
528
+ const row = cellStore.rows[cellIndex];
529
+ const col = cellStore.cols[cellIndex];
530
+ return {
531
+ kind: 'cell',
532
+ address: { sheet: sheetId, row, col },
533
+ sheetName,
534
+ a1: formatAddressCached(row, col),
535
+ newValue: readTrackedCellValue(cellStore, cellIndex, engine),
536
+ };
537
+ }
538
+ function trackedIndicesAllBelongToSheet(cellStore, changedCellIndices, sheetId) {
539
+ for (let index = 0; index < changedCellIndices.length; index += 1) {
540
+ if (cellStore.sheetIds[changedCellIndices[index]] !== sheetId) {
541
+ return false;
542
+ }
543
+ }
544
+ return true;
545
+ }
546
+ function createLazyPhysicalTrackedIndexChanges(sheetId, sheetName, cellStore, engine, changedCellIndices, formatAddressCached, sortedSliceSplit) {
547
+ const changedCellIndicesForMaterialization = changedCellIndices instanceof Uint32Array ? changedCellIndices : copyTrackedCellIndices(changedCellIndices);
548
+ const length = changedCellIndicesForMaterialization.length;
549
+ const cache = [];
550
+ cache.length = length;
551
+ let detached;
552
+ let fullyMaterialized = false;
553
+ let orderedCellIndices;
554
+ const hasMaterializedIndex = (index) => Object.prototype.hasOwnProperty.call(cache, index);
555
+ const orderedCellIndexAt = (index) => {
556
+ if (sortedSliceSplit === undefined) {
557
+ return changedCellIndicesForMaterialization[index];
558
+ }
559
+ if (orderedCellIndices === undefined) {
560
+ orderedCellIndices = new Uint32Array(length);
561
+ copyOrderedTrackedCellIndices(cellStore, changedCellIndicesForMaterialization, orderedCellIndices, 0, sortedSliceSplit);
562
+ }
563
+ return orderedCellIndices[index];
564
+ };
565
+ const forEachOrderedCellIndex = (fn) => {
566
+ if (sortedSliceSplit === undefined) {
567
+ for (let index = 0; index < length; index += 1) {
568
+ fn(index, changedCellIndicesForMaterialization[index]);
569
+ }
570
+ return;
571
+ }
572
+ if (orderedCellIndices === undefined) {
573
+ orderedCellIndices = new Uint32Array(length);
574
+ copyOrderedTrackedCellIndices(cellStore, changedCellIndicesForMaterialization, orderedCellIndices, 0, sortedSliceSplit);
575
+ }
576
+ for (let index = 0; index < length; index += 1) {
577
+ fn(index, orderedCellIndices[index]);
578
+ }
579
+ };
580
+ const materialize = (index) => {
581
+ if (hasMaterializedIndex(index)) {
582
+ return cache[index];
583
+ }
584
+ if (detached) {
585
+ cache[index] = readDetachedPhysicalTrackedIndexChange(index, sheetId, sheetName, cellStore, detached, formatAddressCached);
586
+ return cache[index];
587
+ }
588
+ cache[index] = readPhysicalTrackedIndexChange(orderedCellIndexAt(index), sheetId, sheetName, cellStore, engine, formatAddressCached);
589
+ return cache[index];
590
+ };
591
+ const detach = (options = {}) => {
592
+ if (fullyMaterialized) {
593
+ return;
594
+ }
595
+ const preservePositions = options.preservePositions ?? true;
596
+ if (detached !== undefined) {
597
+ if (preservePositions && detached.rows === undefined) {
598
+ if (detached.cellIndices === undefined) {
599
+ return;
600
+ }
601
+ const rows = new Uint32Array(length);
602
+ const cols = new Uint32Array(length);
603
+ for (let index = 0; index < length; index += 1) {
604
+ const cellIndex = detached.cellIndices[index];
605
+ rows[index] = cellStore.rows[cellIndex] ?? 0;
606
+ cols[index] = cellStore.cols[cellIndex] ?? 0;
607
+ }
608
+ detached = { ...detached, rows, cols };
609
+ }
610
+ return;
611
+ }
612
+ const cellIndices = preservePositions ? undefined : new Uint32Array(length);
613
+ const rows = preservePositions ? new Uint32Array(length) : undefined;
614
+ const cols = preservePositions ? new Uint32Array(length) : undefined;
615
+ const numbers = new Float64Array(length);
616
+ let tags;
617
+ let constantTag;
618
+ let errors;
619
+ let stringIds;
620
+ let strings;
621
+ const ensureTags = (currentIndex) => {
622
+ if (tags !== undefined) {
623
+ return tags;
624
+ }
625
+ const nextTags = new Uint8Array(length);
626
+ nextTags.fill(constantTag ?? ValueTag.Empty, 0, currentIndex);
627
+ tags = nextTags;
628
+ return nextTags;
629
+ };
630
+ forEachOrderedCellIndex((index, cellIndex) => {
631
+ if (cellIndices !== undefined) {
632
+ cellIndices[index] = cellIndex;
633
+ }
634
+ if (rows !== undefined && cols !== undefined) {
635
+ rows[index] = cellStore.rows[cellIndex] ?? 0;
636
+ cols[index] = cellStore.cols[cellIndex] ?? 0;
637
+ }
638
+ const tag = cellStore.tags[cellIndex] ?? ValueTag.Empty;
639
+ if (tags !== undefined) {
640
+ tags[index] = tag;
641
+ }
642
+ else if (constantTag === undefined) {
643
+ constantTag = tag;
644
+ }
645
+ else if (tag !== constantTag) {
646
+ ensureTags(index)[index] = tag;
647
+ }
648
+ switch (tag) {
649
+ case ValueTag.Number:
650
+ case ValueTag.Boolean:
651
+ numbers[index] = cellStore.numbers[cellIndex] ?? 0;
652
+ break;
653
+ case ValueTag.String:
654
+ {
655
+ stringIds ??= new Int32Array(length);
656
+ strings ??= [];
657
+ stringIds[index] = cellStore.stringIds[cellIndex] ?? 0;
658
+ const value = cellStore.getValue(cellIndex, (stringId) => engine.strings.get(stringId));
659
+ strings[index] = value.tag === ValueTag.String ? value.value : '';
660
+ }
661
+ break;
662
+ case ValueTag.Error:
663
+ errors ??= new Int32Array(length);
664
+ errors[index] = cellStore.errors[cellIndex] ?? ErrorCode.None;
665
+ break;
666
+ case ValueTag.Empty:
667
+ default:
668
+ break;
669
+ }
670
+ });
671
+ detached = {
672
+ ...(cellIndices === undefined ? {} : { cellIndices }),
673
+ ...(rows !== undefined && cols !== undefined ? { rows, cols } : {}),
674
+ ...(tags !== undefined ? { tags } : { constantTag: constantTag ?? ValueTag.Empty }),
675
+ numbers,
676
+ ...(errors === undefined ? {} : { errors }),
677
+ ...(stringIds === undefined ? {} : { stringIds }),
678
+ ...(strings === undefined ? {} : { strings }),
679
+ };
680
+ };
681
+ const forceMaterialize = () => {
682
+ if (fullyMaterialized) {
683
+ return;
684
+ }
685
+ if (detached !== undefined) {
686
+ for (let index = 0; index < length; index += 1) {
687
+ if (!hasMaterializedIndex(index)) {
688
+ cache[index] = readDetachedPhysicalTrackedIndexChange(index, sheetId, sheetName, cellStore, detached, formatAddressCached);
689
+ }
690
+ }
691
+ fullyMaterialized = true;
692
+ return;
693
+ }
694
+ for (let index = 0; index < length; index += 1) {
695
+ if (!hasMaterializedIndex(index)) {
696
+ cache[index] = readPhysicalTrackedIndexChange(orderedCellIndexAt(index), sheetId, sheetName, cellStore, engine, formatAddressCached);
697
+ }
698
+ }
699
+ fullyMaterialized = true;
700
+ };
701
+ const numericIndexOf = (property) => {
702
+ if (typeof property !== 'string' || property.length === 0) {
703
+ return undefined;
704
+ }
705
+ const index = Number(property);
706
+ return Number.isInteger(index) && index >= 0 && index < length && String(index) === property ? index : undefined;
707
+ };
708
+ const proxy = new Proxy(cache, {
709
+ get(target, property, receiver) {
710
+ const index = numericIndexOf(property);
711
+ return index === undefined ? Reflect.get(target, property, receiver) : materialize(index);
712
+ },
713
+ getOwnPropertyDescriptor(target, property) {
714
+ const index = numericIndexOf(property);
715
+ if (index === undefined) {
716
+ return Reflect.getOwnPropertyDescriptor(target, property);
717
+ }
718
+ return {
719
+ configurable: true,
720
+ enumerable: true,
721
+ value: materialize(index),
722
+ writable: true,
723
+ };
724
+ },
725
+ has(target, property) {
726
+ return numericIndexOf(property) !== undefined || Reflect.has(target, property);
727
+ },
728
+ ownKeys(target) {
729
+ return [
730
+ ...Array.from({ length }, (_value, index) => String(index)),
731
+ ...Reflect.ownKeys(target).filter((key) => typeof key !== 'string' || numericIndexOf(key) === undefined),
732
+ ];
733
+ },
734
+ });
735
+ DEFERRED_TRACKED_INDEX_CHANGES.set(proxy, { forceMaterialize, detach });
736
+ return proxy;
737
+ }
738
+ function tryCreateLazyPhysicalTrackedIndexChanges(engine, changedCellIndices, sheetId, formatAddressCached, options = {}) {
739
+ const workbook = engine.workbook;
740
+ const sheet = workbook.getSheetById(sheetId);
741
+ if (sheet && sheet.structureVersion !== 1) {
742
+ return null;
743
+ }
744
+ const cellStore = workbook.cellStore;
745
+ const length = changedCellIndices.length;
746
+ const split = options.sortedSliceSplit;
747
+ const sortedSliceSplit = split !== undefined && split > 0 && split < length ? split : undefined;
748
+ let previousRow = -1;
749
+ let previousCol = -1;
750
+ let previousRightRow = -1;
751
+ let previousRightCol = -1;
752
+ for (let index = 0; index < length; index += 1) {
753
+ const cellIndex = changedCellIndices[index];
754
+ if (cellStore.sheetIds[cellIndex] !== sheetId) {
755
+ return null;
756
+ }
757
+ const row = cellStore.rows[cellIndex] ?? 0;
758
+ const col = cellStore.cols[cellIndex] ?? 0;
759
+ if (sortedSliceSplit !== undefined && index >= sortedSliceSplit) {
760
+ if (row < previousRightRow || (row === previousRightRow && col < previousRightCol)) {
761
+ return null;
762
+ }
763
+ previousRightRow = row;
764
+ previousRightCol = col;
765
+ continue;
766
+ }
767
+ if (row < previousRow || (row === previousRow && col < previousCol)) {
768
+ return null;
769
+ }
770
+ previousRow = row;
771
+ previousCol = col;
772
+ }
773
+ const sheetName = sheet?.name ?? workbook.getSheetNameById(sheetId);
774
+ return createLazyPhysicalTrackedIndexChanges(sheetId, sheetName, cellStore, engine, changedCellIndices, formatAddressCached, sortedSliceSplit);
775
+ }
776
+ function copyOrderedTrackedCellIndices(cellStore, changedCellIndices, target, offset, sortedSliceSplit) {
777
+ if (sortedSliceSplit === undefined) {
778
+ for (let index = 0; index < changedCellIndices.length; index += 1) {
779
+ target[offset + index] = changedCellIndices[index];
780
+ }
781
+ return offset + changedCellIndices.length;
782
+ }
783
+ let explicitIndex = 0;
784
+ let recalculatedIndex = sortedSliceSplit;
785
+ let outputIndex = offset;
786
+ while (explicitIndex < sortedSliceSplit && recalculatedIndex < changedCellIndices.length) {
787
+ const explicitCellIndex = changedCellIndices[explicitIndex];
788
+ const recalculatedCellIndex = changedCellIndices[recalculatedIndex];
789
+ if (compareTrackedPhysicalCellIndices(cellStore, explicitCellIndex, recalculatedCellIndex) <= 0) {
790
+ target[outputIndex] = explicitCellIndex;
791
+ explicitIndex += 1;
792
+ }
793
+ else {
794
+ target[outputIndex] = recalculatedCellIndex;
795
+ recalculatedIndex += 1;
796
+ }
797
+ outputIndex += 1;
798
+ }
799
+ while (explicitIndex < sortedSliceSplit) {
800
+ target[outputIndex] = changedCellIndices[explicitIndex];
801
+ explicitIndex += 1;
802
+ outputIndex += 1;
803
+ }
804
+ while (recalculatedIndex < changedCellIndices.length) {
805
+ target[outputIndex] = changedCellIndices[recalculatedIndex];
806
+ recalculatedIndex += 1;
807
+ outputIndex += 1;
808
+ }
809
+ return outputIndex;
810
+ }
811
+ function copyTrackedCellIndices(changedCellIndices) {
812
+ return Uint32Array.from(changedCellIndices);
813
+ }
814
+ function readDetachedPhysicalTrackedIndexChange(index, sheetId, sheetName, cellStore, detached, formatAddressCached) {
815
+ const cellIndex = detached.cellIndices?.[index];
816
+ const row = detached.rows?.[index] ?? (cellIndex === undefined ? 0 : (cellStore.rows[cellIndex] ?? 0));
817
+ const col = detached.cols?.[index] ?? (cellIndex === undefined ? 0 : (cellStore.cols[cellIndex] ?? 0));
818
+ return {
819
+ kind: 'cell',
820
+ address: { sheet: sheetId, row, col },
821
+ sheetName,
822
+ a1: formatAddressCached(row, col),
823
+ newValue: readDetachedCellValue(detached, index),
824
+ };
825
+ }
826
+ function readDetachedCellValue(detached, index) {
827
+ const tag = (detached.tags?.[index] ?? detached.constantTag ?? ValueTag.Empty);
828
+ switch (tag) {
829
+ case ValueTag.Number:
830
+ return { tag: ValueTag.Number, value: detached.numbers[index] ?? 0 };
831
+ case ValueTag.Boolean:
832
+ return { tag: ValueTag.Boolean, value: (detached.numbers[index] ?? 0) !== 0 };
833
+ case ValueTag.String:
834
+ return { tag: ValueTag.String, value: detached.strings?.[index] ?? '', stringId: detached.stringIds?.[index] ?? 0 };
835
+ case ValueTag.Error:
836
+ return { tag: ValueTag.Error, code: detached.errors?.[index] ?? ErrorCode.None };
837
+ case ValueTag.Empty:
838
+ default:
839
+ return { tag: ValueTag.Empty };
840
+ }
841
+ }
842
+ //# sourceMappingURL=tracked-cell-index-changes.js.map