@fluidframework/map 2.117.0 → 3.0.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.
Files changed (65) hide show
  1. package/CHANGELOG.md +46 -2
  2. package/README.md +53 -50
  3. package/{api-extractor.json → api-extractor/api-extractor-model.json} +1 -1
  4. package/api-report/map.legacy.beta.api.md +9 -9
  5. package/api-report/map.legacy.public.api.md +4 -2
  6. package/dist/directory.js +171 -165
  7. package/dist/directory.js.map +1 -1
  8. package/dist/directoryFactory.d.ts +1 -1
  9. package/dist/directoryFactory.d.ts.map +1 -1
  10. package/dist/directoryFactory.js +15 -15
  11. package/dist/directoryFactory.js.map +1 -1
  12. package/dist/index.d.ts +1 -1
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js.map +1 -1
  15. package/dist/interfaces.d.ts +26 -15
  16. package/dist/interfaces.d.ts.map +1 -1
  17. package/dist/interfaces.js.map +1 -1
  18. package/dist/legacy.d.ts +0 -1
  19. package/dist/localValues.d.ts.map +1 -1
  20. package/dist/map.js +9 -6
  21. package/dist/map.js.map +1 -1
  22. package/dist/mapFactory.d.ts +1 -1
  23. package/dist/mapFactory.d.ts.map +1 -1
  24. package/dist/mapFactory.js +15 -15
  25. package/dist/mapFactory.js.map +1 -1
  26. package/dist/mapKernel.js +109 -104
  27. package/dist/mapKernel.js.map +1 -1
  28. package/dist/packageVersion.d.ts +1 -1
  29. package/dist/packageVersion.d.ts.map +1 -1
  30. package/dist/packageVersion.js +1 -1
  31. package/dist/packageVersion.js.map +1 -1
  32. package/dist/utils.d.ts.map +1 -1
  33. package/lib/directory.js +171 -165
  34. package/lib/directory.js.map +1 -1
  35. package/lib/directoryFactory.js +15 -15
  36. package/lib/directoryFactory.js.map +1 -1
  37. package/lib/index.d.ts +1 -1
  38. package/lib/index.d.ts.map +1 -1
  39. package/lib/index.js.map +1 -1
  40. package/lib/interfaces.d.ts +26 -15
  41. package/lib/interfaces.d.ts.map +1 -1
  42. package/lib/interfaces.js.map +1 -1
  43. package/lib/legacy.d.ts +1 -2
  44. package/lib/localValues.d.ts.map +1 -1
  45. package/lib/map.js +9 -6
  46. package/lib/map.js.map +1 -1
  47. package/lib/mapFactory.js +15 -15
  48. package/lib/mapFactory.js.map +1 -1
  49. package/lib/mapKernel.js +109 -104
  50. package/lib/mapKernel.js.map +1 -1
  51. package/lib/packageVersion.d.ts +1 -1
  52. package/lib/packageVersion.d.ts.map +1 -1
  53. package/lib/packageVersion.js +1 -1
  54. package/lib/packageVersion.js.map +1 -1
  55. package/lib/public.d.ts +1 -1
  56. package/lib/utils.d.ts.map +1 -1
  57. package/package.json +44 -60
  58. package/src/index.ts +0 -1
  59. package/src/interfaces.ts +31 -17
  60. package/src/packageVersion.ts +1 -1
  61. package/tsconfig.json +1 -1
  62. package/internal.d.ts +0 -11
  63. package/legacy.d.ts +0 -11
  64. /package/api-extractor/{api-extractor.current.json → api-extractor-report.current.json} +0 -0
  65. /package/api-extractor/{api-extractor.legacy.json → api-extractor-report.legacy.json} +0 -0
package/dist/directory.js CHANGED
@@ -6,7 +6,6 @@
6
6
  var __importDefault = (this && this.__importDefault) || function (mod) {
7
7
  return (mod && mod.__esModule) ? mod : { "default": mod };
8
8
  };
9
- var _a, _b;
10
9
  Object.defineProperty(exports, "__esModule", { value: true });
11
10
  exports.SharedDirectory = void 0;
12
11
  // TODO: Fix prefer-nullish-coalescing and prefer-optional-chain lint violations
@@ -82,12 +81,24 @@ function isAcknowledgedOrDetached(seqData) {
82
81
  * @sealed
83
82
  */
84
83
  class SharedDirectory extends internal_5.SharedObject {
84
+ /**
85
+ * String representation for the class.
86
+ */
87
+ [Symbol.toStringTag] = "SharedDirectory";
85
88
  /**
86
89
  * {@inheritDoc IDirectory.absolutePath}
87
90
  */
88
91
  get absolutePath() {
89
92
  return this.root.absolutePath;
90
93
  }
94
+ /**
95
+ * Root of the SharedDirectory, most operations on the SharedDirectory itself act on the root.
96
+ */
97
+ root = new SubDirectory({ seq: 0, clientSeq: 0 }, new Set(), this, this.runtime, this.serializer, posix.sep, (0, internal_6.extractTelemetryLoggerExt)(this.logger));
98
+ /**
99
+ * Mapping of op types to message handlers.
100
+ */
101
+ messageHandlers = new Map();
91
102
  /**
92
103
  * Constructs a new shared directory. If the object is non-local an id and service interfaces will
93
104
  * be provided.
@@ -97,18 +108,6 @@ class SharedDirectory extends internal_5.SharedObject {
97
108
  */
98
109
  constructor(id, runtime, attributes) {
99
110
  super(id, runtime, attributes, "fluid_directory_");
100
- /**
101
- * String representation for the class.
102
- */
103
- this[_a] = "SharedDirectory";
104
- /**
105
- * Root of the SharedDirectory, most operations on the SharedDirectory itself act on the root.
106
- */
107
- this.root = new SubDirectory({ seq: 0, clientSeq: 0 }, new Set(), this, this.runtime, this.serializer, posix.sep, (0, internal_6.extractTelemetryLoggerExt)(this.logger));
108
- /**
109
- * Mapping of op types to message handlers.
110
- */
111
- this.messageHandlers = new Map();
112
111
  this.setMessageHandlers();
113
112
  // Mirror the containedValueChanged op on the SharedDirectory
114
113
  this.root.on("containedValueChanged", (changed, local) => {
@@ -186,7 +185,7 @@ class SharedDirectory extends internal_5.SharedObject {
186
185
  */
187
186
  // TODO: Use `unknown` instead (breaking change).
188
187
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
189
- [(_a = Symbol.toStringTag, Symbol.iterator)]() {
188
+ [Symbol.iterator]() {
190
189
  return this.root[Symbol.iterator]();
191
190
  }
192
191
  /**
@@ -610,6 +609,31 @@ function assertNonNullClientId(clientId) {
610
609
  * @sealed
611
610
  */
612
611
  class SubDirectory extends client_utils_1.TypedEventEmitter {
612
+ seqData;
613
+ clientIds;
614
+ directory;
615
+ runtime;
616
+ serializer;
617
+ absolutePath;
618
+ /**
619
+ * Tells if the sub directory is deleted or not.
620
+ */
621
+ _deleted = false;
622
+ /**
623
+ * String representation for the class.
624
+ */
625
+ [Symbol.toStringTag] = "SubDirectory";
626
+ /**
627
+ * The sequenced subdirectories the directory is holding independent of any pending
628
+ * create/delete subdirectory operations.
629
+ */
630
+ _sequencedSubdirectories = new Map();
631
+ /**
632
+ * Assigns a unique ID to each subdirectory created locally but pending for acknowledgement, facilitating the tracking
633
+ * of the creation order.
634
+ */
635
+ localCreationSeq = 0;
636
+ mc;
613
637
  /**
614
638
  * Constructor.
615
639
  * @param sequenceNumber - Message seq number at which this was created.
@@ -627,156 +651,6 @@ class SubDirectory extends client_utils_1.TypedEventEmitter {
627
651
  this.runtime = runtime;
628
652
  this.serializer = serializer;
629
653
  this.absolutePath = absolutePath;
630
- /**
631
- * Tells if the sub directory is deleted or not.
632
- */
633
- this._deleted = false;
634
- /**
635
- * String representation for the class.
636
- */
637
- this[_b] = "SubDirectory";
638
- /**
639
- * The sequenced subdirectories the directory is holding independent of any pending
640
- * create/delete subdirectory operations.
641
- */
642
- this._sequencedSubdirectories = new Map();
643
- /**
644
- * Assigns a unique ID to each subdirectory created locally but pending for acknowledgement, facilitating the tracking
645
- * of the creation order.
646
- */
647
- this.localCreationSeq = 0;
648
- /**
649
- * The data this SubDirectory instance is storing, but only including sequenced values (no local pending
650
- * modifications are included).
651
- */
652
- this.sequencedStorageData = new Map();
653
- /**
654
- * A data structure containing all local pending storage modifications, which is used in combination
655
- * with the sequencedStorageData to compute optimistic values.
656
- *
657
- * Pending sets are aggregated into "lifetimes", which permit correct relative iteration order
658
- * even across remote operations and rollbacks.
659
- */
660
- this.pendingStorageData = [];
661
- /**
662
- * A data structure containing all local pending subdirectory create/deletes, which is used in combination
663
- * with the _sequencedSubdirectories to compute optimistic values.
664
- */
665
- this.pendingSubDirectoryData = [];
666
- /**
667
- * An internal iterator that iterates over the entries in the directory.
668
- */
669
- this.internalIterator = () => {
670
- // We perform iteration in two steps - first by iterating over members of the sequenced storage data that are not
671
- // optimistically deleted or cleared, and then over the pending data lifetimes that have not subsequently
672
- // been deleted or cleared. In total, this give an ordering of members based on when they were initially
673
- // added to the sub directory (even if they were later modified), similar to the native Map.
674
- const sequencedStorageDataIterator = this.sequencedStorageData.keys();
675
- const pendingStorageDataIterator = this.pendingStorageData.values();
676
- const next = () => {
677
- let nextSequencedKey = sequencedStorageDataIterator.next();
678
- while (!nextSequencedKey.done) {
679
- const key = nextSequencedKey.value;
680
- // If we have any pending deletes or clears, then we won't iterate to this key yet (if at all).
681
- // Either it is optimistically deleted and will not be part of the iteration, or it was
682
- // re-added later and we'll iterate to it when we get to the pending data.
683
- if (!this.pendingStorageData.some((entry) => entry.type === "clear" || (entry.type === "delete" && entry.key === key))) {
684
- (0, internal_1.assert)(this.has(key), 0xc03 /* key should exist in sequenced or pending data */);
685
- const optimisticValue = this.getOptimisticValue(key);
686
- return { value: [key, optimisticValue], done: false };
687
- }
688
- nextSequencedKey = sequencedStorageDataIterator.next();
689
- }
690
- let nextPending = pendingStorageDataIterator.next();
691
- while (!nextPending.done) {
692
- const nextPendingEntry = nextPending.value;
693
- // A lifetime entry may need to be iterated.
694
- if (nextPendingEntry.type === "lifetime") {
695
- const nextPendingEntryIndex = this.pendingStorageData.indexOf(nextPendingEntry);
696
- const mostRecentDeleteOrClearIndex = (0, utils_js_1.findLastIndex)(this.pendingStorageData, (entry) => entry.type === "clear" ||
697
- (entry.type === "delete" && entry.key === nextPendingEntry.key));
698
- // Only iterate the pending entry now if it hasn't been deleted or cleared.
699
- if (nextPendingEntryIndex > mostRecentDeleteOrClearIndex) {
700
- const latestPendingValue =
701
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
702
- nextPendingEntry.keySets[nextPendingEntry.keySets.length - 1];
703
- // Skip iterating if we would have would have already iterated it as part of the sequenced data.
704
- // This is not a perfect check in the case the map has changed since the iterator was created
705
- // (e.g. if a remote client added the same key in the meantime).
706
- if (!this.sequencedStorageData.has(nextPendingEntry.key) ||
707
- mostRecentDeleteOrClearIndex !== -1) {
708
- return { value: [nextPendingEntry.key, latestPendingValue.value], done: false };
709
- }
710
- }
711
- }
712
- nextPending = pendingStorageDataIterator.next();
713
- }
714
- return { value: undefined, done: true };
715
- };
716
- const iterator = {
717
- next,
718
- [Symbol.iterator]() {
719
- return this;
720
- },
721
- };
722
- return iterator;
723
- };
724
- /**
725
- * Compute the optimistic local value for a given key. This combines the sequenced data with
726
- * any pending changes that have not yet been sequenced.
727
- */
728
- this.getOptimisticValue = (key) => {
729
- const latestPendingEntry = (0, utils_js_1.findLast)(this.pendingStorageData, (entry) => entry.type === "clear" || entry.key === key);
730
- if (latestPendingEntry === undefined) {
731
- return this.sequencedStorageData.get(key);
732
- }
733
- else if (latestPendingEntry.type === "lifetime") {
734
- const latestPendingSet =
735
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
736
- latestPendingEntry.keySets[latestPendingEntry.keySets.length - 1];
737
- return latestPendingSet.value;
738
- }
739
- else {
740
- // Delete or clear
741
- return undefined;
742
- }
743
- };
744
- /**
745
- * Determine if the directory optimistically has the key.
746
- * This will return true even if the value is undefined.
747
- */
748
- this.optimisticallyHas = (key) => {
749
- const latestPendingEntry = (0, utils_js_1.findLast)(this.pendingStorageData, (entry) => entry.type === "clear" || entry.key === key);
750
- return latestPendingEntry === undefined
751
- ? this.sequencedStorageData.has(key)
752
- : latestPendingEntry.type === "lifetime";
753
- };
754
- /**
755
- * Get the optimistic local subdirectory. This combines the sequenced data with
756
- * any pending changes that have not yet been sequenced. By default, we do not
757
- * consider disposed directories as optimistically existing, but if `getIfDisposed`
758
- * is true, we will include them since some scenarios require this.
759
- */
760
- this.getOptimisticSubDirectory = (subdirName, getIfDisposed = false) => {
761
- const latestPendingEntry = (0, utils_js_1.findLast)(this.pendingSubDirectoryData, (entry) => entry.subdirName === subdirName);
762
- let subdir;
763
- if (latestPendingEntry === undefined) {
764
- subdir = this._sequencedSubdirectories.get(subdirName);
765
- }
766
- else if (latestPendingEntry.type === "createSubDirectory") {
767
- subdir = latestPendingEntry.subdir;
768
- (0, internal_1.assert)(subdir !== undefined, 0xc2f /* Subdirectory should exist in pending data */);
769
- }
770
- else {
771
- // Pending delete
772
- return undefined;
773
- }
774
- // If the subdirectory is disposed, treat it as non-existent for optimistic reads (unless specified otherwise)
775
- if (subdir?.disposed && !getIfDisposed) {
776
- return undefined;
777
- }
778
- return subdir;
779
- };
780
654
  this.mc = (0, internal_6.createChildMonitoringContext)({ logger, namespace: "Directory" });
781
655
  }
782
656
  dispose(error) {
@@ -1242,10 +1116,142 @@ class SubDirectory extends client_utils_1.TypedEventEmitter {
1242
1116
  * Get an iterator over the entries under this IDirectory.
1243
1117
  * @returns The iterator
1244
1118
  */
1245
- [(_b = Symbol.toStringTag, Symbol.iterator)]() {
1119
+ [Symbol.iterator]() {
1246
1120
  this.throwIfDisposed();
1247
1121
  return this.internalIterator();
1248
1122
  }
1123
+ /**
1124
+ * The data this SubDirectory instance is storing, but only including sequenced values (no local pending
1125
+ * modifications are included).
1126
+ */
1127
+ sequencedStorageData = new Map();
1128
+ /**
1129
+ * A data structure containing all local pending storage modifications, which is used in combination
1130
+ * with the sequencedStorageData to compute optimistic values.
1131
+ *
1132
+ * Pending sets are aggregated into "lifetimes", which permit correct relative iteration order
1133
+ * even across remote operations and rollbacks.
1134
+ */
1135
+ pendingStorageData = [];
1136
+ /**
1137
+ * A data structure containing all local pending subdirectory create/deletes, which is used in combination
1138
+ * with the _sequencedSubdirectories to compute optimistic values.
1139
+ */
1140
+ pendingSubDirectoryData = [];
1141
+ /**
1142
+ * An internal iterator that iterates over the entries in the directory.
1143
+ */
1144
+ internalIterator = () => {
1145
+ // We perform iteration in two steps - first by iterating over members of the sequenced storage data that are not
1146
+ // optimistically deleted or cleared, and then over the pending data lifetimes that have not subsequently
1147
+ // been deleted or cleared. In total, this give an ordering of members based on when they were initially
1148
+ // added to the sub directory (even if they were later modified), similar to the native Map.
1149
+ const sequencedStorageDataIterator = this.sequencedStorageData.keys();
1150
+ const pendingStorageDataIterator = this.pendingStorageData.values();
1151
+ const next = () => {
1152
+ let nextSequencedKey = sequencedStorageDataIterator.next();
1153
+ while (!nextSequencedKey.done) {
1154
+ const key = nextSequencedKey.value;
1155
+ // If we have any pending deletes or clears, then we won't iterate to this key yet (if at all).
1156
+ // Either it is optimistically deleted and will not be part of the iteration, or it was
1157
+ // re-added later and we'll iterate to it when we get to the pending data.
1158
+ if (!this.pendingStorageData.some((entry) => entry.type === "clear" || (entry.type === "delete" && entry.key === key))) {
1159
+ (0, internal_1.assert)(this.has(key), 0xc03 /* key should exist in sequenced or pending data */);
1160
+ const optimisticValue = this.getOptimisticValue(key);
1161
+ return { value: [key, optimisticValue], done: false };
1162
+ }
1163
+ nextSequencedKey = sequencedStorageDataIterator.next();
1164
+ }
1165
+ let nextPending = pendingStorageDataIterator.next();
1166
+ while (!nextPending.done) {
1167
+ const nextPendingEntry = nextPending.value;
1168
+ // A lifetime entry may need to be iterated.
1169
+ if (nextPendingEntry.type === "lifetime") {
1170
+ const nextPendingEntryIndex = this.pendingStorageData.indexOf(nextPendingEntry);
1171
+ const mostRecentDeleteOrClearIndex = (0, utils_js_1.findLastIndex)(this.pendingStorageData, (entry) => entry.type === "clear" ||
1172
+ (entry.type === "delete" && entry.key === nextPendingEntry.key));
1173
+ // Only iterate the pending entry now if it hasn't been deleted or cleared.
1174
+ if (nextPendingEntryIndex > mostRecentDeleteOrClearIndex) {
1175
+ const latestPendingValue =
1176
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1177
+ nextPendingEntry.keySets[nextPendingEntry.keySets.length - 1];
1178
+ // Skip iterating if we would have would have already iterated it as part of the sequenced data.
1179
+ // This is not a perfect check in the case the map has changed since the iterator was created
1180
+ // (e.g. if a remote client added the same key in the meantime).
1181
+ if (!this.sequencedStorageData.has(nextPendingEntry.key) ||
1182
+ mostRecentDeleteOrClearIndex !== -1) {
1183
+ return { value: [nextPendingEntry.key, latestPendingValue.value], done: false };
1184
+ }
1185
+ }
1186
+ }
1187
+ nextPending = pendingStorageDataIterator.next();
1188
+ }
1189
+ return { value: undefined, done: true };
1190
+ };
1191
+ const iterator = {
1192
+ next,
1193
+ [Symbol.iterator]() {
1194
+ return this;
1195
+ },
1196
+ };
1197
+ return iterator;
1198
+ };
1199
+ /**
1200
+ * Compute the optimistic local value for a given key. This combines the sequenced data with
1201
+ * any pending changes that have not yet been sequenced.
1202
+ */
1203
+ getOptimisticValue = (key) => {
1204
+ const latestPendingEntry = (0, utils_js_1.findLast)(this.pendingStorageData, (entry) => entry.type === "clear" || entry.key === key);
1205
+ if (latestPendingEntry === undefined) {
1206
+ return this.sequencedStorageData.get(key);
1207
+ }
1208
+ else if (latestPendingEntry.type === "lifetime") {
1209
+ const latestPendingSet =
1210
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1211
+ latestPendingEntry.keySets[latestPendingEntry.keySets.length - 1];
1212
+ return latestPendingSet.value;
1213
+ }
1214
+ else {
1215
+ // Delete or clear
1216
+ return undefined;
1217
+ }
1218
+ };
1219
+ /**
1220
+ * Determine if the directory optimistically has the key.
1221
+ * This will return true even if the value is undefined.
1222
+ */
1223
+ optimisticallyHas = (key) => {
1224
+ const latestPendingEntry = (0, utils_js_1.findLast)(this.pendingStorageData, (entry) => entry.type === "clear" || entry.key === key);
1225
+ return latestPendingEntry === undefined
1226
+ ? this.sequencedStorageData.has(key)
1227
+ : latestPendingEntry.type === "lifetime";
1228
+ };
1229
+ /**
1230
+ * Get the optimistic local subdirectory. This combines the sequenced data with
1231
+ * any pending changes that have not yet been sequenced. By default, we do not
1232
+ * consider disposed directories as optimistically existing, but if `getIfDisposed`
1233
+ * is true, we will include them since some scenarios require this.
1234
+ */
1235
+ getOptimisticSubDirectory = (subdirName, getIfDisposed = false) => {
1236
+ const latestPendingEntry = (0, utils_js_1.findLast)(this.pendingSubDirectoryData, (entry) => entry.subdirName === subdirName);
1237
+ let subdir;
1238
+ if (latestPendingEntry === undefined) {
1239
+ subdir = this._sequencedSubdirectories.get(subdirName);
1240
+ }
1241
+ else if (latestPendingEntry.type === "createSubDirectory") {
1242
+ subdir = latestPendingEntry.subdir;
1243
+ (0, internal_1.assert)(subdir !== undefined, 0xc2f /* Subdirectory should exist in pending data */);
1244
+ }
1245
+ else {
1246
+ // Pending delete
1247
+ return undefined;
1248
+ }
1249
+ // If the subdirectory is disposed, treat it as non-existent for optimistic reads (unless specified otherwise)
1250
+ if (subdir?.disposed && !getIfDisposed) {
1251
+ return undefined;
1252
+ }
1253
+ return subdir;
1254
+ };
1249
1255
  /**
1250
1256
  * Checks if this directory should be considered visible in the optimistic view.
1251
1257
  * This requires both: