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