@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.
- package/dist/attachment.d.ts +2 -7
- package/dist/attachment.d.ts.map +1 -1
- package/dist/attachment.js +2 -4
- package/dist/attachment.js.map +1 -1
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +6 -6
- package/dist/container.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/serializedStateManager.d.ts +11 -8
- package/dist/serializedStateManager.d.ts.map +1 -1
- package/dist/serializedStateManager.js +51 -35
- package/dist/serializedStateManager.js.map +1 -1
- package/dist/utils.d.ts +7 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +41 -23
- package/dist/utils.js.map +1 -1
- package/lib/attachment.d.ts +2 -7
- package/lib/attachment.d.ts.map +1 -1
- package/lib/attachment.js +3 -5
- package/lib/attachment.js.map +1 -1
- package/lib/container.d.ts.map +1 -1
- package/lib/container.js +7 -7
- package/lib/container.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/serializedStateManager.d.ts +11 -8
- package/lib/serializedStateManager.d.ts.map +1 -1
- package/lib/serializedStateManager.js +52 -36
- package/lib/serializedStateManager.js.map +1 -1
- package/lib/utils.d.ts +7 -1
- package/lib/utils.d.ts.map +1 -1
- package/lib/utils.js +39 -22
- package/lib/utils.js.map +1 -1
- package/package.json +11 -11
- package/src/attachment.ts +7 -13
- package/src/container.ts +7 -8
- package/src/packageVersion.ts +1 -1
- package/src/serializedStateManager.ts +68 -40
- 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 {
|
|
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
|
|
63
|
-
|
|
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 =
|
|
75
|
-
|
|
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
|
-
|
|
78
|
+
snapshotTree.blobs[key] = summaryObject.id;
|
|
81
79
|
break;
|
|
82
80
|
}
|
|
83
81
|
case SummaryType.Blob: {
|
|
84
82
|
const blobId = uuid();
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
?
|
|
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
|
-
|
|
102
|
-
|
|
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
|
|
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 =
|
|
158
|
+
const snapshotTreeWithBlobContents = convertSummaryToISnapshot(combinedSummary);
|
|
156
159
|
return snapshotTreeWithBlobContents;
|
|
157
160
|
}
|
|
158
|
-
export const
|
|
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 =
|
|
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
|
|
241
|
+
const snapshot = getISnapshotFromSerializedContainer(parsedContainerState);
|
|
224
242
|
const detachedContainerState = {
|
|
225
243
|
attached: false,
|
|
226
|
-
|
|
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-
|
|
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-
|
|
133
|
-
"@fluidframework/container-definitions": "2.63.0-
|
|
134
|
-
"@fluidframework/core-interfaces": "2.63.0-
|
|
135
|
-
"@fluidframework/core-utils": "2.63.0-
|
|
136
|
-
"@fluidframework/driver-definitions": "2.63.0-
|
|
137
|
-
"@fluidframework/driver-utils": "2.63.0-
|
|
138
|
-
"@fluidframework/telemetry-utils": "2.63.0-
|
|
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-
|
|
150
|
-
"@fluid-internal/mocha-test-setup": "2.63.0-
|
|
151
|
-
"@fluid-private/test-loader-utils": "2.63.0-
|
|
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 {
|
|
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
|
|
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<
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
package/src/packageVersion.ts
CHANGED
|
@@ -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 {
|
|
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
|
|
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
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
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
|
|
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(
|
|
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
|
-
|
|
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
|
|
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 } =
|
|
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
|
-
|
|
313
|
-
|
|
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
|
|
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
|
|
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:
|
|
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
|
-
|
|
455
|
+
snapshot.sequenceNumber === 0,
|
|
430
456
|
0x939 /* trying to set a non attachment snapshot */,
|
|
431
457
|
);
|
|
432
458
|
this.snapshot = {
|
|
433
|
-
...snapshot,
|
|
434
|
-
|
|
435
|
-
|
|
459
|
+
...convertISnapshotToSnapshotWithBlobs(snapshot),
|
|
460
|
+
snapshotSequenceNumber: snapshot.sequenceNumber,
|
|
461
|
+
snapshotFetchedTime: Date.now(),
|
|
436
462
|
};
|
|
437
|
-
this.refreshTimer
|
|
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
|
{
|