@fluidframework/container-loader 2.63.0-359461 → 2.63.0-359734

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 (42) hide show
  1. package/dist/attachment.d.ts +2 -7
  2. package/dist/attachment.d.ts.map +1 -1
  3. package/dist/attachment.js +2 -4
  4. package/dist/attachment.js.map +1 -1
  5. package/dist/container.d.ts.map +1 -1
  6. package/dist/container.js +6 -6
  7. package/dist/container.js.map +1 -1
  8. package/dist/packageVersion.d.ts +1 -1
  9. package/dist/packageVersion.js +1 -1
  10. package/dist/packageVersion.js.map +1 -1
  11. package/dist/serializedStateManager.d.ts +11 -8
  12. package/dist/serializedStateManager.d.ts.map +1 -1
  13. package/dist/serializedStateManager.js +51 -35
  14. package/dist/serializedStateManager.js.map +1 -1
  15. package/dist/utils.d.ts +7 -1
  16. package/dist/utils.d.ts.map +1 -1
  17. package/dist/utils.js +41 -23
  18. package/dist/utils.js.map +1 -1
  19. package/lib/attachment.d.ts +2 -7
  20. package/lib/attachment.d.ts.map +1 -1
  21. package/lib/attachment.js +3 -5
  22. package/lib/attachment.js.map +1 -1
  23. package/lib/container.d.ts.map +1 -1
  24. package/lib/container.js +7 -7
  25. package/lib/container.js.map +1 -1
  26. package/lib/packageVersion.d.ts +1 -1
  27. package/lib/packageVersion.js +1 -1
  28. package/lib/packageVersion.js.map +1 -1
  29. package/lib/serializedStateManager.d.ts +11 -8
  30. package/lib/serializedStateManager.d.ts.map +1 -1
  31. package/lib/serializedStateManager.js +52 -36
  32. package/lib/serializedStateManager.js.map +1 -1
  33. package/lib/utils.d.ts +7 -1
  34. package/lib/utils.d.ts.map +1 -1
  35. package/lib/utils.js +39 -22
  36. package/lib/utils.js.map +1 -1
  37. package/package.json +11 -11
  38. package/src/attachment.ts +7 -13
  39. package/src/container.ts +7 -8
  40. package/src/packageVersion.ts +1 -1
  41. package/src/serializedStateManager.ts +68 -40
  42. package/src/utils.ts +49 -26
package/lib/utils.js CHANGED
@@ -2,7 +2,7 @@
2
2
  * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
3
  * Licensed under the MIT License.
4
4
  */
5
- import { Uint8ArrayToString, bufferToString, stringToBuffer, } from "@fluid-internal/client-utils";
5
+ import { Uint8ArrayToArrayBuffer, bufferToString, stringToBuffer, } from "@fluid-internal/client-utils";
6
6
  import { assert, compareArrays, unreachableCase } from "@fluidframework/core-utils/internal";
7
7
  import { SummaryType } from "@fluidframework/driver-definitions";
8
8
  import { DriverErrorTypes, } from "@fluidframework/driver-definitions/internal";
@@ -59,9 +59,8 @@ export function combineAppAndProtocolSummary(appSummary, protocolSummary) {
59
59
  * to align detached container format with IPendingContainerState
60
60
  * @param summary - ISummaryTree
61
61
  */
62
- function convertSummaryToSnapshotAndBlobs(summary) {
63
- let blobContents = {};
64
- const treeNode = {
62
+ function convertSummaryToISnapshot(summary, blobContents = new Map()) {
63
+ const snapshotTree = {
65
64
  blobs: {},
66
65
  trees: {},
67
66
  id: uuid(),
@@ -71,22 +70,20 @@ function convertSummaryToSnapshotAndBlobs(summary) {
71
70
  for (const [key, summaryObject] of Object.entries(summary.tree)) {
72
71
  switch (summaryObject.type) {
73
72
  case SummaryType.Tree: {
74
- const innerSnapshot = convertSummaryToSnapshotAndBlobs(summaryObject);
75
- treeNode.trees[key] = innerSnapshot.baseSnapshot;
76
- blobContents = { ...blobContents, ...innerSnapshot.snapshotBlobs };
73
+ const innerSnapshot = convertSummaryToISnapshot(summaryObject, blobContents);
74
+ snapshotTree.trees[key] = innerSnapshot.snapshotTree;
77
75
  break;
78
76
  }
79
77
  case SummaryType.Attachment: {
80
- treeNode.blobs[key] = summaryObject.id;
78
+ snapshotTree.blobs[key] = summaryObject.id;
81
79
  break;
82
80
  }
83
81
  case SummaryType.Blob: {
84
82
  const blobId = uuid();
85
- treeNode.blobs[key] = blobId;
86
- const contentString = summaryObject.content instanceof Uint8Array
87
- ? Uint8ArrayToString(summaryObject.content)
88
- : summaryObject.content;
89
- blobContents[blobId] = contentString;
83
+ snapshotTree.blobs[key] = blobId;
84
+ blobContents.set(blobId, summaryObject.content instanceof Uint8Array
85
+ ? Uint8ArrayToArrayBuffer(summaryObject.content)
86
+ : stringToBuffer(summaryObject.content, "utf8"));
90
87
  break;
91
88
  }
92
89
  case SummaryType.Handle: {
@@ -98,8 +95,14 @@ function convertSummaryToSnapshotAndBlobs(summary) {
98
95
  }
99
96
  }
100
97
  }
101
- const pendingSnapshot = { baseSnapshot: treeNode, snapshotBlobs: blobContents };
102
- return pendingSnapshot;
98
+ return {
99
+ blobContents,
100
+ latestSequenceNumber: undefined,
101
+ ops: [],
102
+ sequenceNumber: 0,
103
+ snapshotFormatV: 1,
104
+ snapshotTree,
105
+ };
103
106
  }
104
107
  /**
105
108
  * Converts a snapshot to snapshotInfo with its blob contents
@@ -146,20 +149,20 @@ export function convertSnapshotInfoToSnapshot(snapshotInfo, snapshotSequenceNumb
146
149
  * @param protocolSummaryTree - Protocol Summary Tree
147
150
  * @param appSummaryTree - App Summary Tree
148
151
  */
149
- function convertProtocolAndAppSummaryToSnapshotAndBlobs(protocolSummaryTree, appSummaryTree) {
152
+ function convertProtocolAndAppSummaryToISnapshot(protocolSummaryTree, appSummaryTree) {
150
153
  const combinedSummary = {
151
154
  type: SummaryType.Tree,
152
155
  tree: { ...appSummaryTree.tree },
153
156
  };
154
157
  combinedSummary.tree[".protocol"] = protocolSummaryTree;
155
- const snapshotTreeWithBlobContents = convertSummaryToSnapshotAndBlobs(combinedSummary);
158
+ const snapshotTreeWithBlobContents = convertSummaryToISnapshot(combinedSummary);
156
159
  return snapshotTreeWithBlobContents;
157
160
  }
158
- export const getSnapshotTreeAndBlobsFromSerializedContainer = (detachedContainerSnapshot) => {
161
+ export const getISnapshotFromSerializedContainer = (detachedContainerSnapshot) => {
159
162
  assert(isCombinedAppAndProtocolSummary(detachedContainerSnapshot), 0x8e6 /* Protocol and App summary trees should be present */);
160
163
  const protocolSummaryTree = detachedContainerSnapshot.tree[".protocol"];
161
164
  const appSummaryTree = detachedContainerSnapshot.tree[".app"];
162
- const snapshotTreeWithBlobContents = convertProtocolAndAppSummaryToSnapshotAndBlobs(protocolSummaryTree, appSummaryTree);
165
+ const snapshotTreeWithBlobContents = convertProtocolAndAppSummaryToISnapshot(protocolSummaryTree, appSummaryTree);
163
166
  return snapshotTreeWithBlobContents;
164
167
  };
165
168
  export function getProtocolSnapshotTree(snapshot) {
@@ -205,6 +208,21 @@ function isPendingDetachedContainerState(detachedContainerState) {
205
208
  }
206
209
  return true;
207
210
  }
211
+ /**
212
+ * Converts an ISnapshot to a SnapshotWithBlobs, extracting and serializing its blob contents.
213
+ * @param snapshot - The ISnapshot to convert.
214
+ * @returns A SnapshotWithBlobs containing the base snapshot and serialized blob contents.
215
+ */
216
+ export function convertISnapshotToSnapshotWithBlobs(snapshot) {
217
+ const snapshotBlobs = {};
218
+ for (const [id, blob] of snapshot.blobContents.entries()) {
219
+ snapshotBlobs[id] = bufferToString(blob, "utf8");
220
+ }
221
+ return {
222
+ baseSnapshot: snapshot.snapshotTree,
223
+ snapshotBlobs,
224
+ };
225
+ }
208
226
  /**
209
227
  * Parses the given string into {@link IPendingDetachedContainerState} format,
210
228
  * with validation (if invalid, throws a UsageError).
@@ -220,11 +238,10 @@ export function getDetachedContainerStateFromSerializedContainer(serializedConta
220
238
  // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
221
239
  }
222
240
  else if (isCombinedAppAndProtocolSummary(parsedContainerState)) {
223
- const { baseSnapshot, snapshotBlobs } = getSnapshotTreeAndBlobsFromSerializedContainer(parsedContainerState);
241
+ const snapshot = getISnapshotFromSerializedContainer(parsedContainerState);
224
242
  const detachedContainerState = {
225
243
  attached: false,
226
- baseSnapshot,
227
- snapshotBlobs,
244
+ ...convertISnapshotToSnapshotWithBlobs(snapshot),
228
245
  hasAttachmentBlobs: parsedContainerState.tree[hasBlobsSummaryTree] !== undefined,
229
246
  };
230
247
  return detachedContainerState;
package/lib/utils.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport {\n\tUint8ArrayToString,\n\tbufferToString,\n\tstringToBuffer,\n} from \"@fluid-internal/client-utils\";\nimport { assert, compareArrays, unreachableCase } from \"@fluidframework/core-utils/internal\";\nimport { type ISummaryTree, SummaryType } from \"@fluidframework/driver-definitions\";\nimport {\n\tDriverErrorTypes,\n\ttype IDocumentAttributes,\n\ttype ISnapshotTree,\n\ttype IDocumentStorageService,\n\ttype ISnapshot,\n} from \"@fluidframework/driver-definitions/internal\";\nimport {\n\ttype CombinedAppAndProtocolSummary,\n\ttype DeltaStreamConnectionForbiddenError,\n\tisCombinedAppAndProtocolSummary,\n\treadAndParse,\n} from \"@fluidframework/driver-utils/internal\";\nimport {\n\tLoggingError,\n\tUsageError,\n\ttype IFluidErrorBase,\n} from \"@fluidframework/telemetry-utils/internal\";\nimport { v4 as uuid } from \"uuid\";\n\nimport type { ISerializableBlobContents } from \"./containerStorageAdapter.js\";\nimport type {\n\tIPendingContainerState,\n\tIPendingDetachedContainerState,\n\tISnapshotInfo,\n\tSnapshotWithBlobs,\n} from \"./serializedStateManager.js\";\n\n// This is used when we rehydrate a container from the snapshot. Here we put the blob contents\n// in separate property: blobContents.\nexport interface ISnapshotTreeWithBlobContents extends ISnapshotTree {\n\tblobsContents: { [path: string]: ArrayBufferLike };\n\ttrees: { [path: string]: ISnapshotTreeWithBlobContents };\n}\n\n/**\n * Interface to represent the parsed parts of IResolvedUrl.url to help\n * in getting info about different parts of the url.\n * May not be compatible or relevant for any Url Resolver\n * @legacy @beta\n */\nexport interface IParsedUrl {\n\t/**\n\t * It is combination of tenantid/docId part of the url.\n\t */\n\tid: string;\n\t/**\n\t * It is the deep link path in the url.\n\t */\n\tpath: string;\n\t/**\n\t * Query string part of the url.\n\t */\n\tquery: string;\n\t/**\n\t * Undefined means load latest snapshot, otherwise it's version ID passed to IDocumentStorageService.getVersions()\n\t * to figure out what snapshot to use.\n\t */\n\tversion: string | undefined;\n}\n\n/**\n * Utility api to parse the IResolvedUrl.url into specific parts like querystring, path to get\n * deep link info etc.\n * Warning - This function may not be compatible with any Url Resolver's resolved url. It works\n * with urls of type: protocol://<string>/.../..?<querystring>\n * @param url - This is the IResolvedUrl.url part of the resolved url.\n * @returns The IParsedUrl representing the input URL, or undefined if the format was not supported\n * @legacy @beta\n */\nexport function tryParseCompatibleResolvedUrl(url: string): IParsedUrl | undefined {\n\tconst parsed = new URL(url);\n\tif (typeof parsed.pathname !== \"string\") {\n\t\tthrow new LoggingError(\"Failed to parse pathname\");\n\t}\n\tconst query = parsed.search ?? \"\";\n\tconst regex = /^\\/([^/]*\\/[^/]*)(\\/?.*)$/;\n\tconst match = regex.exec(parsed.pathname);\n\treturn match?.length === 3\n\t\t? {\n\t\t\t\tid: match[1],\n\t\t\t\tpath: match[2],\n\t\t\t\tquery,\n\t\t\t\t// URLSearchParams returns null if the param is not provided.\n\t\t\t\tversion: parsed.searchParams.get(\"version\") ?? undefined,\n\t\t\t}\n\t\t: undefined;\n}\n\n/**\n * Combine the app summary and protocol summary in 1 tree.\n * @param appSummary - Summary of the app.\n * @param protocolSummary - Summary of the protocol.\n * @internal\n */\nexport function combineAppAndProtocolSummary(\n\tappSummary: ISummaryTree,\n\tprotocolSummary: ISummaryTree,\n): CombinedAppAndProtocolSummary {\n\tassert(\n\t\t!isCombinedAppAndProtocolSummary(appSummary),\n\t\t0x5a8 /* app summary is already a combined tree! */,\n\t);\n\tassert(\n\t\t!isCombinedAppAndProtocolSummary(protocolSummary),\n\t\t0x5a9 /* protocol summary is already a combined tree! */,\n\t);\n\tconst createNewSummary: CombinedAppAndProtocolSummary = {\n\t\ttype: SummaryType.Tree,\n\t\ttree: {\n\t\t\t\".protocol\": protocolSummary,\n\t\t\t\".app\": appSummary,\n\t\t},\n\t};\n\treturn createNewSummary;\n}\n\n/**\n * Converts a summary to snapshot tree and separate its blob contents\n * to align detached container format with IPendingContainerState\n * @param summary - ISummaryTree\n */\nfunction convertSummaryToSnapshotAndBlobs(summary: ISummaryTree): SnapshotWithBlobs {\n\tlet blobContents: ISerializableBlobContents = {};\n\tconst treeNode: ISnapshotTree = {\n\t\tblobs: {},\n\t\ttrees: {},\n\t\tid: uuid(),\n\t\tunreferenced: summary.unreferenced,\n\t\tgroupId: summary.groupId,\n\t};\n\tfor (const [key, summaryObject] of Object.entries(summary.tree)) {\n\t\tswitch (summaryObject.type) {\n\t\t\tcase SummaryType.Tree: {\n\t\t\t\tconst innerSnapshot = convertSummaryToSnapshotAndBlobs(summaryObject);\n\t\t\t\ttreeNode.trees[key] = innerSnapshot.baseSnapshot;\n\t\t\t\tblobContents = { ...blobContents, ...innerSnapshot.snapshotBlobs };\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase SummaryType.Attachment: {\n\t\t\t\ttreeNode.blobs[key] = summaryObject.id;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase SummaryType.Blob: {\n\t\t\t\tconst blobId = uuid();\n\t\t\t\ttreeNode.blobs[key] = blobId;\n\t\t\t\tconst contentString: string =\n\t\t\t\t\tsummaryObject.content instanceof Uint8Array\n\t\t\t\t\t\t? Uint8ArrayToString(summaryObject.content)\n\t\t\t\t\t\t: summaryObject.content;\n\t\t\t\tblobContents[blobId] = contentString;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase SummaryType.Handle: {\n\t\t\t\tthrow new LoggingError(\n\t\t\t\t\t\"No handles should be there in summary in detached container!!\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tdefault: {\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any\n\t\t\t\tunreachableCase(summaryObject, `Unknown tree type ${(summaryObject as any).type}`);\n\t\t\t}\n\t\t}\n\t}\n\tconst pendingSnapshot = { baseSnapshot: treeNode, snapshotBlobs: blobContents };\n\treturn pendingSnapshot;\n}\n\n/**\n * Converts a snapshot to snapshotInfo with its blob contents\n * to align detached container format with IPendingContainerState\n *\n * Note, this assumes the ISnapshot sequence number is defined. Otherwise an assert will be thrown\n * @param snapshot - ISnapshot\n */\nexport function convertSnapshotToSnapshotInfo(snapshot: ISnapshot): ISnapshotInfo {\n\tassert(\n\t\tsnapshot.sequenceNumber !== undefined,\n\t\t0x93a /* Snapshot sequence number is missing */,\n\t);\n\tconst snapshotBlobs: ISerializableBlobContents = {};\n\tfor (const [blobId, arrayBufferLike] of snapshot.blobContents.entries()) {\n\t\tsnapshotBlobs[blobId] = bufferToString(arrayBufferLike, \"utf8\");\n\t}\n\treturn {\n\t\tbaseSnapshot: snapshot.snapshotTree,\n\t\tsnapshotBlobs,\n\t\tsnapshotSequenceNumber: snapshot.sequenceNumber,\n\t};\n}\n\n/**\n * Converts a snapshot to snapshotInfo with its blob contents\n * to align detached container format with IPendingContainerState\n *\n * Note, this assumes the ISnapshot sequence number is defined. Otherwise an assert will be thrown\n * @param snapshot - ISnapshot\n */\nexport function convertSnapshotInfoToSnapshot(\n\tsnapshotInfo: ISnapshotInfo,\n\tsnapshotSequenceNumber: number,\n): ISnapshot {\n\tconst blobContents = new Map<string, ArrayBufferLike>();\n\tfor (const [blobId, serializedContent] of Object.entries(snapshotInfo.snapshotBlobs)) {\n\t\tblobContents.set(blobId, stringToBuffer(serializedContent, \"utf8\"));\n\t}\n\treturn {\n\t\tsnapshotTree: snapshotInfo.baseSnapshot,\n\t\tblobContents,\n\t\tops: [],\n\t\tsequenceNumber: snapshotSequenceNumber,\n\t\tlatestSequenceNumber: undefined,\n\t\tsnapshotFormatV: 1,\n\t};\n}\n\n/**\n * Converts summary parts into a SnapshotTree and its blob contents.\n * @param protocolSummaryTree - Protocol Summary Tree\n * @param appSummaryTree - App Summary Tree\n */\nfunction convertProtocolAndAppSummaryToSnapshotAndBlobs(\n\tprotocolSummaryTree: ISummaryTree,\n\tappSummaryTree: ISummaryTree,\n): SnapshotWithBlobs {\n\tconst combinedSummary: ISummaryTree = {\n\t\ttype: SummaryType.Tree,\n\t\ttree: { ...appSummaryTree.tree },\n\t};\n\n\tcombinedSummary.tree[\".protocol\"] = protocolSummaryTree;\n\tconst snapshotTreeWithBlobContents = convertSummaryToSnapshotAndBlobs(combinedSummary);\n\treturn snapshotTreeWithBlobContents;\n}\n\nexport const getSnapshotTreeAndBlobsFromSerializedContainer = (\n\tdetachedContainerSnapshot: ISummaryTree,\n): SnapshotWithBlobs => {\n\tassert(\n\t\tisCombinedAppAndProtocolSummary(detachedContainerSnapshot),\n\t\t0x8e6 /* Protocol and App summary trees should be present */,\n\t);\n\tconst protocolSummaryTree = detachedContainerSnapshot.tree[\".protocol\"];\n\tconst appSummaryTree = detachedContainerSnapshot.tree[\".app\"];\n\tconst snapshotTreeWithBlobContents = convertProtocolAndAppSummaryToSnapshotAndBlobs(\n\t\tprotocolSummaryTree,\n\t\tappSummaryTree,\n\t);\n\treturn snapshotTreeWithBlobContents;\n};\n\nexport function getProtocolSnapshotTree(snapshot: ISnapshotTree): ISnapshotTree {\n\treturn \".protocol\" in snapshot.trees ? snapshot.trees[\".protocol\"] : snapshot;\n}\n\nexport const combineSnapshotTreeAndSnapshotBlobs = (\n\tbaseSnapshot: ISnapshotTree,\n\tsnapshotBlobs: ISerializableBlobContents,\n): ISnapshotTreeWithBlobContents => {\n\tconst blobsContents: { [path: string]: ArrayBufferLike } = {};\n\n\t// Process blobs in the current level\n\tfor (const [, id] of Object.entries(baseSnapshot.blobs)) {\n\t\tif (snapshotBlobs[id] !== undefined) {\n\t\t\tblobsContents[id] = stringToBuffer(snapshotBlobs[id], \"utf8\");\n\t\t}\n\t}\n\n\t// Recursively process trees in the current level\n\tconst trees: { [path: string]: ISnapshotTreeWithBlobContents } = {};\n\tfor (const [path, tree] of Object.entries(baseSnapshot.trees)) {\n\t\ttrees[path] = combineSnapshotTreeAndSnapshotBlobs(tree, snapshotBlobs);\n\t}\n\n\t// Create a new snapshot tree with blob contents and processed trees\n\tconst snapshotTreeWithBlobContents: ISnapshotTreeWithBlobContents = {\n\t\t...baseSnapshot,\n\t\tblobsContents,\n\t\ttrees,\n\t};\n\n\treturn snapshotTreeWithBlobContents;\n};\n\nexport function isDeltaStreamConnectionForbiddenError(\n\terror: unknown,\n): error is DeltaStreamConnectionForbiddenError {\n\treturn (\n\t\ttypeof error === \"object\" &&\n\t\terror !== null &&\n\t\t(error as Partial<IFluidErrorBase>)?.errorType ===\n\t\t\tDriverErrorTypes.deltaStreamConnectionForbidden\n\t);\n}\n\n/**\n * Validates format in parsed string get from detached container\n * serialization using IPendingDetachedContainerState format.\n */\nfunction isPendingDetachedContainerState(\n\tdetachedContainerState: IPendingDetachedContainerState,\n): detachedContainerState is IPendingDetachedContainerState {\n\tif (\n\t\tdetachedContainerState?.attached === undefined ||\n\t\tdetachedContainerState?.baseSnapshot === undefined ||\n\t\tdetachedContainerState?.snapshotBlobs === undefined ||\n\t\tdetachedContainerState?.hasAttachmentBlobs === undefined\n\t) {\n\t\treturn false;\n\t}\n\treturn true;\n}\n\n/**\n * Parses the given string into {@link IPendingDetachedContainerState} format,\n * with validation (if invalid, throws a UsageError).\n * This is the inverse of the JSON.stringify call in {@link Container.serialize}\n */\nexport function getDetachedContainerStateFromSerializedContainer(\n\tserializedContainer: string,\n): IPendingDetachedContainerState {\n\tconst hasBlobsSummaryTree = \".hasAttachmentBlobs\";\n\t// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n\tconst parsedContainerState = JSON.parse(serializedContainer);\n\t// eslint-disable-next-line @typescript-eslint/no-unsafe-argument\n\tif (isPendingDetachedContainerState(parsedContainerState)) {\n\t\treturn parsedContainerState;\n\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-argument\n\t} else if (isCombinedAppAndProtocolSummary(parsedContainerState)) {\n\t\tconst { baseSnapshot, snapshotBlobs } =\n\t\t\tgetSnapshotTreeAndBlobsFromSerializedContainer(parsedContainerState);\n\t\tconst detachedContainerState: IPendingDetachedContainerState = {\n\t\t\tattached: false,\n\t\t\tbaseSnapshot,\n\t\t\tsnapshotBlobs,\n\t\t\thasAttachmentBlobs: parsedContainerState.tree[hasBlobsSummaryTree] !== undefined,\n\t\t};\n\t\treturn detachedContainerState;\n\t} else {\n\t\tthrow new UsageError(\"Cannot rehydrate detached container. Incorrect format\");\n\t}\n}\n\n/**\n * Blindly parses the given string into {@link IPendingContainerState} format.\n * This is the inverse of the JSON.stringify call in {@link SerializedStateManager.getPendingLocalState}\n */\nexport function getAttachedContainerStateFromSerializedContainer(\n\tserializedContainer: string | undefined,\n): IPendingContainerState | undefined {\n\treturn serializedContainer === undefined\n\t\t? undefined\n\t\t: (JSON.parse(serializedContainer) as IPendingContainerState);\n}\n\n/**\n * Ensures only a single instance of the provided async function is running.\n * If there are multiple calls they will all get the same promise to wait on.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport const runSingle = <A extends any[], R>(\n\tfunc: (...args: A) => Promise<R>,\n): ((...args: A) => Promise<R>) => {\n\tlet running:\n\t\t| {\n\t\t\t\targs: A;\n\t\t\t\tresult: Promise<R>;\n\t\t }\n\t\t| undefined;\n\t// don't mark this function async, so we return the same promise,\n\t// rather than one that is wrapped due to async\n\t// eslint-disable-next-line @typescript-eslint/promise-function-async\n\treturn (...args: A): Promise<R> => {\n\t\tif (running !== undefined) {\n\t\t\tif (!compareArrays(running.args, args)) {\n\t\t\t\treturn Promise.reject(\n\t\t\t\t\tnew UsageError(\"Subsequent calls cannot use different arguments.\"),\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn running.result;\n\t\t}\n\t\trunning = { args, result: func(...args).finally(() => (running = undefined)) };\n\t\treturn running.result;\n\t};\n};\n\nexport async function getDocumentAttributes(\n\tstorage: Pick<IDocumentStorageService, \"readBlob\">,\n\ttree: ISnapshotTree | undefined,\n): Promise<IDocumentAttributes> {\n\tif (tree === undefined) {\n\t\treturn {\n\t\t\tminimumSequenceNumber: 0,\n\t\t\tsequenceNumber: 0,\n\t\t};\n\t}\n\n\t// Backward compatibility: old docs would have \".attributes\" instead of \"attributes\"\n\tconst attributesHash =\n\t\t\".protocol\" in tree.trees\n\t\t\t? tree.trees[\".protocol\"].blobs.attributes\n\t\t\t: tree.blobs[\".attributes\"];\n\n\tconst attributes = await readAndParse<IDocumentAttributes>(storage, attributesHash);\n\n\treturn attributes;\n}\n"]}
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport {\n\tUint8ArrayToArrayBuffer,\n\tbufferToString,\n\tstringToBuffer,\n} from \"@fluid-internal/client-utils\";\nimport { assert, compareArrays, unreachableCase } from \"@fluidframework/core-utils/internal\";\nimport { type ISummaryTree, SummaryType } from \"@fluidframework/driver-definitions\";\nimport {\n\tDriverErrorTypes,\n\ttype IDocumentAttributes,\n\ttype ISnapshotTree,\n\ttype IDocumentStorageService,\n\ttype ISnapshot,\n} from \"@fluidframework/driver-definitions/internal\";\nimport {\n\ttype CombinedAppAndProtocolSummary,\n\ttype DeltaStreamConnectionForbiddenError,\n\tisCombinedAppAndProtocolSummary,\n\treadAndParse,\n} from \"@fluidframework/driver-utils/internal\";\nimport {\n\tLoggingError,\n\tUsageError,\n\ttype IFluidErrorBase,\n} from \"@fluidframework/telemetry-utils/internal\";\nimport { v4 as uuid } from \"uuid\";\n\nimport type { ISerializableBlobContents } from \"./containerStorageAdapter.js\";\nimport type {\n\tIPendingContainerState,\n\tIPendingDetachedContainerState,\n\tISnapshotInfo,\n\tSnapshotWithBlobs,\n} from \"./serializedStateManager.js\";\n\n// This is used when we rehydrate a container from the snapshot. Here we put the blob contents\n// in separate property: blobContents.\nexport interface ISnapshotTreeWithBlobContents extends ISnapshotTree {\n\tblobsContents: { [path: string]: ArrayBufferLike };\n\ttrees: { [path: string]: ISnapshotTreeWithBlobContents };\n}\n\n/**\n * Interface to represent the parsed parts of IResolvedUrl.url to help\n * in getting info about different parts of the url.\n * May not be compatible or relevant for any Url Resolver\n * @legacy @beta\n */\nexport interface IParsedUrl {\n\t/**\n\t * It is combination of tenantid/docId part of the url.\n\t */\n\tid: string;\n\t/**\n\t * It is the deep link path in the url.\n\t */\n\tpath: string;\n\t/**\n\t * Query string part of the url.\n\t */\n\tquery: string;\n\t/**\n\t * Undefined means load latest snapshot, otherwise it's version ID passed to IDocumentStorageService.getVersions()\n\t * to figure out what snapshot to use.\n\t */\n\tversion: string | undefined;\n}\n\n/**\n * Utility api to parse the IResolvedUrl.url into specific parts like querystring, path to get\n * deep link info etc.\n * Warning - This function may not be compatible with any Url Resolver's resolved url. It works\n * with urls of type: protocol://<string>/.../..?<querystring>\n * @param url - This is the IResolvedUrl.url part of the resolved url.\n * @returns The IParsedUrl representing the input URL, or undefined if the format was not supported\n * @legacy @beta\n */\nexport function tryParseCompatibleResolvedUrl(url: string): IParsedUrl | undefined {\n\tconst parsed = new URL(url);\n\tif (typeof parsed.pathname !== \"string\") {\n\t\tthrow new LoggingError(\"Failed to parse pathname\");\n\t}\n\tconst query = parsed.search ?? \"\";\n\tconst regex = /^\\/([^/]*\\/[^/]*)(\\/?.*)$/;\n\tconst match = regex.exec(parsed.pathname);\n\treturn match?.length === 3\n\t\t? {\n\t\t\t\tid: match[1],\n\t\t\t\tpath: match[2],\n\t\t\t\tquery,\n\t\t\t\t// URLSearchParams returns null if the param is not provided.\n\t\t\t\tversion: parsed.searchParams.get(\"version\") ?? undefined,\n\t\t\t}\n\t\t: undefined;\n}\n\n/**\n * Combine the app summary and protocol summary in 1 tree.\n * @param appSummary - Summary of the app.\n * @param protocolSummary - Summary of the protocol.\n * @internal\n */\nexport function combineAppAndProtocolSummary(\n\tappSummary: ISummaryTree,\n\tprotocolSummary: ISummaryTree,\n): CombinedAppAndProtocolSummary {\n\tassert(\n\t\t!isCombinedAppAndProtocolSummary(appSummary),\n\t\t0x5a8 /* app summary is already a combined tree! */,\n\t);\n\tassert(\n\t\t!isCombinedAppAndProtocolSummary(protocolSummary),\n\t\t0x5a9 /* protocol summary is already a combined tree! */,\n\t);\n\tconst createNewSummary: CombinedAppAndProtocolSummary = {\n\t\ttype: SummaryType.Tree,\n\t\ttree: {\n\t\t\t\".protocol\": protocolSummary,\n\t\t\t\".app\": appSummary,\n\t\t},\n\t};\n\treturn createNewSummary;\n}\n\n/**\n * Converts a summary to snapshot tree and separate its blob contents\n * to align detached container format with IPendingContainerState\n * @param summary - ISummaryTree\n */\nfunction convertSummaryToISnapshot(\n\tsummary: ISummaryTree,\n\tblobContents = new Map<string, ArrayBuffer>(),\n): ISnapshot {\n\tconst snapshotTree: ISnapshotTree = {\n\t\tblobs: {},\n\t\ttrees: {},\n\t\tid: uuid(),\n\t\tunreferenced: summary.unreferenced,\n\t\tgroupId: summary.groupId,\n\t};\n\n\tfor (const [key, summaryObject] of Object.entries(summary.tree)) {\n\t\tswitch (summaryObject.type) {\n\t\t\tcase SummaryType.Tree: {\n\t\t\t\tconst innerSnapshot = convertSummaryToISnapshot(summaryObject, blobContents);\n\t\t\t\tsnapshotTree.trees[key] = innerSnapshot.snapshotTree;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase SummaryType.Attachment: {\n\t\t\t\tsnapshotTree.blobs[key] = summaryObject.id;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase SummaryType.Blob: {\n\t\t\t\tconst blobId = uuid();\n\t\t\t\tsnapshotTree.blobs[key] = blobId;\n\t\t\t\tblobContents.set(\n\t\t\t\t\tblobId,\n\t\t\t\t\tsummaryObject.content instanceof Uint8Array\n\t\t\t\t\t\t? Uint8ArrayToArrayBuffer(summaryObject.content)\n\t\t\t\t\t\t: stringToBuffer(summaryObject.content, \"utf8\"),\n\t\t\t\t);\n\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase SummaryType.Handle: {\n\t\t\t\tthrow new LoggingError(\n\t\t\t\t\t\"No handles should be there in summary in detached container!!\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tdefault: {\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any\n\t\t\t\tunreachableCase(summaryObject, `Unknown tree type ${(summaryObject as any).type}`);\n\t\t\t}\n\t\t}\n\t}\n\treturn {\n\t\tblobContents,\n\t\tlatestSequenceNumber: undefined,\n\t\tops: [],\n\t\tsequenceNumber: 0,\n\t\tsnapshotFormatV: 1,\n\t\tsnapshotTree,\n\t};\n}\n\n/**\n * Converts a snapshot to snapshotInfo with its blob contents\n * to align detached container format with IPendingContainerState\n *\n * Note, this assumes the ISnapshot sequence number is defined. Otherwise an assert will be thrown\n * @param snapshot - ISnapshot\n */\nexport function convertSnapshotToSnapshotInfo(snapshot: ISnapshot): ISnapshotInfo {\n\tassert(\n\t\tsnapshot.sequenceNumber !== undefined,\n\t\t0x93a /* Snapshot sequence number is missing */,\n\t);\n\tconst snapshotBlobs: ISerializableBlobContents = {};\n\tfor (const [blobId, arrayBufferLike] of snapshot.blobContents.entries()) {\n\t\tsnapshotBlobs[blobId] = bufferToString(arrayBufferLike, \"utf8\");\n\t}\n\treturn {\n\t\tbaseSnapshot: snapshot.snapshotTree,\n\t\tsnapshotBlobs,\n\t\tsnapshotSequenceNumber: snapshot.sequenceNumber,\n\t};\n}\n\n/**\n * Converts a snapshot to snapshotInfo with its blob contents\n * to align detached container format with IPendingContainerState\n *\n * Note, this assumes the ISnapshot sequence number is defined. Otherwise an assert will be thrown\n * @param snapshot - ISnapshot\n */\nexport function convertSnapshotInfoToSnapshot(\n\tsnapshotInfo: ISnapshotInfo,\n\tsnapshotSequenceNumber: number,\n): ISnapshot {\n\tconst blobContents = new Map<string, ArrayBuffer>();\n\tfor (const [blobId, serializedContent] of Object.entries(snapshotInfo.snapshotBlobs)) {\n\t\tblobContents.set(blobId, stringToBuffer(serializedContent, \"utf8\"));\n\t}\n\treturn {\n\t\tsnapshotTree: snapshotInfo.baseSnapshot,\n\t\tblobContents,\n\t\tops: [],\n\t\tsequenceNumber: snapshotSequenceNumber,\n\t\tlatestSequenceNumber: undefined,\n\t\tsnapshotFormatV: 1,\n\t};\n}\n\n/**\n * Converts summary parts into a SnapshotTree and its blob contents.\n * @param protocolSummaryTree - Protocol Summary Tree\n * @param appSummaryTree - App Summary Tree\n */\nfunction convertProtocolAndAppSummaryToISnapshot(\n\tprotocolSummaryTree: ISummaryTree,\n\tappSummaryTree: ISummaryTree,\n): ISnapshot {\n\tconst combinedSummary: ISummaryTree = {\n\t\ttype: SummaryType.Tree,\n\t\ttree: { ...appSummaryTree.tree },\n\t};\n\n\tcombinedSummary.tree[\".protocol\"] = protocolSummaryTree;\n\tconst snapshotTreeWithBlobContents = convertSummaryToISnapshot(combinedSummary);\n\treturn snapshotTreeWithBlobContents;\n}\n\nexport const getISnapshotFromSerializedContainer = (\n\tdetachedContainerSnapshot: ISummaryTree,\n): ISnapshot => {\n\tassert(\n\t\tisCombinedAppAndProtocolSummary(detachedContainerSnapshot),\n\t\t0x8e6 /* Protocol and App summary trees should be present */,\n\t);\n\tconst protocolSummaryTree = detachedContainerSnapshot.tree[\".protocol\"];\n\tconst appSummaryTree = detachedContainerSnapshot.tree[\".app\"];\n\tconst snapshotTreeWithBlobContents = convertProtocolAndAppSummaryToISnapshot(\n\t\tprotocolSummaryTree,\n\t\tappSummaryTree,\n\t);\n\treturn snapshotTreeWithBlobContents;\n};\n\nexport function getProtocolSnapshotTree(snapshot: ISnapshotTree): ISnapshotTree {\n\treturn \".protocol\" in snapshot.trees ? snapshot.trees[\".protocol\"] : snapshot;\n}\n\nexport const combineSnapshotTreeAndSnapshotBlobs = (\n\tbaseSnapshot: ISnapshotTree,\n\tsnapshotBlobs: ISerializableBlobContents,\n): ISnapshotTreeWithBlobContents => {\n\tconst blobsContents: { [path: string]: ArrayBufferLike } = {};\n\n\t// Process blobs in the current level\n\tfor (const [, id] of Object.entries(baseSnapshot.blobs)) {\n\t\tif (snapshotBlobs[id] !== undefined) {\n\t\t\tblobsContents[id] = stringToBuffer(snapshotBlobs[id], \"utf8\");\n\t\t}\n\t}\n\n\t// Recursively process trees in the current level\n\tconst trees: { [path: string]: ISnapshotTreeWithBlobContents } = {};\n\tfor (const [path, tree] of Object.entries(baseSnapshot.trees)) {\n\t\ttrees[path] = combineSnapshotTreeAndSnapshotBlobs(tree, snapshotBlobs);\n\t}\n\n\t// Create a new snapshot tree with blob contents and processed trees\n\tconst snapshotTreeWithBlobContents: ISnapshotTreeWithBlobContents = {\n\t\t...baseSnapshot,\n\t\tblobsContents,\n\t\ttrees,\n\t};\n\n\treturn snapshotTreeWithBlobContents;\n};\n\nexport function isDeltaStreamConnectionForbiddenError(\n\terror: unknown,\n): error is DeltaStreamConnectionForbiddenError {\n\treturn (\n\t\ttypeof error === \"object\" &&\n\t\terror !== null &&\n\t\t(error as Partial<IFluidErrorBase>)?.errorType ===\n\t\t\tDriverErrorTypes.deltaStreamConnectionForbidden\n\t);\n}\n\n/**\n * Validates format in parsed string get from detached container\n * serialization using IPendingDetachedContainerState format.\n */\nfunction isPendingDetachedContainerState(\n\tdetachedContainerState: IPendingDetachedContainerState,\n): detachedContainerState is IPendingDetachedContainerState {\n\tif (\n\t\tdetachedContainerState?.attached === undefined ||\n\t\tdetachedContainerState?.baseSnapshot === undefined ||\n\t\tdetachedContainerState?.snapshotBlobs === undefined ||\n\t\tdetachedContainerState?.hasAttachmentBlobs === undefined\n\t) {\n\t\treturn false;\n\t}\n\treturn true;\n}\n/**\n * Converts an ISnapshot to a SnapshotWithBlobs, extracting and serializing its blob contents.\n * @param snapshot - The ISnapshot to convert.\n * @returns A SnapshotWithBlobs containing the base snapshot and serialized blob contents.\n */\nexport function convertISnapshotToSnapshotWithBlobs(snapshot: ISnapshot): SnapshotWithBlobs {\n\tconst snapshotBlobs: ISerializableBlobContents = {};\n\tfor (const [id, blob] of snapshot.blobContents.entries()) {\n\t\tsnapshotBlobs[id] = bufferToString(blob, \"utf8\");\n\t}\n\treturn {\n\t\tbaseSnapshot: snapshot.snapshotTree,\n\t\tsnapshotBlobs,\n\t};\n}\n\n/**\n * Parses the given string into {@link IPendingDetachedContainerState} format,\n * with validation (if invalid, throws a UsageError).\n * This is the inverse of the JSON.stringify call in {@link Container.serialize}\n */\nexport function getDetachedContainerStateFromSerializedContainer(\n\tserializedContainer: string,\n): IPendingDetachedContainerState {\n\tconst hasBlobsSummaryTree = \".hasAttachmentBlobs\";\n\t// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n\tconst parsedContainerState = JSON.parse(serializedContainer);\n\t// eslint-disable-next-line @typescript-eslint/no-unsafe-argument\n\tif (isPendingDetachedContainerState(parsedContainerState)) {\n\t\treturn parsedContainerState;\n\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-argument\n\t} else if (isCombinedAppAndProtocolSummary(parsedContainerState)) {\n\t\tconst snapshot = getISnapshotFromSerializedContainer(parsedContainerState);\n\t\tconst detachedContainerState: IPendingDetachedContainerState = {\n\t\t\tattached: false,\n\t\t\t...convertISnapshotToSnapshotWithBlobs(snapshot),\n\t\t\thasAttachmentBlobs: parsedContainerState.tree[hasBlobsSummaryTree] !== undefined,\n\t\t};\n\t\treturn detachedContainerState;\n\t} else {\n\t\tthrow new UsageError(\"Cannot rehydrate detached container. Incorrect format\");\n\t}\n}\n\n/**\n * Blindly parses the given string into {@link IPendingContainerState} format.\n * This is the inverse of the JSON.stringify call in {@link SerializedStateManager.getPendingLocalState}\n */\nexport function getAttachedContainerStateFromSerializedContainer(\n\tserializedContainer: string | undefined,\n): IPendingContainerState | undefined {\n\treturn serializedContainer === undefined\n\t\t? undefined\n\t\t: (JSON.parse(serializedContainer) as IPendingContainerState);\n}\n\n/**\n * Ensures only a single instance of the provided async function is running.\n * If there are multiple calls they will all get the same promise to wait on.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport const runSingle = <A extends any[], R>(\n\tfunc: (...args: A) => Promise<R>,\n): ((...args: A) => Promise<R>) => {\n\tlet running:\n\t\t| {\n\t\t\t\targs: A;\n\t\t\t\tresult: Promise<R>;\n\t\t }\n\t\t| undefined;\n\t// don't mark this function async, so we return the same promise,\n\t// rather than one that is wrapped due to async\n\t// eslint-disable-next-line @typescript-eslint/promise-function-async\n\treturn (...args: A): Promise<R> => {\n\t\tif (running !== undefined) {\n\t\t\tif (!compareArrays(running.args, args)) {\n\t\t\t\treturn Promise.reject(\n\t\t\t\t\tnew UsageError(\"Subsequent calls cannot use different arguments.\"),\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn running.result;\n\t\t}\n\t\trunning = { args, result: func(...args).finally(() => (running = undefined)) };\n\t\treturn running.result;\n\t};\n};\n\nexport async function getDocumentAttributes(\n\tstorage: Pick<IDocumentStorageService, \"readBlob\">,\n\ttree: ISnapshotTree | undefined,\n): Promise<IDocumentAttributes> {\n\tif (tree === undefined) {\n\t\treturn {\n\t\t\tminimumSequenceNumber: 0,\n\t\t\tsequenceNumber: 0,\n\t\t};\n\t}\n\n\t// Backward compatibility: old docs would have \".attributes\" instead of \"attributes\"\n\tconst attributesHash =\n\t\t\".protocol\" in tree.trees\n\t\t\t? tree.trees[\".protocol\"].blobs.attributes\n\t\t\t: tree.blobs[\".attributes\"];\n\n\tconst attributes = await readAndParse<IDocumentAttributes>(storage, attributesHash);\n\n\treturn attributes;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluidframework/container-loader",
3
- "version": "2.63.0-359461",
3
+ "version": "2.63.0-359734",
4
4
  "description": "Fluid container loader",
5
5
  "homepage": "https://fluidframework.com",
6
6
  "repository": {
@@ -129,13 +129,13 @@
129
129
  "temp-directory": "nyc/.nyc_output"
130
130
  },
131
131
  "dependencies": {
132
- "@fluid-internal/client-utils": "2.63.0-359461",
133
- "@fluidframework/container-definitions": "2.63.0-359461",
134
- "@fluidframework/core-interfaces": "2.63.0-359461",
135
- "@fluidframework/core-utils": "2.63.0-359461",
136
- "@fluidframework/driver-definitions": "2.63.0-359461",
137
- "@fluidframework/driver-utils": "2.63.0-359461",
138
- "@fluidframework/telemetry-utils": "2.63.0-359461",
132
+ "@fluid-internal/client-utils": "2.63.0-359734",
133
+ "@fluidframework/container-definitions": "2.63.0-359734",
134
+ "@fluidframework/core-interfaces": "2.63.0-359734",
135
+ "@fluidframework/core-utils": "2.63.0-359734",
136
+ "@fluidframework/driver-definitions": "2.63.0-359734",
137
+ "@fluidframework/driver-utils": "2.63.0-359734",
138
+ "@fluidframework/telemetry-utils": "2.63.0-359734",
139
139
  "@types/events_pkg": "npm:@types/events@^3.0.0",
140
140
  "@ungap/structured-clone": "^1.2.0",
141
141
  "debug": "^4.3.4",
@@ -146,9 +146,9 @@
146
146
  "devDependencies": {
147
147
  "@arethetypeswrong/cli": "^0.17.1",
148
148
  "@biomejs/biome": "~1.9.3",
149
- "@fluid-internal/client-utils": "2.63.0-359461",
150
- "@fluid-internal/mocha-test-setup": "2.63.0-359461",
151
- "@fluid-private/test-loader-utils": "2.63.0-359461",
149
+ "@fluid-internal/client-utils": "2.63.0-359734",
150
+ "@fluid-internal/mocha-test-setup": "2.63.0-359734",
151
+ "@fluid-private/test-loader-utils": "2.63.0-359734",
152
152
  "@fluid-tools/build-cli": "^0.58.3",
153
153
  "@fluidframework/build-common": "^2.0.3",
154
154
  "@fluidframework/build-tools": "^0.58.3",
package/src/attachment.ts CHANGED
@@ -6,12 +6,14 @@
6
6
  import { AttachState } from "@fluidframework/container-definitions";
7
7
  import { assert } from "@fluidframework/core-utils/internal";
8
8
  import type { ISummaryTree } from "@fluidframework/driver-definitions";
9
- import type { IDocumentStorageService } from "@fluidframework/driver-definitions/internal";
9
+ import type {
10
+ IDocumentStorageService,
11
+ ISnapshot,
12
+ } from "@fluidframework/driver-definitions/internal";
10
13
  import type { CombinedAppAndProtocolSummary } from "@fluidframework/driver-utils/internal";
11
14
 
12
15
  import type { MemoryDetachedBlobStorage } from "./memoryBlobStorage.js";
13
- import type { SnapshotWithBlobs } from "./serializedStateManager.js";
14
- import { getSnapshotTreeAndBlobsFromSerializedContainer } from "./utils.js";
16
+ import { getISnapshotFromSerializedContainer } from "./utils.js";
15
17
 
16
18
  /**
17
19
  * The default state a newly created detached container will have.
@@ -124,11 +126,6 @@ export interface AttachProcessProps {
124
126
  readonly createAttachmentSummary: (
125
127
  redirectTable?: Map<string, string>,
126
128
  ) => CombinedAppAndProtocolSummary;
127
-
128
- /**
129
- * Whether offline load is enabled or not.
130
- */
131
- readonly offlineLoadEnabled: boolean;
132
129
  }
133
130
 
134
131
  /**
@@ -144,9 +141,8 @@ export const runRetriableAttachProcess = async ({
144
141
  createOrGetStorageService,
145
142
  setAttachmentData,
146
143
  createAttachmentSummary,
147
- offlineLoadEnabled,
148
144
  initialAttachmentData,
149
- }: AttachProcessProps): Promise<SnapshotWithBlobs | undefined> => {
145
+ }: AttachProcessProps): Promise<ISnapshot> => {
150
146
  let currentData: AttachmentData = initialAttachmentData;
151
147
 
152
148
  if (currentData.blobs === undefined) {
@@ -214,9 +210,7 @@ export const runRetriableAttachProcess = async ({
214
210
  });
215
211
  }
216
212
 
217
- const snapshot: SnapshotWithBlobs | undefined = offlineLoadEnabled
218
- ? getSnapshotTreeAndBlobsFromSerializedContainer(currentData.summary)
219
- : undefined;
213
+ const snapshot = getISnapshotFromSerializedContainer(currentData.summary);
220
214
 
221
215
  setAttachmentData(
222
216
  (currentData = {
package/src/container.ts CHANGED
@@ -159,8 +159,9 @@ import {
159
159
  getDetachedContainerStateFromSerializedContainer,
160
160
  getDocumentAttributes,
161
161
  getProtocolSnapshotTree,
162
- getSnapshotTreeAndBlobsFromSerializedContainer,
162
+ getISnapshotFromSerializedContainer,
163
163
  runSingle,
164
+ convertISnapshotToSnapshotWithBlobs,
164
165
  } from "./utils.js";
165
166
 
166
167
  const detachedContainerRefSeqNumber = 0;
@@ -1024,7 +1025,6 @@ export class Container
1024
1025
  this.mc.config.getBoolean("Fluid.Container.enableOfflineFull") ??
1025
1026
  options.enableOfflineLoad === true);
1026
1027
  this.serializedStateManager = new SerializedStateManager(
1027
- pendingLocalState,
1028
1028
  this.subLogger,
1029
1029
  this.storageAdapter,
1030
1030
  offlineLoadEnabled,
@@ -1127,6 +1127,7 @@ export class Container
1127
1127
  this._protocolHandler?.close();
1128
1128
 
1129
1129
  this.connectionStateHandler.dispose();
1130
+ this.serializedStateManager.dispose();
1130
1131
  } catch (newError) {
1131
1132
  this.mc.logger.sendErrorEvent({ eventName: "ContainerCloseException" }, newError);
1132
1133
  }
@@ -1173,6 +1174,7 @@ export class Container
1173
1174
  this._protocolHandler?.close();
1174
1175
 
1175
1176
  this.connectionStateHandler.dispose();
1177
+ this.serializedStateManager.dispose();
1176
1178
 
1177
1179
  const maybeError = error === undefined ? undefined : new Error(error.message);
1178
1180
  this._runtime?.dispose(maybeError);
@@ -1251,16 +1253,14 @@ export class Container
1251
1253
  this.captureProtocolSummary(),
1252
1254
  );
1253
1255
 
1254
- const { baseSnapshot, snapshotBlobs } =
1255
- getSnapshotTreeAndBlobsFromSerializedContainer(combinedSummary);
1256
+ const snapshot = getISnapshotFromSerializedContainer(combinedSummary);
1256
1257
  const pendingRuntimeState =
1257
1258
  attachingData === undefined ? undefined : this.runtime.getPendingLocalState();
1258
1259
  assert(!isPromiseLike(pendingRuntimeState), 0x8e3 /* should not be a promise */);
1259
1260
 
1260
1261
  const detachedContainerState: IPendingDetachedContainerState = {
1261
1262
  attached: false,
1262
- baseSnapshot,
1263
- snapshotBlobs,
1263
+ ...convertISnapshotToSnapshotWithBlobs(snapshot),
1264
1264
  pendingRuntimeState,
1265
1265
  hasAttachmentBlobs:
1266
1266
  this.detachedBlobStorage !== undefined && this.detachedBlobStorage.size > 0,
@@ -1352,7 +1352,6 @@ export class Container
1352
1352
 
1353
1353
  let attachP = runRetriableAttachProcess({
1354
1354
  initialAttachmentData: this.attachmentData,
1355
- offlineLoadEnabled: this.serializedStateManager.offlineLoadEnabled,
1356
1355
  detachedBlobStorage: this.detachedBlobStorage,
1357
1356
  setAttachmentData,
1358
1357
  createAttachmentSummary,
@@ -1670,7 +1669,7 @@ export class Container
1670
1669
 
1671
1670
  // Fetch specified snapshot.
1672
1671
  const { baseSnapshot, version, attributes } =
1673
- await this.serializedStateManager.fetchSnapshot(specifiedVersion);
1672
+ await this.serializedStateManager.fetchSnapshot(specifiedVersion, pendingLocalState);
1674
1673
  const baseSnapshotTree: ISnapshotTree | undefined = getSnapshotTree(baseSnapshot);
1675
1674
  this._loadedFromVersion = version;
1676
1675
 
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/container-loader";
9
- export const pkgVersion = "2.63.0-359461";
9
+ export const pkgVersion = "2.63.0-359734";
@@ -10,6 +10,7 @@ import type {
10
10
  IEvent,
11
11
  ITelemetryBaseLogger,
12
12
  } from "@fluidframework/core-interfaces";
13
+ import type { IDisposable } from "@fluidframework/core-interfaces/internal";
13
14
  import { Timer, assert } from "@fluidframework/core-utils/internal";
14
15
  import {
15
16
  FetchSource,
@@ -33,7 +34,11 @@ import {
33
34
  type ISerializableBlobContents,
34
35
  getBlobContentsFromTree,
35
36
  } from "./containerStorageAdapter.js";
36
- import { convertSnapshotToSnapshotInfo, getDocumentAttributes } from "./utils.js";
37
+ import {
38
+ convertSnapshotToSnapshotInfo,
39
+ convertISnapshotToSnapshotWithBlobs,
40
+ getDocumentAttributes,
41
+ } from "./utils.js";
37
42
 
38
43
  /**
39
44
  * This is very similar to {@link @fluidframework/protocol-definitions/internal#ISnapshot}, but the difference is
@@ -161,7 +166,7 @@ class RefreshPromiseTracker {
161
166
  * as well as the snapshot to be used for serialization.
162
167
  * It also keeps track of container dirty state and which local ops have been processed
163
168
  */
164
- export class SerializedStateManager {
169
+ export class SerializedStateManager implements IDisposable {
165
170
  private readonly processedOps: ISequencedDocumentMessage[] = [];
166
171
  private readonly mc: MonitoringContext;
167
172
  private snapshot: ISnapshotInfo | undefined;
@@ -176,12 +181,13 @@ export class SerializedStateManager {
176
181
  error,
177
182
  ),
178
183
  );
179
- private readonly lastSavedOpSequenceNumber: number = 0;
180
- private readonly refreshTimer: Timer;
184
+ private lastSavedOpSequenceNumber: number = 0;
185
+ private readonly refreshTimer: Timer | undefined;
181
186
  private readonly snapshotRefreshTimeoutMs: number = 60 * 60 * 24 * 1000;
187
+ readonly #snapshotRefreshEnabled: boolean;
188
+ #disposed: boolean = false;
182
189
 
183
190
  /**
184
- * @param pendingLocalState - The pendingLocalState being rehydrated, if any (undefined when loading directly from storage)
185
191
  * @param subLogger - Container's logger to use as parent for our logger
186
192
  * @param storageAdapter - Storage adapter for fetching snapshots
187
193
  * @param _offlineLoadEnabled - Is serializing/rehydrating containers allowed?
@@ -189,7 +195,6 @@ export class SerializedStateManager {
189
195
  * @param containerDirty - Is the container "dirty"? That's the opposite of "saved" - there is pending state that may not have been received yet by the service.
190
196
  */
191
197
  constructor(
192
- private readonly pendingLocalState: IPendingContainerState | undefined,
193
198
  subLogger: ITelemetryBaseLogger,
194
199
  private readonly storageAdapter: ISerializedStateManagerDocumentStorageService,
195
200
  private readonly _offlineLoadEnabled: boolean,
@@ -204,19 +209,29 @@ export class SerializedStateManager {
204
209
  });
205
210
 
206
211
  this.snapshotRefreshTimeoutMs = snapshotRefreshTimeoutMs ?? this.snapshotRefreshTimeoutMs;
207
- this.refreshTimer = new Timer(this.snapshotRefreshTimeoutMs, () =>
208
- this.tryRefreshSnapshot(),
209
- );
210
- // special case handle. Obtaining the last saved op seq num to avoid
211
- // refreshing the snapshot before we have processed it. It could cause
212
- // a subsequent stashing to have a newer snapshot than allowed.
213
- if (pendingLocalState && pendingLocalState.savedOps.length > 0) {
214
- const savedOpsSize = pendingLocalState.savedOps.length;
215
- this.lastSavedOpSequenceNumber =
216
- pendingLocalState.savedOps[savedOpsSize - 1].sequenceNumber;
217
- }
212
+
213
+ this.#snapshotRefreshEnabled =
214
+ (this.mc.config.getBoolean("Fluid.Container.enableOfflineSnapshotRefresh") ??
215
+ this.mc.config.getBoolean("Fluid.Container.enableOfflineFull")) === true;
216
+
217
+ this.refreshTimer = this.#snapshotRefreshEnabled
218
+ ? new Timer(this.snapshotRefreshTimeoutMs, () => this.tryRefreshSnapshot())
219
+ : undefined;
218
220
  containerEvent.on("saved", () => this.updateSnapshotAndProcessedOpsMaybe());
219
221
  }
222
+ public get disposed(): boolean {
223
+ return this.#disposed;
224
+ }
225
+ dispose(): void {
226
+ this.#disposed = true;
227
+ this.refreshTimer?.clear();
228
+ }
229
+
230
+ private verifyNotDisposed(): void {
231
+ if (this.#disposed) {
232
+ throw new Error("SerializedStateManager used after dispose.");
233
+ }
234
+ }
220
235
 
221
236
  public get offlineLoadEnabled(): boolean {
222
237
  return this._offlineLoadEnabled;
@@ -248,15 +263,19 @@ export class SerializedStateManager {
248
263
  * Otherwise, fetch it from storage (according to specifiedVersion if provided).
249
264
  *
250
265
  * @param specifiedVersion - If a version is specified and we don't have pendingLocalState, fetch this version from storage.
251
- * @param supportGetSnapshotApi - a boolean indicating whether to use the fetchISnapshot or fetchISnapshotTree.
266
+ * @param pendingLocalState - The pendingLocalState being rehydrated, if any (undefined when loading directly from storage)
252
267
  * @returns The snapshot to boot the container from
253
268
  */
254
- public async fetchSnapshot(specifiedVersion: string | undefined): Promise<{
269
+ public async fetchSnapshot(
270
+ specifiedVersion: string | undefined,
271
+ pendingLocalState: IPendingContainerState | undefined,
272
+ ): Promise<{
255
273
  baseSnapshot: ISnapshot | ISnapshotTree;
256
274
  version: IVersion | undefined;
257
275
  attributes: IDocumentAttributes;
258
276
  }> {
259
- if (this.pendingLocalState === undefined) {
277
+ this.verifyNotDisposed();
278
+ if (pendingLocalState === undefined) {
260
279
  const { baseSnapshot, version } = await getSnapshot(
261
280
  this.mc,
262
281
  this.storageAdapter,
@@ -276,14 +295,23 @@ export class SerializedStateManager {
276
295
  snapshotBlobs,
277
296
  snapshotSequenceNumber: attributes.sequenceNumber,
278
297
  };
279
- this.refreshTimer.start();
298
+ this.refreshTimer?.start();
280
299
  return attributes.sequenceNumber;
281
300
  }),
282
301
  );
283
302
  }
284
303
  return { baseSnapshot, version, attributes };
285
304
  } else {
286
- const { baseSnapshot, snapshotBlobs } = this.pendingLocalState;
305
+ const { baseSnapshot, snapshotBlobs, savedOps } = pendingLocalState;
306
+
307
+ // special case handle. Obtaining the last saved op seq num to avoid
308
+ // refreshing the snapshot before we have processed it. It could cause
309
+ // a subsequent stashing to have a newer snapshot than allowed.
310
+ if (savedOps.length > 0) {
311
+ const savedOpsSize = savedOps.length;
312
+ this.lastSavedOpSequenceNumber = savedOps[savedOpsSize - 1].sequenceNumber;
313
+ }
314
+
287
315
  const attributes = await getDocumentAttributes(this.storageAdapter, baseSnapshot);
288
316
  this.snapshot = {
289
317
  baseSnapshot,
@@ -309,8 +337,8 @@ export class SerializedStateManager {
309
337
 
310
338
  private tryRefreshSnapshot(): void {
311
339
  if (
312
- (this.mc.config.getBoolean("Fluid.Container.enableOfflineSnapshotRefresh") ??
313
- this.mc.config.getBoolean("Fluid.Container.enableOfflineFull")) === true &&
340
+ this.#snapshotRefreshEnabled &&
341
+ !this.#disposed &&
314
342
  !this.refreshTracker.hasPromise &&
315
343
  this.latestSnapshot === undefined
316
344
  ) {
@@ -332,6 +360,10 @@ export class SerializedStateManager {
332
360
  supportGetSnapshotApi,
333
361
  );
334
362
 
363
+ if (this.#disposed) {
364
+ return -1;
365
+ }
366
+
335
367
  // These are loading groupIds that the containerRuntime has requested over its lifetime.
336
368
  // We will fetch the latest snapshot for the groupIds, which will update storageAdapter.loadedGroupIdSnapshots's cache
337
369
  const downloadedGroupIds = Object.keys(this.storageAdapter.loadedGroupIdSnapshots);
@@ -360,6 +392,7 @@ export class SerializedStateManager {
360
392
  private updateSnapshotAndProcessedOpsMaybe(): number {
361
393
  const snapshotSequenceNumber = this.latestSnapshot?.snapshotSequenceNumber;
362
394
  if (
395
+ this.#disposed ||
363
396
  snapshotSequenceNumber === undefined ||
364
397
  this.processedOps.length === 0 ||
365
398
  this.processedOps[this.processedOps.length - 1].sequenceNumber <
@@ -385,14 +418,14 @@ export class SerializedStateManager {
385
418
  stashedSnapshotSequenceNumber: this.snapshot?.snapshotSequenceNumber,
386
419
  });
387
420
  this.latestSnapshot = undefined;
388
- this.refreshTimer.restart();
421
+ this.refreshTimer?.restart();
389
422
  } else if (snapshotSequenceNumber <= lastProcessedOpSequenceNumber) {
390
423
  // Snapshot seq num is between the first and last processed op.
391
424
  // Remove the ops that are already part of the snapshot
392
425
  this.processedOps.splice(0, snapshotSequenceNumber - firstProcessedOpSequenceNumber + 1);
393
426
  this.snapshot = this.latestSnapshot;
394
427
  this.latestSnapshot = undefined;
395
- this.refreshTimer.restart();
428
+ this.refreshTimer?.restart();
396
429
  this.mc.logger.sendTelemetryEvent({
397
430
  eventName: "SnapshotRefreshed",
398
431
  snapshotSequenceNumber,
@@ -410,31 +443,24 @@ export class SerializedStateManager {
410
443
  * base snapshot when attaching.
411
444
  * @param snapshot - snapshot and blobs collected while attaching (a form of the attach summary)
412
445
  */
413
- public setInitialSnapshot(snapshot: SnapshotWithBlobs | undefined): void {
446
+ public setInitialSnapshot(snapshot: ISnapshot): void {
447
+ this.verifyNotDisposed();
414
448
  if (this.offlineLoadEnabled) {
415
449
  assert(
416
450
  this.snapshot === undefined,
417
451
  0x937 /* inital snapshot should only be defined once */,
418
452
  );
419
- assert(snapshot !== undefined, 0x938 /* attachment snapshot should be defined */);
420
- const { baseSnapshot, snapshotBlobs } = snapshot;
421
- const attributesHash =
422
- ".protocol" in baseSnapshot.trees
423
- ? baseSnapshot.trees[".protocol"].blobs.attributes
424
- : baseSnapshot.blobs[".attributes"];
425
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
426
- const attributes = JSON.parse(snapshotBlobs[attributesHash]);
427
453
  assert(
428
454
  // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
429
- attributes.sequenceNumber === 0,
455
+ snapshot.sequenceNumber === 0,
430
456
  0x939 /* trying to set a non attachment snapshot */,
431
457
  );
432
458
  this.snapshot = {
433
- ...snapshot,
434
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
435
- snapshotSequenceNumber: attributes.sequenceNumber as number,
459
+ ...convertISnapshotToSnapshotWithBlobs(snapshot),
460
+ snapshotSequenceNumber: snapshot.sequenceNumber,
461
+ snapshotFetchedTime: Date.now(),
436
462
  };
437
- this.refreshTimer.start();
463
+ this.refreshTimer?.start();
438
464
  }
439
465
  }
440
466
 
@@ -447,6 +473,8 @@ export class SerializedStateManager {
447
473
  runtime: Pick<IRuntime, "getPendingLocalState">,
448
474
  resolvedUrl: IResolvedUrl,
449
475
  ): Promise<string> {
476
+ this.verifyNotDisposed();
477
+
450
478
  return PerformanceEvent.timedExecAsync(
451
479
  this.mc.logger,
452
480
  {