@fluidframework/container-loader 2.0.0-rc.3.0.2 → 2.0.0-rc.4.0.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.
Files changed (188) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/api-report/container-loader.api.md +5 -1
  3. package/dist/attachment.d.ts +3 -2
  4. package/dist/attachment.d.ts.map +1 -1
  5. package/dist/attachment.js +5 -5
  6. package/dist/attachment.js.map +1 -1
  7. package/dist/audience.d.ts +6 -4
  8. package/dist/audience.d.ts.map +1 -1
  9. package/dist/audience.js +18 -3
  10. package/dist/audience.js.map +1 -1
  11. package/dist/catchUpMonitor.d.ts +1 -1
  12. package/dist/catchUpMonitor.d.ts.map +1 -1
  13. package/dist/catchUpMonitor.js.map +1 -1
  14. package/dist/connectionManager.d.ts +7 -3
  15. package/dist/connectionManager.d.ts.map +1 -1
  16. package/dist/connectionManager.js +57 -38
  17. package/dist/connectionManager.js.map +1 -1
  18. package/dist/connectionStateHandler.d.ts +31 -10
  19. package/dist/connectionStateHandler.d.ts.map +1 -1
  20. package/dist/connectionStateHandler.js +49 -36
  21. package/dist/connectionStateHandler.js.map +1 -1
  22. package/dist/container.d.ts +22 -13
  23. package/dist/container.d.ts.map +1 -1
  24. package/dist/container.js +145 -117
  25. package/dist/container.js.map +1 -1
  26. package/dist/containerContext.d.ts +3 -3
  27. package/dist/containerContext.d.ts.map +1 -1
  28. package/dist/containerContext.js.map +1 -1
  29. package/dist/containerStorageAdapter.d.ts +12 -3
  30. package/dist/containerStorageAdapter.d.ts.map +1 -1
  31. package/dist/containerStorageAdapter.js +42 -4
  32. package/dist/containerStorageAdapter.js.map +1 -1
  33. package/dist/contracts.d.ts +2 -2
  34. package/dist/contracts.d.ts.map +1 -1
  35. package/dist/contracts.js.map +1 -1
  36. package/dist/debugLogger.d.ts +1 -2
  37. package/dist/debugLogger.d.ts.map +1 -1
  38. package/dist/debugLogger.js.map +1 -1
  39. package/dist/deltaManager.d.ts +5 -6
  40. package/dist/deltaManager.d.ts.map +1 -1
  41. package/dist/deltaManager.js +29 -24
  42. package/dist/deltaManager.js.map +1 -1
  43. package/dist/deltaQueue.d.ts +1 -1
  44. package/dist/deltaQueue.d.ts.map +1 -1
  45. package/dist/deltaQueue.js.map +1 -1
  46. package/dist/error.d.ts +1 -2
  47. package/dist/error.d.ts.map +1 -1
  48. package/dist/error.js.map +1 -1
  49. package/dist/index.d.ts +1 -0
  50. package/dist/index.d.ts.map +1 -1
  51. package/dist/index.js +3 -1
  52. package/dist/index.js.map +1 -1
  53. package/dist/legacy.d.ts +2 -2
  54. package/dist/loadPaused.d.ts +35 -0
  55. package/dist/loadPaused.d.ts.map +1 -0
  56. package/dist/loadPaused.js +115 -0
  57. package/dist/loadPaused.js.map +1 -0
  58. package/dist/loader.d.ts +1 -1
  59. package/dist/loader.d.ts.map +1 -1
  60. package/dist/loader.js +1 -14
  61. package/dist/loader.js.map +1 -1
  62. package/dist/location-redirection-utilities/resolveWithLocationRedirection.d.ts.map +1 -1
  63. package/dist/location-redirection-utilities/resolveWithLocationRedirection.js +4 -4
  64. package/dist/location-redirection-utilities/resolveWithLocationRedirection.js.map +1 -1
  65. package/dist/packageVersion.d.ts +1 -1
  66. package/dist/packageVersion.js +1 -1
  67. package/dist/packageVersion.js.map +1 -1
  68. package/dist/protocol.d.ts.map +1 -1
  69. package/dist/protocol.js +3 -0
  70. package/dist/protocol.js.map +1 -1
  71. package/dist/public.d.ts +1 -1
  72. package/dist/retriableDocumentStorageService.d.ts +1 -1
  73. package/dist/retriableDocumentStorageService.d.ts.map +1 -1
  74. package/dist/retriableDocumentStorageService.js.map +1 -1
  75. package/dist/serializedStateManager.d.ts +89 -9
  76. package/dist/serializedStateManager.d.ts.map +1 -1
  77. package/dist/serializedStateManager.js +150 -34
  78. package/dist/serializedStateManager.js.map +1 -1
  79. package/dist/utils.d.ts +11 -1
  80. package/dist/utils.d.ts.map +1 -1
  81. package/dist/utils.js +29 -14
  82. package/dist/utils.js.map +1 -1
  83. package/lib/attachment.d.ts +3 -2
  84. package/lib/attachment.d.ts.map +1 -1
  85. package/lib/attachment.js +5 -5
  86. package/lib/attachment.js.map +1 -1
  87. package/lib/audience.d.ts +6 -4
  88. package/lib/audience.d.ts.map +1 -1
  89. package/lib/audience.js +19 -4
  90. package/lib/audience.js.map +1 -1
  91. package/lib/catchUpMonitor.d.ts +1 -1
  92. package/lib/catchUpMonitor.d.ts.map +1 -1
  93. package/lib/catchUpMonitor.js.map +1 -1
  94. package/lib/connectionManager.d.ts +7 -3
  95. package/lib/connectionManager.d.ts.map +1 -1
  96. package/lib/connectionManager.js +36 -17
  97. package/lib/connectionManager.js.map +1 -1
  98. package/lib/connectionStateHandler.d.ts +31 -10
  99. package/lib/connectionStateHandler.d.ts.map +1 -1
  100. package/lib/connectionStateHandler.js +49 -36
  101. package/lib/connectionStateHandler.js.map +1 -1
  102. package/lib/container.d.ts +22 -13
  103. package/lib/container.d.ts.map +1 -1
  104. package/lib/container.js +146 -118
  105. package/lib/container.js.map +1 -1
  106. package/lib/containerContext.d.ts +3 -3
  107. package/lib/containerContext.d.ts.map +1 -1
  108. package/lib/containerContext.js.map +1 -1
  109. package/lib/containerStorageAdapter.d.ts +12 -3
  110. package/lib/containerStorageAdapter.d.ts.map +1 -1
  111. package/lib/containerStorageAdapter.js +42 -4
  112. package/lib/containerStorageAdapter.js.map +1 -1
  113. package/lib/contracts.d.ts +2 -2
  114. package/lib/contracts.d.ts.map +1 -1
  115. package/lib/contracts.js +1 -1
  116. package/lib/contracts.js.map +1 -1
  117. package/lib/debugLogger.d.ts +1 -2
  118. package/lib/debugLogger.d.ts.map +1 -1
  119. package/lib/debugLogger.js.map +1 -1
  120. package/lib/deltaManager.d.ts +5 -6
  121. package/lib/deltaManager.d.ts.map +1 -1
  122. package/lib/deltaManager.js +10 -5
  123. package/lib/deltaManager.js.map +1 -1
  124. package/lib/deltaQueue.d.ts +1 -1
  125. package/lib/deltaQueue.d.ts.map +1 -1
  126. package/lib/deltaQueue.js.map +1 -1
  127. package/lib/error.d.ts +1 -2
  128. package/lib/error.d.ts.map +1 -1
  129. package/lib/error.js.map +1 -1
  130. package/lib/index.d.ts +1 -0
  131. package/lib/index.d.ts.map +1 -1
  132. package/lib/index.js +1 -0
  133. package/lib/index.js.map +1 -1
  134. package/lib/legacy.d.ts +2 -2
  135. package/lib/loadPaused.d.ts +35 -0
  136. package/lib/loadPaused.d.ts.map +1 -0
  137. package/lib/loadPaused.js +111 -0
  138. package/lib/loadPaused.js.map +1 -0
  139. package/lib/loader.d.ts +1 -1
  140. package/lib/loader.d.ts.map +1 -1
  141. package/lib/loader.js +3 -16
  142. package/lib/loader.js.map +1 -1
  143. package/lib/location-redirection-utilities/resolveWithLocationRedirection.d.ts.map +1 -1
  144. package/lib/location-redirection-utilities/resolveWithLocationRedirection.js +1 -1
  145. package/lib/location-redirection-utilities/resolveWithLocationRedirection.js.map +1 -1
  146. package/lib/packageVersion.d.ts +1 -1
  147. package/lib/packageVersion.js +1 -1
  148. package/lib/packageVersion.js.map +1 -1
  149. package/lib/protocol.d.ts.map +1 -1
  150. package/lib/protocol.js +3 -0
  151. package/lib/protocol.js.map +1 -1
  152. package/lib/public.d.ts +1 -1
  153. package/lib/retriableDocumentStorageService.d.ts +1 -1
  154. package/lib/retriableDocumentStorageService.d.ts.map +1 -1
  155. package/lib/retriableDocumentStorageService.js +1 -1
  156. package/lib/retriableDocumentStorageService.js.map +1 -1
  157. package/lib/serializedStateManager.d.ts +89 -9
  158. package/lib/serializedStateManager.d.ts.map +1 -1
  159. package/lib/serializedStateManager.js +146 -30
  160. package/lib/serializedStateManager.js.map +1 -1
  161. package/lib/tsdoc-metadata.json +1 -1
  162. package/lib/utils.d.ts +11 -1
  163. package/lib/utils.d.ts.map +1 -1
  164. package/lib/utils.js +15 -1
  165. package/lib/utils.js.map +1 -1
  166. package/package.json +24 -21
  167. package/src/attachment.ts +12 -13
  168. package/src/audience.ts +30 -9
  169. package/src/catchUpMonitor.ts +1 -1
  170. package/src/connectionManager.ts +45 -22
  171. package/src/connectionStateHandler.ts +78 -45
  172. package/src/container.ts +181 -160
  173. package/src/containerContext.ts +2 -2
  174. package/src/containerStorageAdapter.ts +61 -6
  175. package/src/contracts.ts +5 -4
  176. package/src/debugLogger.ts +1 -1
  177. package/src/deltaManager.ts +15 -8
  178. package/src/deltaQueue.ts +1 -1
  179. package/src/error.ts +1 -1
  180. package/src/index.ts +1 -0
  181. package/src/loadPaused.ts +140 -0
  182. package/src/loader.ts +6 -23
  183. package/src/location-redirection-utilities/resolveWithLocationRedirection.ts +1 -1
  184. package/src/packageVersion.ts +1 -1
  185. package/src/protocol.ts +4 -0
  186. package/src/retriableDocumentStorageService.ts +5 -2
  187. package/src/serializedStateManager.ts +215 -48
  188. package/src/utils.ts +19 -1
@@ -1 +1 @@
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 { IDisposable } from \"@fluidframework/core-interfaces\";\nimport { assert } from \"@fluidframework/core-utils/internal\";\nimport {\n\tFetchSource,\n\tIDocumentStorageService,\n\tIDocumentStorageServicePolicies,\n\tISnapshot,\n\tISnapshotFetchOptions,\n\tISummaryContext,\n} from \"@fluidframework/driver-definitions/internal\";\nimport { runWithRetry } from \"@fluidframework/driver-utils/internal\";\nimport {\n\tICreateBlobResponse,\n\tISnapshotTree,\n\tISummaryHandle,\n\tISummaryTree,\n\tIVersion,\n} from \"@fluidframework/protocol-definitions\";\nimport { ITelemetryLoggerExt } from \"@fluidframework/telemetry-utils\";\nimport { GenericError, UsageError } from \"@fluidframework/telemetry-utils/internal\";\n\nexport class RetriableDocumentStorageService implements IDocumentStorageService, IDisposable {\n\tprivate _disposed = false;\n\tprivate internalStorageService: IDocumentStorageService | undefined;\n\tconstructor(\n\t\tprivate readonly internalStorageServiceP: Promise<IDocumentStorageService>,\n\t\tprivate readonly logger: ITelemetryLoggerExt,\n\t) {\n\t\tthis.internalStorageServiceP.then((s) => (this.internalStorageService = s)).catch(() => {});\n\t}\n\n\tpublic get policies(): IDocumentStorageServicePolicies | undefined {\n\t\tif (this.internalStorageService) {\n\t\t\treturn this.internalStorageService.policies;\n\t\t}\n\t\tthrow new Error(\"storage service not yet instantiated\");\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 async getSnapshotTree(\n\t\tversion?: IVersion,\n\t\tscenarioName?: string,\n\t): Promise<ISnapshotTree | null> {\n\t\treturn this.runWithRetry(\n\t\t\tasync () =>\n\t\t\t\tthis.internalStorageServiceP.then(async (s) =>\n\t\t\t\t\ts.getSnapshotTree(version, scenarioName),\n\t\t\t\t),\n\t\t\t\"storage_getSnapshotTree\",\n\t\t);\n\t}\n\n\tpublic async getSnapshot(snapshotFetchOptions?: ISnapshotFetchOptions): Promise<ISnapshot> {\n\t\treturn this.runWithRetry(\n\t\t\tasync () =>\n\t\t\t\tthis.internalStorageServiceP.then(async (s) => {\n\t\t\t\t\tif (s.getSnapshot !== undefined) {\n\t\t\t\t\t\treturn s.getSnapshot(snapshotFetchOptions);\n\t\t\t\t\t}\n\t\t\t\t\tthrow new UsageError(\n\t\t\t\t\t\t\"getSnapshot api should exist on internal storage in RetriableDocStorageService class\",\n\t\t\t\t\t);\n\t\t\t\t}),\n\t\t\t\"storage_getSnapshot\",\n\t\t);\n\t}\n\n\tpublic async readBlob(id: string): Promise<ArrayBufferLike> {\n\t\treturn this.runWithRetry(\n\t\t\tasync () => this.internalStorageServiceP.then(async (s) => s.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.internalStorageServiceP.then(async (s) =>\n\t\t\t\t\ts.getVersions(versionId, count, scenarioName, fetchSource),\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.internalStorageServiceP.then(async (s) =>\n\t\t\t\ts.uploadSummaryWithContext(summary, context),\n\t\t\t);\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 () =>\n\t\t\t\tthis.internalStorageServiceP.then(async (s) =>\n\t\t\t\t\ts.uploadSummaryWithContext(summary, context),\n\t\t\t\t),\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.internalStorageServiceP.then(async (s) => s.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.internalStorageServiceP.then(async (s) => s.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"]}
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 { IDisposable } from \"@fluidframework/core-interfaces\";\nimport { assert } from \"@fluidframework/core-utils/internal\";\nimport {\n\tFetchSource,\n\tIDocumentStorageService,\n\tIDocumentStorageServicePolicies,\n\tISnapshot,\n\tISnapshotFetchOptions,\n\tISummaryContext,\n} from \"@fluidframework/driver-definitions/internal\";\nimport { runWithRetry } from \"@fluidframework/driver-utils/internal\";\nimport {\n\tICreateBlobResponse,\n\tISnapshotTree,\n\tISummaryHandle,\n\tISummaryTree,\n\tIVersion,\n} from \"@fluidframework/protocol-definitions\";\nimport {\n\tITelemetryLoggerExt,\n\tGenericError,\n\tUsageError,\n} from \"@fluidframework/telemetry-utils/internal\";\n\nexport class RetriableDocumentStorageService implements IDocumentStorageService, IDisposable {\n\tprivate _disposed = false;\n\tprivate internalStorageService: IDocumentStorageService | undefined;\n\tconstructor(\n\t\tprivate readonly internalStorageServiceP: Promise<IDocumentStorageService>,\n\t\tprivate readonly logger: ITelemetryLoggerExt,\n\t) {\n\t\tthis.internalStorageServiceP.then((s) => (this.internalStorageService = s)).catch(() => {});\n\t}\n\n\tpublic get policies(): IDocumentStorageServicePolicies | undefined {\n\t\tif (this.internalStorageService) {\n\t\t\treturn this.internalStorageService.policies;\n\t\t}\n\t\tthrow new Error(\"storage service not yet instantiated\");\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 async getSnapshotTree(\n\t\tversion?: IVersion,\n\t\tscenarioName?: string,\n\t): Promise<ISnapshotTree | null> {\n\t\treturn this.runWithRetry(\n\t\t\tasync () =>\n\t\t\t\tthis.internalStorageServiceP.then(async (s) =>\n\t\t\t\t\ts.getSnapshotTree(version, scenarioName),\n\t\t\t\t),\n\t\t\t\"storage_getSnapshotTree\",\n\t\t);\n\t}\n\n\tpublic async getSnapshot(snapshotFetchOptions?: ISnapshotFetchOptions): Promise<ISnapshot> {\n\t\treturn this.runWithRetry(\n\t\t\tasync () =>\n\t\t\t\tthis.internalStorageServiceP.then(async (s) => {\n\t\t\t\t\tif (s.getSnapshot !== undefined) {\n\t\t\t\t\t\treturn s.getSnapshot(snapshotFetchOptions);\n\t\t\t\t\t}\n\t\t\t\t\tthrow new UsageError(\n\t\t\t\t\t\t\"getSnapshot api should exist on internal storage in RetriableDocStorageService class\",\n\t\t\t\t\t);\n\t\t\t\t}),\n\t\t\t\"storage_getSnapshot\",\n\t\t);\n\t}\n\n\tpublic async readBlob(id: string): Promise<ArrayBufferLike> {\n\t\treturn this.runWithRetry(\n\t\t\tasync () => this.internalStorageServiceP.then(async (s) => s.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.internalStorageServiceP.then(async (s) =>\n\t\t\t\t\ts.getVersions(versionId, count, scenarioName, fetchSource),\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.internalStorageServiceP.then(async (s) =>\n\t\t\t\ts.uploadSummaryWithContext(summary, context),\n\t\t\t);\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 () =>\n\t\t\t\tthis.internalStorageServiceP.then(async (s) =>\n\t\t\t\t\ts.uploadSummaryWithContext(summary, context),\n\t\t\t\t),\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.internalStorageServiceP.then(async (s) => s.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.internalStorageServiceP.then(async (s) => s.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"]}
@@ -5,9 +5,14 @@
5
5
  import { IGetPendingLocalStateProps, IRuntime } from "@fluidframework/container-definitions/internal";
6
6
  import { IDocumentStorageService, IResolvedUrl, ISnapshot } from "@fluidframework/driver-definitions/internal";
7
7
  import { ISequencedDocumentMessage, ISnapshotTree, IVersion } from "@fluidframework/protocol-definitions";
8
- import { ITelemetryLoggerExt } from "@fluidframework/telemetry-utils";
9
8
  import { MonitoringContext } from "@fluidframework/telemetry-utils/internal";
9
+ import type { ITelemetryBaseLogger, IEventProvider, IEvent } from "@fluidframework/core-interfaces";
10
10
  import { ISerializableBlobContents } from "./containerStorageAdapter.js";
11
+ /**
12
+ * This is very similar to {@link @fluidframework/protocol-definitions/internal#ISnapshot}, but the difference is
13
+ * that the blobs of ISnapshot are of type ArrayBufferLike, while the blobs of this interface are serializable because
14
+ * they are already converted to string.
15
+ */
11
16
  export interface SnapshotWithBlobs {
12
17
  /**
13
18
  * Snapshot from which container initially loaded.
@@ -22,18 +27,34 @@ export interface SnapshotWithBlobs {
22
27
  /**
23
28
  * State saved by a container at close time, to be used to load a new instance
24
29
  * of the container to the same state
30
+ *
31
+ * This is very similar to {@link @fluidframework/protocol-definitions/internal#ISnapshot}, but the difference is
32
+ * that the blobs of ISnapshot are of type ArrayBufferLike, while the blobs of this interface are serializable because
33
+ * they are already converted to string.
34
+ *
25
35
  * @internal
26
36
  */
27
37
  export interface IPendingContainerState extends SnapshotWithBlobs {
38
+ /** This container was attached (as opposed to IPendingDetachedContainerState.attached which is false) */
28
39
  attached: true;
40
+ /**
41
+ * Runtime-specific state that will be needed to properly rehydrate
42
+ * (it's included in ContainerContext passed to instantiateRuntime)
43
+ */
29
44
  pendingRuntimeState: unknown;
45
+ /**
46
+ * Any group snapshots (aka delay-loaded) we've downloaded from the service for this container
47
+ */
48
+ loadedGroupIdSnapshots?: Record<string, ISnapshotInfo>;
30
49
  /**
31
50
  * All ops since base snapshot sequence number up to the latest op
32
51
  * seen when the container was closed. Used to apply stashed (saved pending)
33
52
  * ops at the same sequence number at which they were made.
34
53
  */
35
54
  savedOps: ISequencedDocumentMessage[];
55
+ /** The Container's URL in the service, needed to hook up the driver during rehydration */
36
56
  url: string;
57
+ /** If the Container was connected when serialized, its clientId. Used as the initial clientId upon rehydration, until reconnected. */
37
58
  clientId?: string;
38
59
  }
39
60
  /**
@@ -42,41 +63,99 @@ export interface IPendingContainerState extends SnapshotWithBlobs {
42
63
  * @internal
43
64
  */
44
65
  export interface IPendingDetachedContainerState extends SnapshotWithBlobs {
66
+ /** This container was not attached (as opposed to IPendingContainerState.attached which is true) */
45
67
  attached: false;
68
+ /** Indicates whether we expect the rehydrated container to have non-empty Detached Blob Storage */
46
69
  hasAttachmentBlobs: boolean;
70
+ /**
71
+ * Runtime-specific state that will be needed to properly rehydrate
72
+ * (it's included in ContainerContext passed to instantiateRuntime)
73
+ */
47
74
  pendingRuntimeState?: unknown;
48
75
  }
49
76
  export interface ISnapshotInfo extends SnapshotWithBlobs {
50
77
  snapshotSequenceNumber: number;
78
+ snapshotFetchedTime?: number | undefined;
79
+ }
80
+ export type ISerializedStateManagerDocumentStorageService = Pick<IDocumentStorageService, "getSnapshot" | "getSnapshotTree" | "getVersions" | "readBlob"> & {
81
+ loadedGroupIdSnapshots: Record<string, ISnapshot>;
82
+ };
83
+ interface ISerializerEvent extends IEvent {
84
+ (event: "saved", listener: (dirty: boolean) => void): void;
51
85
  }
86
+ /**
87
+ * Helper class to manage the state of the container needed for proper serialization.
88
+ *
89
+ * It holds the pendingLocalState the container was rehydrated from (if any),
90
+ * as well as the snapshot to be used for serialization.
91
+ * It also keeps track of container dirty state and which local ops have been processed
92
+ */
52
93
  export declare class SerializedStateManager {
53
94
  private readonly pendingLocalState;
54
95
  private readonly storageAdapter;
55
96
  private readonly _offlineLoadEnabled;
56
- private readonly newSnapshotFetched?;
97
+ private readonly containerDirty;
57
98
  private readonly processedOps;
58
- private snapshot;
59
99
  private readonly mc;
100
+ private snapshot;
60
101
  private latestSnapshot;
61
- private refreshSnapshot;
62
- constructor(pendingLocalState: IPendingContainerState | undefined, subLogger: ITelemetryLoggerExt, storageAdapter: Pick<IDocumentStorageService, "readBlob" | "getSnapshotTree" | "getSnapshot" | "getVersions">, _offlineLoadEnabled: boolean, newSnapshotFetched?: (() => void) | undefined);
102
+ private refreshSnapshotP;
103
+ private readonly lastSavedOpSequenceNumber;
104
+ /**
105
+ * @param pendingLocalState - The pendingLocalState being rehydrated, if any (undefined when loading directly from storage)
106
+ * @param subLogger - Container's logger to use as parent for our logger
107
+ * @param storageAdapter - Storage adapter for fetching snapshots
108
+ * @param _offlineLoadEnabled - Is serializing/rehydrating containers allowed?
109
+ * @param containerEvent - Source of the "saved" event when the container has all its pending state uploaded
110
+ * @param containerDirty - Is the container "dirty"? That's the opposite of "saved" - there is pending state that may not have been received yet by the service.
111
+ */
112
+ constructor(pendingLocalState: IPendingContainerState | undefined, subLogger: ITelemetryBaseLogger, storageAdapter: ISerializedStateManagerDocumentStorageService, _offlineLoadEnabled: boolean, containerEvent: IEventProvider<ISerializerEvent>, containerDirty: () => boolean);
63
113
  get offlineLoadEnabled(): boolean;
114
+ /**
115
+ * Promise that will resolve (or reject) once we've tried to download the latest snapshot(s) from storage
116
+ */
117
+ get waitForInitialRefresh(): Promise<void> | undefined;
118
+ /**
119
+ * Called whenever an incoming op is processed by the Container
120
+ */
64
121
  addProcessedOp(message: ISequencedDocumentMessage): void;
122
+ /**
123
+ * This wraps the basic functionality of fetching the snapshot for this container during Container load.
124
+ *
125
+ * If we have pendingLocalState, we get the snapshot from there.
126
+ * Otherwise, fetch it from storage (according to specifiedVersion if provided)
127
+ *
128
+ * @param specifiedVersion - If a version is specified and we don't have pendingLocalState, fetch this version from storage
129
+ * @param supportGetSnapshotApi - a boolean indicating whether to use the fetchISnapshot or fetchISnapshotTree.
130
+ * @returns The snapshot to boot the container from
131
+ */
65
132
  fetchSnapshot(specifiedVersion: string | undefined, supportGetSnapshotApi: boolean): Promise<{
66
- baseSnapshot: ISnapshotTree;
133
+ baseSnapshot: ISnapshotTree | ISnapshot;
67
134
  version: IVersion | undefined;
68
135
  }>;
136
+ /**
137
+ * Fetch the latest snapshot for the container, including delay-loaded groupIds if pendingLocalState was provided and contained any groupIds.
138
+ * Note that this will update the StorageAdapter's cached snapshots for the groupIds (if present)
139
+ *
140
+ * @param supportGetSnapshotApi - a boolean indicating whether to use the fetchISnapshot or fetchISnapshotTree (must be true to fetch by groupIds)
141
+ */
142
+ private refreshLatestSnapshot;
69
143
  /**
70
144
  * Updates class snapshot and processedOps if we have a new snapshot and it's among processedOps range.
71
145
  */
72
146
  private updateSnapshotAndProcessedOpsMaybe;
73
147
  /**
148
+ * When the Container attaches, we need to stash the initial snapshot (a form of the attach summary).
74
149
  * This method is only meant to be used by Container.attach() to set the initial
75
150
  * base snapshot when attaching.
76
- * @param snapshot - snapshot and blobs collected while attaching
151
+ * @param snapshot - snapshot and blobs collected while attaching (a form of the attach summary)
77
152
  */
78
153
  setInitialSnapshot(snapshot: SnapshotWithBlobs | undefined): void;
79
- getPendingLocalStateCore(props: IGetPendingLocalStateProps, clientId: string | undefined, runtime: Pick<IRuntime, "getPendingLocalState">, resolvedUrl: IResolvedUrl): Promise<string>;
154
+ /**
155
+ * Assembles and serializes the {@link IPendingContainerState} for the container,
156
+ * to be stored and used to rehydrate the container at a later time.
157
+ */
158
+ getPendingLocalState(props: IGetPendingLocalStateProps, clientId: string | undefined, runtime: Pick<IRuntime, "getPendingLocalState">, resolvedUrl: IResolvedUrl): Promise<string>;
80
159
  }
81
160
  /**
82
161
  * Retrieves the most recent snapshot and returns its info.
@@ -86,7 +165,7 @@ export declare class SerializedStateManager {
86
165
  * @param supportGetSnapshotApi - a boolean indicating whether to use the fetchISnapshot or fetchISnapshotTree.
87
166
  * @returns a SnapshotInfo object containing the snapshot tree, snapshot blobs and its sequence number.
88
167
  */
89
- export declare function getLatestSnapshotInfo(mc: MonitoringContext, storageAdapter: Pick<IDocumentStorageService, "getSnapshot" | "getSnapshotTree" | "getVersions" | "readBlob">, supportGetSnapshotApi: boolean): Promise<ISnapshotInfo | undefined>;
168
+ export declare function getLatestSnapshotInfo(mc: MonitoringContext, storageAdapter: ISerializedStateManagerDocumentStorageService, supportGetSnapshotApi: boolean): Promise<ISnapshotInfo | undefined>;
90
169
  /**
91
170
  * Fetches an ISnapshot from a storage adapter based on the specified version.
92
171
  *
@@ -111,4 +190,5 @@ export declare function fetchISnapshotTree(mc: MonitoringContext, storageAdapter
111
190
  snapshot?: ISnapshotTree;
112
191
  version?: IVersion | undefined;
113
192
  }>;
193
+ export {};
114
194
  //# sourceMappingURL=serializedStateManager.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"serializedStateManager.d.ts","sourceRoot":"","sources":["../src/serializedStateManager.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"serializedStateManager.d.ts","sourceRoot":"","sources":["../src/serializedStateManager.ts"],"names":[],"mappings":"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"}
@@ -5,41 +5,85 @@
5
5
  */
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
7
  exports.fetchISnapshotTree = exports.fetchISnapshot = exports.getLatestSnapshotInfo = exports.SerializedStateManager = void 0;
8
+ const client_utils_1 = require("@fluid-internal/client-utils");
8
9
  const internal_1 = require("@fluidframework/core-utils/internal");
9
- const internal_2 = require("@fluidframework/driver-utils/internal");
10
- const internal_3 = require("@fluidframework/telemetry-utils/internal");
10
+ const internal_2 = require("@fluidframework/driver-definitions/internal");
11
+ const internal_3 = require("@fluidframework/driver-utils/internal");
12
+ const internal_4 = require("@fluidframework/telemetry-utils/internal");
11
13
  const containerStorageAdapter_js_1 = require("./containerStorageAdapter.js");
12
14
  const utils_js_1 = require("./utils.js");
15
+ /**
16
+ * Helper class to manage the state of the container needed for proper serialization.
17
+ *
18
+ * It holds the pendingLocalState the container was rehydrated from (if any),
19
+ * as well as the snapshot to be used for serialization.
20
+ * It also keeps track of container dirty state and which local ops have been processed
21
+ */
13
22
  class SerializedStateManager {
14
- constructor(pendingLocalState, subLogger, storageAdapter, _offlineLoadEnabled, newSnapshotFetched) {
23
+ /**
24
+ * @param pendingLocalState - The pendingLocalState being rehydrated, if any (undefined when loading directly from storage)
25
+ * @param subLogger - Container's logger to use as parent for our logger
26
+ * @param storageAdapter - Storage adapter for fetching snapshots
27
+ * @param _offlineLoadEnabled - Is serializing/rehydrating containers allowed?
28
+ * @param containerEvent - Source of the "saved" event when the container has all its pending state uploaded
29
+ * @param containerDirty - Is the container "dirty"? That's the opposite of "saved" - there is pending state that may not have been received yet by the service.
30
+ */
31
+ constructor(pendingLocalState, subLogger, storageAdapter, _offlineLoadEnabled, containerEvent, containerDirty) {
15
32
  this.pendingLocalState = pendingLocalState;
16
33
  this.storageAdapter = storageAdapter;
17
34
  this._offlineLoadEnabled = _offlineLoadEnabled;
18
- this.newSnapshotFetched = newSnapshotFetched;
35
+ this.containerDirty = containerDirty;
19
36
  this.processedOps = [];
20
- this.mc = (0, internal_3.createChildMonitoringContext)({
37
+ this.lastSavedOpSequenceNumber = 0;
38
+ this.mc = (0, internal_4.createChildMonitoringContext)({
21
39
  logger: subLogger,
22
40
  namespace: "serializedStateManager",
23
41
  });
42
+ if (pendingLocalState && pendingLocalState.savedOps.length > 0) {
43
+ const savedOpsSize = pendingLocalState.savedOps.length;
44
+ this.lastSavedOpSequenceNumber =
45
+ pendingLocalState.savedOps[savedOpsSize - 1].sequenceNumber;
46
+ }
47
+ containerEvent.once("saved", () => this.updateSnapshotAndProcessedOpsMaybe());
24
48
  }
25
49
  get offlineLoadEnabled() {
26
50
  return this._offlineLoadEnabled;
27
51
  }
52
+ /**
53
+ * Promise that will resolve (or reject) once we've tried to download the latest snapshot(s) from storage
54
+ */
55
+ get waitForInitialRefresh() {
56
+ return this.refreshSnapshotP;
57
+ }
58
+ /**
59
+ * Called whenever an incoming op is processed by the Container
60
+ */
28
61
  addProcessedOp(message) {
29
62
  if (this.offlineLoadEnabled) {
30
63
  this.processedOps.push(message);
31
64
  this.updateSnapshotAndProcessedOpsMaybe();
32
65
  }
33
66
  }
67
+ /**
68
+ * This wraps the basic functionality of fetching the snapshot for this container during Container load.
69
+ *
70
+ * If we have pendingLocalState, we get the snapshot from there.
71
+ * Otherwise, fetch it from storage (according to specifiedVersion if provided)
72
+ *
73
+ * @param specifiedVersion - If a version is specified and we don't have pendingLocalState, fetch this version from storage
74
+ * @param supportGetSnapshotApi - a boolean indicating whether to use the fetchISnapshot or fetchISnapshotTree.
75
+ * @returns The snapshot to boot the container from
76
+ */
34
77
  async fetchSnapshot(specifiedVersion, supportGetSnapshotApi) {
35
78
  if (this.pendingLocalState === undefined) {
36
- const { baseSnapshot, version } = await getSnapshotTree(this.mc, this.storageAdapter, supportGetSnapshotApi, specifiedVersion);
79
+ const { baseSnapshot, version } = await getSnapshot(this.mc, this.storageAdapter, supportGetSnapshotApi, specifiedVersion);
80
+ const baseSnapshotTree = (0, internal_3.getSnapshotTree)(baseSnapshot);
37
81
  // non-interactive clients will not have any pending state we want to save
38
82
  if (this.offlineLoadEnabled) {
39
- const snapshotBlobs = await (0, containerStorageAdapter_js_1.getBlobContentsFromTree)(baseSnapshot, this.storageAdapter);
40
- const attributes = await (0, utils_js_1.getDocumentAttributes)(this.storageAdapter, baseSnapshot);
83
+ const snapshotBlobs = await (0, containerStorageAdapter_js_1.getBlobContentsFromTree)(baseSnapshotTree, this.storageAdapter);
84
+ const attributes = await (0, utils_js_1.getDocumentAttributes)(this.storageAdapter, baseSnapshotTree);
41
85
  this.snapshot = {
42
- baseSnapshot,
86
+ baseSnapshot: baseSnapshotTree,
43
87
  snapshotBlobs,
44
88
  snapshotSequenceNumber: attributes.sequenceNumber,
45
89
  };
@@ -54,19 +98,66 @@ class SerializedStateManager {
54
98
  snapshotBlobs,
55
99
  snapshotSequenceNumber: attributes.sequenceNumber,
56
100
  };
57
- this.refreshSnapshot ?? (this.refreshSnapshot = (async () => {
58
- this.latestSnapshot = await getLatestSnapshotInfo(this.mc, this.storageAdapter, supportGetSnapshotApi);
59
- this.newSnapshotFetched?.();
60
- this.updateSnapshotAndProcessedOpsMaybe();
61
- })());
62
- return { baseSnapshot, version: undefined };
101
+ if (this.refreshSnapshotP === undefined &&
102
+ this.mc.config.getBoolean("Fluid.Container.enableOfflineSnapshotRefresh") === true) {
103
+ // Don't block on the refresh snapshot call - it is for the next time we serialize, not booting this incarnation
104
+ this.refreshSnapshotP = this.refreshLatestSnapshot(supportGetSnapshotApi);
105
+ this.refreshSnapshotP.catch((e) => {
106
+ this.mc.logger.sendErrorEvent({
107
+ eventName: "RefreshLatestSnapshotFailed",
108
+ error: e,
109
+ });
110
+ });
111
+ }
112
+ const blobContents = new Map();
113
+ for (const [id, value] of Object.entries(snapshotBlobs)) {
114
+ blobContents.set(id, (0, client_utils_1.stringToBuffer)(value, "utf8"));
115
+ }
116
+ const iSnapshot = {
117
+ sequenceNumber: this.snapshot.snapshotSequenceNumber,
118
+ snapshotTree: baseSnapshot,
119
+ blobContents,
120
+ latestSequenceNumber: undefined,
121
+ ops: [],
122
+ snapshotFormatV: 1,
123
+ };
124
+ return { baseSnapshot: iSnapshot, version: undefined };
63
125
  }
64
126
  }
127
+ /**
128
+ * Fetch the latest snapshot for the container, including delay-loaded groupIds if pendingLocalState was provided and contained any groupIds.
129
+ * Note that this will update the StorageAdapter's cached snapshots for the groupIds (if present)
130
+ *
131
+ * @param supportGetSnapshotApi - a boolean indicating whether to use the fetchISnapshot or fetchISnapshotTree (must be true to fetch by groupIds)
132
+ */
133
+ async refreshLatestSnapshot(supportGetSnapshotApi) {
134
+ this.latestSnapshot = await getLatestSnapshotInfo(this.mc, this.storageAdapter, supportGetSnapshotApi);
135
+ // These are loading groupIds that the containerRuntime has requested over its lifetime.
136
+ // We will fetch the latest snapshot for the groupIds, which will update storageAdapter.loadedGroupIdSnapshots's cache
137
+ const downloadedGroupIds = Object.keys(this.storageAdapter.loadedGroupIdSnapshots);
138
+ if (supportGetSnapshotApi && downloadedGroupIds.length > 0) {
139
+ (0, internal_1.assert)(this.storageAdapter.getSnapshot !== undefined, 0x972 /* getSnapshot should exist */);
140
+ // (This is a separate network call from above because it requires work for storage to add a special base groupId)
141
+ const snapshot = await this.storageAdapter.getSnapshot({
142
+ versionId: undefined,
143
+ scenarioName: "getLatestSnapshotInfo",
144
+ cacheSnapshot: false,
145
+ loadingGroupIds: downloadedGroupIds,
146
+ fetchSource: internal_2.FetchSource.noCache,
147
+ });
148
+ (0, internal_1.assert)(snapshot !== undefined, 0x973 /* Snapshot should exist */);
149
+ }
150
+ this.updateSnapshotAndProcessedOpsMaybe();
151
+ }
65
152
  /**
66
153
  * Updates class snapshot and processedOps if we have a new snapshot and it's among processedOps range.
67
154
  */
68
155
  updateSnapshotAndProcessedOpsMaybe() {
69
- if (this.latestSnapshot === undefined || this.processedOps.length === 0) {
156
+ if (this.latestSnapshot === undefined ||
157
+ this.processedOps.length === 0 ||
158
+ this.processedOps[this.processedOps.length - 1].sequenceNumber <
159
+ this.lastSavedOpSequenceNumber ||
160
+ this.containerDirty()) {
70
161
  // can't refresh latest snapshot until we have processed the ops up to it.
71
162
  // Pending state would be behind the latest snapshot.
72
163
  return;
@@ -103,9 +194,10 @@ class SerializedStateManager {
103
194
  }
104
195
  }
105
196
  /**
197
+ * When the Container attaches, we need to stash the initial snapshot (a form of the attach summary).
106
198
  * This method is only meant to be used by Container.attach() to set the initial
107
199
  * base snapshot when attaching.
108
- * @param snapshot - snapshot and blobs collected while attaching
200
+ * @param snapshot - snapshot and blobs collected while attaching (a form of the attach summary)
109
201
  */
110
202
  setInitialSnapshot(snapshot) {
111
203
  if (this.offlineLoadEnabled) {
@@ -120,27 +212,47 @@ class SerializedStateManager {
120
212
  this.snapshot = { ...snapshot, snapshotSequenceNumber: attributes.sequenceNumber };
121
213
  }
122
214
  }
123
- async getPendingLocalStateCore(props, clientId, runtime, resolvedUrl) {
124
- return internal_3.PerformanceEvent.timedExecAsync(this.mc.logger, {
215
+ /**
216
+ * Assembles and serializes the {@link IPendingContainerState} for the container,
217
+ * to be stored and used to rehydrate the container at a later time.
218
+ */
219
+ async getPendingLocalState(props, clientId, runtime, resolvedUrl) {
220
+ return internal_4.PerformanceEvent.timedExecAsync(this.mc.logger, {
125
221
  eventName: "getPendingLocalState",
126
222
  notifyImminentClosure: props.notifyImminentClosure,
127
223
  processedOpsSize: this.processedOps.length,
128
224
  clientId,
129
225
  }, async () => {
130
226
  if (!this.offlineLoadEnabled) {
131
- throw new internal_3.UsageError("Can't get pending local state unless offline load is enabled");
227
+ throw new internal_4.UsageError("Can't get pending local state unless offline load is enabled");
132
228
  }
133
229
  (0, internal_1.assert)(this.snapshot !== undefined, 0x8e5 /* no base data */);
134
- const pendingRuntimeState = await runtime.getPendingLocalState(props);
230
+ const pendingRuntimeState = await runtime.getPendingLocalState({
231
+ ...props,
232
+ snapshotSequenceNumber: this.snapshot.snapshotSequenceNumber,
233
+ sessionExpiryTimerStarted: this.snapshot.snapshotFetchedTime,
234
+ });
235
+ // This conversion is required because ArrayBufferLike doesn't survive JSON.stringify
236
+ const loadedGroupIdSnapshots = {};
237
+ let hasGroupIdSnapshots = false;
238
+ const groupIdSnapshots = Object.entries(this.storageAdapter.loadedGroupIdSnapshots);
239
+ if (groupIdSnapshots.length > 0) {
240
+ for (const [groupId, snapshot] of groupIdSnapshots) {
241
+ hasGroupIdSnapshots = true;
242
+ loadedGroupIdSnapshots[groupId] = (0, utils_js_1.convertSnapshotToSnapshotInfo)(snapshot);
243
+ }
244
+ }
135
245
  const pendingState = {
136
246
  attached: true,
137
247
  pendingRuntimeState,
138
248
  baseSnapshot: this.snapshot.baseSnapshot,
139
249
  snapshotBlobs: this.snapshot.snapshotBlobs,
250
+ loadedGroupIdSnapshots: hasGroupIdSnapshots
251
+ ? loadedGroupIdSnapshots
252
+ : undefined,
140
253
  savedOps: this.processedOps,
141
254
  url: resolvedUrl.url,
142
- // no need to save this if there is no pending runtime state
143
- clientId: pendingRuntimeState !== undefined ? clientId : undefined,
255
+ clientId,
144
256
  };
145
257
  return JSON.stringify(pendingState);
146
258
  });
@@ -156,12 +268,19 @@ exports.SerializedStateManager = SerializedStateManager;
156
268
  * @returns a SnapshotInfo object containing the snapshot tree, snapshot blobs and its sequence number.
157
269
  */
158
270
  async function getLatestSnapshotInfo(mc, storageAdapter, supportGetSnapshotApi) {
159
- return internal_3.PerformanceEvent.timedExecAsync(mc.logger, { eventName: "GetLatestSnapshotInfo" }, async () => {
160
- const { baseSnapshot } = await getSnapshotTree(mc, storageAdapter, supportGetSnapshotApi, undefined);
161
- const snapshotBlobs = await (0, containerStorageAdapter_js_1.getBlobContentsFromTree)(baseSnapshot, storageAdapter);
162
- const attributes = await (0, utils_js_1.getDocumentAttributes)(storageAdapter, baseSnapshot);
271
+ return internal_4.PerformanceEvent.timedExecAsync(mc.logger, { eventName: "GetLatestSnapshotInfo" }, async () => {
272
+ const { baseSnapshot } = await getSnapshot(mc, storageAdapter, supportGetSnapshotApi, undefined);
273
+ const baseSnapshotTree = (0, internal_3.getSnapshotTree)(baseSnapshot);
274
+ const snapshotFetchedTime = Date.now();
275
+ const snapshotBlobs = await (0, containerStorageAdapter_js_1.getBlobContentsFromTree)(baseSnapshotTree, storageAdapter);
276
+ const attributes = await (0, utils_js_1.getDocumentAttributes)(storageAdapter, baseSnapshotTree);
163
277
  const snapshotSequenceNumber = attributes.sequenceNumber;
164
- return { baseSnapshot, snapshotBlobs, snapshotSequenceNumber };
278
+ return {
279
+ baseSnapshot: baseSnapshotTree,
280
+ snapshotBlobs,
281
+ snapshotSequenceNumber,
282
+ snapshotFetchedTime,
283
+ };
165
284
  }).catch(() => undefined);
166
285
  }
167
286
  exports.getLatestSnapshotInfo = getLatestSnapshotInfo;
@@ -174,15 +293,12 @@ exports.getLatestSnapshotInfo = getLatestSnapshotInfo;
174
293
  * @param specifiedVersion - An optional version string specifying the version of the snapshot tree to fetch.
175
294
  * @returns - An ISnapshotTree and its version.
176
295
  */
177
- async function getSnapshotTree(mc, storageAdapter, supportGetSnapshotApi, specifiedVersion) {
296
+ async function getSnapshot(mc, storageAdapter, supportGetSnapshotApi, specifiedVersion) {
178
297
  const { snapshot, version } = supportGetSnapshotApi
179
298
  ? await fetchISnapshot(mc, storageAdapter, specifiedVersion)
180
299
  : await fetchISnapshotTree(mc, storageAdapter, specifiedVersion);
181
- const baseSnapshot = (0, internal_2.isInstanceOfISnapshot)(snapshot)
182
- ? snapshot.snapshotTree
183
- : snapshot;
184
- (0, internal_1.assert)(baseSnapshot !== undefined, 0x8e4 /* Snapshot should exist */);
185
- return { baseSnapshot, version };
300
+ (0, internal_1.assert)(snapshot !== undefined, 0x8e4 /* Snapshot should exist */);
301
+ return { baseSnapshot: snapshot, version };
186
302
  }
187
303
  /**
188
304
  * Fetches an ISnapshot from a storage adapter based on the specified version.