@fluidframework/map 0.58.1001 → 0.58.2000-58133

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
@@ -802,6 +811,11 @@ export class SharedDirectory extends SharedObject<ISharedDirectoryEvents> implem
802
811
  * @sealed
803
812
  */
804
813
  class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirectory {
814
+ /**
815
+ * Tells if the sub directory is disposed or not.
816
+ */
817
+ private _disposed = false;
818
+
805
819
  /**
806
820
  * String representation for the class.
807
821
  */
@@ -854,12 +868,28 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
854
868
  super();
855
869
  }
856
870
 
871
+ public dispose(error?: Error): void {
872
+ this._disposed = true;
873
+ this.emit("disposed", this);
874
+ }
875
+
876
+ public get disposed(): boolean {
877
+ return this._disposed;
878
+ }
879
+
880
+ private throwIfDisposed() {
881
+ if (this._disposed) {
882
+ throw new UsageError("Cannot access Disposed subDirectory");
883
+ }
884
+ }
885
+
857
886
  /**
858
887
  * Checks whether the given key exists in this IDirectory.
859
888
  * @param key - The key to check
860
889
  * @returns True if the key exists, false otherwise
861
890
  */
862
891
  public has(key: string): boolean {
892
+ this.throwIfDisposed();
863
893
  return this._storage.has(key);
864
894
  }
865
895
 
@@ -867,6 +897,7 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
867
897
  * {@inheritDoc IDirectory.get}
868
898
  */
869
899
  public get<T = any>(key: string): T | undefined {
900
+ this.throwIfDisposed();
870
901
  return this._storage.get(key)?.value as T | undefined;
871
902
  }
872
903
 
@@ -874,6 +905,7 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
874
905
  * {@inheritDoc IDirectory.set}
875
906
  */
876
907
  public set<T = any>(key: string, value: T): this {
908
+ this.throwIfDisposed();
877
909
  // Undefined/null keys can't be serialized to JSON in the manner we currently snapshot.
878
910
  if (key === undefined || key === null) {
879
911
  throw new Error("Undefined and null keys are not supported");
@@ -912,6 +944,7 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
912
944
  * {@inheritDoc IDirectory.createSubDirectory}
913
945
  */
914
946
  public createSubDirectory(subdirName: string): IDirectory {
947
+ this.throwIfDisposed();
915
948
  // Undefined/null subdirectory names can't be serialized to JSON in the manner we currently snapshot.
916
949
  if (subdirName === undefined || subdirName === null) {
917
950
  throw new Error("SubDirectory name may not be undefined or null");
@@ -946,6 +979,7 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
946
979
  * {@inheritDoc IDirectory.getSubDirectory}
947
980
  */
948
981
  public getSubDirectory(subdirName: string): IDirectory | undefined {
982
+ this.throwIfDisposed();
949
983
  return this._subdirectories.get(subdirName);
950
984
  }
951
985
 
@@ -953,6 +987,7 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
953
987
  * {@inheritDoc IDirectory.hasSubDirectory}
954
988
  */
955
989
  public hasSubDirectory(subdirName: string): boolean {
990
+ this.throwIfDisposed();
956
991
  return this._subdirectories.has(subdirName);
957
992
  }
958
993
 
@@ -960,6 +995,7 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
960
995
  * {@inheritDoc IDirectory.deleteSubDirectory}
961
996
  */
962
997
  public deleteSubDirectory(subdirName: string): boolean {
998
+ this.throwIfDisposed();
963
999
  // Delete the sub directory locally first.
964
1000
  const successfullyRemoved = this.deleteSubDirectoryCore(subdirName, true);
965
1001
 
@@ -982,6 +1018,7 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
982
1018
  * {@inheritDoc IDirectory.subdirectories}
983
1019
  */
984
1020
  public subdirectories(): IterableIterator<[string, IDirectory]> {
1021
+ this.throwIfDisposed();
985
1022
  return this._subdirectories.entries();
986
1023
  }
987
1024
 
@@ -989,6 +1026,7 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
989
1026
  * {@inheritDoc IDirectory.getWorkingDirectory}
990
1027
  */
991
1028
  public getWorkingDirectory(relativePath: string): IDirectory | undefined {
1029
+ this.throwIfDisposed();
992
1030
  return this.directory.getWorkingDirectory(this.makeAbsolute(relativePath));
993
1031
  }
994
1032
 
@@ -998,6 +1036,7 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
998
1036
  * @returns True if the key existed and was deleted, false if it did not exist
999
1037
  */
1000
1038
  public delete(key: string): boolean {
1039
+ this.throwIfDisposed();
1001
1040
  // Delete the key locally first.
1002
1041
  const successfullyRemoved = this.deleteCore(key, true);
1003
1042
 
@@ -1020,6 +1059,7 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
1020
1059
  * Deletes all keys from within this IDirectory.
1021
1060
  */
1022
1061
  public clear(): void {
1062
+ this.throwIfDisposed();
1023
1063
  // Clear the data locally first.
1024
1064
  this.clearCore(true);
1025
1065
 
@@ -1040,6 +1080,7 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
1040
1080
  * @param callback - Callback to issue
1041
1081
  */
1042
1082
  public forEach(callback: (value: any, key: string, map: Map<string, any>) => void): void {
1083
+ this.throwIfDisposed();
1043
1084
  this._storage.forEach((localValue, key, map) => {
1044
1085
  callback(localValue.value, key, map);
1045
1086
  });
@@ -1049,6 +1090,7 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
1049
1090
  * The number of entries under this IDirectory.
1050
1091
  */
1051
1092
  public get size(): number {
1093
+ this.throwIfDisposed();
1052
1094
  return this._storage.size;
1053
1095
  }
1054
1096
 
@@ -1057,6 +1099,7 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
1057
1099
  * @returns The iterator
1058
1100
  */
1059
1101
  public entries(): IterableIterator<[string, any]> {
1102
+ this.throwIfDisposed();
1060
1103
  const localEntriesIterator = this._storage.entries();
1061
1104
  const iterator = {
1062
1105
  next(): IteratorResult<[string, any]> {
@@ -1080,6 +1123,7 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
1080
1123
  * @returns The iterator
1081
1124
  */
1082
1125
  public keys(): IterableIterator<string> {
1126
+ this.throwIfDisposed();
1083
1127
  return this._storage.keys();
1084
1128
  }
1085
1129
 
@@ -1088,6 +1132,7 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
1088
1132
  * @returns The iterator
1089
1133
  */
1090
1134
  public values(): IterableIterator<any> {
1135
+ this.throwIfDisposed();
1091
1136
  const localValuesIterator = this._storage.values();
1092
1137
  const iterator = {
1093
1138
  next(): IteratorResult<any> {
@@ -1111,6 +1156,7 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
1111
1156
  * @returns The iterator
1112
1157
  */
1113
1158
  public [Symbol.iterator](): IterableIterator<[string, any]> {
1159
+ this.throwIfDisposed();
1114
1160
  return this.entries();
1115
1161
  }
1116
1162
 
@@ -1128,6 +1174,7 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
1128
1174
  local: boolean,
1129
1175
  localOpMetadata: unknown,
1130
1176
  ): void {
1177
+ this.throwIfDisposed();
1131
1178
  if (local) {
1132
1179
  assert(localOpMetadata !== undefined,
1133
1180
  0x00f /* `pendingMessageId is missing from the local client's ${op.type} operation` */);
@@ -1155,6 +1202,7 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
1155
1202
  local: boolean,
1156
1203
  localOpMetadata: unknown,
1157
1204
  ): void {
1205
+ this.throwIfDisposed();
1158
1206
  if (!this.needProcessStorageOperation(op, local, localOpMetadata)) {
1159
1207
  return;
1160
1208
  }
@@ -1176,6 +1224,7 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
1176
1224
  local: boolean,
1177
1225
  localOpMetadata: unknown,
1178
1226
  ): void {
1227
+ this.throwIfDisposed();
1179
1228
  if (!this.needProcessStorageOperation(op, local, localOpMetadata)) {
1180
1229
  return;
1181
1230
  }
@@ -1201,6 +1250,7 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
1201
1250
  local: boolean,
1202
1251
  localOpMetadata: unknown,
1203
1252
  ): void {
1253
+ this.throwIfDisposed();
1204
1254
  if (!this.needProcessSubDirectoryOperations(op, local, localOpMetadata)) {
1205
1255
  return;
1206
1256
  }
@@ -1221,6 +1271,7 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
1221
1271
  local: boolean,
1222
1272
  localOpMetadata: unknown,
1223
1273
  ): void {
1274
+ this.throwIfDisposed();
1224
1275
  if (!this.needProcessSubDirectoryOperations(op, local, localOpMetadata)) {
1225
1276
  return;
1226
1277
  }
@@ -1233,6 +1284,7 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
1233
1284
  * @internal
1234
1285
  */
1235
1286
  public submitClearMessage(op: IDirectoryClearOperation): void {
1287
+ this.throwIfDisposed();
1236
1288
  const pendingMessageId = ++this.pendingMessageId;
1237
1289
  this.directory.submitDirectoryMessage(op, pendingMessageId);
1238
1290
  this.pendingClearMessageId = pendingMessageId;
@@ -1244,6 +1296,7 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
1244
1296
  * @internal
1245
1297
  */
1246
1298
  public submitKeyMessage(op: IDirectoryKeyOperation): void {
1299
+ this.throwIfDisposed();
1247
1300
  const pendingMessageId = ++this.pendingMessageId;
1248
1301
  this.directory.submitDirectoryMessage(op, pendingMessageId);
1249
1302
  this.pendingKeys.set(op.key, pendingMessageId);
@@ -1255,6 +1308,7 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
1255
1308
  * @internal
1256
1309
  */
1257
1310
  public submitSubDirectoryMessage(op: IDirectorySubDirectoryOperation): void {
1311
+ this.throwIfDisposed();
1258
1312
  const pendingMessageId = ++this.pendingMessageId;
1259
1313
  this.directory.submitDirectoryMessage(op, pendingMessageId);
1260
1314
  this.pendingSubDirectories.set(op.subdirName, pendingMessageId);
@@ -1267,6 +1321,7 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
1267
1321
  * @internal
1268
1322
  */
1269
1323
  public *getSerializedStorage(serializer: IFluidSerializer) {
1324
+ this.throwIfDisposed();
1270
1325
  for (const [key, localValue] of this._storage) {
1271
1326
  const value = localValue.makeSerialized(serializer, this.directory.handle);
1272
1327
  const res: [string, ISerializedValue] = [key, value];
@@ -1281,6 +1336,7 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
1281
1336
  * @internal
1282
1337
  */
1283
1338
  public populateStorage(key: string, localValue: ILocalValue): void {
1339
+ this.throwIfDisposed();
1284
1340
  this._storage.set(key, localValue);
1285
1341
  }
1286
1342
 
@@ -1291,6 +1347,7 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
1291
1347
  * @internal
1292
1348
  */
1293
1349
  public populateSubDirectory(subdirName: string, newSubDir: SubDirectory): void {
1350
+ this.throwIfDisposed();
1294
1351
  this._subdirectories.set(subdirName, newSubDir);
1295
1352
  }
1296
1353
 
@@ -1302,6 +1359,7 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
1302
1359
  * @internal
1303
1360
  */
1304
1361
  public getLocalValue<T extends ILocalValue = ILocalValue>(key: string): T {
1362
+ this.throwIfDisposed();
1305
1363
  return this._storage.get(key) as T;
1306
1364
  }
1307
1365
 
@@ -1475,8 +1533,23 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
1475
1533
  * @param op - The message if from a remote delete, or null if from a local delete
1476
1534
  */
1477
1535
  private deleteSubDirectoryCore(subdirName: string, local: boolean) {
1536
+ const previousValue = this.getSubDirectory(subdirName);
1478
1537
  // This should make the subdirectory structure unreachable so it can be GC'd and won't appear in snapshots
1479
1538
  // Might want to consider cleaning out the structure more exhaustively though?
1480
- return this._subdirectories.delete(subdirName);
1539
+ const successfullyRemoved = this._subdirectories.delete(subdirName);
1540
+ this.disposeSubDirectoryTree(previousValue);
1541
+ return successfullyRemoved;
1542
+ }
1543
+
1544
+ private disposeSubDirectoryTree(directory: IDirectory | undefined) {
1545
+ if (!directory) {
1546
+ return;
1547
+ }
1548
+ // Dispose the subdirectory tree. This will dispose the subdirectories from bottom to top.
1549
+ const subDirectories = directory.subdirectories();
1550
+ for (const [_, subDirectory] of subDirectories) {
1551
+ this.disposeSubDirectoryTree(subDirectory);
1552
+ }
1553
+ directory.dispose();
1481
1554
  }
1482
1555
  }
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>, IDisposable {
31
31
  /**
32
32
  * The absolute path of the directory.
33
33
  */
@@ -164,6 +164,18 @@ export interface ISharedDirectoryEvents extends ISharedObjectEvents {
164
164
  * - `local` - Whether the change originated from the this client.
165
165
  *
166
166
  * - `target` - The IDirectory itself.
167
+ *
168
+ * ### "disposed"
169
+ *
170
+ * The dispose event is emitted when this sub directory is deleted.
171
+ *
172
+ * #### Listener signature
173
+ *
174
+ * ```typescript
175
+ * (local: boolean, target: IEventThisPlaceHolder) => void
176
+ * ```
177
+ *
178
+ * - `target` - The IDirectory itself.
167
179
  */
168
180
  export interface IDirectoryEvents extends IEvent {
169
181
  (event: "containedValueChanged", listener: (
@@ -171,6 +183,9 @@ export interface IDirectoryEvents extends IEvent {
171
183
  local: boolean,
172
184
  target: IEventThisPlaceHolder,
173
185
  ) => void);
186
+ (event: "disposed", listener: (
187
+ target: IEventThisPlaceHolder,
188
+ ) => void);
174
189
  }
175
190
 
176
191
  /**
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/map";
9
- export const pkgVersion = "0.58.1001";
9
+ export const pkgVersion = "0.58.2000-58133";