@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
@@ -5,58 +5,129 @@
5
5
  */
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
7
  exports.mixinSummaryHandler = exports.mixinRequestHandler = exports.FluidDataStoreRuntime = exports.DataStoreMessageType = void 0;
8
- const container_definitions_1 = require("@fluidframework/container-definitions");
9
- const container_utils_1 = require("@fluidframework/container-utils");
10
- const common_utils_1 = require("@fluidframework/common-utils");
8
+ const client_utils_1 = require("@fluid-internal/client-utils");
11
9
  const telemetry_utils_1 = require("@fluidframework/telemetry-utils");
10
+ const core_utils_1 = require("@fluidframework/core-utils");
11
+ const container_definitions_1 = require("@fluidframework/container-definitions");
12
12
  const driver_utils_1 = require("@fluidframework/driver-utils");
13
13
  const protocol_definitions_1 = require("@fluidframework/protocol-definitions");
14
14
  const runtime_definitions_1 = require("@fluidframework/runtime-definitions");
15
15
  const runtime_utils_1 = require("@fluidframework/runtime-utils");
16
- const garbage_collector_1 = require("@fluidframework/garbage-collector");
17
16
  const uuid_1 = require("uuid");
18
- const channelContext_1 = require("./channelContext");
19
- const localChannelContext_1 = require("./localChannelContext");
20
- const remoteChannelContext_1 = require("./remoteChannelContext");
17
+ const channelContext_1 = require("./channelContext.cjs");
18
+ const localChannelContext_1 = require("./localChannelContext.cjs");
19
+ const remoteChannelContext_1 = require("./remoteChannelContext.cjs");
20
+ const fluidHandle_1 = require("./fluidHandle.cjs");
21
+ /**
22
+ * @alpha
23
+ */
21
24
  var DataStoreMessageType;
22
25
  (function (DataStoreMessageType) {
23
26
  // Creates a new channel
24
27
  DataStoreMessageType["Attach"] = "attach";
25
28
  DataStoreMessageType["ChannelOp"] = "op";
26
- })(DataStoreMessageType = exports.DataStoreMessageType || (exports.DataStoreMessageType = {}));
29
+ })(DataStoreMessageType || (exports.DataStoreMessageType = DataStoreMessageType = {}));
27
30
  /**
28
31
  * Base data store class
32
+ * @alpha
29
33
  */
30
- class FluidDataStoreRuntime extends common_utils_1.TypedEventEmitter {
31
- constructor(dataStoreContext, sharedObjectRegistry, existing) {
34
+ class FluidDataStoreRuntime extends client_utils_1.TypedEventEmitter {
35
+ get connected() {
36
+ return this.dataStoreContext.connected;
37
+ }
38
+ get clientId() {
39
+ return this.dataStoreContext.clientId;
40
+ }
41
+ get clientDetails() {
42
+ return this.dataStoreContext.clientDetails;
43
+ }
44
+ get isAttached() {
45
+ return this.attachState !== container_definitions_1.AttachState.Detached;
46
+ }
47
+ get attachState() {
48
+ return this._attachState;
49
+ }
50
+ get absolutePath() {
51
+ return (0, runtime_utils_1.generateHandleContextPath)(this.id, this.routeContext);
52
+ }
53
+ get routeContext() {
54
+ return this.dataStoreContext.IFluidHandleContext;
55
+ }
56
+ get idCompressor() {
57
+ return this.dataStoreContext.idCompressor;
58
+ }
59
+ get IFluidHandleContext() {
60
+ return this;
61
+ }
62
+ get rootRoutingContext() {
63
+ return this;
64
+ }
65
+ get channelsRoutingContext() {
66
+ return this;
67
+ }
68
+ get objectsRoutingContext() {
69
+ return this;
70
+ }
71
+ get disposed() {
72
+ return this._disposed;
73
+ }
74
+ get logger() {
75
+ return this.mc.logger;
76
+ }
77
+ /**
78
+ * Invokes the given callback and expects that no ops are submitted
79
+ * until execution finishes. If an op is submitted, an error will be raised.
80
+ *
81
+ * Can be disabled by feature gate `Fluid.ContainerRuntime.DisableOpReentryCheck`
82
+ *
83
+ * @param callback - the callback to be invoked
84
+ */
85
+ ensureNoDataModelChanges(callback) {
86
+ // back-compat ADO:2309
87
+ return this.dataStoreContext.ensureNoDataModelChanges === undefined
88
+ ? callback()
89
+ : this.dataStoreContext.ensureNoDataModelChanges(callback);
90
+ }
91
+ /**
92
+ * Create an instance of a DataStore runtime.
93
+ *
94
+ * @param dataStoreContext - Context object for the runtime.
95
+ * @param sharedObjectRegistry - The registry of shared objects that this data store will be able to instantiate.
96
+ * @param existing - Pass 'true' if loading this datastore from an existing file; pass 'false' otherwise.
97
+ * @param provideEntryPoint - Function to initialize the entryPoint object for the data store runtime. The
98
+ * handle to this data store runtime will point to the object returned by this function. If this function is not
99
+ * provided, the handle will be left undefined. This is here so we can start making handles a first-class citizen
100
+ * and the primary way of interacting with some Fluid objects, and should be used if possible.
101
+ */
102
+ constructor(dataStoreContext, sharedObjectRegistry, existing, provideEntryPoint) {
32
103
  super();
33
104
  this.dataStoreContext = dataStoreContext;
34
105
  this.sharedObjectRegistry = sharedObjectRegistry;
35
106
  this._disposed = false;
36
107
  this.contexts = new Map();
37
- this.contextsDeferred = new Map();
38
- this.pendingAttach = new Map();
39
- this.deferredAttached = new common_utils_1.Deferred();
108
+ this.pendingAttach = new Set();
109
+ this.deferredAttached = new core_utils_1.Deferred();
40
110
  this.localChannelContextQueue = new Map();
41
111
  this.notBoundedChannelContextSet = new Set();
42
112
  // A list of handles that are bound when the data store is not visible. We have to make them visible when the data
43
113
  // store becomes visible.
44
114
  this.pendingHandlesToMakeVisible = new Set();
45
- (0, common_utils_1.assert)(!dataStoreContext.id.includes("/"), 0x30e /* Id cannot contain slashes. DataStoreContext should have validated this. */);
46
- this.logger = telemetry_utils_1.ChildLogger.create(dataStoreContext.logger, "FluidDataStoreRuntime", { all: { dataStoreId: (0, uuid_1.v4)() } });
115
+ (0, core_utils_1.assert)(!dataStoreContext.id.includes("/"), 0x30e /* Id cannot contain slashes. DataStoreContext should have validated this. */);
116
+ this.mc = (0, telemetry_utils_1.createChildMonitoringContext)({
117
+ logger: dataStoreContext.logger,
118
+ namespace: "FluidDataStoreRuntime",
119
+ properties: {
120
+ all: { dataStoreId: (0, uuid_1.v4)() },
121
+ },
122
+ });
47
123
  this.id = dataStoreContext.id;
48
124
  this.options = dataStoreContext.options;
49
125
  this.deltaManager = dataStoreContext.deltaManager;
50
126
  this.quorum = dataStoreContext.getQuorum();
51
127
  this.audience = dataStoreContext.getAudience();
52
128
  const tree = dataStoreContext.baseSnapshot;
53
- this.channelsBaseGCDetails = new common_utils_1.LazyPromise(async () => {
54
- var _a, _b, _c;
55
- const baseGCDetails = await ((_c = (_b = (_a = this.dataStoreContext).getBaseGCDetails) === null || _b === void 0 ? void 0 : _b.call(_a)) !== null && _c !== void 0 ? _c : this.dataStoreContext.getInitialGCSummaryDetails());
56
- return (0, garbage_collector_1.unpackChildNodesGCDetails)(baseGCDetails);
57
- });
58
129
  // Must always receive the data store type inside of the attributes
59
- if ((tree === null || tree === void 0 ? void 0 : tree.trees) !== undefined) {
130
+ if (tree?.trees !== undefined) {
60
131
  Object.keys(tree.trees).forEach((path) => {
61
132
  // Issue #4414
62
133
  if (path === "_search") {
@@ -80,29 +151,31 @@ class FluidDataStoreRuntime extends common_utils_1.TypedEventEmitter {
80
151
  }
81
152
  }
82
153
  else {
83
- channelContext = new remoteChannelContext_1.RemoteChannelContext(this, dataStoreContext, dataStoreContext.storage, (content, localOpMetadata) => this.submitChannelOp(path, content, localOpMetadata), (address) => this.setChannelDirty(address), (srcHandle, outboundHandle) => this.addedGCOutboundReference(srcHandle, outboundHandle), path, tree.trees[path], this.sharedObjectRegistry, undefined /* extraBlobs */, this.dataStoreContext.getCreateChildSummarizerNodeFn(path, { type: runtime_definitions_1.CreateSummarizerNodeSource.FromSummary }), async () => this.getChannelBaseGCDetails(path));
154
+ channelContext = new remoteChannelContext_1.RemoteChannelContext(this, dataStoreContext, dataStoreContext.storage, (content, localOpMetadata) => this.submitChannelOp(path, content, localOpMetadata), (address) => this.setChannelDirty(address), (srcHandle, outboundHandle) => this.addedGCOutboundReference(srcHandle, outboundHandle), path, tree.trees[path], this.sharedObjectRegistry, undefined /* extraBlobs */, this.dataStoreContext.getCreateChildSummarizerNodeFn(path, {
155
+ type: runtime_definitions_1.CreateSummarizerNodeSource.FromSummary,
156
+ }));
84
157
  }
85
- const deferred = new common_utils_1.Deferred();
86
- deferred.resolve(channelContext);
87
158
  this.contexts.set(path, channelContext);
88
- this.contextsDeferred.set(path, deferred);
89
159
  });
90
160
  }
161
+ this.entryPoint = new fluidHandle_1.FluidObjectHandle(new core_utils_1.LazyPromise(async () => provideEntryPoint(this)), "", this.objectsRoutingContext);
91
162
  this.attachListener();
92
- // If exists on storage or loaded from a snapshot, it should already be bound.
93
- this.bindState = existing ? container_definitions_1.BindState.Bound : container_definitions_1.BindState.NotBound;
94
163
  this._attachState = dataStoreContext.attachState;
95
164
  /**
96
165
  * If existing flag is false, this is a new data store and is not visible. The existing flag can be true in two
97
166
  * conditions:
167
+ *
98
168
  * 1. It's a local data store that is created when a detached container is rehydrated. In this case, the data
99
- * store is locally visible because the snapshot it is loaded from contains locally visible data stores only.
169
+ * store is locally visible because the snapshot it is loaded from contains locally visible data stores only.
170
+ *
100
171
  * 2. It's a remote data store that is created when an attached container is loaded is loaded from snapshot or
101
- * when an attach op comes in. In both these cases, the data store is already globally visible.
172
+ * when an attach op comes in. In both these cases, the data store is already globally visible.
102
173
  */
103
174
  if (existing) {
104
- this.visibilityState = dataStoreContext.attachState === container_definitions_1.AttachState.Detached
105
- ? runtime_definitions_1.VisibilityState.LocallyVisible : runtime_definitions_1.VisibilityState.GloballyVisible;
175
+ this.visibilityState =
176
+ dataStoreContext.attachState === container_definitions_1.AttachState.Detached
177
+ ? runtime_definitions_1.VisibilityState.LocallyVisible
178
+ : runtime_definitions_1.VisibilityState.GloballyVisible;
106
179
  }
107
180
  else {
108
181
  this.visibilityState = runtime_definitions_1.VisibilityState.NotVisible;
@@ -111,43 +184,10 @@ class FluidDataStoreRuntime extends common_utils_1.TypedEventEmitter {
111
184
  if (existing) {
112
185
  this.deferredAttached.resolve();
113
186
  }
187
+ // By default, a data store can log maximum 10 local changes telemetry in summarizer.
188
+ this.localChangesTelemetryCount =
189
+ this.mc.config.getNumber("Fluid.Telemetry.LocalChangesTelemetryCount") ?? 10;
114
190
  }
115
- /**
116
- * Loads the data store runtime
117
- * @param context - The data store context
118
- * @param sharedObjectRegistry - The registry of shared objects used by this data store
119
- * @param existing - If loading from an existing file.
120
- */
121
- static load(context, sharedObjectRegistry, existing) {
122
- return new FluidDataStoreRuntime(context, sharedObjectRegistry, existing);
123
- }
124
- get IFluidRouter() { return this; }
125
- get connected() {
126
- return this.dataStoreContext.connected;
127
- }
128
- get clientId() {
129
- return this.dataStoreContext.clientId;
130
- }
131
- get clientDetails() {
132
- return this.dataStoreContext.clientDetails;
133
- }
134
- get isAttached() {
135
- return this.attachState !== container_definitions_1.AttachState.Detached;
136
- }
137
- get attachState() {
138
- return this._attachState;
139
- }
140
- get absolutePath() {
141
- return (0, runtime_utils_1.generateHandleContextPath)(this.id, this.routeContext);
142
- }
143
- get routeContext() {
144
- return this.dataStoreContext.IFluidHandleContext;
145
- }
146
- get IFluidHandleContext() { return this; }
147
- get rootRoutingContext() { return this; }
148
- get channelsRoutingContext() { return this; }
149
- get objectsRoutingContext() { return this; }
150
- get disposed() { return this._disposed; }
151
191
  dispose() {
152
192
  if (this._disposed) {
153
193
  return;
@@ -164,18 +204,17 @@ class FluidDataStoreRuntime extends common_utils_1.TypedEventEmitter {
164
204
  const parser = runtime_utils_1.RequestParser.create(request);
165
205
  const id = parser.pathParts[0];
166
206
  if (id === "_channels" || id === "_custom") {
167
- return this.request(parser.createSubRequest(1));
207
+ return await this.request(parser.createSubRequest(1));
168
208
  }
169
209
  // Check for a data type reference first
170
- if (this.contextsDeferred.has(id) && parser.isLeaf(1)) {
210
+ const context = this.contexts.get(id);
211
+ if (context !== undefined && parser.isLeaf(1)) {
171
212
  try {
172
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
173
- const value = await this.contextsDeferred.get(id).promise;
174
- const channel = await value.getChannel();
213
+ const channel = await context.getChannel();
175
214
  return { mimeType: "fluid/object", status: 200, value: channel };
176
215
  }
177
216
  catch (error) {
178
- this.logger.sendErrorEvent({ eventName: "GetChannelFailedInRequest" }, error);
217
+ this.mc.logger.sendErrorEvent({ eventName: "GetChannelFailedInRequest" }, error);
179
218
  return (0, runtime_utils_1.createResponseError)(500, `Failed to get Channel: ${error}`, request);
180
219
  }
181
220
  }
@@ -188,37 +227,56 @@ class FluidDataStoreRuntime extends common_utils_1.TypedEventEmitter {
188
227
  }
189
228
  async getChannel(id) {
190
229
  this.verifyNotClosed();
191
- // TODO we don't assume any channels (even root) in the runtime. If you request a channel that doesn't exist
192
- // we will never resolve the promise. May want a flag to getChannel that doesn't wait for the promise if
193
- // it doesn't exist
194
- if (!this.contextsDeferred.has(id)) {
195
- this.contextsDeferred.set(id, new common_utils_1.Deferred());
196
- }
197
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
198
- const context = await this.contextsDeferred.get(id).promise;
199
- const channel = await context.getChannel();
200
- return channel;
230
+ const context = this.contexts.get(id);
231
+ if (context === undefined) {
232
+ throw new telemetry_utils_1.LoggingError("Channel does not exist");
233
+ }
234
+ return context.getChannel();
201
235
  }
202
- createChannel(id = (0, uuid_1.v4)(), type) {
236
+ /**
237
+ * Api which allows caller to create the channel first and then add it to the runtime.
238
+ * The channel type should be present in the registry, otherwise the runtime would reject
239
+ * the channel. Also the runtime used to create the channel object should be same to which
240
+ * it is added.
241
+ * @param channel - channel which needs to be added to the runtime.
242
+ */
243
+ addChannel(channel) {
244
+ const id = channel.id;
203
245
  if (id.includes("/")) {
204
- throw new container_utils_1.UsageError(`Id cannot contain slashes: ${id}`);
246
+ throw new telemetry_utils_1.UsageError(`Id cannot contain slashes: ${id}`);
205
247
  }
206
248
  this.verifyNotClosed();
207
- (0, common_utils_1.assert)(!this.contexts.has(id), 0x179 /* "createChannel() with existing ID" */);
208
- this.notBoundedChannelContextSet.add(id);
209
- const context = new localChannelContext_1.LocalChannelContext(id, this.sharedObjectRegistry, type, this, this.dataStoreContext, this.dataStoreContext.storage, this.logger, (content, localOpMetadata) => this.submitChannelOp(id, content, localOpMetadata), (address) => this.setChannelDirty(address), (srcHandle, outboundHandle) => this.addedGCOutboundReference(srcHandle, outboundHandle));
210
- this.contexts.set(id, context);
211
- if (this.contextsDeferred.has(id)) {
212
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
213
- this.contextsDeferred.get(id).resolve(context);
249
+ (0, core_utils_1.assert)(!this.contexts.has(id), 0x865 /* addChannel() with existing ID */);
250
+ const type = channel.attributes.type;
251
+ const factory = this.sharedObjectRegistry.get(channel.attributes.type);
252
+ if (factory === undefined) {
253
+ throw new Error(`Channel Factory ${type} not registered`);
214
254
  }
215
- else {
216
- const deferred = new common_utils_1.Deferred();
217
- deferred.resolve(context);
218
- this.contextsDeferred.set(id, deferred);
255
+ this.createChannelContext(channel);
256
+ // Channels (DDS) should not be created in summarizer client.
257
+ this.identifyLocalChangeInSummarizer("DDSCreatedInSummarizer", id, type);
258
+ }
259
+ createChannel(id = (0, uuid_1.v4)(), type) {
260
+ if (id.includes("/")) {
261
+ throw new telemetry_utils_1.UsageError(`Id cannot contain slashes: ${id}`);
219
262
  }
220
- (0, common_utils_1.assert)(!!context.channel, 0x17a /* "Channel should be loaded when created!!" */);
221
- return context.channel;
263
+ this.verifyNotClosed();
264
+ (0, core_utils_1.assert)(!this.contexts.has(id), 0x179 /* "createChannel() with existing ID" */);
265
+ (0, core_utils_1.assert)(type !== undefined, 0x209 /* "Factory Type should be defined" */);
266
+ const factory = this.sharedObjectRegistry.get(type);
267
+ if (factory === undefined) {
268
+ throw new Error(`Channel Factory ${type} not registered`);
269
+ }
270
+ const channel = factory.create(this, id);
271
+ this.createChannelContext(channel);
272
+ // Channels (DDS) should not be created in summarizer client.
273
+ this.identifyLocalChangeInSummarizer("DDSCreatedInSummarizer", id, type);
274
+ return channel;
275
+ }
276
+ createChannelContext(channel) {
277
+ this.notBoundedChannelContextSet.add(channel.id);
278
+ const context = new localChannelContext_1.LocalChannelContext(channel, this, this.dataStoreContext, this.dataStoreContext.storage, this.logger, (content, localOpMetadata) => this.submitChannelOp(channel.id, content, localOpMetadata), (address) => this.setChannelDirty(address), (srcHandle, outboundHandle) => this.addedGCOutboundReference(srcHandle, outboundHandle));
279
+ this.contexts.set(channel.id, context);
222
280
  }
223
281
  /**
224
282
  * Binds a channel with the runtime. If the runtime is attached we will attach the channel right away.
@@ -226,7 +284,7 @@ class FluidDataStoreRuntime extends common_utils_1.TypedEventEmitter {
226
284
  * @param channel - channel to be registered.
227
285
  */
228
286
  bindChannel(channel) {
229
- (0, common_utils_1.assert)(this.notBoundedChannelContextSet.has(channel.id), 0x17b /* "Channel to be binded should be in not bounded set" */);
287
+ (0, core_utils_1.assert)(this.notBoundedChannelContextSet.has(channel.id), 0x17b /* "Channel to be bound should be in not bounded set" */);
230
288
  this.notBoundedChannelContextSet.delete(channel.id);
231
289
  // If our data store is attached, then attach the channel.
232
290
  if (this.isAttached) {
@@ -248,11 +306,14 @@ class FluidDataStoreRuntime extends common_utils_1.TypedEventEmitter {
248
306
  }
249
307
  /**
250
308
  * This function is called when a data store becomes root. It does the following:
309
+ *
251
310
  * 1. Marks the data store locally visible in the container.
311
+ *
252
312
  * 2. Attaches the graph of all the handles bound to it.
313
+ *
253
314
  * 3. Calls into the data store context to mark it visible in the container too. If the container is globally
254
- * visible, it will mark us globally visible. Otherwise, it will mark us globally visible when it becomes
255
- * globally visible.
315
+ * visible, it will mark us globally visible. Otherwise, it will mark us globally visible when it becomes
316
+ * globally visible.
256
317
  */
257
318
  makeVisibleAndAttachGraph() {
258
319
  if (this.visibilityState !== runtime_definitions_1.VisibilityState.NotVisible) {
@@ -263,7 +324,7 @@ class FluidDataStoreRuntime extends common_utils_1.TypedEventEmitter {
263
324
  handle.attachGraph();
264
325
  });
265
326
  this.pendingHandlesToMakeVisible.clear();
266
- this.bindToContext();
327
+ this.dataStoreContext.makeLocallyVisible();
267
328
  }
268
329
  /**
269
330
  * This function is called when a handle to this data store is added to a visible DDS.
@@ -271,20 +332,6 @@ class FluidDataStoreRuntime extends common_utils_1.TypedEventEmitter {
271
332
  attachGraph() {
272
333
  this.makeVisibleAndAttachGraph();
273
334
  }
274
- /**
275
- * Binds this runtime to the container
276
- * This includes the following:
277
- * 1. Sending an Attach op that includes all existing state
278
- * 2. Attaching the graph if the data store becomes attached.
279
- */
280
- bindToContext() {
281
- if (this.bindState !== container_definitions_1.BindState.NotBound) {
282
- return;
283
- }
284
- this.bindState = container_definitions_1.BindState.Binding;
285
- this.dataStoreContext.bindToContext();
286
- this.bindState = container_definitions_1.BindState.Bound;
287
- }
288
335
  bind(handle) {
289
336
  // If visible, attach the incoming handle's graph. Else, this will be done when we become visible.
290
337
  if (this.visibilityState !== runtime_definitions_1.VisibilityState.NotVisible) {
@@ -306,9 +353,14 @@ class FluidDataStoreRuntime extends common_utils_1.TypedEventEmitter {
306
353
  getAudience() {
307
354
  return this.audience;
308
355
  }
309
- async uploadBlob(blob) {
356
+ async uploadBlob(blob, signal) {
310
357
  this.verifyNotClosed();
311
- return this.dataStoreContext.uploadBlob(blob);
358
+ return this.dataStoreContext.uploadBlob(blob, signal);
359
+ }
360
+ createRemoteChannelContext(attachMessage, summarizerNodeParams) {
361
+ const flatBlobs = new Map();
362
+ const snapshotTree = (0, driver_utils_1.buildSnapshotTree)(attachMessage.snapshot.entries, flatBlobs);
363
+ return new remoteChannelContext_1.RemoteChannelContext(this, this.dataStoreContext, this.dataStoreContext.storage, (content, localContentMetadata) => this.submitChannelOp(attachMessage.id, content, localContentMetadata), (address) => this.setChannelDirty(address), (srcHandle, outboundHandle) => this.addedGCOutboundReference(srcHandle, outboundHandle), attachMessage.id, snapshotTree, this.sharedObjectRegistry, flatBlobs, this.dataStoreContext.getCreateChildSummarizerNodeFn(attachMessage.id, summarizerNodeParams), attachMessage.type);
312
364
  }
313
365
  process(message, local, localOpMetadata) {
314
366
  this.verifyNotClosed();
@@ -321,28 +373,17 @@ class FluidDataStoreRuntime extends common_utils_1.TypedEventEmitter {
321
373
  // If a non-local operation then go and create the object
322
374
  // Otherwise mark it as officially attached.
323
375
  if (local) {
324
- (0, common_utils_1.assert)(this.pendingAttach.has(id), 0x17c /* "Unexpected attach (local) channel OP" */);
325
- this.pendingAttach.delete(id);
376
+ (0, core_utils_1.assert)(this.pendingAttach.delete(id), 0x17c /* "Unexpected attach (local) channel OP" */);
326
377
  }
327
378
  else {
328
- (0, common_utils_1.assert)(!this.contexts.has(id), 0x17d);
329
- const flatBlobs = new Map();
330
- const snapshotTree = (0, driver_utils_1.buildSnapshotTree)(attachMessage.snapshot.entries, flatBlobs);
331
- const remoteChannelContext = new remoteChannelContext_1.RemoteChannelContext(this, this.dataStoreContext, this.dataStoreContext.storage, (content, localContentMetadata) => this.submitChannelOp(id, content, localContentMetadata), (address) => this.setChannelDirty(address), (srcHandle, outboundHandle) => this.addedGCOutboundReference(srcHandle, outboundHandle), id, snapshotTree, this.sharedObjectRegistry, flatBlobs, this.dataStoreContext.getCreateChildSummarizerNodeFn(id, {
379
+ (0, core_utils_1.assert)(!this.contexts.has(id), 0x17d /* "Unexpected attach channel OP" */);
380
+ const summarizerNodeParams = {
332
381
  type: runtime_definitions_1.CreateSummarizerNodeSource.FromAttach,
333
382
  sequenceNumber: message.sequenceNumber,
334
383
  snapshot: attachMessage.snapshot,
335
- }), async () => this.getChannelBaseGCDetails(id), attachMessage.type);
384
+ };
385
+ const remoteChannelContext = this.createRemoteChannelContext(attachMessage, summarizerNodeParams);
336
386
  this.contexts.set(id, remoteChannelContext);
337
- if (this.contextsDeferred.has(id)) {
338
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
339
- this.contextsDeferred.get(id).resolve(remoteChannelContext);
340
- }
341
- else {
342
- const deferred = new common_utils_1.Deferred();
343
- deferred.resolve(remoteChannelContext);
344
- this.contextsDeferred.set(id, deferred);
345
- }
346
387
  }
347
388
  break;
348
389
  }
@@ -354,7 +395,7 @@ class FluidDataStoreRuntime extends common_utils_1.TypedEventEmitter {
354
395
  this.emit("op", message);
355
396
  }
356
397
  catch (error) {
357
- throw container_utils_1.DataProcessingError.wrapIfUnrecognized(error, "fluidDataStoreRuntimeFailedToProcessMessage", message);
398
+ throw telemetry_utils_1.DataProcessingError.wrapIfUnrecognized(error, "fluidDataStoreRuntimeFailedToProcessMessage", message);
358
399
  }
359
400
  }
360
401
  processSignal(message, local) {
@@ -364,13 +405,13 @@ class FluidDataStoreRuntime extends common_utils_1.TypedEventEmitter {
364
405
  return (
365
406
  // Added in createChannel
366
407
  // Removed when bindChannel is called
367
- !this.notBoundedChannelContextSet.has(id)
408
+ !this.notBoundedChannelContextSet.has(id) &&
368
409
  // Added in bindChannel only if this is not attached yet
369
410
  // Removed when this is attached by calling attachGraph
370
- && !this.localChannelContextQueue.has(id)
411
+ !this.localChannelContextQueue.has(id) &&
371
412
  // Added in attachChannel called by bindChannel
372
413
  // Removed when attach op is broadcast
373
- && !this.pendingAttach.has(id));
414
+ !this.pendingAttach.has(id));
374
415
  }
375
416
  /**
376
417
  * Returns the outbound routes of this channel. Currently, all contexts in this channel are considered
@@ -401,22 +442,27 @@ class FluidDataStoreRuntime extends common_utils_1.TypedEventEmitter {
401
442
  * Generates data used for garbage collection. This includes a list of GC nodes that represent this channel
402
443
  * including any of its child channel contexts. Each node has a set of outbound routes to other GC nodes in the
403
444
  * document. It does the following:
445
+ *
404
446
  * 1. Calls into each child context to get its GC data.
447
+ *
405
448
  * 2. Prefixes the child context's id to the GC nodes in the child's GC data. This makes sure that the node can be
406
- * identified as belonging to the child.
449
+ * identified as belonging to the child.
450
+ *
407
451
  * 3. Adds a GC node for this channel to the nodes received from the children. All these nodes together represent
408
- * the GC data of this channel.
452
+ * the GC data of this channel.
453
+ *
409
454
  * @param fullGC - true to bypass optimizations and force full generation of GC data.
410
455
  */
411
456
  async getGCData(fullGC = false) {
412
- const builder = new garbage_collector_1.GCDataBuilder();
457
+ const builder = new runtime_utils_1.GCDataBuilder();
413
458
  // Iterate over each channel context and get their GC data.
414
459
  await Promise.all(Array.from(this.contexts)
415
460
  .filter(([contextId, _]) => {
416
461
  // Get GC data only for attached contexts. Detached contexts are not connected in the GC reference
417
462
  // graph so any references they might have won't be connected as well.
418
463
  return this.isChannelAttached(contextId);
419
- }).map(async ([contextId, context]) => {
464
+ })
465
+ .map(async ([contextId, context]) => {
420
466
  const contextGCData = await context.getGCData(fullGC);
421
467
  // Prefix the child's id to the ids of its GC nodes so they can be identified as belonging to the child.
422
468
  // This also gradually builds the id of each node to be a path from the root.
@@ -429,20 +475,17 @@ class FluidDataStoreRuntime extends common_utils_1.TypedEventEmitter {
429
475
  * After GC has run, called to notify this channel of routes that are used in it. It calls the child contexts to
430
476
  * update their used routes.
431
477
  * @param usedRoutes - The routes that are used in all contexts in this channel.
432
- * @param gcTimestamp - The time when GC was run that generated these used routes. If any node becomes unreferenced
433
- * as part of this GC run, this should be used to update the time when it happens.
434
478
  */
435
- updateUsedRoutes(usedRoutes, gcTimestamp) {
436
- var _a;
479
+ updateUsedRoutes(usedRoutes) {
437
480
  // Get a map of channel ids to routes used in it.
438
- const usedContextRoutes = (0, garbage_collector_1.unpackChildNodesUsedRoutes)(usedRoutes);
481
+ const usedContextRoutes = (0, runtime_utils_1.unpackChildNodesUsedRoutes)(usedRoutes);
439
482
  // Verify that the used routes are correct.
440
483
  for (const [id] of usedContextRoutes) {
441
- (0, common_utils_1.assert)(this.contexts.has(id), 0x17e /* "Used route does not belong to any known context" */);
484
+ (0, core_utils_1.assert)(this.contexts.has(id), 0x17e /* "Used route does not belong to any known context" */);
442
485
  }
443
486
  // Update the used routes in each context. Used routes is empty for unused context.
444
487
  for (const [contextId, context] of this.contexts) {
445
- context.updateUsedRoutes((_a = usedContextRoutes.get(contextId)) !== null && _a !== void 0 ? _a : [], gcTimestamp);
488
+ context.updateUsedRoutes(usedContextRoutes.get(contextId) ?? []);
446
489
  }
447
490
  }
448
491
  /**
@@ -452,31 +495,7 @@ class FluidDataStoreRuntime extends common_utils_1.TypedEventEmitter {
452
495
  * @param outboundHandle - The handle of the outbound node that is referenced.
453
496
  */
454
497
  addedGCOutboundReference(srcHandle, outboundHandle) {
455
- var _a, _b;
456
- (_b = (_a = this.dataStoreContext).addedGCOutboundReference) === null || _b === void 0 ? void 0 : _b.call(_a, srcHandle, outboundHandle);
457
- }
458
- /**
459
- * Returns the base GC details for the channel with the given id. This is used to initialize its GC state.
460
- * @param channelId - The id of the channel context that is asked for the initial GC details.
461
- * @returns the requested channel's base GC details.
462
- */
463
- async getChannelBaseGCDetails(channelId) {
464
- var _a;
465
- let channelBaseGCDetails = (await this.channelsBaseGCDetails).get(channelId);
466
- if (channelBaseGCDetails === undefined) {
467
- channelBaseGCDetails = {};
468
- }
469
- else if (((_a = channelBaseGCDetails.gcData) === null || _a === void 0 ? void 0 : _a.gcNodes) !== undefined) {
470
- // Note: if the child channel has an explicit handle route to its parent, it will be removed here and
471
- // expected to be added back by the parent when getGCData is called.
472
- (0, garbage_collector_1.removeRouteFromAllNodes)(channelBaseGCDetails.gcData.gcNodes, this.absolutePath);
473
- }
474
- // Currently, channel context's are always considered used. So, it there are no used routes for it, we still
475
- // need to mark it as used. Add self-route (empty string) to the channel context's used routes.
476
- if (channelBaseGCDetails.usedRoutes === undefined || channelBaseGCDetails.usedRoutes.length === 0) {
477
- channelBaseGCDetails.usedRoutes = [""];
478
- }
479
- return channelBaseGCDetails;
498
+ this.dataStoreContext.addedGCOutboundReference?.(srcHandle, outboundHandle);
480
499
  }
481
500
  /**
482
501
  * Returns a summary at the current sequence number.
@@ -491,11 +510,12 @@ class FluidDataStoreRuntime extends common_utils_1.TypedEventEmitter {
491
510
  .filter(([contextId, _]) => {
492
511
  const isAttached = this.isChannelAttached(contextId);
493
512
  // We are not expecting local dds! Summary may not capture local state.
494
- (0, common_utils_1.assert)(isAttached, 0x17f /* "Not expecting detached channels during summarize" */);
513
+ (0, core_utils_1.assert)(isAttached, 0x17f /* "Not expecting detached channels during summarize" */);
495
514
  // If the object is registered - and we have received the sequenced op creating the object
496
515
  // (i.e. it has a base mapping) - then we go ahead and summarize
497
516
  return isAttached;
498
- }).map(async ([contextId, context]) => {
517
+ })
518
+ .map(async ([contextId, context]) => {
499
519
  const contextSummary = await context.summarize(fullTree, trackState, telemetryContext);
500
520
  summaryBuilder.addWithStats(contextId, contextSummary);
501
521
  }));
@@ -506,20 +526,20 @@ class FluidDataStoreRuntime extends common_utils_1.TypedEventEmitter {
506
526
  * back-compat 0.59.1000 - getAttachSummary() is called when making a data store globally visible (previously
507
527
  * attaching state). Ideally, attachGraph() should have already be called making it locally visible. However,
508
528
  * before visibility state was added, this may not have been the case and getAttachSummary() could be called:
509
- * 1) Before attaching the data store - When a detached container is attached.
510
- * 2) After attaching the data store - When a data store is created and bound in an attached container.
529
+ *
530
+ * 1. Before attaching the data store - When a detached container is attached.
531
+ *
532
+ * 2. After attaching the data store - When a data store is created and bound in an attached container.
511
533
  *
512
534
  * The basic idea is that all local object should become locally visible before they are globally visible.
513
535
  */
514
536
  this.attachGraph();
515
- /**
516
- * This assert cannot be added now due to back-compat. To be uncommented when the following issue is fixed -
517
- * https://github.com/microsoft/FluidFramework/issues/9688.
518
- *
519
- * assert(this.visibilityState === VisibilityState.LocallyVisible,
520
- * "The data store should be locally visible when generating attach summary",
521
- * );
522
- */
537
+ // This assert cannot be added now due to back-compat. To be uncommented when the following issue is fixed -
538
+ // https://github.com/microsoft/FluidFramework/issues/9688.
539
+ //
540
+ // assert(this.visibilityState === VisibilityState.LocallyVisible,
541
+ // "The data store should be locally visible when generating attach summary",
542
+ // );
523
543
  const summaryBuilder = new runtime_utils_1.SummaryTreeBuilder();
524
544
  // Craft the .attributes file for each shared object
525
545
  for (const [contextId, context] of this.contexts) {
@@ -530,13 +550,13 @@ class FluidDataStoreRuntime extends common_utils_1.TypedEventEmitter {
530
550
  let summaryTree;
531
551
  if (context.isLoaded) {
532
552
  const contextSummary = context.getAttachSummary(telemetryContext);
533
- (0, common_utils_1.assert)(contextSummary.summary.type === protocol_definitions_1.SummaryType.Tree, 0x180 /* "getAttachSummary should always return a tree" */);
553
+ (0, core_utils_1.assert)(contextSummary.summary.type === protocol_definitions_1.SummaryType.Tree, 0x180 /* "getAttachSummary should always return a tree" */);
534
554
  summaryTree = { stats: contextSummary.stats, summary: contextSummary.summary };
535
555
  }
536
556
  else {
537
557
  // If this channel is not yet loaded, then there should be no changes in the snapshot from which
538
558
  // it was created as it is detached container. So just use the previous snapshot.
539
- (0, common_utils_1.assert)(!!this.dataStoreContext.baseSnapshot, 0x181 /* "BaseSnapshot should be there as detached container loaded from snapshot" */);
559
+ (0, core_utils_1.assert)(!!this.dataStoreContext.baseSnapshot, 0x181 /* "BaseSnapshot should be there as detached container loaded from snapshot" */);
540
560
  summaryTree = (0, runtime_utils_1.convertSnapshotTreeToSummaryTree)(this.dataStoreContext.baseSnapshot.trees[contextId]);
541
561
  }
542
562
  summaryBuilder.addWithStats(contextId, summaryTree);
@@ -547,9 +567,15 @@ class FluidDataStoreRuntime extends common_utils_1.TypedEventEmitter {
547
567
  submitMessage(type, content, localOpMetadata) {
548
568
  this.submit(type, content, localOpMetadata);
549
569
  }
550
- submitSignal(type, content) {
570
+ /**
571
+ * Submits the signal to be sent to other clients.
572
+ * @param type - Type of the signal.
573
+ * @param content - Content of the signal.
574
+ * @param targetClientId - When specified, the signal is only sent to the provided client id.
575
+ */
576
+ submitSignal(type, content, targetClientId) {
551
577
  this.verifyNotClosed();
552
- return this.dataStoreContext.submitSignal(type, content);
578
+ return this.dataStoreContext.submitSignal(type, content, targetClientId);
553
579
  }
554
580
  /**
555
581
  * Will return when the data store is attached.
@@ -567,8 +593,8 @@ class FluidDataStoreRuntime extends common_utils_1.TypedEventEmitter {
567
593
  return;
568
594
  }
569
595
  channel.handle.attachGraph();
570
- (0, common_utils_1.assert)(this.isAttached, 0x182 /* "Data store should be attached to attach the channel." */);
571
- (0, common_utils_1.assert)(this.visibilityState === runtime_definitions_1.VisibilityState.GloballyVisible, 0x2d0 /* "Data store should be globally visible to attach channels." */);
596
+ (0, core_utils_1.assert)(this.isAttached, 0x182 /* "Data store should be attached to attach the channel." */);
597
+ (0, core_utils_1.assert)(this.visibilityState === runtime_definitions_1.VisibilityState.GloballyVisible, 0x2d0 /* "Data store should be globally visible to attach channels." */);
572
598
  const summarizeResult = (0, channelContext_1.summarizeChannel)(channel, true /* fullTree */, false /* trackState */);
573
599
  // Attach message needs the summary in ITree format. Convert the ISummaryTree into an ITree.
574
600
  const snapshot = (0, runtime_utils_1.convertSummaryTreeToITree)(summarizeResult.summary);
@@ -577,7 +603,7 @@ class FluidDataStoreRuntime extends common_utils_1.TypedEventEmitter {
577
603
  snapshot,
578
604
  type: channel.attributes.type,
579
605
  };
580
- this.pendingAttach.set(channel.id, message);
606
+ this.pendingAttach.add(channel.id);
581
607
  this.submit(DataStoreMessageType.Attach, message);
582
608
  const context = this.contexts.get(channel.id);
583
609
  context.makeVisible();
@@ -600,21 +626,20 @@ class FluidDataStoreRuntime extends common_utils_1.TypedEventEmitter {
600
626
  reSubmit(type, content, localOpMetadata) {
601
627
  this.verifyNotClosed();
602
628
  switch (type) {
603
- case DataStoreMessageType.ChannelOp:
604
- {
605
- // For Operations, find the right channel and trigger resubmission on it.
606
- const envelope = content;
607
- const channelContext = this.contexts.get(envelope.address);
608
- (0, common_utils_1.assert)(!!channelContext, 0x183 /* "There should be a channel context for the op" */);
609
- channelContext.reSubmit(envelope.contents, localOpMetadata);
610
- break;
611
- }
629
+ case DataStoreMessageType.ChannelOp: {
630
+ // For Operations, find the right channel and trigger resubmission on it.
631
+ const envelope = content;
632
+ const channelContext = this.contexts.get(envelope.address);
633
+ (0, core_utils_1.assert)(!!channelContext, 0x183 /* "There should be a channel context for the op" */);
634
+ channelContext.reSubmit(envelope.contents, localOpMetadata);
635
+ break;
636
+ }
612
637
  case DataStoreMessageType.Attach:
613
638
  // For Attach messages, just submit them again.
614
639
  this.submit(type, content, localOpMetadata);
615
640
  break;
616
641
  default:
617
- (0, common_utils_1.unreachableCase)(type);
642
+ (0, core_utils_1.unreachableCase)(type);
618
643
  }
619
644
  }
620
645
  /**
@@ -625,25 +650,42 @@ class FluidDataStoreRuntime extends common_utils_1.TypedEventEmitter {
625
650
  rollback(type, content, localOpMetadata) {
626
651
  this.verifyNotClosed();
627
652
  switch (type) {
628
- case DataStoreMessageType.ChannelOp:
629
- {
630
- // For Operations, find the right channel and trigger resubmission on it.
631
- const envelope = content;
632
- const channelContext = this.contexts.get(envelope.address);
633
- (0, common_utils_1.assert)(!!channelContext, 0x2ed /* "There should be a channel context for the op" */);
634
- channelContext.rollback(envelope.contents, localOpMetadata);
635
- break;
636
- }
653
+ case DataStoreMessageType.ChannelOp: {
654
+ // For Operations, find the right channel and trigger resubmission on it.
655
+ const envelope = content;
656
+ const channelContext = this.contexts.get(envelope.address);
657
+ (0, core_utils_1.assert)(!!channelContext, 0x2ed /* "There should be a channel context for the op" */);
658
+ channelContext.rollback(envelope.contents, localOpMetadata);
659
+ break;
660
+ }
637
661
  default:
638
662
  throw new telemetry_utils_1.LoggingError(`Can't rollback ${type} message`);
639
663
  }
640
664
  }
641
665
  async applyStashedOp(content) {
642
- const envelope = content;
643
- const channelContext = this.contexts.get(envelope.address);
644
- (0, common_utils_1.assert)(!!channelContext, 0x184 /* "There should be a channel context for the op" */);
645
- await channelContext.getChannel();
646
- return channelContext.applyStashedOp(envelope.contents);
666
+ const type = content?.type;
667
+ switch (type) {
668
+ case DataStoreMessageType.Attach: {
669
+ const attachMessage = content.content;
670
+ // local means this node will throw if summarized; this is fine because only interactive clients will have stashed ops
671
+ const summarizerNodeParams = {
672
+ type: runtime_definitions_1.CreateSummarizerNodeSource.Local,
673
+ };
674
+ const context = this.createRemoteChannelContext(attachMessage, summarizerNodeParams);
675
+ this.pendingAttach.add(attachMessage.id);
676
+ this.contexts.set(attachMessage.id, context);
677
+ return;
678
+ }
679
+ case DataStoreMessageType.ChannelOp: {
680
+ const envelope = content.content;
681
+ const channelContext = this.contexts.get(envelope.address);
682
+ (0, core_utils_1.assert)(!!channelContext, 0x184 /* "There should be a channel context for the op" */);
683
+ await channelContext.getChannel();
684
+ return channelContext.applyStashedOp(envelope.contents);
685
+ }
686
+ default:
687
+ (0, core_utils_1.unreachableCase)(type);
688
+ }
647
689
  }
648
690
  setChannelDirty(address) {
649
691
  this.verifyNotClosed();
@@ -652,9 +694,12 @@ class FluidDataStoreRuntime extends common_utils_1.TypedEventEmitter {
652
694
  processChannelOp(message, local, localOpMetadata) {
653
695
  this.verifyNotClosed();
654
696
  const envelope = message.contents;
655
- const transformed = Object.assign(Object.assign({}, message), { contents: envelope.contents });
697
+ const transformed = {
698
+ ...message,
699
+ contents: envelope.contents,
700
+ };
656
701
  const channelContext = this.contexts.get(envelope.address);
657
- (0, common_utils_1.assert)(!!channelContext, 0x185 /* "Channel not found" */);
702
+ (0, core_utils_1.assert)(!!channelContext, 0x185 /* "Channel not found" */);
658
703
  channelContext.processOp(transformed, local, localOpMetadata);
659
704
  return channelContext;
660
705
  }
@@ -672,7 +717,7 @@ class FluidDataStoreRuntime extends common_utils_1.TypedEventEmitter {
672
717
  */
673
718
  this.attachGraph();
674
719
  this._attachState = container_definitions_1.AttachState.Attaching;
675
- (0, common_utils_1.assert)(this.visibilityState === runtime_definitions_1.VisibilityState.LocallyVisible, 0x2d1 /* "Data store should be locally visible before it can become globally visible." */);
720
+ (0, core_utils_1.assert)(this.visibilityState === runtime_definitions_1.VisibilityState.LocallyVisible, 0x2d1 /* "Data store should be locally visible before it can become globally visible." */);
676
721
  // Mark the data store globally visible and make its child channels visible as well.
677
722
  this.visibilityState = runtime_definitions_1.VisibilityState.GloballyVisible;
678
723
  this.localChannelContextQueue.forEach((channel) => {
@@ -684,7 +729,7 @@ class FluidDataStoreRuntime extends common_utils_1.TypedEventEmitter {
684
729
  this.emit("attaching");
685
730
  });
686
731
  this.dataStoreContext.once("attached", () => {
687
- (0, common_utils_1.assert)(this.visibilityState === runtime_definitions_1.VisibilityState.GloballyVisible, 0x2d2 /* "Data store should be globally visible when its attached." */);
732
+ (0, core_utils_1.assert)(this.visibilityState === runtime_definitions_1.VisibilityState.GloballyVisible, 0x2d2 /* "Data store should be globally visible when its attached." */);
688
733
  this._attachState = container_definitions_1.AttachState.Attached;
689
734
  this.emit("attached");
690
735
  });
@@ -694,6 +739,30 @@ class FluidDataStoreRuntime extends common_utils_1.TypedEventEmitter {
694
739
  throw new telemetry_utils_1.LoggingError("Runtime is closed");
695
740
  }
696
741
  }
742
+ /**
743
+ * Summarizer client should not have local changes. These changes can become part of the summary and can break
744
+ * eventual consistency. For example, the next summary (say at ref seq# 100) may contain these changes whereas
745
+ * other clients that are up-to-date till seq# 100 may not have them yet.
746
+ */
747
+ identifyLocalChangeInSummarizer(eventName, channelId, channelType) {
748
+ if (this.clientDetails.type !== "summarizer" || this.localChangesTelemetryCount <= 0) {
749
+ return;
750
+ }
751
+ // Log a telemetry if there are local changes in the summarizer. This will give us data on how often
752
+ // this is happening and which data stores do this. The eventual goal is to disallow local changes
753
+ // in the summarizer and the data will help us plan this.
754
+ this.mc.logger.sendTelemetryEvent({
755
+ eventName,
756
+ ...(0, telemetry_utils_1.tagCodeArtifacts)({
757
+ channelType,
758
+ channelId,
759
+ fluidDataStoreId: this.id,
760
+ fluidDataStorePackagePath: this.dataStoreContext.packagePath.join("/"),
761
+ }),
762
+ stack: (0, telemetry_utils_1.generateStack)(),
763
+ });
764
+ this.localChangesTelemetryCount--;
765
+ }
697
766
  }
698
767
  exports.FluidDataStoreRuntime = FluidDataStoreRuntime;
699
768
  /**
@@ -701,6 +770,7 @@ exports.FluidDataStoreRuntime = FluidDataStoreRuntime;
701
770
  * Request handler is only called when data store can't resolve request, i.e. for custom requests.
702
771
  * @param Base - base class, inherits from FluidDataStoreRuntime
703
772
  * @param requestHandler - request handler to mix in
773
+ * @internal
704
774
  */
705
775
  const mixinRequestHandler = (requestHandler, Base = FluidDataStoreRuntime) => class RuntimeWithRequestHandler extends Base {
706
776
  async request(request) {
@@ -717,6 +787,7 @@ exports.mixinRequestHandler = mixinRequestHandler;
717
787
  * @param handler - handler that returns info about blob to be added to summary.
718
788
  * Or undefined not to add anything to summary.
719
789
  * @param Base - base class, inherits from FluidDataStoreRuntime
790
+ * @alpha
720
791
  */
721
792
  const mixinSummaryHandler = (handler, Base = FluidDataStoreRuntime) => class RuntimeWithSummarizerHandler extends Base {
722
793
  addBlob(summary, path, content) {
@@ -741,12 +812,18 @@ const mixinSummaryHandler = (handler, Base = FluidDataStoreRuntime) => class Run
741
812
  }
742
813
  async summarize(...args) {
743
814
  const summary = await super.summarize(...args);
744
- const content = await handler(this);
745
- if (content !== undefined) {
746
- this.addBlob(summary, content.path, content.content);
815
+ try {
816
+ const content = await handler(this);
817
+ if (content !== undefined) {
818
+ this.addBlob(summary, content.path, content.content);
819
+ }
820
+ }
821
+ catch (e) {
822
+ // Any error coming from app-provided handler should be marked as DataProcessingError
823
+ throw telemetry_utils_1.DataProcessingError.wrapIfUnrecognized(e, "mixinSummaryHandler");
747
824
  }
748
825
  return summary;
749
826
  }
750
827
  };
751
828
  exports.mixinSummaryHandler = mixinSummaryHandler;
752
- //# sourceMappingURL=dataStoreRuntime.js.map
829
+ //# sourceMappingURL=dataStoreRuntime.cjs.map