@fluidframework/matrix 2.1.0-276985 → 2.1.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/.eslintrc.cjs +2 -5
- package/CHANGELOG.md +4 -0
- package/api-extractor/api-extractor.current.json +5 -0
- package/api-extractor/api-extractor.legacy.json +1 -1
- package/api-extractor.json +1 -1
- package/api-report/matrix.legacy.public.api.md +9 -0
- package/dist/handlecache.d.ts +7 -3
- package/dist/handlecache.d.ts.map +1 -1
- package/dist/handlecache.js +25 -7
- package/dist/handlecache.js.map +1 -1
- package/dist/handletable.d.ts +3 -1
- package/dist/handletable.d.ts.map +1 -1
- package/dist/handletable.js.map +1 -1
- package/dist/legacy.d.ts +1 -1
- package/dist/matrix.d.ts +2 -1
- package/dist/matrix.d.ts.map +1 -1
- package/dist/matrix.js +23 -9
- package/dist/matrix.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.d.ts.map +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/permutationvector.d.ts +5 -5
- package/dist/permutationvector.d.ts.map +1 -1
- package/dist/permutationvector.js +11 -4
- package/dist/permutationvector.js.map +1 -1
- package/dist/public.d.ts +1 -1
- package/dist/range.d.ts.map +1 -1
- package/dist/range.js.map +1 -1
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js.map +1 -1
- package/dist/serialization.d.ts.map +1 -1
- package/dist/serialization.js.map +1 -1
- package/dist/sparsearray2d.d.ts +9 -5
- package/dist/sparsearray2d.d.ts.map +1 -1
- package/dist/sparsearray2d.js +27 -7
- package/dist/sparsearray2d.js.map +1 -1
- package/dist/undoprovider.d.ts.map +1 -1
- package/dist/undoprovider.js +10 -3
- package/dist/undoprovider.js.map +1 -1
- package/internal.d.ts +1 -1
- package/legacy.d.ts +1 -1
- package/lib/handlecache.d.ts +7 -3
- package/lib/handlecache.d.ts.map +1 -1
- package/lib/handlecache.js +25 -7
- package/lib/handlecache.js.map +1 -1
- package/lib/handletable.d.ts +3 -1
- package/lib/handletable.d.ts.map +1 -1
- package/lib/handletable.js.map +1 -1
- package/lib/legacy.d.ts +1 -1
- package/lib/matrix.d.ts +2 -1
- package/lib/matrix.d.ts.map +1 -1
- package/lib/matrix.js +23 -9
- package/lib/matrix.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.d.ts.map +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/permutationvector.d.ts +5 -5
- package/lib/permutationvector.d.ts.map +1 -1
- package/lib/permutationvector.js +11 -4
- package/lib/permutationvector.js.map +1 -1
- package/lib/public.d.ts +1 -1
- package/lib/range.d.ts.map +1 -1
- package/lib/range.js.map +1 -1
- package/lib/runtime.d.ts.map +1 -1
- package/lib/runtime.js.map +1 -1
- package/lib/serialization.d.ts.map +1 -1
- package/lib/serialization.js.map +1 -1
- package/lib/sparsearray2d.d.ts +9 -5
- package/lib/sparsearray2d.d.ts.map +1 -1
- package/lib/sparsearray2d.js +27 -7
- package/lib/sparsearray2d.js.map +1 -1
- package/lib/undoprovider.d.ts.map +1 -1
- package/lib/undoprovider.js +10 -3
- package/lib/undoprovider.js.map +1 -1
- package/package.json +36 -30
- package/src/handlecache.ts +31 -16
- package/src/handletable.ts +11 -9
- package/src/matrix.ts +80 -50
- package/src/packageVersion.ts +1 -1
- package/src/permutationvector.ts +38 -23
- package/src/range.ts +1 -1
- package/src/runtime.ts +5 -2
- package/src/serialization.ts +4 -2
- package/src/sparsearray2d.ts +55 -36
- package/src/undoprovider.ts +26 -18
- package/tsconfig.json +0 -1
package/src/matrix.ts
CHANGED
|
@@ -46,6 +46,7 @@ import {
|
|
|
46
46
|
} from "@tiny-calc/nano";
|
|
47
47
|
import Deque from "double-ended-queue";
|
|
48
48
|
|
|
49
|
+
import type { HandleCache } from "./handlecache.js";
|
|
49
50
|
import { Handle, isHandleValid } from "./handletable.js";
|
|
50
51
|
import {
|
|
51
52
|
ISetOp,
|
|
@@ -58,7 +59,7 @@ import {
|
|
|
58
59
|
import { PermutationVector, reinsertSegmentIntoVector } from "./permutationvector.js";
|
|
59
60
|
import { ensureRange } from "./range.js";
|
|
60
61
|
import { deserializeBlob } from "./serialization.js";
|
|
61
|
-
import { SparseArray2D } from "./sparsearray2d.js";
|
|
62
|
+
import { SparseArray2D, type RecurArray } from "./sparsearray2d.js";
|
|
62
63
|
import { IUndoConsumer } from "./types.js";
|
|
63
64
|
import { MatrixUndoProvider } from "./undoprovider.js";
|
|
64
65
|
|
|
@@ -120,6 +121,9 @@ interface CellLastWriteTrackerItem {
|
|
|
120
121
|
* @legacy
|
|
121
122
|
* @alpha
|
|
122
123
|
*/
|
|
124
|
+
// Changing this to `unknown` would be a breaking change.
|
|
125
|
+
// TODO: if possible, transition ISharedMatrix to not use `any`.
|
|
126
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
123
127
|
export interface ISharedMatrix<T = any>
|
|
124
128
|
extends IEventProvider<ISharedMatrixEvents<T>>,
|
|
125
129
|
IMatrixProducer<MatrixItem<T>>,
|
|
@@ -215,6 +219,9 @@ export interface ISharedMatrix<T = any>
|
|
|
215
219
|
* @legacy
|
|
216
220
|
* @alpha
|
|
217
221
|
*/
|
|
222
|
+
// Changing this to `unknown` would be a breaking change.
|
|
223
|
+
// TODO: if possible, transition SharedMatrix to not use `any`.
|
|
224
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
218
225
|
export class SharedMatrix<T = any>
|
|
219
226
|
extends SharedObject<ISharedMatrixEvents<T> & ISharedObjectEvents>
|
|
220
227
|
implements ISharedMatrix<T>
|
|
@@ -231,6 +238,7 @@ export class SharedMatrix<T = any>
|
|
|
231
238
|
* on the SharedMatrix op is 11.
|
|
232
239
|
*/
|
|
233
240
|
private readonly inFlightRefSeqs = new Deque<number>();
|
|
241
|
+
readonly getMinInFlightRefSeq = (): number | undefined => this.inFlightRefSeqs.get(0);
|
|
234
242
|
|
|
235
243
|
private readonly rows: PermutationVector; // Map logical row to storage handle (if any)
|
|
236
244
|
private readonly cols: PermutationVector; // Map logical col to storage handle (if any)
|
|
@@ -261,14 +269,13 @@ export class SharedMatrix<T = any>
|
|
|
261
269
|
super(id, runtime, attributes, "fluid_matrix_");
|
|
262
270
|
|
|
263
271
|
this.setCellLwwToFwwPolicySwitchOpSeqNumber = -1;
|
|
264
|
-
const getMinInFlightRefSeq = () => this.inFlightRefSeqs.get(0);
|
|
265
272
|
this.rows = new PermutationVector(
|
|
266
273
|
SnapshotPath.rows,
|
|
267
274
|
this.logger,
|
|
268
275
|
runtime,
|
|
269
276
|
this.onRowDelta,
|
|
270
277
|
this.onRowHandlesRecycled,
|
|
271
|
-
getMinInFlightRefSeq,
|
|
278
|
+
this.getMinInFlightRefSeq,
|
|
272
279
|
);
|
|
273
280
|
|
|
274
281
|
this.cols = new PermutationVector(
|
|
@@ -277,7 +284,7 @@ export class SharedMatrix<T = any>
|
|
|
277
284
|
runtime,
|
|
278
285
|
this.onColDelta,
|
|
279
286
|
this.onColHandlesRecycled,
|
|
280
|
-
getMinInFlightRefSeq,
|
|
287
|
+
this.getMinInFlightRefSeq,
|
|
281
288
|
);
|
|
282
289
|
}
|
|
283
290
|
|
|
@@ -286,7 +293,7 @@ export class SharedMatrix<T = any>
|
|
|
286
293
|
/**
|
|
287
294
|
* Subscribes the given IUndoConsumer to the matrix.
|
|
288
295
|
*/
|
|
289
|
-
public openUndo(consumer: IUndoConsumer) {
|
|
296
|
+
public openUndo(consumer: IUndoConsumer): void {
|
|
290
297
|
assert(
|
|
291
298
|
this.undo === undefined,
|
|
292
299
|
0x019 /* "SharedMatrix.openUndo() supports at most a single IUndoConsumer." */,
|
|
@@ -297,10 +304,10 @@ export class SharedMatrix<T = any>
|
|
|
297
304
|
|
|
298
305
|
// TODO: closeUndo()?
|
|
299
306
|
|
|
300
|
-
private get rowHandles() {
|
|
307
|
+
private get rowHandles(): HandleCache {
|
|
301
308
|
return this.rows.handleCache;
|
|
302
309
|
}
|
|
303
|
-
private get colHandles() {
|
|
310
|
+
private get colHandles(): HandleCache {
|
|
304
311
|
return this.cols.handleCache;
|
|
305
312
|
}
|
|
306
313
|
|
|
@@ -319,14 +326,14 @@ export class SharedMatrix<T = any>
|
|
|
319
326
|
|
|
320
327
|
// #region IMatrixReader
|
|
321
328
|
|
|
322
|
-
public get rowCount() {
|
|
329
|
+
public get rowCount(): number {
|
|
323
330
|
return this.rows.getLength();
|
|
324
331
|
}
|
|
325
|
-
public get colCount() {
|
|
332
|
+
public get colCount(): number {
|
|
326
333
|
return this.cols.getLength();
|
|
327
334
|
}
|
|
328
335
|
|
|
329
|
-
public isSetCellConflictResolutionPolicyFWW() {
|
|
336
|
+
public isSetCellConflictResolutionPolicyFWW(): boolean {
|
|
330
337
|
return this.setCellLwwToFwwPolicySwitchOpSeqNumber > -1 || this.userSwitchedSetCellPolicy;
|
|
331
338
|
}
|
|
332
339
|
|
|
@@ -357,7 +364,7 @@ export class SharedMatrix<T = any>
|
|
|
357
364
|
|
|
358
365
|
// #endregion IMatrixReader
|
|
359
366
|
|
|
360
|
-
public setCell(row: number, col: number, value: MatrixItem<T>) {
|
|
367
|
+
public setCell(row: number, col: number, value: MatrixItem<T>): void {
|
|
361
368
|
if (row < 0 || row >= this.rowCount || col < 0 || col >= this.colCount) {
|
|
362
369
|
throw new UsageError("Trying to set out-of-bounds cell.");
|
|
363
370
|
}
|
|
@@ -370,7 +377,7 @@ export class SharedMatrix<T = any>
|
|
|
370
377
|
colStart: number,
|
|
371
378
|
colCount: number,
|
|
372
379
|
values: readonly MatrixItem<T>[],
|
|
373
|
-
) {
|
|
380
|
+
): void {
|
|
374
381
|
const rowCount = Math.ceil(values.length / colCount);
|
|
375
382
|
|
|
376
383
|
assert(
|
|
@@ -404,7 +411,7 @@ export class SharedMatrix<T = any>
|
|
|
404
411
|
value: MatrixItem<T>,
|
|
405
412
|
rowHandle = this.rows.getAllocatedHandle(row),
|
|
406
413
|
colHandle = this.cols.getAllocatedHandle(col),
|
|
407
|
-
) {
|
|
414
|
+
): void {
|
|
408
415
|
this.protectAgainstReentrancy(() => {
|
|
409
416
|
if (this.undo !== undefined) {
|
|
410
417
|
let oldValue = this.cells.getCell(rowHandle, colHandle);
|
|
@@ -428,7 +435,11 @@ export class SharedMatrix<T = any>
|
|
|
428
435
|
});
|
|
429
436
|
}
|
|
430
437
|
|
|
431
|
-
private createOpMetadataLocalRef(
|
|
438
|
+
private createOpMetadataLocalRef(
|
|
439
|
+
vector: PermutationVector,
|
|
440
|
+
pos: number,
|
|
441
|
+
localSeq: number,
|
|
442
|
+
): LocalReferencePosition {
|
|
432
443
|
const segoff = vector.getContainingSegment(pos, undefined, localSeq);
|
|
433
444
|
assert(
|
|
434
445
|
segoff.segment !== undefined && segoff.offset !== undefined,
|
|
@@ -449,7 +460,7 @@ export class SharedMatrix<T = any>
|
|
|
449
460
|
rowHandle: Handle,
|
|
450
461
|
colHandle: Handle,
|
|
451
462
|
localSeq = this.nextLocalSeq(),
|
|
452
|
-
) {
|
|
463
|
+
): void {
|
|
453
464
|
assert(
|
|
454
465
|
this.isAttached(),
|
|
455
466
|
0x1e2 /* "Caller must ensure 'isAttached()' before calling 'sendSetCellOp'." */,
|
|
@@ -487,7 +498,7 @@ export class SharedMatrix<T = any>
|
|
|
487
498
|
* a deleted row/col.
|
|
488
499
|
* @param callback - code that needs to protected against reentrancy.
|
|
489
500
|
*/
|
|
490
|
-
private protectAgainstReentrancy(callback: () => void) {
|
|
501
|
+
private protectAgainstReentrancy(callback: () => void): void {
|
|
491
502
|
if (this.reentrantCount !== 0) {
|
|
492
503
|
// Validate that applications don't submit edits in response to matrix change notifications. This is unsupported.
|
|
493
504
|
throw new UsageError("Reentrancy detected in SharedMatrix.");
|
|
@@ -509,7 +520,7 @@ export class SharedMatrix<T = any>
|
|
|
509
520
|
oppositeVector: PermutationVector,
|
|
510
521
|
target: SnapshotPath.rows | SnapshotPath.cols,
|
|
511
522
|
message: IMergeTreeOp,
|
|
512
|
-
) {
|
|
523
|
+
): void {
|
|
513
524
|
// Ideally, we would have a single 'localSeq' counter that is shared between both PermutationVectors
|
|
514
525
|
// and the SharedMatrix's cell data. Instead, we externally advance each MergeTree's 'localSeq' counter
|
|
515
526
|
// for each submitted op it not aware of to keep them synchronized.
|
|
@@ -540,11 +551,11 @@ export class SharedMatrix<T = any>
|
|
|
540
551
|
}
|
|
541
552
|
}
|
|
542
553
|
|
|
543
|
-
private submitColMessage(message: IMergeTreeOp) {
|
|
554
|
+
private submitColMessage(message: IMergeTreeOp): void {
|
|
544
555
|
this.submitVectorMessage(this.cols, this.rows, SnapshotPath.cols, message);
|
|
545
556
|
}
|
|
546
557
|
|
|
547
|
-
public insertCols(colStart: number, count: number) {
|
|
558
|
+
public insertCols(colStart: number, count: number): void {
|
|
548
559
|
if (count === 0) {
|
|
549
560
|
return;
|
|
550
561
|
}
|
|
@@ -558,7 +569,7 @@ export class SharedMatrix<T = any>
|
|
|
558
569
|
});
|
|
559
570
|
}
|
|
560
571
|
|
|
561
|
-
public removeCols(colStart: number, count: number) {
|
|
572
|
+
public removeCols(colStart: number, count: number): void {
|
|
562
573
|
if (count === 0) {
|
|
563
574
|
return;
|
|
564
575
|
}
|
|
@@ -570,11 +581,11 @@ export class SharedMatrix<T = any>
|
|
|
570
581
|
);
|
|
571
582
|
}
|
|
572
583
|
|
|
573
|
-
private submitRowMessage(message: IMergeTreeOp) {
|
|
584
|
+
private submitRowMessage(message: IMergeTreeOp): void {
|
|
574
585
|
this.submitVectorMessage(this.rows, this.cols, SnapshotPath.rows, message);
|
|
575
586
|
}
|
|
576
587
|
|
|
577
|
-
public insertRows(rowStart: number, count: number) {
|
|
588
|
+
public insertRows(rowStart: number, count: number): void {
|
|
578
589
|
if (count === 0) {
|
|
579
590
|
return;
|
|
580
591
|
}
|
|
@@ -588,7 +599,7 @@ export class SharedMatrix<T = any>
|
|
|
588
599
|
});
|
|
589
600
|
}
|
|
590
601
|
|
|
591
|
-
public removeRows(rowStart: number, count: number) {
|
|
602
|
+
public removeRows(rowStart: number, count: number): void {
|
|
592
603
|
if (count === 0) {
|
|
593
604
|
return;
|
|
594
605
|
}
|
|
@@ -600,7 +611,7 @@ export class SharedMatrix<T = any>
|
|
|
600
611
|
);
|
|
601
612
|
}
|
|
602
613
|
|
|
603
|
-
public _undoRemoveRows(rowStart: number, spec: IJSONSegment) {
|
|
614
|
+
public _undoRemoveRows(rowStart: number, spec: IJSONSegment): void {
|
|
604
615
|
const { op, inserted } = reinsertSegmentIntoVector(this.rows, rowStart, spec);
|
|
605
616
|
assert(op !== undefined, 0x8b6 /* must be defined */);
|
|
606
617
|
this.submitRowMessage(op);
|
|
@@ -610,7 +621,9 @@ export class SharedMatrix<T = any>
|
|
|
610
621
|
const rowCount = inserted.cachedLength;
|
|
611
622
|
for (let row = rowStart; row < rowStart + rowCount; row++, rowHandle++) {
|
|
612
623
|
for (let col = 0; col < this.colCount; col++) {
|
|
613
|
-
|
|
624
|
+
// TODO Non null asserting, why is this not null?
|
|
625
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
626
|
+
const colHandle = this.colHandles.getHandle(col)!;
|
|
614
627
|
const value = this.cells.getCell(rowHandle, colHandle);
|
|
615
628
|
if (this.isAttached() && value !== undefined && value !== null) {
|
|
616
629
|
this.sendSetCellOp(row, col, value, rowHandle, colHandle);
|
|
@@ -624,7 +637,7 @@ export class SharedMatrix<T = any>
|
|
|
624
637
|
}
|
|
625
638
|
}
|
|
626
639
|
|
|
627
|
-
/***/ public _undoRemoveCols(colStart: number, spec: IJSONSegment) {
|
|
640
|
+
/***/ public _undoRemoveCols(colStart: number, spec: IJSONSegment): void {
|
|
628
641
|
const { op, inserted } = reinsertSegmentIntoVector(this.cols, colStart, spec);
|
|
629
642
|
assert(op !== undefined, 0x8b7 /* must be defined */);
|
|
630
643
|
this.submitColMessage(op);
|
|
@@ -634,7 +647,9 @@ export class SharedMatrix<T = any>
|
|
|
634
647
|
const colCount = inserted.cachedLength;
|
|
635
648
|
for (let col = colStart; col < colStart + colCount; col++, colHandle++) {
|
|
636
649
|
for (let row = 0; row < this.rowCount; row++) {
|
|
637
|
-
|
|
650
|
+
// TODO Non null asserting, why is this not null?
|
|
651
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
652
|
+
const rowHandle = this.rowHandles.getHandle(row)!;
|
|
638
653
|
const value = this.cells.getCell(rowHandle, colHandle);
|
|
639
654
|
if (this.isAttached() && value !== undefined && value !== null) {
|
|
640
655
|
this.sendSetCellOp(row, col, value, rowHandle, colHandle);
|
|
@@ -679,7 +694,7 @@ export class SharedMatrix<T = any>
|
|
|
679
694
|
* Runs serializer on the GC data for this SharedMatrix.
|
|
680
695
|
* All the IFluidHandle's stored in the cells represent routes to other objects.
|
|
681
696
|
*/
|
|
682
|
-
protected processGCDataCore(serializer: IFluidSerializer) {
|
|
697
|
+
protected processGCDataCore(serializer: IFluidSerializer): void {
|
|
683
698
|
for (let row = 0; row < this.rowCount; row++) {
|
|
684
699
|
for (let col = 0; col < this.colCount; col++) {
|
|
685
700
|
serializer.stringify(this.getCell(row, col), this.handle);
|
|
@@ -693,7 +708,7 @@ export class SharedMatrix<T = any>
|
|
|
693
708
|
* Do not use with 'submitColMessage()/submitRowMessage()' as these helpers + the MergeTree will
|
|
694
709
|
* automatically advance 'localSeq'.
|
|
695
710
|
*/
|
|
696
|
-
private nextLocalSeq() {
|
|
711
|
+
private nextLocalSeq(): number {
|
|
697
712
|
// Ideally, we would have a single 'localSeq' counter that is shared between both PermutationVectors
|
|
698
713
|
// and the SharedMatrix's cell data. Instead, we externally bump each MergeTree's 'localSeq' counter
|
|
699
714
|
// for SharedMatrix ops it's not aware of to keep them synchronized. (For cell data operations, we
|
|
@@ -703,7 +718,7 @@ export class SharedMatrix<T = any>
|
|
|
703
718
|
return ++this.rows.getCollabWindow().localSeq;
|
|
704
719
|
}
|
|
705
720
|
|
|
706
|
-
protected submitLocalMessage(message:
|
|
721
|
+
protected submitLocalMessage(message: unknown, localOpMetadata?: unknown): void {
|
|
707
722
|
// TODO: Recommend moving this assertion into SharedObject
|
|
708
723
|
// (See https://github.com/microsoft/FluidFramework/issues/2559)
|
|
709
724
|
assert(
|
|
@@ -721,7 +736,7 @@ export class SharedMatrix<T = any>
|
|
|
721
736
|
);
|
|
722
737
|
}
|
|
723
738
|
|
|
724
|
-
protected didAttach() {
|
|
739
|
+
protected didAttach(): void {
|
|
725
740
|
// We've attached we need to start generating and sending ops.
|
|
726
741
|
// so start collaboration and provide a default client id incase we are not connected
|
|
727
742
|
if (this.isAttached()) {
|
|
@@ -730,7 +745,7 @@ export class SharedMatrix<T = any>
|
|
|
730
745
|
}
|
|
731
746
|
}
|
|
732
747
|
|
|
733
|
-
protected onConnect() {
|
|
748
|
+
protected onConnect(): void {
|
|
734
749
|
assert(
|
|
735
750
|
this.rows.getCollabWindow().collaborating === this.cols.getCollabWindow().collaborating,
|
|
736
751
|
0x01f /* "Row and col collab window 'collaborating' status desynchronized!" */,
|
|
@@ -763,7 +778,7 @@ export class SharedMatrix<T = any>
|
|
|
763
778
|
return client.findReconnectionPosition(segment, localSeq) + offset;
|
|
764
779
|
}
|
|
765
780
|
|
|
766
|
-
protected reSubmitCore(incoming: unknown, localOpMetadata: unknown) {
|
|
781
|
+
protected reSubmitCore(incoming: unknown, localOpMetadata: unknown): void {
|
|
767
782
|
const originalRefSeq = this.inFlightRefSeqs.shift();
|
|
768
783
|
assert(
|
|
769
784
|
originalRefSeq !== undefined,
|
|
@@ -805,7 +820,7 @@ export class SharedMatrix<T = any>
|
|
|
805
820
|
}
|
|
806
821
|
} else {
|
|
807
822
|
switch (content.target) {
|
|
808
|
-
case SnapshotPath.cols:
|
|
823
|
+
case SnapshotPath.cols: {
|
|
809
824
|
this.submitColMessage(
|
|
810
825
|
this.cols.regeneratePendingOp(
|
|
811
826
|
content,
|
|
@@ -814,7 +829,8 @@ export class SharedMatrix<T = any>
|
|
|
814
829
|
),
|
|
815
830
|
);
|
|
816
831
|
break;
|
|
817
|
-
|
|
832
|
+
}
|
|
833
|
+
case SnapshotPath.rows: {
|
|
818
834
|
this.submitRowMessage(
|
|
819
835
|
this.rows.regeneratePendingOp(
|
|
820
836
|
content,
|
|
@@ -823,6 +839,7 @@ export class SharedMatrix<T = any>
|
|
|
823
839
|
),
|
|
824
840
|
);
|
|
825
841
|
break;
|
|
842
|
+
}
|
|
826
843
|
default: {
|
|
827
844
|
unreachableCase(content);
|
|
828
845
|
}
|
|
@@ -830,12 +847,12 @@ export class SharedMatrix<T = any>
|
|
|
830
847
|
}
|
|
831
848
|
}
|
|
832
849
|
|
|
833
|
-
protected onDisconnect() {}
|
|
850
|
+
protected onDisconnect(): void {}
|
|
834
851
|
|
|
835
852
|
/**
|
|
836
853
|
* {@inheritDoc @fluidframework/shared-object-base#SharedObject.loadCore}
|
|
837
854
|
*/
|
|
838
|
-
protected async loadCore(storage: IChannelStorageService) {
|
|
855
|
+
protected async loadCore(storage: IChannelStorageService): Promise<void> {
|
|
839
856
|
try {
|
|
840
857
|
await this.rows.load(
|
|
841
858
|
this.runtime,
|
|
@@ -852,7 +869,13 @@ export class SharedMatrix<T = any>
|
|
|
852
869
|
_pendingCliSeqData,
|
|
853
870
|
setCellLwwToFwwPolicySwitchOpSeqNumber,
|
|
854
871
|
cellLastWriteTracker,
|
|
855
|
-
|
|
872
|
+
// Cast is needed since the (de)serializer returns content of type `any`.
|
|
873
|
+
] = (await deserializeBlob(storage, SnapshotPath.cells, this.serializer)) as [
|
|
874
|
+
RecurArray<MatrixItem<T>>,
|
|
875
|
+
unknown,
|
|
876
|
+
number,
|
|
877
|
+
RecurArray<CellLastWriteTrackerItem>,
|
|
878
|
+
];
|
|
856
879
|
|
|
857
880
|
this.cells = SparseArray2D.load(cellData);
|
|
858
881
|
this.setCellLwwToFwwPolicySwitchOpSeqNumber =
|
|
@@ -873,7 +896,7 @@ export class SharedMatrix<T = any>
|
|
|
873
896
|
rowHandle: Handle,
|
|
874
897
|
colHandle: Handle,
|
|
875
898
|
message: ISequencedDocumentMessage,
|
|
876
|
-
) {
|
|
899
|
+
): boolean {
|
|
877
900
|
assert(
|
|
878
901
|
this.setCellLwwToFwwPolicySwitchOpSeqNumber > -1,
|
|
879
902
|
0x85f /* should be in Fww mode when calling this method */,
|
|
@@ -896,7 +919,7 @@ export class SharedMatrix<T = any>
|
|
|
896
919
|
msg: ISequencedDocumentMessage,
|
|
897
920
|
local: boolean,
|
|
898
921
|
localOpMetadata: unknown,
|
|
899
|
-
) {
|
|
922
|
+
): void {
|
|
900
923
|
if (local) {
|
|
901
924
|
const recordedRefSeq = this.inFlightRefSeqs.shift();
|
|
902
925
|
assert(recordedRefSeq !== undefined, 0x8ba /* No pending recorded refSeq found */);
|
|
@@ -913,12 +936,14 @@ export class SharedMatrix<T = any>
|
|
|
913
936
|
const target = contents.target;
|
|
914
937
|
|
|
915
938
|
switch (target) {
|
|
916
|
-
case SnapshotPath.cols:
|
|
939
|
+
case SnapshotPath.cols: {
|
|
917
940
|
this.cols.applyMsg(msg, local);
|
|
918
941
|
break;
|
|
919
|
-
|
|
942
|
+
}
|
|
943
|
+
case SnapshotPath.rows: {
|
|
920
944
|
this.rows.applyMsg(msg, local);
|
|
921
945
|
break;
|
|
946
|
+
}
|
|
922
947
|
case undefined: {
|
|
923
948
|
assert(
|
|
924
949
|
contents.type === MatrixOp.set,
|
|
@@ -1018,8 +1043,9 @@ export class SharedMatrix<T = any>
|
|
|
1018
1043
|
}
|
|
1019
1044
|
break;
|
|
1020
1045
|
}
|
|
1021
|
-
default:
|
|
1046
|
+
default: {
|
|
1022
1047
|
unreachableCase(target, "unknown target");
|
|
1048
|
+
}
|
|
1023
1049
|
}
|
|
1024
1050
|
}
|
|
1025
1051
|
|
|
@@ -1028,7 +1054,7 @@ export class SharedMatrix<T = any>
|
|
|
1028
1054
|
position: number,
|
|
1029
1055
|
removedCount: number,
|
|
1030
1056
|
insertedCount: number,
|
|
1031
|
-
) => {
|
|
1057
|
+
): void => {
|
|
1032
1058
|
for (const consumer of this.consumers) {
|
|
1033
1059
|
consumer.rowsChanged(position, removedCount, insertedCount, this);
|
|
1034
1060
|
}
|
|
@@ -1039,13 +1065,13 @@ export class SharedMatrix<T = any>
|
|
|
1039
1065
|
position: number,
|
|
1040
1066
|
removedCount: number,
|
|
1041
1067
|
insertedCount: number,
|
|
1042
|
-
) => {
|
|
1068
|
+
): void => {
|
|
1043
1069
|
for (const consumer of this.consumers) {
|
|
1044
1070
|
consumer.colsChanged(position, removedCount, insertedCount, this);
|
|
1045
1071
|
}
|
|
1046
1072
|
};
|
|
1047
1073
|
|
|
1048
|
-
private readonly onRowHandlesRecycled = (rowHandles: Handle[]) => {
|
|
1074
|
+
private readonly onRowHandlesRecycled = (rowHandles: Handle[]): void => {
|
|
1049
1075
|
for (const rowHandle of rowHandles) {
|
|
1050
1076
|
this.cells.clearRows(/* rowStart: */ rowHandle, /* rowCount: */ 1);
|
|
1051
1077
|
this.pending.clearRows(/* rowStart: */ rowHandle, /* rowCount: */ 1);
|
|
@@ -1053,7 +1079,7 @@ export class SharedMatrix<T = any>
|
|
|
1053
1079
|
}
|
|
1054
1080
|
};
|
|
1055
1081
|
|
|
1056
|
-
private readonly onColHandlesRecycled = (colHandles: Handle[]) => {
|
|
1082
|
+
private readonly onColHandlesRecycled = (colHandles: Handle[]): void => {
|
|
1057
1083
|
for (const colHandle of colHandles) {
|
|
1058
1084
|
this.cells.clearCols(/* colStart: */ colHandle, /* colCount: */ 1);
|
|
1059
1085
|
this.pending.clearCols(/* colStart: */ colHandle, /* colCount: */ 1);
|
|
@@ -1061,7 +1087,7 @@ export class SharedMatrix<T = any>
|
|
|
1061
1087
|
}
|
|
1062
1088
|
};
|
|
1063
1089
|
|
|
1064
|
-
public switchSetCellPolicy() {
|
|
1090
|
+
public switchSetCellPolicy(): void {
|
|
1065
1091
|
if (this.setCellLwwToFwwPolicySwitchOpSeqNumber === -1) {
|
|
1066
1092
|
if (this.isAttached()) {
|
|
1067
1093
|
this.userSwitchedSetCellPolicy = true;
|
|
@@ -1079,7 +1105,11 @@ export class SharedMatrix<T = any>
|
|
|
1079
1105
|
* clobber the write op at the given 'localSeq'. This includes later ops that overwrite the cell
|
|
1080
1106
|
* with a different value as well as row/col removals that might recycled the given row/col handles.
|
|
1081
1107
|
*/
|
|
1082
|
-
private isLatestPendingWrite(
|
|
1108
|
+
private isLatestPendingWrite(
|
|
1109
|
+
rowHandle: Handle,
|
|
1110
|
+
colHandle: Handle,
|
|
1111
|
+
localSeq: number,
|
|
1112
|
+
): boolean {
|
|
1083
1113
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
1084
1114
|
const pendingLocalSeq = this.pending.getCell(rowHandle, colHandle)!;
|
|
1085
1115
|
|
|
@@ -1096,7 +1126,7 @@ export class SharedMatrix<T = any>
|
|
|
1096
1126
|
return pendingLocalSeq === localSeq;
|
|
1097
1127
|
}
|
|
1098
1128
|
|
|
1099
|
-
public toString() {
|
|
1129
|
+
public toString(): string {
|
|
1100
1130
|
let s = `client:${
|
|
1101
1131
|
this.runtime.clientId
|
|
1102
1132
|
}\nrows: ${this.rows.toString()}\ncols: ${this.cols.toString()}\n\n`;
|
package/src/packageVersion.ts
CHANGED
package/src/permutationvector.ts
CHANGED
|
@@ -20,6 +20,8 @@ import {
|
|
|
20
20
|
ISegment,
|
|
21
21
|
MergeTreeDeltaType,
|
|
22
22
|
MergeTreeMaintenanceType,
|
|
23
|
+
type IMergeTreeInsertMsg,
|
|
24
|
+
type IMergeTreeRemoveMsg,
|
|
23
25
|
} from "@fluidframework/merge-tree/internal";
|
|
24
26
|
import { ISummaryTreeWithStats } from "@fluidframework/runtime-definitions/internal";
|
|
25
27
|
import {
|
|
@@ -45,7 +47,7 @@ export class PermutationSegment extends BaseSegment {
|
|
|
45
47
|
public static readonly typeString: string = "PermutationSegment";
|
|
46
48
|
private _start = Handle.unallocated;
|
|
47
49
|
|
|
48
|
-
public static fromJSONObject(spec:
|
|
50
|
+
public static fromJSONObject(spec: IJSONSegment): PermutationSegment {
|
|
49
51
|
const [length, start] = spec as PermutationSegmentSpec;
|
|
50
52
|
return new PermutationSegment(length, start);
|
|
51
53
|
}
|
|
@@ -58,7 +60,7 @@ export class PermutationSegment extends BaseSegment {
|
|
|
58
60
|
this.cachedLength = length;
|
|
59
61
|
}
|
|
60
62
|
|
|
61
|
-
public get start() {
|
|
63
|
+
public get start(): Handle {
|
|
62
64
|
return this._start;
|
|
63
65
|
}
|
|
64
66
|
public set start(value: Handle) {
|
|
@@ -74,15 +76,15 @@ export class PermutationSegment extends BaseSegment {
|
|
|
74
76
|
this._start = value;
|
|
75
77
|
}
|
|
76
78
|
|
|
77
|
-
public reset() {
|
|
79
|
+
public reset(): void {
|
|
78
80
|
this._start = Handle.unallocated;
|
|
79
81
|
}
|
|
80
82
|
|
|
81
|
-
public toJSONObject() {
|
|
83
|
+
public toJSONObject(): number[] {
|
|
82
84
|
return [this.cachedLength, this.start];
|
|
83
85
|
}
|
|
84
86
|
|
|
85
|
-
public clone(start = 0, end = this.cachedLength) {
|
|
87
|
+
public clone(start = 0, end = this.cachedLength): PermutationSegment {
|
|
86
88
|
const b = new PermutationSegment(
|
|
87
89
|
/* length: */ end - start,
|
|
88
90
|
/* start: */ this.start + start,
|
|
@@ -91,7 +93,7 @@ export class PermutationSegment extends BaseSegment {
|
|
|
91
93
|
return b;
|
|
92
94
|
}
|
|
93
95
|
|
|
94
|
-
public canAppend(segment: ISegment) {
|
|
96
|
+
public canAppend(segment: ISegment): boolean {
|
|
95
97
|
const asPerm = segment as PermutationSegment;
|
|
96
98
|
|
|
97
99
|
return this.start === Handle.unallocated
|
|
@@ -99,7 +101,7 @@ export class PermutationSegment extends BaseSegment {
|
|
|
99
101
|
: asPerm.start === this.start + this.cachedLength;
|
|
100
102
|
}
|
|
101
103
|
|
|
102
|
-
protected createSplitSegmentAt(pos: number) {
|
|
104
|
+
protected createSplitSegmentAt(pos: number): PermutationSegment {
|
|
103
105
|
assert(
|
|
104
106
|
0 < pos && pos < this.cachedLength,
|
|
105
107
|
0x026 /* "Trying to split segment at out-of-bounds position!" */,
|
|
@@ -115,7 +117,7 @@ export class PermutationSegment extends BaseSegment {
|
|
|
115
117
|
return leafSegment;
|
|
116
118
|
}
|
|
117
119
|
|
|
118
|
-
public toString() {
|
|
120
|
+
public toString(): string {
|
|
119
121
|
return this.start === Handle.unallocated
|
|
120
122
|
? `<${this.cachedLength} empty>`
|
|
121
123
|
: `<${this.cachedLength}: ${this.start}..${this.start + this.cachedLength - 1}>`;
|
|
@@ -154,11 +156,11 @@ export class PermutationVector extends Client {
|
|
|
154
156
|
this.on("maintenance", this.onMaintenance);
|
|
155
157
|
}
|
|
156
158
|
|
|
157
|
-
public insert(start: number, length: number) {
|
|
159
|
+
public insert(start: number, length: number): IMergeTreeInsertMsg | undefined {
|
|
158
160
|
return this.insertSegmentLocal(start, new PermutationSegment(length));
|
|
159
161
|
}
|
|
160
162
|
|
|
161
|
-
public remove(start: number, length: number) {
|
|
163
|
+
public remove(start: number, length: number): IMergeTreeRemoveMsg {
|
|
162
164
|
return this.removeRangeLocal(start, start + length);
|
|
163
165
|
}
|
|
164
166
|
|
|
@@ -168,7 +170,9 @@ export class PermutationVector extends Client {
|
|
|
168
170
|
0x027 /* "Trying to get handle of out-of-bounds position!" */,
|
|
169
171
|
);
|
|
170
172
|
|
|
171
|
-
|
|
173
|
+
// TODO Non null asserting, why is this not null?
|
|
174
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
175
|
+
return this.handleCache.getHandle(pos)!;
|
|
172
176
|
}
|
|
173
177
|
|
|
174
178
|
public getAllocatedHandle(pos: number): Handle {
|
|
@@ -197,7 +201,7 @@ export class PermutationVector extends Client {
|
|
|
197
201
|
public adjustPosition(
|
|
198
202
|
pos: number,
|
|
199
203
|
op: Pick<ISequencedDocumentMessage, "referenceSequenceNumber" | "clientId">,
|
|
200
|
-
) {
|
|
204
|
+
): number | undefined {
|
|
201
205
|
const { segment, offset } = this.getContainingSegment(pos, {
|
|
202
206
|
referenceSequenceNumber: op.referenceSequenceNumber,
|
|
203
207
|
clientId: op.clientId,
|
|
@@ -214,7 +218,7 @@ export class PermutationVector extends Client {
|
|
|
214
218
|
return this.getPosition(segment) + offset!;
|
|
215
219
|
}
|
|
216
220
|
|
|
217
|
-
public handleToPosition(handle: Handle, localSeq = this.getCollabWindow().localSeq) {
|
|
221
|
+
public handleToPosition(handle: Handle, localSeq = this.getCollabWindow().localSeq): number {
|
|
218
222
|
assert(
|
|
219
223
|
localSeq <= this.getCollabWindow().localSeq,
|
|
220
224
|
0x028 /* "'localSeq' for op being resubmitted must be <= the 'localSeq' of the last submitted op." */,
|
|
@@ -296,12 +300,15 @@ export class PermutationVector extends Client {
|
|
|
296
300
|
runtime: IFluidDataStoreRuntime,
|
|
297
301
|
storage: IChannelStorageService,
|
|
298
302
|
serializer: IFluidSerializer,
|
|
299
|
-
) {
|
|
300
|
-
|
|
303
|
+
): Promise<{
|
|
304
|
+
catchupOpsP: Promise<ISequencedDocumentMessage[]>;
|
|
305
|
+
}> {
|
|
306
|
+
const handleTableData = (await deserializeBlob(
|
|
301
307
|
storage,
|
|
302
308
|
SnapshotPath.handleTable,
|
|
303
309
|
serializer,
|
|
304
|
-
|
|
310
|
+
// Cast is needed since the (de)serializer returns content of type `any`.
|
|
311
|
+
)) as Handle[];
|
|
305
312
|
|
|
306
313
|
this.handleTable = HandleTable.load<never>(handleTableData);
|
|
307
314
|
|
|
@@ -315,7 +322,7 @@ export class PermutationVector extends Client {
|
|
|
315
322
|
private readonly onDelta = (
|
|
316
323
|
opArgs: IMergeTreeDeltaOpArgs,
|
|
317
324
|
deltaArgs: IMergeTreeDeltaCallbackArgs,
|
|
318
|
-
) => {
|
|
325
|
+
): void => {
|
|
319
326
|
// Apply deltas in descending order to prevent positions from shifting.
|
|
320
327
|
const ranges = deltaArgs.deltaSegments
|
|
321
328
|
.map(({ segment }) => ({
|
|
@@ -332,7 +339,7 @@ export class PermutationVector extends Client {
|
|
|
332
339
|
}
|
|
333
340
|
|
|
334
341
|
switch (deltaArgs.operation) {
|
|
335
|
-
case MergeTreeDeltaType.INSERT:
|
|
342
|
+
case MergeTreeDeltaType.INSERT: {
|
|
336
343
|
// Pass 1: Perform any internal maintenance first to avoid reentrancy.
|
|
337
344
|
for (const { segment, position } of ranges) {
|
|
338
345
|
// HACK: We need to include the allocated handle in the segment's JSON representation
|
|
@@ -356,6 +363,7 @@ export class PermutationVector extends Client {
|
|
|
356
363
|
);
|
|
357
364
|
}
|
|
358
365
|
break;
|
|
366
|
+
}
|
|
359
367
|
|
|
360
368
|
case MergeTreeDeltaType.REMOVE: {
|
|
361
369
|
// Pass 1: Perform any internal maintenance first to avoid reentrancy.
|
|
@@ -378,12 +386,13 @@ export class PermutationVector extends Client {
|
|
|
378
386
|
break;
|
|
379
387
|
}
|
|
380
388
|
|
|
381
|
-
default:
|
|
389
|
+
default: {
|
|
382
390
|
throw new Error("Unhandled MergeTreeDeltaType");
|
|
391
|
+
}
|
|
383
392
|
}
|
|
384
393
|
};
|
|
385
394
|
|
|
386
|
-
private readonly onMaintenance = (args: IMergeTreeMaintenanceCallbackArgs) => {
|
|
395
|
+
private readonly onMaintenance = (args: IMergeTreeMaintenanceCallbackArgs): void => {
|
|
387
396
|
if (args.operation === MergeTreeMaintenanceType.UNLINK) {
|
|
388
397
|
let freed: number[] = [];
|
|
389
398
|
|
|
@@ -391,8 +400,11 @@ export class PermutationVector extends Client {
|
|
|
391
400
|
const asPerm = segment as PermutationSegment;
|
|
392
401
|
if (isHandleValid(asPerm.start)) {
|
|
393
402
|
// Note: Using the spread operator with `.splice()` can exhaust the stack.
|
|
403
|
+
// eslint-disable-next-line unicorn/prefer-spread
|
|
394
404
|
freed = freed.concat(
|
|
395
|
-
|
|
405
|
+
Array.from({ length: asPerm.cachedLength })
|
|
406
|
+
.fill(0)
|
|
407
|
+
.map((value, index) => index + asPerm.start),
|
|
396
408
|
);
|
|
397
409
|
}
|
|
398
410
|
}
|
|
@@ -408,7 +420,7 @@ export class PermutationVector extends Client {
|
|
|
408
420
|
}
|
|
409
421
|
};
|
|
410
422
|
|
|
411
|
-
public toString() {
|
|
423
|
+
public toString(): string {
|
|
412
424
|
const s: string[] = [];
|
|
413
425
|
|
|
414
426
|
this.walkSegments((segment) => {
|
|
@@ -425,7 +437,10 @@ export function reinsertSegmentIntoVector(
|
|
|
425
437
|
vector: PermutationVector,
|
|
426
438
|
pos: number,
|
|
427
439
|
spec: IJSONSegment,
|
|
428
|
-
) {
|
|
440
|
+
): {
|
|
441
|
+
op: IMergeTreeInsertMsg | undefined;
|
|
442
|
+
inserted: PermutationSegment;
|
|
443
|
+
} {
|
|
429
444
|
const original = PermutationSegment.fromJSONObject(spec);
|
|
430
445
|
|
|
431
446
|
// (Re)insert the removed number of rows at the original position.
|
package/src/range.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
/**
|
|
7
7
|
* Ensures that 0 \<= 'value' \< 'limit'. Throws a RangeError otherwise.
|
|
8
8
|
*/
|
|
9
|
-
export function ensureRange(value: number, limit: number) {
|
|
9
|
+
export function ensureRange(value: number, limit: number): void {
|
|
10
10
|
// Coerce 'value' to Uint32 so that we can range check with a single branch.
|
|
11
11
|
const _value = value >>> 0; // eslint-disable-line no-bitwise
|
|
12
12
|
|