@fluidframework/container-loader 2.70.0-361788 → 2.70.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/api-report/container-loader.legacy.alpha.api.md +13 -0
- package/dist/container.d.ts +7 -4
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +89 -14
- package/dist/container.js.map +1 -1
- package/dist/containerContext.d.ts +1 -0
- package/dist/containerContext.d.ts.map +1 -1
- package/dist/containerContext.js +1 -0
- package/dist/containerContext.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/legacyAlpha.d.ts +1 -0
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.d.ts.map +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/pendingLocalStateStore.d.ts +84 -0
- package/dist/pendingLocalStateStore.d.ts.map +1 -0
- package/dist/pendingLocalStateStore.js +157 -0
- package/dist/pendingLocalStateStore.js.map +1 -0
- package/dist/protocol.d.ts +1 -0
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +43 -1
- package/dist/protocol.js.map +1 -1
- package/dist/utils.d.ts +1 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +0 -4
- package/dist/utils.js.map +1 -1
- package/lib/container.d.ts +7 -4
- package/lib/container.d.ts.map +1 -1
- package/lib/container.js +90 -15
- package/lib/container.js.map +1 -1
- package/lib/containerContext.d.ts +1 -0
- package/lib/containerContext.d.ts.map +1 -1
- package/lib/containerContext.js +1 -0
- package/lib/containerContext.js.map +1 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/lib/legacyAlpha.d.ts +1 -0
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.d.ts.map +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/pendingLocalStateStore.d.ts +84 -0
- package/lib/pendingLocalStateStore.d.ts.map +1 -0
- package/lib/pendingLocalStateStore.js +153 -0
- package/lib/pendingLocalStateStore.js.map +1 -0
- package/lib/protocol.d.ts +1 -0
- package/lib/protocol.d.ts.map +1 -1
- package/lib/protocol.js +41 -0
- package/lib/protocol.js.map +1 -1
- package/lib/utils.d.ts +1 -0
- package/lib/utils.d.ts.map +1 -1
- package/lib/utils.js +0 -4
- package/lib/utils.js.map +1 -1
- package/package.json +11 -11
- package/src/container.ts +124 -30
- package/src/containerContext.ts +2 -0
- package/src/index.ts +1 -0
- package/src/packageVersion.ts +1 -1
- package/src/pendingLocalStateStore.ts +160 -0
- package/src/protocol.ts +49 -0
- package/src/utils.ts +6 -0
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\tbufferToString,\n\tstringToBuffer,\n\tUint8ArrayToArrayBuffer,\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\tSerializedSnapshotInfo,\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): SerializedSnapshotInfo {\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: SerializedSnapshotInfo,\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: snapshotInfo.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\tblobContents,\n\tsnapshotTree,\n}: Pick<ISnapshot, \"blobContents\" | \"snapshotTree\">): ISnapshotTreeWithBlobContents => {\n\tconst currentTreeBlobs: { [path: string]: ArrayBufferLike } = {};\n\n\t// Process blobs in the current level\n\tfor (const [, id] of Object.entries(snapshotTree.blobs)) {\n\t\tconst blob = blobContents.get(id);\n\t\tif (blob !== undefined) {\n\t\t\tcurrentTreeBlobs[id] = blob;\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(snapshotTree.trees)) {\n\t\ttrees[path] = combineSnapshotTreeAndSnapshotBlobs({ snapshotTree: tree, blobContents });\n\t}\n\n\t// Create a new snapshot tree with blob contents and processed trees\n\tconst snapshot: ISnapshotTreeWithBlobContents = {\n\t\t...snapshotTree,\n\t\tblobsContents: currentTreeBlobs,\n\t\ttrees,\n\t};\n\n\treturn snapshot;\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"]}
|
|
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\tbufferToString,\n\tstringToBuffer,\n\tUint8ArrayToArrayBuffer,\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\tSerializedSnapshotInfo,\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): SerializedSnapshotInfo {\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: SerializedSnapshotInfo,\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: snapshotInfo.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\tblobContents,\n\tsnapshotTree,\n}: Pick<ISnapshot, \"blobContents\" | \"snapshotTree\">): ISnapshotTreeWithBlobContents => {\n\tconst currentTreeBlobs: { [path: string]: ArrayBufferLike } = {};\n\n\t// Process blobs in the current level\n\tfor (const [, id] of Object.entries(snapshotTree.blobs)) {\n\t\tconst blob = blobContents.get(id);\n\t\tif (blob !== undefined) {\n\t\t\tcurrentTreeBlobs[id] = blob;\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(snapshotTree.trees)) {\n\t\ttrees[path] = combineSnapshotTreeAndSnapshotBlobs({ snapshotTree: tree, blobContents });\n\t}\n\n\t// Create a new snapshot tree with blob contents and processed trees\n\tconst snapshot: ISnapshotTreeWithBlobContents = {\n\t\t...snapshotTree,\n\t\tblobsContents: currentTreeBlobs,\n\t\ttrees,\n\t};\n\n\treturn snapshot;\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,\n): IPendingContainerState;\nexport function getAttachedContainerStateFromSerializedContainer(\n\tserializedContainer: string | undefined,\n): IPendingContainerState | undefined;\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.70.0
|
|
3
|
+
"version": "2.70.0",
|
|
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.70.0
|
|
133
|
-
"@fluidframework/container-definitions": "2.70.0
|
|
134
|
-
"@fluidframework/core-interfaces": "2.70.0
|
|
135
|
-
"@fluidframework/core-utils": "2.70.0
|
|
136
|
-
"@fluidframework/driver-definitions": "2.70.0
|
|
137
|
-
"@fluidframework/driver-utils": "2.70.0
|
|
138
|
-
"@fluidframework/telemetry-utils": "2.70.0
|
|
132
|
+
"@fluid-internal/client-utils": "~2.70.0",
|
|
133
|
+
"@fluidframework/container-definitions": "~2.70.0",
|
|
134
|
+
"@fluidframework/core-interfaces": "~2.70.0",
|
|
135
|
+
"@fluidframework/core-utils": "~2.70.0",
|
|
136
|
+
"@fluidframework/driver-definitions": "~2.70.0",
|
|
137
|
+
"@fluidframework/driver-utils": "~2.70.0",
|
|
138
|
+
"@fluidframework/telemetry-utils": "~2.70.0",
|
|
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.70.0
|
|
150
|
-
"@fluid-internal/mocha-test-setup": "2.70.0
|
|
151
|
-
"@fluid-private/test-loader-utils": "2.70.0
|
|
149
|
+
"@fluid-internal/client-utils": "~2.70.0",
|
|
150
|
+
"@fluid-internal/mocha-test-setup": "~2.70.0",
|
|
151
|
+
"@fluid-private/test-loader-utils": "~2.70.0",
|
|
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/container.ts
CHANGED
|
@@ -145,6 +145,7 @@ import {
|
|
|
145
145
|
type ProtocolHandlerBuilder,
|
|
146
146
|
type ProtocolHandlerInternal,
|
|
147
147
|
protocolHandlerShouldProcessSignal,
|
|
148
|
+
wrapProtocolHandlerBuilder,
|
|
148
149
|
} from "./protocol.js";
|
|
149
150
|
import { initQuorumValuesFromCodeDetails } from "./quorum.js";
|
|
150
151
|
import {
|
|
@@ -498,6 +499,7 @@ export class Container
|
|
|
498
499
|
private readonly subLogger: ITelemetryLoggerExt;
|
|
499
500
|
private readonly detachedBlobStorage: MemoryDetachedBlobStorage | undefined;
|
|
500
501
|
private readonly protocolHandlerBuilder: InternalProtocolHandlerBuilder;
|
|
502
|
+
private readonly signalAudience = new Audience();
|
|
501
503
|
private readonly client: IClient;
|
|
502
504
|
|
|
503
505
|
private readonly mc: MonitoringContext;
|
|
@@ -557,12 +559,19 @@ export class Container
|
|
|
557
559
|
// Ideally, we should supply pendingLocalState?.clientId here as well, not in constructor, but it does not matter (at least today)
|
|
558
560
|
this.connectionStateHandler.initProtocol(this.protocolHandler);
|
|
559
561
|
|
|
560
|
-
// Propagate current connection state through the system.
|
|
561
|
-
const readonly = this.readOnlyInfo.readonly ?? false;
|
|
562
562
|
// This call does not look like needed any more, with delaying all connection-related events past loaded phase.
|
|
563
563
|
// Yet, there could be some customer code that would break if we do not deliver it.
|
|
564
564
|
// Will be removed in further PRs with proper changeset.
|
|
565
|
-
|
|
565
|
+
const runtime = this._runtime;
|
|
566
|
+
if (
|
|
567
|
+
runtime !== undefined &&
|
|
568
|
+
// Check for older runtime that may need this call
|
|
569
|
+
!("setConnectionStatus" in runtime) &&
|
|
570
|
+
runtime.disposed === false
|
|
571
|
+
) {
|
|
572
|
+
runtime.setConnectionState(false /* canSendOps */, this.clientId);
|
|
573
|
+
}
|
|
574
|
+
|
|
566
575
|
// Deliver delayed calls to DeltaManager - we ignored "connect" events while loading.
|
|
567
576
|
const cm = this._deltaManager.connectionManager;
|
|
568
577
|
if (cm.connected) {
|
|
@@ -827,20 +836,22 @@ export class Container
|
|
|
827
836
|
// Tracking alternative ways to handle this in AB#4129.
|
|
828
837
|
this.options = { ...options };
|
|
829
838
|
this.scope = scope;
|
|
830
|
-
this.protocolHandlerBuilder =
|
|
839
|
+
this.protocolHandlerBuilder = wrapProtocolHandlerBuilder(
|
|
831
840
|
protocolHandlerBuilder ??
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
841
|
+
((
|
|
842
|
+
attributes: IDocumentAttributes,
|
|
843
|
+
quorumSnapshot: IQuorumSnapshot,
|
|
844
|
+
sendProposal: (key: string, value: unknown) => number,
|
|
845
|
+
): ProtocolHandlerInternal =>
|
|
846
|
+
new ProtocolHandler(
|
|
847
|
+
attributes,
|
|
848
|
+
quorumSnapshot,
|
|
849
|
+
sendProposal,
|
|
850
|
+
new Audience(),
|
|
851
|
+
(clientId: string) => this.clientsWhoShouldHaveLeft.has(clientId),
|
|
852
|
+
)),
|
|
853
|
+
this.signalAudience,
|
|
854
|
+
);
|
|
844
855
|
|
|
845
856
|
// Note that we capture the createProps here so we can replicate the creation call when we want to clone.
|
|
846
857
|
this.clone = async (
|
|
@@ -2118,10 +2129,7 @@ export class Container
|
|
|
2118
2129
|
|
|
2119
2130
|
deltaManager.on("readonly", (readonly) => {
|
|
2120
2131
|
if (this.loaded) {
|
|
2121
|
-
this.
|
|
2122
|
-
this.connectionState === ConnectionState.Connected,
|
|
2123
|
-
readonly,
|
|
2124
|
-
);
|
|
2132
|
+
this.setConnectionStatus(readonly);
|
|
2125
2133
|
}
|
|
2126
2134
|
this.emit("readonly", readonly);
|
|
2127
2135
|
});
|
|
@@ -2223,8 +2231,20 @@ export class Container
|
|
|
2223
2231
|
const clientId = this.connectionStateHandler.clientId;
|
|
2224
2232
|
assert(clientId !== undefined, 0x96e /* there has to be clientId */);
|
|
2225
2233
|
this.protocolHandler.audience.setCurrentClientId(clientId);
|
|
2234
|
+
this.signalAudience.setCurrentClientId(clientId);
|
|
2235
|
+
} else if (this.connectionState === ConnectionState.CatchingUp) {
|
|
2236
|
+
// Signal-based Audience does not wait for ops. So provide clientId
|
|
2237
|
+
// as soon as possible.
|
|
2238
|
+
const clientId = this.connectionStateHandler.pendingClientId;
|
|
2239
|
+
assert(clientId !== undefined, 0xc89 /* catching up without clientId */);
|
|
2240
|
+
this.signalAudience.setCurrentClientId(clientId);
|
|
2226
2241
|
}
|
|
2227
2242
|
|
|
2243
|
+
this.setConnectionStatus(
|
|
2244
|
+
/* readonly */ this.readOnlyInfo.readonly ?? false,
|
|
2245
|
+
/* onlyCallSetConnectionStateIfConnectedOrDisconnected */ true,
|
|
2246
|
+
);
|
|
2247
|
+
|
|
2228
2248
|
// We communicate only transitions to Connected & Disconnected states, skipping all other states.
|
|
2229
2249
|
// This can be changed in the future, for example we likely should add "CatchingUp" event on Container.
|
|
2230
2250
|
if (
|
|
@@ -2236,7 +2256,6 @@ export class Container
|
|
|
2236
2256
|
|
|
2237
2257
|
// Both protocol and context should not be undefined if we got so far.
|
|
2238
2258
|
|
|
2239
|
-
this.setContextConnectedState(connected, this.readOnlyInfo.readonly ?? false);
|
|
2240
2259
|
this.protocolHandler.setConnectionState(connected, this.clientId);
|
|
2241
2260
|
raiseConnectedEvent(
|
|
2242
2261
|
this.mc.logger,
|
|
@@ -2449,6 +2468,7 @@ export class Container
|
|
|
2449
2468
|
storage: this.storageAdapter,
|
|
2450
2469
|
quorum: this.protocolHandler.quorum,
|
|
2451
2470
|
audience: this.protocolHandler.audience,
|
|
2471
|
+
signalAudience: this.signalAudience,
|
|
2452
2472
|
loader,
|
|
2453
2473
|
submitFn: (type, contents, batch, metadata) =>
|
|
2454
2474
|
this.submitContainerMessage(type, contents, batch, metadata),
|
|
@@ -2505,18 +2525,92 @@ export class Container
|
|
|
2505
2525
|
};
|
|
2506
2526
|
|
|
2507
2527
|
/**
|
|
2508
|
-
*
|
|
2509
|
-
* This controls the "connected" state of the ContainerRuntime as well
|
|
2510
|
-
* @param connected - Is the container currently connected?
|
|
2528
|
+
* Send the connected status to the runtime.
|
|
2511
2529
|
* @param readonly - Is the container in readonly mode?
|
|
2530
|
+
* @param onlyCallSetConnectionStateIfConnectedOrDisconnected - If true, only
|
|
2531
|
+
* call older `setConnectionState` on the runtime if the connection state is
|
|
2532
|
+
* either Connected or Disconnected. This exists to preserve older behavior
|
|
2533
|
+
* where the runtime was only notified of these two states.
|
|
2512
2534
|
*/
|
|
2513
|
-
private
|
|
2535
|
+
private setConnectionStatus(
|
|
2536
|
+
readonly: boolean,
|
|
2537
|
+
onlyCallSetConnectionStateIfConnectedOrDisconnected: boolean = false,
|
|
2538
|
+
): void {
|
|
2514
2539
|
if (this._runtime?.disposed === false && this.loaded) {
|
|
2515
|
-
this.runtime.
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2540
|
+
const setConnectionStatus = this.runtime.setConnectionStatus?.bind(this.runtime);
|
|
2541
|
+
if (setConnectionStatus === undefined) {
|
|
2542
|
+
if (
|
|
2543
|
+
!onlyCallSetConnectionStateIfConnectedOrDisconnected ||
|
|
2544
|
+
this.connectionState === ConnectionState.Connected ||
|
|
2545
|
+
this.connectionState === ConnectionState.Disconnected
|
|
2546
|
+
) {
|
|
2547
|
+
this.runtime.setConnectionState(
|
|
2548
|
+
this.connectionState === ConnectionState.Connected &&
|
|
2549
|
+
!readonly /* container can send ops if connected to service and not in readonly mode */,
|
|
2550
|
+
this.clientId,
|
|
2551
|
+
);
|
|
2552
|
+
}
|
|
2553
|
+
} else {
|
|
2554
|
+
const pendingClientConnectionId = this.connectionStateHandler.pendingClientId;
|
|
2555
|
+
const connectionState = this.connectionState;
|
|
2556
|
+
switch (connectionState) {
|
|
2557
|
+
case ConnectionState.EstablishingConnection: {
|
|
2558
|
+
setConnectionStatus({
|
|
2559
|
+
connectionState,
|
|
2560
|
+
canSendOps: false,
|
|
2561
|
+
readonly,
|
|
2562
|
+
});
|
|
2563
|
+
|
|
2564
|
+
break;
|
|
2565
|
+
}
|
|
2566
|
+
case ConnectionState.CatchingUp: {
|
|
2567
|
+
// When catching up, we have a pending clientId, but it
|
|
2568
|
+
// is not usable for ops. Send clientId with canSendOps false.
|
|
2569
|
+
assert(
|
|
2570
|
+
pendingClientConnectionId !== undefined,
|
|
2571
|
+
0xc8a /* catching up without clientId */,
|
|
2572
|
+
);
|
|
2573
|
+
setConnectionStatus({
|
|
2574
|
+
connectionState,
|
|
2575
|
+
pendingClientConnectionId,
|
|
2576
|
+
canSendOps: false,
|
|
2577
|
+
readonly,
|
|
2578
|
+
});
|
|
2579
|
+
|
|
2580
|
+
break;
|
|
2581
|
+
}
|
|
2582
|
+
case ConnectionState.Connected: {
|
|
2583
|
+
// When connected, we have an active clientId. Pass it along
|
|
2584
|
+
// with canSendOps true/false based on readonly.
|
|
2585
|
+
const clientConnectionId = this.clientId;
|
|
2586
|
+
assert(clientConnectionId !== undefined, 0xc8b /* connected without clientId */);
|
|
2587
|
+
assert(
|
|
2588
|
+
clientConnectionId === pendingClientConnectionId,
|
|
2589
|
+
0xc8c /* connected with different clientId than pending */,
|
|
2590
|
+
);
|
|
2591
|
+
setConnectionStatus({
|
|
2592
|
+
connectionState,
|
|
2593
|
+
clientConnectionId,
|
|
2594
|
+
canSendOps: !readonly,
|
|
2595
|
+
readonly,
|
|
2596
|
+
});
|
|
2597
|
+
|
|
2598
|
+
break;
|
|
2599
|
+
}
|
|
2600
|
+
case ConnectionState.Disconnected: {
|
|
2601
|
+
setConnectionStatus({
|
|
2602
|
+
connectionState,
|
|
2603
|
+
priorPendingClientConnectionId: pendingClientConnectionId,
|
|
2604
|
+
priorConnectedClientConnectionId: this.clientId,
|
|
2605
|
+
canSendOps: false,
|
|
2606
|
+
readonly,
|
|
2607
|
+
});
|
|
2608
|
+
|
|
2609
|
+
break;
|
|
2610
|
+
}
|
|
2611
|
+
// No default
|
|
2612
|
+
}
|
|
2613
|
+
}
|
|
2520
2614
|
}
|
|
2521
2615
|
}
|
|
2522
2616
|
|
package/src/containerContext.ts
CHANGED
|
@@ -102,6 +102,7 @@ export class ContainerContext
|
|
|
102
102
|
public readonly storage: IContainerStorageService;
|
|
103
103
|
public readonly quorum: IQuorumClients;
|
|
104
104
|
public readonly audience: IAudience;
|
|
105
|
+
public readonly signalAudience: IAudience;
|
|
105
106
|
public readonly loader: ILoader;
|
|
106
107
|
public readonly submitFn: (
|
|
107
108
|
type: MessageType,
|
|
@@ -182,6 +183,7 @@ export class ContainerContext
|
|
|
182
183
|
this.storage = config.storage;
|
|
183
184
|
this.quorum = config.quorum;
|
|
184
185
|
this.audience = config.audience;
|
|
186
|
+
this.signalAudience = config.signalAudience;
|
|
185
187
|
this.loader = config.loader;
|
|
186
188
|
this.submitFn = config.submitFn;
|
|
187
189
|
this.submitSummaryFn = config.submitSummaryFn;
|
package/src/index.ts
CHANGED
package/src/packageVersion.ts
CHANGED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { ISequencedDocumentMessage } from "@fluidframework/driver-definitions/internal";
|
|
7
|
+
import { UsageError } from "@fluidframework/telemetry-utils/internal";
|
|
8
|
+
|
|
9
|
+
import type {
|
|
10
|
+
IPendingContainerState,
|
|
11
|
+
SerializedSnapshotInfo,
|
|
12
|
+
} from "./serializedStateManager.js";
|
|
13
|
+
import { getAttachedContainerStateFromSerializedContainer } from "./utils.js";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* A Map-like store for managing pending local container states from attached containers.
|
|
17
|
+
* Optimizes storage by deduplicating shared resources across stored states.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* const store = new PendingLocalStateStore<string>();
|
|
22
|
+
*
|
|
23
|
+
* // Store pending state
|
|
24
|
+
* const pendingState = await attachedContainer.getPendingLocalState();
|
|
25
|
+
* store.set("session1", pendingState);
|
|
26
|
+
*
|
|
27
|
+
* // Load from stored state
|
|
28
|
+
* const restored = store.get("session1");
|
|
29
|
+
* const newContainer = await loadFrozenContainerFromPendingState({
|
|
30
|
+
* pendingLocalState: restored,
|
|
31
|
+
* // ... other loader options
|
|
32
|
+
* });
|
|
33
|
+
* ```
|
|
34
|
+
*
|
|
35
|
+
* @remarks
|
|
36
|
+
* Only use with attached containers from the same URL. Only store strings
|
|
37
|
+
* returned by `container.getPendingLocalState()`.
|
|
38
|
+
*
|
|
39
|
+
* @typeParam TKey - The type of keys used to identify stored states
|
|
40
|
+
*
|
|
41
|
+
* @legacy @alpha
|
|
42
|
+
*/
|
|
43
|
+
export class PendingLocalStateStore<TKey> {
|
|
44
|
+
#firstUrl: string | undefined;
|
|
45
|
+
readonly #pendingStates = new Map<TKey, IPendingContainerState>();
|
|
46
|
+
readonly #savedOps: Record<number, ISequencedDocumentMessage> = {};
|
|
47
|
+
readonly #blobs: Record<string, string> = {};
|
|
48
|
+
readonly #loadingGroups: Record<string, SerializedSnapshotInfo> = {};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Removes all stored pending states.
|
|
52
|
+
*/
|
|
53
|
+
clear(): void {
|
|
54
|
+
return this.#pendingStates.clear();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Removes the pending state for the specified key.
|
|
59
|
+
*
|
|
60
|
+
* @param key - The key to remove
|
|
61
|
+
* @returns `true` if the state existed and was removed, `false` otherwise
|
|
62
|
+
*/
|
|
63
|
+
delete(key: TKey): boolean {
|
|
64
|
+
return this.#pendingStates.delete(key);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Retrieves the serialized pending state for the specified key.
|
|
69
|
+
*
|
|
70
|
+
* @param key - The key to retrieve
|
|
71
|
+
* @returns The serialized state as a JSON string, or `undefined` if not found
|
|
72
|
+
*/
|
|
73
|
+
get(key: TKey): string | undefined {
|
|
74
|
+
return JSON.stringify(this.#pendingStates.get(key));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Checks whether a pending state exists for the specified key.
|
|
79
|
+
*/
|
|
80
|
+
has(key: TKey): boolean {
|
|
81
|
+
return this.#pendingStates.has(key);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Stores a pending state from `container.getPendingLocalState()`.
|
|
86
|
+
*
|
|
87
|
+
* @param key - The key to associate with the state
|
|
88
|
+
* @param pendingLocalState - String returned by `getPendingLocalState()` from an attached container
|
|
89
|
+
* @returns This store instance for method chaining
|
|
90
|
+
*
|
|
91
|
+
* @throws When storing states from different container URLs
|
|
92
|
+
*/
|
|
93
|
+
set(key: TKey, pendingLocalState: string): this {
|
|
94
|
+
const state = getAttachedContainerStateFromSerializedContainer(pendingLocalState);
|
|
95
|
+
const { savedOps, snapshotBlobs, loadedGroupIdSnapshots, url } = state;
|
|
96
|
+
|
|
97
|
+
this.#firstUrl ??= url;
|
|
98
|
+
if (this.#firstUrl !== url) {
|
|
99
|
+
throw new UsageError("PendingLocalStateStore can only be used with a single container.");
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
for (let i = 0; i < savedOps.length; i++) {
|
|
103
|
+
savedOps[i] = this.#savedOps[savedOps[i].sequenceNumber] ??= savedOps[i];
|
|
104
|
+
}
|
|
105
|
+
for (const [id, blob] of Object.entries(snapshotBlobs)) {
|
|
106
|
+
snapshotBlobs[id] = this.#blobs[id] ??= blob;
|
|
107
|
+
}
|
|
108
|
+
if (loadedGroupIdSnapshots !== undefined) {
|
|
109
|
+
for (const [id, lg] of Object.entries(loadedGroupIdSnapshots)) {
|
|
110
|
+
if (
|
|
111
|
+
this.#loadingGroups[id] === undefined ||
|
|
112
|
+
lg.snapshotSequenceNumber < this.#loadingGroups[id].snapshotSequenceNumber
|
|
113
|
+
) {
|
|
114
|
+
loadedGroupIdSnapshots[id] = this.#loadingGroups[id] = lg;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
this.#pendingStates.set(key, state);
|
|
120
|
+
return this;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Gets the number of stored pending states.
|
|
125
|
+
*/
|
|
126
|
+
get size(): number {
|
|
127
|
+
return this.#pendingStates.size;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Returns an iterator over [key, serializedState] pairs.
|
|
132
|
+
*/
|
|
133
|
+
entries(): Iterator<[TKey, string]> {
|
|
134
|
+
const iterator = this.#pendingStates.entries();
|
|
135
|
+
return {
|
|
136
|
+
next: (): IteratorResult<[TKey, string]> => {
|
|
137
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
138
|
+
const { done, value } = iterator.next();
|
|
139
|
+
if (done === true) {
|
|
140
|
+
return { done, value: undefined };
|
|
141
|
+
}
|
|
142
|
+
return { done, value: [value[0], JSON.stringify(value[1])] };
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Returns an iterator over the stored keys.
|
|
149
|
+
*/
|
|
150
|
+
keys(): IterableIterator<TKey> {
|
|
151
|
+
return this.#pendingStates.keys();
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Makes the store iterable with `for...of` loops.
|
|
156
|
+
*/
|
|
157
|
+
[Symbol.iterator](): Iterator<[TKey, string]> {
|
|
158
|
+
return this.entries();
|
|
159
|
+
}
|
|
160
|
+
}
|
package/src/protocol.ts
CHANGED
|
@@ -213,3 +213,52 @@ export function protocolHandlerShouldProcessSignal(
|
|
|
213
213
|
}
|
|
214
214
|
return false;
|
|
215
215
|
}
|
|
216
|
+
|
|
217
|
+
export function wrapProtocolHandlerBuilder(
|
|
218
|
+
builder: ProtocolHandlerBuilder,
|
|
219
|
+
signalAudience: IAudienceOwner,
|
|
220
|
+
): InternalProtocolHandlerBuilder {
|
|
221
|
+
return (
|
|
222
|
+
attributes: IDocumentAttributes,
|
|
223
|
+
snapshot: IQuorumSnapshot,
|
|
224
|
+
sendProposal: (key: string, value: unknown) => number,
|
|
225
|
+
): ProtocolHandlerInternal => {
|
|
226
|
+
const baseHandler = builder(attributes, snapshot, sendProposal);
|
|
227
|
+
// Create proxy handler with an overridden processSignal method.
|
|
228
|
+
// Use a Proxy since base may use [dynamic] property getters.
|
|
229
|
+
return new Proxy(baseHandler, {
|
|
230
|
+
get(target, prop, receiver) {
|
|
231
|
+
if (prop === "processSignal") {
|
|
232
|
+
return (message: AudienceSignal) => {
|
|
233
|
+
const innerContent = message.content;
|
|
234
|
+
switch (innerContent.type) {
|
|
235
|
+
case SignalType.Clear: {
|
|
236
|
+
const members = signalAudience.getMembers();
|
|
237
|
+
for (const clientId of members.keys()) {
|
|
238
|
+
signalAudience.removeMember(clientId);
|
|
239
|
+
}
|
|
240
|
+
break;
|
|
241
|
+
}
|
|
242
|
+
case SignalType.ClientJoin: {
|
|
243
|
+
const newClient = innerContent.content;
|
|
244
|
+
signalAudience.addMember(newClient.clientId, newClient.client);
|
|
245
|
+
break;
|
|
246
|
+
}
|
|
247
|
+
case SignalType.ClientLeave: {
|
|
248
|
+
const leftClientId = innerContent.content;
|
|
249
|
+
signalAudience.removeMember(leftClientId);
|
|
250
|
+
break;
|
|
251
|
+
}
|
|
252
|
+
default: {
|
|
253
|
+
break;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
target.processSignal(message);
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
260
|
+
return Reflect.get(target, prop, receiver);
|
|
261
|
+
},
|
|
262
|
+
});
|
|
263
|
+
};
|
|
264
|
+
}
|
package/src/utils.ts
CHANGED
|
@@ -380,6 +380,12 @@ export function getDetachedContainerStateFromSerializedContainer(
|
|
|
380
380
|
* Blindly parses the given string into {@link IPendingContainerState} format.
|
|
381
381
|
* This is the inverse of the JSON.stringify call in {@link SerializedStateManager.getPendingLocalState}
|
|
382
382
|
*/
|
|
383
|
+
export function getAttachedContainerStateFromSerializedContainer(
|
|
384
|
+
serializedContainer: string,
|
|
385
|
+
): IPendingContainerState;
|
|
386
|
+
export function getAttachedContainerStateFromSerializedContainer(
|
|
387
|
+
serializedContainer: string | undefined,
|
|
388
|
+
): IPendingContainerState | undefined;
|
|
383
389
|
export function getAttachedContainerStateFromSerializedContainer(
|
|
384
390
|
serializedContainer: string | undefined,
|
|
385
391
|
): IPendingContainerState | undefined {
|