@fluidframework/container-loader 2.0.0-dev-rc.1.0.0.228517 → 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.
- package/README.md +3 -3
- package/dist/attachment.d.ts +116 -0
- package/dist/attachment.d.ts.map +1 -0
- package/dist/attachment.js +82 -0
- package/dist/attachment.js.map +1 -0
- package/dist/connectionManager.d.ts.map +1 -1
- package/dist/connectionManager.js +1 -2
- package/dist/connectionManager.js.map +1 -1
- package/dist/container-loader-alpha.d.ts +1 -1
- package/dist/container-loader-untrimmed.d.ts +1 -1
- package/dist/container.d.ts +21 -8
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +177 -149
- package/dist/container.js.map +1 -1
- package/dist/containerContext.d.ts +3 -2
- package/dist/containerContext.d.ts.map +1 -1
- package/dist/containerContext.js +2 -1
- package/dist/containerContext.js.map +1 -1
- package/dist/containerStorageAdapter.d.ts +3 -3
- package/dist/containerStorageAdapter.d.ts.map +1 -1
- package/dist/containerStorageAdapter.js +10 -11
- package/dist/containerStorageAdapter.js.map +1 -1
- package/dist/loader.d.ts +1 -1
- package/dist/loader.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/protocolTreeDocumentStorageService.d.ts +1 -0
- package/dist/protocolTreeDocumentStorageService.d.ts.map +1 -1
- package/dist/protocolTreeDocumentStorageService.js +1 -0
- package/dist/protocolTreeDocumentStorageService.js.map +1 -1
- package/dist/retriableDocumentStorageService.d.ts +2 -1
- package/dist/retriableDocumentStorageService.d.ts.map +1 -1
- package/dist/retriableDocumentStorageService.js +8 -0
- package/dist/retriableDocumentStorageService.js.map +1 -1
- package/dist/tsdoc-metadata.json +1 -1
- package/dist/utils.d.ts +13 -7
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +98 -29
- package/dist/utils.js.map +1 -1
- package/lib/attachment.d.mts +112 -0
- package/lib/attachment.d.mts.map +1 -0
- package/lib/attachment.mjs +74 -0
- package/lib/attachment.mjs.map +1 -0
- package/lib/connectionManager.d.mts.map +1 -1
- package/lib/connectionManager.mjs +2 -5
- package/lib/connectionManager.mjs.map +1 -1
- package/lib/container-loader-alpha.d.mts +1 -1
- package/lib/container-loader-untrimmed.d.mts +1 -1
- package/lib/container.d.mts +21 -8
- package/lib/container.d.mts.map +1 -1
- package/lib/container.mjs +176 -151
- package/lib/container.mjs.map +1 -1
- package/lib/containerContext.d.mts +3 -2
- package/lib/containerContext.d.mts.map +1 -1
- package/lib/containerContext.mjs +2 -1
- package/lib/containerContext.mjs.map +1 -1
- package/lib/containerStorageAdapter.d.mts +3 -3
- package/lib/containerStorageAdapter.d.mts.map +1 -1
- package/lib/containerStorageAdapter.mjs +10 -11
- package/lib/containerStorageAdapter.mjs.map +1 -1
- package/lib/loader.d.mts +1 -1
- package/lib/loader.mjs.map +1 -1
- package/lib/packageVersion.d.mts +1 -1
- package/lib/packageVersion.mjs +1 -1
- package/lib/packageVersion.mjs.map +1 -1
- package/lib/protocolTreeDocumentStorageService.d.mts +1 -0
- package/lib/protocolTreeDocumentStorageService.d.mts.map +1 -1
- package/lib/protocolTreeDocumentStorageService.mjs +1 -0
- package/lib/protocolTreeDocumentStorageService.mjs.map +1 -1
- package/lib/retriableDocumentStorageService.d.mts +2 -1
- package/lib/retriableDocumentStorageService.d.mts.map +1 -1
- package/lib/retriableDocumentStorageService.mjs +9 -1
- package/lib/retriableDocumentStorageService.mjs.map +1 -1
- package/lib/utils.d.mts +13 -7
- package/lib/utils.d.mts.map +1 -1
- package/lib/utils.mjs +96 -29
- package/lib/utils.mjs.map +1 -1
- package/package.json +21 -15
- package/src/attachment.ts +219 -0
- package/src/connectionManager.ts +2 -4
- package/src/container.ts +260 -191
- package/src/containerContext.ts +2 -1
- package/src/containerStorageAdapter.ts +15 -12
- package/src/loader.ts +1 -1
- package/src/packageVersion.ts +1 -1
- package/src/protocolTreeDocumentStorageService.ts +1 -0
- package/src/retriableDocumentStorageService.ts +18 -1
- package/src/utils.ts +128 -36
- /package/{.eslintrc.js → .eslintrc.cjs} +0 -0
package/src/containerContext.ts
CHANGED
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
IBatchMessage,
|
|
17
17
|
} from "@fluidframework/container-definitions";
|
|
18
18
|
import { FluidObject } from "@fluidframework/core-interfaces";
|
|
19
|
-
import { IDocumentStorageService } from "@fluidframework/driver-definitions";
|
|
19
|
+
import { IDocumentStorageService, ISnapshot } from "@fluidframework/driver-definitions";
|
|
20
20
|
import {
|
|
21
21
|
IClientDetails,
|
|
22
22
|
IDocumentMessage,
|
|
@@ -99,6 +99,7 @@ export class ContainerContext implements IContainerContext {
|
|
|
99
99
|
public readonly existing: boolean,
|
|
100
100
|
public readonly taggedLogger: ITelemetryLoggerExt,
|
|
101
101
|
public readonly pendingLocalState?: unknown,
|
|
102
|
+
public readonly snapshotWithContents?: ISnapshot,
|
|
102
103
|
) {}
|
|
103
104
|
|
|
104
105
|
public getLoadedFromVersion(): IVersion | undefined {
|
|
@@ -13,6 +13,8 @@ import {
|
|
|
13
13
|
IDocumentService,
|
|
14
14
|
IDocumentStorageService,
|
|
15
15
|
IDocumentStorageServicePolicies,
|
|
16
|
+
ISnapshot,
|
|
17
|
+
ISnapshotFetchOptions,
|
|
16
18
|
ISummaryContext,
|
|
17
19
|
} from "@fluidframework/driver-definitions";
|
|
18
20
|
import { UsageError } from "@fluidframework/driver-utils";
|
|
@@ -101,18 +103,9 @@ export class ContainerStorageAdapter implements IDocumentStorageService, IDispos
|
|
|
101
103
|
}
|
|
102
104
|
}
|
|
103
105
|
|
|
104
|
-
public
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
private getBlobContents(snapshotTree: ISnapshotTreeWithBlobContents) {
|
|
109
|
-
if (snapshotTree.blobsContents !== undefined) {
|
|
110
|
-
for (const [id, value] of Object.entries(snapshotTree.blobsContents ?? {})) {
|
|
111
|
-
this.blobContents[id] = value;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
for (const [_, tree] of Object.entries(snapshotTree.trees)) {
|
|
115
|
-
this.getBlobContents(tree);
|
|
106
|
+
public loadSnapshotFromSnapshotBlobs(snapshotBlobs: ISerializableBlobContents) {
|
|
107
|
+
for (const [id, value] of Object.entries(snapshotBlobs)) {
|
|
108
|
+
this.blobContents[id] = value;
|
|
116
109
|
}
|
|
117
110
|
}
|
|
118
111
|
|
|
@@ -136,6 +129,15 @@ export class ContainerStorageAdapter implements IDocumentStorageService, IDispos
|
|
|
136
129
|
return this._storageService.getSnapshotTree(version, scenarioName);
|
|
137
130
|
}
|
|
138
131
|
|
|
132
|
+
public async getSnapshot(snapshotFetchOptions?: ISnapshotFetchOptions): Promise<ISnapshot> {
|
|
133
|
+
if (this._storageService.getSnapshot !== undefined) {
|
|
134
|
+
return this._storageService.getSnapshot(snapshotFetchOptions);
|
|
135
|
+
}
|
|
136
|
+
throw new UsageError(
|
|
137
|
+
"getSnapshot api should exist in internal storage in ContainerStorageAdapter",
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
139
141
|
public async readBlob(id: string): Promise<ArrayBufferLike> {
|
|
140
142
|
const maybeBlob = this.blobContents[id];
|
|
141
143
|
if (maybeBlob !== undefined) {
|
|
@@ -208,6 +210,7 @@ class BlobOnlyStorage implements IDocumentStorageService {
|
|
|
208
210
|
|
|
209
211
|
/* eslint-disable @typescript-eslint/unbound-method */
|
|
210
212
|
public getSnapshotTree: () => Promise<ISnapshotTree | null> = this.notCalled;
|
|
213
|
+
public getSnapshot: () => Promise<ISnapshot> = this.notCalled;
|
|
211
214
|
public getVersions: () => Promise<IVersion[]> = this.notCalled;
|
|
212
215
|
public write: () => Promise<IVersion> = this.notCalled;
|
|
213
216
|
public uploadSummaryWithContext: () => Promise<string> = this.notCalled;
|
package/src/loader.ts
CHANGED
|
@@ -108,7 +108,7 @@ export interface IFluidModuleWithDetails {
|
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
/**
|
|
111
|
-
* @deprecated ICodeDetailsLoader interface is moved to {@link @fluidframework/container-
|
|
111
|
+
* @deprecated ICodeDetailsLoader interface is moved to {@link @fluidframework/container-definitions#ICodeDetailsLoader}
|
|
112
112
|
* to have code loading modules in one package. #8193
|
|
113
113
|
* Fluid code loader resolves a code module matching the document schema, i.e. code details, such as
|
|
114
114
|
* a package name and package version range.
|
package/src/packageVersion.ts
CHANGED
|
@@ -27,6 +27,7 @@ export class ProtocolTreeStorageService implements IDocumentStorageService, IDis
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
getSnapshotTree = this.internalStorageService.getSnapshotTree.bind(this.internalStorageService);
|
|
30
|
+
getSnapshot = this.internalStorageService.getSnapshot?.bind(this.internalStorageService);
|
|
30
31
|
getVersions = this.internalStorageService.getVersions.bind(this.internalStorageService);
|
|
31
32
|
createBlob = this.internalStorageService.createBlob.bind(this.internalStorageService);
|
|
32
33
|
readBlob = this.internalStorageService.readBlob.bind(this.internalStorageService);
|
|
@@ -8,6 +8,8 @@ import {
|
|
|
8
8
|
FetchSource,
|
|
9
9
|
IDocumentStorageService,
|
|
10
10
|
IDocumentStorageServicePolicies,
|
|
11
|
+
ISnapshot,
|
|
12
|
+
ISnapshotFetchOptions,
|
|
11
13
|
ISummaryContext,
|
|
12
14
|
} from "@fluidframework/driver-definitions";
|
|
13
15
|
import {
|
|
@@ -18,7 +20,7 @@ import {
|
|
|
18
20
|
IVersion,
|
|
19
21
|
} from "@fluidframework/protocol-definitions";
|
|
20
22
|
import { IDisposable } from "@fluidframework/core-interfaces";
|
|
21
|
-
import { GenericError, ITelemetryLoggerExt } from "@fluidframework/telemetry-utils";
|
|
23
|
+
import { GenericError, ITelemetryLoggerExt, UsageError } from "@fluidframework/telemetry-utils";
|
|
22
24
|
import { runWithRetry } from "@fluidframework/driver-utils";
|
|
23
25
|
|
|
24
26
|
export class RetriableDocumentStorageService implements IDocumentStorageService, IDisposable {
|
|
@@ -64,6 +66,21 @@ export class RetriableDocumentStorageService implements IDocumentStorageService,
|
|
|
64
66
|
);
|
|
65
67
|
}
|
|
66
68
|
|
|
69
|
+
public async getSnapshot(snapshotFetchOptions?: ISnapshotFetchOptions): Promise<ISnapshot> {
|
|
70
|
+
return this.runWithRetry(
|
|
71
|
+
async () =>
|
|
72
|
+
this.internalStorageServiceP.then(async (s) => {
|
|
73
|
+
if (s.getSnapshot !== undefined) {
|
|
74
|
+
return s.getSnapshot(snapshotFetchOptions);
|
|
75
|
+
}
|
|
76
|
+
throw new UsageError(
|
|
77
|
+
"getSnapshot api should exist on internal storage in RetriableDocStorageService class",
|
|
78
|
+
);
|
|
79
|
+
}),
|
|
80
|
+
"storage_getSnapshot",
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
67
84
|
public async readBlob(id: string): Promise<ArrayBufferLike> {
|
|
68
85
|
return this.runWithRetry(
|
|
69
86
|
async () => this.internalStorageServiceP.then(async (s) => s.readBlob(id)),
|
package/src/utils.ts
CHANGED
|
@@ -5,16 +5,18 @@
|
|
|
5
5
|
|
|
6
6
|
import { parse } from "url";
|
|
7
7
|
import { v4 as uuid } from "uuid";
|
|
8
|
-
import {
|
|
9
|
-
import { assert, unreachableCase } from "@fluidframework/core-utils";
|
|
8
|
+
import { Uint8ArrayToString, stringToBuffer } from "@fluid-internal/client-utils";
|
|
9
|
+
import { assert, compareArrays, unreachableCase } from "@fluidframework/core-utils";
|
|
10
10
|
import { ISummaryTree, ISnapshotTree, SummaryType } from "@fluidframework/protocol-definitions";
|
|
11
|
-
import { LoggingError } from "@fluidframework/telemetry-utils";
|
|
11
|
+
import { LoggingError, UsageError } from "@fluidframework/telemetry-utils";
|
|
12
12
|
import {
|
|
13
13
|
CombinedAppAndProtocolSummary,
|
|
14
14
|
DeltaStreamConnectionForbiddenError,
|
|
15
15
|
isCombinedAppAndProtocolSummary,
|
|
16
16
|
} from "@fluidframework/driver-utils";
|
|
17
17
|
import { DriverErrorTypes } from "@fluidframework/driver-definitions";
|
|
18
|
+
import { ISerializableBlobContents } from "./containerStorageAdapter";
|
|
19
|
+
import { IPendingDetachedContainerState } from "./container";
|
|
18
20
|
|
|
19
21
|
// This is used when we rehydrate a container from the snapshot. Here we put the blob contents
|
|
20
22
|
// in separate property: blobContents.
|
|
@@ -101,21 +103,17 @@ export function combineAppAndProtocolSummary(
|
|
|
101
103
|
}
|
|
102
104
|
|
|
103
105
|
/**
|
|
104
|
-
* Converts summary
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
* snapshot tree for each blob in the summary tree. One will be the regular
|
|
108
|
-
* path pointing to a uniquely generated ID. Then there will be another
|
|
109
|
-
* entry with the path as that uniquely generated ID, and value as the
|
|
110
|
-
* blob contents as a base-64 string.
|
|
111
|
-
* @param summary - summary to convert
|
|
106
|
+
* Converts a summary to snapshot tree and separate its blob contents
|
|
107
|
+
* to align detached container format with IPendingContainerState
|
|
108
|
+
* @param summary - ISummaryTree
|
|
112
109
|
*/
|
|
113
|
-
function
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
110
|
+
function convertSummaryToSnapshotAndBlobs(summary: ISummaryTree): {
|
|
111
|
+
tree: ISnapshotTree;
|
|
112
|
+
blobs: ISerializableBlobContents;
|
|
113
|
+
} {
|
|
114
|
+
let blobContents: ISerializableBlobContents = {};
|
|
115
|
+
const treeNode: ISnapshotTree = {
|
|
117
116
|
blobs: {},
|
|
118
|
-
blobsContents: {},
|
|
119
117
|
trees: {},
|
|
120
118
|
id: uuid(),
|
|
121
119
|
unreferenced: summary.unreferenced,
|
|
@@ -126,8 +124,9 @@ function convertSummaryToSnapshotWithEmbeddedBlobContents(
|
|
|
126
124
|
|
|
127
125
|
switch (summaryObject.type) {
|
|
128
126
|
case SummaryType.Tree: {
|
|
129
|
-
|
|
130
|
-
|
|
127
|
+
const { tree, blobs } = convertSummaryToSnapshotAndBlobs(summaryObject);
|
|
128
|
+
treeNode.trees[key] = tree;
|
|
129
|
+
blobContents = { ...blobContents, ...blobs };
|
|
131
130
|
break;
|
|
132
131
|
}
|
|
133
132
|
case SummaryType.Attachment:
|
|
@@ -136,11 +135,11 @@ function convertSummaryToSnapshotWithEmbeddedBlobContents(
|
|
|
136
135
|
case SummaryType.Blob: {
|
|
137
136
|
const blobId = uuid();
|
|
138
137
|
treeNode.blobs[key] = blobId;
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
?
|
|
142
|
-
:
|
|
143
|
-
|
|
138
|
+
const contentString: string =
|
|
139
|
+
summaryObject.content instanceof Uint8Array
|
|
140
|
+
? Uint8ArrayToString(summaryObject.content)
|
|
141
|
+
: summaryObject.content;
|
|
142
|
+
blobContents[blobId] = contentString;
|
|
144
143
|
break;
|
|
145
144
|
}
|
|
146
145
|
case SummaryType.Handle:
|
|
@@ -153,42 +152,38 @@ function convertSummaryToSnapshotWithEmbeddedBlobContents(
|
|
|
153
152
|
}
|
|
154
153
|
}
|
|
155
154
|
}
|
|
156
|
-
return treeNode;
|
|
155
|
+
return { tree: treeNode, blobs: blobContents };
|
|
157
156
|
}
|
|
158
157
|
|
|
159
158
|
/**
|
|
160
|
-
*
|
|
159
|
+
* Converts summary parts into a SnapshotTree and its blob contents.
|
|
161
160
|
* @param protocolSummaryTree - Protocol Summary Tree
|
|
162
161
|
* @param appSummaryTree - App Summary Tree
|
|
163
162
|
*/
|
|
164
|
-
|
|
163
|
+
function convertProtocolAndAppSummaryToSnapshotAndBlobs(
|
|
165
164
|
protocolSummaryTree: ISummaryTree,
|
|
166
165
|
appSummaryTree: ISummaryTree,
|
|
167
|
-
):
|
|
168
|
-
// Shallow copy is fine, since we are doing a deep clone below.
|
|
166
|
+
): { tree: ISnapshotTree; blobs: ISerializableBlobContents } {
|
|
169
167
|
const combinedSummary: ISummaryTree = {
|
|
170
168
|
type: SummaryType.Tree,
|
|
171
169
|
tree: { ...appSummaryTree.tree },
|
|
172
170
|
};
|
|
173
171
|
|
|
174
172
|
combinedSummary.tree[".protocol"] = protocolSummaryTree;
|
|
175
|
-
const snapshotTreeWithBlobContents =
|
|
176
|
-
convertSummaryToSnapshotWithEmbeddedBlobContents(combinedSummary);
|
|
173
|
+
const snapshotTreeWithBlobContents = convertSummaryToSnapshotAndBlobs(combinedSummary);
|
|
177
174
|
return snapshotTreeWithBlobContents;
|
|
178
175
|
}
|
|
179
176
|
|
|
180
|
-
|
|
181
|
-
// a detached container can be rehydrated.
|
|
182
|
-
export const getSnapshotTreeFromSerializedContainer = (
|
|
177
|
+
export const getSnapshotTreeAndBlobsFromSerializedContainer = (
|
|
183
178
|
detachedContainerSnapshot: ISummaryTree,
|
|
184
|
-
):
|
|
179
|
+
): { tree: ISnapshotTree; blobs: ISerializableBlobContents } => {
|
|
185
180
|
assert(
|
|
186
181
|
isCombinedAppAndProtocolSummary(detachedContainerSnapshot),
|
|
187
|
-
|
|
182
|
+
"Protocol and App summary trees should be present",
|
|
188
183
|
);
|
|
189
184
|
const protocolSummaryTree = detachedContainerSnapshot.tree[".protocol"];
|
|
190
185
|
const appSummaryTree = detachedContainerSnapshot.tree[".app"];
|
|
191
|
-
const snapshotTreeWithBlobContents =
|
|
186
|
+
const snapshotTreeWithBlobContents = convertProtocolAndAppSummaryToSnapshotAndBlobs(
|
|
192
187
|
protocolSummaryTree,
|
|
193
188
|
appSummaryTree,
|
|
194
189
|
);
|
|
@@ -199,6 +194,35 @@ export function getProtocolSnapshotTree(snapshot: ISnapshotTree): ISnapshotTree
|
|
|
199
194
|
return ".protocol" in snapshot.trees ? snapshot.trees[".protocol"] : snapshot;
|
|
200
195
|
}
|
|
201
196
|
|
|
197
|
+
export const combineSnapshotTreeAndSnapshotBlobs = (
|
|
198
|
+
baseSnapshot: ISnapshotTree,
|
|
199
|
+
snapshotBlobs: ISerializableBlobContents,
|
|
200
|
+
): ISnapshotTreeWithBlobContents => {
|
|
201
|
+
const blobsContents: { [path: string]: ArrayBufferLike } = {};
|
|
202
|
+
|
|
203
|
+
// Process blobs in the current level
|
|
204
|
+
for (const [, id] of Object.entries(baseSnapshot.blobs)) {
|
|
205
|
+
if (snapshotBlobs[id]) {
|
|
206
|
+
blobsContents[id] = stringToBuffer(snapshotBlobs[id], "utf8");
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Recursively process trees in the current level
|
|
211
|
+
const trees: { [path: string]: ISnapshotTreeWithBlobContents } = {};
|
|
212
|
+
for (const [path, tree] of Object.entries(baseSnapshot.trees)) {
|
|
213
|
+
trees[path] = combineSnapshotTreeAndSnapshotBlobs(tree, snapshotBlobs);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Create a new snapshot tree with blob contents and processed trees
|
|
217
|
+
const snapshotTreeWithBlobContents: ISnapshotTreeWithBlobContents = {
|
|
218
|
+
...baseSnapshot,
|
|
219
|
+
blobsContents,
|
|
220
|
+
trees,
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
return snapshotTreeWithBlobContents;
|
|
224
|
+
};
|
|
225
|
+
|
|
202
226
|
export function isDeltaStreamConnectionForbiddenError(
|
|
203
227
|
error: any,
|
|
204
228
|
): error is DeltaStreamConnectionForbiddenError {
|
|
@@ -208,3 +232,71 @@ export function isDeltaStreamConnectionForbiddenError(
|
|
|
208
232
|
error?.errorType === DriverErrorTypes.deltaStreamConnectionForbidden
|
|
209
233
|
);
|
|
210
234
|
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Validates format in parsed string get from detached container
|
|
238
|
+
* serialization using IPendingDetachedContainerState format.
|
|
239
|
+
*/
|
|
240
|
+
function isPendingDetachedContainerState(
|
|
241
|
+
detachedContainerState: IPendingDetachedContainerState,
|
|
242
|
+
): detachedContainerState is IPendingDetachedContainerState {
|
|
243
|
+
if (
|
|
244
|
+
detachedContainerState?.attached === undefined ||
|
|
245
|
+
detachedContainerState?.baseSnapshot === undefined ||
|
|
246
|
+
detachedContainerState?.snapshotBlobs === undefined ||
|
|
247
|
+
detachedContainerState?.hasAttachmentBlobs === undefined
|
|
248
|
+
) {
|
|
249
|
+
return false;
|
|
250
|
+
}
|
|
251
|
+
return true;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export function getDetachedContainerStateFromSerializedContainer(
|
|
255
|
+
serializedContainer: string,
|
|
256
|
+
): IPendingDetachedContainerState {
|
|
257
|
+
const hasBlobsSummaryTree = ".hasAttachmentBlobs";
|
|
258
|
+
const parsedContainerState = JSON.parse(serializedContainer);
|
|
259
|
+
if (isPendingDetachedContainerState(parsedContainerState)) {
|
|
260
|
+
return parsedContainerState;
|
|
261
|
+
} else if (isCombinedAppAndProtocolSummary(parsedContainerState)) {
|
|
262
|
+
const { tree, blobs } =
|
|
263
|
+
getSnapshotTreeAndBlobsFromSerializedContainer(parsedContainerState);
|
|
264
|
+
const detachedContainerState: IPendingDetachedContainerState = {
|
|
265
|
+
attached: false,
|
|
266
|
+
baseSnapshot: tree,
|
|
267
|
+
snapshotBlobs: blobs,
|
|
268
|
+
hasAttachmentBlobs: parsedContainerState.tree[hasBlobsSummaryTree] !== undefined,
|
|
269
|
+
};
|
|
270
|
+
return detachedContainerState;
|
|
271
|
+
} else {
|
|
272
|
+
throw new UsageError("Cannot rehydrate detached container. Incorrect format");
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Ensures only a single instance of the provided async function is running.
|
|
278
|
+
* If there are multiple calls they will all get the same promise to wait on.
|
|
279
|
+
*/
|
|
280
|
+
export const runSingle = <A extends any[], R>(func: (...args: A) => Promise<R>) => {
|
|
281
|
+
let running:
|
|
282
|
+
| {
|
|
283
|
+
args: A;
|
|
284
|
+
result: Promise<R>;
|
|
285
|
+
}
|
|
286
|
+
| undefined;
|
|
287
|
+
// don't mark this function async, so we return the same promise,
|
|
288
|
+
// rather than one that is wrapped due to async
|
|
289
|
+
// eslint-disable-next-line @typescript-eslint/promise-function-async
|
|
290
|
+
return (...args: A) => {
|
|
291
|
+
if (running !== undefined) {
|
|
292
|
+
if (!compareArrays(running.args, args)) {
|
|
293
|
+
return Promise.reject(
|
|
294
|
+
new UsageError("Subsequent calls cannot use different arguments."),
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
return running.result;
|
|
298
|
+
}
|
|
299
|
+
running = { args, result: func(...args).finally(() => (running = undefined)) };
|
|
300
|
+
return running.result;
|
|
301
|
+
};
|
|
302
|
+
};
|
|
File without changes
|