@fluidframework/container-loader 2.0.0-dev-rc.1.0.0.225277 → 2.0.0-dev-rc.1.0.0.232845

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 (159) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/README.md +3 -9
  3. package/api-report/container-loader.api.md +1 -1
  4. package/dist/attachment.d.ts +116 -0
  5. package/dist/attachment.d.ts.map +1 -0
  6. package/dist/attachment.js +82 -0
  7. package/dist/attachment.js.map +1 -0
  8. package/dist/{audience.cjs → audience.js} +1 -1
  9. package/dist/audience.js.map +1 -0
  10. package/dist/{catchUpMonitor.cjs → catchUpMonitor.js} +1 -1
  11. package/dist/catchUpMonitor.js.map +1 -0
  12. package/dist/connectionManager.d.ts.map +1 -1
  13. package/dist/{connectionManager.cjs → connectionManager.js} +6 -7
  14. package/dist/connectionManager.js.map +1 -0
  15. package/dist/connectionState.d.ts +1 -1
  16. package/dist/{connectionState.cjs → connectionState.js} +2 -2
  17. package/dist/{connectionState.cjs.map → connectionState.js.map} +1 -1
  18. package/dist/{connectionStateHandler.cjs → connectionStateHandler.js} +3 -3
  19. package/dist/connectionStateHandler.js.map +1 -0
  20. package/dist/container-loader-alpha.d.ts +2 -2
  21. package/dist/container-loader-beta.d.ts +24 -1
  22. package/dist/container-loader-public.d.ts +24 -1
  23. package/dist/container-loader-untrimmed.d.ts +2 -2
  24. package/dist/container.d.ts +21 -8
  25. package/dist/container.d.ts.map +1 -1
  26. package/dist/{container.cjs → container.js} +192 -164
  27. package/dist/container.js.map +1 -0
  28. package/dist/containerContext.d.ts +3 -2
  29. package/dist/containerContext.d.ts.map +1 -1
  30. package/dist/{containerContext.cjs → containerContext.js} +3 -2
  31. package/dist/containerContext.js.map +1 -0
  32. package/dist/containerStorageAdapter.d.ts +3 -3
  33. package/dist/containerStorageAdapter.d.ts.map +1 -1
  34. package/dist/{containerStorageAdapter.cjs → containerStorageAdapter.js} +13 -14
  35. package/dist/containerStorageAdapter.js.map +1 -0
  36. package/dist/{contracts.cjs → contracts.js} +1 -1
  37. package/dist/contracts.js.map +1 -0
  38. package/dist/{debugLogger.cjs → debugLogger.js} +1 -1
  39. package/dist/debugLogger.js.map +1 -0
  40. package/dist/{deltaManager.cjs → deltaManager.js} +3 -3
  41. package/dist/deltaManager.js.map +1 -0
  42. package/dist/{deltaQueue.cjs → deltaQueue.js} +1 -1
  43. package/dist/deltaQueue.js.map +1 -0
  44. package/dist/{disposal.cjs → disposal.js} +1 -1
  45. package/dist/disposal.js.map +1 -0
  46. package/dist/{error.cjs → error.js} +1 -1
  47. package/dist/error.js.map +1 -0
  48. package/dist/{index.cjs → index.js} +6 -6
  49. package/dist/index.js.map +1 -0
  50. package/dist/loader.d.ts +1 -1
  51. package/dist/{loader.cjs → loader.js} +5 -5
  52. package/dist/{loader.cjs.map → loader.js.map} +1 -1
  53. package/dist/location-redirection-utilities/{index.cjs → index.js} +2 -2
  54. package/dist/location-redirection-utilities/index.js.map +1 -0
  55. package/dist/location-redirection-utilities/{resolveWithLocationRedirection.cjs → resolveWithLocationRedirection.js} +1 -1
  56. package/dist/location-redirection-utilities/resolveWithLocationRedirection.js.map +1 -0
  57. package/dist/{noopHeuristic.cjs → noopHeuristic.js} +1 -1
  58. package/dist/noopHeuristic.js.map +1 -0
  59. package/dist/packageVersion.d.ts +1 -1
  60. package/dist/{packageVersion.cjs → packageVersion.js} +2 -2
  61. package/dist/packageVersion.js.map +1 -0
  62. package/dist/{protocol.cjs → protocol.js} +1 -1
  63. package/dist/protocol.js.map +1 -0
  64. package/dist/protocolTreeDocumentStorageService.d.ts +1 -0
  65. package/dist/protocolTreeDocumentStorageService.d.ts.map +1 -1
  66. package/dist/{protocolTreeDocumentStorageService.cjs → protocolTreeDocumentStorageService.js} +2 -1
  67. package/dist/protocolTreeDocumentStorageService.js.map +1 -0
  68. package/dist/{quorum.cjs → quorum.js} +1 -1
  69. package/dist/quorum.js.map +1 -0
  70. package/dist/retriableDocumentStorageService.d.ts +2 -1
  71. package/dist/retriableDocumentStorageService.d.ts.map +1 -1
  72. package/dist/{retriableDocumentStorageService.cjs → retriableDocumentStorageService.js} +9 -1
  73. package/dist/retriableDocumentStorageService.js.map +1 -0
  74. package/dist/tsdoc-metadata.json +1 -1
  75. package/dist/utils.d.ts +13 -7
  76. package/dist/utils.d.ts.map +1 -1
  77. package/dist/utils.js +211 -0
  78. package/dist/utils.js.map +1 -0
  79. package/lib/attachment.d.mts +112 -0
  80. package/lib/attachment.d.mts.map +1 -0
  81. package/lib/attachment.mjs +74 -0
  82. package/lib/attachment.mjs.map +1 -0
  83. package/lib/connectionManager.d.mts.map +1 -1
  84. package/lib/connectionManager.mjs +2 -5
  85. package/lib/connectionManager.mjs.map +1 -1
  86. package/lib/connectionState.d.mts +1 -1
  87. package/lib/connectionState.mjs +1 -1
  88. package/lib/connectionState.mjs.map +1 -1
  89. package/lib/container-loader-alpha.d.mts +2 -2
  90. package/lib/container-loader-beta.d.mts +24 -1
  91. package/lib/container-loader-public.d.mts +24 -1
  92. package/lib/container-loader-untrimmed.d.mts +2 -2
  93. package/lib/container.d.mts +21 -8
  94. package/lib/container.d.mts.map +1 -1
  95. package/lib/container.mjs +176 -151
  96. package/lib/container.mjs.map +1 -1
  97. package/lib/containerContext.d.mts +3 -2
  98. package/lib/containerContext.d.mts.map +1 -1
  99. package/lib/containerContext.mjs +2 -1
  100. package/lib/containerContext.mjs.map +1 -1
  101. package/lib/containerStorageAdapter.d.mts +3 -3
  102. package/lib/containerStorageAdapter.d.mts.map +1 -1
  103. package/lib/containerStorageAdapter.mjs +10 -11
  104. package/lib/containerStorageAdapter.mjs.map +1 -1
  105. package/lib/loader.d.mts +1 -1
  106. package/lib/loader.mjs.map +1 -1
  107. package/lib/packageVersion.d.mts +1 -1
  108. package/lib/packageVersion.mjs +1 -1
  109. package/lib/packageVersion.mjs.map +1 -1
  110. package/lib/protocolTreeDocumentStorageService.d.mts +1 -0
  111. package/lib/protocolTreeDocumentStorageService.d.mts.map +1 -1
  112. package/lib/protocolTreeDocumentStorageService.mjs +1 -0
  113. package/lib/protocolTreeDocumentStorageService.mjs.map +1 -1
  114. package/lib/retriableDocumentStorageService.d.mts +2 -1
  115. package/lib/retriableDocumentStorageService.d.mts.map +1 -1
  116. package/lib/retriableDocumentStorageService.mjs +9 -1
  117. package/lib/retriableDocumentStorageService.mjs.map +1 -1
  118. package/lib/utils.d.mts +13 -7
  119. package/lib/utils.d.mts.map +1 -1
  120. package/lib/utils.mjs +96 -29
  121. package/lib/utils.mjs.map +1 -1
  122. package/package.json +36 -30
  123. package/src/attachment.ts +219 -0
  124. package/src/connectionManager.ts +2 -4
  125. package/src/connectionState.ts +1 -1
  126. package/src/container.ts +260 -191
  127. package/src/containerContext.ts +2 -1
  128. package/src/containerStorageAdapter.ts +15 -12
  129. package/src/loader.ts +1 -1
  130. package/src/packageVersion.ts +1 -1
  131. package/src/protocolTreeDocumentStorageService.ts +1 -0
  132. package/src/retriableDocumentStorageService.ts +18 -1
  133. package/src/utils.ts +128 -36
  134. package/dist/audience.cjs.map +0 -1
  135. package/dist/catchUpMonitor.cjs.map +0 -1
  136. package/dist/connectionManager.cjs.map +0 -1
  137. package/dist/connectionStateHandler.cjs.map +0 -1
  138. package/dist/container.cjs.map +0 -1
  139. package/dist/containerContext.cjs.map +0 -1
  140. package/dist/containerStorageAdapter.cjs.map +0 -1
  141. package/dist/contracts.cjs.map +0 -1
  142. package/dist/debugLogger.cjs.map +0 -1
  143. package/dist/deltaManager.cjs.map +0 -1
  144. package/dist/deltaQueue.cjs.map +0 -1
  145. package/dist/disposal.cjs.map +0 -1
  146. package/dist/error.cjs.map +0 -1
  147. package/dist/index.cjs.map +0 -1
  148. package/dist/location-redirection-utilities/index.cjs.map +0 -1
  149. package/dist/location-redirection-utilities/resolveWithLocationRedirection.cjs.map +0 -1
  150. package/dist/noopHeuristic.cjs.map +0 -1
  151. package/dist/packageVersion.cjs.map +0 -1
  152. package/dist/protocol.cjs.map +0 -1
  153. package/dist/protocolTreeDocumentStorageService.cjs.map +0 -1
  154. package/dist/quorum.cjs.map +0 -1
  155. package/dist/retriableDocumentStorageService.cjs.map +0 -1
  156. package/dist/utils.cjs +0 -142
  157. package/dist/utils.cjs.map +0 -1
  158. package/tsc-multi.test.json +0 -4
  159. /package/{.eslintrc.js → .eslintrc.cjs} +0 -0
package/lib/utils.mjs CHANGED
@@ -4,10 +4,10 @@
4
4
  */
5
5
  import { parse } from "url";
6
6
  import { v4 as uuid } from "uuid";
7
- import { stringToBuffer, Uint8ArrayToArrayBuffer } from "@fluid-internal/client-utils";
8
- import { assert, unreachableCase } from "@fluidframework/core-utils";
7
+ import { Uint8ArrayToString, stringToBuffer } from "@fluid-internal/client-utils";
8
+ import { assert, compareArrays, unreachableCase } from "@fluidframework/core-utils";
9
9
  import { SummaryType } from "@fluidframework/protocol-definitions";
10
- import { LoggingError } from "@fluidframework/telemetry-utils";
10
+ import { LoggingError, UsageError } from "@fluidframework/telemetry-utils";
11
11
  import { isCombinedAppAndProtocolSummary, } from "@fluidframework/driver-utils";
12
12
  import { DriverErrorTypes } from "@fluidframework/driver-definitions";
13
13
  /**
@@ -50,19 +50,14 @@ export function combineAppAndProtocolSummary(appSummary, protocolSummary) {
50
50
  return createNewSummary;
51
51
  }
52
52
  /**
53
- * Converts summary tree (for upload) to snapshot tree (for download).
54
- * Summary tree blobs contain contents, but snapshot tree blobs normally
55
- * contain IDs pointing to storage. This will create 2 blob entries in the
56
- * snapshot tree for each blob in the summary tree. One will be the regular
57
- * path pointing to a uniquely generated ID. Then there will be another
58
- * entry with the path as that uniquely generated ID, and value as the
59
- * blob contents as a base-64 string.
60
- * @param summary - summary to convert
53
+ * Converts a summary to snapshot tree and separate its blob contents
54
+ * to align detached container format with IPendingContainerState
55
+ * @param summary - ISummaryTree
61
56
  */
62
- function convertSummaryToSnapshotWithEmbeddedBlobContents(summary) {
57
+ function convertSummaryToSnapshotAndBlobs(summary) {
58
+ let blobContents = {};
63
59
  const treeNode = {
64
60
  blobs: {},
65
- blobsContents: {},
66
61
  trees: {},
67
62
  id: uuid(),
68
63
  unreferenced: summary.unreferenced,
@@ -72,8 +67,9 @@ function convertSummaryToSnapshotWithEmbeddedBlobContents(summary) {
72
67
  const summaryObject = summary.tree[key];
73
68
  switch (summaryObject.type) {
74
69
  case SummaryType.Tree: {
75
- treeNode.trees[key] =
76
- convertSummaryToSnapshotWithEmbeddedBlobContents(summaryObject);
70
+ const { tree, blobs } = convertSummaryToSnapshotAndBlobs(summaryObject);
71
+ treeNode.trees[key] = tree;
72
+ blobContents = { ...blobContents, ...blobs };
77
73
  break;
78
74
  }
79
75
  case SummaryType.Attachment:
@@ -82,10 +78,10 @@ function convertSummaryToSnapshotWithEmbeddedBlobContents(summary) {
82
78
  case SummaryType.Blob: {
83
79
  const blobId = uuid();
84
80
  treeNode.blobs[key] = blobId;
85
- const contentBuffer = typeof summaryObject.content === "string"
86
- ? stringToBuffer(summaryObject.content, "utf8")
87
- : Uint8ArrayToArrayBuffer(summaryObject.content);
88
- treeNode.blobsContents[blobId] = contentBuffer;
81
+ const contentString = summaryObject.content instanceof Uint8Array
82
+ ? Uint8ArrayToString(summaryObject.content)
83
+ : summaryObject.content;
84
+ blobContents[blobId] = contentString;
89
85
  break;
90
86
  }
91
87
  case SummaryType.Handle:
@@ -96,38 +92,109 @@ function convertSummaryToSnapshotWithEmbeddedBlobContents(summary) {
96
92
  }
97
93
  }
98
94
  }
99
- return treeNode;
95
+ return { tree: treeNode, blobs: blobContents };
100
96
  }
101
97
  /**
102
- * Combine and convert protocol and app summary tree to format which is readable by container while rehydrating.
98
+ * Converts summary parts into a SnapshotTree and its blob contents.
103
99
  * @param protocolSummaryTree - Protocol Summary Tree
104
100
  * @param appSummaryTree - App Summary Tree
105
101
  */
106
- export function convertProtocolAndAppSummaryToSnapshotTree(protocolSummaryTree, appSummaryTree) {
107
- // Shallow copy is fine, since we are doing a deep clone below.
102
+ function convertProtocolAndAppSummaryToSnapshotAndBlobs(protocolSummaryTree, appSummaryTree) {
108
103
  const combinedSummary = {
109
104
  type: SummaryType.Tree,
110
105
  tree: { ...appSummaryTree.tree },
111
106
  };
112
107
  combinedSummary.tree[".protocol"] = protocolSummaryTree;
113
- const snapshotTreeWithBlobContents = convertSummaryToSnapshotWithEmbeddedBlobContents(combinedSummary);
108
+ const snapshotTreeWithBlobContents = convertSummaryToSnapshotAndBlobs(combinedSummary);
114
109
  return snapshotTreeWithBlobContents;
115
110
  }
116
- // This function converts the snapshot taken in detached container(by serialize api) to snapshotTree with which
117
- // a detached container can be rehydrated.
118
- export const getSnapshotTreeFromSerializedContainer = (detachedContainerSnapshot) => {
119
- assert(isCombinedAppAndProtocolSummary(detachedContainerSnapshot), 0x1e0 /* "Protocol and App summary trees should be present" */);
111
+ export const getSnapshotTreeAndBlobsFromSerializedContainer = (detachedContainerSnapshot) => {
112
+ assert(isCombinedAppAndProtocolSummary(detachedContainerSnapshot), "Protocol and App summary trees should be present");
120
113
  const protocolSummaryTree = detachedContainerSnapshot.tree[".protocol"];
121
114
  const appSummaryTree = detachedContainerSnapshot.tree[".app"];
122
- const snapshotTreeWithBlobContents = convertProtocolAndAppSummaryToSnapshotTree(protocolSummaryTree, appSummaryTree);
115
+ const snapshotTreeWithBlobContents = convertProtocolAndAppSummaryToSnapshotAndBlobs(protocolSummaryTree, appSummaryTree);
123
116
  return snapshotTreeWithBlobContents;
124
117
  };
125
118
  export function getProtocolSnapshotTree(snapshot) {
126
119
  return ".protocol" in snapshot.trees ? snapshot.trees[".protocol"] : snapshot;
127
120
  }
121
+ export const combineSnapshotTreeAndSnapshotBlobs = (baseSnapshot, snapshotBlobs) => {
122
+ const blobsContents = {};
123
+ // Process blobs in the current level
124
+ for (const [, id] of Object.entries(baseSnapshot.blobs)) {
125
+ if (snapshotBlobs[id]) {
126
+ blobsContents[id] = stringToBuffer(snapshotBlobs[id], "utf8");
127
+ }
128
+ }
129
+ // Recursively process trees in the current level
130
+ const trees = {};
131
+ for (const [path, tree] of Object.entries(baseSnapshot.trees)) {
132
+ trees[path] = combineSnapshotTreeAndSnapshotBlobs(tree, snapshotBlobs);
133
+ }
134
+ // Create a new snapshot tree with blob contents and processed trees
135
+ const snapshotTreeWithBlobContents = {
136
+ ...baseSnapshot,
137
+ blobsContents,
138
+ trees,
139
+ };
140
+ return snapshotTreeWithBlobContents;
141
+ };
128
142
  export function isDeltaStreamConnectionForbiddenError(error) {
129
143
  return (typeof error === "object" &&
130
144
  error !== null &&
131
145
  error?.errorType === DriverErrorTypes.deltaStreamConnectionForbidden);
132
146
  }
147
+ /**
148
+ * Validates format in parsed string get from detached container
149
+ * serialization using IPendingDetachedContainerState format.
150
+ */
151
+ function isPendingDetachedContainerState(detachedContainerState) {
152
+ if (detachedContainerState?.attached === undefined ||
153
+ detachedContainerState?.baseSnapshot === undefined ||
154
+ detachedContainerState?.snapshotBlobs === undefined ||
155
+ detachedContainerState?.hasAttachmentBlobs === undefined) {
156
+ return false;
157
+ }
158
+ return true;
159
+ }
160
+ export function getDetachedContainerStateFromSerializedContainer(serializedContainer) {
161
+ const hasBlobsSummaryTree = ".hasAttachmentBlobs";
162
+ const parsedContainerState = JSON.parse(serializedContainer);
163
+ if (isPendingDetachedContainerState(parsedContainerState)) {
164
+ return parsedContainerState;
165
+ }
166
+ else if (isCombinedAppAndProtocolSummary(parsedContainerState)) {
167
+ const { tree, blobs } = getSnapshotTreeAndBlobsFromSerializedContainer(parsedContainerState);
168
+ const detachedContainerState = {
169
+ attached: false,
170
+ baseSnapshot: tree,
171
+ snapshotBlobs: blobs,
172
+ hasAttachmentBlobs: parsedContainerState.tree[hasBlobsSummaryTree] !== undefined,
173
+ };
174
+ return detachedContainerState;
175
+ }
176
+ else {
177
+ throw new UsageError("Cannot rehydrate detached container. Incorrect format");
178
+ }
179
+ }
180
+ /**
181
+ * Ensures only a single instance of the provided async function is running.
182
+ * If there are multiple calls they will all get the same promise to wait on.
183
+ */
184
+ export const runSingle = (func) => {
185
+ let running;
186
+ // don't mark this function async, so we return the same promise,
187
+ // rather than one that is wrapped due to async
188
+ // eslint-disable-next-line @typescript-eslint/promise-function-async
189
+ return (...args) => {
190
+ if (running !== undefined) {
191
+ if (!compareArrays(running.args, args)) {
192
+ return Promise.reject(new UsageError("Subsequent calls cannot use different arguments."));
193
+ }
194
+ return running.result;
195
+ }
196
+ running = { args, result: func(...args).finally(() => (running = undefined)) };
197
+ return running.result;
198
+ };
199
+ };
133
200
  //# sourceMappingURL=utils.mjs.map
package/lib/utils.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"utils.mjs","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 { stringToBuffer, Uint8ArrayToArrayBuffer } from \"@fluid-internal/client-utils\";\nimport { assert, unreachableCase } from \"@fluidframework/core-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 { DriverErrorTypes } 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\n/**\n * Interface to represent the parsed parts of IResolvedUrl.url to help\n * in getting info about different parts of the url.\n * May not be compatible or relevant for any Url Resolver\n * @internal\n */\nexport interface IParsedUrl {\n\t/**\n\t * It is combination of tenantid/docId part of the url.\n\t */\n\tid: string;\n\t/**\n\t * It is the deep link path in the url.\n\t */\n\tpath: string;\n\t/**\n\t * Query string part of the url.\n\t */\n\tquery: string;\n\t/**\n\t * 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\n/**\n * Utility api to parse the IResolvedUrl.url into specific parts like querystring, path to get\n * deep link info etc.\n * Warning - This function may not be compatible with any Url Resolver's resolved url. It works\n * with urls of type: protocol://<string>/.../..?<querystring>\n * @param url - This is the IResolvedUrl.url part of the resolved url.\n * @returns The IParsedUrl representing the input URL, or undefined if the format was not supported\n * @internal\n */\nexport function tryParseCompatibleResolvedUrl(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 === DriverErrorTypes.deltaStreamConnectionForbidden\n\t);\n}\n"]}
1
+ {"version":3,"file":"utils.mjs","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 { Uint8ArrayToString, stringToBuffer } from \"@fluid-internal/client-utils\";\nimport { assert, compareArrays, unreachableCase } from \"@fluidframework/core-utils\";\nimport { ISummaryTree, ISnapshotTree, SummaryType } from \"@fluidframework/protocol-definitions\";\nimport { LoggingError, UsageError } from \"@fluidframework/telemetry-utils\";\nimport {\n\tCombinedAppAndProtocolSummary,\n\tDeltaStreamConnectionForbiddenError,\n\tisCombinedAppAndProtocolSummary,\n} from \"@fluidframework/driver-utils\";\nimport { DriverErrorTypes } from \"@fluidframework/driver-definitions\";\nimport { ISerializableBlobContents } from \"./containerStorageAdapter\";\nimport { IPendingDetachedContainerState } from \"./container\";\n\n// This is used when we rehydrate a container from the snapshot. Here we put the blob contents\n// in separate property: blobContents.\nexport interface ISnapshotTreeWithBlobContents extends ISnapshotTree {\n\tblobsContents: { [path: string]: ArrayBufferLike };\n\ttrees: { [path: string]: ISnapshotTreeWithBlobContents };\n}\n\n/**\n * Interface to represent the parsed parts of IResolvedUrl.url to help\n * in getting info about different parts of the url.\n * May not be compatible or relevant for any Url Resolver\n * @internal\n */\nexport interface IParsedUrl {\n\t/**\n\t * It is combination of tenantid/docId part of the url.\n\t */\n\tid: string;\n\t/**\n\t * It is the deep link path in the url.\n\t */\n\tpath: string;\n\t/**\n\t * Query string part of the url.\n\t */\n\tquery: string;\n\t/**\n\t * 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\n/**\n * Utility api to parse the IResolvedUrl.url into specific parts like querystring, path to get\n * deep link info etc.\n * Warning - This function may not be compatible with any Url Resolver's resolved url. It works\n * with urls of type: protocol://<string>/.../..?<querystring>\n * @param url - This is the IResolvedUrl.url part of the resolved url.\n * @returns The IParsedUrl representing the input URL, or undefined if the format was not supported\n * @internal\n */\nexport function tryParseCompatibleResolvedUrl(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 a summary to snapshot tree and separate its blob contents\n * to align detached container format with IPendingContainerState\n * @param summary - ISummaryTree\n */\nfunction convertSummaryToSnapshotAndBlobs(summary: ISummaryTree): {\n\ttree: ISnapshotTree;\n\tblobs: ISerializableBlobContents;\n} {\n\tlet blobContents: ISerializableBlobContents = {};\n\tconst treeNode: ISnapshotTree = {\n\t\tblobs: {},\n\t\ttrees: {},\n\t\tid: uuid(),\n\t\tunreferenced: summary.unreferenced,\n\t};\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\tconst { tree, blobs } = convertSummaryToSnapshotAndBlobs(summaryObject);\n\t\t\t\ttreeNode.trees[key] = tree;\n\t\t\t\tblobContents = { ...blobContents, ...blobs };\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 contentString: string =\n\t\t\t\t\tsummaryObject.content instanceof Uint8Array\n\t\t\t\t\t\t? Uint8ArrayToString(summaryObject.content)\n\t\t\t\t\t\t: summaryObject.content;\n\t\t\t\tblobContents[blobId] = contentString;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase SummaryType.Handle:\n\t\t\t\tthrow new LoggingError(\n\t\t\t\t\t\"No handles should be there in summary in detached container!!\",\n\t\t\t\t);\n\t\t\t\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 { tree: treeNode, blobs: blobContents };\n}\n\n/**\n * Converts summary parts into a SnapshotTree and its blob contents.\n * @param protocolSummaryTree - Protocol Summary Tree\n * @param appSummaryTree - App Summary Tree\n */\nfunction convertProtocolAndAppSummaryToSnapshotAndBlobs(\n\tprotocolSummaryTree: ISummaryTree,\n\tappSummaryTree: ISummaryTree,\n): { tree: ISnapshotTree; blobs: ISerializableBlobContents } {\n\tconst combinedSummary: ISummaryTree = {\n\t\ttype: SummaryType.Tree,\n\t\ttree: { ...appSummaryTree.tree },\n\t};\n\n\tcombinedSummary.tree[\".protocol\"] = protocolSummaryTree;\n\tconst snapshotTreeWithBlobContents = convertSummaryToSnapshotAndBlobs(combinedSummary);\n\treturn snapshotTreeWithBlobContents;\n}\n\nexport const getSnapshotTreeAndBlobsFromSerializedContainer = (\n\tdetachedContainerSnapshot: ISummaryTree,\n): { tree: ISnapshotTree; blobs: ISerializableBlobContents } => {\n\tassert(\n\t\tisCombinedAppAndProtocolSummary(detachedContainerSnapshot),\n\t\t\"Protocol and App summary trees should be present\",\n\t);\n\tconst protocolSummaryTree = detachedContainerSnapshot.tree[\".protocol\"];\n\tconst appSummaryTree = detachedContainerSnapshot.tree[\".app\"];\n\tconst snapshotTreeWithBlobContents = convertProtocolAndAppSummaryToSnapshotAndBlobs(\n\t\tprotocolSummaryTree,\n\t\tappSummaryTree,\n\t);\n\treturn snapshotTreeWithBlobContents;\n};\n\nexport function getProtocolSnapshotTree(snapshot: ISnapshotTree): ISnapshotTree {\n\treturn \".protocol\" in snapshot.trees ? snapshot.trees[\".protocol\"] : snapshot;\n}\n\nexport const combineSnapshotTreeAndSnapshotBlobs = (\n\tbaseSnapshot: ISnapshotTree,\n\tsnapshotBlobs: ISerializableBlobContents,\n): ISnapshotTreeWithBlobContents => {\n\tconst blobsContents: { [path: string]: ArrayBufferLike } = {};\n\n\t// Process blobs in the current level\n\tfor (const [, id] of Object.entries(baseSnapshot.blobs)) {\n\t\tif (snapshotBlobs[id]) {\n\t\t\tblobsContents[id] = stringToBuffer(snapshotBlobs[id], \"utf8\");\n\t\t}\n\t}\n\n\t// Recursively process trees in the current level\n\tconst trees: { [path: string]: ISnapshotTreeWithBlobContents } = {};\n\tfor (const [path, tree] of Object.entries(baseSnapshot.trees)) {\n\t\ttrees[path] = combineSnapshotTreeAndSnapshotBlobs(tree, snapshotBlobs);\n\t}\n\n\t// Create a new snapshot tree with blob contents and processed trees\n\tconst snapshotTreeWithBlobContents: ISnapshotTreeWithBlobContents = {\n\t\t...baseSnapshot,\n\t\tblobsContents,\n\t\ttrees,\n\t};\n\n\treturn snapshotTreeWithBlobContents;\n};\n\nexport function isDeltaStreamConnectionForbiddenError(\n\terror: any,\n): error is DeltaStreamConnectionForbiddenError {\n\treturn (\n\t\ttypeof error === \"object\" &&\n\t\terror !== null &&\n\t\terror?.errorType === DriverErrorTypes.deltaStreamConnectionForbidden\n\t);\n}\n\n/**\n * Validates format in parsed string get from detached container\n * serialization using IPendingDetachedContainerState format.\n */\nfunction isPendingDetachedContainerState(\n\tdetachedContainerState: IPendingDetachedContainerState,\n): detachedContainerState is IPendingDetachedContainerState {\n\tif (\n\t\tdetachedContainerState?.attached === undefined ||\n\t\tdetachedContainerState?.baseSnapshot === undefined ||\n\t\tdetachedContainerState?.snapshotBlobs === undefined ||\n\t\tdetachedContainerState?.hasAttachmentBlobs === undefined\n\t) {\n\t\treturn false;\n\t}\n\treturn true;\n}\n\nexport function getDetachedContainerStateFromSerializedContainer(\n\tserializedContainer: string,\n): IPendingDetachedContainerState {\n\tconst hasBlobsSummaryTree = \".hasAttachmentBlobs\";\n\tconst parsedContainerState = JSON.parse(serializedContainer);\n\tif (isPendingDetachedContainerState(parsedContainerState)) {\n\t\treturn parsedContainerState;\n\t} else if (isCombinedAppAndProtocolSummary(parsedContainerState)) {\n\t\tconst { tree, blobs } =\n\t\t\tgetSnapshotTreeAndBlobsFromSerializedContainer(parsedContainerState);\n\t\tconst detachedContainerState: IPendingDetachedContainerState = {\n\t\t\tattached: false,\n\t\t\tbaseSnapshot: tree,\n\t\t\tsnapshotBlobs: blobs,\n\t\t\thasAttachmentBlobs: parsedContainerState.tree[hasBlobsSummaryTree] !== undefined,\n\t\t};\n\t\treturn detachedContainerState;\n\t} else {\n\t\tthrow new UsageError(\"Cannot rehydrate detached container. Incorrect format\");\n\t}\n}\n\n/**\n * Ensures only a single instance of the provided async function is running.\n * If there are multiple calls they will all get the same promise to wait on.\n */\nexport const runSingle = <A extends any[], R>(func: (...args: A) => Promise<R>) => {\n\tlet running:\n\t\t| {\n\t\t\t\targs: A;\n\t\t\t\tresult: Promise<R>;\n\t\t }\n\t\t| undefined;\n\t// don't mark this function async, so we return the same promise,\n\t// rather than one that is wrapped due to async\n\t// eslint-disable-next-line @typescript-eslint/promise-function-async\n\treturn (...args: A) => {\n\t\tif (running !== undefined) {\n\t\t\tif (!compareArrays(running.args, args)) {\n\t\t\t\treturn Promise.reject(\n\t\t\t\t\tnew UsageError(\"Subsequent calls cannot use different arguments.\"),\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn running.result;\n\t\t}\n\t\trunning = { args, result: func(...args).finally(() => (running = undefined)) };\n\t\treturn running.result;\n\t};\n};\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluidframework/container-loader",
3
- "version": "2.0.0-dev-rc.1.0.0.225277",
3
+ "version": "2.0.0-dev-rc.1.0.0.232845",
4
4
  "description": "Fluid container loader",
5
5
  "homepage": "https://fluidframework.com",
6
6
  "repository": {
@@ -19,7 +19,7 @@
19
19
  },
20
20
  "require": {
21
21
  "types": "./dist/index.d.ts",
22
- "default": "./dist/index.cjs"
22
+ "default": "./dist/index.js"
23
23
  }
24
24
  },
25
25
  "./test/container": {
@@ -29,7 +29,7 @@
29
29
  },
30
30
  "require": {
31
31
  "types": "./dist/container.d.ts",
32
- "default": "./dist/container.cjs"
32
+ "default": "./dist/container.js"
33
33
  }
34
34
  },
35
35
  "./test/contracts": {
@@ -39,7 +39,7 @@
39
39
  },
40
40
  "require": {
41
41
  "types": "./dist/contracts.d.ts",
42
- "default": "./dist/contracts.cjs"
42
+ "default": "./dist/contracts.js"
43
43
  }
44
44
  },
45
45
  "./test/connectionManager": {
@@ -49,7 +49,7 @@
49
49
  },
50
50
  "require": {
51
51
  "types": "./dist/connectionManager.d.ts",
52
- "default": "./dist/connectionManager.cjs"
52
+ "default": "./dist/connectionManager.js"
53
53
  }
54
54
  },
55
55
  "./test/deltaManager": {
@@ -59,7 +59,7 @@
59
59
  },
60
60
  "require": {
61
61
  "types": "./dist/deltaManager.d.ts",
62
- "default": "./dist/deltaManager.cjs"
62
+ "default": "./dist/deltaManager.js"
63
63
  }
64
64
  },
65
65
  "./test/utils": {
@@ -69,7 +69,7 @@
69
69
  },
70
70
  "require": {
71
71
  "types": "./dist/utils.d.ts",
72
- "default": "./dist/utils.cjs"
72
+ "default": "./dist/utils.js"
73
73
  }
74
74
  },
75
75
  "./alpha": {
@@ -79,7 +79,7 @@
79
79
  },
80
80
  "require": {
81
81
  "types": "./dist/container-loader-alpha.d.ts",
82
- "default": "./dist/index.cjs"
82
+ "default": "./dist/index.js"
83
83
  }
84
84
  },
85
85
  "./beta": {
@@ -89,7 +89,7 @@
89
89
  },
90
90
  "require": {
91
91
  "types": "./dist/container-loader-beta.d.ts",
92
- "default": "./dist/index.cjs"
92
+ "default": "./dist/index.js"
93
93
  }
94
94
  },
95
95
  "./internal": {
@@ -99,7 +99,7 @@
99
99
  },
100
100
  "require": {
101
101
  "types": "./dist/index.d.ts",
102
- "default": "./dist/index.cjs"
102
+ "default": "./dist/index.js"
103
103
  }
104
104
  },
105
105
  "./public": {
@@ -109,11 +109,11 @@
109
109
  },
110
110
  "require": {
111
111
  "types": "./dist/container-loader-public.d.ts",
112
- "default": "./dist/index.cjs"
112
+ "default": "./dist/index.js"
113
113
  }
114
114
  }
115
115
  },
116
- "main": "dist/index.cjs",
116
+ "main": "dist/index.js",
117
117
  "module": "lib/index.mjs",
118
118
  "types": "dist/index.d.ts",
119
119
  "c8": {
@@ -137,15 +137,16 @@
137
137
  "temp-directory": "nyc/.nyc_output"
138
138
  },
139
139
  "dependencies": {
140
- "@fluid-internal/client-utils": "2.0.0-dev-rc.1.0.0.225277",
141
- "@fluidframework/container-definitions": "2.0.0-dev-rc.1.0.0.225277",
142
- "@fluidframework/core-interfaces": "2.0.0-dev-rc.1.0.0.225277",
143
- "@fluidframework/core-utils": "2.0.0-dev-rc.1.0.0.225277",
144
- "@fluidframework/driver-definitions": "2.0.0-dev-rc.1.0.0.225277",
145
- "@fluidframework/driver-utils": "2.0.0-dev-rc.1.0.0.225277",
146
- "@fluidframework/protocol-base": "^3.0.0-223236",
147
- "@fluidframework/protocol-definitions": "^3.1.0-223007",
148
- "@fluidframework/telemetry-utils": "2.0.0-dev-rc.1.0.0.225277",
140
+ "@fluid-internal/client-utils": "2.0.0-dev-rc.1.0.0.232845",
141
+ "@fluidframework/container-definitions": "2.0.0-dev-rc.1.0.0.232845",
142
+ "@fluidframework/core-interfaces": "2.0.0-dev-rc.1.0.0.232845",
143
+ "@fluidframework/core-utils": "2.0.0-dev-rc.1.0.0.232845",
144
+ "@fluidframework/driver-definitions": "2.0.0-dev-rc.1.0.0.232845",
145
+ "@fluidframework/driver-utils": "2.0.0-dev-rc.1.0.0.232845",
146
+ "@fluidframework/protocol-base": "^3.1.0-231702",
147
+ "@fluidframework/protocol-definitions": "^3.2.0-231454",
148
+ "@fluidframework/telemetry-utils": "2.0.0-dev-rc.1.0.0.232845",
149
+ "@ungap/structured-clone": "^1.2.0",
149
150
  "debug": "^4.3.4",
150
151
  "double-ended-queue": "^2.1.0-0",
151
152
  "events": "^3.1.0",
@@ -154,20 +155,21 @@
154
155
  },
155
156
  "devDependencies": {
156
157
  "@arethetypeswrong/cli": "^0.13.3",
157
- "@fluid-private/test-loader-utils": "2.0.0-dev-rc.1.0.0.225277",
158
- "@fluid-tools/build-cli": "0.29.0-222379",
158
+ "@fluid-private/test-loader-utils": "2.0.0-dev-rc.1.0.0.232845",
159
+ "@fluid-tools/build-cli": "^0.29.0",
159
160
  "@fluidframework/build-common": "^2.0.3",
160
- "@fluidframework/build-tools": "0.29.0-222379",
161
+ "@fluidframework/build-tools": "^0.29.0",
161
162
  "@fluidframework/container-loader-previous": "npm:@fluidframework/container-loader@2.0.0-internal.8.0.0",
162
- "@fluidframework/eslint-config-fluid": "^3.1.0",
163
- "@fluidframework/mocha-test-setup": "2.0.0-dev-rc.1.0.0.225277",
164
- "@microsoft/api-extractor": "^7.38.3",
163
+ "@fluidframework/eslint-config-fluid": "^3.3.0",
164
+ "@fluidframework/mocha-test-setup": "2.0.0-dev-rc.1.0.0.232845",
165
+ "@microsoft/api-extractor": "^7.39.1",
165
166
  "@types/debug": "^4.1.5",
166
167
  "@types/double-ended-queue": "^2.1.0",
167
168
  "@types/events": "^3.0.0",
168
169
  "@types/mocha": "^9.1.1",
169
170
  "@types/node": "^18.19.0",
170
171
  "@types/sinon": "^7.0.13",
172
+ "@types/ungap__structured-clone": "^1.2.0",
171
173
  "c8": "^8.0.1",
172
174
  "copyfiles": "^2.4.1",
173
175
  "cross-env": "^7.0.3",
@@ -195,7 +197,11 @@
195
197
  }
196
198
  },
197
199
  "typeValidation": {
198
- "broken": {}
200
+ "broken": {
201
+ "InterfaceDeclaration_IContainerExperimental": {
202
+ "forwardCompat": false
203
+ }
204
+ }
199
205
  },
200
206
  "scripts": {
201
207
  "api": "fluid-build . --task api",
@@ -207,7 +213,7 @@
207
213
  "build:docs": "fluid-build . --task api",
208
214
  "build:esnext": "tsc-multi --config ../../../common/build/build-common/tsc-multi.esm.json",
209
215
  "build:genver": "gen-version",
210
- "build:test": "tsc-multi --config ./tsc-multi.test.json",
216
+ "build:test": "tsc --project ./src/test/tsconfig.json",
211
217
  "check:are-the-types-wrong": "attw --pack . --entrypoints .",
212
218
  "check:release-tags": "api-extractor run --local --config ./api-extractor-lint.json",
213
219
  "ci:build:docs": "api-extractor run",
@@ -223,7 +229,7 @@
223
229
  "test:coverage": "c8 npm test",
224
230
  "test:mocha": "mocha --ignore \"dist/test/types/*\" --recursive dist/test -r node_modules/@fluidframework/mocha-test-setup",
225
231
  "test:mocha:verbose": "cross-env FLUID_TEST_VERBOSE=1 npm run test:mocha",
226
- "tsc": "tsc-multi --config ../../../common/build/build-common/tsc-multi.cjs.json",
232
+ "tsc": "tsc",
227
233
  "tsc:watch": "tsc --watch",
228
234
  "typetests:gen": "fluid-type-test-generator",
229
235
  "typetests:prepare": "flub typetests --dir . --reset --previous --normalize"
@@ -0,0 +1,219 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+ import { AttachState } from "@fluidframework/container-definitions";
6
+ import { CombinedAppAndProtocolSummary } from "@fluidframework/driver-utils";
7
+ import { ISnapshotTree, ISummaryTree } from "@fluidframework/protocol-definitions";
8
+ import { assert } from "@fluidframework/core-utils";
9
+ import { IDocumentStorageService } from "@fluidframework/driver-definitions";
10
+ import { getSnapshotTreeAndBlobsFromSerializedContainer } from "./utils";
11
+ import { ISerializableBlobContents } from "./containerStorageAdapter";
12
+ import { IDetachedBlobStorage } from ".";
13
+
14
+ /**
15
+ * The default state a newly created detached container will have.
16
+ * All but the state are optional and undefined, they just exist
17
+ * to make the union easy to deal with for both Detached types
18
+ */
19
+ export interface DetachedDefaultData {
20
+ readonly state: AttachState.Detached;
21
+ readonly blobs?: undefined;
22
+ readonly summary?: undefined;
23
+ readonly redirectTable?: undefined;
24
+ }
25
+
26
+ /**
27
+ * This always follows DetachedDefaultData when there are
28
+ * outstanding blobs in the detached blob storage.
29
+ * The redirect table will get filled up to include data
30
+ * about the blobs as they are uploaded.
31
+ */
32
+ export interface DetachedDataWithOutstandingBlobs {
33
+ readonly state: AttachState.Detached;
34
+ readonly blobs: "outstanding";
35
+ readonly summary?: undefined;
36
+ readonly redirectTable: Map<string, string>;
37
+ }
38
+
39
+ /**
40
+ * This state always follows DetachedDataWithOutstandingBlobs.
41
+ * It signals that all outstanding blobs are done being uploaded,
42
+ * so the container can move to the attaching state.
43
+ */
44
+ export interface AttachingDataWithBlobs {
45
+ readonly state: AttachState.Attaching;
46
+ readonly blobs: "done";
47
+ readonly summary: CombinedAppAndProtocolSummary;
48
+ }
49
+
50
+ /**
51
+ * This always follows DefaultDetachedState when there are
52
+ * no blobs in the detached blob storage. Because there are
53
+ * no blobs we can immediately get the summary and transition
54
+ * to the attaching state.
55
+ */
56
+ export interface AttachingDataWithoutBlobs {
57
+ readonly state: AttachState.Attaching;
58
+ readonly summary: CombinedAppAndProtocolSummary;
59
+ readonly blobs: "none";
60
+ }
61
+
62
+ /**
63
+ * The final attachment state which signals the container is fully attached.
64
+ * The baseSnapshotAndBlobs will only be enabled when offline load is enabled.
65
+ */
66
+ export interface AttachedData {
67
+ readonly state: AttachState.Attached;
68
+ readonly snapshot?: {
69
+ tree: ISnapshotTree;
70
+ blobs: ISerializableBlobContents;
71
+ };
72
+ }
73
+
74
+ /**
75
+ * A union of all the attachment data types for
76
+ * tracking across all container attachment states
77
+ */
78
+ export type AttachmentData =
79
+ | DetachedDefaultData
80
+ | DetachedDataWithOutstandingBlobs
81
+ | AttachingDataWithoutBlobs
82
+ | AttachingDataWithBlobs
83
+ | AttachedData;
84
+
85
+ /**
86
+ * The data and services necessary for runRetriableAttachProcess.
87
+ */
88
+ export interface AttachProcessProps {
89
+ /**
90
+ * The initial attachment data this call should start with
91
+ */
92
+ readonly initialAttachmentData: Exclude<AttachmentData, AttachedData>;
93
+
94
+ /**
95
+ * The caller should use this callback to keep track of the current
96
+ * attachment data, and perform any other operations necessary
97
+ * for dealing with attachment state changes, like emitting events
98
+ *
99
+ * @param attachmentData - the updated attachment data */
100
+ readonly setAttachmentData: (attachmentData: AttachmentData) => void;
101
+
102
+ /**
103
+ * The caller should create and or get services based on the data, and its own information.
104
+ * @param data - the data to create services from,
105
+ * the summary property being the most relevant part of the data.
106
+ * @returns A compatible storage service
107
+ */
108
+ readonly createOrGetStorageService: (
109
+ data: ISummaryTree | undefined,
110
+ ) => Promise<Pick<IDocumentStorageService, "createBlob" | "uploadSummaryWithContext">>;
111
+
112
+ /**
113
+ * The detached blob storage if it exists.
114
+ */
115
+ readonly detachedBlobStorage?: Pick<IDetachedBlobStorage, "getBlobIds" | "readBlob" | "size">;
116
+
117
+ /**
118
+ * The caller should create the attachment summary for the container.
119
+ * @param redirectTable - Maps local blob ids to remote blobs ids.
120
+ * @returns The attachment summary for the container.
121
+ */
122
+ readonly createAttachmentSummary: (
123
+ redirectTable?: Map<string, string>,
124
+ ) => CombinedAppAndProtocolSummary;
125
+
126
+ /**
127
+ * Whether offline load is enabled or not.
128
+ */
129
+ readonly offlineLoadEnabled: boolean;
130
+ }
131
+
132
+ /**
133
+ * Executes the attach process state machine based on the provided data and services.
134
+ * This method is retriable on failure. Based on the provided initialAttachmentData
135
+ * this method will resume the attachment process and attempt to complete it.
136
+ *
137
+ * @param props - The data and services necessary to run the attachment process
138
+ */
139
+ export const runRetriableAttachProcess = async (props: AttachProcessProps): Promise<void> => {
140
+ const {
141
+ detachedBlobStorage,
142
+ createOrGetStorageService,
143
+ setAttachmentData,
144
+ createAttachmentSummary,
145
+ offlineLoadEnabled,
146
+ } = props;
147
+ let currentData: AttachmentData = props.initialAttachmentData;
148
+
149
+ if (currentData.blobs === undefined) {
150
+ // If attachment blobs were uploaded in detached state we will go through a different attach flow
151
+ const outstandingAttachmentBlobs =
152
+ detachedBlobStorage !== undefined && detachedBlobStorage.size > 0;
153
+ // Determine the next phase of attaching which depends on if there are attachment blobs
154
+ // if there are, we will stay detached, so an empty file can be created, and the blobs
155
+ // uploaded, otherwise we will get the summary to create the file with and move to attaching
156
+ currentData = outstandingAttachmentBlobs
157
+ ? {
158
+ state: AttachState.Detached,
159
+ blobs: "outstanding",
160
+ redirectTable: new Map<string, string>(),
161
+ }
162
+ : {
163
+ state: AttachState.Attaching,
164
+ summary: props.createAttachmentSummary(),
165
+ blobs: "none",
166
+ };
167
+ setAttachmentData(currentData);
168
+ }
169
+
170
+ // this has to run here, as it is what creates the file
171
+ // and we need to file for all possible cases after this point
172
+ const storage = await createOrGetStorageService(currentData.summary);
173
+
174
+ if (currentData.blobs === "outstanding") {
175
+ const { redirectTable } = currentData;
176
+ // upload blobs to storage
177
+ assert(!!detachedBlobStorage, 0x24e /* "assertion for type narrowing" */);
178
+
179
+ // build a table mapping IDs assigned locally to IDs assigned by storage and pass it to runtime to
180
+ // support blob handles that only know about the local IDs
181
+ while (redirectTable.size < detachedBlobStorage.size) {
182
+ const newIds = detachedBlobStorage.getBlobIds().filter((id) => !redirectTable.has(id));
183
+ for (const id of newIds) {
184
+ const blob = await detachedBlobStorage.readBlob(id);
185
+ const response = await storage.createBlob(blob);
186
+ redirectTable.set(id, response.id);
187
+ }
188
+ }
189
+ setAttachmentData(
190
+ (currentData = {
191
+ state: AttachState.Attaching,
192
+ summary: createAttachmentSummary(redirectTable),
193
+ blobs: "done",
194
+ }),
195
+ );
196
+ }
197
+
198
+ assert(currentData.state === AttachState.Attaching, "must be attaching by this point");
199
+
200
+ if (currentData.blobs === "done") {
201
+ // done means outstanding blobs were uploaded.
202
+ // in that case an empty file was created, the blobs were uploaded
203
+ // and now this finally uploads the summary
204
+ await storage.uploadSummaryWithContext(currentData.summary, {
205
+ referenceSequenceNumber: 0,
206
+ ackHandle: undefined,
207
+ proposalHandle: undefined,
208
+ });
209
+ }
210
+
211
+ setAttachmentData(
212
+ (currentData = {
213
+ state: AttachState.Attached,
214
+ snapshot: offlineLoadEnabled
215
+ ? getSnapshotTreeAndBlobsFromSerializedContainer(currentData.summary)
216
+ : undefined,
217
+ }),
218
+ );
219
+ };