@fluidframework/container-loader 2.0.0-dev.2.2.0.111723 → 2.0.0-dev.3.1.0.125672
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/.eslintrc.js +18 -21
- package/.mocharc.js +2 -2
- package/README.md +58 -40
- package/api-extractor.json +2 -2
- package/dist/audience.d.ts +0 -1
- package/dist/audience.d.ts.map +1 -1
- package/dist/audience.js +6 -1
- package/dist/audience.js.map +1 -1
- package/dist/catchUpMonitor.d.ts.map +1 -1
- package/dist/catchUpMonitor.js.map +1 -1
- package/dist/collabWindowTracker.d.ts.map +1 -1
- package/dist/collabWindowTracker.js +5 -4
- package/dist/collabWindowTracker.js.map +1 -1
- package/dist/connectionManager.d.ts +5 -5
- package/dist/connectionManager.d.ts.map +1 -1
- package/dist/connectionManager.js +66 -32
- package/dist/connectionManager.js.map +1 -1
- package/dist/connectionState.d.ts.map +1 -1
- package/dist/connectionState.js.map +1 -1
- package/dist/connectionStateHandler.d.ts +3 -3
- package/dist/connectionStateHandler.d.ts.map +1 -1
- package/dist/connectionStateHandler.js +46 -24
- package/dist/connectionStateHandler.js.map +1 -1
- package/dist/container.d.ts +27 -0
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +191 -57
- package/dist/container.js.map +1 -1
- package/dist/containerContext.d.ts +3 -2
- package/dist/containerContext.d.ts.map +1 -1
- package/dist/containerContext.js +11 -6
- package/dist/containerContext.js.map +1 -1
- package/dist/containerStorageAdapter.d.ts.map +1 -1
- package/dist/containerStorageAdapter.js +2 -4
- package/dist/containerStorageAdapter.js.map +1 -1
- package/dist/contracts.d.ts +9 -1
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js.map +1 -1
- package/dist/deltaManager.d.ts +18 -6
- package/dist/deltaManager.d.ts.map +1 -1
- package/dist/deltaManager.js +110 -37
- package/dist/deltaManager.js.map +1 -1
- package/dist/deltaManagerProxy.d.ts.map +1 -1
- package/dist/deltaManagerProxy.js.map +1 -1
- package/dist/deltaQueue.d.ts.map +1 -1
- package/dist/deltaQueue.js +4 -2
- package/dist/deltaQueue.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/loader.d.ts +3 -3
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +16 -8
- package/dist/loader.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/protocol.d.ts.map +1 -1
- package/dist/protocol.js +2 -1
- package/dist/protocol.js.map +1 -1
- package/dist/protocolTreeDocumentStorageService.d.ts.map +1 -1
- package/dist/protocolTreeDocumentStorageService.js.map +1 -1
- package/dist/quorum.d.ts.map +1 -1
- package/dist/quorum.js.map +1 -1
- package/dist/retriableDocumentStorageService.d.ts.map +1 -1
- package/dist/retriableDocumentStorageService.js +6 -2
- package/dist/retriableDocumentStorageService.js.map +1 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +6 -4
- package/dist/utils.js.map +1 -1
- package/lib/audience.d.ts +0 -1
- package/lib/audience.d.ts.map +1 -1
- package/lib/audience.js +6 -1
- package/lib/audience.js.map +1 -1
- package/lib/catchUpMonitor.d.ts.map +1 -1
- package/lib/catchUpMonitor.js.map +1 -1
- package/lib/collabWindowTracker.d.ts.map +1 -1
- package/lib/collabWindowTracker.js +5 -4
- package/lib/collabWindowTracker.js.map +1 -1
- package/lib/connectionManager.d.ts +5 -5
- package/lib/connectionManager.d.ts.map +1 -1
- package/lib/connectionManager.js +68 -34
- package/lib/connectionManager.js.map +1 -1
- package/lib/connectionState.d.ts.map +1 -1
- package/lib/connectionState.js.map +1 -1
- package/lib/connectionStateHandler.d.ts +3 -3
- package/lib/connectionStateHandler.d.ts.map +1 -1
- package/lib/connectionStateHandler.js +46 -24
- package/lib/connectionStateHandler.js.map +1 -1
- package/lib/container.d.ts +27 -0
- package/lib/container.d.ts.map +1 -1
- package/lib/container.js +194 -61
- package/lib/container.js.map +1 -1
- package/lib/containerContext.d.ts +3 -2
- package/lib/containerContext.d.ts.map +1 -1
- package/lib/containerContext.js +11 -6
- package/lib/containerContext.js.map +1 -1
- package/lib/containerStorageAdapter.d.ts.map +1 -1
- package/lib/containerStorageAdapter.js +2 -4
- package/lib/containerStorageAdapter.js.map +1 -1
- package/lib/contracts.d.ts +9 -1
- package/lib/contracts.d.ts.map +1 -1
- package/lib/contracts.js.map +1 -1
- package/lib/deltaManager.d.ts +18 -6
- package/lib/deltaManager.d.ts.map +1 -1
- package/lib/deltaManager.js +112 -39
- package/lib/deltaManager.js.map +1 -1
- package/lib/deltaManagerProxy.d.ts.map +1 -1
- package/lib/deltaManagerProxy.js.map +1 -1
- package/lib/deltaQueue.d.ts.map +1 -1
- package/lib/deltaQueue.js +4 -2
- package/lib/deltaQueue.js.map +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js.map +1 -1
- package/lib/loader.d.ts +3 -3
- package/lib/loader.d.ts.map +1 -1
- package/lib/loader.js +16 -8
- package/lib/loader.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/protocol.d.ts.map +1 -1
- package/lib/protocol.js +2 -1
- package/lib/protocol.js.map +1 -1
- package/lib/protocolTreeDocumentStorageService.d.ts.map +1 -1
- package/lib/protocolTreeDocumentStorageService.js.map +1 -1
- package/lib/quorum.d.ts.map +1 -1
- package/lib/quorum.js.map +1 -1
- package/lib/retriableDocumentStorageService.d.ts.map +1 -1
- package/lib/retriableDocumentStorageService.js +6 -2
- package/lib/retriableDocumentStorageService.js.map +1 -1
- package/lib/utils.d.ts.map +1 -1
- package/lib/utils.js +6 -4
- package/lib/utils.js.map +1 -1
- package/package.json +22 -19
- package/prettier.config.cjs +1 -1
- package/src/audience.ts +52 -42
- package/src/catchUpMonitor.ts +39 -37
- package/src/collabWindowTracker.ts +75 -70
- package/src/connectionManager.ts +1009 -938
- package/src/connectionState.ts +19 -19
- package/src/connectionStateHandler.ts +544 -462
- package/src/container.ts +2040 -1785
- package/src/containerContext.ts +352 -337
- package/src/containerStorageAdapter.ts +163 -153
- package/src/contracts.ts +155 -145
- package/src/deltaManager.ts +1069 -945
- package/src/deltaManagerProxy.ts +143 -137
- package/src/deltaQueue.ts +155 -151
- package/src/index.ts +14 -17
- package/src/loader.ts +427 -422
- package/src/packageVersion.ts +1 -1
- package/src/protocol.ts +93 -87
- package/src/protocolTreeDocumentStorageService.ts +30 -33
- package/src/quorum.ts +34 -34
- package/src/retriableDocumentStorageService.ts +118 -102
- package/src/utils.ts +89 -82
- package/tsconfig.esnext.json +6 -6
- package/tsconfig.json +8 -12
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"retriableDocumentStorageService.js","sourceRoot":"","sources":["../src/retriableDocumentStorageService.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,8BAA8B,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAe/D,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAE5D,MAAM,OAAO,+BAA+B;
|
|
1
|
+
{"version":3,"file":"retriableDocumentStorageService.js","sourceRoot":"","sources":["../src/retriableDocumentStorageService.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 { assert } from \"@fluidframework/common-utils\";\nimport { GenericError } from \"@fluidframework/container-utils\";\nimport {\n\tFetchSource,\n\tIDocumentStorageService,\n\tIDocumentStorageServicePolicies,\n\tISummaryContext,\n} from \"@fluidframework/driver-definitions\";\nimport {\n\tICreateBlobResponse,\n\tISnapshotTree,\n\tISummaryHandle,\n\tISummaryTree,\n\tIVersion,\n} from \"@fluidframework/protocol-definitions\";\nimport { IDisposable, ITelemetryLogger } from \"@fluidframework/common-definitions\";\nimport { runWithRetry } from \"@fluidframework/driver-utils\";\n\nexport class RetriableDocumentStorageService implements IDocumentStorageService, IDisposable {\n\tprivate _disposed = false;\n\tconstructor(\n\t\tprivate readonly internalStorageService: IDocumentStorageService,\n\t\tprivate readonly logger: ITelemetryLogger,\n\t) {}\n\n\tpublic get policies(): IDocumentStorageServicePolicies | undefined {\n\t\treturn this.internalStorageService.policies;\n\t}\n\tpublic get disposed() {\n\t\treturn this._disposed;\n\t}\n\tpublic dispose() {\n\t\tthis._disposed = true;\n\t}\n\n\tpublic get repositoryUrl(): string {\n\t\treturn this.internalStorageService.repositoryUrl;\n\t}\n\n\tpublic async getSnapshotTree(\n\t\tversion?: IVersion,\n\t\tscenarioName?: string,\n\t): Promise<ISnapshotTree | null> {\n\t\treturn this.runWithRetry(\n\t\t\tasync () => this.internalStorageService.getSnapshotTree(version, scenarioName),\n\t\t\t\"storage_getSnapshotTree\",\n\t\t);\n\t}\n\n\tpublic async readBlob(id: string): Promise<ArrayBufferLike> {\n\t\treturn this.runWithRetry(\n\t\t\tasync () => this.internalStorageService.readBlob(id),\n\t\t\t\"storage_readBlob\",\n\t\t);\n\t}\n\n\tpublic async getVersions(\n\t\tversionId: string | null,\n\t\tcount: number,\n\t\tscenarioName?: string,\n\t\tfetchSource?: FetchSource,\n\t): Promise<IVersion[]> {\n\t\treturn this.runWithRetry(\n\t\t\tasync () =>\n\t\t\t\tthis.internalStorageService.getVersions(\n\t\t\t\t\tversionId,\n\t\t\t\t\tcount,\n\t\t\t\t\tscenarioName,\n\t\t\t\t\tfetchSource,\n\t\t\t\t),\n\t\t\t\"storage_getVersions\",\n\t\t);\n\t}\n\n\tpublic async uploadSummaryWithContext(\n\t\tsummary: ISummaryTree,\n\t\tcontext: ISummaryContext,\n\t): Promise<string> {\n\t\t// Not using retry loop here. Couple reasons:\n\t\t// 1. If client lost connectivity, then retry loop will result in uploading stale summary\n\t\t// by stale summarizer after connectivity comes back. It will cause failures for this client and for\n\t\t// real (new) summarizer. This problem in particular should be solved in future by supplying abort handle\n\t\t// on all APIs and caller (ContainerRuntime.submitSummary) aborting call on loss of connectivity\n\t\t// 2. Similar, if we get 429 with retryAfter = 10 minutes, it's likely not the right call to retry summary\n\t\t// upload in 10 minutes - it's better to keep processing ops and retry later. Though caller needs to take\n\t\t// retryAfter into account!\n\t\t// But retry loop is required for creation flow (Container.attach)\n\t\tassert(\n\t\t\t(context.referenceSequenceNumber === 0) === (context.ackHandle === undefined),\n\t\t\t0x251 /* \"creation summary has to have seq=0 && handle === undefined\" */,\n\t\t);\n\t\tif (context.referenceSequenceNumber !== 0) {\n\t\t\treturn this.internalStorageService.uploadSummaryWithContext(summary, context);\n\t\t}\n\n\t\t// Creation flow with attachment blobs - need to do retries!\n\t\treturn this.runWithRetry(\n\t\t\tasync () => this.internalStorageService.uploadSummaryWithContext(summary, context),\n\t\t\t\"storage_uploadSummaryWithContext\",\n\t\t);\n\t}\n\n\tpublic async downloadSummary(handle: ISummaryHandle): Promise<ISummaryTree> {\n\t\treturn this.runWithRetry(\n\t\t\tasync () => this.internalStorageService.downloadSummary(handle),\n\t\t\t\"storage_downloadSummary\",\n\t\t);\n\t}\n\n\tpublic async createBlob(file: ArrayBufferLike): Promise<ICreateBlobResponse> {\n\t\treturn this.runWithRetry(\n\t\t\tasync () => this.internalStorageService.createBlob(file),\n\t\t\t\"storage_createBlob\",\n\t\t);\n\t}\n\n\tprivate checkStorageDisposed(callName: string, error: unknown) {\n\t\tif (this._disposed) {\n\t\t\tthis.logger.sendTelemetryEvent(\n\t\t\t\t{\n\t\t\t\t\teventName: `${callName}_abortedStorageDisposed`,\n\t\t\t\t\tfetchCallName: callName, // fetchCallName matches logs in runWithRetry.ts\n\t\t\t\t},\n\t\t\t\terror,\n\t\t\t);\n\t\t\t// pre-0.58 error message: storageServiceDisposedCannotRetry\n\t\t\tthrow new GenericError(\"Storage Service is disposed. Cannot retry\", {\n\t\t\t\tcanRetry: false,\n\t\t\t});\n\t\t}\n\t\treturn;\n\t}\n\n\tprivate async runWithRetry<T>(api: () => Promise<T>, callName: string): Promise<T> {\n\t\treturn runWithRetry(api, callName, this.logger, {\n\t\t\tonRetry: (_delayInMs: number, error: unknown) =>\n\t\t\t\tthis.checkStorageDisposed(callName, error),\n\t\t});\n\t}\n}\n"]}
|
package/lib/utils.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAUH,OAAO,EAAE,YAAY,EAAE,aAAa,EAAe,MAAM,sCAAsC,CAAC;AAKhG,MAAM,WAAW,6BAA8B,SAAQ,aAAa;
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAUH,OAAO,EAAE,YAAY,EAAE,aAAa,EAAe,MAAM,sCAAsC,CAAC;AAKhG,MAAM,WAAW,6BAA8B,SAAQ,aAAa;IACnE,aAAa,EAAE;QAAE,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,CAAA;KAAE,CAAC;IACnD,KAAK,EAAE;QAAE,CAAC,IAAI,EAAE,MAAM,GAAG,6BAA6B,CAAA;KAAE,CAAC;CACzD;AAED,MAAM,WAAW,UAAU;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;CACnC;AAED,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAW5D;AA0DD;;;;GAIG;AACH,wBAAgB,0CAA0C,CACzD,mBAAmB,EAAE,YAAY,EACjC,cAAc,EAAE,YAAY,GAC1B,6BAA6B,CAW/B;AAID,eAAO,MAAM,sCAAsC,8BAA+B,YAAY,kCAY7F,CAAC;AAEF,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,aAAa,GAAG,aAAa,CAE9E"}
|
package/lib/utils.js
CHANGED
|
@@ -16,7 +16,7 @@ export function parseUrl(url) {
|
|
|
16
16
|
const query = (_a = parsed.search) !== null && _a !== void 0 ? _a : "";
|
|
17
17
|
const regex = /^\/([^/]*\/[^/]*)(\/?.*)$/;
|
|
18
18
|
const match = regex.exec(parsed.pathname);
|
|
19
|
-
return (
|
|
19
|
+
return (match === null || match === void 0 ? void 0 : match.length) === 3
|
|
20
20
|
? { id: match[1], path: match[2], query, version: parsed.query.version }
|
|
21
21
|
: undefined;
|
|
22
22
|
}
|
|
@@ -43,7 +43,8 @@ function convertSummaryToSnapshotWithEmbeddedBlobContents(summary) {
|
|
|
43
43
|
const summaryObject = summary.tree[key];
|
|
44
44
|
switch (summaryObject.type) {
|
|
45
45
|
case SummaryType.Tree: {
|
|
46
|
-
treeNode.trees[key] =
|
|
46
|
+
treeNode.trees[key] =
|
|
47
|
+
convertSummaryToSnapshotWithEmbeddedBlobContents(summaryObject);
|
|
47
48
|
break;
|
|
48
49
|
}
|
|
49
50
|
case SummaryType.Attachment:
|
|
@@ -52,8 +53,9 @@ function convertSummaryToSnapshotWithEmbeddedBlobContents(summary) {
|
|
|
52
53
|
case SummaryType.Blob: {
|
|
53
54
|
const blobId = uuid();
|
|
54
55
|
treeNode.blobs[key] = blobId;
|
|
55
|
-
const contentBuffer = typeof summaryObject.content === "string"
|
|
56
|
-
stringToBuffer(summaryObject.content, "utf8")
|
|
56
|
+
const contentBuffer = typeof summaryObject.content === "string"
|
|
57
|
+
? stringToBuffer(summaryObject.content, "utf8")
|
|
58
|
+
: Uint8ArrayToArrayBuffer(summaryObject.content);
|
|
57
59
|
treeNode.blobsContents[blobId] = contentBuffer;
|
|
58
60
|
break;
|
|
59
61
|
}
|
package/lib/utils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,KAAK,CAAC;AAC5B,OAAO,EAAE,EAAE,IAAI,IAAI,EAAE,MAAM,MAAM,CAAC;AAClC,OAAO,
|
|
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 { parse } from \"url\";\nimport { v4 as uuid } from \"uuid\";\nimport {\n\tassert,\n\tstringToBuffer,\n\tUint8ArrayToArrayBuffer,\n\tunreachableCase,\n} from \"@fluidframework/common-utils\";\nimport { ISummaryTree, ISnapshotTree, SummaryType } from \"@fluidframework/protocol-definitions\";\nimport { LoggingError } from \"@fluidframework/telemetry-utils\";\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\nexport interface IParsedUrl {\n\tid: string;\n\tpath: string;\n\tquery: string;\n\t/**\n\t * Null means do not use snapshots, undefined means load latest snapshot\n\t * otherwise it's version ID passed to IDocumentStorageService.getVersions() to figure out what snapshot to use.\n\t * If needed, can add undefined which is treated by Container.load() as load latest snapshot.\n\t */\n\tversion: string | null | undefined;\n}\n\nexport function parseUrl(url: string): IParsedUrl | undefined {\n\tconst parsed = parse(url, true);\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? { id: match[1], path: match[2], query, version: parsed.query.version as string }\n\t\t: undefined;\n}\n\n/**\n * Converts summary tree (for upload) to snapshot tree (for download).\n * Summary tree blobs contain contents, but snapshot tree blobs normally\n * contain IDs pointing to storage. This will create 2 blob entries in the\n * snapshot tree for each blob in the summary tree. One will be the regular\n * path pointing to a uniquely generated ID. Then there will be another\n * entry with the path as that uniquely generated ID, and value as the\n * blob contents as a base-64 string.\n * @param summary - summary to convert\n */\nfunction convertSummaryToSnapshotWithEmbeddedBlobContents(\n\tsummary: ISummaryTree,\n): ISnapshotTreeWithBlobContents {\n\tconst treeNode: ISnapshotTreeWithBlobContents = {\n\t\tblobs: {},\n\t\tblobsContents: {},\n\t\ttrees: {},\n\t\tid: uuid(),\n\t\tunreferenced: summary.unreferenced,\n\t};\n\tconst keys = Object.keys(summary.tree);\n\tfor (const key of keys) {\n\t\tconst summaryObject = summary.tree[key];\n\n\t\tswitch (summaryObject.type) {\n\t\t\tcase SummaryType.Tree: {\n\t\t\t\ttreeNode.trees[key] =\n\t\t\t\t\tconvertSummaryToSnapshotWithEmbeddedBlobContents(summaryObject);\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\tcase SummaryType.Blob: {\n\t\t\t\tconst blobId = uuid();\n\t\t\t\ttreeNode.blobs[key] = blobId;\n\t\t\t\tconst contentBuffer =\n\t\t\t\t\ttypeof summaryObject.content === \"string\"\n\t\t\t\t\t\t? stringToBuffer(summaryObject.content, \"utf8\")\n\t\t\t\t\t\t: Uint8ArrayToArrayBuffer(summaryObject.content);\n\t\t\t\ttreeNode.blobsContents[blobId] = contentBuffer;\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\tbreak;\n\t\t\tdefault: {\n\t\t\t\tunreachableCase(summaryObject, `Unknown tree type ${(summaryObject as any).type}`);\n\t\t\t}\n\t\t}\n\t}\n\treturn treeNode;\n}\n\n/**\n * Combine and convert protocol and app summary tree to format which is readable by container while rehydrating.\n * @param protocolSummaryTree - Protocol Summary Tree\n * @param appSummaryTree - App Summary Tree\n */\nexport function convertProtocolAndAppSummaryToSnapshotTree(\n\tprotocolSummaryTree: ISummaryTree,\n\tappSummaryTree: ISummaryTree,\n): ISnapshotTreeWithBlobContents {\n\t// Shallow copy is fine, since we are doing a deep clone below.\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 =\n\t\tconvertSummaryToSnapshotWithEmbeddedBlobContents(combinedSummary);\n\treturn snapshotTreeWithBlobContents;\n}\n\n// This function converts the snapshot taken in detached container(by serialize api) to snapshotTree with which\n// a detached container can be rehydrated.\nexport const getSnapshotTreeFromSerializedContainer = (detachedContainerSnapshot: ISummaryTree) => {\n\tconst protocolSummaryTree = detachedContainerSnapshot.tree[\".protocol\"] as ISummaryTree;\n\tconst appSummaryTree = detachedContainerSnapshot.tree[\".app\"] as ISummaryTree;\n\tassert(\n\t\tprotocolSummaryTree !== undefined && appSummaryTree !== undefined,\n\t\t0x1e0 /* \"Protocol and App summary trees should be present\" */,\n\t);\n\tconst snapshotTreeWithBlobContents = convertProtocolAndAppSummaryToSnapshotTree(\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"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fluidframework/container-loader",
|
|
3
|
-
"version": "2.0.0-dev.
|
|
3
|
+
"version": "2.0.0-dev.3.1.0.125672",
|
|
4
4
|
"description": "Fluid container loader",
|
|
5
5
|
"homepage": "https://fluidframework.com",
|
|
6
6
|
"repository": {
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
"eslint": "eslint --format stylish src",
|
|
30
30
|
"eslint:fix": "eslint --format stylish src --fix --fix-type problem,suggestion,layout",
|
|
31
31
|
"format": "npm run prettier:fix",
|
|
32
|
-
"lint": "npm run eslint",
|
|
33
|
-
"lint:fix": "npm run eslint:fix",
|
|
32
|
+
"lint": "npm run prettier && npm run eslint",
|
|
33
|
+
"lint:fix": "npm run prettier:fix && npm run eslint:fix",
|
|
34
34
|
"prettier": "prettier --check . --ignore-path ../../../.prettierignore",
|
|
35
35
|
"prettier:fix": "prettier --write . --ignore-path ../../../.prettierignore",
|
|
36
36
|
"test": "npm run test:mocha",
|
|
@@ -65,34 +65,36 @@
|
|
|
65
65
|
"dependencies": {
|
|
66
66
|
"@fluidframework/common-definitions": "^0.20.1",
|
|
67
67
|
"@fluidframework/common-utils": "^1.0.0",
|
|
68
|
-
"@fluidframework/container-definitions": ">=2.0.0-dev.
|
|
69
|
-
"@fluidframework/container-utils": ">=2.0.0-dev.
|
|
70
|
-
"@fluidframework/core-interfaces": ">=2.0.0-dev.
|
|
71
|
-
"@fluidframework/driver-definitions": ">=2.0.0-dev.
|
|
72
|
-
"@fluidframework/driver-utils": ">=2.0.0-dev.
|
|
68
|
+
"@fluidframework/container-definitions": ">=2.0.0-dev.3.1.0.125672 <2.0.0-dev.4.0.0",
|
|
69
|
+
"@fluidframework/container-utils": ">=2.0.0-dev.3.1.0.125672 <2.0.0-dev.4.0.0",
|
|
70
|
+
"@fluidframework/core-interfaces": ">=2.0.0-dev.3.1.0.125672 <2.0.0-dev.4.0.0",
|
|
71
|
+
"@fluidframework/driver-definitions": ">=2.0.0-dev.3.1.0.125672 <2.0.0-dev.4.0.0",
|
|
72
|
+
"@fluidframework/driver-utils": ">=2.0.0-dev.3.1.0.125672 <2.0.0-dev.4.0.0",
|
|
73
73
|
"@fluidframework/protocol-base": "^0.1038.2000",
|
|
74
74
|
"@fluidframework/protocol-definitions": "^1.1.0",
|
|
75
|
-
"@fluidframework/telemetry-utils": ">=2.0.0-dev.
|
|
75
|
+
"@fluidframework/telemetry-utils": ">=2.0.0-dev.3.1.0.125672 <2.0.0-dev.4.0.0",
|
|
76
76
|
"abort-controller": "^3.0.0",
|
|
77
77
|
"double-ended-queue": "^2.1.0-0",
|
|
78
|
+
"events": "^3.1.0",
|
|
78
79
|
"lodash": "^4.17.21",
|
|
79
80
|
"url": "^0.11.0",
|
|
80
81
|
"uuid": "^8.3.1"
|
|
81
82
|
},
|
|
82
83
|
"devDependencies": {
|
|
83
|
-
"@fluid-tools/build-cli": "^0.
|
|
84
|
+
"@fluid-tools/build-cli": "^0.8.0",
|
|
84
85
|
"@fluidframework/build-common": "^1.1.0",
|
|
85
|
-
"@fluidframework/build-tools": "^0.
|
|
86
|
-
"@fluidframework/container-loader-previous": "npm:@fluidframework/container-loader@2.0.0-internal.
|
|
87
|
-
"@fluidframework/eslint-config-fluid": "^
|
|
88
|
-
"@fluidframework/mocha-test-setup": ">=2.0.0-dev.
|
|
89
|
-
"@fluidframework/test-loader-utils": ">=2.0.0-dev.
|
|
86
|
+
"@fluidframework/build-tools": "^0.8.0",
|
|
87
|
+
"@fluidframework/container-loader-previous": "npm:@fluidframework/container-loader@2.0.0-internal.3.0.0",
|
|
88
|
+
"@fluidframework/eslint-config-fluid": "^2.0.0",
|
|
89
|
+
"@fluidframework/mocha-test-setup": ">=2.0.0-dev.3.1.0.125672 <2.0.0-dev.4.0.0",
|
|
90
|
+
"@fluidframework/test-loader-utils": ">=2.0.0-dev.3.1.0.125672 <2.0.0-dev.4.0.0",
|
|
90
91
|
"@microsoft/api-extractor": "^7.22.2",
|
|
91
92
|
"@rushstack/eslint-config": "^2.5.1",
|
|
92
93
|
"@types/double-ended-queue": "^2.1.0",
|
|
94
|
+
"@types/events": "^3.0.0",
|
|
93
95
|
"@types/lodash": "^4.14.118",
|
|
94
96
|
"@types/mocha": "^9.1.1",
|
|
95
|
-
"@types/node": "^14.18.
|
|
97
|
+
"@types/node": "^14.18.36",
|
|
96
98
|
"@types/sinon": "^7.0.13",
|
|
97
99
|
"concurrently": "^6.2.0",
|
|
98
100
|
"copyfiles": "^2.4.1",
|
|
@@ -106,9 +108,10 @@
|
|
|
106
108
|
"typescript": "~4.5.5"
|
|
107
109
|
},
|
|
108
110
|
"typeValidation": {
|
|
109
|
-
"version": "2.0.0-internal.
|
|
110
|
-
"
|
|
111
|
-
"
|
|
111
|
+
"version": "2.0.0-internal.3.1.0",
|
|
112
|
+
"previousVersionStyle": "~previousMinor",
|
|
113
|
+
"baselineRange": ">=2.0.0-internal.3.0.0 <2.0.0-internal.3.1.0",
|
|
114
|
+
"baselineVersion": "2.0.0-internal.3.0.0",
|
|
112
115
|
"broken": {}
|
|
113
116
|
}
|
|
114
117
|
}
|
package/prettier.config.cjs
CHANGED
package/src/audience.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
import { EventEmitter } from "events";
|
|
6
|
+
import { assert } from "@fluidframework/common-utils";
|
|
6
7
|
import { IAudienceOwner } from "@fluidframework/container-definitions";
|
|
7
8
|
import { IClient } from "@fluidframework/protocol-definitions";
|
|
8
9
|
|
|
@@ -10,51 +11,60 @@ import { IClient } from "@fluidframework/protocol-definitions";
|
|
|
10
11
|
* Audience represents all clients connected to the op stream.
|
|
11
12
|
*/
|
|
12
13
|
export class Audience extends EventEmitter implements IAudienceOwner {
|
|
13
|
-
|
|
14
|
+
private readonly members = new Map<string, IClient>();
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
public on(
|
|
17
|
+
event: "addMember" | "removeMember",
|
|
18
|
+
listener: (clientId: string, client: IClient) => void,
|
|
19
|
+
): this;
|
|
20
|
+
public on(event: string, listener: (...args: any[]) => void): this {
|
|
21
|
+
return super.on(event, listener);
|
|
22
|
+
}
|
|
19
23
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
24
|
+
/**
|
|
25
|
+
* Adds a new client to the audience
|
|
26
|
+
*/
|
|
27
|
+
public addMember(clientId: string, details: IClient) {
|
|
28
|
+
// Given that signal delivery is unreliable process, we might observe same client being added twice
|
|
29
|
+
// In such case we should see exactly same payload (IClient), and should not raise event twice!
|
|
30
|
+
if (this.members.has(clientId)) {
|
|
31
|
+
const client = this.members.get(clientId);
|
|
32
|
+
assert(
|
|
33
|
+
JSON.stringify(client) === JSON.stringify(details),
|
|
34
|
+
0x4b2 /* new client has different payload from existing one */,
|
|
35
|
+
);
|
|
36
|
+
} else {
|
|
37
|
+
this.members.set(clientId, details);
|
|
38
|
+
this.emit("addMember", clientId, details);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
31
41
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
42
|
+
/**
|
|
43
|
+
* Removes a client from the audience. Only emits an event if a client is actually removed
|
|
44
|
+
* @returns if a client was removed from the audience
|
|
45
|
+
*/
|
|
46
|
+
public removeMember(clientId: string): boolean {
|
|
47
|
+
const removedClient = this.members.get(clientId);
|
|
48
|
+
if (removedClient !== undefined) {
|
|
49
|
+
this.members.delete(clientId);
|
|
50
|
+
this.emit("removeMember", clientId, removedClient);
|
|
51
|
+
return true;
|
|
52
|
+
} else {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
46
56
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
57
|
+
/**
|
|
58
|
+
* Retrieves all the members in the audience
|
|
59
|
+
*/
|
|
60
|
+
public getMembers(): Map<string, IClient> {
|
|
61
|
+
return new Map(this.members);
|
|
62
|
+
}
|
|
53
63
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
64
|
+
/**
|
|
65
|
+
* Retrieves a specific member of the audience
|
|
66
|
+
*/
|
|
67
|
+
public getMember(clientId: string): IClient | undefined {
|
|
68
|
+
return this.members.get(clientId);
|
|
69
|
+
}
|
|
60
70
|
}
|
package/src/catchUpMonitor.ts
CHANGED
|
@@ -19,41 +19,43 @@ export type ICatchUpMonitor = IDisposable;
|
|
|
19
19
|
* that were known at the time the monitor was created.
|
|
20
20
|
*/
|
|
21
21
|
export class CatchUpMonitor implements ICatchUpMonitor {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
22
|
+
private readonly targetSeqNumber: number;
|
|
23
|
+
private caughtUp: boolean = false;
|
|
24
|
+
|
|
25
|
+
private readonly opHandler = (message: Pick<ISequencedDocumentMessage, "sequenceNumber">) => {
|
|
26
|
+
if (!this.caughtUp && message.sequenceNumber >= this.targetSeqNumber) {
|
|
27
|
+
this.caughtUp = true;
|
|
28
|
+
this.listener();
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Create the CatchUpMonitor, setting the target sequence number to wait for based on DeltaManager's current state.
|
|
34
|
+
*/
|
|
35
|
+
constructor(
|
|
36
|
+
private readonly deltaManager: IDeltaManager<any, any>,
|
|
37
|
+
private readonly listener: CaughtUpListener,
|
|
38
|
+
) {
|
|
39
|
+
this.targetSeqNumber = this.deltaManager.lastKnownSeqNumber;
|
|
40
|
+
|
|
41
|
+
assert(
|
|
42
|
+
this.targetSeqNumber >= this.deltaManager.lastSequenceNumber,
|
|
43
|
+
0x37c /* Cannot wait for seqNumber below last processed sequence number */,
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
this.deltaManager.on("op", this.opHandler);
|
|
47
|
+
|
|
48
|
+
// Simulate the last processed op to set caughtUp in case we already are
|
|
49
|
+
this.opHandler({ sequenceNumber: this.deltaManager.lastSequenceNumber });
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
public disposed: boolean = false;
|
|
53
|
+
public dispose() {
|
|
54
|
+
if (this.disposed) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
this.disposed = true;
|
|
58
|
+
|
|
59
|
+
this.deltaManager.off("op", this.opHandler);
|
|
60
|
+
}
|
|
59
61
|
}
|
|
@@ -30,82 +30,87 @@ const defaultNoopCountFrequency = 50;
|
|
|
30
30
|
// 2. If there are more than 50 ops received without sending any ops, send noop to keep collab window small.
|
|
31
31
|
// Note that system ops (including noops themselves) are excluded, so it's 1 noop per 50 real ops.
|
|
32
32
|
export class CollabWindowTracker {
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
private opsCountSinceNoop = 0;
|
|
34
|
+
private readonly timer: Timer | undefined;
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
36
|
+
constructor(
|
|
37
|
+
private readonly submit: (type: MessageType) => void,
|
|
38
|
+
NoopTimeFrequency: number = defaultNoopTimeFrequency,
|
|
39
|
+
private readonly NoopCountFrequency: number = defaultNoopCountFrequency,
|
|
40
|
+
) {
|
|
41
|
+
if (NoopTimeFrequency !== Infinity) {
|
|
42
|
+
this.timer = new Timer(NoopTimeFrequency, () => {
|
|
43
|
+
// Can get here due to this.stopSequenceNumberUpdate() not resetting timer.
|
|
44
|
+
// Also timer callback can fire even after timer cancellation if it was queued before cancellation.
|
|
45
|
+
if (this.opsCountSinceNoop !== 0) {
|
|
46
|
+
this.submitNoop(false /* immediate */);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
52
|
+
/**
|
|
53
|
+
* Schedules as ack to the server to update the reference sequence number
|
|
54
|
+
*/
|
|
55
|
+
public scheduleSequenceNumberUpdate(
|
|
56
|
+
message: ISequencedDocumentMessage,
|
|
57
|
+
immediateNoOp: boolean,
|
|
58
|
+
): void {
|
|
59
|
+
// While processing a message, an immediate no-op can be requested.
|
|
60
|
+
// i.e. to expedite approve or commit phase of quorum.
|
|
61
|
+
if (immediateNoOp) {
|
|
62
|
+
this.submitNoop(true /* immediate */);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
62
65
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
66
|
+
// We don't acknowledge no-ops to avoid acknowledgement cycles (i.e. ack the MSN
|
|
67
|
+
// update, which updates the MSN, then ack the update, etc...).
|
|
68
|
+
// Intent here is for runtime (and DDSes) not to keep too much tracking state / memory
|
|
69
|
+
// due to runtime ops from other clients.
|
|
70
|
+
if (!isRuntimeMessage(message)) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
70
73
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
74
|
+
this.opsCountSinceNoop++;
|
|
75
|
+
if (this.opsCountSinceNoop === this.NoopCountFrequency) {
|
|
76
|
+
// Ensure we only send noop after a batch of many ops is processed
|
|
77
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
78
|
+
Promise.resolve().then(() => {
|
|
79
|
+
if (this.opsCountSinceNoop >= this.NoopCountFrequency) {
|
|
80
|
+
this.submitNoop(false /* immediate */);
|
|
81
|
+
// reset count now that all ops are processed
|
|
82
|
+
this.opsCountSinceNoop = 0;
|
|
83
|
+
}
|
|
84
|
+
return;
|
|
85
|
+
});
|
|
86
|
+
}
|
|
84
87
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
88
|
+
if (this.timer !== undefined) {
|
|
89
|
+
if (this.opsCountSinceNoop === 1) {
|
|
90
|
+
this.timer.restart();
|
|
91
|
+
}
|
|
89
92
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
+
assert(this.timer.hasTimer, 0x242 /* "has timer" */);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
93
96
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
97
|
+
private submitNoop(immediate: boolean) {
|
|
98
|
+
// Anything other than null is immediate noop
|
|
99
|
+
// ADO:1385: Remove cast and use MessageType once definition changes propagate
|
|
100
|
+
this.submit(immediate ? (MessageType2.Accept as unknown as MessageType) : MessageType.NoOp);
|
|
101
|
+
assert(
|
|
102
|
+
this.opsCountSinceNoop === 0,
|
|
103
|
+
0x243 /* "stopSequenceNumberUpdate should be called as result of sending any op!" */,
|
|
104
|
+
);
|
|
105
|
+
}
|
|
101
106
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
107
|
+
public stopSequenceNumberUpdate(): void {
|
|
108
|
+
this.opsCountSinceNoop = 0;
|
|
109
|
+
// Ideally, we cancel timer here. But that will result in too often set/reset cycle if this client
|
|
110
|
+
// keeps sending ops. In most cases it's actually better to let it expire (at most - 4 times per second)
|
|
111
|
+
// for nothing, then have a ton of set/reset cycles.
|
|
112
|
+
// Note that Timer.restart() is smart and will not change timer expiration if we keep extending timer
|
|
113
|
+
// expiration - it will restart the timer instead when it fires with adjusted expiration.
|
|
114
|
+
// this.timer.clear();
|
|
115
|
+
}
|
|
111
116
|
}
|