@fluidframework/map 2.0.0-internal.3.3.2 → 2.0.0-internal.3.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/directory.d.ts +32 -0
- package/dist/directory.d.ts.map +1 -1
- package/dist/directory.js +219 -60
- package/dist/directory.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/lib/directory.d.ts +32 -0
- package/lib/directory.d.ts.map +1 -1
- package/lib/directory.js +219 -60
- package/lib/directory.js.map +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/package.json +21 -22
- package/src/directory.ts +308 -58
- package/src/index.ts +1 -0
- package/src/packageVersion.ts +1 -1
package/dist/directory.js
CHANGED
|
@@ -115,7 +115,7 @@ class SharedDirectory extends shared_object_base_1.SharedObject {
|
|
|
115
115
|
/**
|
|
116
116
|
* Root of the SharedDirectory, most operations on the SharedDirectory itself act on the root.
|
|
117
117
|
*/
|
|
118
|
-
this.root = new SubDirectory(this, this.runtime, this.serializer, posix.sep);
|
|
118
|
+
this.root = new SubDirectory(0, new Set(), this, this.runtime, this.serializer, posix.sep);
|
|
119
119
|
/**
|
|
120
120
|
* Mapping of op types to message handlers.
|
|
121
121
|
*/
|
|
@@ -371,7 +371,10 @@ class SharedDirectory extends shared_object_base_1.SharedObject {
|
|
|
371
371
|
for (const [subdirName, subdirObject] of Object.entries(currentSubDirObject.subdirectories)) {
|
|
372
372
|
let newSubDir = currentSubDir.getSubDirectory(subdirName);
|
|
373
373
|
if (!newSubDir) {
|
|
374
|
-
|
|
374
|
+
const createInfo = subdirObject.ci;
|
|
375
|
+
newSubDir = new SubDirectory(createInfo !== undefined ? createInfo.csn : 0, createInfo !== undefined
|
|
376
|
+
? new Set(createInfo.ccIds)
|
|
377
|
+
: new Set(), this, this.runtime, this.serializer, posix.join(currentSubDir.absolutePath, subdirName));
|
|
375
378
|
currentSubDir.populateSubDirectory(subdirName, newSubDir);
|
|
376
379
|
}
|
|
377
380
|
stack.push([newSubDir, subdirObject]);
|
|
@@ -394,7 +397,7 @@ class SharedDirectory extends shared_object_base_1.SharedObject {
|
|
|
394
397
|
const op = message.contents;
|
|
395
398
|
const handler = this.messageHandlers.get(op.type);
|
|
396
399
|
(0, common_utils_1.assert)(handler !== undefined, 0x00e /* Missing message handler for message type */);
|
|
397
|
-
handler.process(op, local, localOpMetadata);
|
|
400
|
+
handler.process(message, op, local, localOpMetadata);
|
|
398
401
|
}
|
|
399
402
|
}
|
|
400
403
|
/**
|
|
@@ -430,15 +433,41 @@ class SharedDirectory extends shared_object_base_1.SharedObject {
|
|
|
430
433
|
serializable.type === shared_object_base_1.ValueType[shared_object_base_1.ValueType.Shared], 0x1e4 /* "Unexpected serializable type" */);
|
|
431
434
|
return this.localValueMaker.fromSerializable(serializable);
|
|
432
435
|
}
|
|
436
|
+
/**
|
|
437
|
+
* This checks if there is pending delete op for local delete for a subdirectory.
|
|
438
|
+
* @param relativePath - path of sub directory.
|
|
439
|
+
* @returns - true if there is pending delete.
|
|
440
|
+
*/
|
|
441
|
+
isSubDirectoryDeletePending(relativePath) {
|
|
442
|
+
const parentSubDir = this.getParentDirectory(relativePath);
|
|
443
|
+
const index = relativePath.lastIndexOf(posix.sep);
|
|
444
|
+
const dirName = relativePath.substring(index + 1);
|
|
445
|
+
return !!(parentSubDir === null || parentSubDir === void 0 ? void 0 : parentSubDir.isSubDirectoryDeletePending(dirName));
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* Gets the parent directory of a sub directory.
|
|
449
|
+
* @param relativePath - path of sub directory of which parent needs to be find out.
|
|
450
|
+
*/
|
|
451
|
+
getParentDirectory(relativePath) {
|
|
452
|
+
const absolutePath = this.makeAbsolute(relativePath);
|
|
453
|
+
if (absolutePath === posix.sep) {
|
|
454
|
+
return undefined;
|
|
455
|
+
}
|
|
456
|
+
const index = absolutePath.lastIndexOf(posix.sep);
|
|
457
|
+
const parentAbsPath = absolutePath.substring(0, index);
|
|
458
|
+
return this.getWorkingDirectory(parentAbsPath);
|
|
459
|
+
}
|
|
433
460
|
/**
|
|
434
461
|
* Set the message handlers for the directory.
|
|
435
462
|
*/
|
|
436
463
|
setMessageHandlers() {
|
|
437
464
|
this.messageHandlers.set("clear", {
|
|
438
|
-
process: (op, local, localOpMetadata) => {
|
|
465
|
+
process: (msg, op, local, localOpMetadata) => {
|
|
439
466
|
const subdir = this.getWorkingDirectory(op.path);
|
|
440
|
-
|
|
441
|
-
|
|
467
|
+
// If there is pending delete op for this subDirectory, then don't apply the this op as we are going
|
|
468
|
+
// to delete this subDirectory.
|
|
469
|
+
if (subdir && !this.isSubDirectoryDeletePending(op.path)) {
|
|
470
|
+
subdir.processClearMessage(msg, op, local, localOpMetadata);
|
|
442
471
|
}
|
|
443
472
|
},
|
|
444
473
|
submit: (op, localOpMetadata) => {
|
|
@@ -455,10 +484,12 @@ class SharedDirectory extends shared_object_base_1.SharedObject {
|
|
|
455
484
|
},
|
|
456
485
|
});
|
|
457
486
|
this.messageHandlers.set("delete", {
|
|
458
|
-
process: (op, local, localOpMetadata) => {
|
|
487
|
+
process: (msg, op, local, localOpMetadata) => {
|
|
459
488
|
const subdir = this.getWorkingDirectory(op.path);
|
|
460
|
-
|
|
461
|
-
|
|
489
|
+
// If there is pending delete op for this subDirectory, then don't apply the this op as we are going
|
|
490
|
+
// to delete this subDirectory.
|
|
491
|
+
if (subdir && !this.isSubDirectoryDeletePending(op.path)) {
|
|
492
|
+
subdir.processDeleteMessage(msg, op, local, localOpMetadata);
|
|
462
493
|
}
|
|
463
494
|
},
|
|
464
495
|
submit: (op, localOpMetadata) => {
|
|
@@ -475,11 +506,13 @@ class SharedDirectory extends shared_object_base_1.SharedObject {
|
|
|
475
506
|
},
|
|
476
507
|
});
|
|
477
508
|
this.messageHandlers.set("set", {
|
|
478
|
-
process: (op, local, localOpMetadata) => {
|
|
509
|
+
process: (msg, op, local, localOpMetadata) => {
|
|
479
510
|
const subdir = this.getWorkingDirectory(op.path);
|
|
480
|
-
|
|
511
|
+
// If there is pending delete op for this subDirectory, then don't apply the this op as we are going
|
|
512
|
+
// to delete this subDirectory.
|
|
513
|
+
if (subdir && !this.isSubDirectoryDeletePending(op.path)) {
|
|
481
514
|
const context = local ? undefined : this.makeLocal(op.key, op.path, op.value);
|
|
482
|
-
subdir.processSetMessage(op, context, local, localOpMetadata);
|
|
515
|
+
subdir.processSetMessage(msg, op, context, local, localOpMetadata);
|
|
483
516
|
}
|
|
484
517
|
},
|
|
485
518
|
submit: (op, localOpMetadata) => {
|
|
@@ -497,10 +530,10 @@ class SharedDirectory extends shared_object_base_1.SharedObject {
|
|
|
497
530
|
},
|
|
498
531
|
});
|
|
499
532
|
this.messageHandlers.set("createSubDirectory", {
|
|
500
|
-
process: (op, local, localOpMetadata) => {
|
|
533
|
+
process: (msg, op, local, localOpMetadata) => {
|
|
501
534
|
const parentSubdir = this.getWorkingDirectory(op.path);
|
|
502
535
|
if (parentSubdir) {
|
|
503
|
-
parentSubdir.processCreateSubDirectoryMessage(op, local, localOpMetadata);
|
|
536
|
+
parentSubdir.processCreateSubDirectoryMessage(msg, op, local, localOpMetadata);
|
|
504
537
|
}
|
|
505
538
|
},
|
|
506
539
|
submit: (op, localOpMetadata) => {
|
|
@@ -518,10 +551,10 @@ class SharedDirectory extends shared_object_base_1.SharedObject {
|
|
|
518
551
|
},
|
|
519
552
|
});
|
|
520
553
|
this.messageHandlers.set("deleteSubDirectory", {
|
|
521
|
-
process: (op, local, localOpMetadata) => {
|
|
554
|
+
process: (msg, op, local, localOpMetadata) => {
|
|
522
555
|
const parentSubdir = this.getWorkingDirectory(op.path);
|
|
523
556
|
if (parentSubdir) {
|
|
524
|
-
parentSubdir.processDeleteSubDirectoryMessage(op, local, localOpMetadata);
|
|
557
|
+
parentSubdir.processDeleteSubDirectoryMessage(msg, op, local, localOpMetadata);
|
|
525
558
|
}
|
|
526
559
|
},
|
|
527
560
|
submit: (op, localOpMetadata) => {
|
|
@@ -561,6 +594,7 @@ class SharedDirectory extends shared_object_base_1.SharedObject {
|
|
|
561
594
|
while (stack.length > 0) {
|
|
562
595
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
563
596
|
const [currentSubDir, currentSubDirObject] = stack.pop();
|
|
597
|
+
currentSubDirObject.ci = currentSubDir.getSerializableCreateInfo();
|
|
564
598
|
for (const [key, value] of currentSubDir.getSerializedStorage(serializer)) {
|
|
565
599
|
if (!currentSubDirObject.storage) {
|
|
566
600
|
currentSubDirObject.storage = {};
|
|
@@ -622,8 +656,7 @@ function isClearLocalOpMetadata(metadata) {
|
|
|
622
656
|
function isSubDirLocalOpMetadata(metadata) {
|
|
623
657
|
return (metadata !== undefined &&
|
|
624
658
|
typeof metadata.pendingMessageId === "number" &&
|
|
625
|
-
(
|
|
626
|
-
metadata.type === "deleteSubDir"));
|
|
659
|
+
(metadata.type === "createSubDir" || metadata.type === "deleteSubDir"));
|
|
627
660
|
}
|
|
628
661
|
function isDirectoryLocalOpMetadata(metadata) {
|
|
629
662
|
return (metadata !== undefined &&
|
|
@@ -631,7 +664,7 @@ function isDirectoryLocalOpMetadata(metadata) {
|
|
|
631
664
|
(metadata.type === "edit" ||
|
|
632
665
|
metadata.type === "deleteSubDir" ||
|
|
633
666
|
(metadata.type === "clear" && typeof metadata.previousStorage === "object") ||
|
|
634
|
-
|
|
667
|
+
metadata.type === "createSubDir"));
|
|
635
668
|
}
|
|
636
669
|
/* eslint-enable @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access */
|
|
637
670
|
/**
|
|
@@ -641,13 +674,17 @@ function isDirectoryLocalOpMetadata(metadata) {
|
|
|
641
674
|
class SubDirectory extends common_utils_1.TypedEventEmitter {
|
|
642
675
|
/**
|
|
643
676
|
* Constructor.
|
|
677
|
+
* @param sequenceNumber - Message seq number at which this was created.
|
|
678
|
+
* @param clientIds - Ids of client which created this directory.
|
|
644
679
|
* @param directory - Reference back to the SharedDirectory to perform operations
|
|
645
680
|
* @param runtime - The data store runtime this directory is associated with
|
|
646
681
|
* @param serializer - The serializer to serialize / parse handles
|
|
647
682
|
* @param absolutePath - The absolute path of this IDirectory
|
|
648
683
|
*/
|
|
649
|
-
constructor(directory, runtime, serializer, absolutePath) {
|
|
684
|
+
constructor(sequenceNumber, clientIds, directory, runtime, serializer, absolutePath) {
|
|
650
685
|
super();
|
|
686
|
+
this.sequenceNumber = sequenceNumber;
|
|
687
|
+
this.clientIds = clientIds;
|
|
651
688
|
this.directory = directory;
|
|
652
689
|
this.runtime = runtime;
|
|
653
690
|
this.serializer = serializer;
|
|
@@ -673,9 +710,14 @@ class SubDirectory extends common_utils_1.TypedEventEmitter {
|
|
|
673
710
|
*/
|
|
674
711
|
this.pendingKeys = new Map();
|
|
675
712
|
/**
|
|
676
|
-
* Subdirectories that have been
|
|
713
|
+
* Subdirectories that have been created/deleted locally but not yet ack'd from the server.
|
|
677
714
|
*/
|
|
678
715
|
this.pendingSubDirectories = new Map();
|
|
716
|
+
/**
|
|
717
|
+
* Subdirectories that have been deleted locally but not yet ack'd from the server. This maintains the count
|
|
718
|
+
* of delete op that are pending or yet to be acked from server.
|
|
719
|
+
*/
|
|
720
|
+
this.pendingDeleteSubDirectoriesCount = new Map();
|
|
679
721
|
/**
|
|
680
722
|
* This is used to assign a unique id to every outgoing operation and helps in tracking unack'd ops.
|
|
681
723
|
*/
|
|
@@ -758,6 +800,7 @@ class SubDirectory extends common_utils_1.TypedEventEmitter {
|
|
|
758
800
|
* {@inheritDoc IDirectory.createSubDirectory}
|
|
759
801
|
*/
|
|
760
802
|
createSubDirectory(subdirName) {
|
|
803
|
+
var _c;
|
|
761
804
|
this.throwIfDisposed();
|
|
762
805
|
// Undefined/null subdirectory names can't be serialized to JSON in the manner we currently snapshot.
|
|
763
806
|
if (subdirName === undefined || subdirName === null) {
|
|
@@ -767,19 +810,22 @@ class SubDirectory extends common_utils_1.TypedEventEmitter {
|
|
|
767
810
|
throw new Error(`SubDirectory name may not contain ${posix.sep}`);
|
|
768
811
|
}
|
|
769
812
|
// Create the sub directory locally first.
|
|
770
|
-
const isNew = this.createSubDirectoryCore(subdirName, true);
|
|
771
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
813
|
+
const isNew = this.createSubDirectoryCore(subdirName, true, -1, (_c = this.runtime.clientId) !== null && _c !== void 0 ? _c : "detached");
|
|
772
814
|
const subDir = this._subdirectories.get(subdirName);
|
|
815
|
+
(0, common_utils_1.assert)(subDir !== undefined, 0x5aa /* subdirectory should exist after creation */);
|
|
773
816
|
// If we are not attached, don't submit the op.
|
|
774
817
|
if (!this.directory.isAttached()) {
|
|
775
818
|
return subDir;
|
|
776
819
|
}
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
820
|
+
// Only submit the op, if it is newly created.
|
|
821
|
+
if (isNew) {
|
|
822
|
+
const op = {
|
|
823
|
+
path: this.absolutePath,
|
|
824
|
+
subdirName,
|
|
825
|
+
type: "createSubDirectory",
|
|
826
|
+
};
|
|
827
|
+
this.submitCreateSubDirectoryMessage(op);
|
|
828
|
+
}
|
|
783
829
|
return subDir;
|
|
784
830
|
}
|
|
785
831
|
/**
|
|
@@ -807,12 +853,15 @@ class SubDirectory extends common_utils_1.TypedEventEmitter {
|
|
|
807
853
|
if (!this.directory.isAttached()) {
|
|
808
854
|
return subDir !== undefined;
|
|
809
855
|
}
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
856
|
+
// Only submit the op, if the directory existed and we deleted it.
|
|
857
|
+
if (subDir !== undefined) {
|
|
858
|
+
const op = {
|
|
859
|
+
path: this.absolutePath,
|
|
860
|
+
subdirName,
|
|
861
|
+
type: "deleteSubDirectory",
|
|
862
|
+
};
|
|
863
|
+
this.submitDeleteSubDirectoryMessage(op, subDir);
|
|
864
|
+
}
|
|
816
865
|
return subDir !== undefined;
|
|
817
866
|
}
|
|
818
867
|
/**
|
|
@@ -829,6 +878,18 @@ class SubDirectory extends common_utils_1.TypedEventEmitter {
|
|
|
829
878
|
this.throwIfDisposed();
|
|
830
879
|
return this.directory.getWorkingDirectory(this.makeAbsolute(relativePath));
|
|
831
880
|
}
|
|
881
|
+
/**
|
|
882
|
+
* This checks if there is pending delete op for local delete for a given child subdirectory.
|
|
883
|
+
* @param subDirName - directory name.
|
|
884
|
+
* @returns - true if there is pending delete.
|
|
885
|
+
*/
|
|
886
|
+
isSubDirectoryDeletePending(subDirName) {
|
|
887
|
+
const pendingDeleteSubDirectory = this.pendingDeleteSubDirectoriesCount.get(subDirName);
|
|
888
|
+
if (pendingDeleteSubDirectory !== undefined && pendingDeleteSubDirectory > 0) {
|
|
889
|
+
return true;
|
|
890
|
+
}
|
|
891
|
+
return false;
|
|
892
|
+
}
|
|
832
893
|
/**
|
|
833
894
|
* Deletes the given key from within this IDirectory.
|
|
834
895
|
* @param key - The key to delete
|
|
@@ -944,14 +1005,18 @@ class SubDirectory extends common_utils_1.TypedEventEmitter {
|
|
|
944
1005
|
}
|
|
945
1006
|
/**
|
|
946
1007
|
* Process a clear operation.
|
|
1008
|
+
* @param msg - The message from the server to apply.
|
|
947
1009
|
* @param op - The op to process
|
|
948
1010
|
* @param local - Whether the message originated from the local client
|
|
949
1011
|
* @param localOpMetadata - For local client messages, this is the metadata that was submitted with the message.
|
|
950
1012
|
* For messages from a remote client, this will be undefined.
|
|
951
1013
|
* @internal
|
|
952
1014
|
*/
|
|
953
|
-
processClearMessage(op, local, localOpMetadata) {
|
|
1015
|
+
processClearMessage(msg, op, local, localOpMetadata) {
|
|
954
1016
|
this.throwIfDisposed();
|
|
1017
|
+
if (!this.isMessageForCurrentInstanceOfSubDirectory(msg)) {
|
|
1018
|
+
return;
|
|
1019
|
+
}
|
|
955
1020
|
if (local) {
|
|
956
1021
|
(0, common_utils_1.assert)(isClearLocalOpMetadata(localOpMetadata), 0x00f /* pendingMessageId is missing from the local client's operation */);
|
|
957
1022
|
const pendingClearMessageId = this.pendingClearMessageIds.shift();
|
|
@@ -980,15 +1045,17 @@ class SubDirectory extends common_utils_1.TypedEventEmitter {
|
|
|
980
1045
|
}
|
|
981
1046
|
/**
|
|
982
1047
|
* Process a delete operation.
|
|
1048
|
+
* @param msg - The message from the server to apply.
|
|
983
1049
|
* @param op - The op to process
|
|
984
1050
|
* @param local - Whether the message originated from the local client
|
|
985
1051
|
* @param localOpMetadata - For local client messages, this is the metadata that was submitted with the message.
|
|
986
1052
|
* For messages from a remote client, this will be undefined.
|
|
987
1053
|
* @internal
|
|
988
1054
|
*/
|
|
989
|
-
processDeleteMessage(op, local, localOpMetadata) {
|
|
1055
|
+
processDeleteMessage(msg, op, local, localOpMetadata) {
|
|
990
1056
|
this.throwIfDisposed();
|
|
991
|
-
if (!this.
|
|
1057
|
+
if (!(this.isMessageForCurrentInstanceOfSubDirectory(msg) &&
|
|
1058
|
+
this.needProcessStorageOperation(op, local, localOpMetadata))) {
|
|
992
1059
|
return;
|
|
993
1060
|
}
|
|
994
1061
|
this.deleteCore(op.key, local);
|
|
@@ -1011,15 +1078,17 @@ class SubDirectory extends common_utils_1.TypedEventEmitter {
|
|
|
1011
1078
|
}
|
|
1012
1079
|
/**
|
|
1013
1080
|
* Process a set operation.
|
|
1081
|
+
* @param msg - The message from the server to apply.
|
|
1014
1082
|
* @param op - The op to process
|
|
1015
1083
|
* @param local - Whether the message originated from the local client
|
|
1016
1084
|
* @param localOpMetadata - For local client messages, this is the metadata that was submitted with the message.
|
|
1017
1085
|
* For messages from a remote client, this will be undefined.
|
|
1018
1086
|
* @internal
|
|
1019
1087
|
*/
|
|
1020
|
-
processSetMessage(op, context, local, localOpMetadata) {
|
|
1088
|
+
processSetMessage(msg, op, context, local, localOpMetadata) {
|
|
1021
1089
|
this.throwIfDisposed();
|
|
1022
|
-
if (!this.
|
|
1090
|
+
if (!(this.isMessageForCurrentInstanceOfSubDirectory(msg) &&
|
|
1091
|
+
this.needProcessStorageOperation(op, local, localOpMetadata))) {
|
|
1023
1092
|
return;
|
|
1024
1093
|
}
|
|
1025
1094
|
// needProcessStorageOperation should have returned false if local is true
|
|
@@ -1047,18 +1116,19 @@ class SubDirectory extends common_utils_1.TypedEventEmitter {
|
|
|
1047
1116
|
}
|
|
1048
1117
|
/**
|
|
1049
1118
|
* Process a create subdirectory operation.
|
|
1119
|
+
* @param msg - The message from the server to apply.
|
|
1050
1120
|
* @param op - The op to process
|
|
1051
1121
|
* @param local - Whether the message originated from the local client
|
|
1052
1122
|
* @param localOpMetadata - For local client messages, this is the metadata that was submitted with the message.
|
|
1053
1123
|
* For messages from a remote client, this will be undefined.
|
|
1054
1124
|
* @internal
|
|
1055
1125
|
*/
|
|
1056
|
-
processCreateSubDirectoryMessage(op, local, localOpMetadata) {
|
|
1126
|
+
processCreateSubDirectoryMessage(msg, op, local, localOpMetadata) {
|
|
1057
1127
|
this.throwIfDisposed();
|
|
1058
|
-
if (!this.needProcessSubDirectoryOperation(op, local, localOpMetadata)) {
|
|
1128
|
+
if (!this.needProcessSubDirectoryOperation(msg, op, local, localOpMetadata)) {
|
|
1059
1129
|
return;
|
|
1060
1130
|
}
|
|
1061
|
-
this.createSubDirectoryCore(op.subdirName, local);
|
|
1131
|
+
this.createSubDirectoryCore(op.subdirName, local, msg.sequenceNumber, msg.clientId);
|
|
1062
1132
|
}
|
|
1063
1133
|
/**
|
|
1064
1134
|
* Apply createSubDirectory operation locally and generate metadata
|
|
@@ -1066,28 +1136,30 @@ class SubDirectory extends common_utils_1.TypedEventEmitter {
|
|
|
1066
1136
|
* @returns metadata generated for stahed op
|
|
1067
1137
|
*/
|
|
1068
1138
|
applyStashedCreateSubDirMessage(op) {
|
|
1139
|
+
var _c;
|
|
1069
1140
|
this.throwIfDisposed();
|
|
1070
1141
|
// Create the sub directory locally first.
|
|
1071
|
-
|
|
1142
|
+
this.createSubDirectoryCore(op.subdirName, true, -1, (_c = this.runtime.clientId) !== null && _c !== void 0 ? _c : "detached");
|
|
1072
1143
|
const newMessageId = this.getSubDirMessageId(op);
|
|
1073
1144
|
const localOpMetadata = {
|
|
1074
1145
|
type: "createSubDir",
|
|
1075
1146
|
pendingMessageId: newMessageId,
|
|
1076
|
-
previouslyExisted: !isNew,
|
|
1077
1147
|
};
|
|
1078
1148
|
return localOpMetadata;
|
|
1079
1149
|
}
|
|
1080
1150
|
/**
|
|
1081
1151
|
* Process a delete subdirectory operation.
|
|
1152
|
+
* @param msg - The message from the server to apply.
|
|
1082
1153
|
* @param op - The op to process
|
|
1083
1154
|
* @param local - Whether the message originated from the local client
|
|
1084
1155
|
* @param localOpMetadata - For local client messages, this is the metadata that was submitted with the message.
|
|
1085
1156
|
* For messages from a remote client, this will be undefined.
|
|
1086
1157
|
* @internal
|
|
1087
1158
|
*/
|
|
1088
|
-
processDeleteSubDirectoryMessage(op, local, localOpMetadata) {
|
|
1159
|
+
processDeleteSubDirectoryMessage(msg, op, local, localOpMetadata) {
|
|
1089
1160
|
this.throwIfDisposed();
|
|
1090
|
-
if (!this.
|
|
1161
|
+
if (!(this.isMessageForCurrentInstanceOfSubDirectory(msg) &&
|
|
1162
|
+
this.needProcessSubDirectoryOperation(msg, op, local, localOpMetadata))) {
|
|
1091
1163
|
return;
|
|
1092
1164
|
}
|
|
1093
1165
|
this.deleteSubDirectoryCore(op.subdirName, local);
|
|
@@ -1183,6 +1255,7 @@ class SubDirectory extends common_utils_1.TypedEventEmitter {
|
|
|
1183
1255
|
* Get a new pending message id for the op and cache it to track the pending op
|
|
1184
1256
|
*/
|
|
1185
1257
|
getSubDirMessageId(op) {
|
|
1258
|
+
var _c;
|
|
1186
1259
|
// We don't reuse the metadata pendingMessageId but send a new one on each submit.
|
|
1187
1260
|
const newMessageId = ++this.pendingMessageId;
|
|
1188
1261
|
const pendingMessageIds = this.pendingSubDirectories.get(op.subdirName);
|
|
@@ -1192,20 +1265,22 @@ class SubDirectory extends common_utils_1.TypedEventEmitter {
|
|
|
1192
1265
|
else {
|
|
1193
1266
|
this.pendingSubDirectories.set(op.subdirName, [newMessageId]);
|
|
1194
1267
|
}
|
|
1268
|
+
if (op.type === "deleteSubDirectory") {
|
|
1269
|
+
const count = (_c = this.pendingDeleteSubDirectoriesCount.get(op.subdirName)) !== null && _c !== void 0 ? _c : 0;
|
|
1270
|
+
this.pendingDeleteSubDirectoriesCount.set(op.subdirName, count + 1);
|
|
1271
|
+
}
|
|
1195
1272
|
return newMessageId;
|
|
1196
1273
|
}
|
|
1197
1274
|
/**
|
|
1198
1275
|
* Submit a create subdirectory operation.
|
|
1199
1276
|
* @param op - The operation
|
|
1200
|
-
* @param prevExisted - Whether the subdirectory existed before the op
|
|
1201
1277
|
*/
|
|
1202
|
-
submitCreateSubDirectoryMessage(op
|
|
1278
|
+
submitCreateSubDirectoryMessage(op) {
|
|
1203
1279
|
this.throwIfDisposed();
|
|
1204
1280
|
const newMessageId = this.getSubDirMessageId(op);
|
|
1205
1281
|
const localOpMetadata = {
|
|
1206
1282
|
type: "createSubDir",
|
|
1207
1283
|
pendingMessageId: newMessageId,
|
|
1208
|
-
previouslyExisted: prevExisted,
|
|
1209
1284
|
};
|
|
1210
1285
|
this.directory.submitDirectoryMessage(op, localOpMetadata);
|
|
1211
1286
|
}
|
|
@@ -1241,7 +1316,7 @@ class SubDirectory extends common_utils_1.TypedEventEmitter {
|
|
|
1241
1316
|
this.pendingSubDirectories.delete(op.subdirName);
|
|
1242
1317
|
}
|
|
1243
1318
|
if (localOpMetadata.type === "createSubDir") {
|
|
1244
|
-
this.submitCreateSubDirectoryMessage(op
|
|
1319
|
+
this.submitCreateSubDirectoryMessage(op);
|
|
1245
1320
|
}
|
|
1246
1321
|
else {
|
|
1247
1322
|
this.submitDeleteSubDirectoryMessage(op, localOpMetadata.subDirectory);
|
|
@@ -1261,6 +1336,14 @@ class SubDirectory extends common_utils_1.TypedEventEmitter {
|
|
|
1261
1336
|
yield res;
|
|
1262
1337
|
}
|
|
1263
1338
|
}
|
|
1339
|
+
getSerializableCreateInfo() {
|
|
1340
|
+
this.throwIfDisposed();
|
|
1341
|
+
const createInfo = {
|
|
1342
|
+
csn: this.sequenceNumber,
|
|
1343
|
+
ccIds: Array.from(this.clientIds),
|
|
1344
|
+
};
|
|
1345
|
+
return createInfo;
|
|
1346
|
+
}
|
|
1264
1347
|
/**
|
|
1265
1348
|
* Populate a key value in this subdirectory's storage, to be used when loading from snapshot.
|
|
1266
1349
|
* @param key - The key to populate
|
|
@@ -1338,9 +1421,7 @@ class SubDirectory extends common_utils_1.TypedEventEmitter {
|
|
|
1338
1421
|
this.rollbackPendingMessageId(this.pendingKeys, op.key, localOpMetadata.pendingMessageId);
|
|
1339
1422
|
}
|
|
1340
1423
|
else if (op.type === "createSubDirectory" && localOpMetadata.type === "createSubDir") {
|
|
1341
|
-
|
|
1342
|
-
this.deleteSubDirectoryCore(op.subdirName, true);
|
|
1343
|
-
}
|
|
1424
|
+
this.deleteSubDirectoryCore(op.subdirName, true);
|
|
1344
1425
|
this.rollbackPendingMessageId(this.pendingSubDirectories, op.subdirName, localOpMetadata.pendingMessageId);
|
|
1345
1426
|
}
|
|
1346
1427
|
else if (op.type === "deleteSubDirectory" && localOpMetadata.type === "deleteSubDir") {
|
|
@@ -1351,6 +1432,12 @@ class SubDirectory extends common_utils_1.TypedEventEmitter {
|
|
|
1351
1432
|
this.emit("subDirectoryCreated", op.subdirName, true, this);
|
|
1352
1433
|
}
|
|
1353
1434
|
this.rollbackPendingMessageId(this.pendingSubDirectories, op.subdirName, localOpMetadata.pendingMessageId);
|
|
1435
|
+
const count = this.pendingDeleteSubDirectoriesCount.get(op.subdirName);
|
|
1436
|
+
(0, common_utils_1.assert)(count !== undefined && count > 0, 0x5ab /* should have record for delete op */);
|
|
1437
|
+
this.pendingDeleteSubDirectoriesCount.set(op.subdirName, count - 1);
|
|
1438
|
+
if (count === 1) {
|
|
1439
|
+
this.pendingDeleteSubDirectoriesCount.delete(op.subdirName);
|
|
1440
|
+
}
|
|
1354
1441
|
}
|
|
1355
1442
|
else {
|
|
1356
1443
|
throw new Error("Unsupported op for rollback");
|
|
@@ -1380,6 +1467,22 @@ class SubDirectory extends common_utils_1.TypedEventEmitter {
|
|
|
1380
1467
|
(0, common_utils_1.assert)(localOpMetadata !== undefined &&
|
|
1381
1468
|
isKeyEditLocalOpMetadata(localOpMetadata) &&
|
|
1382
1469
|
localOpMetadata.pendingMessageId < this.pendingClearMessageIds[0], 0x010 /* "Received out of order storage op when there is an unackd clear message" */);
|
|
1470
|
+
// Remove all pendingMessageIds lower than first pendingClearMessageId.
|
|
1471
|
+
const lowestPendingClearMessageId = this.pendingClearMessageIds[0];
|
|
1472
|
+
const pendingKeyMessageIdArray = this.pendingKeys.get(op.key);
|
|
1473
|
+
if (pendingKeyMessageIdArray !== undefined) {
|
|
1474
|
+
let index = 0;
|
|
1475
|
+
while (pendingKeyMessageIdArray[index] < lowestPendingClearMessageId) {
|
|
1476
|
+
index += 1;
|
|
1477
|
+
}
|
|
1478
|
+
const newPendingKeyMessageId = pendingKeyMessageIdArray.splice(index);
|
|
1479
|
+
if (newPendingKeyMessageId.length === 0) {
|
|
1480
|
+
this.pendingKeys.delete(op.key);
|
|
1481
|
+
}
|
|
1482
|
+
else {
|
|
1483
|
+
this.pendingKeys.set(op.key, newPendingKeyMessageId);
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1383
1486
|
}
|
|
1384
1487
|
// If I have a NACK clear, we can ignore all ops.
|
|
1385
1488
|
return false;
|
|
@@ -1403,6 +1506,19 @@ class SubDirectory extends common_utils_1.TypedEventEmitter {
|
|
|
1403
1506
|
// If we don't have a NACK op on the key, we need to process the remote ops.
|
|
1404
1507
|
return !local;
|
|
1405
1508
|
}
|
|
1509
|
+
/**
|
|
1510
|
+
* This return true if the message is for the current instance of this sub directory. As the sub directory
|
|
1511
|
+
* can be deleted and created again, then this finds if the message is for current instance of directory or not.
|
|
1512
|
+
* @param msg - message for the directory
|
|
1513
|
+
*/
|
|
1514
|
+
isMessageForCurrentInstanceOfSubDirectory(msg) {
|
|
1515
|
+
// If the message is either from the creator of directory or this directory was created when
|
|
1516
|
+
// container was detached or in case this directory is already live(known to other clients)
|
|
1517
|
+
// and the op was created after the directory was created then apply this op.
|
|
1518
|
+
return (this.clientIds.has(msg.clientId) ||
|
|
1519
|
+
this.clientIds.has("detached") ||
|
|
1520
|
+
(this.sequenceNumber !== -1 && this.sequenceNumber <= msg.referenceSequenceNumber));
|
|
1521
|
+
}
|
|
1406
1522
|
/**
|
|
1407
1523
|
* If our local operations that have not yet been ack'd will eventually overwrite an incoming operation, we should
|
|
1408
1524
|
* not process the incoming operation.
|
|
@@ -1413,7 +1529,7 @@ class SubDirectory extends common_utils_1.TypedEventEmitter {
|
|
|
1413
1529
|
* For messages from a remote client, this will be undefined.
|
|
1414
1530
|
* @returns True if the operation should be processed, false otherwise
|
|
1415
1531
|
*/
|
|
1416
|
-
needProcessSubDirectoryOperation(op, local, localOpMetadata) {
|
|
1532
|
+
needProcessSubDirectoryOperation(msg, op, local, localOpMetadata) {
|
|
1417
1533
|
const pendingSubDirectoryMessageId = this.pendingSubDirectories.get(op.subdirName);
|
|
1418
1534
|
if (pendingSubDirectoryMessageId !== undefined) {
|
|
1419
1535
|
if (local) {
|
|
@@ -1425,6 +1541,40 @@ class SubDirectory extends common_utils_1.TypedEventEmitter {
|
|
|
1425
1541
|
if (pendingMessageIds.length === 0) {
|
|
1426
1542
|
this.pendingSubDirectories.delete(op.subdirName);
|
|
1427
1543
|
}
|
|
1544
|
+
if (op.type === "deleteSubDirectory") {
|
|
1545
|
+
const count = this.pendingDeleteSubDirectoriesCount.get(op.subdirName);
|
|
1546
|
+
(0, common_utils_1.assert)(count !== undefined && count > 0, 0x5ac /* should have record for delete op */);
|
|
1547
|
+
this.pendingDeleteSubDirectoriesCount.set(op.subdirName, count - 1);
|
|
1548
|
+
if (count === 1) {
|
|
1549
|
+
this.pendingDeleteSubDirectoriesCount.delete(op.subdirName);
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1553
|
+
else if (op.type === "deleteSubDirectory") {
|
|
1554
|
+
// If this is remote delete op and we have keys in this subDirectory, then we need to delete these
|
|
1555
|
+
// keys except the pending ones as they will be sequenced after this delete.
|
|
1556
|
+
const subDirectory = this._subdirectories.get(op.subdirName);
|
|
1557
|
+
if (subDirectory) {
|
|
1558
|
+
subDirectory.clearExceptPendingKeys(local);
|
|
1559
|
+
// In case of remote delete op, we need to reset the creation seq number and client ids of
|
|
1560
|
+
// creators as the previous directory is getting deleted and we will initialize again when
|
|
1561
|
+
// we will receive op for the create again.
|
|
1562
|
+
subDirectory.sequenceNumber = -1;
|
|
1563
|
+
subDirectory.clientIds.clear();
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
if (op.type === "createSubDirectory") {
|
|
1567
|
+
const dir = this._subdirectories.get(op.subdirName);
|
|
1568
|
+
if ((dir === null || dir === void 0 ? void 0 : dir.sequenceNumber) === -1) {
|
|
1569
|
+
// Only set the seq on the first message, could be more
|
|
1570
|
+
dir.sequenceNumber = msg.sequenceNumber;
|
|
1571
|
+
}
|
|
1572
|
+
// The client created the dir at or after the dirs seq, so list its client id as a creator.
|
|
1573
|
+
if (dir !== undefined &&
|
|
1574
|
+
!dir.clientIds.has(msg.clientId) &&
|
|
1575
|
+
dir.sequenceNumber <= msg.sequenceNumber) {
|
|
1576
|
+
dir.clientIds.add(msg.clientId);
|
|
1577
|
+
}
|
|
1428
1578
|
}
|
|
1429
1579
|
return false;
|
|
1430
1580
|
}
|
|
@@ -1438,8 +1588,11 @@ class SubDirectory extends common_utils_1.TypedEventEmitter {
|
|
|
1438
1588
|
// we will get the value for the pendingKeys and clear the map
|
|
1439
1589
|
const temp = new Map();
|
|
1440
1590
|
for (const [key] of this.pendingKeys) {
|
|
1441
|
-
|
|
1442
|
-
|
|
1591
|
+
const value = this._storage.get(key);
|
|
1592
|
+
// If this key is already deleted, then we don't need to add it again.
|
|
1593
|
+
if (value !== undefined) {
|
|
1594
|
+
temp.set(key, value);
|
|
1595
|
+
}
|
|
1443
1596
|
}
|
|
1444
1597
|
this.clearCore(local);
|
|
1445
1598
|
for (const [key, value] of temp.entries()) {
|
|
@@ -1493,17 +1646,23 @@ class SubDirectory extends common_utils_1.TypedEventEmitter {
|
|
|
1493
1646
|
* Create subdirectory implementation used for both locally sourced creation as well as incoming remote creation.
|
|
1494
1647
|
* @param subdirName - The name of the subdirectory being created
|
|
1495
1648
|
* @param local - Whether the message originated from the local client
|
|
1649
|
+
* @param seq - Sequence number at which this directory is created
|
|
1650
|
+
* @param clientId - Id of client which created this directory.
|
|
1496
1651
|
* @returns - True if is newly created, false if it already existed.
|
|
1497
1652
|
*/
|
|
1498
|
-
createSubDirectoryCore(subdirName, local) {
|
|
1499
|
-
|
|
1653
|
+
createSubDirectoryCore(subdirName, local, seq, clientId) {
|
|
1654
|
+
const subdir = this._subdirectories.get(subdirName);
|
|
1655
|
+
if (subdir === undefined) {
|
|
1500
1656
|
const absolutePath = posix.join(this.absolutePath, subdirName);
|
|
1501
|
-
const subDir = new SubDirectory(this.directory, this.runtime, this.serializer, absolutePath);
|
|
1657
|
+
const subDir = new SubDirectory(seq, new Set([clientId]), this.directory, this.runtime, this.serializer, absolutePath);
|
|
1502
1658
|
this.registerEventsOnSubDirectory(subDir, subdirName);
|
|
1503
1659
|
this._subdirectories.set(subdirName, subDir);
|
|
1504
1660
|
this.emit("subDirectoryCreated", subdirName, local, this);
|
|
1505
1661
|
return true;
|
|
1506
1662
|
}
|
|
1663
|
+
else {
|
|
1664
|
+
subdir.clientIds.add(clientId);
|
|
1665
|
+
}
|
|
1507
1666
|
return false;
|
|
1508
1667
|
}
|
|
1509
1668
|
registerEventsOnSubDirectory(subDirectory, subDirName) {
|