@fluidframework/map 2.0.0-dev-rc.1.0.0.228517 → 2.0.0-dev-rc.2.0.0.245554
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.js → .eslintrc.cjs} +10 -1
- package/{.mocharc.js → .mocharc.cjs} +1 -1
- package/CHANGELOG.md +11 -0
- package/{api-extractor-esm.json → api-extractor-cjs.json} +5 -1
- package/api-extractor-lint.json +1 -1
- package/api-extractor.json +1 -1
- package/api-report/map.api.md +14 -57
- package/dist/directory.d.ts +14 -51
- package/dist/directory.d.ts.map +1 -1
- package/dist/directory.js +109 -172
- package/dist/directory.js.map +1 -1
- package/dist/index.d.ts +45 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +43 -8
- package/dist/index.js.map +1 -1
- package/dist/interfaces.d.ts.map +1 -1
- package/dist/interfaces.js.map +1 -1
- package/dist/internalInterfaces.d.ts +2 -2
- package/dist/internalInterfaces.d.ts.map +1 -1
- package/dist/internalInterfaces.js.map +1 -1
- package/dist/localValues.d.ts +3 -5
- package/dist/localValues.d.ts.map +1 -1
- package/dist/localValues.js +9 -8
- package/dist/localValues.js.map +1 -1
- package/dist/map-alpha.d.ts +31 -116
- package/dist/map-beta.d.ts +24 -105
- package/dist/map-public.d.ts +24 -105
- package/dist/map-untrimmed.d.ts +31 -116
- package/dist/map.d.ts +4 -23
- package/dist/map.d.ts.map +1 -1
- package/dist/map.js +6 -29
- package/dist/map.js.map +1 -1
- package/dist/mapKernel.d.ts +3 -4
- package/dist/mapKernel.d.ts.map +1 -1
- package/dist/mapKernel.js +30 -35
- package/dist/mapKernel.js.map +1 -1
- package/dist/package.json +3 -0
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/tsdoc-metadata.json +1 -1
- package/lib/{directory.d.mts → directory.d.ts} +15 -52
- package/lib/directory.d.ts.map +1 -0
- package/lib/{directory.mjs → directory.js} +110 -173
- package/lib/directory.js.map +1 -0
- package/lib/index.d.ts +61 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +55 -0
- package/lib/index.js.map +1 -0
- package/lib/{interfaces.d.mts → interfaces.d.ts} +1 -1
- package/lib/interfaces.d.ts.map +1 -0
- package/lib/{interfaces.mjs → interfaces.js} +1 -1
- package/lib/interfaces.js.map +1 -0
- package/lib/{internalInterfaces.d.mts → internalInterfaces.d.ts} +3 -3
- package/lib/internalInterfaces.d.ts.map +1 -0
- package/lib/{internalInterfaces.mjs → internalInterfaces.js} +1 -1
- package/lib/internalInterfaces.js.map +1 -0
- package/lib/{localValues.d.mts → localValues.d.ts} +4 -6
- package/lib/localValues.d.ts.map +1 -0
- package/lib/{localValues.mjs → localValues.js} +10 -9
- package/lib/localValues.js.map +1 -0
- package/lib/{map-alpha.d.mts → map-alpha.d.ts} +43 -116
- package/lib/{map-beta.d.mts → map-beta.d.ts} +36 -105
- package/lib/{map-public.d.mts → map-public.d.ts} +36 -105
- package/lib/{map-untrimmed.d.mts → map-untrimmed.d.ts} +43 -116
- package/lib/{map.d.mts → map.d.ts} +5 -24
- package/lib/map.d.ts.map +1 -0
- package/lib/{map.mjs → map.js} +5 -28
- package/lib/map.js.map +1 -0
- package/lib/{mapKernel.d.mts → mapKernel.d.ts} +4 -5
- package/lib/mapKernel.d.ts.map +1 -0
- package/lib/{mapKernel.mjs → mapKernel.js} +32 -37
- package/lib/mapKernel.js.map +1 -0
- package/lib/{packageVersion.d.mts → packageVersion.d.ts} +2 -2
- package/lib/packageVersion.d.ts.map +1 -0
- package/lib/{packageVersion.mjs → packageVersion.js} +2 -2
- package/lib/packageVersion.js.map +1 -0
- package/lib/test/memory/directory.spec.js +71 -0
- package/lib/test/memory/directory.spec.js.map +1 -0
- package/lib/test/memory/map.spec.js +71 -0
- package/lib/test/memory/map.spec.js.map +1 -0
- package/lib/test/mocha/directory.order.spec.js +422 -0
- package/lib/test/mocha/directory.order.spec.js.map +1 -0
- package/lib/test/mocha/directory.snapshot.spec.js +111 -0
- package/lib/test/mocha/directory.snapshot.spec.js.map +1 -0
- package/lib/test/mocha/directory.spec.js +1406 -0
- package/lib/test/mocha/directory.spec.js.map +1 -0
- package/lib/test/mocha/directoryEquivalenceUtils.js +36 -0
- package/lib/test/mocha/directoryEquivalenceUtils.js.map +1 -0
- package/lib/test/mocha/directoryFuzzTests.spec.js +337 -0
- package/lib/test/mocha/directoryFuzzTests.spec.js.map +1 -0
- package/lib/test/mocha/dirname.cjs +16 -0
- package/lib/test/mocha/dirname.cjs.map +1 -0
- package/lib/test/mocha/map.fuzz.spec.js +114 -0
- package/lib/test/mocha/map.fuzz.spec.js.map +1 -0
- package/lib/test/mocha/map.spec.js +687 -0
- package/lib/test/mocha/map.spec.js.map +1 -0
- package/lib/test/mocha/rebasing.spec.js +158 -0
- package/lib/test/mocha/rebasing.spec.js.map +1 -0
- package/lib/test/mocha/reconnection.spec.js +327 -0
- package/lib/test/mocha/reconnection.spec.js.map +1 -0
- package/lib/test/types/validateMapPrevious.generated.js +66 -0
- package/lib/test/types/validateMapPrevious.generated.js.map +1 -0
- package/package.json +55 -52
- package/src/directory.ts +159 -226
- package/src/index.ts +57 -4
- package/src/interfaces.ts +2 -2
- package/src/internalInterfaces.ts +2 -2
- package/src/localValues.ts +14 -9
- package/src/map.ts +7 -32
- package/src/mapKernel.ts +40 -42
- package/src/packageVersion.ts +1 -1
- package/tsconfig.cjs.json +7 -0
- package/tsconfig.json +2 -5
- package/lib/directory.d.mts.map +0 -1
- package/lib/directory.mjs.map +0 -1
- package/lib/index.d.mts +0 -9
- package/lib/index.d.mts.map +0 -1
- package/lib/index.mjs +0 -8
- package/lib/index.mjs.map +0 -1
- package/lib/interfaces.d.mts.map +0 -1
- package/lib/interfaces.mjs.map +0 -1
- package/lib/internalInterfaces.d.mts.map +0 -1
- package/lib/internalInterfaces.mjs.map +0 -1
- package/lib/localValues.d.mts.map +0 -1
- package/lib/localValues.mjs.map +0 -1
- package/lib/map.d.mts.map +0 -1
- package/lib/map.mjs.map +0 -1
- package/lib/mapKernel.d.mts.map +0 -1
- package/lib/mapKernel.mjs.map +0 -1
- package/lib/packageVersion.d.mts.map +0 -1
- package/lib/packageVersion.mjs.map +0 -1
package/src/directory.ts
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { assert } from "@fluidframework/core-utils";
|
|
6
|
+
import { assert, unreachableCase } from "@fluidframework/core-utils";
|
|
7
7
|
import { TypedEventEmitter } from "@fluid-internal/client-utils";
|
|
8
|
-
import { UsageError } from "@fluidframework/telemetry-utils";
|
|
8
|
+
import { ITelemetryLoggerExt, UsageError } from "@fluidframework/telemetry-utils";
|
|
9
9
|
import { readAndParse } from "@fluidframework/driver-utils";
|
|
10
10
|
import { ISequencedDocumentMessage, MessageType } from "@fluidframework/protocol-definitions";
|
|
11
11
|
import {
|
|
@@ -16,7 +16,12 @@ import {
|
|
|
16
16
|
IChannelFactory,
|
|
17
17
|
} from "@fluidframework/datastore-definitions";
|
|
18
18
|
import { ISummaryTreeWithStats, ITelemetryContext } from "@fluidframework/runtime-definitions";
|
|
19
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
IFluidSerializer,
|
|
21
|
+
SharedObject,
|
|
22
|
+
ValueType,
|
|
23
|
+
parseHandles,
|
|
24
|
+
} from "@fluidframework/shared-object-base";
|
|
20
25
|
import { SummaryTreeBuilder } from "@fluidframework/runtime-utils";
|
|
21
26
|
import path from "path-browserify";
|
|
22
27
|
import { RedBlackTree } from "@fluidframework/merge-tree";
|
|
@@ -30,9 +35,9 @@ import {
|
|
|
30
35
|
ISharedDirectory,
|
|
31
36
|
ISharedDirectoryEvents,
|
|
32
37
|
IValueChanged,
|
|
33
|
-
} from "./interfaces";
|
|
34
|
-
import { ILocalValue, LocalValueMaker, makeSerializable } from "./localValues";
|
|
35
|
-
import { pkgVersion } from "./packageVersion";
|
|
38
|
+
} from "./interfaces.js";
|
|
39
|
+
import { ILocalValue, LocalValueMaker, makeSerializable } from "./localValues.js";
|
|
40
|
+
import { pkgVersion } from "./packageVersion.js";
|
|
36
41
|
|
|
37
42
|
// We use path-browserify since this code can run safely on the server or the browser.
|
|
38
43
|
// We standardize on using posix slashes everywhere.
|
|
@@ -65,8 +70,6 @@ interface IDirectoryMessageHandler {
|
|
|
65
70
|
* @param localOpMetadata - The metadata to be submitted with the message.
|
|
66
71
|
*/
|
|
67
72
|
submit(op: IDirectoryOperation, localOpMetadata: unknown): void;
|
|
68
|
-
|
|
69
|
-
applyStashedOp(op: IDirectoryOperation): unknown;
|
|
70
73
|
}
|
|
71
74
|
|
|
72
75
|
/**
|
|
@@ -271,7 +274,7 @@ export interface IDirectoryNewStorageFormat {
|
|
|
271
274
|
* @sealed
|
|
272
275
|
* @alpha
|
|
273
276
|
*/
|
|
274
|
-
export class DirectoryFactory implements IChannelFactory {
|
|
277
|
+
export class DirectoryFactory implements IChannelFactory<ISharedDirectory> {
|
|
275
278
|
/**
|
|
276
279
|
* {@inheritDoc @fluidframework/datastore-definitions#IChannelFactory."type"}
|
|
277
280
|
*/
|
|
@@ -346,25 +349,25 @@ export class DirectoryFactory implements IChannelFactory {
|
|
|
346
349
|
* 4. A 'seq' value of zero indicates that the subdirectory was created in detached state, and it is considered acknowledged for the
|
|
347
350
|
* purpose of ordering.
|
|
348
351
|
*/
|
|
349
|
-
const seqDataComparator = (a: SequenceData, b: SequenceData) => {
|
|
352
|
+
const seqDataComparator = (a: SequenceData, b: SequenceData): number => {
|
|
350
353
|
if (isAcknowledgedOrDetached(a)) {
|
|
351
354
|
if (isAcknowledgedOrDetached(b)) {
|
|
352
355
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
353
|
-
return a.seq
|
|
356
|
+
return a.seq === b.seq ? a.clientSeq! - b.clientSeq! : a.seq - b.seq;
|
|
354
357
|
} else {
|
|
355
358
|
return -1;
|
|
356
359
|
}
|
|
357
360
|
} else {
|
|
358
|
-
if (
|
|
359
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
360
|
-
return a.seq !== b.seq ? a.seq - b.seq : a.clientSeq! - b.clientSeq!;
|
|
361
|
-
} else {
|
|
361
|
+
if (isAcknowledgedOrDetached(b)) {
|
|
362
362
|
return 1;
|
|
363
|
+
} else {
|
|
364
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
365
|
+
return a.seq === b.seq ? a.clientSeq! - b.clientSeq! : a.seq - b.seq;
|
|
363
366
|
}
|
|
364
367
|
}
|
|
365
368
|
};
|
|
366
369
|
|
|
367
|
-
function isAcknowledgedOrDetached(seqData: SequenceData) {
|
|
370
|
+
function isAcknowledgedOrDetached(seqData: SequenceData): boolean {
|
|
368
371
|
return seqData.seq >= 0;
|
|
369
372
|
}
|
|
370
373
|
|
|
@@ -433,6 +436,10 @@ class DirectoryCreationTracker {
|
|
|
433
436
|
}, keys);
|
|
434
437
|
return keys;
|
|
435
438
|
}
|
|
439
|
+
|
|
440
|
+
public get size(): number {
|
|
441
|
+
return this.keyToIndex.size;
|
|
442
|
+
}
|
|
436
443
|
}
|
|
437
444
|
|
|
438
445
|
/**
|
|
@@ -460,8 +467,8 @@ export class SharedDirectory
|
|
|
460
467
|
* @param id - Optional name of the shared directory
|
|
461
468
|
* @returns Newly create shared directory (but not attached yet)
|
|
462
469
|
*/
|
|
463
|
-
public static create(runtime: IFluidDataStoreRuntime, id?: string):
|
|
464
|
-
return runtime.createChannel(id, DirectoryFactory.Type) as
|
|
470
|
+
public static create(runtime: IFluidDataStoreRuntime, id?: string): ISharedDirectory {
|
|
471
|
+
return runtime.createChannel(id, DirectoryFactory.Type) as ISharedDirectory;
|
|
465
472
|
}
|
|
466
473
|
|
|
467
474
|
/**
|
|
@@ -469,7 +476,7 @@ export class SharedDirectory
|
|
|
469
476
|
*
|
|
470
477
|
* @returns A factory that creates and load SharedDirectory
|
|
471
478
|
*/
|
|
472
|
-
public static getFactory(): IChannelFactory {
|
|
479
|
+
public static getFactory(): IChannelFactory<ISharedDirectory> {
|
|
473
480
|
return new DirectoryFactory();
|
|
474
481
|
}
|
|
475
482
|
|
|
@@ -498,6 +505,7 @@ export class SharedDirectory
|
|
|
498
505
|
this.runtime,
|
|
499
506
|
this.serializer,
|
|
500
507
|
posix.sep,
|
|
508
|
+
this.logger,
|
|
501
509
|
);
|
|
502
510
|
|
|
503
511
|
/**
|
|
@@ -518,7 +526,7 @@ export class SharedDirectory
|
|
|
518
526
|
attributes: IChannelAttributes,
|
|
519
527
|
) {
|
|
520
528
|
super(id, runtime, attributes, "fluid_directory_");
|
|
521
|
-
this.localValueMaker = new LocalValueMaker(
|
|
529
|
+
this.localValueMaker = new LocalValueMaker();
|
|
522
530
|
this.setMessageHandlers();
|
|
523
531
|
// Mirror the containedValueChanged op on the SharedDirectory
|
|
524
532
|
this.root.on("containedValueChanged", (changed: IValueChanged, local: boolean) => {
|
|
@@ -781,10 +789,7 @@ export class SharedDirectory
|
|
|
781
789
|
// guaranteed during the serialization process. As a result, it is only essential to utilize the
|
|
782
790
|
// "fake" client sequence number to signify the loading order, and there is no need to retain
|
|
783
791
|
// the actual client sequence number at this point.
|
|
784
|
-
if (createInfo !== undefined && createInfo.csn >
|
|
785
|
-
// If csn is -1, then initialize it with 0, otherwise we will never process ops for this
|
|
786
|
-
// sub directory. This could be done at serialization time too, but we need to maintain
|
|
787
|
-
// back compat too and also we will actually know the state when it was serialized.
|
|
792
|
+
if (createInfo !== undefined && createInfo.csn > 0) {
|
|
788
793
|
if (!tempSeqNums.has(createInfo.csn)) {
|
|
789
794
|
tempSeqNums.set(createInfo.csn, 0);
|
|
790
795
|
}
|
|
@@ -792,6 +797,13 @@ export class SharedDirectory
|
|
|
792
797
|
seqData = { seq: createInfo.csn, clientSeq: fakeClientSeq };
|
|
793
798
|
tempSeqNums.set(createInfo.csn, ++fakeClientSeq);
|
|
794
799
|
} else {
|
|
800
|
+
/**
|
|
801
|
+
* 1. If csn is -1, then initialize it with 0, otherwise we will never process ops for this
|
|
802
|
+
* sub directory. This could be done at serialization time too, but we need to maintain
|
|
803
|
+
* back compat too and also we will actually know the state when it was serialized.
|
|
804
|
+
* 2. We need to make the csn = -1 and csn = 0 share the same counter, there are cases
|
|
805
|
+
* where both -1 and 0 coexist within a single document.
|
|
806
|
+
*/
|
|
795
807
|
seqData = {
|
|
796
808
|
seq: 0,
|
|
797
809
|
clientSeq: ++currentSubDir.localCreationSeq,
|
|
@@ -799,13 +811,14 @@ export class SharedDirectory
|
|
|
799
811
|
}
|
|
800
812
|
newSubDir = new SubDirectory(
|
|
801
813
|
seqData,
|
|
802
|
-
createInfo
|
|
803
|
-
? new Set
|
|
804
|
-
: new Set(),
|
|
814
|
+
createInfo === undefined
|
|
815
|
+
? new Set()
|
|
816
|
+
: new Set<string>(createInfo.ccIds),
|
|
805
817
|
this,
|
|
806
818
|
this.runtime,
|
|
807
819
|
this.serializer,
|
|
808
820
|
posix.join(currentSubDir.absolutePath, subdirName),
|
|
821
|
+
this.logger,
|
|
809
822
|
);
|
|
810
823
|
currentSubDir.populateSubDirectory(subdirName, newSubDir);
|
|
811
824
|
// Record the newly inserted subdirectory to the creation tracker
|
|
@@ -822,7 +835,8 @@ export class SharedDirectory
|
|
|
822
835
|
const localValue = this.makeLocal(
|
|
823
836
|
key,
|
|
824
837
|
currentSubDir.absolutePath,
|
|
825
|
-
|
|
838
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
839
|
+
parseHandles(serializable, this.serializer),
|
|
826
840
|
);
|
|
827
841
|
currentSubDir.populateStorage(key, localValue);
|
|
828
842
|
}
|
|
@@ -886,7 +900,7 @@ export class SharedDirectory
|
|
|
886
900
|
serializable.type === ValueType[ValueType.Shared],
|
|
887
901
|
0x1e4 /* "Unexpected serializable type" */,
|
|
888
902
|
);
|
|
889
|
-
return this.localValueMaker.fromSerializable(serializable);
|
|
903
|
+
return this.localValueMaker.fromSerializable(serializable, this.serializer, this.handle);
|
|
890
904
|
}
|
|
891
905
|
|
|
892
906
|
/**
|
|
@@ -940,12 +954,6 @@ export class SharedDirectory
|
|
|
940
954
|
subdir.resubmitClearMessage(op, localOpMetadata);
|
|
941
955
|
}
|
|
942
956
|
},
|
|
943
|
-
applyStashedOp: (op: IDirectoryClearOperation): IClearLocalOpMetadata | undefined => {
|
|
944
|
-
const subdir = this.getWorkingDirectory(op.path) as SubDirectory | undefined;
|
|
945
|
-
if (subdir) {
|
|
946
|
-
return subdir.applyStashedClearMessage(op);
|
|
947
|
-
}
|
|
948
|
-
},
|
|
949
957
|
});
|
|
950
958
|
this.messageHandlers.set("delete", {
|
|
951
959
|
process: (
|
|
@@ -967,14 +975,6 @@ export class SharedDirectory
|
|
|
967
975
|
subdir.resubmitKeyMessage(op, localOpMetadata);
|
|
968
976
|
}
|
|
969
977
|
},
|
|
970
|
-
applyStashedOp: (
|
|
971
|
-
op: IDirectoryDeleteOperation,
|
|
972
|
-
): IKeyEditLocalOpMetadata | undefined => {
|
|
973
|
-
const subdir = this.getWorkingDirectory(op.path) as SubDirectory | undefined;
|
|
974
|
-
if (subdir) {
|
|
975
|
-
return subdir.applyStashedDeleteMessage(op);
|
|
976
|
-
}
|
|
977
|
-
},
|
|
978
978
|
});
|
|
979
979
|
this.messageHandlers.set("set", {
|
|
980
980
|
process: (
|
|
@@ -997,13 +997,6 @@ export class SharedDirectory
|
|
|
997
997
|
subdir.resubmitKeyMessage(op, localOpMetadata);
|
|
998
998
|
}
|
|
999
999
|
},
|
|
1000
|
-
applyStashedOp: (op: IDirectorySetOperation): IKeyEditLocalOpMetadata | undefined => {
|
|
1001
|
-
const subdir = this.getWorkingDirectory(op.path) as SubDirectory | undefined;
|
|
1002
|
-
if (subdir) {
|
|
1003
|
-
const context = this.makeLocal(op.key, op.path, op.value);
|
|
1004
|
-
return subdir.applyStashedSetMessage(op, context);
|
|
1005
|
-
}
|
|
1006
|
-
},
|
|
1007
1000
|
});
|
|
1008
1001
|
|
|
1009
1002
|
this.messageHandlers.set("createSubDirectory", {
|
|
@@ -1027,14 +1020,6 @@ export class SharedDirectory
|
|
|
1027
1020
|
parentSubdir.resubmitSubDirectoryMessage(op, localOpMetadata);
|
|
1028
1021
|
}
|
|
1029
1022
|
},
|
|
1030
|
-
applyStashedOp: (
|
|
1031
|
-
op: IDirectoryCreateSubDirectoryOperation,
|
|
1032
|
-
): ICreateSubDirLocalOpMetadata | undefined => {
|
|
1033
|
-
const parentSubdir = this.getWorkingDirectory(op.path) as SubDirectory | undefined;
|
|
1034
|
-
if (parentSubdir) {
|
|
1035
|
-
return parentSubdir.applyStashedCreateSubDirMessage(op);
|
|
1036
|
-
}
|
|
1037
|
-
},
|
|
1038
1023
|
});
|
|
1039
1024
|
|
|
1040
1025
|
this.messageHandlers.set("deleteSubDirectory", {
|
|
@@ -1058,26 +1043,47 @@ export class SharedDirectory
|
|
|
1058
1043
|
parentSubdir.resubmitSubDirectoryMessage(op, localOpMetadata);
|
|
1059
1044
|
}
|
|
1060
1045
|
},
|
|
1061
|
-
applyStashedOp: (
|
|
1062
|
-
op: IDirectoryDeleteSubDirectoryOperation,
|
|
1063
|
-
): IDeleteSubDirLocalOpMetadata | undefined => {
|
|
1064
|
-
const parentSubdir = this.getWorkingDirectory(op.path) as SubDirectory | undefined;
|
|
1065
|
-
if (parentSubdir) {
|
|
1066
|
-
return parentSubdir.applyStashedDeleteSubDirMessage(op);
|
|
1067
|
-
}
|
|
1068
|
-
},
|
|
1069
1046
|
});
|
|
1070
1047
|
}
|
|
1071
1048
|
|
|
1072
1049
|
/**
|
|
1073
1050
|
* {@inheritDoc @fluidframework/shared-object-base#SharedObjectCore.applyStashedOp}
|
|
1074
1051
|
*/
|
|
1075
|
-
protected applyStashedOp(op: unknown):
|
|
1076
|
-
const
|
|
1077
|
-
|
|
1078
|
-
|
|
1052
|
+
protected applyStashedOp(op: unknown): void {
|
|
1053
|
+
const directoryOp = op as IDirectoryOperation;
|
|
1054
|
+
const dir = this.getWorkingDirectory(directoryOp.path);
|
|
1055
|
+
switch (directoryOp.type) {
|
|
1056
|
+
case "clear": {
|
|
1057
|
+
dir?.clear();
|
|
1058
|
+
break;
|
|
1059
|
+
}
|
|
1060
|
+
case "createSubDirectory": {
|
|
1061
|
+
dir?.createSubDirectory(directoryOp.subdirName);
|
|
1062
|
+
break;
|
|
1063
|
+
}
|
|
1064
|
+
case "delete": {
|
|
1065
|
+
dir?.delete(directoryOp.key);
|
|
1066
|
+
break;
|
|
1067
|
+
}
|
|
1068
|
+
case "deleteSubDirectory": {
|
|
1069
|
+
dir?.deleteSubDirectory(directoryOp.subdirName);
|
|
1070
|
+
break;
|
|
1071
|
+
}
|
|
1072
|
+
case "set": {
|
|
1073
|
+
dir?.set(
|
|
1074
|
+
directoryOp.key,
|
|
1075
|
+
this.localValueMaker.fromSerializable(
|
|
1076
|
+
directoryOp.value,
|
|
1077
|
+
this.serializer,
|
|
1078
|
+
this.handle,
|
|
1079
|
+
).value,
|
|
1080
|
+
);
|
|
1081
|
+
break;
|
|
1082
|
+
}
|
|
1083
|
+
default: {
|
|
1084
|
+
unreachableCase(directoryOp);
|
|
1085
|
+
}
|
|
1079
1086
|
}
|
|
1080
|
-
return handler.applyStashedOp(op as IDirectoryOperation);
|
|
1081
1087
|
}
|
|
1082
1088
|
|
|
1083
1089
|
private serializeDirectory(
|
|
@@ -1211,10 +1217,13 @@ function isDirectoryLocalOpMetadata(metadata: any): metadata is DirectoryLocalOp
|
|
|
1211
1217
|
|
|
1212
1218
|
/* eslint-enable @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access */
|
|
1213
1219
|
|
|
1220
|
+
// eslint-disable-next-line @rushstack/no-new-null
|
|
1214
1221
|
function assertNonNullClientId(clientId: string | null): asserts clientId is string {
|
|
1215
1222
|
assert(clientId !== null, 0x6af /* client id should never be null */);
|
|
1216
1223
|
}
|
|
1217
1224
|
|
|
1225
|
+
let hasLoggedDirectoryInconsistency = false;
|
|
1226
|
+
|
|
1218
1227
|
/**
|
|
1219
1228
|
* Node of the directory tree.
|
|
1220
1229
|
* @sealed
|
|
@@ -1305,6 +1314,7 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
|
|
|
1305
1314
|
private readonly runtime: IFluidDataStoreRuntime,
|
|
1306
1315
|
private readonly serializer: IFluidSerializer,
|
|
1307
1316
|
public readonly absolutePath: string,
|
|
1317
|
+
private readonly logger: ITelemetryLoggerExt,
|
|
1308
1318
|
) {
|
|
1309
1319
|
super();
|
|
1310
1320
|
this.localCreationSeqTracker = new DirectoryCreationTracker();
|
|
@@ -1438,13 +1448,15 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
|
|
|
1438
1448
|
}
|
|
1439
1449
|
|
|
1440
1450
|
/**
|
|
1441
|
-
*
|
|
1451
|
+
* Gets the Sequence Data which should be used for local changes.
|
|
1452
|
+
*
|
|
1442
1453
|
* @remarks While detached, 0 is used rather than -1 to represent a change which should be universally known (as opposed to known
|
|
1443
1454
|
* only by the local client). This ensures that if the directory is later attached, none of its data needs to be updated (the values
|
|
1444
1455
|
* last set while detached will now be known to any new client, until they are changed).
|
|
1445
1456
|
*
|
|
1446
1457
|
* The client sequence number is incremented by 1 for maintaining the internal order of locally created subdirectories
|
|
1447
|
-
*
|
|
1458
|
+
*
|
|
1459
|
+
* @privateRemarks TODO: Convert these conventions to named constants. The semantics used here match those for merge-tree.
|
|
1448
1460
|
*/
|
|
1449
1461
|
private getLocalSeq(): SequenceData {
|
|
1450
1462
|
return this.directory.isAttached()
|
|
@@ -1506,23 +1518,41 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
|
|
|
1506
1518
|
|
|
1507
1519
|
const subdirNames = [...ackedSubdirsInOrder, ...localSubdirsInOrder];
|
|
1508
1520
|
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1521
|
+
if (subdirNames.length !== this._subdirectories.size) {
|
|
1522
|
+
// TODO: AB#7022: Hitting this block indicates that the eventual consistency scheme for ordering subdirectories
|
|
1523
|
+
// has failed. Fall back to previous directory behavior, which didn't guarantee ordering.
|
|
1524
|
+
// It's not currently clear how to reach this state, so log some diagnostics to help understand the issue.
|
|
1525
|
+
// This whole block should eventually be replaced by an assert that the two sizes align.
|
|
1526
|
+
if (!hasLoggedDirectoryInconsistency) {
|
|
1527
|
+
this.logger.sendTelemetryEvent({
|
|
1528
|
+
eventName: "inconsistentSubdirectoryOrdering",
|
|
1529
|
+
localKeyCount: this.localCreationSeqTracker.size,
|
|
1530
|
+
ackedKeyCount: this.ackedCreationSeqTracker.size,
|
|
1531
|
+
subdirNamesLength: subdirNames.length,
|
|
1532
|
+
subdirectoriesSize: this._subdirectories.size,
|
|
1533
|
+
});
|
|
1534
|
+
hasLoggedDirectoryInconsistency = true;
|
|
1535
|
+
}
|
|
1536
|
+
|
|
1537
|
+
return this._subdirectories.entries();
|
|
1538
|
+
}
|
|
1513
1539
|
|
|
1514
1540
|
const entriesIterator = {
|
|
1515
1541
|
index: 0,
|
|
1516
1542
|
dirs: this._subdirectories,
|
|
1517
|
-
next(): IteratorResult<[string,
|
|
1543
|
+
next(): IteratorResult<[string, IDirectory]> {
|
|
1518
1544
|
if (this.index < subdirNames.length) {
|
|
1519
1545
|
const subdirName = subdirNames[this.index++];
|
|
1520
1546
|
const subdir = this.dirs.get(subdirName);
|
|
1547
|
+
assert(
|
|
1548
|
+
subdir !== undefined,
|
|
1549
|
+
0x8ac /* Could not find expected sub-directory. */,
|
|
1550
|
+
);
|
|
1521
1551
|
return { value: [subdirName, subdir], done: false };
|
|
1522
1552
|
}
|
|
1523
1553
|
return { value: undefined, done: true };
|
|
1524
1554
|
},
|
|
1525
|
-
[Symbol.iterator](): IterableIterator<[string,
|
|
1555
|
+
[Symbol.iterator](): IterableIterator<[string, IDirectory]> {
|
|
1526
1556
|
return this;
|
|
1527
1557
|
},
|
|
1528
1558
|
};
|
|
@@ -1685,7 +1715,6 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
|
|
|
1685
1715
|
* @param local - Whether the message originated from the local client
|
|
1686
1716
|
* @param localOpMetadata - For local client messages, this is the metadata that was submitted with the message.
|
|
1687
1717
|
* For messages from a remote client, this will be undefined.
|
|
1688
|
-
* @internal
|
|
1689
1718
|
*/
|
|
1690
1719
|
public processClearMessage(
|
|
1691
1720
|
msg: ISequencedDocumentMessage,
|
|
@@ -1712,25 +1741,6 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
|
|
|
1712
1741
|
this.clearExceptPendingKeys(false);
|
|
1713
1742
|
}
|
|
1714
1743
|
|
|
1715
|
-
/**
|
|
1716
|
-
* Apply clear operation locally and generate metadata
|
|
1717
|
-
* @param op - Op to apply
|
|
1718
|
-
* @returns metadata generated for stahed op
|
|
1719
|
-
*/
|
|
1720
|
-
public applyStashedClearMessage(op: IDirectoryClearOperation): IClearLocalOpMetadata {
|
|
1721
|
-
this.throwIfDisposed();
|
|
1722
|
-
const previousValue = new Map<string, ILocalValue>(this._storage);
|
|
1723
|
-
this.clearExceptPendingKeys(true);
|
|
1724
|
-
const pendingMsgId = ++this.pendingMessageId;
|
|
1725
|
-
this.pendingClearMessageIds.push(pendingMsgId);
|
|
1726
|
-
const metadata: IClearLocalOpMetadata = {
|
|
1727
|
-
type: "clear",
|
|
1728
|
-
pendingMessageId: pendingMsgId,
|
|
1729
|
-
previousStorage: previousValue,
|
|
1730
|
-
};
|
|
1731
|
-
return metadata;
|
|
1732
|
-
}
|
|
1733
|
-
|
|
1734
1744
|
/**
|
|
1735
1745
|
* Process a delete operation.
|
|
1736
1746
|
* @param msg - The message from the server to apply.
|
|
@@ -1738,7 +1748,6 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
|
|
|
1738
1748
|
* @param local - Whether the message originated from the local client
|
|
1739
1749
|
* @param localOpMetadata - For local client messages, this is the metadata that was submitted with the message.
|
|
1740
1750
|
* For messages from a remote client, this will be undefined.
|
|
1741
|
-
* @internal
|
|
1742
1751
|
*/
|
|
1743
1752
|
public processDeleteMessage(
|
|
1744
1753
|
msg: ISequencedDocumentMessage,
|
|
@@ -1758,23 +1767,6 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
|
|
|
1758
1767
|
this.deleteCore(op.key, local);
|
|
1759
1768
|
}
|
|
1760
1769
|
|
|
1761
|
-
/**
|
|
1762
|
-
* Apply delete operation locally and generate metadata
|
|
1763
|
-
* @param op - Op to apply
|
|
1764
|
-
* @returns metadata generated for stahed op
|
|
1765
|
-
*/
|
|
1766
|
-
public applyStashedDeleteMessage(op: IDirectoryDeleteOperation): IKeyEditLocalOpMetadata {
|
|
1767
|
-
this.throwIfDisposed();
|
|
1768
|
-
const previousValue = this.deleteCore(op.key, true);
|
|
1769
|
-
const pendingMessageId = this.getKeyMessageId(op);
|
|
1770
|
-
const localMetadata: IKeyEditLocalOpMetadata = {
|
|
1771
|
-
type: "edit",
|
|
1772
|
-
pendingMessageId,
|
|
1773
|
-
previousValue,
|
|
1774
|
-
};
|
|
1775
|
-
return localMetadata;
|
|
1776
|
-
}
|
|
1777
|
-
|
|
1778
1770
|
/**
|
|
1779
1771
|
* Process a set operation.
|
|
1780
1772
|
* @param msg - The message from the server to apply.
|
|
@@ -1782,7 +1774,6 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
|
|
|
1782
1774
|
* @param local - Whether the message originated from the local client
|
|
1783
1775
|
* @param localOpMetadata - For local client messages, this is the metadata that was submitted with the message.
|
|
1784
1776
|
* For messages from a remote client, this will be undefined.
|
|
1785
|
-
* @internal
|
|
1786
1777
|
*/
|
|
1787
1778
|
public processSetMessage(
|
|
1788
1779
|
msg: ISequencedDocumentMessage,
|
|
@@ -1807,28 +1798,6 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
|
|
|
1807
1798
|
this.setCore(op.key, context!, local);
|
|
1808
1799
|
}
|
|
1809
1800
|
|
|
1810
|
-
/**
|
|
1811
|
-
* Apply set operation locally and generate metadata
|
|
1812
|
-
* @param op - Op to apply
|
|
1813
|
-
* @returns metadata generated for stahed op
|
|
1814
|
-
*/
|
|
1815
|
-
public applyStashedSetMessage(
|
|
1816
|
-
op: IDirectorySetOperation,
|
|
1817
|
-
context: ILocalValue,
|
|
1818
|
-
): IKeyEditLocalOpMetadata {
|
|
1819
|
-
this.throwIfDisposed();
|
|
1820
|
-
// Set the value locally.
|
|
1821
|
-
const previousValue = this.setCore(op.key, context, true);
|
|
1822
|
-
|
|
1823
|
-
// Create metadata
|
|
1824
|
-
const pendingMessageId = this.getKeyMessageId(op);
|
|
1825
|
-
const localMetadata: IKeyEditLocalOpMetadata = {
|
|
1826
|
-
type: "edit",
|
|
1827
|
-
pendingMessageId,
|
|
1828
|
-
previousValue,
|
|
1829
|
-
};
|
|
1830
|
-
return localMetadata;
|
|
1831
|
-
}
|
|
1832
1801
|
/**
|
|
1833
1802
|
* Process a create subdirectory operation.
|
|
1834
1803
|
* @param msg - The message from the server to apply.
|
|
@@ -1836,7 +1805,6 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
|
|
|
1836
1805
|
* @param local - Whether the message originated from the local client
|
|
1837
1806
|
* @param localOpMetadata - For local client messages, this is the metadata that was submitted with the message.
|
|
1838
1807
|
* For messages from a remote client, this will be undefined.
|
|
1839
|
-
* @internal
|
|
1840
1808
|
*/
|
|
1841
1809
|
public processCreateSubDirectoryMessage(
|
|
1842
1810
|
msg: ISequencedDocumentMessage,
|
|
@@ -1862,30 +1830,6 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
|
|
|
1862
1830
|
);
|
|
1863
1831
|
}
|
|
1864
1832
|
|
|
1865
|
-
/**
|
|
1866
|
-
* Apply createSubDirectory operation locally and generate metadata
|
|
1867
|
-
* @param op - Op to apply
|
|
1868
|
-
* @returns metadata generated for stahed op
|
|
1869
|
-
*/
|
|
1870
|
-
public applyStashedCreateSubDirMessage(
|
|
1871
|
-
op: IDirectoryCreateSubDirectoryOperation,
|
|
1872
|
-
): ICreateSubDirLocalOpMetadata {
|
|
1873
|
-
this.throwIfDisposed();
|
|
1874
|
-
// Create the sub directory locally first.
|
|
1875
|
-
this.createSubDirectoryCore(
|
|
1876
|
-
op.subdirName,
|
|
1877
|
-
true,
|
|
1878
|
-
this.getLocalSeq(),
|
|
1879
|
-
this.runtime.clientId ?? "detached",
|
|
1880
|
-
);
|
|
1881
|
-
this.updatePendingSubDirMessageCount(op);
|
|
1882
|
-
|
|
1883
|
-
const localOpMetadata: ICreateSubDirLocalOpMetadata = {
|
|
1884
|
-
type: "createSubDir",
|
|
1885
|
-
};
|
|
1886
|
-
return localOpMetadata;
|
|
1887
|
-
}
|
|
1888
|
-
|
|
1889
1833
|
/**
|
|
1890
1834
|
* Process a delete subdirectory operation.
|
|
1891
1835
|
* @param msg - The message from the server to apply.
|
|
@@ -1893,7 +1837,6 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
|
|
|
1893
1837
|
* @param local - Whether the message originated from the local client
|
|
1894
1838
|
* @param localOpMetadata - For local client messages, this is the metadata that was submitted with the message.
|
|
1895
1839
|
* For messages from a remote client, this will be undefined.
|
|
1896
|
-
* @internal
|
|
1897
1840
|
*/
|
|
1898
1841
|
public processDeleteSubDirectoryMessage(
|
|
1899
1842
|
msg: ISequencedDocumentMessage,
|
|
@@ -1913,24 +1856,6 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
|
|
|
1913
1856
|
this.deleteSubDirectoryCore(op.subdirName, local);
|
|
1914
1857
|
}
|
|
1915
1858
|
|
|
1916
|
-
/**
|
|
1917
|
-
* Apply deleteSubDirectory operation locally and generate metadata
|
|
1918
|
-
* @param op - Op to apply
|
|
1919
|
-
* @returns metadata generated for stahed op
|
|
1920
|
-
*/
|
|
1921
|
-
public applyStashedDeleteSubDirMessage(
|
|
1922
|
-
op: IDirectoryDeleteSubDirectoryOperation,
|
|
1923
|
-
): IDeleteSubDirLocalOpMetadata {
|
|
1924
|
-
this.throwIfDisposed();
|
|
1925
|
-
const subDir = this.deleteSubDirectoryCore(op.subdirName, true);
|
|
1926
|
-
this.updatePendingSubDirMessageCount(op);
|
|
1927
|
-
const metadata: IDeleteSubDirLocalOpMetadata = {
|
|
1928
|
-
type: "deleteSubDir",
|
|
1929
|
-
subDirectory: subDir,
|
|
1930
|
-
};
|
|
1931
|
-
return metadata;
|
|
1932
|
-
}
|
|
1933
|
-
|
|
1934
1859
|
/**
|
|
1935
1860
|
* Submit a clear operation.
|
|
1936
1861
|
* @param op - The operation
|
|
@@ -1953,7 +1878,6 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
|
|
|
1953
1878
|
/**
|
|
1954
1879
|
* Resubmit a clear operation.
|
|
1955
1880
|
* @param op - The operation
|
|
1956
|
-
* @internal
|
|
1957
1881
|
*/
|
|
1958
1882
|
public resubmitClearMessage(op: IDirectoryClearOperation, localOpMetadata: unknown): void {
|
|
1959
1883
|
assert(
|
|
@@ -1976,10 +1900,10 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
|
|
|
1976
1900
|
// We don't reuse the metadata pendingMessageId but send a new one on each submit.
|
|
1977
1901
|
const pendingMessageId = ++this.pendingMessageId;
|
|
1978
1902
|
const pendingMessageIds = this.pendingKeys.get(op.key);
|
|
1979
|
-
if (pendingMessageIds
|
|
1980
|
-
pendingMessageIds.push(pendingMessageId);
|
|
1981
|
-
} else {
|
|
1903
|
+
if (pendingMessageIds === undefined) {
|
|
1982
1904
|
this.pendingKeys.set(op.key, [pendingMessageId]);
|
|
1905
|
+
} else {
|
|
1906
|
+
pendingMessageIds.push(pendingMessageId);
|
|
1983
1907
|
}
|
|
1984
1908
|
return pendingMessageId;
|
|
1985
1909
|
}
|
|
@@ -2000,7 +1924,6 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
|
|
|
2000
1924
|
* Submit a key message to remote clients based on a previous submit.
|
|
2001
1925
|
* @param op - The map key message
|
|
2002
1926
|
* @param localOpMetadata - Metadata from the previous submit
|
|
2003
|
-
* @internal
|
|
2004
1927
|
*/
|
|
2005
1928
|
public resubmitKeyMessage(op: IDirectoryKeyOperation, localOpMetadata: unknown): void {
|
|
2006
1929
|
assert(
|
|
@@ -2013,9 +1936,7 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
|
|
|
2013
1936
|
// Only submit the op, if we have record for it, otherwise it is possible that the older instance
|
|
2014
1937
|
// is already deleted, in which case we don't need to submit the op.
|
|
2015
1938
|
if (pendingMessageIds !== undefined) {
|
|
2016
|
-
const index = pendingMessageIds.
|
|
2017
|
-
(id) => id === localOpMetadata.pendingMessageId,
|
|
2018
|
-
);
|
|
1939
|
+
const index = pendingMessageIds.indexOf(localOpMetadata.pendingMessageId);
|
|
2019
1940
|
if (index === -1) {
|
|
2020
1941
|
return;
|
|
2021
1942
|
}
|
|
@@ -2027,12 +1948,12 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
|
|
|
2027
1948
|
}
|
|
2028
1949
|
}
|
|
2029
1950
|
|
|
2030
|
-
private incrementPendingSubDirCount(map: Map<string, number>, subDirName: string) {
|
|
1951
|
+
private incrementPendingSubDirCount(map: Map<string, number>, subDirName: string): void {
|
|
2031
1952
|
const count = map.get(subDirName) ?? 0;
|
|
2032
1953
|
map.set(subDirName, count + 1);
|
|
2033
1954
|
}
|
|
2034
1955
|
|
|
2035
|
-
private decrementPendingSubDirCount(map: Map<string, number>, subDirName: string) {
|
|
1956
|
+
private decrementPendingSubDirCount(map: Map<string, number>, subDirName: string): void {
|
|
2036
1957
|
const count = map.get(subDirName) ?? 0;
|
|
2037
1958
|
map.set(subDirName, count - 1);
|
|
2038
1959
|
if (count <= 1) {
|
|
@@ -2044,7 +1965,7 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
|
|
|
2044
1965
|
* Update the count for pending create/delete of the sub directory so that it can be validated on receiving op
|
|
2045
1966
|
* or while resubmitting the op.
|
|
2046
1967
|
*/
|
|
2047
|
-
private updatePendingSubDirMessageCount(op: IDirectorySubDirectoryOperation) {
|
|
1968
|
+
private updatePendingSubDirMessageCount(op: IDirectorySubDirectoryOperation): void {
|
|
2048
1969
|
if (op.type === "deleteSubDirectory") {
|
|
2049
1970
|
this.incrementPendingSubDirCount(
|
|
2050
1971
|
this.pendingDeleteSubDirectoriesTracker,
|
|
@@ -2095,7 +2016,6 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
|
|
|
2095
2016
|
* Submit a subdirectory operation again
|
|
2096
2017
|
* @param op - The operation
|
|
2097
2018
|
* @param localOpMetadata - metadata submitted with the op originally
|
|
2098
|
-
* @internal
|
|
2099
2019
|
*/
|
|
2100
2020
|
public resubmitSubDirectoryMessage(
|
|
2101
2021
|
op: IDirectorySubDirectoryOperation,
|
|
@@ -2139,7 +2059,6 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
|
|
|
2139
2059
|
* Get the storage of this subdirectory in a serializable format, to be used in snapshotting.
|
|
2140
2060
|
* @param serializer - The serializer to use to serialize handles in its values.
|
|
2141
2061
|
* @returns The JSONable string representing the storage of this subdirectory
|
|
2142
|
-
* @internal
|
|
2143
2062
|
*/
|
|
2144
2063
|
public *getSerializedStorage(
|
|
2145
2064
|
serializer: IFluidSerializer,
|
|
@@ -2152,11 +2071,11 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
|
|
|
2152
2071
|
}
|
|
2153
2072
|
}
|
|
2154
2073
|
|
|
2155
|
-
public getSerializableCreateInfo() {
|
|
2074
|
+
public getSerializableCreateInfo(): ICreateInfo {
|
|
2156
2075
|
this.throwIfDisposed();
|
|
2157
2076
|
const createInfo: ICreateInfo = {
|
|
2158
2077
|
csn: this.seqData.seq,
|
|
2159
|
-
ccIds:
|
|
2078
|
+
ccIds: [...this.clientIds],
|
|
2160
2079
|
};
|
|
2161
2080
|
return createInfo;
|
|
2162
2081
|
}
|
|
@@ -2165,7 +2084,6 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
|
|
|
2165
2084
|
* Populate a key value in this subdirectory's storage, to be used when loading from snapshot.
|
|
2166
2085
|
* @param key - The key to populate
|
|
2167
2086
|
* @param localValue - The local value to populate into it
|
|
2168
|
-
* @internal
|
|
2169
2087
|
*/
|
|
2170
2088
|
public populateStorage(key: string, localValue: ILocalValue): void {
|
|
2171
2089
|
this.throwIfDisposed();
|
|
@@ -2176,7 +2094,6 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
|
|
|
2176
2094
|
* Populate a subdirectory into this subdirectory, to be used when loading from snapshot.
|
|
2177
2095
|
* @param subdirName - The name of the subdirectory to add
|
|
2178
2096
|
* @param newSubDir - The new subdirectory to add
|
|
2179
|
-
* @internal
|
|
2180
2097
|
*/
|
|
2181
2098
|
public populateSubDirectory(subdirName: string, newSubDir: SubDirectory): void {
|
|
2182
2099
|
this.throwIfDisposed();
|
|
@@ -2188,7 +2105,6 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
|
|
|
2188
2105
|
* value so op handlers can be retrieved
|
|
2189
2106
|
* @param key - The key to retrieve from
|
|
2190
2107
|
* @returns The local value
|
|
2191
|
-
* @internal
|
|
2192
2108
|
*/
|
|
2193
2109
|
public getLocalValue<T extends ILocalValue = ILocalValue>(key: string): T {
|
|
2194
2110
|
this.throwIfDisposed();
|
|
@@ -2241,46 +2157,62 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
|
|
|
2241
2157
|
throw new Error("Rollback op does match last clear");
|
|
2242
2158
|
}
|
|
2243
2159
|
} else if ((op.type === "delete" || op.type === "set") && localOpMetadata.type === "edit") {
|
|
2160
|
+
const key: unknown = op.key;
|
|
2161
|
+
assert(key !== undefined, 0x8ad /* "key" property is missing from edit operation. */);
|
|
2162
|
+
assert(
|
|
2163
|
+
typeof key === "string",
|
|
2164
|
+
0x8ae /* "key" property in edit operation is misconfigured. Expected a string. */,
|
|
2165
|
+
);
|
|
2166
|
+
|
|
2244
2167
|
if (localOpMetadata.previousValue === undefined) {
|
|
2245
|
-
this.deleteCore(
|
|
2168
|
+
this.deleteCore(key, true);
|
|
2246
2169
|
} else {
|
|
2247
|
-
this.setCore(
|
|
2170
|
+
this.setCore(key, localOpMetadata.previousValue, true);
|
|
2248
2171
|
}
|
|
2249
2172
|
|
|
2250
|
-
this.rollbackPendingMessageId(
|
|
2251
|
-
this.pendingKeys,
|
|
2252
|
-
op.key as string,
|
|
2253
|
-
localOpMetadata.pendingMessageId,
|
|
2254
|
-
);
|
|
2173
|
+
this.rollbackPendingMessageId(this.pendingKeys, key, localOpMetadata.pendingMessageId);
|
|
2255
2174
|
} else if (op.type === "createSubDirectory" && localOpMetadata.type === "createSubDir") {
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2175
|
+
const subdirName: unknown = op.subdirName;
|
|
2176
|
+
assert(
|
|
2177
|
+
subdirName !== undefined,
|
|
2178
|
+
0x8af /* "subdirName" property is missing from "createSubDirectory" operation. */,
|
|
2179
|
+
);
|
|
2180
|
+
assert(
|
|
2181
|
+
typeof subdirName === "string",
|
|
2182
|
+
0x8b0 /* "subdirName" property in "createSubDirectory" operation is misconfigured. Expected a string. */,
|
|
2261
2183
|
);
|
|
2184
|
+
|
|
2185
|
+
this.deleteSubDirectoryCore(subdirName, true);
|
|
2186
|
+
this.decrementPendingSubDirCount(this.pendingCreateSubDirectoriesTracker, subdirName);
|
|
2262
2187
|
} else if (op.type === "deleteSubDirectory" && localOpMetadata.type === "deleteSubDir") {
|
|
2188
|
+
const subdirName: unknown = op.subdirName;
|
|
2189
|
+
assert(
|
|
2190
|
+
subdirName !== undefined,
|
|
2191
|
+
0x8b1 /* "subdirName" property is missing from "deleteSubDirectory" operation. */,
|
|
2192
|
+
);
|
|
2193
|
+
assert(
|
|
2194
|
+
typeof subdirName === "string",
|
|
2195
|
+
0x8b2 /* "subdirName" property in "deleteSubDirectory" operation is misconfigured. Expected a string. */,
|
|
2196
|
+
);
|
|
2197
|
+
|
|
2263
2198
|
if (localOpMetadata.subDirectory !== undefined) {
|
|
2264
2199
|
this.undeleteSubDirectoryTree(localOpMetadata.subDirectory);
|
|
2265
2200
|
// don't need to register events because deleting never unregistered
|
|
2266
|
-
this._subdirectories.set(
|
|
2201
|
+
this._subdirectories.set(subdirName, localOpMetadata.subDirectory);
|
|
2267
2202
|
// Restore the record in creation tracker
|
|
2268
2203
|
if (isAcknowledgedOrDetached(localOpMetadata.subDirectory.seqData)) {
|
|
2269
|
-
this.ackedCreationSeqTracker.set(
|
|
2204
|
+
this.ackedCreationSeqTracker.set(subdirName, {
|
|
2270
2205
|
...localOpMetadata.subDirectory.seqData,
|
|
2271
2206
|
});
|
|
2272
2207
|
} else {
|
|
2273
|
-
this.localCreationSeqTracker.set(
|
|
2208
|
+
this.localCreationSeqTracker.set(subdirName, {
|
|
2274
2209
|
...localOpMetadata.subDirectory.seqData,
|
|
2275
2210
|
});
|
|
2276
2211
|
}
|
|
2277
|
-
this.emit("subDirectoryCreated",
|
|
2212
|
+
this.emit("subDirectoryCreated", subdirName, true, this);
|
|
2278
2213
|
}
|
|
2279
2214
|
|
|
2280
|
-
this.decrementPendingSubDirCount(
|
|
2281
|
-
this.pendingDeleteSubDirectoriesTracker,
|
|
2282
|
-
op.subDirName as string,
|
|
2283
|
-
);
|
|
2215
|
+
this.decrementPendingSubDirCount(this.pendingDeleteSubDirectoriesTracker, subdirName);
|
|
2284
2216
|
} else {
|
|
2285
2217
|
throw new Error("Unsupported op for rollback");
|
|
2286
2218
|
}
|
|
@@ -2370,7 +2302,7 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
|
|
|
2370
2302
|
* can be deleted and created again, then this finds if the message is for current instance of directory or not.
|
|
2371
2303
|
* @param msg - message for the directory
|
|
2372
2304
|
*/
|
|
2373
|
-
private isMessageForCurrentInstanceOfSubDirectory(msg: ISequencedDocumentMessage) {
|
|
2305
|
+
private isMessageForCurrentInstanceOfSubDirectory(msg: ISequencedDocumentMessage): boolean {
|
|
2374
2306
|
// If the message is either from the creator of directory or this directory was created when
|
|
2375
2307
|
// container was detached or in case this directory is already live(known to other clients)
|
|
2376
2308
|
// and the op was created after the directory was created then apply this op.
|
|
@@ -2592,15 +2524,16 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
|
|
|
2592
2524
|
this.runtime,
|
|
2593
2525
|
this.serializer,
|
|
2594
2526
|
absolutePath,
|
|
2527
|
+
this.logger,
|
|
2595
2528
|
);
|
|
2596
2529
|
/**
|
|
2597
2530
|
* Store the sequnce numbers of newly created subdirectory to the proper creation tracker, based
|
|
2598
2531
|
* on whether the creation behavior has been ack'd or not
|
|
2599
2532
|
*/
|
|
2600
|
-
if (
|
|
2601
|
-
this.localCreationSeqTracker.set(subdirName, { ...seqData });
|
|
2602
|
-
} else {
|
|
2533
|
+
if (isAcknowledgedOrDetached(seqData)) {
|
|
2603
2534
|
this.ackedCreationSeqTracker.set(subdirName, { ...seqData });
|
|
2535
|
+
} else {
|
|
2536
|
+
this.localCreationSeqTracker.set(subdirName, { ...seqData });
|
|
2604
2537
|
}
|
|
2605
2538
|
|
|
2606
2539
|
this.registerEventsOnSubDirectory(subDir, subdirName);
|