@fluidframework/shared-object-base 2.43.0 → 2.50.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # @fluidframework/shared-object-base
2
2
 
3
+ ## 2.50.0
4
+
5
+ Dependency updates only.
6
+
3
7
  ## 2.43.0
4
8
 
5
9
  ### Minor Changes
package/dist/handle.d.ts CHANGED
@@ -27,15 +27,14 @@ export interface ISharedObjectHandle extends IFluidHandleInternal<ISharedObject>
27
27
  */
28
28
  export declare function isISharedObjectHandle(handle: unknown): handle is ISharedObjectHandle;
29
29
  /**
30
- * Handle for a shared object.
30
+ * Handle for a shared object (DDS).
31
31
  *
32
32
  * @remarks
33
33
  *
34
- * This object is used for already loaded (in-memory) shared objects and is used only for serialization purposes.
34
+ * This object is used for already loaded (in-memory) shared objects.
35
35
  *
36
- * De-serialization process goes through {@link @fluidframework/datastore#FluidObjectHandle}, and request flow:
37
- * {@link @fluidframework/datastore#FluidDataStoreRuntime.request} recognizes requests in the form of
38
- * '/\<shared object id\>' and loads shared object.
36
+ * It provides a "bind" function that is expected to be invoked on all handles stored in this DDS,
37
+ * ensuring the target object becomes attached along with this DDS.
39
38
  */
40
39
  export declare class SharedObjectHandle extends FluidObjectHandle<ISharedObject> implements ISharedObjectHandle {
41
40
  protected readonly value: ISharedObject;
@@ -44,6 +43,15 @@ export declare class SharedObjectHandle extends FluidObjectHandle<ISharedObject>
44
43
  * Whether services have been attached for the associated shared object.
45
44
  */
46
45
  get isAttached(): boolean;
46
+ /**
47
+ * Tells whether the object of this handle is visible in the container locally or globally.
48
+ */
49
+ private get isVisible();
50
+ /**
51
+ * Tracks whether this handle is locally visible in the container.
52
+ */
53
+ private isLocallyVisible;
54
+ private readonly pendingHandles;
47
55
  /**
48
56
  * Creates a new SharedObjectHandle.
49
57
  * @param value - The shared object this handle is for.
@@ -1 +1 @@
1
- {"version":3,"file":"handle.d.ts","sourceRoot":"","sources":["../src/handle.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,KAAK,oBAAoB,EAAE,MAAM,0CAA0C,CAAC;AACrF,OAAO,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AAEvE,OAAO,KAAK,EAAE,kCAAkC,EAAE,MAAM,gDAAgD,CAAC;AAGzG,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C;;;;;GAKG;AACH,MAAM,WAAW,mBAAoB,SAAQ,oBAAoB,CAAC,aAAa,CAAC;IAC/E;;;;;OAKG;IACH,IAAI,CAAC,MAAM,EAAE,oBAAoB,GAAG,IAAI,CAAC;CACzC;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,IAAI,mBAAmB,CAEpF;AAED;;;;;;;;;;GAUG;AACH,qBAAa,kBACZ,SAAQ,iBAAiB,CAAC,aAAa,CACvC,YAAW,mBAAmB;IAiB7B,SAAS,CAAC,QAAQ,CAAC,KAAK,EAAE,aAAa;IAGvC,OAAO,CAAC,QAAQ,CAAC,OAAO;IAlBzB;;OAEG;IACH,IAAW,UAAU,IAAI,OAAO,CAE/B;IAED;;;;;;OAMG;gBAEiB,KAAK,EAAE,aAAa,EACvC,IAAI,EAAE,MAAM,EAEK,OAAO,EAAE,kCAAkC;IAK7D;;;OAGG;IACI,WAAW,IAAI,IAAI;IAKnB,IAAI,CAAC,MAAM,EAAE,oBAAoB,GAAG,IAAI;CAQ/C"}
1
+ {"version":3,"file":"handle.d.ts","sourceRoot":"","sources":["../src/handle.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,KAAK,oBAAoB,EAAE,MAAM,0CAA0C,CAAC;AACrF,OAAO,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AAEvE,OAAO,KAAK,EAAE,kCAAkC,EAAE,MAAM,gDAAgD,CAAC;AAGzG,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C;;;;;GAKG;AACH,MAAM,WAAW,mBAAoB,SAAQ,oBAAoB,CAAC,aAAa,CAAC;IAC/E;;;;;OAKG;IACH,IAAI,CAAC,MAAM,EAAE,oBAAoB,GAAG,IAAI,CAAC;CACzC;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,IAAI,mBAAmB,CAEpF;AAED;;;;;;;;;GASG;AACH,qBAAa,kBACZ,SAAQ,iBAAiB,CAAC,aAAa,CACvC,YAAW,mBAAmB;IA2C7B,SAAS,CAAC,QAAQ,CAAC,KAAK,EAAE,aAAa;IAGvC,OAAO,CAAC,QAAQ,CAAC,OAAO;IA5CzB;;OAEG;IACH,IAAW,UAAU,IAAI,OAAO,CAE/B;IAED;;OAEG;IACH,OAAO,KAAK,SAAS,GAcpB;IAED;;OAEG;IACH,OAAO,CAAC,gBAAgB,CAAkB;IAE1C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAwC;IAEvE;;;;;;OAMG;gBAEiB,KAAK,EAAE,aAAa,EACvC,IAAI,EAAE,MAAM,EAEK,OAAO,EAAE,kCAAkC;IAK7D;;;OAGG;IACI,WAAW,IAAI,IAAI;IAmBnB,IAAI,CAAC,MAAM,EAAE,oBAAoB,GAAG,IAAI;CAiB/C"}
package/dist/handle.js CHANGED
@@ -16,15 +16,14 @@ function isISharedObjectHandle(handle) {
16
16
  }
17
17
  exports.isISharedObjectHandle = isISharedObjectHandle;
18
18
  /**
19
- * Handle for a shared object.
19
+ * Handle for a shared object (DDS).
20
20
  *
21
21
  * @remarks
22
22
  *
23
- * This object is used for already loaded (in-memory) shared objects and is used only for serialization purposes.
23
+ * This object is used for already loaded (in-memory) shared objects.
24
24
  *
25
- * De-serialization process goes through {@link @fluidframework/datastore#FluidObjectHandle}, and request flow:
26
- * {@link @fluidframework/datastore#FluidDataStoreRuntime.request} recognizes requests in the form of
27
- * '/\<shared object id\>' and loads shared object.
25
+ * It provides a "bind" function that is expected to be invoked on all handles stored in this DDS,
26
+ * ensuring the target object becomes attached along with this DDS.
28
27
  */
29
28
  class SharedObjectHandle extends internal_1.FluidObjectHandle {
30
29
  /**
@@ -33,6 +32,24 @@ class SharedObjectHandle extends internal_1.FluidObjectHandle {
33
32
  get isAttached() {
34
33
  return this.value.isAttached();
35
34
  }
35
+ /**
36
+ * Tells whether the object of this handle is visible in the container locally or globally.
37
+ */
38
+ get isVisible() {
39
+ /**
40
+ * If the object of this handle is attached, it is visible in the container. Ideally, checking local visibility
41
+ * should be enough for a handle. However, there are scenarios where the object becomes locally visible but the
42
+ * handle does not know this - This will happen is attachGraph is never called on the handle. Couple of examples
43
+ * where this can happen:
44
+ *
45
+ * 1. Handles to DDS other than the default handle won't know if the DDS becomes visible after the handle was
46
+ * created.
47
+ *
48
+ * 2. Handles to root data stores will never know that it was visible because the handle will not be stores in
49
+ * another DDS and so, attachGraph will never be called on it.
50
+ */
51
+ return this.isAttached || this.isLocallyVisible;
52
+ }
36
53
  /**
37
54
  * Creates a new SharedObjectHandle.
38
55
  * @param value - The shared object this handle is for.
@@ -46,22 +63,45 @@ class SharedObjectHandle extends internal_1.FluidObjectHandle {
46
63
  super(value, path, runtime.IFluidHandleContext);
47
64
  this.value = value;
48
65
  this.runtime = runtime;
66
+ /**
67
+ * Tracks whether this handle is locally visible in the container.
68
+ */
69
+ this.isLocallyVisible = false;
70
+ this.pendingHandles = new Set();
49
71
  }
50
72
  /**
51
73
  * Attaches all bound handles first (which may in turn attach further handles), then attaches this handle.
52
74
  * When attaching the handle, it registers the associated shared object.
53
75
  */
54
76
  attachGraph() {
77
+ if (this.isVisible) {
78
+ return;
79
+ }
80
+ // Recursively attach all pending handles
81
+ this.isLocallyVisible = true;
82
+ for (const handle of this.pendingHandles) {
83
+ handle.attachGraph();
84
+ }
85
+ this.pendingHandles.clear();
86
+ // Bind this SharedObject to its context (typically the DataStore) so it attaches with it
55
87
  this.value.bindToContext();
88
+ // This will trigger the context (typically the DataStore) to attach its graph
56
89
  super.attachGraph();
57
90
  }
58
91
  bind(handle) {
59
92
  // We don't bind handles in staging mode to defer the attachment of any new objects
60
93
  // until we've exited staging mode. This way if we discard changes or a new handle is not present in the final
61
94
  // committed state, we will never end up attaching the discarded object.
62
- if (this.runtime.inStagingMode !== true) {
63
- super.bind(handle);
95
+ if (this.runtime.inStagingMode === true) {
96
+ return;
97
+ }
98
+ // If this handle is visible, attach the graph of the incoming handle as well.
99
+ if (this.isVisible) {
100
+ handle.attachGraph();
101
+ return;
64
102
  }
103
+ // If this handle is not visible, we will attach it later when this handle's attachGraph is called.
104
+ this.pendingHandles.add(handle);
65
105
  }
66
106
  }
67
107
  exports.SharedObjectHandle = SharedObjectHandle;
@@ -1 +1 @@
1
- {"version":3,"file":"handle.js","sourceRoot":"","sources":["../src/handle.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAGH,iEAAuE;AAGvE,iEAA8D;AAoB9D;;;GAGG;AACH,SAAgB,qBAAqB,CAAC,MAAe;IACpD,OAAO,IAAA,6BAAa,EAAC,MAAM,CAAC,IAAI,OAAQ,MAA8B,CAAC,IAAI,KAAK,UAAU,CAAC;AAC5F,CAAC;AAFD,sDAEC;AAED;;;;;;;;;;GAUG;AACH,MAAa,kBACZ,SAAQ,4BAAgC;IAGxC;;OAEG;IACH,IAAW,UAAU;QACpB,OAAO,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC;IAChC,CAAC;IAED;;;;;;OAMG;IACH,YACoB,KAAoB,EACvC,IAAY;IACZ,gDAAgD;IAC/B,OAA2C;QAE5D,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE,OAAO,CAAC,mBAAmB,CAAC,CAAC;QAL7B,UAAK,GAAL,KAAK,CAAe;QAGtB,YAAO,GAAP,OAAO,CAAoC;IAG7D,CAAC;IAED;;;OAGG;IACI,WAAW;QACjB,IAAI,CAAC,KAAK,CAAC,aAAa,EAAE,CAAC;QAC3B,KAAK,CAAC,WAAW,EAAE,CAAC;IACrB,CAAC;IAEM,IAAI,CAAC,MAA4B;QACvC,mFAAmF;QACnF,8GAA8G;QAC9G,wEAAwE;QACxE,IAAI,IAAI,CAAC,OAAO,CAAC,aAAa,KAAK,IAAI,EAAE,CAAC;YACzC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACpB,CAAC;IACF,CAAC;CACD;AA5CD,gDA4CC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { type IFluidHandleInternal } from \"@fluidframework/core-interfaces/internal\";\nimport { FluidObjectHandle } from \"@fluidframework/datastore/internal\";\n// eslint-disable-next-line import/no-deprecated\nimport type { IFluidDataStoreRuntimeExperimental } from \"@fluidframework/datastore-definitions/internal\";\nimport { isFluidHandle } from \"@fluidframework/runtime-utils\";\n\nimport { ISharedObject } from \"./types.js\";\n\n/**\n * Handle for a shared object. See also `SharedObjectHandle`.\n * Supports binding other handles to the underlying Shared Object (see {@link ISharedObjectHandle.bind}).\n *\n * @internal\n */\nexport interface ISharedObjectHandle extends IFluidHandleInternal<ISharedObject> {\n\t/**\n\t * Binds the given handle to this DDS or attach the given handle if this DDS is attached.\n\t * A bound handle will also be attached once this DDS is attached.\n\t *\n\t * @param handle - The target handle to bind to this DDS\n\t */\n\tbind(handle: IFluidHandleInternal): void;\n}\n\n/**\n * Type guard for {@link ISharedObjectHandle}.\n * @internal\n */\nexport function isISharedObjectHandle(handle: unknown): handle is ISharedObjectHandle {\n\treturn isFluidHandle(handle) && typeof (handle as ISharedObjectHandle).bind === \"function\";\n}\n\n/**\n * Handle for a shared object.\n *\n * @remarks\n *\n * This object is used for already loaded (in-memory) shared objects and is used only for serialization purposes.\n *\n * De-serialization process goes through {@link @fluidframework/datastore#FluidObjectHandle}, and request flow:\n * {@link @fluidframework/datastore#FluidDataStoreRuntime.request} recognizes requests in the form of\n * '/\\<shared object id\\>' and loads shared object.\n */\nexport class SharedObjectHandle\n\textends FluidObjectHandle<ISharedObject>\n\timplements ISharedObjectHandle\n{\n\t/**\n\t * Whether services have been attached for the associated shared object.\n\t */\n\tpublic get isAttached(): boolean {\n\t\treturn this.value.isAttached();\n\t}\n\n\t/**\n\t * Creates a new SharedObjectHandle.\n\t * @param value - The shared object this handle is for.\n\t * @param path - The id of the shared object. It is also the path to this object relative to the routeContext.\n\t * @param routeContext - The parent {@link @fluidframework/core-interfaces#IFluidHandleContext} that has a route\n\t * to this handle.\n\t */\n\tconstructor(\n\t\tprotected readonly value: ISharedObject,\n\t\tpath: string,\n\t\t// eslint-disable-next-line import/no-deprecated\n\t\tprivate readonly runtime: IFluidDataStoreRuntimeExperimental,\n\t) {\n\t\tsuper(value, path, runtime.IFluidHandleContext);\n\t}\n\n\t/**\n\t * Attaches all bound handles first (which may in turn attach further handles), then attaches this handle.\n\t * When attaching the handle, it registers the associated shared object.\n\t */\n\tpublic attachGraph(): void {\n\t\tthis.value.bindToContext();\n\t\tsuper.attachGraph();\n\t}\n\n\tpublic bind(handle: IFluidHandleInternal): void {\n\t\t// We don't bind handles in staging mode to defer the attachment of any new objects\n\t\t// until we've exited staging mode. This way if we discard changes or a new handle is not present in the final\n\t\t// committed state, we will never end up attaching the discarded object.\n\t\tif (this.runtime.inStagingMode !== true) {\n\t\t\tsuper.bind(handle);\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"handle.js","sourceRoot":"","sources":["../src/handle.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAGH,iEAAuE;AAGvE,iEAA8D;AAoB9D;;;GAGG;AACH,SAAgB,qBAAqB,CAAC,MAAe;IACpD,OAAO,IAAA,6BAAa,EAAC,MAAM,CAAC,IAAI,OAAQ,MAA8B,CAAC,IAAI,KAAK,UAAU,CAAC;AAC5F,CAAC;AAFD,sDAEC;AAED;;;;;;;;;GASG;AACH,MAAa,kBACZ,SAAQ,4BAAgC;IAGxC;;OAEG;IACH,IAAW,UAAU;QACpB,OAAO,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC;IAChC,CAAC;IAED;;OAEG;IACH,IAAY,SAAS;QACpB;;;;;;;;;;;WAWG;QACH,OAAO,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,gBAAgB,CAAC;IACjD,CAAC;IASD;;;;;;OAMG;IACH,YACoB,KAAoB,EACvC,IAAY;IACZ,gDAAgD;IAC/B,OAA2C;QAE5D,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE,OAAO,CAAC,mBAAmB,CAAC,CAAC;QAL7B,UAAK,GAAL,KAAK,CAAe;QAGtB,YAAO,GAAP,OAAO,CAAoC;QAlB7D;;WAEG;QACK,qBAAgB,GAAY,KAAK,CAAC;QAEzB,mBAAc,GAA8B,IAAI,GAAG,EAAE,CAAC;IAgBvE,CAAC;IAED;;;OAGG;IACI,WAAW;QACjB,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACpB,OAAO;QACR,CAAC;QAED,yCAAyC;QACzC,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC;QAC7B,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YAC1C,MAAM,CAAC,WAAW,EAAE,CAAC;QACtB,CAAC;QACD,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,CAAC;QAE5B,yFAAyF;QACzF,IAAI,CAAC,KAAK,CAAC,aAAa,EAAE,CAAC;QAE3B,8EAA8E;QAC9E,KAAK,CAAC,WAAW,EAAE,CAAC;IACrB,CAAC;IAEM,IAAI,CAAC,MAA4B;QACvC,mFAAmF;QACnF,8GAA8G;QAC9G,wEAAwE;QACxE,IAAI,IAAI,CAAC,OAAO,CAAC,aAAa,KAAK,IAAI,EAAE,CAAC;YACzC,OAAO;QACR,CAAC;QAED,8EAA8E;QAC9E,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACpB,MAAM,CAAC,WAAW,EAAE,CAAC;YACrB,OAAO;QACR,CAAC;QAED,mGAAmG;QACnG,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACjC,CAAC;CACD;AA7FD,gDA6FC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { type IFluidHandleInternal } from \"@fluidframework/core-interfaces/internal\";\nimport { FluidObjectHandle } from \"@fluidframework/datastore/internal\";\n// eslint-disable-next-line import/no-deprecated\nimport type { IFluidDataStoreRuntimeExperimental } from \"@fluidframework/datastore-definitions/internal\";\nimport { isFluidHandle } from \"@fluidframework/runtime-utils\";\n\nimport { ISharedObject } from \"./types.js\";\n\n/**\n * Handle for a shared object. See also `SharedObjectHandle`.\n * Supports binding other handles to the underlying Shared Object (see {@link ISharedObjectHandle.bind}).\n *\n * @internal\n */\nexport interface ISharedObjectHandle extends IFluidHandleInternal<ISharedObject> {\n\t/**\n\t * Binds the given handle to this DDS or attach the given handle if this DDS is attached.\n\t * A bound handle will also be attached once this DDS is attached.\n\t *\n\t * @param handle - The target handle to bind to this DDS\n\t */\n\tbind(handle: IFluidHandleInternal): void;\n}\n\n/**\n * Type guard for {@link ISharedObjectHandle}.\n * @internal\n */\nexport function isISharedObjectHandle(handle: unknown): handle is ISharedObjectHandle {\n\treturn isFluidHandle(handle) && typeof (handle as ISharedObjectHandle).bind === \"function\";\n}\n\n/**\n * Handle for a shared object (DDS).\n *\n * @remarks\n *\n * This object is used for already loaded (in-memory) shared objects.\n *\n * It provides a \"bind\" function that is expected to be invoked on all handles stored in this DDS,\n * ensuring the target object becomes attached along with this DDS.\n */\nexport class SharedObjectHandle\n\textends FluidObjectHandle<ISharedObject>\n\timplements ISharedObjectHandle\n{\n\t/**\n\t * Whether services have been attached for the associated shared object.\n\t */\n\tpublic get isAttached(): boolean {\n\t\treturn this.value.isAttached();\n\t}\n\n\t/**\n\t * Tells whether the object of this handle is visible in the container locally or globally.\n\t */\n\tprivate get isVisible(): boolean {\n\t\t/**\n\t\t * If the object of this handle is attached, it is visible in the container. Ideally, checking local visibility\n\t\t * should be enough for a handle. However, there are scenarios where the object becomes locally visible but the\n\t\t * handle does not know this - This will happen is attachGraph is never called on the handle. Couple of examples\n\t\t * where this can happen:\n\t\t *\n\t\t * 1. Handles to DDS other than the default handle won't know if the DDS becomes visible after the handle was\n\t\t * created.\n\t\t *\n\t\t * 2. Handles to root data stores will never know that it was visible because the handle will not be stores in\n\t\t * another DDS and so, attachGraph will never be called on it.\n\t\t */\n\t\treturn this.isAttached || this.isLocallyVisible;\n\t}\n\n\t/**\n\t * Tracks whether this handle is locally visible in the container.\n\t */\n\tprivate isLocallyVisible: boolean = false;\n\n\tprivate readonly pendingHandles: Set<IFluidHandleInternal> = new Set();\n\n\t/**\n\t * Creates a new SharedObjectHandle.\n\t * @param value - The shared object this handle is for.\n\t * @param path - The id of the shared object. It is also the path to this object relative to the routeContext.\n\t * @param routeContext - The parent {@link @fluidframework/core-interfaces#IFluidHandleContext} that has a route\n\t * to this handle.\n\t */\n\tconstructor(\n\t\tprotected readonly value: ISharedObject,\n\t\tpath: string,\n\t\t// eslint-disable-next-line import/no-deprecated\n\t\tprivate readonly runtime: IFluidDataStoreRuntimeExperimental,\n\t) {\n\t\tsuper(value, path, runtime.IFluidHandleContext);\n\t}\n\n\t/**\n\t * Attaches all bound handles first (which may in turn attach further handles), then attaches this handle.\n\t * When attaching the handle, it registers the associated shared object.\n\t */\n\tpublic attachGraph(): void {\n\t\tif (this.isVisible) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Recursively attach all pending handles\n\t\tthis.isLocallyVisible = true;\n\t\tfor (const handle of this.pendingHandles) {\n\t\t\thandle.attachGraph();\n\t\t}\n\t\tthis.pendingHandles.clear();\n\n\t\t// Bind this SharedObject to its context (typically the DataStore) so it attaches with it\n\t\tthis.value.bindToContext();\n\n\t\t// This will trigger the context (typically the DataStore) to attach its graph\n\t\tsuper.attachGraph();\n\t}\n\n\tpublic bind(handle: IFluidHandleInternal): void {\n\t\t// We don't bind handles in staging mode to defer the attachment of any new objects\n\t\t// until we've exited staging mode. This way if we discard changes or a new handle is not present in the final\n\t\t// committed state, we will never end up attaching the discarded object.\n\t\tif (this.runtime.inStagingMode === true) {\n\t\t\treturn;\n\t\t}\n\n\t\t// If this handle is visible, attach the graph of the incoming handle as well.\n\t\tif (this.isVisible) {\n\t\t\thandle.attachGraph();\n\t\t\treturn;\n\t\t}\n\n\t\t// If this handle is not visible, we will attach it later when this handle's attachGraph is called.\n\t\tthis.pendingHandles.add(handle);\n\t}\n}\n"]}
@@ -5,5 +5,5 @@
5
5
  * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY
6
6
  */
7
7
  export declare const pkgName = "@fluidframework/shared-object-base";
8
- export declare const pkgVersion = "2.43.0";
8
+ export declare const pkgVersion = "2.50.0";
9
9
  //# sourceMappingURL=packageVersion.d.ts.map
@@ -8,5 +8,5 @@
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
9
  exports.pkgVersion = exports.pkgName = void 0;
10
10
  exports.pkgName = "@fluidframework/shared-object-base";
11
- exports.pkgVersion = "2.43.0";
11
+ exports.pkgVersion = "2.50.0";
12
12
  //# sourceMappingURL=packageVersion.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAEU,QAAA,OAAO,GAAG,oCAAoC,CAAC;AAC/C,QAAA,UAAU,GAAG,QAAQ,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n *\n * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY\n */\n\nexport const pkgName = \"@fluidframework/shared-object-base\";\nexport const pkgVersion = \"2.43.0\";\n"]}
1
+ {"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAEU,QAAA,OAAO,GAAG,oCAAoC,CAAC;AAC/C,QAAA,UAAU,GAAG,QAAQ,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n *\n * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY\n */\n\nexport const pkgName = \"@fluidframework/shared-object-base\";\nexport const pkgVersion = \"2.50.0\";\n"]}
package/lib/handle.d.ts CHANGED
@@ -27,15 +27,14 @@ export interface ISharedObjectHandle extends IFluidHandleInternal<ISharedObject>
27
27
  */
28
28
  export declare function isISharedObjectHandle(handle: unknown): handle is ISharedObjectHandle;
29
29
  /**
30
- * Handle for a shared object.
30
+ * Handle for a shared object (DDS).
31
31
  *
32
32
  * @remarks
33
33
  *
34
- * This object is used for already loaded (in-memory) shared objects and is used only for serialization purposes.
34
+ * This object is used for already loaded (in-memory) shared objects.
35
35
  *
36
- * De-serialization process goes through {@link @fluidframework/datastore#FluidObjectHandle}, and request flow:
37
- * {@link @fluidframework/datastore#FluidDataStoreRuntime.request} recognizes requests in the form of
38
- * '/\<shared object id\>' and loads shared object.
36
+ * It provides a "bind" function that is expected to be invoked on all handles stored in this DDS,
37
+ * ensuring the target object becomes attached along with this DDS.
39
38
  */
40
39
  export declare class SharedObjectHandle extends FluidObjectHandle<ISharedObject> implements ISharedObjectHandle {
41
40
  protected readonly value: ISharedObject;
@@ -44,6 +43,15 @@ export declare class SharedObjectHandle extends FluidObjectHandle<ISharedObject>
44
43
  * Whether services have been attached for the associated shared object.
45
44
  */
46
45
  get isAttached(): boolean;
46
+ /**
47
+ * Tells whether the object of this handle is visible in the container locally or globally.
48
+ */
49
+ private get isVisible();
50
+ /**
51
+ * Tracks whether this handle is locally visible in the container.
52
+ */
53
+ private isLocallyVisible;
54
+ private readonly pendingHandles;
47
55
  /**
48
56
  * Creates a new SharedObjectHandle.
49
57
  * @param value - The shared object this handle is for.
@@ -1 +1 @@
1
- {"version":3,"file":"handle.d.ts","sourceRoot":"","sources":["../src/handle.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,KAAK,oBAAoB,EAAE,MAAM,0CAA0C,CAAC;AACrF,OAAO,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AAEvE,OAAO,KAAK,EAAE,kCAAkC,EAAE,MAAM,gDAAgD,CAAC;AAGzG,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C;;;;;GAKG;AACH,MAAM,WAAW,mBAAoB,SAAQ,oBAAoB,CAAC,aAAa,CAAC;IAC/E;;;;;OAKG;IACH,IAAI,CAAC,MAAM,EAAE,oBAAoB,GAAG,IAAI,CAAC;CACzC;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,IAAI,mBAAmB,CAEpF;AAED;;;;;;;;;;GAUG;AACH,qBAAa,kBACZ,SAAQ,iBAAiB,CAAC,aAAa,CACvC,YAAW,mBAAmB;IAiB7B,SAAS,CAAC,QAAQ,CAAC,KAAK,EAAE,aAAa;IAGvC,OAAO,CAAC,QAAQ,CAAC,OAAO;IAlBzB;;OAEG;IACH,IAAW,UAAU,IAAI,OAAO,CAE/B;IAED;;;;;;OAMG;gBAEiB,KAAK,EAAE,aAAa,EACvC,IAAI,EAAE,MAAM,EAEK,OAAO,EAAE,kCAAkC;IAK7D;;;OAGG;IACI,WAAW,IAAI,IAAI;IAKnB,IAAI,CAAC,MAAM,EAAE,oBAAoB,GAAG,IAAI;CAQ/C"}
1
+ {"version":3,"file":"handle.d.ts","sourceRoot":"","sources":["../src/handle.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,KAAK,oBAAoB,EAAE,MAAM,0CAA0C,CAAC;AACrF,OAAO,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AAEvE,OAAO,KAAK,EAAE,kCAAkC,EAAE,MAAM,gDAAgD,CAAC;AAGzG,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C;;;;;GAKG;AACH,MAAM,WAAW,mBAAoB,SAAQ,oBAAoB,CAAC,aAAa,CAAC;IAC/E;;;;;OAKG;IACH,IAAI,CAAC,MAAM,EAAE,oBAAoB,GAAG,IAAI,CAAC;CACzC;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,IAAI,mBAAmB,CAEpF;AAED;;;;;;;;;GASG;AACH,qBAAa,kBACZ,SAAQ,iBAAiB,CAAC,aAAa,CACvC,YAAW,mBAAmB;IA2C7B,SAAS,CAAC,QAAQ,CAAC,KAAK,EAAE,aAAa;IAGvC,OAAO,CAAC,QAAQ,CAAC,OAAO;IA5CzB;;OAEG;IACH,IAAW,UAAU,IAAI,OAAO,CAE/B;IAED;;OAEG;IACH,OAAO,KAAK,SAAS,GAcpB;IAED;;OAEG;IACH,OAAO,CAAC,gBAAgB,CAAkB;IAE1C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAwC;IAEvE;;;;;;OAMG;gBAEiB,KAAK,EAAE,aAAa,EACvC,IAAI,EAAE,MAAM,EAEK,OAAO,EAAE,kCAAkC;IAK7D;;;OAGG;IACI,WAAW,IAAI,IAAI;IAmBnB,IAAI,CAAC,MAAM,EAAE,oBAAoB,GAAG,IAAI;CAiB/C"}
package/lib/handle.js CHANGED
@@ -12,15 +12,14 @@ export function isISharedObjectHandle(handle) {
12
12
  return isFluidHandle(handle) && typeof handle.bind === "function";
13
13
  }
14
14
  /**
15
- * Handle for a shared object.
15
+ * Handle for a shared object (DDS).
16
16
  *
17
17
  * @remarks
18
18
  *
19
- * This object is used for already loaded (in-memory) shared objects and is used only for serialization purposes.
19
+ * This object is used for already loaded (in-memory) shared objects.
20
20
  *
21
- * De-serialization process goes through {@link @fluidframework/datastore#FluidObjectHandle}, and request flow:
22
- * {@link @fluidframework/datastore#FluidDataStoreRuntime.request} recognizes requests in the form of
23
- * '/\<shared object id\>' and loads shared object.
21
+ * It provides a "bind" function that is expected to be invoked on all handles stored in this DDS,
22
+ * ensuring the target object becomes attached along with this DDS.
24
23
  */
25
24
  export class SharedObjectHandle extends FluidObjectHandle {
26
25
  /**
@@ -29,6 +28,24 @@ export class SharedObjectHandle extends FluidObjectHandle {
29
28
  get isAttached() {
30
29
  return this.value.isAttached();
31
30
  }
31
+ /**
32
+ * Tells whether the object of this handle is visible in the container locally or globally.
33
+ */
34
+ get isVisible() {
35
+ /**
36
+ * If the object of this handle is attached, it is visible in the container. Ideally, checking local visibility
37
+ * should be enough for a handle. However, there are scenarios where the object becomes locally visible but the
38
+ * handle does not know this - This will happen is attachGraph is never called on the handle. Couple of examples
39
+ * where this can happen:
40
+ *
41
+ * 1. Handles to DDS other than the default handle won't know if the DDS becomes visible after the handle was
42
+ * created.
43
+ *
44
+ * 2. Handles to root data stores will never know that it was visible because the handle will not be stores in
45
+ * another DDS and so, attachGraph will never be called on it.
46
+ */
47
+ return this.isAttached || this.isLocallyVisible;
48
+ }
32
49
  /**
33
50
  * Creates a new SharedObjectHandle.
34
51
  * @param value - The shared object this handle is for.
@@ -42,22 +59,45 @@ export class SharedObjectHandle extends FluidObjectHandle {
42
59
  super(value, path, runtime.IFluidHandleContext);
43
60
  this.value = value;
44
61
  this.runtime = runtime;
62
+ /**
63
+ * Tracks whether this handle is locally visible in the container.
64
+ */
65
+ this.isLocallyVisible = false;
66
+ this.pendingHandles = new Set();
45
67
  }
46
68
  /**
47
69
  * Attaches all bound handles first (which may in turn attach further handles), then attaches this handle.
48
70
  * When attaching the handle, it registers the associated shared object.
49
71
  */
50
72
  attachGraph() {
73
+ if (this.isVisible) {
74
+ return;
75
+ }
76
+ // Recursively attach all pending handles
77
+ this.isLocallyVisible = true;
78
+ for (const handle of this.pendingHandles) {
79
+ handle.attachGraph();
80
+ }
81
+ this.pendingHandles.clear();
82
+ // Bind this SharedObject to its context (typically the DataStore) so it attaches with it
51
83
  this.value.bindToContext();
84
+ // This will trigger the context (typically the DataStore) to attach its graph
52
85
  super.attachGraph();
53
86
  }
54
87
  bind(handle) {
55
88
  // We don't bind handles in staging mode to defer the attachment of any new objects
56
89
  // until we've exited staging mode. This way if we discard changes or a new handle is not present in the final
57
90
  // committed state, we will never end up attaching the discarded object.
58
- if (this.runtime.inStagingMode !== true) {
59
- super.bind(handle);
91
+ if (this.runtime.inStagingMode === true) {
92
+ return;
93
+ }
94
+ // If this handle is visible, attach the graph of the incoming handle as well.
95
+ if (this.isVisible) {
96
+ handle.attachGraph();
97
+ return;
60
98
  }
99
+ // If this handle is not visible, we will attach it later when this handle's attachGraph is called.
100
+ this.pendingHandles.add(handle);
61
101
  }
62
102
  }
63
103
  //# sourceMappingURL=handle.js.map
package/lib/handle.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"handle.js","sourceRoot":"","sources":["../src/handle.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AAGvE,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAoB9D;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CAAC,MAAe;IACpD,OAAO,aAAa,CAAC,MAAM,CAAC,IAAI,OAAQ,MAA8B,CAAC,IAAI,KAAK,UAAU,CAAC;AAC5F,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,OAAO,kBACZ,SAAQ,iBAAgC;IAGxC;;OAEG;IACH,IAAW,UAAU;QACpB,OAAO,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC;IAChC,CAAC;IAED;;;;;;OAMG;IACH,YACoB,KAAoB,EACvC,IAAY;IACZ,gDAAgD;IAC/B,OAA2C;QAE5D,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE,OAAO,CAAC,mBAAmB,CAAC,CAAC;QAL7B,UAAK,GAAL,KAAK,CAAe;QAGtB,YAAO,GAAP,OAAO,CAAoC;IAG7D,CAAC;IAED;;;OAGG;IACI,WAAW;QACjB,IAAI,CAAC,KAAK,CAAC,aAAa,EAAE,CAAC;QAC3B,KAAK,CAAC,WAAW,EAAE,CAAC;IACrB,CAAC;IAEM,IAAI,CAAC,MAA4B;QACvC,mFAAmF;QACnF,8GAA8G;QAC9G,wEAAwE;QACxE,IAAI,IAAI,CAAC,OAAO,CAAC,aAAa,KAAK,IAAI,EAAE,CAAC;YACzC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACpB,CAAC;IACF,CAAC;CACD","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { type IFluidHandleInternal } from \"@fluidframework/core-interfaces/internal\";\nimport { FluidObjectHandle } from \"@fluidframework/datastore/internal\";\n// eslint-disable-next-line import/no-deprecated\nimport type { IFluidDataStoreRuntimeExperimental } from \"@fluidframework/datastore-definitions/internal\";\nimport { isFluidHandle } from \"@fluidframework/runtime-utils\";\n\nimport { ISharedObject } from \"./types.js\";\n\n/**\n * Handle for a shared object. See also `SharedObjectHandle`.\n * Supports binding other handles to the underlying Shared Object (see {@link ISharedObjectHandle.bind}).\n *\n * @internal\n */\nexport interface ISharedObjectHandle extends IFluidHandleInternal<ISharedObject> {\n\t/**\n\t * Binds the given handle to this DDS or attach the given handle if this DDS is attached.\n\t * A bound handle will also be attached once this DDS is attached.\n\t *\n\t * @param handle - The target handle to bind to this DDS\n\t */\n\tbind(handle: IFluidHandleInternal): void;\n}\n\n/**\n * Type guard for {@link ISharedObjectHandle}.\n * @internal\n */\nexport function isISharedObjectHandle(handle: unknown): handle is ISharedObjectHandle {\n\treturn isFluidHandle(handle) && typeof (handle as ISharedObjectHandle).bind === \"function\";\n}\n\n/**\n * Handle for a shared object.\n *\n * @remarks\n *\n * This object is used for already loaded (in-memory) shared objects and is used only for serialization purposes.\n *\n * De-serialization process goes through {@link @fluidframework/datastore#FluidObjectHandle}, and request flow:\n * {@link @fluidframework/datastore#FluidDataStoreRuntime.request} recognizes requests in the form of\n * '/\\<shared object id\\>' and loads shared object.\n */\nexport class SharedObjectHandle\n\textends FluidObjectHandle<ISharedObject>\n\timplements ISharedObjectHandle\n{\n\t/**\n\t * Whether services have been attached for the associated shared object.\n\t */\n\tpublic get isAttached(): boolean {\n\t\treturn this.value.isAttached();\n\t}\n\n\t/**\n\t * Creates a new SharedObjectHandle.\n\t * @param value - The shared object this handle is for.\n\t * @param path - The id of the shared object. It is also the path to this object relative to the routeContext.\n\t * @param routeContext - The parent {@link @fluidframework/core-interfaces#IFluidHandleContext} that has a route\n\t * to this handle.\n\t */\n\tconstructor(\n\t\tprotected readonly value: ISharedObject,\n\t\tpath: string,\n\t\t// eslint-disable-next-line import/no-deprecated\n\t\tprivate readonly runtime: IFluidDataStoreRuntimeExperimental,\n\t) {\n\t\tsuper(value, path, runtime.IFluidHandleContext);\n\t}\n\n\t/**\n\t * Attaches all bound handles first (which may in turn attach further handles), then attaches this handle.\n\t * When attaching the handle, it registers the associated shared object.\n\t */\n\tpublic attachGraph(): void {\n\t\tthis.value.bindToContext();\n\t\tsuper.attachGraph();\n\t}\n\n\tpublic bind(handle: IFluidHandleInternal): void {\n\t\t// We don't bind handles in staging mode to defer the attachment of any new objects\n\t\t// until we've exited staging mode. This way if we discard changes or a new handle is not present in the final\n\t\t// committed state, we will never end up attaching the discarded object.\n\t\tif (this.runtime.inStagingMode !== true) {\n\t\t\tsuper.bind(handle);\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"handle.js","sourceRoot":"","sources":["../src/handle.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AAGvE,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAoB9D;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CAAC,MAAe;IACpD,OAAO,aAAa,CAAC,MAAM,CAAC,IAAI,OAAQ,MAA8B,CAAC,IAAI,KAAK,UAAU,CAAC;AAC5F,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,OAAO,kBACZ,SAAQ,iBAAgC;IAGxC;;OAEG;IACH,IAAW,UAAU;QACpB,OAAO,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC;IAChC,CAAC;IAED;;OAEG;IACH,IAAY,SAAS;QACpB;;;;;;;;;;;WAWG;QACH,OAAO,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,gBAAgB,CAAC;IACjD,CAAC;IASD;;;;;;OAMG;IACH,YACoB,KAAoB,EACvC,IAAY;IACZ,gDAAgD;IAC/B,OAA2C;QAE5D,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE,OAAO,CAAC,mBAAmB,CAAC,CAAC;QAL7B,UAAK,GAAL,KAAK,CAAe;QAGtB,YAAO,GAAP,OAAO,CAAoC;QAlB7D;;WAEG;QACK,qBAAgB,GAAY,KAAK,CAAC;QAEzB,mBAAc,GAA8B,IAAI,GAAG,EAAE,CAAC;IAgBvE,CAAC;IAED;;;OAGG;IACI,WAAW;QACjB,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACpB,OAAO;QACR,CAAC;QAED,yCAAyC;QACzC,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC;QAC7B,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YAC1C,MAAM,CAAC,WAAW,EAAE,CAAC;QACtB,CAAC;QACD,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,CAAC;QAE5B,yFAAyF;QACzF,IAAI,CAAC,KAAK,CAAC,aAAa,EAAE,CAAC;QAE3B,8EAA8E;QAC9E,KAAK,CAAC,WAAW,EAAE,CAAC;IACrB,CAAC;IAEM,IAAI,CAAC,MAA4B;QACvC,mFAAmF;QACnF,8GAA8G;QAC9G,wEAAwE;QACxE,IAAI,IAAI,CAAC,OAAO,CAAC,aAAa,KAAK,IAAI,EAAE,CAAC;YACzC,OAAO;QACR,CAAC;QAED,8EAA8E;QAC9E,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACpB,MAAM,CAAC,WAAW,EAAE,CAAC;YACrB,OAAO;QACR,CAAC;QAED,mGAAmG;QACnG,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACjC,CAAC;CACD","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { type IFluidHandleInternal } from \"@fluidframework/core-interfaces/internal\";\nimport { FluidObjectHandle } from \"@fluidframework/datastore/internal\";\n// eslint-disable-next-line import/no-deprecated\nimport type { IFluidDataStoreRuntimeExperimental } from \"@fluidframework/datastore-definitions/internal\";\nimport { isFluidHandle } from \"@fluidframework/runtime-utils\";\n\nimport { ISharedObject } from \"./types.js\";\n\n/**\n * Handle for a shared object. See also `SharedObjectHandle`.\n * Supports binding other handles to the underlying Shared Object (see {@link ISharedObjectHandle.bind}).\n *\n * @internal\n */\nexport interface ISharedObjectHandle extends IFluidHandleInternal<ISharedObject> {\n\t/**\n\t * Binds the given handle to this DDS or attach the given handle if this DDS is attached.\n\t * A bound handle will also be attached once this DDS is attached.\n\t *\n\t * @param handle - The target handle to bind to this DDS\n\t */\n\tbind(handle: IFluidHandleInternal): void;\n}\n\n/**\n * Type guard for {@link ISharedObjectHandle}.\n * @internal\n */\nexport function isISharedObjectHandle(handle: unknown): handle is ISharedObjectHandle {\n\treturn isFluidHandle(handle) && typeof (handle as ISharedObjectHandle).bind === \"function\";\n}\n\n/**\n * Handle for a shared object (DDS).\n *\n * @remarks\n *\n * This object is used for already loaded (in-memory) shared objects.\n *\n * It provides a \"bind\" function that is expected to be invoked on all handles stored in this DDS,\n * ensuring the target object becomes attached along with this DDS.\n */\nexport class SharedObjectHandle\n\textends FluidObjectHandle<ISharedObject>\n\timplements ISharedObjectHandle\n{\n\t/**\n\t * Whether services have been attached for the associated shared object.\n\t */\n\tpublic get isAttached(): boolean {\n\t\treturn this.value.isAttached();\n\t}\n\n\t/**\n\t * Tells whether the object of this handle is visible in the container locally or globally.\n\t */\n\tprivate get isVisible(): boolean {\n\t\t/**\n\t\t * If the object of this handle is attached, it is visible in the container. Ideally, checking local visibility\n\t\t * should be enough for a handle. However, there are scenarios where the object becomes locally visible but the\n\t\t * handle does not know this - This will happen is attachGraph is never called on the handle. Couple of examples\n\t\t * where this can happen:\n\t\t *\n\t\t * 1. Handles to DDS other than the default handle won't know if the DDS becomes visible after the handle was\n\t\t * created.\n\t\t *\n\t\t * 2. Handles to root data stores will never know that it was visible because the handle will not be stores in\n\t\t * another DDS and so, attachGraph will never be called on it.\n\t\t */\n\t\treturn this.isAttached || this.isLocallyVisible;\n\t}\n\n\t/**\n\t * Tracks whether this handle is locally visible in the container.\n\t */\n\tprivate isLocallyVisible: boolean = false;\n\n\tprivate readonly pendingHandles: Set<IFluidHandleInternal> = new Set();\n\n\t/**\n\t * Creates a new SharedObjectHandle.\n\t * @param value - The shared object this handle is for.\n\t * @param path - The id of the shared object. It is also the path to this object relative to the routeContext.\n\t * @param routeContext - The parent {@link @fluidframework/core-interfaces#IFluidHandleContext} that has a route\n\t * to this handle.\n\t */\n\tconstructor(\n\t\tprotected readonly value: ISharedObject,\n\t\tpath: string,\n\t\t// eslint-disable-next-line import/no-deprecated\n\t\tprivate readonly runtime: IFluidDataStoreRuntimeExperimental,\n\t) {\n\t\tsuper(value, path, runtime.IFluidHandleContext);\n\t}\n\n\t/**\n\t * Attaches all bound handles first (which may in turn attach further handles), then attaches this handle.\n\t * When attaching the handle, it registers the associated shared object.\n\t */\n\tpublic attachGraph(): void {\n\t\tif (this.isVisible) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Recursively attach all pending handles\n\t\tthis.isLocallyVisible = true;\n\t\tfor (const handle of this.pendingHandles) {\n\t\t\thandle.attachGraph();\n\t\t}\n\t\tthis.pendingHandles.clear();\n\n\t\t// Bind this SharedObject to its context (typically the DataStore) so it attaches with it\n\t\tthis.value.bindToContext();\n\n\t\t// This will trigger the context (typically the DataStore) to attach its graph\n\t\tsuper.attachGraph();\n\t}\n\n\tpublic bind(handle: IFluidHandleInternal): void {\n\t\t// We don't bind handles in staging mode to defer the attachment of any new objects\n\t\t// until we've exited staging mode. This way if we discard changes or a new handle is not present in the final\n\t\t// committed state, we will never end up attaching the discarded object.\n\t\tif (this.runtime.inStagingMode === true) {\n\t\t\treturn;\n\t\t}\n\n\t\t// If this handle is visible, attach the graph of the incoming handle as well.\n\t\tif (this.isVisible) {\n\t\t\thandle.attachGraph();\n\t\t\treturn;\n\t\t}\n\n\t\t// If this handle is not visible, we will attach it later when this handle's attachGraph is called.\n\t\tthis.pendingHandles.add(handle);\n\t}\n}\n"]}
@@ -5,5 +5,5 @@
5
5
  * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY
6
6
  */
7
7
  export declare const pkgName = "@fluidframework/shared-object-base";
8
- export declare const pkgVersion = "2.43.0";
8
+ export declare const pkgVersion = "2.50.0";
9
9
  //# sourceMappingURL=packageVersion.d.ts.map
@@ -5,5 +5,5 @@
5
5
  * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY
6
6
  */
7
7
  export const pkgName = "@fluidframework/shared-object-base";
8
- export const pkgVersion = "2.43.0";
8
+ export const pkgVersion = "2.50.0";
9
9
  //# sourceMappingURL=packageVersion.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,CAAC,MAAM,OAAO,GAAG,oCAAoC,CAAC;AAC5D,MAAM,CAAC,MAAM,UAAU,GAAG,QAAQ,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n *\n * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY\n */\n\nexport const pkgName = \"@fluidframework/shared-object-base\";\nexport const pkgVersion = \"2.43.0\";\n"]}
1
+ {"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,CAAC,MAAM,OAAO,GAAG,oCAAoC,CAAC;AAC5D,MAAM,CAAC,MAAM,UAAU,GAAG,QAAQ,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n *\n * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY\n */\n\nexport const pkgName = \"@fluidframework/shared-object-base\";\nexport const pkgVersion = \"2.50.0\";\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluidframework/shared-object-base",
3
- "version": "2.43.0",
3
+ "version": "2.50.0",
4
4
  "description": "Fluid base class for shared distributed data structures",
5
5
  "homepage": "https://fluidframework.com",
6
6
  "repository": {
@@ -69,30 +69,30 @@
69
69
  "temp-directory": "nyc/.nyc_output"
70
70
  },
71
71
  "dependencies": {
72
- "@fluid-internal/client-utils": "~2.43.0",
73
- "@fluidframework/container-definitions": "~2.43.0",
74
- "@fluidframework/core-interfaces": "~2.43.0",
75
- "@fluidframework/core-utils": "~2.43.0",
76
- "@fluidframework/datastore": "~2.43.0",
77
- "@fluidframework/datastore-definitions": "~2.43.0",
78
- "@fluidframework/driver-definitions": "~2.43.0",
79
- "@fluidframework/id-compressor": "~2.43.0",
80
- "@fluidframework/runtime-definitions": "~2.43.0",
81
- "@fluidframework/runtime-utils": "~2.43.0",
82
- "@fluidframework/telemetry-utils": "~2.43.0",
72
+ "@fluid-internal/client-utils": "~2.50.0",
73
+ "@fluidframework/container-definitions": "~2.50.0",
74
+ "@fluidframework/core-interfaces": "~2.50.0",
75
+ "@fluidframework/core-utils": "~2.50.0",
76
+ "@fluidframework/datastore": "~2.50.0",
77
+ "@fluidframework/datastore-definitions": "~2.50.0",
78
+ "@fluidframework/driver-definitions": "~2.50.0",
79
+ "@fluidframework/id-compressor": "~2.50.0",
80
+ "@fluidframework/runtime-definitions": "~2.50.0",
81
+ "@fluidframework/runtime-utils": "~2.50.0",
82
+ "@fluidframework/telemetry-utils": "~2.50.0",
83
83
  "uuid": "^9.0.0"
84
84
  },
85
85
  "devDependencies": {
86
86
  "@arethetypeswrong/cli": "^0.17.1",
87
87
  "@biomejs/biome": "~1.9.3",
88
- "@fluid-internal/mocha-test-setup": "~2.43.0",
89
- "@fluid-private/test-pairwise-generator": "~2.43.0",
88
+ "@fluid-internal/mocha-test-setup": "~2.50.0",
89
+ "@fluid-private/test-pairwise-generator": "~2.50.0",
90
90
  "@fluid-tools/build-cli": "^0.56.0",
91
91
  "@fluidframework/build-common": "^2.0.3",
92
92
  "@fluidframework/build-tools": "^0.56.0",
93
93
  "@fluidframework/eslint-config-fluid": "^5.7.4",
94
- "@fluidframework/shared-object-base-previous": "npm:@fluidframework/shared-object-base@2.42.0",
95
- "@fluidframework/test-runtime-utils": "~2.43.0",
94
+ "@fluidframework/shared-object-base-previous": "npm:@fluidframework/shared-object-base@2.43.0",
95
+ "@fluidframework/test-runtime-utils": "~2.50.0",
96
96
  "@microsoft/api-extractor": "7.52.8",
97
97
  "@types/benchmark": "^2.1.0",
98
98
  "@types/mocha": "^10.0.10",
@@ -113,7 +113,20 @@
113
113
  "typescript": "~5.4.5"
114
114
  },
115
115
  "typeValidation": {
116
- "broken": {},
116
+ "broken": {
117
+ "Class_SharedObject": {
118
+ "backCompat": false
119
+ },
120
+ "Class_SharedObjectCore": {
121
+ "backCompat": false
122
+ },
123
+ "ClassStatics_SharedObject": {
124
+ "backCompat": false
125
+ },
126
+ "ClassStatics_SharedObjectCore": {
127
+ "backCompat": false
128
+ }
129
+ },
117
130
  "entrypoint": "legacy"
118
131
  },
119
132
  "scripts": {
package/src/handle.ts CHANGED
@@ -36,15 +36,14 @@ export function isISharedObjectHandle(handle: unknown): handle is ISharedObjectH
36
36
  }
37
37
 
38
38
  /**
39
- * Handle for a shared object.
39
+ * Handle for a shared object (DDS).
40
40
  *
41
41
  * @remarks
42
42
  *
43
- * This object is used for already loaded (in-memory) shared objects and is used only for serialization purposes.
43
+ * This object is used for already loaded (in-memory) shared objects.
44
44
  *
45
- * De-serialization process goes through {@link @fluidframework/datastore#FluidObjectHandle}, and request flow:
46
- * {@link @fluidframework/datastore#FluidDataStoreRuntime.request} recognizes requests in the form of
47
- * '/\<shared object id\>' and loads shared object.
45
+ * It provides a "bind" function that is expected to be invoked on all handles stored in this DDS,
46
+ * ensuring the target object becomes attached along with this DDS.
48
47
  */
49
48
  export class SharedObjectHandle
50
49
  extends FluidObjectHandle<ISharedObject>
@@ -57,6 +56,32 @@ export class SharedObjectHandle
57
56
  return this.value.isAttached();
58
57
  }
59
58
 
59
+ /**
60
+ * Tells whether the object of this handle is visible in the container locally or globally.
61
+ */
62
+ private get isVisible(): boolean {
63
+ /**
64
+ * If the object of this handle is attached, it is visible in the container. Ideally, checking local visibility
65
+ * should be enough for a handle. However, there are scenarios where the object becomes locally visible but the
66
+ * handle does not know this - This will happen is attachGraph is never called on the handle. Couple of examples
67
+ * where this can happen:
68
+ *
69
+ * 1. Handles to DDS other than the default handle won't know if the DDS becomes visible after the handle was
70
+ * created.
71
+ *
72
+ * 2. Handles to root data stores will never know that it was visible because the handle will not be stores in
73
+ * another DDS and so, attachGraph will never be called on it.
74
+ */
75
+ return this.isAttached || this.isLocallyVisible;
76
+ }
77
+
78
+ /**
79
+ * Tracks whether this handle is locally visible in the container.
80
+ */
81
+ private isLocallyVisible: boolean = false;
82
+
83
+ private readonly pendingHandles: Set<IFluidHandleInternal> = new Set();
84
+
60
85
  /**
61
86
  * Creates a new SharedObjectHandle.
62
87
  * @param value - The shared object this handle is for.
@@ -78,7 +103,21 @@ export class SharedObjectHandle
78
103
  * When attaching the handle, it registers the associated shared object.
79
104
  */
80
105
  public attachGraph(): void {
106
+ if (this.isVisible) {
107
+ return;
108
+ }
109
+
110
+ // Recursively attach all pending handles
111
+ this.isLocallyVisible = true;
112
+ for (const handle of this.pendingHandles) {
113
+ handle.attachGraph();
114
+ }
115
+ this.pendingHandles.clear();
116
+
117
+ // Bind this SharedObject to its context (typically the DataStore) so it attaches with it
81
118
  this.value.bindToContext();
119
+
120
+ // This will trigger the context (typically the DataStore) to attach its graph
82
121
  super.attachGraph();
83
122
  }
84
123
 
@@ -86,8 +125,17 @@ export class SharedObjectHandle
86
125
  // We don't bind handles in staging mode to defer the attachment of any new objects
87
126
  // until we've exited staging mode. This way if we discard changes or a new handle is not present in the final
88
127
  // committed state, we will never end up attaching the discarded object.
89
- if (this.runtime.inStagingMode !== true) {
90
- super.bind(handle);
128
+ if (this.runtime.inStagingMode === true) {
129
+ return;
130
+ }
131
+
132
+ // If this handle is visible, attach the graph of the incoming handle as well.
133
+ if (this.isVisible) {
134
+ handle.attachGraph();
135
+ return;
91
136
  }
137
+
138
+ // If this handle is not visible, we will attach it later when this handle's attachGraph is called.
139
+ this.pendingHandles.add(handle);
92
140
  }
93
141
  }
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/shared-object-base";
9
- export const pkgVersion = "2.43.0";
9
+ export const pkgVersion = "2.50.0";