@fluidframework/container-loader 2.0.0-rc.4.0.1 → 2.0.0-rc.4.0.2

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 (51) hide show
  1. package/api-report/container-loader.api.md +3 -1
  2. package/dist/attachment.d.ts.map +1 -1
  3. package/dist/attachment.js.map +1 -1
  4. package/dist/container.d.ts.map +1 -1
  5. package/dist/container.js +12 -5
  6. package/dist/container.js.map +1 -1
  7. package/dist/containerStorageAdapter.d.ts.map +1 -1
  8. package/dist/containerStorageAdapter.js +7 -2
  9. package/dist/containerStorageAdapter.js.map +1 -1
  10. package/dist/loader.d.ts +7 -0
  11. package/dist/loader.d.ts.map +1 -1
  12. package/dist/loader.js.map +1 -1
  13. package/dist/memoryBlobStorage.d.ts +9 -0
  14. package/dist/memoryBlobStorage.d.ts.map +1 -0
  15. package/dist/memoryBlobStorage.js +58 -0
  16. package/dist/memoryBlobStorage.js.map +1 -0
  17. package/dist/packageVersion.d.ts +1 -1
  18. package/dist/packageVersion.js +1 -1
  19. package/dist/packageVersion.js.map +1 -1
  20. package/dist/serializedStateManager.d.ts +2 -0
  21. package/dist/serializedStateManager.d.ts.map +1 -1
  22. package/dist/serializedStateManager.js.map +1 -1
  23. package/lib/attachment.d.ts.map +1 -1
  24. package/lib/attachment.js.map +1 -1
  25. package/lib/container.d.ts.map +1 -1
  26. package/lib/container.js +12 -5
  27. package/lib/container.js.map +1 -1
  28. package/lib/containerStorageAdapter.d.ts.map +1 -1
  29. package/lib/containerStorageAdapter.js +7 -2
  30. package/lib/containerStorageAdapter.js.map +1 -1
  31. package/lib/loader.d.ts +7 -0
  32. package/lib/loader.d.ts.map +1 -1
  33. package/lib/loader.js.map +1 -1
  34. package/lib/memoryBlobStorage.d.ts +9 -0
  35. package/lib/memoryBlobStorage.d.ts.map +1 -0
  36. package/lib/memoryBlobStorage.js +52 -0
  37. package/lib/memoryBlobStorage.js.map +1 -0
  38. package/lib/packageVersion.d.ts +1 -1
  39. package/lib/packageVersion.js +1 -1
  40. package/lib/packageVersion.js.map +1 -1
  41. package/lib/serializedStateManager.d.ts +2 -0
  42. package/lib/serializedStateManager.d.ts.map +1 -1
  43. package/lib/serializedStateManager.js.map +1 -1
  44. package/package.json +10 -10
  45. package/src/attachment.ts +2 -0
  46. package/src/container.ts +19 -5
  47. package/src/containerStorageAdapter.ts +4 -0
  48. package/src/loader.ts +8 -0
  49. package/src/memoryBlobStorage.ts +79 -0
  50. package/src/packageVersion.ts +1 -1
  51. package/src/serializedStateManager.ts +2 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluidframework/container-loader",
3
- "version": "2.0.0-rc.4.0.1",
3
+ "version": "2.0.0-rc.4.0.2",
4
4
  "description": "Fluid container loader",
5
5
  "homepage": "https://fluidframework.com",
6
6
  "repository": {
@@ -117,15 +117,15 @@
117
117
  "temp-directory": "nyc/.nyc_output"
118
118
  },
119
119
  "dependencies": {
120
- "@fluid-internal/client-utils": ">=2.0.0-rc.4.0.1 <2.0.0-rc.4.1.0",
121
- "@fluidframework/container-definitions": ">=2.0.0-rc.4.0.1 <2.0.0-rc.4.1.0",
122
- "@fluidframework/core-interfaces": ">=2.0.0-rc.4.0.1 <2.0.0-rc.4.1.0",
123
- "@fluidframework/core-utils": ">=2.0.0-rc.4.0.1 <2.0.0-rc.4.1.0",
124
- "@fluidframework/driver-definitions": ">=2.0.0-rc.4.0.1 <2.0.0-rc.4.1.0",
125
- "@fluidframework/driver-utils": ">=2.0.0-rc.4.0.1 <2.0.0-rc.4.1.0",
120
+ "@fluid-internal/client-utils": ">=2.0.0-rc.4.0.2 <2.0.0-rc.4.1.0",
121
+ "@fluidframework/container-definitions": ">=2.0.0-rc.4.0.2 <2.0.0-rc.4.1.0",
122
+ "@fluidframework/core-interfaces": ">=2.0.0-rc.4.0.2 <2.0.0-rc.4.1.0",
123
+ "@fluidframework/core-utils": ">=2.0.0-rc.4.0.2 <2.0.0-rc.4.1.0",
124
+ "@fluidframework/driver-definitions": ">=2.0.0-rc.4.0.2 <2.0.0-rc.4.1.0",
125
+ "@fluidframework/driver-utils": ">=2.0.0-rc.4.0.2 <2.0.0-rc.4.1.0",
126
126
  "@fluidframework/protocol-base": "^4.0.0",
127
127
  "@fluidframework/protocol-definitions": "^3.2.0",
128
- "@fluidframework/telemetry-utils": ">=2.0.0-rc.4.0.1 <2.0.0-rc.4.1.0",
128
+ "@fluidframework/telemetry-utils": ">=2.0.0-rc.4.0.2 <2.0.0-rc.4.1.0",
129
129
  "@ungap/structured-clone": "^1.2.0",
130
130
  "debug": "^4.3.4",
131
131
  "double-ended-queue": "^2.1.0-0",
@@ -134,8 +134,8 @@
134
134
  "devDependencies": {
135
135
  "@arethetypeswrong/cli": "^0.15.2",
136
136
  "@biomejs/biome": "^1.6.2",
137
- "@fluid-internal/mocha-test-setup": ">=2.0.0-rc.4.0.1 <2.0.0-rc.4.1.0",
138
- "@fluid-private/test-loader-utils": ">=2.0.0-rc.4.0.1 <2.0.0-rc.4.1.0",
137
+ "@fluid-internal/mocha-test-setup": ">=2.0.0-rc.4.0.2 <2.0.0-rc.4.1.0",
138
+ "@fluid-private/test-loader-utils": ">=2.0.0-rc.4.0.2 <2.0.0-rc.4.1.0",
139
139
  "@fluid-tools/build-cli": "^0.38.0",
140
140
  "@fluidframework/build-common": "^2.0.3",
141
141
  "@fluidframework/build-tools": "^0.38.0",
package/src/attachment.ts CHANGED
@@ -9,6 +9,7 @@ import { IDocumentStorageService } from "@fluidframework/driver-definitions/inte
9
9
  import { CombinedAppAndProtocolSummary } from "@fluidframework/driver-utils/internal";
10
10
  import { ISummaryTree } from "@fluidframework/protocol-definitions";
11
11
 
12
+ // eslint-disable-next-line import/no-deprecated
12
13
  import { IDetachedBlobStorage } from "./loader.js";
13
14
  import type { SnapshotWithBlobs } from "./serializedStateManager.js";
14
15
  import { getSnapshotTreeAndBlobsFromSerializedContainer } from "./utils.js";
@@ -110,6 +111,7 @@ export interface AttachProcessProps {
110
111
  /**
111
112
  * The detached blob storage if it exists.
112
113
  */
114
+ // eslint-disable-next-line import/no-deprecated
113
115
  readonly detachedBlobStorage?: Pick<IDetachedBlobStorage, "getBlobIds" | "readBlob" | "size">;
114
116
 
115
117
  /**
package/src/container.ts CHANGED
@@ -111,6 +111,7 @@ import {
111
111
  getPackageName,
112
112
  } from "./contracts.js";
113
113
  import { DeltaManager, IConnectionArgs } from "./deltaManager.js";
114
+ // eslint-disable-next-line import/no-deprecated
114
115
  import { IDetachedBlobStorage, ILoaderOptions, RelativeLoader } from "./loader.js";
115
116
  import { NoopHeuristic } from "./noopHeuristic.js";
116
117
  import { pkgVersion } from "./packageVersion.js";
@@ -136,6 +137,11 @@ import {
136
137
  getSnapshotTreeAndBlobsFromSerializedContainer,
137
138
  runSingle,
138
139
  } from "./utils.js";
140
+ import {
141
+ serializeMemoryDetachedBlobStorage,
142
+ createMemoryDetachedBlobStorage,
143
+ tryInitializeMemoryDetachedBlobStorage,
144
+ } from "./memoryBlobStorage.js";
139
145
 
140
146
  const detachedContainerRefSeqNumber = 0;
141
147
 
@@ -218,6 +224,7 @@ export interface IContainerCreateProps {
218
224
  /**
219
225
  * Blobs storage for detached containers.
220
226
  */
227
+ // eslint-disable-next-line import/no-deprecated
221
228
  readonly detachedBlobStorage?: IDetachedBlobStorage;
222
229
 
223
230
  /**
@@ -465,7 +472,8 @@ export class Container
465
472
  private readonly options: ILoaderOptions;
466
473
  private readonly scope: FluidObject;
467
474
  private readonly subLogger: ITelemetryLoggerExt;
468
- private readonly detachedBlobStorage: IDetachedBlobStorage | undefined;
475
+ // eslint-disable-next-line import/no-deprecated
476
+ private readonly detachedBlobStorage: IDetachedBlobStorage;
469
477
  private readonly protocolHandlerBuilder: ProtocolHandlerBuilder;
470
478
  private readonly client: IClient;
471
479
 
@@ -767,7 +775,7 @@ export class Container
767
775
  // Tracking alternative ways to handle this in AB#4129.
768
776
  this.options = { ...options };
769
777
  this.scope = scope;
770
- this.detachedBlobStorage = detachedBlobStorage;
778
+ this.detachedBlobStorage = detachedBlobStorage ?? createMemoryDetachedBlobStorage();
771
779
  this.protocolHandlerBuilder =
772
780
  protocolHandlerBuilder ??
773
781
  ((
@@ -944,7 +952,7 @@ export class Container
944
952
  options.summarizeProtocolTree;
945
953
 
946
954
  this.storageAdapter = new ContainerStorageAdapter(
947
- detachedBlobStorage,
955
+ this.detachedBlobStorage,
948
956
  this.mc.logger,
949
957
  pendingLocalState?.snapshotBlobs,
950
958
  pendingLocalState?.loadedGroupIdSnapshots,
@@ -1208,7 +1216,8 @@ export class Container
1208
1216
  baseSnapshot,
1209
1217
  snapshotBlobs,
1210
1218
  pendingRuntimeState,
1211
- hasAttachmentBlobs: !!this.detachedBlobStorage && this.detachedBlobStorage.size > 0,
1219
+ hasAttachmentBlobs: this.detachedBlobStorage.size > 0,
1220
+ attachmentBlobs: serializeMemoryDetachedBlobStorage(this.detachedBlobStorage),
1212
1221
  };
1213
1222
  return JSON.stringify(detachedContainerState);
1214
1223
  }
@@ -1328,6 +1337,7 @@ export class Container
1328
1337
  this.serializedStateManager.setInitialSnapshot(snapshotWithBlobs);
1329
1338
 
1330
1339
  if (!this.closed) {
1340
+ this.detachedBlobStorage.dispose?.();
1331
1341
  this.handleDeltaConnectionArg(attachProps?.deltaConnection, {
1332
1342
  fetchOpsFromStorage: false,
1333
1343
  reason: { text: "createDetached" },
@@ -1760,11 +1770,15 @@ export class Container
1760
1770
  baseSnapshot,
1761
1771
  snapshotBlobs,
1762
1772
  hasAttachmentBlobs,
1773
+ attachmentBlobs,
1763
1774
  pendingRuntimeState,
1764
1775
  }: IPendingDetachedContainerState) {
1765
1776
  if (hasAttachmentBlobs) {
1777
+ if (attachmentBlobs !== undefined) {
1778
+ tryInitializeMemoryDetachedBlobStorage(this.detachedBlobStorage, attachmentBlobs);
1779
+ }
1766
1780
  assert(
1767
- !!this.detachedBlobStorage && this.detachedBlobStorage.size > 0,
1781
+ this.detachedBlobStorage.size > 0,
1768
1782
  0x250 /* "serialized container with attachment blobs must be rehydrated with detached blob storage" */,
1769
1783
  );
1770
1784
  }
@@ -26,6 +26,7 @@ import {
26
26
  } from "@fluidframework/protocol-definitions";
27
27
  import { ITelemetryLoggerExt } from "@fluidframework/telemetry-utils/internal";
28
28
 
29
+ // eslint-disable-next-line import/no-deprecated
29
30
  import { IDetachedBlobStorage } from "./loader.js";
30
31
  import { ProtocolTreeStorageService } from "./protocolTreeDocumentStorageService.js";
31
32
  import { RetriableDocumentStorageService } from "./retriableDocumentStorageService.js";
@@ -79,6 +80,7 @@ export class ContainerStorageAdapter
79
80
  * @param forceEnableSummarizeProtocolTree - Enforce uploading a protocol summary regardless of the service's policy
80
81
  */
81
82
  public constructor(
83
+ // eslint-disable-next-line import/no-deprecated
82
84
  detachedBlobStorage: IDetachedBlobStorage | undefined,
83
85
  private readonly logger: ITelemetryLoggerExt,
84
86
  /**
@@ -233,6 +235,7 @@ export class ContainerStorageAdapter
233
235
  */
234
236
  class BlobOnlyStorage implements IDocumentStorageService {
235
237
  constructor(
238
+ // eslint-disable-next-line import/no-deprecated
236
239
  private readonly detachedStorage: IDetachedBlobStorage | undefined,
237
240
  private readonly logger: ITelemetryLoggerExt,
238
241
  ) {}
@@ -245,6 +248,7 @@ class BlobOnlyStorage implements IDocumentStorageService {
245
248
  return this.verifyStorage().readBlob(blobId);
246
249
  }
247
250
 
251
+ // eslint-disable-next-line import/no-deprecated
248
252
  private verifyStorage(): IDetachedBlobStorage {
249
253
  if (this.detachedStorage === undefined) {
250
254
  throw new UsageError("Real storage calls not allowed in Unattached container");
package/src/loader.ts CHANGED
@@ -227,6 +227,7 @@ export interface ILoaderServices {
227
227
 
228
228
  /**
229
229
  * Blobs storage for detached containers.
230
+ * @deprecated - IDetachedBlobStorage will be removed in a future release without a replacement. Blobs created while detached will be stored in memory to align with attached container behavior. AB#8049
230
231
  */
231
232
  readonly detachedBlobStorage?: IDetachedBlobStorage;
232
233
 
@@ -241,6 +242,8 @@ export interface ILoaderServices {
241
242
  * Subset of IDocumentStorageService which only supports createBlob() and readBlob(). This is used to support
242
243
  * blobs in detached containers.
243
244
  * @alpha
245
+ *
246
+ * @deprecated - IDetachedBlobStorage will be removed in a future release without a replacement. Blobs created while detached will be stored in memory to align with attached container behavior. AB#8049
244
247
  */
245
248
  export type IDetachedBlobStorage = Pick<IDocumentStorageService, "createBlob" | "readBlob"> & {
246
249
  size: number;
@@ -248,6 +251,11 @@ export type IDetachedBlobStorage = Pick<IDocumentStorageService, "createBlob" |
248
251
  * Return an array of all blob IDs present in storage
249
252
  */
250
253
  getBlobIds(): string[];
254
+
255
+ /**
256
+ * After the container is attached, the detached blob storage is no longer needed and will be disposed.
257
+ */
258
+ dispose?(): void;
251
259
  };
252
260
 
253
261
  /**
@@ -0,0 +1,79 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ import type { ICreateBlobResponse } from "@fluidframework/protocol-definitions";
7
+ import { bufferToString, stringToBuffer } from "@fluid-internal/client-utils";
8
+ import { assert, isObject } from "@fluidframework/core-utils/internal";
9
+ // eslint-disable-next-line import/no-deprecated
10
+ import type { IDetachedBlobStorage } from "./loader.js";
11
+
12
+ const MemoryDetachedBlobStorageIdentifier = Symbol();
13
+
14
+ // eslint-disable-next-line import/no-deprecated
15
+ interface MemoryDetachedBlobStorage extends IDetachedBlobStorage {
16
+ [MemoryDetachedBlobStorageIdentifier]: typeof MemoryDetachedBlobStorageIdentifier;
17
+ initialize(attachmentBlobs: string[]): void;
18
+ serialize(): string | undefined;
19
+ }
20
+
21
+ function isMemoryDetachedBlobStorage(
22
+ // eslint-disable-next-line import/no-deprecated
23
+ detachedStorage: IDetachedBlobStorage,
24
+ ): detachedStorage is MemoryDetachedBlobStorage {
25
+ return (
26
+ isObject(detachedStorage) &&
27
+ MemoryDetachedBlobStorageIdentifier in detachedStorage &&
28
+ detachedStorage[MemoryDetachedBlobStorageIdentifier] === MemoryDetachedBlobStorageIdentifier
29
+ );
30
+ }
31
+
32
+ export function serializeMemoryDetachedBlobStorage(
33
+ // eslint-disable-next-line import/no-deprecated
34
+ detachedStorage: IDetachedBlobStorage,
35
+ ): string | undefined {
36
+ if (detachedStorage.size > 0 && isMemoryDetachedBlobStorage(detachedStorage)) {
37
+ return detachedStorage.serialize();
38
+ }
39
+ }
40
+
41
+ export function tryInitializeMemoryDetachedBlobStorage(
42
+ // eslint-disable-next-line import/no-deprecated
43
+ detachedStorage: IDetachedBlobStorage,
44
+ attachmentBlobs: string,
45
+ ) {
46
+ if (!isMemoryDetachedBlobStorage(detachedStorage)) {
47
+ throw new Error(
48
+ "DetachedBlobStorage was not provided to the loader during serialize so cannot be provided during rehydrate.",
49
+ );
50
+ }
51
+
52
+ assert(detachedStorage.size === 0, "Blob storage already initialized");
53
+ const maybeAttachmentBlobs = JSON.parse(attachmentBlobs);
54
+ assert(Array.isArray(maybeAttachmentBlobs), "Invalid attachmentBlobs");
55
+
56
+ detachedStorage.initialize(maybeAttachmentBlobs);
57
+ }
58
+
59
+ // eslint-disable-next-line import/no-deprecated
60
+ export function createMemoryDetachedBlobStorage(): IDetachedBlobStorage {
61
+ const blobs: ArrayBufferLike[] = [];
62
+ const storage: MemoryDetachedBlobStorage = {
63
+ [MemoryDetachedBlobStorageIdentifier]: MemoryDetachedBlobStorageIdentifier,
64
+ createBlob: async (file: ArrayBufferLike): Promise<ICreateBlobResponse> => ({
65
+ id: `${blobs.push(file) - 1}`,
66
+ }),
67
+ readBlob: async (id: string): Promise<ArrayBufferLike> =>
68
+ blobs[Number(id)] ?? Promise.reject(new Error(`Blob not found: ${id}`)),
69
+ get size() {
70
+ return blobs.length;
71
+ },
72
+ getBlobIds: (): string[] => blobs.map((_, i) => `${i}`),
73
+ dispose: () => blobs.splice(0),
74
+ serialize: () => JSON.stringify(blobs.map((b) => bufferToString(b, "utf-8"))),
75
+ initialize: (attachmentBlobs: string[]) =>
76
+ blobs.push(...attachmentBlobs.map((maybeBlob) => stringToBuffer(maybeBlob, "utf-8"))),
77
+ };
78
+ return storage;
79
+ }
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/container-loader";
9
- export const pkgVersion = "2.0.0-rc.4.0.1";
9
+ export const pkgVersion = "2.0.0-rc.4.0.2";
@@ -93,6 +93,8 @@ export interface IPendingDetachedContainerState extends SnapshotWithBlobs {
93
93
  attached: false;
94
94
  /** Indicates whether we expect the rehydrated container to have non-empty Detached Blob Storage */
95
95
  hasAttachmentBlobs: boolean;
96
+ /** Used by the memory blob storage to persisted attachment blobs */
97
+ attachmentBlobs?: string;
96
98
  /**
97
99
  * Runtime-specific state that will be needed to properly rehydrate
98
100
  * (it's included in ContainerContext passed to instantiateRuntime)