@fluidframework/container-loader 2.100.0 → 2.101.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.
- package/CHANGELOG.md +4 -0
- package/api-report/container-loader.legacy.alpha.api.md +13 -1
- package/dist/captureReferencedContents.d.ts +154 -0
- package/dist/captureReferencedContents.d.ts.map +1 -0
- package/dist/captureReferencedContents.js +349 -0
- package/dist/captureReferencedContents.js.map +1 -0
- package/dist/connectionManager.d.ts.map +1 -1
- package/dist/connectionManager.js +25 -7
- package/dist/connectionManager.js.map +1 -1
- package/dist/connectionStateHandler.d.ts.map +1 -1
- package/dist/connectionStateHandler.js +3 -1
- package/dist/connectionStateHandler.js.map +1 -1
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +6 -1
- package/dist/container.js.map +1 -1
- package/dist/containerStorageAdapter.d.ts +19 -1
- package/dist/containerStorageAdapter.d.ts.map +1 -1
- package/dist/containerStorageAdapter.js.map +1 -1
- package/dist/createAndLoadContainerUtils.d.ts +95 -0
- package/dist/createAndLoadContainerUtils.d.ts.map +1 -1
- package/dist/createAndLoadContainerUtils.js +137 -11
- package/dist/createAndLoadContainerUtils.js.map +1 -1
- package/dist/frozenServices.d.ts +113 -30
- package/dist/frozenServices.d.ts.map +1 -1
- package/dist/frozenServices.js +236 -58
- package/dist/frozenServices.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/legacyAlpha.d.ts +2 -0
- package/dist/loaderLayerCompatState.d.ts +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/pendingLocalStateStore.d.ts.map +1 -1
- package/dist/pendingLocalStateStore.js +9 -3
- package/dist/pendingLocalStateStore.js.map +1 -1
- package/dist/serializedStateManager.d.ts +16 -1
- package/dist/serializedStateManager.d.ts.map +1 -1
- package/dist/serializedStateManager.js +11 -1
- package/dist/serializedStateManager.js.map +1 -1
- package/lib/captureReferencedContents.d.ts +154 -0
- package/lib/captureReferencedContents.d.ts.map +1 -0
- package/lib/captureReferencedContents.js +338 -0
- package/lib/captureReferencedContents.js.map +1 -0
- package/lib/connectionManager.d.ts.map +1 -1
- package/lib/connectionManager.js +26 -8
- package/lib/connectionManager.js.map +1 -1
- package/lib/connectionStateHandler.d.ts.map +1 -1
- package/lib/connectionStateHandler.js +3 -1
- package/lib/connectionStateHandler.js.map +1 -1
- package/lib/container.d.ts.map +1 -1
- package/lib/container.js +6 -1
- package/lib/container.js.map +1 -1
- package/lib/containerStorageAdapter.d.ts +19 -1
- package/lib/containerStorageAdapter.d.ts.map +1 -1
- package/lib/containerStorageAdapter.js.map +1 -1
- package/lib/createAndLoadContainerUtils.d.ts +95 -0
- package/lib/createAndLoadContainerUtils.d.ts.map +1 -1
- package/lib/createAndLoadContainerUtils.js +128 -3
- package/lib/createAndLoadContainerUtils.js.map +1 -1
- package/lib/frozenServices.d.ts +113 -30
- package/lib/frozenServices.d.ts.map +1 -1
- package/lib/frozenServices.js +233 -57
- package/lib/frozenServices.js.map +1 -1
- package/lib/index.d.ts +2 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +2 -1
- package/lib/index.js.map +1 -1
- package/lib/legacyAlpha.d.ts +2 -0
- package/lib/loaderLayerCompatState.d.ts +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/pendingLocalStateStore.d.ts.map +1 -1
- package/lib/pendingLocalStateStore.js +9 -3
- package/lib/pendingLocalStateStore.js.map +1 -1
- package/lib/serializedStateManager.d.ts +16 -1
- package/lib/serializedStateManager.d.ts.map +1 -1
- package/lib/serializedStateManager.js +11 -1
- package/lib/serializedStateManager.js.map +1 -1
- package/package.json +11 -11
- package/src/captureReferencedContents.ts +446 -0
- package/src/connectionManager.ts +30 -8
- package/src/connectionStateHandler.ts +14 -9
- package/src/container.ts +6 -0
- package/src/containerStorageAdapter.ts +20 -1
- package/src/createAndLoadContainerUtils.ts +229 -2
- package/src/frozenServices.ts +285 -64
- package/src/index.ts +7 -0
- package/src/packageVersion.ts +1 -1
- package/src/pendingLocalStateStore.ts +8 -1
- package/src/serializedStateManager.ts +28 -1
|
@@ -19,11 +19,16 @@ import type {
|
|
|
19
19
|
import type { IClientDetails } from "@fluidframework/driver-definitions";
|
|
20
20
|
import type {
|
|
21
21
|
IDocumentServiceFactory,
|
|
22
|
+
ISequencedDocumentMessage,
|
|
23
|
+
ISnapshot,
|
|
24
|
+
ISnapshotTree,
|
|
22
25
|
IUrlResolver,
|
|
23
26
|
} from "@fluidframework/driver-definitions/internal";
|
|
24
|
-
import { DriverHeader } from "@fluidframework/driver-definitions/internal";
|
|
27
|
+
import { DriverHeader, FetchSource } from "@fluidframework/driver-definitions/internal";
|
|
28
|
+
import { getSnapshotTree } from "@fluidframework/driver-utils/internal";
|
|
25
29
|
import {
|
|
26
30
|
GenericError,
|
|
31
|
+
UsageError,
|
|
27
32
|
normalizeError,
|
|
28
33
|
createChildMonitoringContext,
|
|
29
34
|
mixinMonitoringContext,
|
|
@@ -33,16 +38,28 @@ import {
|
|
|
33
38
|
} from "@fluidframework/telemetry-utils/internal";
|
|
34
39
|
import { v4 as uuid } from "uuid";
|
|
35
40
|
|
|
41
|
+
import {
|
|
42
|
+
captureReferencedAttachmentBlobs,
|
|
43
|
+
extractBlobAttachReferences,
|
|
44
|
+
inlineAttachmentBlobsByReference,
|
|
45
|
+
parseGcSnapshotData,
|
|
46
|
+
readReferencedSnapshotBlobs,
|
|
47
|
+
snapshotHasLoadingGroups,
|
|
48
|
+
unreferencedAttachmentBlobLocalIds,
|
|
49
|
+
type IBlobAttachReference,
|
|
50
|
+
} from "./captureReferencedContents.js";
|
|
36
51
|
import { DebugLogger } from "./debugLogger.js";
|
|
37
52
|
import { createFrozenDocumentServiceFactory } from "./frozenServices.js";
|
|
38
53
|
import { Loader } from "./loader.js";
|
|
39
54
|
import { pkgVersion } from "./packageVersion.js";
|
|
40
55
|
import type { ProtocolHandlerBuilder } from "./protocol.js";
|
|
56
|
+
import type { IPendingContainerState } from "./serializedStateManager.js";
|
|
41
57
|
import type {
|
|
42
58
|
LoadSummarizerSummaryResult,
|
|
43
59
|
OnDemandSummaryResults,
|
|
44
60
|
SummarizeOnDemandResults,
|
|
45
61
|
} from "./summarizerResultTypes.js";
|
|
62
|
+
import { getDocumentAttributes } from "./utils.js";
|
|
46
63
|
|
|
47
64
|
interface OnDemandSummarizeResultsPromises {
|
|
48
65
|
readonly summarySubmitted: Promise<SummarizeOnDemandResults["summarySubmitted"]>;
|
|
@@ -229,6 +246,34 @@ export interface ILoadFrozenContainerFromPendingStateProps
|
|
|
229
246
|
* Pending local state to be applied to the container.
|
|
230
247
|
*/
|
|
231
248
|
readonly pendingLocalState: string;
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Controls whether the frozen container is surfaced as read-only.
|
|
252
|
+
*
|
|
253
|
+
* Defaults to `true`. When `true`, the container reports `readOnlyInfo.readonly === true`
|
|
254
|
+
* with `storageOnly === true`, matching the historical behavior of frozen loads.
|
|
255
|
+
*
|
|
256
|
+
* When `false`, the container loads as writable so the runtime will accept DDS submissions.
|
|
257
|
+
* The connection itself stays `Connected`: the connection manager recognizes the synthetic
|
|
258
|
+
* frozen delta stream and drops outbound messages at the network layer, so no read→write
|
|
259
|
+
* reconnect is attempted. Local DDS state continues to update via optimistic apply, and
|
|
260
|
+
* submitted ops accumulate in the runtime's pending-state manager. Use this when callers
|
|
261
|
+
* want to accrue and capture pending state without publishing it.
|
|
262
|
+
*
|
|
263
|
+
* @remarks
|
|
264
|
+
* The flag uses negative polarity (`readOnly`) rather than a positive opt-in (`writable`)
|
|
265
|
+
* to align with `IContainer.readOnlyInfo.readonly`, which is the established surface for
|
|
266
|
+
* read/write state on a loaded container. A future positive-polarity option can layer on
|
|
267
|
+
* top of this without breaking callers, but flipping the polarity now would split readers
|
|
268
|
+
* between two conventions for the same concept.
|
|
269
|
+
*
|
|
270
|
+
* Subsystem behavior is unchanged from the read-only frozen path regardless of `readOnly`:
|
|
271
|
+
* storage operations still throw (only `readBlob` is supported); summarizer / id-compressor
|
|
272
|
+
* never fire because no acks arrive; the quorum is whatever was captured in pending state
|
|
273
|
+
* and gains no members during the writable-frozen lifetime. The only behavioral delta is
|
|
274
|
+
* that the runtime accepts DDS submissions and accumulates them in `pendingStateManager`.
|
|
275
|
+
*/
|
|
276
|
+
readonly readOnly?: boolean;
|
|
232
277
|
}
|
|
233
278
|
|
|
234
279
|
/**
|
|
@@ -241,10 +286,192 @@ export async function loadFrozenContainerFromPendingState(
|
|
|
241
286
|
): Promise<IContainer> {
|
|
242
287
|
return loadExistingContainer({
|
|
243
288
|
...props,
|
|
244
|
-
documentServiceFactory: createFrozenDocumentServiceFactory(
|
|
289
|
+
documentServiceFactory: createFrozenDocumentServiceFactory(
|
|
290
|
+
props.documentServiceFactory,
|
|
291
|
+
props.readOnly,
|
|
292
|
+
),
|
|
245
293
|
});
|
|
246
294
|
}
|
|
247
295
|
|
|
296
|
+
/**
|
|
297
|
+
* Properties for {@link captureFullContainerState}.
|
|
298
|
+
* @legacy @alpha
|
|
299
|
+
*/
|
|
300
|
+
export interface ICaptureFullContainerStateProps {
|
|
301
|
+
/**
|
|
302
|
+
* The url resolver used to resolve the request into a Fluid resolved url.
|
|
303
|
+
*/
|
|
304
|
+
readonly urlResolver: IUrlResolver;
|
|
305
|
+
/**
|
|
306
|
+
* The document service factory used to construct the driver services
|
|
307
|
+
* against which the state is captured.
|
|
308
|
+
*/
|
|
309
|
+
readonly documentServiceFactory: IDocumentServiceFactory;
|
|
310
|
+
/**
|
|
311
|
+
* The request identifying the container whose state is to be captured.
|
|
312
|
+
*/
|
|
313
|
+
readonly request: IRequest;
|
|
314
|
+
/**
|
|
315
|
+
* Optional logger for driver-side telemetry.
|
|
316
|
+
*/
|
|
317
|
+
readonly logger?: ITelemetryBaseLogger | undefined;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Captures the current state of an attached container using only driver-level
|
|
322
|
+
* services, without instantiating a runtime or loading a full container. The
|
|
323
|
+
* returned string is a serialized pending container state in the same wire
|
|
324
|
+
* format produced by a live container's pending-state serialization, and can
|
|
325
|
+
* be handed to {@link loadExistingContainer} as `pendingLocalState`.
|
|
326
|
+
*
|
|
327
|
+
* The output is a self-contained view of the container's referenced graph:
|
|
328
|
+
* the latest snapshot, inlined contents of every blob reachable through
|
|
329
|
+
* referenced subtrees, inlined contents of every referenced attachment blob
|
|
330
|
+
* keyed by storage id, and all ops with sequence numbers after the base
|
|
331
|
+
* snapshot's sequence number (as read from its attributes blob).
|
|
332
|
+
*
|
|
333
|
+
* Reachability respects GC. Snapshot subtrees flagged `unreferenced: true`
|
|
334
|
+
* are skipped (their contents are not inlined). Attachment blobs that GC has
|
|
335
|
+
* marked unreferenced, tombstoned, or deleted are skipped. When the snapshot
|
|
336
|
+
* has no GC tree (GC disabled or pre-GC document), no filtering is applied.
|
|
337
|
+
*
|
|
338
|
+
* Blob reads on load hit the `ContainerStorageAdapter` cache populated from
|
|
339
|
+
* the captured `snapshotBlobs` map, so a frozen loader can serve the full
|
|
340
|
+
* referenced graph without a live storage service.
|
|
341
|
+
*
|
|
342
|
+
* `pendingRuntimeState` is `undefined` — no runtime is instantiated — so the
|
|
343
|
+
* output cannot carry DDS-level in-flight changes. It is intended for state
|
|
344
|
+
* relay, inspection, and durable-state snapshot use cases.
|
|
345
|
+
*
|
|
346
|
+
* Containers that declare loading groups are not yet supported: the function
|
|
347
|
+
* throws `UsageError` if any referenced subtree carries a `groupId`. Group
|
|
348
|
+
* snapshots would need a separate prefetch + serialization path; until there
|
|
349
|
+
* is a known consumer and end-to-end coverage, the capture refuses rather
|
|
350
|
+
* than silently producing pending state that omits group data.
|
|
351
|
+
*
|
|
352
|
+
* Note: if a new snapshot lands between the snapshot fetch and the ops fetch,
|
|
353
|
+
* the returned state may not reflect the very latest snapshot, but remains
|
|
354
|
+
* internally consistent: ops are anchored to the snapshot that was captured.
|
|
355
|
+
*
|
|
356
|
+
* No `mixinMonitoringContext` / `configProvider` is wired here, deliberately
|
|
357
|
+
* diverging from the sibling entry points in this file. The function reads
|
|
358
|
+
* no feature flags and instantiates no runtime, so there is nothing for a
|
|
359
|
+
* monitoring context to gate or attribute. If a future change introduces
|
|
360
|
+
* config-gated behavior or runtime-attributed telemetry, add the wiring
|
|
361
|
+
* back together with that change.
|
|
362
|
+
* @legacy @alpha
|
|
363
|
+
*/
|
|
364
|
+
export async function captureFullContainerState({
|
|
365
|
+
urlResolver,
|
|
366
|
+
documentServiceFactory,
|
|
367
|
+
request,
|
|
368
|
+
logger,
|
|
369
|
+
}: ICaptureFullContainerStateProps): Promise<string> {
|
|
370
|
+
const resolvedUrl = await urlResolver.resolve(request);
|
|
371
|
+
if (resolvedUrl === undefined) {
|
|
372
|
+
throw new UsageError("Failed to resolve request to a Fluid URL");
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
const documentService = await documentServiceFactory.createDocumentService(
|
|
376
|
+
resolvedUrl,
|
|
377
|
+
logger,
|
|
378
|
+
);
|
|
379
|
+
try {
|
|
380
|
+
const storage = await documentService.connectToStorage();
|
|
381
|
+
|
|
382
|
+
const versions = await storage.getVersions(
|
|
383
|
+
// `null` signals "latest"
|
|
384
|
+
// eslint-disable-next-line unicorn/no-null
|
|
385
|
+
null,
|
|
386
|
+
1,
|
|
387
|
+
"captureFullContainerState",
|
|
388
|
+
FetchSource.noCache,
|
|
389
|
+
);
|
|
390
|
+
const version = versions[0];
|
|
391
|
+
const snapshot: ISnapshot | ISnapshotTree | undefined =
|
|
392
|
+
storage.getSnapshot === undefined
|
|
393
|
+
? ((await storage.getSnapshotTree(version, "captureFullContainerState")) ?? undefined)
|
|
394
|
+
: await storage.getSnapshot({
|
|
395
|
+
cacheSnapshot: false,
|
|
396
|
+
versionId: version?.id,
|
|
397
|
+
scenarioName: "captureFullContainerState",
|
|
398
|
+
});
|
|
399
|
+
if (snapshot === undefined) {
|
|
400
|
+
throw new GenericError("Failed to fetch snapshot for captureFullContainerState");
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
const baseSnapshot = getSnapshotTree(snapshot);
|
|
404
|
+
if (snapshotHasLoadingGroups(baseSnapshot)) {
|
|
405
|
+
throw new UsageError(
|
|
406
|
+
"captureFullContainerState does not yet support containers with loading groups",
|
|
407
|
+
);
|
|
408
|
+
}
|
|
409
|
+
const attributes = await getDocumentAttributes(storage, baseSnapshot);
|
|
410
|
+
const gcData = await parseGcSnapshotData(baseSnapshot, storage);
|
|
411
|
+
// Structural snapshot blobs (JSON/text the runtime authored) are
|
|
412
|
+
// UTF-8-encoded; attachment blobs may carry arbitrary binary bytes
|
|
413
|
+
// and are base64-encoded. Keep them on separate fields of the
|
|
414
|
+
// pending state so the load side can apply the matching decoder
|
|
415
|
+
// without ambiguity. See IPendingContainerState.attachmentBlobContents.
|
|
416
|
+
const [snapshotBlobs, attachmentBlobContents] = await Promise.all([
|
|
417
|
+
readReferencedSnapshotBlobs(snapshot, storage), // utf8 encoded
|
|
418
|
+
captureReferencedAttachmentBlobs(baseSnapshot, storage, gcData), // base64 encoded
|
|
419
|
+
]);
|
|
420
|
+
|
|
421
|
+
const deltaStorage = await documentService.connectToDeltaStorage();
|
|
422
|
+
const opsStream = deltaStorage.fetchMessages(
|
|
423
|
+
attributes.sequenceNumber + 1,
|
|
424
|
+
undefined,
|
|
425
|
+
undefined,
|
|
426
|
+
false,
|
|
427
|
+
"captureFullContainerState",
|
|
428
|
+
);
|
|
429
|
+
const savedOps: ISequencedDocumentMessage[] = [];
|
|
430
|
+
const postSnapshotBlobReferences: IBlobAttachReference[] = [];
|
|
431
|
+
let opsResult = await opsStream.read();
|
|
432
|
+
while (!opsResult.done) {
|
|
433
|
+
for (const op of opsResult.value) {
|
|
434
|
+
savedOps.push(op);
|
|
435
|
+
// Blobs uploaded after the base snapshot are not in its
|
|
436
|
+
// `.blobs` redirect table, so `captureReferencedAttachmentBlobs`
|
|
437
|
+
// did not see them. The wire-format BlobAttach op carries
|
|
438
|
+
// `(localId, storageId)` in its metadata; collect those here so
|
|
439
|
+
// we can backfill the bytes before sealing the artifact.
|
|
440
|
+
const refs = extractBlobAttachReferences(op);
|
|
441
|
+
if (refs.length > 0) {
|
|
442
|
+
postSnapshotBlobReferences.push(...refs);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
opsResult = await opsStream.read();
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
if (postSnapshotBlobReferences.length > 0) {
|
|
449
|
+
const added = await inlineAttachmentBlobsByReference(
|
|
450
|
+
postSnapshotBlobReferences,
|
|
451
|
+
storage,
|
|
452
|
+
unreferencedAttachmentBlobLocalIds(gcData),
|
|
453
|
+
attachmentBlobContents,
|
|
454
|
+
);
|
|
455
|
+
Object.assign(attachmentBlobContents, added);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
const pendingState: IPendingContainerState = {
|
|
459
|
+
attached: true,
|
|
460
|
+
baseSnapshot,
|
|
461
|
+
snapshotBlobs,
|
|
462
|
+
attachmentBlobContents:
|
|
463
|
+
Object.keys(attachmentBlobContents).length === 0 ? undefined : attachmentBlobContents,
|
|
464
|
+
loadedGroupIdSnapshots: undefined,
|
|
465
|
+
pendingRuntimeState: undefined,
|
|
466
|
+
savedOps,
|
|
467
|
+
url: resolvedUrl.url,
|
|
468
|
+
};
|
|
469
|
+
return JSON.stringify(pendingState);
|
|
470
|
+
} finally {
|
|
471
|
+
documentService.dispose();
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
248
475
|
/**
|
|
249
476
|
* Loads a summarizer container with the required headers, triggers an on-demand summary, and then closes it.
|
|
250
477
|
* Returns success/failure and an optional error for host-side handling.
|