@fluidframework/datastore 1.4.0-121020 → 2.0.0-dev-rc.1.0.0.224419

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 (145) hide show
  1. package/.eslintrc.js +5 -7
  2. package/.mocharc.js +12 -0
  3. package/CHANGELOG.md +273 -0
  4. package/README.md +41 -0
  5. package/api-extractor-lint.json +4 -0
  6. package/api-extractor.json +2 -2
  7. package/api-report/datastore.api.md +168 -0
  8. package/dist/channelContext.cjs +86 -0
  9. package/dist/channelContext.cjs.map +1 -0
  10. package/dist/channelContext.d.ts +15 -9
  11. package/dist/channelContext.d.ts.map +1 -1
  12. package/dist/{channelDeltaConnection.js → channelDeltaConnection.cjs} +14 -15
  13. package/dist/channelDeltaConnection.cjs.map +1 -0
  14. package/dist/channelDeltaConnection.d.ts +4 -5
  15. package/dist/channelDeltaConnection.d.ts.map +1 -1
  16. package/dist/{channelStorageService.js → channelStorageService.cjs} +13 -16
  17. package/dist/channelStorageService.cjs.map +1 -0
  18. package/dist/channelStorageService.d.ts +2 -2
  19. package/dist/channelStorageService.d.ts.map +1 -1
  20. package/dist/{dataStoreRuntime.js → dataStoreRuntime.cjs} +302 -225
  21. package/dist/dataStoreRuntime.cjs.map +1 -0
  22. package/dist/dataStoreRuntime.d.ts +81 -37
  23. package/dist/dataStoreRuntime.d.ts.map +1 -1
  24. package/dist/datastore-alpha.d.ts +317 -0
  25. package/dist/datastore-beta.d.ts +47 -0
  26. package/dist/datastore-public.d.ts +47 -0
  27. package/dist/datastore-untrimmed.d.ts +324 -0
  28. package/dist/{fluidHandle.js → fluidHandle.cjs} +44 -16
  29. package/dist/fluidHandle.cjs.map +1 -0
  30. package/dist/fluidHandle.d.ts +33 -6
  31. package/dist/fluidHandle.d.ts.map +1 -1
  32. package/dist/index.cjs +15 -0
  33. package/dist/index.cjs.map +1 -0
  34. package/dist/index.d.ts +2 -2
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/localChannelContext.cjs +190 -0
  37. package/dist/localChannelContext.cjs.map +1 -0
  38. package/dist/localChannelContext.d.ts +12 -21
  39. package/dist/localChannelContext.d.ts.map +1 -1
  40. package/dist/{localChannelStorageService.js → localChannelStorageService.cjs} +3 -3
  41. package/dist/localChannelStorageService.cjs.map +1 -0
  42. package/dist/localChannelStorageService.d.ts.map +1 -1
  43. package/dist/remoteChannelContext.cjs +124 -0
  44. package/dist/remoteChannelContext.cjs.map +1 -0
  45. package/dist/remoteChannelContext.d.ts +5 -10
  46. package/dist/remoteChannelContext.d.ts.map +1 -1
  47. package/dist/tsdoc-metadata.json +11 -0
  48. package/lib/{channelContext.d.ts → channelContext.d.mts} +16 -10
  49. package/lib/channelContext.d.mts.map +1 -0
  50. package/lib/channelContext.mjs +78 -0
  51. package/lib/channelContext.mjs.map +1 -0
  52. package/lib/{channelDeltaConnection.d.ts → channelDeltaConnection.d.mts} +4 -5
  53. package/lib/channelDeltaConnection.d.mts.map +1 -0
  54. package/lib/{channelDeltaConnection.js → channelDeltaConnection.mjs} +11 -12
  55. package/lib/channelDeltaConnection.mjs.map +1 -0
  56. package/lib/{channelStorageService.d.ts → channelStorageService.d.mts} +2 -2
  57. package/lib/channelStorageService.d.mts.map +1 -0
  58. package/lib/{channelStorageService.js → channelStorageService.mjs} +13 -16
  59. package/lib/channelStorageService.mjs.map +1 -0
  60. package/lib/{dataStoreRuntime.d.ts → dataStoreRuntime.d.mts} +81 -37
  61. package/lib/dataStoreRuntime.d.mts.map +1 -0
  62. package/lib/{dataStoreRuntime.js → dataStoreRuntime.mjs} +286 -209
  63. package/lib/dataStoreRuntime.mjs.map +1 -0
  64. package/lib/datastore-alpha.d.mts +317 -0
  65. package/lib/datastore-beta.d.mts +47 -0
  66. package/lib/datastore-public.d.mts +47 -0
  67. package/lib/datastore-untrimmed.d.mts +324 -0
  68. package/lib/fluidHandle.d.mts +57 -0
  69. package/lib/fluidHandle.d.mts.map +1 -0
  70. package/lib/{fluidHandle.js → fluidHandle.mjs} +44 -16
  71. package/lib/fluidHandle.mjs.map +1 -0
  72. package/lib/index.d.mts +7 -0
  73. package/lib/index.d.mts.map +1 -0
  74. package/lib/index.mjs +7 -0
  75. package/lib/index.mjs.map +1 -0
  76. package/lib/{localChannelContext.d.ts → localChannelContext.d.mts} +13 -22
  77. package/lib/localChannelContext.d.mts.map +1 -0
  78. package/lib/{localChannelContext.js → localChannelContext.mjs} +73 -85
  79. package/lib/localChannelContext.mjs.map +1 -0
  80. package/lib/localChannelStorageService.d.mts.map +1 -0
  81. package/lib/{localChannelStorageService.js → localChannelStorageService.mjs} +2 -2
  82. package/lib/localChannelStorageService.mjs.map +1 -0
  83. package/lib/{remoteChannelContext.d.ts → remoteChannelContext.d.mts} +7 -12
  84. package/lib/remoteChannelContext.d.mts.map +1 -0
  85. package/lib/remoteChannelContext.mjs +120 -0
  86. package/lib/remoteChannelContext.mjs.map +1 -0
  87. package/package.json +107 -72
  88. package/{lib/index.js → prettier.config.cjs} +4 -3
  89. package/src/channelContext.ts +168 -71
  90. package/src/channelDeltaConnection.ts +52 -47
  91. package/src/channelStorageService.ts +59 -55
  92. package/src/dataStoreRuntime.ts +1158 -983
  93. package/src/fluidHandle.ts +92 -64
  94. package/src/index.ts +8 -2
  95. package/src/localChannelContext.ts +278 -272
  96. package/src/localChannelStorageService.ts +48 -46
  97. package/src/remoteChannelContext.ts +237 -300
  98. package/tsc-multi.test.json +4 -0
  99. package/tsconfig.json +11 -13
  100. package/dist/channelContext.js +0 -35
  101. package/dist/channelContext.js.map +0 -1
  102. package/dist/channelDeltaConnection.js.map +0 -1
  103. package/dist/channelStorageService.js.map +0 -1
  104. package/dist/dataStoreRuntime.js.map +0 -1
  105. package/dist/fluidHandle.js.map +0 -1
  106. package/dist/index.js +0 -19
  107. package/dist/index.js.map +0 -1
  108. package/dist/localChannelContext.js +0 -202
  109. package/dist/localChannelContext.js.map +0 -1
  110. package/dist/localChannelStorageService.js.map +0 -1
  111. package/dist/packageVersion.d.ts +0 -9
  112. package/dist/packageVersion.d.ts.map +0 -1
  113. package/dist/packageVersion.js +0 -12
  114. package/dist/packageVersion.js.map +0 -1
  115. package/dist/remoteChannelContext.js +0 -207
  116. package/dist/remoteChannelContext.js.map +0 -1
  117. package/lib/channelContext.d.ts.map +0 -1
  118. package/lib/channelContext.js +0 -29
  119. package/lib/channelContext.js.map +0 -1
  120. package/lib/channelDeltaConnection.d.ts.map +0 -1
  121. package/lib/channelDeltaConnection.js.map +0 -1
  122. package/lib/channelStorageService.d.ts.map +0 -1
  123. package/lib/channelStorageService.js.map +0 -1
  124. package/lib/dataStoreRuntime.d.ts.map +0 -1
  125. package/lib/dataStoreRuntime.js.map +0 -1
  126. package/lib/fluidHandle.d.ts +0 -30
  127. package/lib/fluidHandle.d.ts.map +0 -1
  128. package/lib/fluidHandle.js.map +0 -1
  129. package/lib/index.d.ts +0 -7
  130. package/lib/index.d.ts.map +0 -1
  131. package/lib/index.js.map +0 -1
  132. package/lib/localChannelContext.d.ts.map +0 -1
  133. package/lib/localChannelContext.js.map +0 -1
  134. package/lib/localChannelStorageService.d.ts.map +0 -1
  135. package/lib/localChannelStorageService.js.map +0 -1
  136. package/lib/packageVersion.d.ts +0 -9
  137. package/lib/packageVersion.d.ts.map +0 -1
  138. package/lib/packageVersion.js +0 -9
  139. package/lib/packageVersion.js.map +0 -1
  140. package/lib/remoteChannelContext.d.ts.map +0 -1
  141. package/lib/remoteChannelContext.js +0 -203
  142. package/lib/remoteChannelContext.js.map +0 -1
  143. package/src/packageVersion.ts +0 -9
  144. package/tsconfig.esnext.json +0 -7
  145. /package/lib/{localChannelStorageService.d.ts → localChannelStorageService.d.mts} +0 -0
@@ -3,311 +3,317 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
- // eslint-disable-next-line import/no-internal-modules
7
- import cloneDeep from "lodash/cloneDeep";
8
- import { ITelemetryLogger } from "@fluidframework/common-definitions";
6
+ import lodashPkg from "lodash";
7
+ // eslint-disable-next-line @typescript-eslint/unbound-method
8
+ const { cloneDeep } = lodashPkg;
9
+
10
+ import { DataProcessingError, ITelemetryLoggerExt } from "@fluidframework/telemetry-utils";
9
11
  import { IDocumentStorageService } from "@fluidframework/driver-definitions";
10
12
  import { ISequencedDocumentMessage, ISnapshotTree } from "@fluidframework/protocol-definitions";
13
+ import { IChannel, IFluidDataStoreRuntime } from "@fluidframework/datastore-definitions";
11
14
  import {
12
- IChannel,
13
- IFluidDataStoreRuntime,
14
- IChannelFactory,
15
- IChannelAttributes,
16
- } from "@fluidframework/datastore-definitions";
17
- import {
18
- IFluidDataStoreContext,
19
- IGarbageCollectionData,
20
- ISummarizeResult,
21
- ITelemetryContext,
15
+ IFluidDataStoreContext,
16
+ IGarbageCollectionData,
17
+ ISummarizeResult,
18
+ ITelemetryContext,
22
19
  } from "@fluidframework/runtime-definitions";
23
- import { readAndParse } from "@fluidframework/driver-utils";
24
- import { DataProcessingError } from "@fluidframework/container-utils";
25
- import { assert, Lazy } from "@fluidframework/common-utils";
20
+ import { assert, Lazy, LazyPromise } from "@fluidframework/core-utils";
26
21
  import { IFluidHandle } from "@fluidframework/core-interfaces";
22
+ import { ISnapshotTreeWithBlobContents } from "@fluidframework/container-definitions";
27
23
  import {
28
- createServiceEndpoints,
29
- IChannelContext,
30
- summarizeChannel,
31
- summarizeChannelAsync,
24
+ ChannelServiceEndpoints,
25
+ createChannelServiceEndpoints,
26
+ IChannelContext,
27
+ loadChannel,
28
+ loadChannelFactoryAndAttributes,
29
+ summarizeChannel,
30
+ summarizeChannelAsync,
32
31
  } from "./channelContext";
33
- import { ChannelDeltaConnection } from "./channelDeltaConnection";
34
32
  import { ISharedObjectRegistry } from "./dataStoreRuntime";
35
- import { ChannelStorageService } from "./channelStorageService";
36
33
 
37
34
  /**
38
35
  * Channel context for a locally created channel
39
36
  */
40
37
  export abstract class LocalChannelContextBase implements IChannelContext {
41
- public channel: IChannel | undefined;
42
- private globallyVisible = false;
43
- protected readonly pending: ISequencedDocumentMessage[] = [];
44
- protected factory: IChannelFactory | undefined;
45
- constructor(
46
- protected readonly id: string,
47
- protected readonly registry: ISharedObjectRegistry,
48
- protected readonly runtime: IFluidDataStoreRuntime,
49
- private readonly servicesGetter: () => Lazy<{
50
- readonly deltaConnection: ChannelDeltaConnection;
51
- readonly objectStorage: ChannelStorageService;
52
- }>,
53
- ) {
54
- assert(!this.id.includes("/"), 0x30f /* Channel context ID cannot contain slashes */);
55
- }
38
+ private globallyVisible = false;
39
+ protected readonly pending: ISequencedDocumentMessage[] = [];
40
+ constructor(
41
+ protected readonly id: string,
42
+ protected readonly runtime: IFluidDataStoreRuntime,
43
+ protected readonly services: Lazy<ChannelServiceEndpoints>,
44
+ private readonly channelP: Promise<IChannel>,
45
+ private _channel?: IChannel,
46
+ ) {
47
+ assert(!this.id.includes("/"), 0x30f /* Channel context ID cannot contain slashes */);
48
+ }
56
49
 
57
- public async getChannel(): Promise<IChannel> {
58
- assert(this.channel !== undefined, 0x207 /* "Channel should be defined" */);
59
- return this.channel;
60
- }
50
+ public async getChannel(): Promise<IChannel> {
51
+ if (this._channel === undefined) {
52
+ return this.channelP.then((c) => (this._channel = c));
53
+ }
54
+ return this.channelP;
55
+ }
61
56
 
62
- public get isLoaded(): boolean {
63
- return this.channel !== undefined;
64
- }
57
+ public get isLoaded(): boolean {
58
+ return this._channel !== undefined;
59
+ }
65
60
 
66
- public setConnectionState(connected: boolean, clientId?: string) {
67
- // Connection events are ignored if the data store is not yet globallyVisible or loaded
68
- if (this.globallyVisible && this.isLoaded) {
69
- this.servicesGetter().value.deltaConnection.setConnectionState(connected);
70
- }
71
- }
61
+ public setConnectionState(connected: boolean, clientId?: string) {
62
+ // Connection events are ignored if the data store is not yet globallyVisible or loaded
63
+ if (this.globallyVisible && this.isLoaded) {
64
+ this.services.value.deltaConnection.setConnectionState(connected);
65
+ }
66
+ }
72
67
 
73
- public processOp(message: ISequencedDocumentMessage, local: boolean, localOpMetadata: unknown): void {
74
- assert(this.globallyVisible, 0x2d3 /* "Local channel must be globally visible when processing op" */);
68
+ public processOp(
69
+ message: ISequencedDocumentMessage,
70
+ local: boolean,
71
+ localOpMetadata: unknown,
72
+ ): void {
73
+ assert(
74
+ this.globallyVisible,
75
+ 0x2d3 /* "Local channel must be globally visible when processing op" */,
76
+ );
75
77
 
76
- // A local channel may not be loaded in case where we rehydrate the container from a snapshot because of
77
- // delay loading. So after the container is attached and some other client joins which start generating
78
- // ops for this channel. So not loaded local channel can still receive ops and we store them to process later.
79
- if (this.isLoaded) {
80
- this.servicesGetter().value.deltaConnection.process(message, local, localOpMetadata);
81
- } else {
82
- assert(local === false,
83
- 0x189 /* "Should always be remote because a local dds shouldn't generate ops before loading" */);
84
- this.pending.push(message);
85
- }
86
- }
78
+ // A local channel may not be loaded in case where we rehydrate the container from a snapshot because of
79
+ // delay loading. So after the container is attached and some other client joins which start generating
80
+ // ops for this channel. So not loaded local channel can still receive ops and we store them to process later.
81
+ if (this.isLoaded) {
82
+ this.services.value.deltaConnection.process(message, local, localOpMetadata);
83
+ } else {
84
+ assert(
85
+ local === false,
86
+ 0x189 /* "Should always be remote because a local dds shouldn't generate ops before loading" */,
87
+ );
88
+ this.pending.push(message);
89
+ }
90
+ }
87
91
 
88
- public reSubmit(content: any, localOpMetadata: unknown) {
89
- assert(this.isLoaded, 0x18a /* "Channel should be loaded to resubmit ops" */);
90
- assert(this.globallyVisible, 0x2d4 /* "Local channel must be globally visible when resubmitting op" */);
91
- this.servicesGetter().value.deltaConnection.reSubmit(content, localOpMetadata);
92
- }
93
- public rollback(content: any, localOpMetadata: unknown) {
94
- assert(this.isLoaded, 0x2ee /* "Channel should be loaded to rollback ops" */);
95
- assert(this.globallyVisible, 0x2ef /* "Local channel must be globally visible when rolling back op" */);
96
- this.servicesGetter().value.deltaConnection.rollback(content, localOpMetadata);
97
- }
92
+ public reSubmit(content: any, localOpMetadata: unknown) {
93
+ assert(this.isLoaded, 0x18a /* "Channel should be loaded to resubmit ops" */);
94
+ assert(
95
+ this.globallyVisible,
96
+ 0x2d4 /* "Local channel must be globally visible when resubmitting op" */,
97
+ );
98
+ this.services.value.deltaConnection.reSubmit(content, localOpMetadata);
99
+ }
100
+ public rollback(content: any, localOpMetadata: unknown) {
101
+ assert(this.isLoaded, 0x2ee /* "Channel should be loaded to rollback ops" */);
102
+ assert(
103
+ this.globallyVisible,
104
+ 0x2ef /* "Local channel must be globally visible when rolling back op" */,
105
+ );
106
+ this.services.value.deltaConnection.rollback(content, localOpMetadata);
107
+ }
98
108
 
99
- public applyStashedOp() {
100
- throw new Error("no stashed ops on local channel");
101
- }
109
+ public applyStashedOp() {
110
+ throw new Error("no stashed ops on local channel");
111
+ }
102
112
 
103
- /**
104
- * Returns a summary at the current sequence number.
105
- * @param fullTree - true to bypass optimizations and force a full summary tree
106
- * @param trackState - This tells whether we should track state from this summary.
107
- * @param telemetryContext - summary data passed through the layers for telemetry purposes
108
- */
109
- public async summarize(
110
- fullTree: boolean = false,
111
- trackState: boolean = false,
112
- telemetryContext?: ITelemetryContext,
113
- ): Promise<ISummarizeResult> {
114
- assert(this.isLoaded && this.channel !== undefined, 0x18c /* "Channel should be loaded to summarize" */);
115
- return summarizeChannelAsync(this.channel, fullTree, trackState, telemetryContext);
116
- }
113
+ /**
114
+ * Returns a summary at the current sequence number.
115
+ * @param fullTree - true to bypass optimizations and force a full summary tree
116
+ * @param trackState - This tells whether we should track state from this summary.
117
+ * @param telemetryContext - summary data passed through the layers for telemetry purposes
118
+ */
119
+ public async summarize(
120
+ fullTree: boolean = false,
121
+ trackState: boolean = false,
122
+ telemetryContext?: ITelemetryContext,
123
+ ): Promise<ISummarizeResult> {
124
+ const channel = await this.getChannel();
125
+ return summarizeChannelAsync(channel, fullTree, trackState, telemetryContext);
126
+ }
117
127
 
118
- public getAttachSummary(telemetryContext?: ITelemetryContext): ISummarizeResult {
119
- assert(this.isLoaded && this.channel !== undefined, 0x18d /* "Channel should be loaded to take snapshot" */);
120
- return summarizeChannel(this.channel, true /* fullTree */, false /* trackState */, telemetryContext);
121
- }
128
+ public getAttachSummary(telemetryContext?: ITelemetryContext): ISummarizeResult {
129
+ assert(
130
+ this._channel !== undefined,
131
+ 0x18d /* "Channel should be loaded to take snapshot" */,
132
+ );
133
+ return summarizeChannel(
134
+ this._channel,
135
+ true /* fullTree */,
136
+ false /* trackState */,
137
+ telemetryContext,
138
+ );
139
+ }
122
140
 
123
- public makeVisible(): void {
124
- if (this.globallyVisible) {
125
- throw new Error("Channel is already globally visible");
126
- }
141
+ public makeVisible(): void {
142
+ if (this.globallyVisible) {
143
+ throw new Error("Channel is already globally visible");
144
+ }
127
145
 
128
- if (this.isLoaded) {
129
- assert(!!this.channel, 0x192 /* "Channel should be there if loaded!!" */);
130
- this.channel.connect(this.servicesGetter().value);
131
- }
132
- this.globallyVisible = true;
133
- }
146
+ if (this.isLoaded) {
147
+ assert(!!this._channel, 0x192 /* "Channel should be there if loaded!!" */);
148
+ this._channel.connect(this.services.value);
149
+ }
150
+ this.globallyVisible = true;
151
+ }
134
152
 
135
- /**
136
- * Returns the data used for garbage collection. This includes a list of GC nodes that represent this context.
137
- * Each node has a set of outbound routes to other GC nodes in the document. This should be called only after
138
- * the context has loaded.
139
- * @param fullGC - true to bypass optimizations and force full generation of GC data.
140
- */
141
- public async getGCData(fullGC: boolean = false): Promise<IGarbageCollectionData> {
142
- assert(this.isLoaded && this.channel !== undefined, 0x193 /* "Channel should be loaded to run GC" */);
143
- return this.channel.getGCData(fullGC);
144
- }
153
+ /**
154
+ * Returns the data used for garbage collection. This includes a list of GC nodes that represent this context.
155
+ * Each node has a set of outbound routes to other GC nodes in the document. This should be called only after
156
+ * the context has loaded.
157
+ * @param fullGC - true to bypass optimizations and force full generation of GC data.
158
+ */
159
+ public async getGCData(fullGC: boolean = false): Promise<IGarbageCollectionData> {
160
+ const channel = await this.getChannel();
161
+ return channel.getGCData(fullGC);
162
+ }
145
163
 
146
- public updateUsedRoutes(usedRoutes: string[], gcTimestamp?: number) {
147
- /**
148
- * Currently, DDSes are always considered referenced and are not garbage collected.
149
- * Once we have GC at DDS level, this channel context's used routes will be updated as per the passed
150
- * value. See - https://github.com/microsoft/FluidFramework/issues/4611
151
- */
152
- }
164
+ public updateUsedRoutes(usedRoutes: string[]) {
165
+ /**
166
+ * Currently, DDSes are always considered referenced and are not garbage collected.
167
+ * Once we have GC at DDS level, this channel context's used routes will be updated as per the passed
168
+ * value. See - https://github.com/microsoft/FluidFramework/issues/4611
169
+ */
170
+ }
153
171
  }
154
172
 
155
173
  export class RehydratedLocalChannelContext extends LocalChannelContextBase {
156
- private readonly services: Lazy<{
157
- readonly deltaConnection: ChannelDeltaConnection;
158
- readonly objectStorage: ChannelStorageService;
159
- }>;
160
-
161
- private readonly dirtyFn: () => void;
162
-
163
- constructor(
164
- id: string,
165
- registry: ISharedObjectRegistry,
166
- runtime: IFluidDataStoreRuntime,
167
- dataStoreContext: IFluidDataStoreContext,
168
- storageService: IDocumentStorageService,
169
- logger: ITelemetryLogger,
170
- submitFn: (content: any, localOpMetadata: unknown) => void,
171
- dirtyFn: (address: string) => void,
172
- addedGCOutboundReferenceFn: (srcHandle: IFluidHandle, outboundHandle: IFluidHandle) => void,
173
- private readonly snapshotTree: ISnapshotTree,
174
- ) {
175
- super(id, registry, runtime, () => this.services);
176
- const blobMap: Map<string, ArrayBufferLike> = new Map<string, ArrayBufferLike>();
177
- const clonedSnapshotTree = cloneDeep(this.snapshotTree);
178
- // 0.47 back-compat Need to sanitize if snapshotTree.blobs still contains blob contents too.
179
- // This is for older snapshot which is generated by loader <=0.47 version which still contains
180
- // the contents within blobs. After a couple of revisions we can remove it.
181
- if (this.isSnapshotInOldFormatAndCollectBlobs(clonedSnapshotTree, blobMap)) {
182
- this.sanitizeSnapshot(clonedSnapshotTree);
183
- }
184
-
185
- this.services = new Lazy(() => {
186
- return createServiceEndpoints(
187
- this.id,
188
- dataStoreContext.connected,
189
- submitFn,
190
- this.dirtyFn,
191
- addedGCOutboundReferenceFn,
192
- storageService,
193
- logger,
194
- clonedSnapshotTree,
195
- blobMap,
196
- );
197
- });
198
- this.dirtyFn = () => { dirtyFn(id); };
199
- }
174
+ private readonly dirtyFn: () => void;
175
+ constructor(
176
+ id: string,
177
+ registry: ISharedObjectRegistry,
178
+ runtime: IFluidDataStoreRuntime,
179
+ dataStoreContext: IFluidDataStoreContext,
180
+ storageService: IDocumentStorageService,
181
+ logger: ITelemetryLoggerExt,
182
+ submitFn: (content: any, localOpMetadata: unknown) => void,
183
+ dirtyFn: (address: string) => void,
184
+ addedGCOutboundReferenceFn: (srcHandle: IFluidHandle, outboundHandle: IFluidHandle) => void,
185
+ private readonly snapshotTree: ISnapshotTree,
186
+ ) {
187
+ super(
188
+ id,
189
+ runtime,
190
+ new Lazy(() => {
191
+ const blobMap: Map<string, ArrayBufferLike> = new Map<string, ArrayBufferLike>();
192
+ const clonedSnapshotTree = cloneDeep(this.snapshotTree);
193
+ // 0.47 back-compat Need to sanitize if snapshotTree.blobs still contains blob contents too.
194
+ // This is for older snapshot which is generated by loader <=0.47 version which still contains
195
+ // the contents within blobs. After a couple of revisions we can remove it.
196
+ if (this.isSnapshotInOldFormatAndCollectBlobs(clonedSnapshotTree, blobMap)) {
197
+ this.sanitizeSnapshot(clonedSnapshotTree);
198
+ }
199
+ return createChannelServiceEndpoints(
200
+ dataStoreContext.connected,
201
+ submitFn,
202
+ this.dirtyFn,
203
+ addedGCOutboundReferenceFn,
204
+ storageService,
205
+ logger,
206
+ clonedSnapshotTree,
207
+ blobMap,
208
+ );
209
+ }),
210
+ new LazyPromise<IChannel>(async () => {
211
+ try {
212
+ const { attributes, factory } = await loadChannelFactoryAndAttributes(
213
+ dataStoreContext,
214
+ this.services.value,
215
+ this.id,
216
+ registry,
217
+ );
218
+ const channel = await loadChannel(
219
+ runtime,
220
+ attributes,
221
+ factory,
222
+ this.services.value,
223
+ logger,
224
+ this.id,
225
+ );
226
+ // Send all pending messages to the channel
227
+ for (const message of this.pending) {
228
+ this.services.value.deltaConnection.process(
229
+ message,
230
+ false,
231
+ undefined /* localOpMetadata */,
232
+ );
233
+ }
234
+ return channel;
235
+ } catch (err) {
236
+ throw DataProcessingError.wrapIfUnrecognized(
237
+ err,
238
+ "rehydratedLocalChannelContextFailedToLoadChannel",
239
+ undefined,
240
+ );
241
+ }
242
+ }),
243
+ );
200
244
 
201
- public async getChannel(): Promise<IChannel> {
202
- if (this.channel === undefined) {
203
- this.channel = await this.loadChannel()
204
- .catch((err) => {
205
- throw DataProcessingError.wrapIfUnrecognized(
206
- err, "rehydratedLocalChannelContextFailedToLoadChannel", undefined);
207
- });
208
- }
209
- return this.channel;
210
- }
245
+ this.dirtyFn = () => {
246
+ dirtyFn(id);
247
+ };
248
+ }
211
249
 
212
- private async loadChannel(): Promise<IChannel> {
213
- assert(!this.isLoaded, 0x18e /* "Channel must not already be loaded when loading" */);
214
- assert(await this.services.value.objectStorage.contains(".attributes"),
215
- 0x190 /* ".attributes blob should be present" */);
216
- const attributes = await readAndParse<IChannelAttributes>(
217
- this.services.value.objectStorage,
218
- ".attributes");
250
+ private isSnapshotInOldFormatAndCollectBlobs(
251
+ snapshotTree: ISnapshotTreeWithBlobContents,
252
+ blobMap: Map<string, ArrayBufferLike>,
253
+ ): boolean {
254
+ let sanitize = false;
255
+ const blobsContents = snapshotTree.blobsContents;
256
+ if (blobsContents !== undefined) {
257
+ Object.entries(blobsContents).forEach(([key, value]) => {
258
+ blobMap.set(key, value);
259
+ if (snapshotTree.blobs[key] !== undefined) {
260
+ sanitize = true;
261
+ }
262
+ });
263
+ }
264
+ for (const value of Object.values(snapshotTree.trees)) {
265
+ sanitize = sanitize || this.isSnapshotInOldFormatAndCollectBlobs(value, blobMap);
266
+ }
267
+ return sanitize;
268
+ }
219
269
 
220
- assert(this.factory === undefined, 0x208 /* "Factory should be undefined before loading" */);
221
- this.factory = this.registry.get(attributes.type);
222
- if (this.factory === undefined) {
223
- throw new Error(`Channel Factory ${attributes.type} not registered`);
224
- }
225
- // Services will be assigned during this load.
226
- const channel = await this.factory.load(
227
- this.runtime,
228
- this.id,
229
- this.services.value,
230
- attributes);
231
-
232
- // Commit changes.
233
- this.channel = channel;
234
-
235
- // Send all pending messages to the channel
236
- for (const message of this.pending) {
237
- this.services.value.deltaConnection.process(message, false, undefined /* localOpMetadata */);
238
- }
239
- return this.channel;
240
- }
241
-
242
- private isSnapshotInOldFormatAndCollectBlobs(
243
- snapshotTree: ISnapshotTree,
244
- blobMap: Map<string, ArrayBufferLike>,
245
- ): boolean {
246
- let sanitize = false;
247
- const blobsContents: { [path: string]: ArrayBufferLike; } = (snapshotTree as any).blobsContents;
248
- Object.entries(blobsContents).forEach(([key, value]) => {
249
- blobMap.set(key, value);
250
- if (snapshotTree.blobs[key] !== undefined) {
251
- sanitize = true;
252
- }
253
- });
254
- for (const value of Object.values(snapshotTree.trees)) {
255
- sanitize = sanitize || this.isSnapshotInOldFormatAndCollectBlobs(value, blobMap);
256
- }
257
- return sanitize;
258
- }
259
-
260
- private sanitizeSnapshot(snapshotTree: ISnapshotTree) {
261
- const blobMapInitial = new Map(Object.entries(snapshotTree.blobs));
262
- for (const [blobName, blobId] of blobMapInitial.entries()) {
263
- const blobValue = blobMapInitial.get(blobId);
264
- if (blobValue === undefined) {
265
- // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
266
- delete snapshotTree.blobs[blobName];
267
- }
268
- }
269
- for (const value of Object.values(snapshotTree.trees)) {
270
- this.sanitizeSnapshot(value);
271
- }
272
- }
270
+ private sanitizeSnapshot(snapshotTree: ISnapshotTree) {
271
+ const blobMapInitial = new Map(Object.entries(snapshotTree.blobs));
272
+ for (const [blobName, blobId] of blobMapInitial.entries()) {
273
+ const blobValue = blobMapInitial.get(blobId);
274
+ if (blobValue === undefined) {
275
+ // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
276
+ delete snapshotTree.blobs[blobName];
277
+ }
278
+ }
279
+ for (const value of Object.values(snapshotTree.trees)) {
280
+ this.sanitizeSnapshot(value);
281
+ }
282
+ }
273
283
  }
274
284
 
275
285
  export class LocalChannelContext extends LocalChannelContextBase {
276
- private readonly services: Lazy<{
277
- readonly deltaConnection: ChannelDeltaConnection;
278
- readonly objectStorage: ChannelStorageService;
279
- }>;
280
- private readonly dirtyFn: () => void;
281
- constructor(
282
- id: string,
283
- registry: ISharedObjectRegistry,
284
- type: string,
285
- runtime: IFluidDataStoreRuntime,
286
- dataStoreContext: IFluidDataStoreContext,
287
- storageService: IDocumentStorageService,
288
- logger: ITelemetryLogger,
289
- submitFn: (content: any, localOpMetadata: unknown) => void,
290
- dirtyFn: (address: string) => void,
291
- addedGCOutboundReferenceFn: (srcHandle: IFluidHandle, outboundHandle: IFluidHandle) => void,
292
- ) {
293
- super(id, registry, runtime, () => this.services);
294
- assert(type !== undefined, 0x209 /* "Factory Type should be defined" */);
295
- this.factory = registry.get(type);
296
- if (this.factory === undefined) {
297
- throw new Error(`Channel Factory ${type} not registered`);
298
- }
299
- this.channel = this.factory.create(runtime, id);
300
- this.services = new Lazy(() => {
301
- return createServiceEndpoints(
302
- this.id,
303
- dataStoreContext.connected,
304
- submitFn,
305
- this.dirtyFn,
306
- addedGCOutboundReferenceFn,
307
- storageService,
308
- logger,
309
- );
310
- });
311
- this.dirtyFn = () => { dirtyFn(id); };
312
- }
286
+ private readonly dirtyFn: () => void;
287
+ constructor(
288
+ public readonly channel: IChannel,
289
+ runtime: IFluidDataStoreRuntime,
290
+ dataStoreContext: IFluidDataStoreContext,
291
+ storageService: IDocumentStorageService,
292
+ logger: ITelemetryLoggerExt,
293
+ submitFn: (content: any, localOpMetadata: unknown) => void,
294
+ dirtyFn: (address: string) => void,
295
+ addedGCOutboundReferenceFn: (srcHandle: IFluidHandle, outboundHandle: IFluidHandle) => void,
296
+ ) {
297
+ super(
298
+ channel.id,
299
+ runtime,
300
+ new Lazy(() => {
301
+ return createChannelServiceEndpoints(
302
+ dataStoreContext.connected,
303
+ submitFn,
304
+ this.dirtyFn,
305
+ addedGCOutboundReferenceFn,
306
+ storageService,
307
+ logger,
308
+ );
309
+ }),
310
+ Promise.resolve(channel),
311
+ channel,
312
+ );
313
+ this.channel = channel;
314
+
315
+ this.dirtyFn = () => {
316
+ dirtyFn(channel.id);
317
+ };
318
+ }
313
319
  }