@fluidframework/map 0.58.2002 → 0.59.1000

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/src/directory.ts CHANGED
@@ -4,6 +4,7 @@
4
4
  */
5
5
 
6
6
  import { assert,TypedEventEmitter } from "@fluidframework/common-utils";
7
+ import { UsageError } from "@fluidframework/container-utils";
7
8
  import { readAndParse } from "@fluidframework/driver-utils";
8
9
  import {
9
10
  ISequencedDocumentMessage,
@@ -424,6 +425,14 @@ export class SharedDirectory extends SharedObject<ISharedDirectoryEvents> implem
424
425
  return this;
425
426
  }
426
427
 
428
+ public dispose(error?: Error): void {
429
+ this.root.dispose(error);
430
+ }
431
+
432
+ public get disposed(): boolean {
433
+ return this.root.disposed;
434
+ }
435
+
427
436
  /**
428
437
  * Deletes the given key from within this IDirectory.
429
438
  * @param key - The key to delete
@@ -480,6 +489,13 @@ export class SharedDirectory extends SharedObject<ISharedDirectoryEvents> implem
480
489
  return this.root.entries();
481
490
  }
482
491
 
492
+ /**
493
+ * {@inheritDoc IDirectory.countSubDirectory}
494
+ */
495
+ public countSubDirectory(): number {
496
+ return this.root.countSubDirectory();
497
+ }
498
+
483
499
  /**
484
500
  * Get an iterator over the keys under this IDirectory.
485
501
  * @returns The iterator
@@ -802,6 +818,11 @@ export class SharedDirectory extends SharedObject<ISharedDirectoryEvents> implem
802
818
  * @sealed
803
819
  */
804
820
  class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirectory {
821
+ /**
822
+ * Tells if the sub directory is disposed or not.
823
+ */
824
+ private _disposed = false;
825
+
805
826
  /**
806
827
  * String representation for the class.
807
828
  */
@@ -854,12 +875,28 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
854
875
  super();
855
876
  }
856
877
 
878
+ public dispose(error?: Error): void {
879
+ this._disposed = true;
880
+ this.emit("disposed", this);
881
+ }
882
+
883
+ public get disposed(): boolean {
884
+ return this._disposed;
885
+ }
886
+
887
+ private throwIfDisposed() {
888
+ if (this._disposed) {
889
+ throw new UsageError("Cannot access Disposed subDirectory");
890
+ }
891
+ }
892
+
857
893
  /**
858
894
  * Checks whether the given key exists in this IDirectory.
859
895
  * @param key - The key to check
860
896
  * @returns True if the key exists, false otherwise
861
897
  */
862
898
  public has(key: string): boolean {
899
+ this.throwIfDisposed();
863
900
  return this._storage.has(key);
864
901
  }
865
902
 
@@ -867,6 +904,7 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
867
904
  * {@inheritDoc IDirectory.get}
868
905
  */
869
906
  public get<T = any>(key: string): T | undefined {
907
+ this.throwIfDisposed();
870
908
  return this._storage.get(key)?.value as T | undefined;
871
909
  }
872
910
 
@@ -874,6 +912,7 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
874
912
  * {@inheritDoc IDirectory.set}
875
913
  */
876
914
  public set<T = any>(key: string, value: T): this {
915
+ this.throwIfDisposed();
877
916
  // Undefined/null keys can't be serialized to JSON in the manner we currently snapshot.
878
917
  if (key === undefined || key === null) {
879
918
  throw new Error("Undefined and null keys are not supported");
@@ -908,10 +947,18 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
908
947
  return this;
909
948
  }
910
949
 
950
+ /**
951
+ * {@inheritDoc IDirectory.countSubDirectory}
952
+ */
953
+ public countSubDirectory(): number {
954
+ return this._subdirectories.size;
955
+ }
956
+
911
957
  /**
912
958
  * {@inheritDoc IDirectory.createSubDirectory}
913
959
  */
914
960
  public createSubDirectory(subdirName: string): IDirectory {
961
+ this.throwIfDisposed();
915
962
  // Undefined/null subdirectory names can't be serialized to JSON in the manner we currently snapshot.
916
963
  if (subdirName === undefined || subdirName === null) {
917
964
  throw new Error("SubDirectory name may not be undefined or null");
@@ -946,6 +993,7 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
946
993
  * {@inheritDoc IDirectory.getSubDirectory}
947
994
  */
948
995
  public getSubDirectory(subdirName: string): IDirectory | undefined {
996
+ this.throwIfDisposed();
949
997
  return this._subdirectories.get(subdirName);
950
998
  }
951
999
 
@@ -953,6 +1001,7 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
953
1001
  * {@inheritDoc IDirectory.hasSubDirectory}
954
1002
  */
955
1003
  public hasSubDirectory(subdirName: string): boolean {
1004
+ this.throwIfDisposed();
956
1005
  return this._subdirectories.has(subdirName);
957
1006
  }
958
1007
 
@@ -960,6 +1009,7 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
960
1009
  * {@inheritDoc IDirectory.deleteSubDirectory}
961
1010
  */
962
1011
  public deleteSubDirectory(subdirName: string): boolean {
1012
+ this.throwIfDisposed();
963
1013
  // Delete the sub directory locally first.
964
1014
  const successfullyRemoved = this.deleteSubDirectoryCore(subdirName, true);
965
1015
 
@@ -982,6 +1032,7 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
982
1032
  * {@inheritDoc IDirectory.subdirectories}
983
1033
  */
984
1034
  public subdirectories(): IterableIterator<[string, IDirectory]> {
1035
+ this.throwIfDisposed();
985
1036
  return this._subdirectories.entries();
986
1037
  }
987
1038
 
@@ -989,6 +1040,7 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
989
1040
  * {@inheritDoc IDirectory.getWorkingDirectory}
990
1041
  */
991
1042
  public getWorkingDirectory(relativePath: string): IDirectory | undefined {
1043
+ this.throwIfDisposed();
992
1044
  return this.directory.getWorkingDirectory(this.makeAbsolute(relativePath));
993
1045
  }
994
1046
 
@@ -998,6 +1050,7 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
998
1050
  * @returns True if the key existed and was deleted, false if it did not exist
999
1051
  */
1000
1052
  public delete(key: string): boolean {
1053
+ this.throwIfDisposed();
1001
1054
  // Delete the key locally first.
1002
1055
  const successfullyRemoved = this.deleteCore(key, true);
1003
1056
 
@@ -1020,6 +1073,7 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
1020
1073
  * Deletes all keys from within this IDirectory.
1021
1074
  */
1022
1075
  public clear(): void {
1076
+ this.throwIfDisposed();
1023
1077
  // Clear the data locally first.
1024
1078
  this.clearCore(true);
1025
1079
 
@@ -1040,6 +1094,7 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
1040
1094
  * @param callback - Callback to issue
1041
1095
  */
1042
1096
  public forEach(callback: (value: any, key: string, map: Map<string, any>) => void): void {
1097
+ this.throwIfDisposed();
1043
1098
  this._storage.forEach((localValue, key, map) => {
1044
1099
  callback(localValue.value, key, map);
1045
1100
  });
@@ -1049,6 +1104,7 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
1049
1104
  * The number of entries under this IDirectory.
1050
1105
  */
1051
1106
  public get size(): number {
1107
+ this.throwIfDisposed();
1052
1108
  return this._storage.size;
1053
1109
  }
1054
1110
 
@@ -1057,6 +1113,7 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
1057
1113
  * @returns The iterator
1058
1114
  */
1059
1115
  public entries(): IterableIterator<[string, any]> {
1116
+ this.throwIfDisposed();
1060
1117
  const localEntriesIterator = this._storage.entries();
1061
1118
  const iterator = {
1062
1119
  next(): IteratorResult<[string, any]> {
@@ -1080,6 +1137,7 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
1080
1137
  * @returns The iterator
1081
1138
  */
1082
1139
  public keys(): IterableIterator<string> {
1140
+ this.throwIfDisposed();
1083
1141
  return this._storage.keys();
1084
1142
  }
1085
1143
 
@@ -1088,6 +1146,7 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
1088
1146
  * @returns The iterator
1089
1147
  */
1090
1148
  public values(): IterableIterator<any> {
1149
+ this.throwIfDisposed();
1091
1150
  const localValuesIterator = this._storage.values();
1092
1151
  const iterator = {
1093
1152
  next(): IteratorResult<any> {
@@ -1111,6 +1170,7 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
1111
1170
  * @returns The iterator
1112
1171
  */
1113
1172
  public [Symbol.iterator](): IterableIterator<[string, any]> {
1173
+ this.throwIfDisposed();
1114
1174
  return this.entries();
1115
1175
  }
1116
1176
 
@@ -1128,6 +1188,7 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
1128
1188
  local: boolean,
1129
1189
  localOpMetadata: unknown,
1130
1190
  ): void {
1191
+ this.throwIfDisposed();
1131
1192
  if (local) {
1132
1193
  assert(localOpMetadata !== undefined,
1133
1194
  0x00f /* `pendingMessageId is missing from the local client's ${op.type} operation` */);
@@ -1155,6 +1216,7 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
1155
1216
  local: boolean,
1156
1217
  localOpMetadata: unknown,
1157
1218
  ): void {
1219
+ this.throwIfDisposed();
1158
1220
  if (!this.needProcessStorageOperation(op, local, localOpMetadata)) {
1159
1221
  return;
1160
1222
  }
@@ -1176,6 +1238,7 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
1176
1238
  local: boolean,
1177
1239
  localOpMetadata: unknown,
1178
1240
  ): void {
1241
+ this.throwIfDisposed();
1179
1242
  if (!this.needProcessStorageOperation(op, local, localOpMetadata)) {
1180
1243
  return;
1181
1244
  }
@@ -1201,6 +1264,7 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
1201
1264
  local: boolean,
1202
1265
  localOpMetadata: unknown,
1203
1266
  ): void {
1267
+ this.throwIfDisposed();
1204
1268
  if (!this.needProcessSubDirectoryOperations(op, local, localOpMetadata)) {
1205
1269
  return;
1206
1270
  }
@@ -1221,6 +1285,7 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
1221
1285
  local: boolean,
1222
1286
  localOpMetadata: unknown,
1223
1287
  ): void {
1288
+ this.throwIfDisposed();
1224
1289
  if (!this.needProcessSubDirectoryOperations(op, local, localOpMetadata)) {
1225
1290
  return;
1226
1291
  }
@@ -1233,6 +1298,7 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
1233
1298
  * @internal
1234
1299
  */
1235
1300
  public submitClearMessage(op: IDirectoryClearOperation): void {
1301
+ this.throwIfDisposed();
1236
1302
  const pendingMessageId = ++this.pendingMessageId;
1237
1303
  this.directory.submitDirectoryMessage(op, pendingMessageId);
1238
1304
  this.pendingClearMessageId = pendingMessageId;
@@ -1244,6 +1310,7 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
1244
1310
  * @internal
1245
1311
  */
1246
1312
  public submitKeyMessage(op: IDirectoryKeyOperation): void {
1313
+ this.throwIfDisposed();
1247
1314
  const pendingMessageId = ++this.pendingMessageId;
1248
1315
  this.directory.submitDirectoryMessage(op, pendingMessageId);
1249
1316
  this.pendingKeys.set(op.key, pendingMessageId);
@@ -1255,6 +1322,7 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
1255
1322
  * @internal
1256
1323
  */
1257
1324
  public submitSubDirectoryMessage(op: IDirectorySubDirectoryOperation): void {
1325
+ this.throwIfDisposed();
1258
1326
  const pendingMessageId = ++this.pendingMessageId;
1259
1327
  this.directory.submitDirectoryMessage(op, pendingMessageId);
1260
1328
  this.pendingSubDirectories.set(op.subdirName, pendingMessageId);
@@ -1267,6 +1335,7 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
1267
1335
  * @internal
1268
1336
  */
1269
1337
  public *getSerializedStorage(serializer: IFluidSerializer) {
1338
+ this.throwIfDisposed();
1270
1339
  for (const [key, localValue] of this._storage) {
1271
1340
  const value = localValue.makeSerialized(serializer, this.directory.handle);
1272
1341
  const res: [string, ISerializedValue] = [key, value];
@@ -1281,6 +1350,7 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
1281
1350
  * @internal
1282
1351
  */
1283
1352
  public populateStorage(key: string, localValue: ILocalValue): void {
1353
+ this.throwIfDisposed();
1284
1354
  this._storage.set(key, localValue);
1285
1355
  }
1286
1356
 
@@ -1291,6 +1361,7 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
1291
1361
  * @internal
1292
1362
  */
1293
1363
  public populateSubDirectory(subdirName: string, newSubDir: SubDirectory): void {
1364
+ this.throwIfDisposed();
1294
1365
  this._subdirectories.set(subdirName, newSubDir);
1295
1366
  }
1296
1367
 
@@ -1302,6 +1373,7 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
1302
1373
  * @internal
1303
1374
  */
1304
1375
  public getLocalValue<T extends ILocalValue = ILocalValue>(key: string): T {
1376
+ this.throwIfDisposed();
1305
1377
  return this._storage.get(key) as T;
1306
1378
  }
1307
1379
 
@@ -1475,8 +1547,25 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
1475
1547
  * @param op - The message if from a remote delete, or null if from a local delete
1476
1548
  */
1477
1549
  private deleteSubDirectoryCore(subdirName: string, local: boolean) {
1550
+ const previousValue = this.getSubDirectory(subdirName);
1478
1551
  // This should make the subdirectory structure unreachable so it can be GC'd and won't appear in snapshots
1479
1552
  // Might want to consider cleaning out the structure more exhaustively though?
1480
- return this._subdirectories.delete(subdirName);
1553
+ const successfullyRemoved = this._subdirectories.delete(subdirName);
1554
+ this.disposeSubDirectoryTree(previousValue);
1555
+ return successfullyRemoved;
1556
+ }
1557
+
1558
+ private disposeSubDirectoryTree(directory: IDirectory | undefined) {
1559
+ if (!directory) {
1560
+ return;
1561
+ }
1562
+ // Dispose the subdirectory tree. This will dispose the subdirectories from bottom to top.
1563
+ const subDirectories = directory.subdirectories();
1564
+ for (const [_, subDirectory] of subDirectories) {
1565
+ this.disposeSubDirectoryTree(subDirectory);
1566
+ }
1567
+ if (typeof directory.dispose === "function") {
1568
+ directory.dispose();
1569
+ }
1481
1570
  }
1482
1571
  }
package/src/interfaces.ts CHANGED
@@ -4,7 +4,7 @@
4
4
  */
5
5
 
6
6
  import { ISharedObject, ISharedObjectEvents } from "@fluidframework/shared-object-base";
7
- import { IEvent, IEventProvider, IEventThisPlaceHolder } from "@fluidframework/common-definitions";
7
+ import { IDisposable, IEvent, IEventProvider, IEventThisPlaceHolder } from "@fluidframework/common-definitions";
8
8
 
9
9
  /**
10
10
  * Type of "valueChanged" event parameter.
@@ -27,7 +27,7 @@ export interface IValueChanged {
27
27
  * @remarks
28
28
  * When used as a Map, operates on its keys.
29
29
  */
30
- export interface IDirectory extends Map<string, any>, IEventProvider<IDirectoryEvents> {
30
+ export interface IDirectory extends Map<string, any>, IEventProvider<IDirectoryEvents>, Partial<IDisposable> {
31
31
  /**
32
32
  * The absolute path of the directory.
33
33
  */
@@ -48,6 +48,12 @@ export interface IDirectory extends Map<string, any>, IEventProvider<IDirectoryE
48
48
  */
49
49
  set<T = any>(key: string, value: T): this;
50
50
 
51
+ /**
52
+ * Get the number of sub directory within the directory.
53
+ * @returns The number of sub directory within a directory.
54
+ */
55
+ countSubDirectory?(): number;
56
+
51
57
  /**
52
58
  * Creates an IDirectory child of this IDirectory, or retrieves the existing IDirectory child if one with the
53
59
  * same name already exists.
@@ -164,6 +170,18 @@ export interface ISharedDirectoryEvents extends ISharedObjectEvents {
164
170
  * - `local` - Whether the change originated from the this client.
165
171
  *
166
172
  * - `target` - The IDirectory itself.
173
+ *
174
+ * ### "disposed"
175
+ *
176
+ * The dispose event is emitted when this sub directory is deleted.
177
+ *
178
+ * #### Listener signature
179
+ *
180
+ * ```typescript
181
+ * (local: boolean, target: IEventThisPlaceHolder) => void
182
+ * ```
183
+ *
184
+ * - `target` - The IDirectory itself.
167
185
  */
168
186
  export interface IDirectoryEvents extends IEvent {
169
187
  (event: "containedValueChanged", listener: (
@@ -171,6 +189,9 @@ export interface IDirectoryEvents extends IEvent {
171
189
  local: boolean,
172
190
  target: IEventThisPlaceHolder,
173
191
  ) => void);
192
+ (event: "disposed", listener: (
193
+ target: IEventThisPlaceHolder,
194
+ ) => void);
174
195
  }
175
196
 
176
197
  /**
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/map";
9
- export const pkgVersion = "0.58.2002";
9
+ export const pkgVersion = "0.59.1000";