@fluidframework/container-loader 2.0.0-internal.5.4.2 → 2.0.0-internal.6.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/CHANGELOG.md +81 -0
  2. package/dist/connectionManager.d.ts +1 -1
  3. package/dist/connectionManager.d.ts.map +1 -1
  4. package/dist/connectionManager.js +24 -25
  5. package/dist/connectionManager.js.map +1 -1
  6. package/dist/connectionStateHandler.d.ts +2 -1
  7. package/dist/connectionStateHandler.d.ts.map +1 -1
  8. package/dist/connectionStateHandler.js +9 -16
  9. package/dist/connectionStateHandler.js.map +1 -1
  10. package/dist/container.d.ts +10 -5
  11. package/dist/container.d.ts.map +1 -1
  12. package/dist/container.js +161 -99
  13. package/dist/container.js.map +1 -1
  14. package/dist/containerContext.d.ts +2 -7
  15. package/dist/containerContext.d.ts.map +1 -1
  16. package/dist/containerContext.js +2 -14
  17. package/dist/containerContext.js.map +1 -1
  18. package/dist/containerStorageAdapter.js +3 -5
  19. package/dist/containerStorageAdapter.js.map +1 -1
  20. package/dist/contracts.d.ts +11 -2
  21. package/dist/contracts.d.ts.map +1 -1
  22. package/dist/contracts.js +3 -3
  23. package/dist/contracts.js.map +1 -1
  24. package/dist/debugLogger.js +2 -3
  25. package/dist/debugLogger.js.map +1 -1
  26. package/dist/deltaManager.d.ts +16 -3
  27. package/dist/deltaManager.d.ts.map +1 -1
  28. package/dist/deltaManager.js +62 -24
  29. package/dist/deltaManager.js.map +1 -1
  30. package/dist/deltaQueue.js +1 -2
  31. package/dist/deltaQueue.js.map +1 -1
  32. package/dist/loader.d.ts +12 -0
  33. package/dist/loader.d.ts.map +1 -1
  34. package/dist/loader.js +57 -42
  35. package/dist/loader.js.map +1 -1
  36. package/dist/packageVersion.d.ts +1 -1
  37. package/dist/packageVersion.js +1 -1
  38. package/dist/packageVersion.js.map +1 -1
  39. package/dist/protocol.d.ts.map +1 -1
  40. package/dist/protocol.js +2 -3
  41. package/dist/protocol.js.map +1 -1
  42. package/dist/utils.d.ts +8 -1
  43. package/dist/utils.d.ts.map +1 -1
  44. package/dist/utils.js +24 -6
  45. package/dist/utils.js.map +1 -1
  46. package/lib/connectionManager.d.ts +1 -1
  47. package/lib/connectionManager.d.ts.map +1 -1
  48. package/lib/connectionManager.js +25 -26
  49. package/lib/connectionManager.js.map +1 -1
  50. package/lib/connectionStateHandler.d.ts +2 -1
  51. package/lib/connectionStateHandler.d.ts.map +1 -1
  52. package/lib/connectionStateHandler.js +9 -16
  53. package/lib/connectionStateHandler.js.map +1 -1
  54. package/lib/container.d.ts +10 -5
  55. package/lib/container.d.ts.map +1 -1
  56. package/lib/container.js +160 -98
  57. package/lib/container.js.map +1 -1
  58. package/lib/containerContext.d.ts +2 -7
  59. package/lib/containerContext.d.ts.map +1 -1
  60. package/lib/containerContext.js +2 -14
  61. package/lib/containerContext.js.map +1 -1
  62. package/lib/containerStorageAdapter.js +3 -5
  63. package/lib/containerStorageAdapter.js.map +1 -1
  64. package/lib/contracts.d.ts +11 -2
  65. package/lib/contracts.d.ts.map +1 -1
  66. package/lib/contracts.js +3 -3
  67. package/lib/contracts.js.map +1 -1
  68. package/lib/debugLogger.js +2 -3
  69. package/lib/debugLogger.js.map +1 -1
  70. package/lib/deltaManager.d.ts +16 -3
  71. package/lib/deltaManager.d.ts.map +1 -1
  72. package/lib/deltaManager.js +62 -24
  73. package/lib/deltaManager.js.map +1 -1
  74. package/lib/deltaQueue.js +1 -2
  75. package/lib/deltaQueue.js.map +1 -1
  76. package/lib/loader.d.ts +12 -0
  77. package/lib/loader.d.ts.map +1 -1
  78. package/lib/loader.js +57 -42
  79. package/lib/loader.js.map +1 -1
  80. package/lib/packageVersion.d.ts +1 -1
  81. package/lib/packageVersion.js +1 -1
  82. package/lib/packageVersion.js.map +1 -1
  83. package/lib/protocol.d.ts.map +1 -1
  84. package/lib/protocol.js +2 -3
  85. package/lib/protocol.js.map +1 -1
  86. package/lib/utils.d.ts +8 -1
  87. package/lib/utils.d.ts.map +1 -1
  88. package/lib/utils.js +22 -5
  89. package/lib/utils.js.map +1 -1
  90. package/package.json +14 -14
  91. package/src/connectionManager.ts +7 -3
  92. package/src/connectionStateHandler.ts +3 -2
  93. package/src/container.ts +113 -27
  94. package/src/containerContext.ts +0 -16
  95. package/src/contracts.ts +16 -5
  96. package/src/deltaManager.ts +22 -5
  97. package/src/loader.ts +37 -23
  98. package/src/packageVersion.ts +1 -1
  99. package/src/protocol.ts +0 -1
  100. package/src/utils.ts +29 -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 { 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\";\nimport {\n\tDeltaStreamConnectionForbiddenError,\n\tisCombinedAppAndProtocolSummary,\n} from \"@fluidframework/driver-utils\";\nimport { DriverErrorType } from \"@fluidframework/driver-definitions\";\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 = (\n\tdetachedContainerSnapshot: ISummaryTree,\n): ISnapshotTreeWithBlobContents => {\n\tassert(\n\t\tisCombinedAppAndProtocolSummary(detachedContainerSnapshot),\n\t\t0x1e0 /* \"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 = 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\nexport function isDeltaStreamConnectionForbiddenError(\n\terror: any,\n): error is DeltaStreamConnectionForbiddenError {\n\treturn (\n\t\ttypeof error === \"object\" &&\n\t\terror !== null &&\n\t\terror?.errorType === DriverErrorType.deltaStreamConnectionForbidden\n\t);\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 { 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\";\nimport {\n\tCombinedAppAndProtocolSummary,\n\tDeltaStreamConnectionForbiddenError,\n\tisCombinedAppAndProtocolSummary,\n} from \"@fluidframework/driver-utils\";\nimport { DriverErrorType } from \"@fluidframework/driver-definitions\";\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 * 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 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 = (\n\tdetachedContainerSnapshot: ISummaryTree,\n): ISnapshotTreeWithBlobContents => {\n\tassert(\n\t\tisCombinedAppAndProtocolSummary(detachedContainerSnapshot),\n\t\t0x1e0 /* \"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 = 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\nexport function isDeltaStreamConnectionForbiddenError(\n\terror: any,\n): error is DeltaStreamConnectionForbiddenError {\n\treturn (\n\t\ttypeof error === \"object\" &&\n\t\terror !== null &&\n\t\terror?.errorType === DriverErrorType.deltaStreamConnectionForbidden\n\t);\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluidframework/container-loader",
3
- "version": "2.0.0-internal.5.4.2",
3
+ "version": "2.0.0-internal.6.0.1",
4
4
  "description": "Fluid container loader",
5
5
  "homepage": "https://fluidframework.com",
6
6
  "repository": {
@@ -37,14 +37,14 @@
37
37
  "dependencies": {
38
38
  "@fluidframework/common-definitions": "^0.20.1",
39
39
  "@fluidframework/common-utils": "^1.1.1",
40
- "@fluidframework/container-definitions": ">=2.0.0-internal.5.4.2 <2.0.0-internal.5.5.0",
41
- "@fluidframework/container-utils": ">=2.0.0-internal.5.4.2 <2.0.0-internal.5.5.0",
42
- "@fluidframework/core-interfaces": ">=2.0.0-internal.5.4.2 <2.0.0-internal.5.5.0",
43
- "@fluidframework/driver-definitions": ">=2.0.0-internal.5.4.2 <2.0.0-internal.5.5.0",
44
- "@fluidframework/driver-utils": ">=2.0.0-internal.5.4.2 <2.0.0-internal.5.5.0",
45
- "@fluidframework/protocol-base": "^0.1039.1000",
40
+ "@fluidframework/container-definitions": ">=2.0.0-internal.6.0.1 <2.0.0-internal.6.1.0",
41
+ "@fluidframework/container-utils": ">=2.0.0-internal.6.0.1 <2.0.0-internal.6.1.0",
42
+ "@fluidframework/core-interfaces": ">=2.0.0-internal.6.0.1 <2.0.0-internal.6.1.0",
43
+ "@fluidframework/driver-definitions": ">=2.0.0-internal.6.0.1 <2.0.0-internal.6.1.0",
44
+ "@fluidframework/driver-utils": ">=2.0.0-internal.6.0.1 <2.0.0-internal.6.1.0",
45
+ "@fluidframework/protocol-base": "^1.0.0",
46
46
  "@fluidframework/protocol-definitions": "^1.1.0",
47
- "@fluidframework/telemetry-utils": ">=2.0.0-internal.5.4.2 <2.0.0-internal.5.5.0",
47
+ "@fluidframework/telemetry-utils": ">=2.0.0-internal.6.0.1 <2.0.0-internal.6.1.0",
48
48
  "debug": "^4.1.1",
49
49
  "double-ended-queue": "^2.1.0-0",
50
50
  "events": "^3.1.0",
@@ -53,13 +53,13 @@
53
53
  "uuid": "^8.3.1"
54
54
  },
55
55
  "devDependencies": {
56
- "@fluid-internal/test-loader-utils": ">=2.0.0-internal.5.4.2 <2.0.0-internal.5.5.0",
56
+ "@fluid-internal/test-loader-utils": ">=2.0.0-internal.6.0.1 <2.0.0-internal.6.1.0",
57
57
  "@fluid-tools/build-cli": "^0.21.0",
58
- "@fluidframework/build-common": "^1.2.0",
58
+ "@fluidframework/build-common": "^2.0.0",
59
59
  "@fluidframework/build-tools": "^0.21.0",
60
- "@fluidframework/container-loader-previous": "npm:@fluidframework/container-loader@2.0.0-internal.5.2.0",
60
+ "@fluidframework/container-loader-previous": "npm:@fluidframework/container-loader@2.0.0-internal.6.0.0",
61
61
  "@fluidframework/eslint-config-fluid": "^2.0.0",
62
- "@fluidframework/mocha-test-setup": ">=2.0.0-internal.5.4.2 <2.0.0-internal.5.5.0",
62
+ "@fluidframework/mocha-test-setup": ">=2.0.0-internal.6.0.1 <2.0.0-internal.6.1.0",
63
63
  "@microsoft/api-extractor": "^7.34.4",
64
64
  "@types/double-ended-queue": "^2.1.0",
65
65
  "@types/events": "^3.0.0",
@@ -83,8 +83,8 @@
83
83
  },
84
84
  "typeValidation": {
85
85
  "broken": {
86
- "InterfaceDeclaration_IContainerExperimental": {
87
- "backCompat": false
86
+ "InterfaceDeclaration_IContainerContext": {
87
+ "forwardCompat": false
88
88
  }
89
89
  }
90
90
  },
@@ -6,10 +6,9 @@
6
6
  import { IDisposable, ITelemetryProperties } from "@fluidframework/core-interfaces";
7
7
  import { assert, performance, TypedEventEmitter } from "@fluidframework/common-utils";
8
8
  import {
9
+ ICriticalContainerError,
9
10
  IDeltaQueue,
10
11
  ReadOnlyInfo,
11
- IConnectionDetailsInternal,
12
- ICriticalContainerError,
13
12
  } from "@fluidframework/container-definitions";
14
13
  import { GenericError, UsageError } from "@fluidframework/container-utils";
15
14
  import {
@@ -44,7 +43,12 @@ import {
44
43
  ISequencedDocumentSystemMessage,
45
44
  } from "@fluidframework/protocol-definitions";
46
45
  import { ITelemetryLoggerExt, formatTick, normalizeError } from "@fluidframework/telemetry-utils";
47
- import { ReconnectMode, IConnectionManager, IConnectionManagerFactoryArgs } from "./contracts";
46
+ import {
47
+ ReconnectMode,
48
+ IConnectionManager,
49
+ IConnectionManagerFactoryArgs,
50
+ IConnectionDetailsInternal,
51
+ } from "./contracts";
48
52
  import { DeltaQueue } from "./deltaQueue";
49
53
  import { SignalType } from "./protocol";
50
54
  import { isDeltaStreamConnectionForbiddenError } from "./utils";
@@ -5,7 +5,7 @@
5
5
 
6
6
  import { ITelemetryProperties, TelemetryEventCategory } from "@fluidframework/core-interfaces";
7
7
  import { assert, Timer } from "@fluidframework/common-utils";
8
- import { IConnectionDetailsInternal, IDeltaManager } from "@fluidframework/container-definitions";
8
+ import { IDeltaManager } from "@fluidframework/container-definitions";
9
9
  import { IAnyDriverError } from "@fluidframework/driver-definitions";
10
10
  import { ISequencedClient, IClient } from "@fluidframework/protocol-definitions";
11
11
  import {
@@ -13,8 +13,9 @@ import {
13
13
  PerformanceEvent,
14
14
  loggerToMonitoringContext,
15
15
  } from "@fluidframework/telemetry-utils";
16
- import { ConnectionState } from "./connectionState";
17
16
  import { CatchUpMonitor, ICatchUpMonitor } from "./catchUpMonitor";
17
+ import { ConnectionState } from "./connectionState";
18
+ import { IConnectionDetailsInternal } from "./contracts";
18
19
  import { IProtocolHandler } from "./protocol";
19
20
 
20
21
  // Based on recent data, it looks like majority of cases where we get stuck are due to really slow or
package/src/container.ts CHANGED
@@ -23,27 +23,26 @@ import {
23
23
  FluidObject,
24
24
  } from "@fluidframework/core-interfaces";
25
25
  import {
26
+ AttachState,
27
+ ContainerWarning,
26
28
  IAudience,
27
- IConnectionDetailsInternal,
29
+ IBatchMessage,
30
+ ICodeDetailsLoader,
28
31
  IContainer,
29
32
  IContainerEvents,
30
- IDeltaManager,
31
- ICriticalContainerError,
32
- ContainerWarning,
33
- AttachState,
34
- IThrottlingWarning,
35
- ReadOnlyInfo,
36
33
  IContainerLoadMode,
34
+ ICriticalContainerError,
35
+ IDeltaManager,
37
36
  IFluidCodeDetails,
38
- isFluidCodeDetails,
39
- IBatchMessage,
40
- ICodeDetailsLoader,
41
37
  IHostLoader,
42
38
  IFluidModuleWithDetails,
43
39
  IProvideRuntimeFactory,
44
40
  IProvideFluidCodeDetailsComparer,
45
41
  IFluidCodeDetailsComparer,
46
42
  IRuntime,
43
+ isFluidCodeDetails,
44
+ IThrottlingWarning,
45
+ ReadOnlyInfo,
47
46
  } from "@fluidframework/container-definitions";
48
47
  import { GenericError, UsageError } from "@fluidframework/container-utils";
49
48
  import {
@@ -58,7 +57,6 @@ import {
58
57
  readAndParse,
59
58
  OnlineStatus,
60
59
  isOnline,
61
- combineAppAndProtocolSummary,
62
60
  runWithRetry,
63
61
  isCombinedAppAndProtocolSummary,
64
62
  MessageType2,
@@ -98,7 +96,12 @@ import {
98
96
  } from "@fluidframework/telemetry-utils";
99
97
  import { Audience } from "./audience";
100
98
  import { ContainerContext } from "./containerContext";
101
- import { ReconnectMode, IConnectionManagerFactoryArgs, getPackageName } from "./contracts";
99
+ import {
100
+ ReconnectMode,
101
+ IConnectionManagerFactoryArgs,
102
+ getPackageName,
103
+ IConnectionDetailsInternal,
104
+ } from "./contracts";
102
105
  import { DeltaManager, IConnectionArgs } from "./deltaManager";
103
106
  import { IDetachedBlobStorage, ILoaderOptions, RelativeLoader } from "./loader";
104
107
  import { pkgVersion } from "./packageVersion";
@@ -109,7 +112,11 @@ import {
109
112
  ISerializableBlobContents,
110
113
  } from "./containerStorageAdapter";
111
114
  import { IConnectionStateHandler, createConnectionStateHandler } from "./connectionStateHandler";
112
- import { getProtocolSnapshotTree, getSnapshotTreeFromSerializedContainer } from "./utils";
115
+ import {
116
+ combineAppAndProtocolSummary,
117
+ getProtocolSnapshotTree,
118
+ getSnapshotTreeFromSerializedContainer,
119
+ } from "./utils";
113
120
  import { initQuorumValuesFromCodeDetails } from "./quorum";
114
121
  import { NoopHeuristic } from "./noopHeuristic";
115
122
  import { ConnectionManager } from "./connectionManager";
@@ -150,6 +157,11 @@ export interface IContainerLoadProps {
150
157
  * The pending state serialized from a pervious container instance
151
158
  */
152
159
  readonly pendingLocalState?: IPendingContainerState;
160
+
161
+ /**
162
+ * Load the container to at least this sequence number.
163
+ */
164
+ readonly loadToSequenceNumber?: number;
153
165
  }
154
166
 
155
167
  /**
@@ -368,7 +380,8 @@ export class Container
368
380
  loadProps: IContainerLoadProps,
369
381
  createProps: IContainerCreateProps,
370
382
  ): Promise<Container> {
371
- const { version, pendingLocalState, loadMode, resolvedUrl } = loadProps;
383
+ const { version, pendingLocalState, loadMode, resolvedUrl, loadToSequenceNumber } =
384
+ loadProps;
372
385
 
373
386
  const container = new Container(createProps, loadProps);
374
387
 
@@ -397,7 +410,7 @@ export class Container
397
410
  container.on("closed", onClosed);
398
411
 
399
412
  container
400
- .load(version, mode, resolvedUrl, pendingLocalState)
413
+ .load(version, mode, resolvedUrl, pendingLocalState, loadToSequenceNumber)
401
414
  .finally(() => {
402
415
  container.removeListener("closed", onClosed);
403
416
  })
@@ -1065,16 +1078,21 @@ export class Container
1065
1078
  }
1066
1079
  }
1067
1080
 
1068
- public closeAndGetPendingLocalState(): string {
1081
+ public async closeAndGetPendingLocalState(): Promise<string> {
1069
1082
  // runtime matches pending ops to successful ones by clientId and client seq num, so we need to close the
1070
1083
  // container at the same time we get pending state, otherwise this container could reconnect and resubmit with
1071
1084
  // a new clientId and a future container using stale pending state without the new clientId would resubmit them
1072
- const pendingState = this.getPendingLocalState();
1085
+ this.disconnect(); // TODO https://dev.azure.com/fluidframework/internal/_workitems/edit/5127
1086
+ const pendingState = await this.getPendingLocalStateCore({ notifyImminentClosure: true });
1073
1087
  this.close();
1074
1088
  return pendingState;
1075
1089
  }
1076
1090
 
1077
- public getPendingLocalState(): string {
1091
+ public async getPendingLocalState(): Promise<string> {
1092
+ return this.getPendingLocalStateCore({ notifyImminentClosure: false });
1093
+ }
1094
+
1095
+ private async getPendingLocalStateCore(props: { notifyImminentClosure: boolean }) {
1078
1096
  if (!this.offlineLoadEnabled) {
1079
1097
  throw new UsageError("Can't get pending local state unless offline load is enabled");
1080
1098
  }
@@ -1093,8 +1111,9 @@ export class Container
1093
1111
  );
1094
1112
  assert(!!this.baseSnapshot, 0x5d4 /* no base snapshot */);
1095
1113
  assert(!!this.baseSnapshotBlobs, 0x5d5 /* no snapshot blobs */);
1114
+ const pendingRuntimeState = await this.runtime.getPendingLocalState(props);
1096
1115
  const pendingState: IPendingContainerState = {
1097
- pendingRuntimeState: this.runtime.getPendingLocalState(),
1116
+ pendingRuntimeState,
1098
1117
  baseSnapshot: this.baseSnapshot,
1099
1118
  snapshotBlobs: this.baseSnapshotBlobs,
1100
1119
  savedOps: this.savedOps,
@@ -1479,7 +1498,8 @@ export class Container
1479
1498
  specifiedVersion: string | undefined,
1480
1499
  loadMode: IContainerLoadMode,
1481
1500
  resolvedUrl: IResolvedUrl,
1482
- pendingLocalState?: IPendingContainerState,
1501
+ pendingLocalState: IPendingContainerState | undefined,
1502
+ loadToSequenceNumber: number | undefined,
1483
1503
  ) {
1484
1504
  this.service = await this.serviceFactory.createDocumentService(
1485
1505
  resolvedUrl,
@@ -1553,6 +1573,57 @@ export class Container
1553
1573
 
1554
1574
  let opsBeforeReturnP: Promise<void> | undefined;
1555
1575
 
1576
+ if (loadMode.pauseAfterLoad === true) {
1577
+ // If we are trying to pause at a specific sequence number, ensure the latest snapshot is not newer than the desired sequence number.
1578
+ if (loadMode.opsBeforeReturn === "sequenceNumber") {
1579
+ assert(
1580
+ loadToSequenceNumber !== undefined,
1581
+ 0x727 /* sequenceNumber should be defined */,
1582
+ );
1583
+ // Note: It is possible that we think the latest snapshot is newer than the specified sequence number
1584
+ // due to saved ops that may be replayed after the snapshot.
1585
+ // https://dev.azure.com/fluidframework/internal/_workitems/edit/5055
1586
+ if (dmAttributes.sequenceNumber > loadToSequenceNumber) {
1587
+ throw new Error(
1588
+ "Cannot satisfy request to pause the container at the specified sequence number. Most recent snapshot is newer than the specified sequence number.",
1589
+ );
1590
+ }
1591
+ }
1592
+
1593
+ // Force readonly mode - this will ensure we don't receive an error for the lack of join op
1594
+ this.forceReadonly(true);
1595
+
1596
+ // We need to setup a listener to stop op processing once we reach the desired sequence number (if specified).
1597
+ const opHandler = () => {
1598
+ if (loadToSequenceNumber === undefined) {
1599
+ // If there is no specified sequence number, pause after the inbound queue is empty.
1600
+ if (this.deltaManager.inbound.length !== 0) {
1601
+ return;
1602
+ }
1603
+ } else {
1604
+ // If there is a specified sequence number, keep processing until we reach it.
1605
+ if (this.deltaManager.lastSequenceNumber < loadToSequenceNumber) {
1606
+ return;
1607
+ }
1608
+ }
1609
+
1610
+ // Pause op processing once we have processed the desired number of ops.
1611
+ void this.deltaManager.inbound.pause();
1612
+ void this.deltaManager.outbound.pause();
1613
+ this.off("op", opHandler);
1614
+ };
1615
+ if (
1616
+ (loadToSequenceNumber === undefined && this.deltaManager.inbound.length === 0) ||
1617
+ this.deltaManager.lastSequenceNumber === loadToSequenceNumber
1618
+ ) {
1619
+ // If we have already reached the desired sequence number, call opHandler() to pause immediately.
1620
+ opHandler();
1621
+ } else {
1622
+ // If we have not yet reached the desired sequence number, setup a listener to pause once we reach it.
1623
+ this.on("op", opHandler);
1624
+ }
1625
+ }
1626
+
1556
1627
  // Attach op handlers to finish initialization and be able to start processing ops
1557
1628
  // Kick off any ops fetching if required.
1558
1629
  switch (loadMode.opsBeforeReturn) {
@@ -1564,6 +1635,9 @@ export class Container
1564
1635
  loadMode.deltaConnection !== "none" ? "all" : "none",
1565
1636
  );
1566
1637
  break;
1638
+ case "sequenceNumber":
1639
+ opsBeforeReturnP = this.attachDeltaManagerOpHandler(dmAttributes, "sequenceNumber");
1640
+ break;
1567
1641
  case "cached":
1568
1642
  opsBeforeReturnP = this.attachDeltaManagerOpHandler(dmAttributes, "cached");
1569
1643
  break;
@@ -1647,6 +1721,22 @@ export class Container
1647
1721
  }
1648
1722
  }
1649
1723
 
1724
+ // If we have not yet reached `loadToSequenceNumber`, we will wait for ops to arrive until we reach it
1725
+ if (
1726
+ loadToSequenceNumber !== undefined &&
1727
+ this.deltaManager.lastSequenceNumber < loadToSequenceNumber
1728
+ ) {
1729
+ await new Promise<void>((resolve, reject) => {
1730
+ const opHandler = (message: ISequencedDocumentMessage) => {
1731
+ if (message.sequenceNumber >= loadToSequenceNumber) {
1732
+ resolve();
1733
+ this.off("op", opHandler);
1734
+ }
1735
+ };
1736
+ this.on("op", opHandler);
1737
+ });
1738
+ }
1739
+
1650
1740
  // Safety net: static version of Container.load() should have learned about it through "closed" handler.
1651
1741
  // But if that did not happen for some reason, fail load for sure.
1652
1742
  // Otherwise we can get into situations where container is closed and does not try to connect to ordering
@@ -1976,7 +2066,7 @@ export class Container
1976
2066
 
1977
2067
  private async attachDeltaManagerOpHandler(
1978
2068
  attributes: IDocumentAttributes,
1979
- prefetchType?: "cached" | "all" | "none",
2069
+ prefetchType?: "sequenceNumber" | "cached" | "all" | "none",
1980
2070
  ) {
1981
2071
  return this._deltaManager.attachOpHandler(
1982
2072
  attributes.minimumSequenceNumber,
@@ -2314,7 +2404,6 @@ export class Container
2314
2404
  this.getAbsoluteUrl,
2315
2405
  () => this.resolvedUrl?.id,
2316
2406
  () => this.clientId,
2317
- () => this._deltaManager.serviceConfiguration,
2318
2407
  () => this.attachState,
2319
2408
  () => this.connected,
2320
2409
  getSpecifiedCodeDetails,
@@ -2323,9 +2412,6 @@ export class Container
2323
2412
  this.subLogger,
2324
2413
  pendingLocalState,
2325
2414
  );
2326
- this._lifecycleEvents.once("disposed", () => {
2327
- context.dispose();
2328
- });
2329
2415
 
2330
2416
  this._runtime = await PerformanceEvent.timedExecAsync(
2331
2417
  this.subLogger,
@@ -2377,7 +2463,7 @@ export interface IContainerExperimental extends IContainer {
2377
2463
  * @experimental misuse of this API can result in duplicate op submission and potential document corruption
2378
2464
  * {@link https://github.com/microsoft/FluidFramework/blob/main/packages/loader/container-loader/closeAndGetPendingLocalState.md}
2379
2465
  */
2380
- getPendingLocalState?(): string;
2466
+ getPendingLocalState?(): Promise<string>;
2381
2467
 
2382
2468
  /**
2383
2469
  * Closes the container and returns serialized local state intended to be
@@ -2385,5 +2471,5 @@ export interface IContainerExperimental extends IContainer {
2385
2471
  * @experimental
2386
2472
  * {@link https://github.com/microsoft/FluidFramework/blob/main/packages/loader/container-loader/closeAndGetPendingLocalState.md}
2387
2473
  */
2388
- closeAndGetPendingLocalState(): string;
2474
+ closeAndGetPendingLocalState?(): Promise<string>;
2389
2475
  }
@@ -18,7 +18,6 @@ import {
18
18
  import { FluidObject } from "@fluidframework/core-interfaces";
19
19
  import { IDocumentStorageService } from "@fluidframework/driver-definitions";
20
20
  import {
21
- IClientConfiguration,
22
21
  IClientDetails,
23
22
  IDocumentMessage,
24
23
  IQuorumClients,
@@ -61,16 +60,6 @@ export class ContainerContext implements IContainerContext {
61
60
  return this._getConnected();
62
61
  }
63
62
 
64
- public get serviceConfiguration(): IClientConfiguration | undefined {
65
- return this._getServiceConfiguration();
66
- }
67
-
68
- private _disposed = false;
69
-
70
- public get disposed() {
71
- return this._disposed;
72
- }
73
-
74
63
  constructor(
75
64
  public readonly options: ILoaderOptions,
76
65
  public readonly scope: FluidObject,
@@ -103,7 +92,6 @@ export class ContainerContext implements IContainerContext {
103
92
  public readonly getAbsoluteUrl: (relativeUrl: string) => Promise<string | undefined>,
104
93
  private readonly _getContainerDiagnosticId: () => string | undefined,
105
94
  private readonly _getClientId: () => string | undefined,
106
- private readonly _getServiceConfiguration: () => IClientConfiguration | undefined,
107
95
  private readonly _getAttachState: () => AttachState,
108
96
  private readonly _getConnected: () => boolean,
109
97
  public readonly getSpecifiedCodeDetails: () => IFluidCodeDetails | undefined,
@@ -113,10 +101,6 @@ export class ContainerContext implements IContainerContext {
113
101
  public readonly pendingLocalState?: unknown,
114
102
  ) {}
115
103
 
116
- public dispose(error?: Error): void {
117
- this._disposed = true;
118
- }
119
-
120
104
  public getLoadedFromVersion(): IVersion | undefined {
121
105
  return this._version;
122
106
  }
package/src/contracts.ts CHANGED
@@ -5,19 +5,20 @@
5
5
 
6
6
  import { ITelemetryProperties } from "@fluidframework/core-interfaces";
7
7
  import {
8
- IDeltaQueue,
9
- ReadOnlyInfo,
10
- IConnectionDetailsInternal,
8
+ IConnectionDetails,
11
9
  ICriticalContainerError,
10
+ IDeltaQueue,
12
11
  IFluidCodeDetails,
13
12
  isFluidPackage,
13
+ ReadOnlyInfo,
14
14
  } from "@fluidframework/container-definitions";
15
15
  import {
16
16
  ConnectionMode,
17
- IDocumentMessage,
18
- ISequencedDocumentMessage,
19
17
  IClientConfiguration,
20
18
  IClientDetails,
19
+ IDocumentMessage,
20
+ ISequencedDocumentMessage,
21
+ ISignalClient,
21
22
  ISignalMessage,
22
23
  } from "@fluidframework/protocol-definitions";
23
24
  import { IAnyDriverError, IContainerPackageInfo } from "@fluidframework/driver-definitions";
@@ -28,6 +29,16 @@ export enum ReconnectMode {
28
29
  Enabled = "Enabled",
29
30
  }
30
31
 
32
+ /**
33
+ * Internal version of IConnectionDetails with props are only exposed internally
34
+ */
35
+ export interface IConnectionDetailsInternal extends IConnectionDetails {
36
+ mode: ConnectionMode;
37
+ version: string;
38
+ initialClients: ISignalClient[];
39
+ reason: string;
40
+ }
41
+
31
42
  /**
32
43
  * Connection manager (implements this interface) is responsible for maintaining connection
33
44
  * to relay service.
@@ -7,13 +7,11 @@ import { v4 as uuid } from "uuid";
7
7
  import { IEventProvider } from "@fluidframework/common-definitions";
8
8
  import { ITelemetryProperties, ITelemetryErrorEvent } from "@fluidframework/core-interfaces";
9
9
  import {
10
- IDeltaHandlerStrategy,
10
+ ICriticalContainerError,
11
11
  IDeltaManager,
12
12
  IDeltaManagerEvents,
13
13
  IDeltaQueue,
14
- ICriticalContainerError,
15
14
  IThrottlingWarning,
16
- IConnectionDetailsInternal,
17
15
  } from "@fluidframework/container-definitions";
18
16
  import { assert, TypedEventEmitter } from "@fluidframework/common-utils";
19
17
  import {
@@ -44,7 +42,11 @@ import {
44
42
  DataProcessingError,
45
43
  UsageError,
46
44
  } from "@fluidframework/container-utils";
47
- import { IConnectionManagerFactoryArgs, IConnectionManager } from "./contracts";
45
+ import {
46
+ IConnectionDetailsInternal,
47
+ IConnectionManager,
48
+ IConnectionManagerFactoryArgs,
49
+ } from "./contracts";
48
50
  import { DeltaQueue } from "./deltaQueue";
49
51
  import { OnlyValidTermValue } from "./protocol";
50
52
 
@@ -73,6 +75,21 @@ interface IBatchMetadata {
73
75
  batch?: boolean;
74
76
  }
75
77
 
78
+ /**
79
+ * Interface used to define a strategy for handling incoming delta messages
80
+ */
81
+ export interface IDeltaHandlerStrategy {
82
+ /**
83
+ * Processes the message.
84
+ */
85
+ process: (message: ISequencedDocumentMessage) => void;
86
+
87
+ /**
88
+ * Processes the signal.
89
+ */
90
+ processSignal: (message: ISignalMessage) => void;
91
+ }
92
+
76
93
  /**
77
94
  * Determines if message was sent by client, not service
78
95
  */
@@ -495,7 +512,7 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
495
512
  minSequenceNumber: number,
496
513
  sequenceNumber: number,
497
514
  handler: IDeltaHandlerStrategy,
498
- prefetchType: "cached" | "all" | "none" = "none",
515
+ prefetchType: "sequenceNumber" | "cached" | "all" | "none" = "none",
499
516
  ) {
500
517
  this.initSequenceNumber = sequenceNumber;
501
518
  this.lastProcessedSequenceNumber = sequenceNumber;