@fluidframework/shared-object-base 2.32.0 → 2.33.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 (74) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/api-report/shared-object-base.legacy.alpha.api.md +8 -8
  3. package/dist/gcHandleVisitor.d.ts +19 -0
  4. package/dist/gcHandleVisitor.d.ts.map +1 -0
  5. package/dist/gcHandleVisitor.js +34 -0
  6. package/dist/gcHandleVisitor.js.map +1 -0
  7. package/dist/handle.d.ts +5 -2
  8. package/dist/handle.d.ts.map +1 -1
  9. package/dist/handle.js +13 -2
  10. package/dist/handle.js.map +1 -1
  11. package/dist/index.d.ts +0 -1
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +1 -3
  14. package/dist/index.js.map +1 -1
  15. package/dist/packageVersion.d.ts +1 -1
  16. package/dist/packageVersion.js +1 -1
  17. package/dist/packageVersion.js.map +1 -1
  18. package/dist/serializer.d.ts +2 -2
  19. package/dist/serializer.d.ts.map +1 -1
  20. package/dist/serializer.js +32 -32
  21. package/dist/serializer.js.map +1 -1
  22. package/dist/sharedObject.d.ts +6 -0
  23. package/dist/sharedObject.d.ts.map +1 -1
  24. package/dist/sharedObject.js +20 -11
  25. package/dist/sharedObject.js.map +1 -1
  26. package/dist/utils.d.ts +1 -1
  27. package/dist/utils.d.ts.map +1 -1
  28. package/dist/utils.js +2 -0
  29. package/dist/utils.js.map +1 -1
  30. package/lib/gcHandleVisitor.d.ts +19 -0
  31. package/lib/gcHandleVisitor.d.ts.map +1 -0
  32. package/lib/gcHandleVisitor.js +30 -0
  33. package/lib/gcHandleVisitor.js.map +1 -0
  34. package/lib/handle.d.ts +5 -2
  35. package/lib/handle.d.ts.map +1 -1
  36. package/lib/handle.js +13 -2
  37. package/lib/handle.js.map +1 -1
  38. package/lib/index.d.ts +0 -1
  39. package/lib/index.d.ts.map +1 -1
  40. package/lib/index.js +0 -1
  41. package/lib/index.js.map +1 -1
  42. package/lib/packageVersion.d.ts +1 -1
  43. package/lib/packageVersion.js +1 -1
  44. package/lib/packageVersion.js.map +1 -1
  45. package/lib/serializer.d.ts +2 -2
  46. package/lib/serializer.d.ts.map +1 -1
  47. package/lib/serializer.js +32 -32
  48. package/lib/serializer.js.map +1 -1
  49. package/lib/sharedObject.d.ts +6 -0
  50. package/lib/sharedObject.d.ts.map +1 -1
  51. package/lib/sharedObject.js +21 -12
  52. package/lib/sharedObject.js.map +1 -1
  53. package/lib/tsdoc-metadata.json +1 -1
  54. package/lib/utils.d.ts +1 -1
  55. package/lib/utils.d.ts.map +1 -1
  56. package/lib/utils.js +2 -0
  57. package/lib/utils.js.map +1 -1
  58. package/package.json +18 -18
  59. package/src/gcHandleVisitor.ts +36 -0
  60. package/src/handle.ts +15 -3
  61. package/src/index.ts +0 -1
  62. package/src/packageVersion.ts +1 -1
  63. package/src/serializer.ts +11 -7
  64. package/src/sharedObject.ts +23 -12
  65. package/src/utils.ts +6 -3
  66. package/dist/summarySerializer.d.ts +0 -17
  67. package/dist/summarySerializer.d.ts.map +0 -1
  68. package/dist/summarySerializer.js +0 -27
  69. package/dist/summarySerializer.js.map +0 -1
  70. package/lib/summarySerializer.d.ts +0 -17
  71. package/lib/summarySerializer.d.ts.map +0 -1
  72. package/lib/summarySerializer.js +0 -23
  73. package/lib/summarySerializer.js.map +0 -1
  74. package/src/summarySerializer.ts +0 -28
package/lib/utils.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,EAAE,kBAAkB,EAAE,MAAM,wCAAwC,CAAC;AAI5E;;;;;;;;;GASG;AACH,MAAM,UAAU,gBAAgB,CAC/B,KAAc,EACd,UAA4B,EAC5B,IAAkB;IAElB,OAAO,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;AACxE,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,uBAAuB,CACtC,KAAc,EACd,UAA4B,EAC5B,IAAkB;IAElB,OAAO,UAAU,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;AACvC,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,YAAY,CAAC,KAAc,EAAE,UAA4B;IACxE,OAAO,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACjC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,uBAAuB,CACtC,GAAW,EACX,OAA4B;IAE5B,MAAM,OAAO,GAAG,IAAI,kBAAkB,EAAE,CAAC;IACzC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAC9B,OAAO,OAAO,CAAC,cAAc,EAAE,CAAC;AACjC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAC1B,KAAc,EACd,UAA4B,EAC5B,IAAkB;IAElB,sEAAsE;IACtE,0EAA0E;IAC1E,4EAA4E;IAC5E,sEAAsE;IACtE,UAAU,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;AAChC,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { IFluidHandle } from \"@fluidframework/core-interfaces\";\nimport type { IChannel } from \"@fluidframework/datastore-definitions/internal\";\nimport { ISummaryTreeWithStats } from \"@fluidframework/runtime-definitions/internal\";\nimport { SummaryTreeBuilder } from \"@fluidframework/runtime-utils/internal\";\n\nimport { IFluidSerializer } from \"./serializer.js\";\n\n/**\n * Given a mostly-plain object that may have handle objects embedded within, return a string representation of an object\n * where the handle objects have been replaced with a serializable form.\n * @param value - The mostly-plain object\n * @param serializer - The serializer that knows how to convert handles into serializable format\n * @param context - The handle context for the container\n * @param bind - Bind any other handles we find in the object against this given handle.\n * @returns Result of strigifying an object\n * @internal\n */\nexport function serializeHandles(\n\tvalue: unknown,\n\tserializer: IFluidSerializer,\n\tbind: IFluidHandle,\n): string | undefined {\n\treturn value === undefined ? value : serializer.stringify(value, bind);\n}\n\n/**\n * Given a mostly-plain object that may have handle objects embedded within, will return a fully-plain object\n * where any embedded IFluidHandles have been replaced with a serializable form.\n *\n * The original `input` object is not mutated. This method will shallowly clones all objects in the path from\n * the root to any replaced handles. (If no handles are found, returns the original object.)\n *\n * @param input - The mostly-plain object\n * @param context - The handle context for the container\n * @param bind - Bind any other handles we find in the object against this given handle.\n * @returns The fully-plain object\n * @legacy\n * @alpha\n */\nexport function makeHandlesSerializable(\n\tvalue: unknown,\n\tserializer: IFluidSerializer,\n\tbind: IFluidHandle,\n): unknown {\n\treturn serializer.encode(value, bind);\n}\n\n/**\n * Given a fully-plain object that may have serializable-form handles within, will return the mostly-plain object\n * with handle objects created instead.\n * @remarks Idempotent when called multiple times.\n * @param value - The fully-plain object\n * @param serializer - The serializer that knows how to convert serializable-form handles into handle objects\n * @param context - The handle context for the container\n * @returns The mostly-plain object with handle objects within\n * @legacy\n * @alpha\n */\nexport function parseHandles(value: unknown, serializer: IFluidSerializer): unknown {\n\treturn serializer.decode(value);\n}\n\n/**\n * Create a new summary containing one blob\n * @param key - the key for the blob in the summary\n * @param content - blob content\n * @returns The summary containing the blob\n * @internal\n */\nexport function createSingleBlobSummary(\n\tkey: string,\n\tcontent: string | Uint8Array,\n): ISummaryTreeWithStats {\n\tconst builder = new SummaryTreeBuilder();\n\tbuilder.addBlob(key, content);\n\treturn builder.getSummaryTree();\n}\n\n/**\n * Binds all handles found in `value` to `bind`. Does not modify original input.\n *\n * @internal\n */\nexport function bindHandles(\n\tvalue: unknown,\n\tserializer: IFluidSerializer,\n\tbind: IFluidHandle,\n): void {\n\t// N.B. AB#7316 this could be made more efficient by writing an ad hoc\n\t// implementation that doesn't clone at all. Today the distinction between\n\t// this function and `encode` is purely semantic -- encoding both serializes\n\t// handles and binds them, but sometimes we only wish to do the latter\n\tserializer.encode(value, bind);\n}\n\n/**\n * Information about a Fluid channel.\n * @privateRemarks\n * This is distinct from {@link IChannel} as it omits the APIs used by the runtime to manage the channel and instead only has things which are useful (and safe) to expose to users of the channel.\n * @internal\n */\nexport type IChannelView = Pick<IChannel, \"id\" | \"attributes\" | \"isAttached\">;\n"]}
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,EAAE,kBAAkB,EAAE,MAAM,wCAAwC,CAAC;AAI5E;;;;;;;;;GASG;AACH,MAAM,UAAU,gBAAgB,CAC/B,KAAc,EACd,UAA4B,EAC5B,IAAkB;IAElB,OAAO,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;AACxE,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,uBAAuB,CACtC,KAAc,EACd,UAA4B,EAC5B,IAAkB;IAElB,OAAO,UAAU,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;AACvC,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,YAAY,CAAC,KAAc,EAAE,UAA4B;IACxE,OAAO,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACjC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,uBAAuB,CACtC,GAAW,EACX,OAA4B;IAE5B,MAAM,OAAO,GAAG,IAAI,kBAAkB,EAAE,CAAC;IACzC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAC9B,OAAO,OAAO,CAAC,cAAc,EAAE,CAAC;AACjC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAC1B,KAAQ,EACR,UAA4B,EAC5B,IAAkB;IAElB,sEAAsE;IACtE,0EAA0E;IAC1E,4EAA4E;IAC5E,sEAAsE;IACtE,UAAU,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IAE/B,wFAAwF;IACxF,OAAO,KAAK,CAAC;AACd,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { IFluidHandle } from \"@fluidframework/core-interfaces\";\nimport type { IChannel } from \"@fluidframework/datastore-definitions/internal\";\nimport { ISummaryTreeWithStats } from \"@fluidframework/runtime-definitions/internal\";\nimport { SummaryTreeBuilder } from \"@fluidframework/runtime-utils/internal\";\n\nimport { IFluidSerializer } from \"./serializer.js\";\n\n/**\n * Given a mostly-plain object that may have handle objects embedded within, return a string representation of an object\n * where the handle objects have been replaced with a serializable form.\n * @param value - The mostly-plain object\n * @param serializer - The serializer that knows how to convert handles into serializable format\n * @param context - The handle context for the container\n * @param bind - Bind any other handles we find in the object against this given handle.\n * @returns Result of strigifying an object\n * @internal\n */\nexport function serializeHandles(\n\tvalue: unknown,\n\tserializer: IFluidSerializer,\n\tbind: IFluidHandle,\n): string | undefined {\n\treturn value === undefined ? value : serializer.stringify(value, bind);\n}\n\n/**\n * Given a mostly-plain object that may have handle objects embedded within, will return a fully-plain object\n * where any embedded IFluidHandles have been replaced with a serializable form.\n *\n * The original `input` object is not mutated. This method will shallowly clones all objects in the path from\n * the root to any replaced handles. (If no handles are found, returns the original object.)\n *\n * @param input - The mostly-plain object\n * @param context - The handle context for the container\n * @param bind - Bind any other handles we find in the object against this given handle.\n * @returns The fully-plain object\n * @legacy\n * @alpha\n */\nexport function makeHandlesSerializable(\n\tvalue: unknown,\n\tserializer: IFluidSerializer,\n\tbind: IFluidHandle,\n): unknown {\n\treturn serializer.encode(value, bind);\n}\n\n/**\n * Given a fully-plain object that may have serializable-form handles within, will return the mostly-plain object\n * with handle objects created instead.\n * @remarks Idempotent when called multiple times.\n * @param value - The fully-plain object\n * @param serializer - The serializer that knows how to convert serializable-form handles into handle objects\n * @param context - The handle context for the container\n * @returns The mostly-plain object with handle objects within\n * @legacy\n * @alpha\n */\nexport function parseHandles(value: unknown, serializer: IFluidSerializer): unknown {\n\treturn serializer.decode(value);\n}\n\n/**\n * Create a new summary containing one blob\n * @param key - the key for the blob in the summary\n * @param content - blob content\n * @returns The summary containing the blob\n * @internal\n */\nexport function createSingleBlobSummary(\n\tkey: string,\n\tcontent: string | Uint8Array,\n): ISummaryTreeWithStats {\n\tconst builder = new SummaryTreeBuilder();\n\tbuilder.addBlob(key, content);\n\treturn builder.getSummaryTree();\n}\n\n/**\n * Binds all handles found in `value` to `bind`. Does not modify original input.\n *\n * @internal\n */\nexport function bindHandles<T = unknown>(\n\tvalue: T,\n\tserializer: IFluidSerializer,\n\tbind: IFluidHandle,\n): T {\n\t// N.B. AB#7316 this could be made more efficient by writing an ad hoc\n\t// implementation that doesn't clone at all. Today the distinction between\n\t// this function and `encode` is purely semantic -- encoding both serializes\n\t// handles and binds them, but sometimes we only wish to do the latter\n\tserializer.encode(value, bind);\n\n\t// Return the input value so this function can be swapped in for makeHandlesSerializable\n\treturn value;\n}\n\n/**\n * Information about a Fluid channel.\n * @privateRemarks\n * This is distinct from {@link IChannel} as it omits the APIs used by the runtime to manage the channel and instead only has things which are useful (and safe) to expose to users of the channel.\n * @internal\n */\nexport type IChannelView = Pick<IChannel, \"id\" | \"attributes\" | \"isAttached\">;\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluidframework/shared-object-base",
3
- "version": "2.32.0",
3
+ "version": "2.33.0",
4
4
  "description": "Fluid base class for shared distributed data structures",
5
5
  "homepage": "https://fluidframework.com",
6
6
  "repository": {
@@ -69,33 +69,34 @@
69
69
  "temp-directory": "nyc/.nyc_output"
70
70
  },
71
71
  "dependencies": {
72
- "@fluid-internal/client-utils": "~2.32.0",
73
- "@fluidframework/container-definitions": "~2.32.0",
74
- "@fluidframework/core-interfaces": "~2.32.0",
75
- "@fluidframework/core-utils": "~2.32.0",
76
- "@fluidframework/datastore": "~2.32.0",
77
- "@fluidframework/datastore-definitions": "~2.32.0",
78
- "@fluidframework/driver-definitions": "~2.32.0",
79
- "@fluidframework/runtime-definitions": "~2.32.0",
80
- "@fluidframework/runtime-utils": "~2.32.0",
81
- "@fluidframework/telemetry-utils": "~2.32.0",
72
+ "@fluid-internal/client-utils": "~2.33.0",
73
+ "@fluidframework/container-definitions": "~2.33.0",
74
+ "@fluidframework/core-interfaces": "~2.33.0",
75
+ "@fluidframework/core-utils": "~2.33.0",
76
+ "@fluidframework/datastore": "~2.33.0",
77
+ "@fluidframework/datastore-definitions": "~2.33.0",
78
+ "@fluidframework/driver-definitions": "~2.33.0",
79
+ "@fluidframework/runtime-definitions": "~2.33.0",
80
+ "@fluidframework/runtime-utils": "~2.33.0",
81
+ "@fluidframework/telemetry-utils": "~2.33.0",
82
82
  "uuid": "^9.0.0"
83
83
  },
84
84
  "devDependencies": {
85
85
  "@arethetypeswrong/cli": "^0.17.1",
86
86
  "@biomejs/biome": "~1.9.3",
87
- "@fluid-internal/mocha-test-setup": "~2.32.0",
88
- "@fluid-private/test-pairwise-generator": "~2.32.0",
87
+ "@fluid-internal/mocha-test-setup": "~2.33.0",
88
+ "@fluid-private/test-pairwise-generator": "~2.33.0",
89
89
  "@fluid-tools/build-cli": "^0.55.0",
90
90
  "@fluidframework/build-common": "^2.0.3",
91
91
  "@fluidframework/build-tools": "^0.55.0",
92
92
  "@fluidframework/eslint-config-fluid": "^5.7.3",
93
- "@fluidframework/shared-object-base-previous": "npm:@fluidframework/shared-object-base@2.31.0",
94
- "@fluidframework/test-runtime-utils": "~2.32.0",
95
- "@microsoft/api-extractor": "7.50.1",
93
+ "@fluidframework/shared-object-base-previous": "npm:@fluidframework/shared-object-base@2.32.0",
94
+ "@fluidframework/test-runtime-utils": "~2.33.0",
95
+ "@microsoft/api-extractor": "7.52.5",
96
96
  "@types/benchmark": "^2.1.0",
97
97
  "@types/mocha": "^10.0.10",
98
98
  "@types/node": "^18.19.0",
99
+ "@types/sinon": "^17.0.3",
99
100
  "@types/uuid": "^9.0.2",
100
101
  "benchmark": "^2.1.4",
101
102
  "c8": "^8.0.1",
@@ -105,9 +106,8 @@
105
106
  "eslint": "~8.55.0",
106
107
  "mocha": "^10.8.2",
107
108
  "mocha-multi-reporters": "^1.5.1",
108
- "moment": "^2.21.0",
109
- "replace-in-file": "^6.3.5",
110
109
  "rimraf": "^4.4.0",
110
+ "sinon": "^18.0.1",
111
111
  "ts-node": "^10.9.1",
112
112
  "typescript": "~5.4.5"
113
113
  },
@@ -0,0 +1,36 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ import { type IFluidHandleInternal } from "@fluidframework/core-interfaces/internal";
7
+ import { type ISerializedHandle } from "@fluidframework/runtime-utils/internal";
8
+
9
+ import { FluidSerializer } from "./serializer.js";
10
+
11
+ /**
12
+ * Implementation of IFluidSerializer used by GC to visit all the handles in the DDS to collect its outbound routes
13
+ *
14
+ * @remarks - This is given to DDS code that typically produces a serialization of the data, which is then ignored.
15
+ * All that is needed is getSerializedRoutes() to get the routes. This strategy could be optimized if needed.
16
+ */
17
+ export class GCHandleVisitor extends FluidSerializer {
18
+ private readonly visitedHandlePaths: Set<string> = new Set();
19
+ public getVisitedHandlePaths(): string[] {
20
+ return [...this.visitedHandlePaths];
21
+ }
22
+
23
+ protected bindAndEncodeHandle(
24
+ handle: IFluidHandleInternal,
25
+ bind: IFluidHandleInternal,
26
+ ): ISerializedHandle {
27
+ this.visitedHandlePaths.add(handle.absolutePath);
28
+
29
+ // Just return a dummy value. The serialization itself is not used.
30
+ // It's especially important we don't bind since that has side effects that are irrelevant to GC.
31
+ return {
32
+ type: "__fluid_handle__",
33
+ url: "UNUSED (see GCHandleVisitor)",
34
+ };
35
+ }
36
+ }
package/src/handle.ts CHANGED
@@ -3,8 +3,10 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
- import { IFluidHandleContext } from "@fluidframework/core-interfaces/internal";
6
+ import { type IFluidHandleInternal } from "@fluidframework/core-interfaces/internal";
7
7
  import { FluidObjectHandle } from "@fluidframework/datastore/internal";
8
+ // eslint-disable-next-line import/no-deprecated
9
+ import type { IFluidDataStoreRuntimeExperimental } from "@fluidframework/datastore-definitions/internal";
8
10
 
9
11
  import { ISharedObject } from "./types.js";
10
12
 
@@ -37,9 +39,10 @@ export class SharedObjectHandle extends FluidObjectHandle<ISharedObject> {
37
39
  constructor(
38
40
  protected readonly value: ISharedObject,
39
41
  path: string,
40
- routeContext: IFluidHandleContext,
42
+ // eslint-disable-next-line import/no-deprecated
43
+ private readonly runtime: IFluidDataStoreRuntimeExperimental,
41
44
  ) {
42
- super(value, path, routeContext);
45
+ super(value, path, runtime.IFluidHandleContext);
43
46
  }
44
47
 
45
48
  /**
@@ -50,4 +53,13 @@ export class SharedObjectHandle extends FluidObjectHandle<ISharedObject> {
50
53
  this.value.bindToContext();
51
54
  super.attachGraph();
52
55
  }
56
+
57
+ public bind(handle: IFluidHandleInternal): void {
58
+ // We don't bind handles in staging mode to defer the attachment of any new objects
59
+ // until we've exited staging mode. This way if we discard changes or a new handle is not present in the final
60
+ // committed state, we will never end up attaching the discarded object.
61
+ if (this.runtime.inStagingMode !== true) {
62
+ super.bind(handle);
63
+ }
64
+ }
53
65
  }
package/src/index.ts CHANGED
@@ -11,7 +11,6 @@ export {
11
11
  SharedObjectKind,
12
12
  createSharedObjectKind,
13
13
  } from "./sharedObject.js";
14
- export { SummarySerializer } from "./summarySerializer.js";
15
14
  export { ISharedObject, ISharedObjectEvents } from "./types.js";
16
15
  export {
17
16
  createSingleBlobSummary,
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/shared-object-base";
9
- export const pkgVersion = "2.32.0";
9
+ export const pkgVersion = "2.33.0";
package/src/serializer.ts CHANGED
@@ -88,7 +88,7 @@ export class FluidSerializer implements IFluidSerializer {
88
88
  // return the result of 'recursivelyReplace()'.
89
89
  // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
90
90
  return !!input && typeof input === "object"
91
- ? this.recursivelyReplace(input, this.encodeValue, bind)
91
+ ? this.recursivelyReplace(input, this.encodeValue.bind(this), bind)
92
92
  : input;
93
93
  }
94
94
 
@@ -106,7 +106,7 @@ export class FluidSerializer implements IFluidSerializer {
106
106
  // return the result of 'recursivelyReplace()'.
107
107
  // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
108
108
  return !!input && typeof input === "object"
109
- ? this.recursivelyReplace(input, this.decodeValue)
109
+ ? this.recursivelyReplace(input, this.decodeValue.bind(this))
110
110
  : input;
111
111
  }
112
112
 
@@ -131,20 +131,20 @@ export class FluidSerializer implements IFluidSerializer {
131
131
  * If the given 'value' is an IFluidHandle, returns the encoded IFluidHandle.
132
132
  * Otherwise returns the original 'value'. Used by 'encode()' and 'stringify()'.
133
133
  */
134
- private readonly encodeValue = (value: unknown, bind?: IFluidHandleInternal): unknown => {
134
+ protected encodeValue(value: unknown, bind?: IFluidHandleInternal): unknown {
135
135
  // If 'value' is an IFluidHandle return its encoded form.
136
136
  if (isFluidHandle(value)) {
137
137
  assert(bind !== undefined, 0xa93 /* Cannot encode a handle without a bind context */);
138
138
  return this.bindAndEncodeHandle(toFluidHandleInternal(value), bind);
139
139
  }
140
140
  return value;
141
- };
141
+ }
142
142
 
143
143
  /**
144
144
  * If the given 'value' is an encoded IFluidHandle, returns the decoded IFluidHandle.
145
145
  * Otherwise returns the original 'value'. Used by 'decode()' and 'parse()'.
146
146
  */
147
- private readonly decodeValue = (value: unknown): unknown => {
147
+ protected decodeValue(value: unknown): unknown {
148
148
  // If 'value' is a serialized IFluidHandle return the deserialized result.
149
149
  if (isSerializedHandle(value)) {
150
150
  // Old documents may have handles with relative path in their summaries. Convert these to absolute
@@ -153,11 +153,15 @@ export class FluidSerializer implements IFluidSerializer {
153
153
  ? value.url
154
154
  : generateHandleContextPath(value.url, this.context);
155
155
 
156
- return new RemoteFluidObjectHandle(absolutePath, this.root);
156
+ return new RemoteFluidObjectHandle(
157
+ absolutePath,
158
+ this.root,
159
+ value.payloadPending === true,
160
+ );
157
161
  } else {
158
162
  return value;
159
163
  }
160
- };
164
+ }
161
165
 
162
166
  /**
163
167
  * Invoked for non-null objects to recursively replace references to IFluidHandles.
@@ -20,6 +20,7 @@ import {
20
20
  type IChannelFactory,
21
21
  IFluidDataStoreRuntime,
22
22
  type IDeltaHandler,
23
+ type IFluidDataStoreRuntimeInternalConfig,
23
24
  } from "@fluidframework/datastore-definitions/internal";
24
25
  import {
25
26
  type IDocumentMessage,
@@ -53,11 +54,11 @@ import {
53
54
  } from "@fluidframework/telemetry-utils/internal";
54
55
  import { v4 as uuid } from "uuid";
55
56
 
57
+ import { GCHandleVisitor } from "./gcHandleVisitor.js";
56
58
  import { SharedObjectHandle } from "./handle.js";
57
59
  import { FluidSerializer, IFluidSerializer } from "./serializer.js";
58
- import { SummarySerializer } from "./summarySerializer.js";
59
60
  import { ISharedObject, ISharedObjectEvents } from "./types.js";
60
- import { makeHandlesSerializable, parseHandles } from "./utils.js";
61
+ import { bindHandles, makeHandlesSerializable, parseHandles } from "./utils.js";
61
62
 
62
63
  /**
63
64
  * Custom telemetry properties used in {@link SharedObjectCore} to instantiate {@link TelemetryEventBatcher} class.
@@ -144,7 +145,7 @@ export abstract class SharedObjectCore<
144
145
 
145
146
  assert(!id.includes("/"), 0x304 /* Id cannot contain slashes */);
146
147
 
147
- this.handle = new SharedObjectHandle(this, id, runtime.IFluidHandleContext);
148
+ this.handle = new SharedObjectHandle(this, id, runtime);
148
149
 
149
150
  this.logger = createChildLogger({
150
151
  logger: runtime.logger,
@@ -459,8 +460,13 @@ export abstract class SharedObjectCore<
459
460
  this.verifyNotClosed();
460
461
  if (this.isAttached()) {
461
462
  // NOTE: We may also be encoding in the ContainerRuntime layer.
462
- // Once the layer-compat window passes we can stop encoding here and only bind
463
- const contentToSubmit = makeHandlesSerializable(content, this.serializer, this.handle);
463
+ // Once the layer-compat window passes we can remove the encoding codepath here altogether
464
+ const onlyBind =
465
+ (this.runtime as IFluidDataStoreRuntimeInternalConfig)
466
+ .submitMessagesWithoutEncodingHandles === true;
467
+ const contentToSubmit = onlyBind
468
+ ? bindHandles(content, this.serializer, this.handle)
469
+ : makeHandlesSerializable(content, this.serializer, this.handle);
464
470
 
465
471
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
466
472
  this.services!.deltaConnection.submit(contentToSubmit, localOpMetadata);
@@ -743,10 +749,10 @@ export abstract class SharedObject<
743
749
 
744
750
  protected get serializer(): IFluidSerializer {
745
751
  /**
746
- * During garbage collection, the SummarySerializer keeps track of IFluidHandles that are serialized. These
752
+ * During garbage collection, the GCHandleVisitor "serializer" keeps track of IFluidHandles that are serialized. These
747
753
  * handles represent references to other Fluid objects.
748
754
  *
749
- * This is fine for now. However, if we implement delay loading in DDss, they may load and de-serialize content
755
+ * This is fine for now. However, if we implement delay loading in DDSes, they may load and de-serialize content
750
756
  * in summarize. When that happens, they may incorrectly hit this assert and we will have to change this.
751
757
  */
752
758
  assert(
@@ -825,8 +831,7 @@ export abstract class SharedObject<
825
831
  * {@inheritDoc @fluidframework/datastore-definitions#(IChannel:interface).getGCData}
826
832
  */
827
833
  public getGCData(fullGC: boolean = false): IGarbageCollectionData {
828
- // Set _isGCing to true. This flag is used to ensure that we only use SummarySerializer to serialize handles
829
- // in this object's data.
834
+ // Set _isGCing to true. This flag is used to ensure that we only use GCHandleVisitor in this codepath and not when trying to truly serialize.
830
835
  assert(
831
836
  !this._isGCing,
832
837
  0x078 /* "Possible re-entrancy! Summary should not already be in progress." */,
@@ -835,11 +840,11 @@ export abstract class SharedObject<
835
840
 
836
841
  let gcData: IGarbageCollectionData;
837
842
  try {
838
- const serializer = new SummarySerializer(this.runtime.channelsRoutingContext);
839
- this.processGCDataCore(serializer);
843
+ const handleVisitor = new GCHandleVisitor(this.runtime.channelsRoutingContext);
844
+ this.processGCDataCore(handleVisitor);
840
845
  // The GC data for this shared object contains a single GC node. The outbound routes of this node are the
841
846
  // routes of handles serialized during summarization.
842
- gcData = { gcNodes: { "/": serializer.getSerializedRoutes() } };
847
+ gcData = { gcNodes: { "/": handleVisitor.getVisitedHandlePaths() } };
843
848
  assert(
844
849
  this._isGCing,
845
850
  0x079 /* "Possible re-entrancy! Summary should have been in progress." */,
@@ -854,6 +859,12 @@ export abstract class SharedObject<
854
859
  /**
855
860
  * Calls the serializer over all data in this object that reference other GC nodes.
856
861
  * Derived classes must override this to provide custom list of references to other GC nodes.
862
+ *
863
+ * @remarks Serialization itself doesn't matter (the result is ignored). We're tapping into the serialization infrastructure
864
+ * as a way to visit all the content in this content that may reference other objects via handle.
865
+ *
866
+ * @param serializer - The "serializer" (more like handle visitor) to use.
867
+ * Implementations should ensure that serialize is called on all handles, as the way to visit them.
857
868
  */
858
869
  protected processGCDataCore(serializer: IFluidSerializer): void {
859
870
  // We run the full summarize logic to get the list of outbound routes from this object. This is a little
package/src/utils.ts CHANGED
@@ -86,16 +86,19 @@ export function createSingleBlobSummary(
86
86
  *
87
87
  * @internal
88
88
  */
89
- export function bindHandles(
90
- value: unknown,
89
+ export function bindHandles<T = unknown>(
90
+ value: T,
91
91
  serializer: IFluidSerializer,
92
92
  bind: IFluidHandle,
93
- ): void {
93
+ ): T {
94
94
  // N.B. AB#7316 this could be made more efficient by writing an ad hoc
95
95
  // implementation that doesn't clone at all. Today the distinction between
96
96
  // this function and `encode` is purely semantic -- encoding both serializes
97
97
  // handles and binds them, but sometimes we only wish to do the latter
98
98
  serializer.encode(value, bind);
99
+
100
+ // Return the input value so this function can be swapped in for makeHandlesSerializable
101
+ return value;
99
102
  }
100
103
 
101
104
  /**
@@ -1,17 +0,0 @@
1
- /*!
2
- * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
- * Licensed under the MIT License.
4
- */
5
- import { type IFluidHandleInternal } from "@fluidframework/core-interfaces/internal";
6
- import type { ISerializedHandle } from "@fluidframework/runtime-utils/internal";
7
- import { FluidSerializer } from "./serializer.js";
8
- /**
9
- * Serializer implementation for serializing handles during summary.
10
- * @internal
11
- */
12
- export declare class SummarySerializer extends FluidSerializer {
13
- private readonly serializedRoutes;
14
- getSerializedRoutes(): string[];
15
- protected bindAndEncodeHandle(handle: IFluidHandleInternal, bind: IFluidHandleInternal): ISerializedHandle;
16
- }
17
- //# sourceMappingURL=summarySerializer.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"summarySerializer.d.ts","sourceRoot":"","sources":["../src/summarySerializer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,KAAK,oBAAoB,EAAE,MAAM,0CAA0C,CAAC;AACrF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wCAAwC,CAAC;AAEhF,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAElD;;;GAGG;AACH,qBAAa,iBAAkB,SAAQ,eAAe;IACrD,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAA0B;IACpD,mBAAmB,IAAI,MAAM,EAAE;IAItC,SAAS,CAAC,mBAAmB,CAC5B,MAAM,EAAE,oBAAoB,EAC5B,IAAI,EAAE,oBAAoB,GACxB,iBAAiB;CAIpB"}
@@ -1,27 +0,0 @@
1
- "use strict";
2
- /*!
3
- * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
4
- * Licensed under the MIT License.
5
- */
6
- Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.SummarySerializer = void 0;
8
- const serializer_js_1 = require("./serializer.js");
9
- /**
10
- * Serializer implementation for serializing handles during summary.
11
- * @internal
12
- */
13
- class SummarySerializer extends serializer_js_1.FluidSerializer {
14
- constructor() {
15
- super(...arguments);
16
- this.serializedRoutes = new Set();
17
- }
18
- getSerializedRoutes() {
19
- return [...this.serializedRoutes];
20
- }
21
- bindAndEncodeHandle(handle, bind) {
22
- this.serializedRoutes.add(handle.absolutePath);
23
- return super.bindAndEncodeHandle(handle, bind);
24
- }
25
- }
26
- exports.SummarySerializer = SummarySerializer;
27
- //# sourceMappingURL=summarySerializer.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"summarySerializer.js","sourceRoot":"","sources":["../src/summarySerializer.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAKH,mDAAkD;AAElD;;;GAGG;AACH,MAAa,iBAAkB,SAAQ,+BAAe;IAAtD;;QACkB,qBAAgB,GAAgB,IAAI,GAAG,EAAE,CAAC;IAY5D,CAAC;IAXO,mBAAmB;QACzB,OAAO,CAAC,GAAG,IAAI,CAAC,gBAAgB,CAAC,CAAC;IACnC,CAAC;IAES,mBAAmB,CAC5B,MAA4B,EAC5B,IAA0B;QAE1B,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;QAC/C,OAAO,KAAK,CAAC,mBAAmB,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAChD,CAAC;CACD;AAbD,8CAaC","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 type { ISerializedHandle } from \"@fluidframework/runtime-utils/internal\";\n\nimport { FluidSerializer } from \"./serializer.js\";\n\n/**\n * Serializer implementation for serializing handles during summary.\n * @internal\n */\nexport class SummarySerializer extends FluidSerializer {\n\tprivate readonly serializedRoutes: Set<string> = new Set();\n\tpublic getSerializedRoutes(): string[] {\n\t\treturn [...this.serializedRoutes];\n\t}\n\n\tprotected bindAndEncodeHandle(\n\t\thandle: IFluidHandleInternal,\n\t\tbind: IFluidHandleInternal,\n\t): ISerializedHandle {\n\t\tthis.serializedRoutes.add(handle.absolutePath);\n\t\treturn super.bindAndEncodeHandle(handle, bind);\n\t}\n}\n"]}
@@ -1,17 +0,0 @@
1
- /*!
2
- * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
- * Licensed under the MIT License.
4
- */
5
- import { type IFluidHandleInternal } from "@fluidframework/core-interfaces/internal";
6
- import type { ISerializedHandle } from "@fluidframework/runtime-utils/internal";
7
- import { FluidSerializer } from "./serializer.js";
8
- /**
9
- * Serializer implementation for serializing handles during summary.
10
- * @internal
11
- */
12
- export declare class SummarySerializer extends FluidSerializer {
13
- private readonly serializedRoutes;
14
- getSerializedRoutes(): string[];
15
- protected bindAndEncodeHandle(handle: IFluidHandleInternal, bind: IFluidHandleInternal): ISerializedHandle;
16
- }
17
- //# sourceMappingURL=summarySerializer.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"summarySerializer.d.ts","sourceRoot":"","sources":["../src/summarySerializer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,KAAK,oBAAoB,EAAE,MAAM,0CAA0C,CAAC;AACrF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wCAAwC,CAAC;AAEhF,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAElD;;;GAGG;AACH,qBAAa,iBAAkB,SAAQ,eAAe;IACrD,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAA0B;IACpD,mBAAmB,IAAI,MAAM,EAAE;IAItC,SAAS,CAAC,mBAAmB,CAC5B,MAAM,EAAE,oBAAoB,EAC5B,IAAI,EAAE,oBAAoB,GACxB,iBAAiB;CAIpB"}
@@ -1,23 +0,0 @@
1
- /*!
2
- * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
- * Licensed under the MIT License.
4
- */
5
- import { FluidSerializer } from "./serializer.js";
6
- /**
7
- * Serializer implementation for serializing handles during summary.
8
- * @internal
9
- */
10
- export class SummarySerializer extends FluidSerializer {
11
- constructor() {
12
- super(...arguments);
13
- this.serializedRoutes = new Set();
14
- }
15
- getSerializedRoutes() {
16
- return [...this.serializedRoutes];
17
- }
18
- bindAndEncodeHandle(handle, bind) {
19
- this.serializedRoutes.add(handle.absolutePath);
20
- return super.bindAndEncodeHandle(handle, bind);
21
- }
22
- }
23
- //# sourceMappingURL=summarySerializer.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"summarySerializer.js","sourceRoot":"","sources":["../src/summarySerializer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAElD;;;GAGG;AACH,MAAM,OAAO,iBAAkB,SAAQ,eAAe;IAAtD;;QACkB,qBAAgB,GAAgB,IAAI,GAAG,EAAE,CAAC;IAY5D,CAAC;IAXO,mBAAmB;QACzB,OAAO,CAAC,GAAG,IAAI,CAAC,gBAAgB,CAAC,CAAC;IACnC,CAAC;IAES,mBAAmB,CAC5B,MAA4B,EAC5B,IAA0B;QAE1B,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;QAC/C,OAAO,KAAK,CAAC,mBAAmB,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAChD,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 type { ISerializedHandle } from \"@fluidframework/runtime-utils/internal\";\n\nimport { FluidSerializer } from \"./serializer.js\";\n\n/**\n * Serializer implementation for serializing handles during summary.\n * @internal\n */\nexport class SummarySerializer extends FluidSerializer {\n\tprivate readonly serializedRoutes: Set<string> = new Set();\n\tpublic getSerializedRoutes(): string[] {\n\t\treturn [...this.serializedRoutes];\n\t}\n\n\tprotected bindAndEncodeHandle(\n\t\thandle: IFluidHandleInternal,\n\t\tbind: IFluidHandleInternal,\n\t): ISerializedHandle {\n\t\tthis.serializedRoutes.add(handle.absolutePath);\n\t\treturn super.bindAndEncodeHandle(handle, bind);\n\t}\n}\n"]}
@@ -1,28 +0,0 @@
1
- /*!
2
- * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
- * Licensed under the MIT License.
4
- */
5
-
6
- import { type IFluidHandleInternal } from "@fluidframework/core-interfaces/internal";
7
- import type { ISerializedHandle } from "@fluidframework/runtime-utils/internal";
8
-
9
- import { FluidSerializer } from "./serializer.js";
10
-
11
- /**
12
- * Serializer implementation for serializing handles during summary.
13
- * @internal
14
- */
15
- export class SummarySerializer extends FluidSerializer {
16
- private readonly serializedRoutes: Set<string> = new Set();
17
- public getSerializedRoutes(): string[] {
18
- return [...this.serializedRoutes];
19
- }
20
-
21
- protected bindAndEncodeHandle(
22
- handle: IFluidHandleInternal,
23
- bind: IFluidHandleInternal,
24
- ): ISerializedHandle {
25
- this.serializedRoutes.add(handle.absolutePath);
26
- return super.bindAndEncodeHandle(handle, bind);
27
- }
28
- }