@fluidframework/cell 2.0.0-internal.2.1.2 → 2.0.0-internal.2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +1 -1
- package/dist/cell.d.ts +14 -0
- package/dist/cell.d.ts.map +1 -1
- package/dist/cell.js +59 -13
- package/dist/cell.js.map +1 -1
- package/dist/interfaces.d.ts +4 -0
- package/dist/interfaces.d.ts.map +1 -1
- package/dist/interfaces.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/lib/cell.d.ts +14 -0
- package/lib/cell.d.ts.map +1 -1
- package/lib/cell.js +59 -13
- package/lib/cell.js.map +1 -1
- package/lib/interfaces.d.ts +4 -0
- package/lib/interfaces.d.ts.map +1 -1
- package/lib/interfaces.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/package.json +19 -14
- package/prettier.config.cjs +8 -0
- package/src/cell.ts +70 -17
- package/src/interfaces.ts +4 -0
- package/src/packageVersion.ts +1 -1
package/.eslintrc.js
CHANGED
package/dist/cell.d.ts
CHANGED
|
@@ -83,6 +83,7 @@ export declare class SharedCell<T = any> extends SharedObject<ISharedCellEvents<
|
|
|
83
83
|
* we a message is ack'd with it's messageId.
|
|
84
84
|
*/
|
|
85
85
|
private messageIdObserved;
|
|
86
|
+
private readonly pendingMessageIds;
|
|
86
87
|
/**
|
|
87
88
|
* Constructs a new shared cell. If the object is non-local an id and service interfaces will
|
|
88
89
|
* be provided
|
|
@@ -142,10 +143,23 @@ export declare class SharedCell<T = any> extends SharedObject<ISharedCellEvents<
|
|
|
142
143
|
private setCore;
|
|
143
144
|
private deleteCore;
|
|
144
145
|
private decode;
|
|
146
|
+
private createLocalOpMetadata;
|
|
145
147
|
/**
|
|
146
148
|
* {@inheritDoc @fluidframework/shared-object-base#SharedObjectCore.applyStashedOp}
|
|
147
149
|
* @internal
|
|
148
150
|
*/
|
|
149
151
|
protected applyStashedOp(content: unknown): unknown;
|
|
152
|
+
/**
|
|
153
|
+
* Rollback a local op
|
|
154
|
+
* @param content - The operation to rollback
|
|
155
|
+
* @param localOpMetadata - The local metadata associated with the op.
|
|
156
|
+
*/
|
|
157
|
+
protected rollback(content: any, localOpMetadata: unknown): void;
|
|
158
|
+
/**
|
|
159
|
+
* Submit a cell message to remote clients.
|
|
160
|
+
* @param op - The cell message
|
|
161
|
+
* @param previousValue - The value of the cell before this op
|
|
162
|
+
*/
|
|
163
|
+
private submitCellMessage;
|
|
150
164
|
}
|
|
151
165
|
//# sourceMappingURL=cell.d.ts.map
|
package/dist/cell.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cell.d.ts","sourceRoot":"","sources":["../src/cell.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,yBAAyB,EAAe,MAAM,sCAAsC,CAAC;AAC9F,OAAO,EACH,kBAAkB,EAClB,sBAAsB,EACtB,sBAAsB,EACtB,eAAe,EACf,YAAY,EACf,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EAAE,qBAAqB,EAAE,MAAM,qCAAqC,CAAC;AAE5E,OAAO,EAA2B,gBAAgB,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAE7G,OAAO,
|
|
1
|
+
{"version":3,"file":"cell.d.ts","sourceRoot":"","sources":["../src/cell.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,yBAAyB,EAAe,MAAM,sCAAsC,CAAC;AAC9F,OAAO,EACH,kBAAkB,EAClB,sBAAsB,EACtB,sBAAsB,EACtB,eAAe,EACf,YAAY,EACf,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EAAE,qBAAqB,EAAE,MAAM,qCAAqC,CAAC;AAE5E,OAAO,EAA2B,gBAAgB,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAE7G,OAAO,EACH,WAAW,EACX,iBAAiB,EAEpB,MAAM,cAAc,CAAC;AAuBtB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AACH,qBAAa,UAAU,CAAC,CAAC,GAAG,GAAG,CAAE,SAAQ,YAAY,CAAC,iBAAiB,CAAC,CAAC,CAAC,CACtE,YAAW,WAAW,CAAC,CAAC,CAAC;IACzB;;;;;;OAMG;WACW,MAAM,CAAC,OAAO,EAAE,sBAAsB,EAAE,EAAE,CAAC,EAAE,MAAM;IAIjE;;;;OAIG;WACW,UAAU,IAAI,eAAe;IAG3C;;OAEG;IACH,OAAO,CAAC,IAAI,CAA8B;IAE1C;;;OAGG;IACH,OAAO,CAAC,SAAS,CAAc;IAE/B;;;OAGG;IACH,OAAO,CAAC,iBAAiB,CAAc;IAEvC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAgB;IAElD;;;;;;OAMG;gBACS,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,sBAAsB,EAAE,UAAU,EAAE,kBAAkB;IAIvF;;OAEG;IACI,GAAG,IAAI,YAAY,CAAC,CAAC,CAAC,GAAG,SAAS;IAIzC;;OAEG;IACI,GAAG,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC;IAqBjC;;OAEG;IACI,MAAM;IAeb;;OAEG;IACI,KAAK;IAIZ;;;;OAIG;IACH,SAAS,CAAC,aAAa,CAAC,UAAU,EAAE,gBAAgB,GAAG,qBAAqB;IAK5E;;OAEG;cACa,QAAQ,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC;IAMxE;;OAEG;IACH,SAAS,CAAC,mBAAmB;IAI7B;;OAEG;IACH,SAAS,CAAC,YAAY;IAEtB;;;OAGG;IACH,OAAO,CAAC,YAAY;IAapB;;;;;;;OAOG;IACH,SAAS,CAAC,WAAW,CAAC,OAAO,EAAE,yBAAyB,EAAE,KAAK,EAAE,OAAO,EAAE,eAAe,EAAE,OAAO;IAwBlG,OAAO,CAAC,OAAO;IAOf,OAAO,CAAC,UAAU;IAOlB,OAAO,CAAC,MAAM;IAMd,OAAO,CAAC,qBAAqB;IAS7B;;;OAGG;IACH,SAAS,CAAC,cAAc,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO;IAMnD;;;;OAIG;IACH,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,GAAG,EAAE,eAAe,EAAE,OAAO;IAkBxD;;;;MAIE;IACH,OAAO,CAAC,iBAAiB;CAI5B"}
|
package/dist/cell.js
CHANGED
|
@@ -78,6 +78,7 @@ class SharedCell extends shared_object_base_1.SharedObject {
|
|
|
78
78
|
* we a message is ack'd with it's messageId.
|
|
79
79
|
*/
|
|
80
80
|
this.messageIdObserved = -1;
|
|
81
|
+
this.pendingMessageIds = [];
|
|
81
82
|
}
|
|
82
83
|
/**
|
|
83
84
|
* Create a new shared cell
|
|
@@ -112,7 +113,7 @@ class SharedCell extends shared_object_base_1.SharedObject {
|
|
|
112
113
|
value: this.serializer.encode(value, this.handle),
|
|
113
114
|
};
|
|
114
115
|
// Set the value locally.
|
|
115
|
-
this.setCore(value);
|
|
116
|
+
const previousValue = this.setCore(value);
|
|
116
117
|
// If we are not attached, don't submit the op.
|
|
117
118
|
if (!this.isAttached()) {
|
|
118
119
|
return;
|
|
@@ -121,14 +122,14 @@ class SharedCell extends shared_object_base_1.SharedObject {
|
|
|
121
122
|
type: "setCell",
|
|
122
123
|
value: operationValue,
|
|
123
124
|
};
|
|
124
|
-
this.
|
|
125
|
+
this.submitCellMessage(op, previousValue);
|
|
125
126
|
}
|
|
126
127
|
/**
|
|
127
128
|
* {@inheritDoc ISharedCell.delete}
|
|
128
129
|
*/
|
|
129
130
|
delete() {
|
|
130
131
|
// Delete the value locally.
|
|
131
|
-
this.deleteCore();
|
|
132
|
+
const previousValue = this.deleteCore();
|
|
132
133
|
// If we are not attached, don't submit the op.
|
|
133
134
|
if (!this.isAttached()) {
|
|
134
135
|
return;
|
|
@@ -136,7 +137,7 @@ class SharedCell extends shared_object_base_1.SharedObject {
|
|
|
136
137
|
const op = {
|
|
137
138
|
type: "deleteCell",
|
|
138
139
|
};
|
|
139
|
-
this.
|
|
140
|
+
this.submitCellMessage(op, previousValue);
|
|
140
141
|
}
|
|
141
142
|
/**
|
|
142
143
|
* {@inheritDoc ISharedCell.empty}
|
|
@@ -177,11 +178,9 @@ class SharedCell extends shared_object_base_1.SharedObject {
|
|
|
177
178
|
applyInnerOp(content) {
|
|
178
179
|
switch (content.type) {
|
|
179
180
|
case "setCell":
|
|
180
|
-
this.setCore(this.decode(content.value));
|
|
181
|
-
break;
|
|
181
|
+
return this.setCore(this.decode(content.value));
|
|
182
182
|
case "deleteCell":
|
|
183
|
-
this.deleteCore();
|
|
184
|
-
break;
|
|
183
|
+
return this.deleteCore();
|
|
185
184
|
default:
|
|
186
185
|
throw new Error("Unknown operation");
|
|
187
186
|
}
|
|
@@ -195,13 +194,17 @@ class SharedCell extends shared_object_base_1.SharedObject {
|
|
|
195
194
|
* For messages from a remote client, this will be undefined.
|
|
196
195
|
*/
|
|
197
196
|
processCore(message, local, localOpMetadata) {
|
|
197
|
+
const cellOpMetadata = localOpMetadata;
|
|
198
198
|
if (this.messageId !== this.messageIdObserved) {
|
|
199
199
|
// We are waiting for an ACK on our change to this cell - we will ignore all messages until we get it.
|
|
200
200
|
if (local) {
|
|
201
|
-
const messageIdReceived =
|
|
201
|
+
const messageIdReceived = cellOpMetadata.pendingMessageId;
|
|
202
202
|
(0, common_utils_1.assert)(messageIdReceived !== undefined && messageIdReceived <= this.messageId, 0x00c /* "messageId is incorrect from from the local client's ACK" */);
|
|
203
|
+
(0, common_utils_1.assert)(this.pendingMessageIds !== undefined &&
|
|
204
|
+
this.pendingMessageIds[0] === cellOpMetadata.pendingMessageId, 0x471 /* Unexpected pending message received */);
|
|
205
|
+
this.pendingMessageIds.shift();
|
|
203
206
|
// We got an ACK. Update messageIdObserved.
|
|
204
|
-
this.messageIdObserved =
|
|
207
|
+
this.messageIdObserved = cellOpMetadata.pendingMessageId;
|
|
205
208
|
}
|
|
206
209
|
return;
|
|
207
210
|
}
|
|
@@ -211,27 +214,70 @@ class SharedCell extends shared_object_base_1.SharedObject {
|
|
|
211
214
|
}
|
|
212
215
|
}
|
|
213
216
|
setCore(value) {
|
|
217
|
+
const previousLocalValue = this.get();
|
|
214
218
|
this.data = value;
|
|
215
219
|
this.emit("valueChanged", value);
|
|
220
|
+
return previousLocalValue;
|
|
216
221
|
}
|
|
217
222
|
deleteCore() {
|
|
223
|
+
const previousLocalValue = this.get();
|
|
218
224
|
this.data = undefined;
|
|
219
225
|
this.emit("delete");
|
|
226
|
+
return previousLocalValue;
|
|
220
227
|
}
|
|
221
228
|
decode(cellValue) {
|
|
222
229
|
const value = cellValue.value;
|
|
223
230
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
224
231
|
return this.serializer.decode(value);
|
|
225
232
|
}
|
|
233
|
+
createLocalOpMetadata(op, previousValue) {
|
|
234
|
+
const pendingMessageId = ++this.messageId;
|
|
235
|
+
this.pendingMessageIds.push(pendingMessageId);
|
|
236
|
+
const localMetadata = {
|
|
237
|
+
pendingMessageId, previousValue,
|
|
238
|
+
};
|
|
239
|
+
return localMetadata;
|
|
240
|
+
}
|
|
226
241
|
/**
|
|
227
242
|
* {@inheritDoc @fluidframework/shared-object-base#SharedObjectCore.applyStashedOp}
|
|
228
243
|
* @internal
|
|
229
244
|
*/
|
|
230
245
|
applyStashedOp(content) {
|
|
231
246
|
const cellContent = content;
|
|
232
|
-
this.applyInnerOp(cellContent);
|
|
233
|
-
|
|
234
|
-
|
|
247
|
+
const previousValue = this.applyInnerOp(cellContent);
|
|
248
|
+
return this.createLocalOpMetadata(cellContent, previousValue);
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Rollback a local op
|
|
252
|
+
* @param content - The operation to rollback
|
|
253
|
+
* @param localOpMetadata - The local metadata associated with the op.
|
|
254
|
+
*/
|
|
255
|
+
rollback(content, localOpMetadata) {
|
|
256
|
+
const cellOpMetadata = localOpMetadata;
|
|
257
|
+
if (content.type === "setCell" || content.type === "deleteCell") {
|
|
258
|
+
if (cellOpMetadata.previousValue === undefined) {
|
|
259
|
+
this.deleteCore();
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
262
|
+
this.setCore(cellOpMetadata.previousValue);
|
|
263
|
+
}
|
|
264
|
+
const lastPendingMessageId = this.pendingMessageIds.pop();
|
|
265
|
+
if (lastPendingMessageId !== cellOpMetadata.pendingMessageId) {
|
|
266
|
+
throw new Error("Rollback op does not match last pending");
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
else {
|
|
270
|
+
throw new Error("Unsupported op for rollback");
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Submit a cell message to remote clients.
|
|
275
|
+
* @param op - The cell message
|
|
276
|
+
* @param previousValue - The value of the cell before this op
|
|
277
|
+
*/
|
|
278
|
+
submitCellMessage(op, previousValue) {
|
|
279
|
+
const localMetadata = this.createLocalOpMetadata(op, previousValue);
|
|
280
|
+
this.submitLocalMessage(op, localMetadata);
|
|
235
281
|
}
|
|
236
282
|
}
|
|
237
283
|
exports.SharedCell = SharedCell;
|
package/dist/cell.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cell.js","sourceRoot":"","sources":["../src/cell.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { assert } from \"@fluidframework/common-utils\";\nimport { ISequencedDocumentMessage, MessageType } from \"@fluidframework/protocol-definitions\";\nimport {\n IChannelAttributes,\n IFluidDataStoreRuntime,\n IChannelStorageService,\n IChannelFactory,\n Serializable,\n} from \"@fluidframework/datastore-definitions\";\nimport { ISummaryTreeWithStats } from \"@fluidframework/runtime-definitions\";\nimport { readAndParse } from \"@fluidframework/driver-utils\";\nimport { createSingleBlobSummary, IFluidSerializer, SharedObject } from \"@fluidframework/shared-object-base\";\nimport { CellFactory } from \"./cellFactory\";\nimport { ISharedCell, ISharedCellEvents } from \"./interfaces\";\n\n/**\n * Description of a cell delta operation\n */\ntype ICellOperation = ISetCellOperation | IDeleteCellOperation;\n\ninterface ISetCellOperation {\n type: \"setCell\";\n value: ICellValue;\n}\n\ninterface IDeleteCellOperation {\n type: \"deleteCell\";\n}\n\ninterface ICellValue {\n // The actual value contained in the cell which needs to be wrapped to handle undefined\n value: any;\n}\n\nconst snapshotFileName = \"header\";\n\n/**\n * The SharedCell distributed data structure can be used to store a single serializable value.\n *\n * @remarks\n * ### Creation\n *\n * To create a `SharedCell`, call the static create method:\n *\n * ```typescript\n * const myCell = SharedCell.create(this.runtime, id);\n * ```\n *\n * ### Usage\n *\n * The value stored in the cell can be set with the `.set()` method and retrieved with the `.get()` method:\n *\n * ```typescript\n * myCell.set(3);\n * console.log(myCell.get()); // 3\n * ```\n *\n * The value must only be plain JS objects or `SharedObject` handles (e.g. to another DDS or Fluid object).\n * In collaborative scenarios, the value is settled with a policy of _last write wins_.\n *\n * The `.delete()` method will delete the stored value from the cell:\n *\n * ```typescript\n * myCell.delete();\n * console.log(myCell.get()); // undefined\n * ```\n *\n * The `.empty()` method will check if the value is undefined.\n *\n * ```typescript\n * if (myCell.empty()) {\n * // myCell.get() will return undefined\n * } else {\n * // myCell.get() will return a non-undefined value\n * }\n * ```\n *\n * ### Eventing\n *\n * `SharedCell` is an `EventEmitter`, and will emit events when other clients make modifications. You should\n * register for these events and respond appropriately as the data is modified. `valueChanged` will be emitted\n * in response to a `set`, and `delete` will be emitted in response to a `delete`.\n */\nexport class SharedCell<T = any> extends SharedObject<ISharedCellEvents<T>>\n implements ISharedCell<T> {\n /**\n * Create a new shared cell\n *\n * @param runtime - data store runtime the new shared map belongs to\n * @param id - optional name of the shared map\n * @returns newly create shared map (but not attached yet)\n */\n public static create(runtime: IFluidDataStoreRuntime, id?: string) {\n return runtime.createChannel(id, CellFactory.Type) as SharedCell;\n }\n\n /**\n * Get a factory for SharedCell to register with the data store.\n *\n * @returns a factory that creates and load SharedCell\n */\n public static getFactory(): IChannelFactory {\n return new CellFactory();\n }\n /**\n * The data held by this cell.\n */\n private data: Serializable<T> | undefined;\n\n /**\n * This is used to assign a unique id to outgoing messages. It is used to track messages until\n * they are ack'd.\n */\n private messageId: number = -1;\n\n /**\n * This keeps track of the messageId of messages that have been ack'd. It is updated every time\n * we a message is ack'd with it's messageId.\n */\n private messageIdObserved: number = -1;\n\n /**\n * Constructs a new shared cell. If the object is non-local an id and service interfaces will\n * be provided\n *\n * @param runtime - data store runtime the shared map belongs to\n * @param id - optional name of the shared map\n */\n constructor(id: string, runtime: IFluidDataStoreRuntime, attributes: IChannelAttributes) {\n super(id, runtime, attributes, \"fluid_cell_\");\n }\n\n /**\n * {@inheritDoc ISharedCell.get}\n */\n public get(): Serializable<T> | undefined {\n return this.data;\n }\n\n /**\n * {@inheritDoc ISharedCell.set}\n */\n public set(value: Serializable<T>) {\n // Serialize the value if required.\n const operationValue: ICellValue = {\n value: this.serializer.encode(value, this.handle),\n };\n\n // Set the value locally.\n this.setCore(value);\n\n // If we are not attached, don't submit the op.\n if (!this.isAttached()) {\n return;\n }\n\n const op: ISetCellOperation = {\n type: \"setCell\",\n value: operationValue,\n };\n this.submitLocalMessage(op, ++this.messageId);\n }\n\n /**\n * {@inheritDoc ISharedCell.delete}\n */\n public delete() {\n // Delete the value locally.\n this.deleteCore();\n\n // If we are not attached, don't submit the op.\n if (!this.isAttached()) {\n return;\n }\n\n const op: IDeleteCellOperation = {\n type: \"deleteCell\",\n };\n this.submitLocalMessage(op, ++this.messageId);\n }\n\n /**\n * {@inheritDoc ISharedCell.empty}\n */\n public empty() {\n return this.data === undefined;\n }\n\n /**\n * Create a summary for the cell\n *\n * @returns the summary of the current state of the cell\n */\n protected summarizeCore(serializer: IFluidSerializer): ISummaryTreeWithStats {\n const content: ICellValue = { value: this.data };\n return createSingleBlobSummary(snapshotFileName, serializer.stringify(content, this.handle));\n }\n\n /**\n * {@inheritDoc @fluidframework/shared-object-base#SharedObject.loadCore}\n */\n protected async loadCore(storage: IChannelStorageService): Promise<void> {\n const content = await readAndParse<ICellValue>(storage, snapshotFileName);\n\n this.data = this.decode(content);\n }\n\n /**\n * Initialize a local instance of cell\n */\n protected initializeLocalCore() {\n this.data = undefined;\n }\n\n /**\n * Call back on disconnect\n */\n protected onDisconnect() { }\n\n /**\n * Apply inner op\n * @param content - ICellOperation content\n */\n private applyInnerOp(content: ICellOperation) {\n switch (content.type) {\n case \"setCell\":\n this.setCore(this.decode(content.value));\n break;\n\n case \"deleteCell\":\n this.deleteCore();\n break;\n\n default:\n throw new Error(\"Unknown operation\");\n }\n }\n\n /**\n * Process a cell operation\n *\n * @param message - the message to prepare\n * @param local - whether the message was sent by the local client\n * @param localOpMetadata - For local client messages, this is the metadata that was submitted with the message.\n * For messages from a remote client, this will be undefined.\n */\n protected processCore(message: ISequencedDocumentMessage, local: boolean, localOpMetadata: unknown) {\n if (this.messageId !== this.messageIdObserved) {\n // We are waiting for an ACK on our change to this cell - we will ignore all messages until we get it.\n if (local) {\n const messageIdReceived = localOpMetadata as number;\n assert(messageIdReceived !== undefined && messageIdReceived <= this.messageId,\n 0x00c /* \"messageId is incorrect from from the local client's ACK\" */);\n\n // We got an ACK. Update messageIdObserved.\n this.messageIdObserved = localOpMetadata as number;\n }\n return;\n }\n\n if (message.type === MessageType.Operation && !local) {\n const op = message.contents as ICellOperation;\n this.applyInnerOp(op);\n }\n }\n\n private setCore(value: Serializable<T>) {\n this.data = value;\n this.emit(\"valueChanged\", value);\n }\n\n private deleteCore() {\n this.data = undefined;\n this.emit(\"delete\");\n }\n\n private decode(cellValue: ICellValue) {\n const value = cellValue.value;\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return this.serializer.decode(value);\n }\n\n /**\n * {@inheritDoc @fluidframework/shared-object-base#SharedObjectCore.applyStashedOp}\n * @internal\n */\n protected applyStashedOp(content: unknown): unknown {\n const cellContent = content as ICellOperation;\n this.applyInnerOp(cellContent);\n ++this.messageId;\n return this.messageId;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"cell.js","sourceRoot":"","sources":["../src/cell.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { assert } from \"@fluidframework/common-utils\";\nimport { ISequencedDocumentMessage, MessageType } from \"@fluidframework/protocol-definitions\";\nimport {\n IChannelAttributes,\n IFluidDataStoreRuntime,\n IChannelStorageService,\n IChannelFactory,\n Serializable,\n} from \"@fluidframework/datastore-definitions\";\nimport { ISummaryTreeWithStats } from \"@fluidframework/runtime-definitions\";\nimport { readAndParse } from \"@fluidframework/driver-utils\";\nimport { createSingleBlobSummary, IFluidSerializer, SharedObject } from \"@fluidframework/shared-object-base\";\nimport { CellFactory } from \"./cellFactory\";\nimport {\n ISharedCell,\n ISharedCellEvents,\n ICellLocalOpMetadata,\n} from \"./interfaces\";\n\n/**\n * Description of a cell delta operation\n */\ntype ICellOperation = ISetCellOperation | IDeleteCellOperation;\n\ninterface ISetCellOperation {\n type: \"setCell\";\n value: ICellValue;\n}\n\ninterface IDeleteCellOperation {\n type: \"deleteCell\";\n}\n\ninterface ICellValue {\n // The actual value contained in the cell which needs to be wrapped to handle undefined\n value: any;\n}\n\nconst snapshotFileName = \"header\";\n\n/**\n * The SharedCell distributed data structure can be used to store a single serializable value.\n *\n * @remarks\n * ### Creation\n *\n * To create a `SharedCell`, call the static create method:\n *\n * ```typescript\n * const myCell = SharedCell.create(this.runtime, id);\n * ```\n *\n * ### Usage\n *\n * The value stored in the cell can be set with the `.set()` method and retrieved with the `.get()` method:\n *\n * ```typescript\n * myCell.set(3);\n * console.log(myCell.get()); // 3\n * ```\n *\n * The value must only be plain JS objects or `SharedObject` handles (e.g. to another DDS or Fluid object).\n * In collaborative scenarios, the value is settled with a policy of _last write wins_.\n *\n * The `.delete()` method will delete the stored value from the cell:\n *\n * ```typescript\n * myCell.delete();\n * console.log(myCell.get()); // undefined\n * ```\n *\n * The `.empty()` method will check if the value is undefined.\n *\n * ```typescript\n * if (myCell.empty()) {\n * // myCell.get() will return undefined\n * } else {\n * // myCell.get() will return a non-undefined value\n * }\n * ```\n *\n * ### Eventing\n *\n * `SharedCell` is an `EventEmitter`, and will emit events when other clients make modifications. You should\n * register for these events and respond appropriately as the data is modified. `valueChanged` will be emitted\n * in response to a `set`, and `delete` will be emitted in response to a `delete`.\n */\nexport class SharedCell<T = any> extends SharedObject<ISharedCellEvents<T>>\n implements ISharedCell<T> {\n /**\n * Create a new shared cell\n *\n * @param runtime - data store runtime the new shared map belongs to\n * @param id - optional name of the shared map\n * @returns newly create shared map (but not attached yet)\n */\n public static create(runtime: IFluidDataStoreRuntime, id?: string) {\n return runtime.createChannel(id, CellFactory.Type) as SharedCell;\n }\n\n /**\n * Get a factory for SharedCell to register with the data store.\n *\n * @returns a factory that creates and load SharedCell\n */\n public static getFactory(): IChannelFactory {\n return new CellFactory();\n }\n /**\n * The data held by this cell.\n */\n private data: Serializable<T> | undefined;\n\n /**\n * This is used to assign a unique id to outgoing messages. It is used to track messages until\n * they are ack'd.\n */\n private messageId: number = -1;\n\n /**\n * This keeps track of the messageId of messages that have been ack'd. It is updated every time\n * we a message is ack'd with it's messageId.\n */\n private messageIdObserved: number = -1;\n\n private readonly pendingMessageIds: number[] = [];\n\n /**\n * Constructs a new shared cell. If the object is non-local an id and service interfaces will\n * be provided\n *\n * @param runtime - data store runtime the shared map belongs to\n * @param id - optional name of the shared map\n */\n constructor(id: string, runtime: IFluidDataStoreRuntime, attributes: IChannelAttributes) {\n super(id, runtime, attributes, \"fluid_cell_\");\n }\n\n /**\n * {@inheritDoc ISharedCell.get}\n */\n public get(): Serializable<T> | undefined {\n return this.data;\n }\n\n /**\n * {@inheritDoc ISharedCell.set}\n */\n public set(value: Serializable<T>) {\n // Serialize the value if required.\n const operationValue: ICellValue = {\n value: this.serializer.encode(value, this.handle),\n };\n\n // Set the value locally.\n const previousValue = this.setCore(value);\n\n // If we are not attached, don't submit the op.\n if (!this.isAttached()) {\n return;\n }\n\n const op: ISetCellOperation = {\n type: \"setCell\",\n value: operationValue,\n };\n this.submitCellMessage(op, previousValue);\n }\n\n /**\n * {@inheritDoc ISharedCell.delete}\n */\n public delete() {\n // Delete the value locally.\n const previousValue = this.deleteCore();\n\n // If we are not attached, don't submit the op.\n if (!this.isAttached()) {\n return;\n }\n\n const op: IDeleteCellOperation = {\n type: \"deleteCell\",\n };\n this.submitCellMessage(op, previousValue);\n }\n\n /**\n * {@inheritDoc ISharedCell.empty}\n */\n public empty() {\n return this.data === undefined;\n }\n\n /**\n * Create a summary for the cell\n *\n * @returns the summary of the current state of the cell\n */\n protected summarizeCore(serializer: IFluidSerializer): ISummaryTreeWithStats {\n const content: ICellValue = { value: this.data };\n return createSingleBlobSummary(snapshotFileName, serializer.stringify(content, this.handle));\n }\n\n /**\n * {@inheritDoc @fluidframework/shared-object-base#SharedObject.loadCore}\n */\n protected async loadCore(storage: IChannelStorageService): Promise<void> {\n const content = await readAndParse<ICellValue>(storage, snapshotFileName);\n\n this.data = this.decode(content);\n }\n\n /**\n * Initialize a local instance of cell\n */\n protected initializeLocalCore() {\n this.data = undefined;\n }\n\n /**\n * Call back on disconnect\n */\n protected onDisconnect() { }\n\n /**\n * Apply inner op\n * @param content - ICellOperation content\n */\n private applyInnerOp(content: ICellOperation) {\n switch (content.type) {\n case \"setCell\":\n return this.setCore(this.decode(content.value));\n\n case \"deleteCell\":\n return this.deleteCore();\n\n default:\n throw new Error(\"Unknown operation\");\n }\n }\n\n /**\n * Process a cell operation\n *\n * @param message - the message to prepare\n * @param local - whether the message was sent by the local client\n * @param localOpMetadata - For local client messages, this is the metadata that was submitted with the message.\n * For messages from a remote client, this will be undefined.\n */\n protected processCore(message: ISequencedDocumentMessage, local: boolean, localOpMetadata: unknown) {\n const cellOpMetadata = localOpMetadata as ICellLocalOpMetadata;\n if (this.messageId !== this.messageIdObserved) {\n // We are waiting for an ACK on our change to this cell - we will ignore all messages until we get it.\n if (local) {\n const messageIdReceived = cellOpMetadata.pendingMessageId;\n assert(messageIdReceived !== undefined && messageIdReceived <= this.messageId,\n 0x00c /* \"messageId is incorrect from from the local client's ACK\" */);\n assert(this.pendingMessageIds !== undefined &&\n this.pendingMessageIds[0] === cellOpMetadata.pendingMessageId,\n 0x471 /* Unexpected pending message received */);\n this.pendingMessageIds.shift();\n // We got an ACK. Update messageIdObserved.\n this.messageIdObserved = cellOpMetadata.pendingMessageId;\n }\n return;\n }\n\n if (message.type === MessageType.Operation && !local) {\n const op = message.contents as ICellOperation;\n this.applyInnerOp(op);\n }\n }\n\n private setCore(value: Serializable<T>): Serializable<T> | undefined {\n const previousLocalValue = this.get();\n this.data = value;\n this.emit(\"valueChanged\", value);\n return previousLocalValue;\n }\n\n private deleteCore(): Serializable<T> | undefined {\n const previousLocalValue = this.get();\n this.data = undefined;\n this.emit(\"delete\");\n return previousLocalValue;\n }\n\n private decode(cellValue: ICellValue) {\n const value = cellValue.value;\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return this.serializer.decode(value);\n }\n\n private createLocalOpMetadata(op: ICellOperation, previousValue?: Serializable<T>): ICellLocalOpMetadata {\n const pendingMessageId = ++this.messageId;\n this.pendingMessageIds.push(pendingMessageId);\n const localMetadata: ICellLocalOpMetadata = {\n pendingMessageId, previousValue,\n };\n return localMetadata;\n }\n\n /**\n * {@inheritDoc @fluidframework/shared-object-base#SharedObjectCore.applyStashedOp}\n * @internal\n */\n protected applyStashedOp(content: unknown): unknown {\n const cellContent = content as ICellOperation;\n const previousValue = this.applyInnerOp(cellContent);\n return this.createLocalOpMetadata(cellContent, previousValue);\n }\n\n /**\n * Rollback a local op\n * @param content - The operation to rollback\n * @param localOpMetadata - The local metadata associated with the op.\n */\n protected rollback(content: any, localOpMetadata: unknown) {\n const cellOpMetadata = localOpMetadata as ICellLocalOpMetadata;\n if (content.type === \"setCell\" || content.type === \"deleteCell\") {\n if (cellOpMetadata.previousValue === undefined) {\n this.deleteCore();\n } else {\n this.setCore(cellOpMetadata.previousValue);\n }\n\n const lastPendingMessageId = this.pendingMessageIds.pop();\n if (lastPendingMessageId !== cellOpMetadata.pendingMessageId) {\n throw new Error(\"Rollback op does not match last pending\");\n }\n } else {\n throw new Error(\"Unsupported op for rollback\");\n }\n }\n\n /**\n * Submit a cell message to remote clients.\n * @param op - The cell message\n * @param previousValue - The value of the cell before this op\n */\n private submitCellMessage(op: ICellOperation, previousValue?: any): void {\n const localMetadata = this.createLocalOpMetadata(op, previousValue);\n this.submitLocalMessage(op, localMetadata);\n }\n}\n"]}
|
package/dist/interfaces.d.ts
CHANGED
package/dist/interfaces.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interfaces.d.ts","sourceRoot":"","sources":["../src/interfaces.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,oCAAoC,CAAC;AACxF,OAAO,EAAE,YAAY,EAAE,MAAM,uCAAuC,CAAC;AAErE,MAAM,WAAW,iBAAiB,CAAC,CAAC,CAAE,SAAQ,mBAAmB;IAC7D,CAAC,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC,KAAK,IAAI,OAAE;IACpE,CAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,IAAI,OAAE;CAC3C;AAED;;GAEG;AAEH,MAAM,WAAW,WAAW,CAAC,CAAC,GAAG,GAAG,CAAE,SAAQ,aAAa,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC;IAC7E;;;;OAIG;IACH,GAAG,IAAI,YAAY,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IAEnC;;;;OAIG;IACH,GAAG,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAElC;;;;OAIG;IACH,KAAK,IAAI,OAAO,CAAC;IAEjB;;OAEG;IACH,MAAM,IAAI,IAAI,CAAC;CAClB"}
|
|
1
|
+
{"version":3,"file":"interfaces.d.ts","sourceRoot":"","sources":["../src/interfaces.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,oCAAoC,CAAC;AACxF,OAAO,EAAE,YAAY,EAAE,MAAM,uCAAuC,CAAC;AAErE,MAAM,WAAW,iBAAiB,CAAC,CAAC,CAAE,SAAQ,mBAAmB;IAC7D,CAAC,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC,KAAK,IAAI,OAAE;IACpE,CAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,IAAI,OAAE;CAC3C;AAED;;GAEG;AAEH,MAAM,WAAW,WAAW,CAAC,CAAC,GAAG,GAAG,CAAE,SAAQ,aAAa,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC;IAC7E;;;;OAIG;IACH,GAAG,IAAI,YAAY,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IAEnC;;;;OAIG;IACH,GAAG,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAElC;;;;OAIG;IACH,KAAK,IAAI,OAAO,CAAC;IAEjB;;OAEG;IACH,MAAM,IAAI,IAAI,CAAC;CAClB;AACD,MAAM,WAAW,oBAAoB;IACjC,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,CAAC,EAAE,GAAG,CAAC;CACvB"}
|
package/dist/interfaces.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interfaces.js","sourceRoot":"","sources":["../src/interfaces.ts"],"names":[],"mappings":";AAAA;;;GAGG","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { ISharedObject, ISharedObjectEvents } from \"@fluidframework/shared-object-base\";\nimport { Serializable } from \"@fluidframework/datastore-definitions\";\n\nexport interface ISharedCellEvents<T> extends ISharedObjectEvents {\n (event: \"valueChanged\", listener: (value: Serializable<T>) => void);\n (event: \"delete\", listener: () => void);\n}\n\n/**\n * Shared cell interface\n */\n\nexport interface ISharedCell<T = any> extends ISharedObject<ISharedCellEvents<T>> {\n /**\n * Retrieves the cell value.\n *\n * @returns - the value of the cell\n */\n get(): Serializable<T> | undefined;\n\n /**\n * Sets the cell value.\n *\n * @param value - a JSON-able or SharedObject value to set the cell to\n */\n set(value: Serializable<T>): void;\n\n /**\n * Checks whether cell is empty or not.\n *\n * @returns - `true` if the value of cell is `undefined`, `false` otherwise\n */\n empty(): boolean;\n\n /**\n * Delete the value from the cell.\n */\n delete(): void;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"interfaces.js","sourceRoot":"","sources":["../src/interfaces.ts"],"names":[],"mappings":";AAAA;;;GAGG","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { ISharedObject, ISharedObjectEvents } from \"@fluidframework/shared-object-base\";\nimport { Serializable } from \"@fluidframework/datastore-definitions\";\n\nexport interface ISharedCellEvents<T> extends ISharedObjectEvents {\n (event: \"valueChanged\", listener: (value: Serializable<T>) => void);\n (event: \"delete\", listener: () => void);\n}\n\n/**\n * Shared cell interface\n */\n\nexport interface ISharedCell<T = any> extends ISharedObject<ISharedCellEvents<T>> {\n /**\n * Retrieves the cell value.\n *\n * @returns - the value of the cell\n */\n get(): Serializable<T> | undefined;\n\n /**\n * Sets the cell value.\n *\n * @param value - a JSON-able or SharedObject value to set the cell to\n */\n set(value: Serializable<T>): void;\n\n /**\n * Checks whether cell is empty or not.\n *\n * @returns - `true` if the value of cell is `undefined`, `false` otherwise\n */\n empty(): boolean;\n\n /**\n * Delete the value from the cell.\n */\n delete(): void;\n}\nexport interface ICellLocalOpMetadata {\n pendingMessageId: number;\n previousValue?: any;\n}\n"]}
|
package/dist/packageVersion.d.ts
CHANGED
|
@@ -5,5 +5,5 @@
|
|
|
5
5
|
* THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY
|
|
6
6
|
*/
|
|
7
7
|
export declare const pkgName = "@fluidframework/cell";
|
|
8
|
-
export declare const pkgVersion = "2.0.0-internal.2.
|
|
8
|
+
export declare const pkgVersion = "2.0.0-internal.2.2.0";
|
|
9
9
|
//# sourceMappingURL=packageVersion.d.ts.map
|
package/dist/packageVersion.js
CHANGED
|
@@ -8,5 +8,5 @@
|
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
9
|
exports.pkgVersion = exports.pkgName = void 0;
|
|
10
10
|
exports.pkgName = "@fluidframework/cell";
|
|
11
|
-
exports.pkgVersion = "2.0.0-internal.2.
|
|
11
|
+
exports.pkgVersion = "2.0.0-internal.2.2.0";
|
|
12
12
|
//# sourceMappingURL=packageVersion.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAEU,QAAA,OAAO,GAAG,sBAAsB,CAAC;AACjC,QAAA,UAAU,GAAG,sBAAsB,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n *\n * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY\n */\n\nexport const pkgName = \"@fluidframework/cell\";\nexport const pkgVersion = \"2.0.0-internal.2.
|
|
1
|
+
{"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAEU,QAAA,OAAO,GAAG,sBAAsB,CAAC;AACjC,QAAA,UAAU,GAAG,sBAAsB,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n *\n * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY\n */\n\nexport const pkgName = \"@fluidframework/cell\";\nexport const pkgVersion = \"2.0.0-internal.2.2.0\";\n"]}
|
package/lib/cell.d.ts
CHANGED
|
@@ -83,6 +83,7 @@ export declare class SharedCell<T = any> extends SharedObject<ISharedCellEvents<
|
|
|
83
83
|
* we a message is ack'd with it's messageId.
|
|
84
84
|
*/
|
|
85
85
|
private messageIdObserved;
|
|
86
|
+
private readonly pendingMessageIds;
|
|
86
87
|
/**
|
|
87
88
|
* Constructs a new shared cell. If the object is non-local an id and service interfaces will
|
|
88
89
|
* be provided
|
|
@@ -142,10 +143,23 @@ export declare class SharedCell<T = any> extends SharedObject<ISharedCellEvents<
|
|
|
142
143
|
private setCore;
|
|
143
144
|
private deleteCore;
|
|
144
145
|
private decode;
|
|
146
|
+
private createLocalOpMetadata;
|
|
145
147
|
/**
|
|
146
148
|
* {@inheritDoc @fluidframework/shared-object-base#SharedObjectCore.applyStashedOp}
|
|
147
149
|
* @internal
|
|
148
150
|
*/
|
|
149
151
|
protected applyStashedOp(content: unknown): unknown;
|
|
152
|
+
/**
|
|
153
|
+
* Rollback a local op
|
|
154
|
+
* @param content - The operation to rollback
|
|
155
|
+
* @param localOpMetadata - The local metadata associated with the op.
|
|
156
|
+
*/
|
|
157
|
+
protected rollback(content: any, localOpMetadata: unknown): void;
|
|
158
|
+
/**
|
|
159
|
+
* Submit a cell message to remote clients.
|
|
160
|
+
* @param op - The cell message
|
|
161
|
+
* @param previousValue - The value of the cell before this op
|
|
162
|
+
*/
|
|
163
|
+
private submitCellMessage;
|
|
150
164
|
}
|
|
151
165
|
//# sourceMappingURL=cell.d.ts.map
|
package/lib/cell.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cell.d.ts","sourceRoot":"","sources":["../src/cell.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,yBAAyB,EAAe,MAAM,sCAAsC,CAAC;AAC9F,OAAO,EACH,kBAAkB,EAClB,sBAAsB,EACtB,sBAAsB,EACtB,eAAe,EACf,YAAY,EACf,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EAAE,qBAAqB,EAAE,MAAM,qCAAqC,CAAC;AAE5E,OAAO,EAA2B,gBAAgB,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAE7G,OAAO,
|
|
1
|
+
{"version":3,"file":"cell.d.ts","sourceRoot":"","sources":["../src/cell.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,yBAAyB,EAAe,MAAM,sCAAsC,CAAC;AAC9F,OAAO,EACH,kBAAkB,EAClB,sBAAsB,EACtB,sBAAsB,EACtB,eAAe,EACf,YAAY,EACf,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EAAE,qBAAqB,EAAE,MAAM,qCAAqC,CAAC;AAE5E,OAAO,EAA2B,gBAAgB,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAE7G,OAAO,EACH,WAAW,EACX,iBAAiB,EAEpB,MAAM,cAAc,CAAC;AAuBtB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AACH,qBAAa,UAAU,CAAC,CAAC,GAAG,GAAG,CAAE,SAAQ,YAAY,CAAC,iBAAiB,CAAC,CAAC,CAAC,CACtE,YAAW,WAAW,CAAC,CAAC,CAAC;IACzB;;;;;;OAMG;WACW,MAAM,CAAC,OAAO,EAAE,sBAAsB,EAAE,EAAE,CAAC,EAAE,MAAM;IAIjE;;;;OAIG;WACW,UAAU,IAAI,eAAe;IAG3C;;OAEG;IACH,OAAO,CAAC,IAAI,CAA8B;IAE1C;;;OAGG;IACH,OAAO,CAAC,SAAS,CAAc;IAE/B;;;OAGG;IACH,OAAO,CAAC,iBAAiB,CAAc;IAEvC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAgB;IAElD;;;;;;OAMG;gBACS,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,sBAAsB,EAAE,UAAU,EAAE,kBAAkB;IAIvF;;OAEG;IACI,GAAG,IAAI,YAAY,CAAC,CAAC,CAAC,GAAG,SAAS;IAIzC;;OAEG;IACI,GAAG,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC;IAqBjC;;OAEG;IACI,MAAM;IAeb;;OAEG;IACI,KAAK;IAIZ;;;;OAIG;IACH,SAAS,CAAC,aAAa,CAAC,UAAU,EAAE,gBAAgB,GAAG,qBAAqB;IAK5E;;OAEG;cACa,QAAQ,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC;IAMxE;;OAEG;IACH,SAAS,CAAC,mBAAmB;IAI7B;;OAEG;IACH,SAAS,CAAC,YAAY;IAEtB;;;OAGG;IACH,OAAO,CAAC,YAAY;IAapB;;;;;;;OAOG;IACH,SAAS,CAAC,WAAW,CAAC,OAAO,EAAE,yBAAyB,EAAE,KAAK,EAAE,OAAO,EAAE,eAAe,EAAE,OAAO;IAwBlG,OAAO,CAAC,OAAO;IAOf,OAAO,CAAC,UAAU;IAOlB,OAAO,CAAC,MAAM;IAMd,OAAO,CAAC,qBAAqB;IAS7B;;;OAGG;IACH,SAAS,CAAC,cAAc,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO;IAMnD;;;;OAIG;IACH,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,GAAG,EAAE,eAAe,EAAE,OAAO;IAkBxD;;;;MAIE;IACH,OAAO,CAAC,iBAAiB;CAI5B"}
|
package/lib/cell.js
CHANGED
|
@@ -75,6 +75,7 @@ export class SharedCell extends SharedObject {
|
|
|
75
75
|
* we a message is ack'd with it's messageId.
|
|
76
76
|
*/
|
|
77
77
|
this.messageIdObserved = -1;
|
|
78
|
+
this.pendingMessageIds = [];
|
|
78
79
|
}
|
|
79
80
|
/**
|
|
80
81
|
* Create a new shared cell
|
|
@@ -109,7 +110,7 @@ export class SharedCell extends SharedObject {
|
|
|
109
110
|
value: this.serializer.encode(value, this.handle),
|
|
110
111
|
};
|
|
111
112
|
// Set the value locally.
|
|
112
|
-
this.setCore(value);
|
|
113
|
+
const previousValue = this.setCore(value);
|
|
113
114
|
// If we are not attached, don't submit the op.
|
|
114
115
|
if (!this.isAttached()) {
|
|
115
116
|
return;
|
|
@@ -118,14 +119,14 @@ export class SharedCell extends SharedObject {
|
|
|
118
119
|
type: "setCell",
|
|
119
120
|
value: operationValue,
|
|
120
121
|
};
|
|
121
|
-
this.
|
|
122
|
+
this.submitCellMessage(op, previousValue);
|
|
122
123
|
}
|
|
123
124
|
/**
|
|
124
125
|
* {@inheritDoc ISharedCell.delete}
|
|
125
126
|
*/
|
|
126
127
|
delete() {
|
|
127
128
|
// Delete the value locally.
|
|
128
|
-
this.deleteCore();
|
|
129
|
+
const previousValue = this.deleteCore();
|
|
129
130
|
// If we are not attached, don't submit the op.
|
|
130
131
|
if (!this.isAttached()) {
|
|
131
132
|
return;
|
|
@@ -133,7 +134,7 @@ export class SharedCell extends SharedObject {
|
|
|
133
134
|
const op = {
|
|
134
135
|
type: "deleteCell",
|
|
135
136
|
};
|
|
136
|
-
this.
|
|
137
|
+
this.submitCellMessage(op, previousValue);
|
|
137
138
|
}
|
|
138
139
|
/**
|
|
139
140
|
* {@inheritDoc ISharedCell.empty}
|
|
@@ -174,11 +175,9 @@ export class SharedCell extends SharedObject {
|
|
|
174
175
|
applyInnerOp(content) {
|
|
175
176
|
switch (content.type) {
|
|
176
177
|
case "setCell":
|
|
177
|
-
this.setCore(this.decode(content.value));
|
|
178
|
-
break;
|
|
178
|
+
return this.setCore(this.decode(content.value));
|
|
179
179
|
case "deleteCell":
|
|
180
|
-
this.deleteCore();
|
|
181
|
-
break;
|
|
180
|
+
return this.deleteCore();
|
|
182
181
|
default:
|
|
183
182
|
throw new Error("Unknown operation");
|
|
184
183
|
}
|
|
@@ -192,13 +191,17 @@ export class SharedCell extends SharedObject {
|
|
|
192
191
|
* For messages from a remote client, this will be undefined.
|
|
193
192
|
*/
|
|
194
193
|
processCore(message, local, localOpMetadata) {
|
|
194
|
+
const cellOpMetadata = localOpMetadata;
|
|
195
195
|
if (this.messageId !== this.messageIdObserved) {
|
|
196
196
|
// We are waiting for an ACK on our change to this cell - we will ignore all messages until we get it.
|
|
197
197
|
if (local) {
|
|
198
|
-
const messageIdReceived =
|
|
198
|
+
const messageIdReceived = cellOpMetadata.pendingMessageId;
|
|
199
199
|
assert(messageIdReceived !== undefined && messageIdReceived <= this.messageId, 0x00c /* "messageId is incorrect from from the local client's ACK" */);
|
|
200
|
+
assert(this.pendingMessageIds !== undefined &&
|
|
201
|
+
this.pendingMessageIds[0] === cellOpMetadata.pendingMessageId, 0x471 /* Unexpected pending message received */);
|
|
202
|
+
this.pendingMessageIds.shift();
|
|
200
203
|
// We got an ACK. Update messageIdObserved.
|
|
201
|
-
this.messageIdObserved =
|
|
204
|
+
this.messageIdObserved = cellOpMetadata.pendingMessageId;
|
|
202
205
|
}
|
|
203
206
|
return;
|
|
204
207
|
}
|
|
@@ -208,27 +211,70 @@ export class SharedCell extends SharedObject {
|
|
|
208
211
|
}
|
|
209
212
|
}
|
|
210
213
|
setCore(value) {
|
|
214
|
+
const previousLocalValue = this.get();
|
|
211
215
|
this.data = value;
|
|
212
216
|
this.emit("valueChanged", value);
|
|
217
|
+
return previousLocalValue;
|
|
213
218
|
}
|
|
214
219
|
deleteCore() {
|
|
220
|
+
const previousLocalValue = this.get();
|
|
215
221
|
this.data = undefined;
|
|
216
222
|
this.emit("delete");
|
|
223
|
+
return previousLocalValue;
|
|
217
224
|
}
|
|
218
225
|
decode(cellValue) {
|
|
219
226
|
const value = cellValue.value;
|
|
220
227
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
221
228
|
return this.serializer.decode(value);
|
|
222
229
|
}
|
|
230
|
+
createLocalOpMetadata(op, previousValue) {
|
|
231
|
+
const pendingMessageId = ++this.messageId;
|
|
232
|
+
this.pendingMessageIds.push(pendingMessageId);
|
|
233
|
+
const localMetadata = {
|
|
234
|
+
pendingMessageId, previousValue,
|
|
235
|
+
};
|
|
236
|
+
return localMetadata;
|
|
237
|
+
}
|
|
223
238
|
/**
|
|
224
239
|
* {@inheritDoc @fluidframework/shared-object-base#SharedObjectCore.applyStashedOp}
|
|
225
240
|
* @internal
|
|
226
241
|
*/
|
|
227
242
|
applyStashedOp(content) {
|
|
228
243
|
const cellContent = content;
|
|
229
|
-
this.applyInnerOp(cellContent);
|
|
230
|
-
|
|
231
|
-
|
|
244
|
+
const previousValue = this.applyInnerOp(cellContent);
|
|
245
|
+
return this.createLocalOpMetadata(cellContent, previousValue);
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Rollback a local op
|
|
249
|
+
* @param content - The operation to rollback
|
|
250
|
+
* @param localOpMetadata - The local metadata associated with the op.
|
|
251
|
+
*/
|
|
252
|
+
rollback(content, localOpMetadata) {
|
|
253
|
+
const cellOpMetadata = localOpMetadata;
|
|
254
|
+
if (content.type === "setCell" || content.type === "deleteCell") {
|
|
255
|
+
if (cellOpMetadata.previousValue === undefined) {
|
|
256
|
+
this.deleteCore();
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
this.setCore(cellOpMetadata.previousValue);
|
|
260
|
+
}
|
|
261
|
+
const lastPendingMessageId = this.pendingMessageIds.pop();
|
|
262
|
+
if (lastPendingMessageId !== cellOpMetadata.pendingMessageId) {
|
|
263
|
+
throw new Error("Rollback op does not match last pending");
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
else {
|
|
267
|
+
throw new Error("Unsupported op for rollback");
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Submit a cell message to remote clients.
|
|
272
|
+
* @param op - The cell message
|
|
273
|
+
* @param previousValue - The value of the cell before this op
|
|
274
|
+
*/
|
|
275
|
+
submitCellMessage(op, previousValue) {
|
|
276
|
+
const localMetadata = this.createLocalOpMetadata(op, previousValue);
|
|
277
|
+
this.submitLocalMessage(op, localMetadata);
|
|
232
278
|
}
|
|
233
279
|
}
|
|
234
280
|
//# sourceMappingURL=cell.js.map
|
package/lib/cell.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cell.js","sourceRoot":"","sources":["../src/cell.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { assert } from \"@fluidframework/common-utils\";\nimport { ISequencedDocumentMessage, MessageType } from \"@fluidframework/protocol-definitions\";\nimport {\n IChannelAttributes,\n IFluidDataStoreRuntime,\n IChannelStorageService,\n IChannelFactory,\n Serializable,\n} from \"@fluidframework/datastore-definitions\";\nimport { ISummaryTreeWithStats } from \"@fluidframework/runtime-definitions\";\nimport { readAndParse } from \"@fluidframework/driver-utils\";\nimport { createSingleBlobSummary, IFluidSerializer, SharedObject } from \"@fluidframework/shared-object-base\";\nimport { CellFactory } from \"./cellFactory\";\nimport { ISharedCell, ISharedCellEvents } from \"./interfaces\";\n\n/**\n * Description of a cell delta operation\n */\ntype ICellOperation = ISetCellOperation | IDeleteCellOperation;\n\ninterface ISetCellOperation {\n type: \"setCell\";\n value: ICellValue;\n}\n\ninterface IDeleteCellOperation {\n type: \"deleteCell\";\n}\n\ninterface ICellValue {\n // The actual value contained in the cell which needs to be wrapped to handle undefined\n value: any;\n}\n\nconst snapshotFileName = \"header\";\n\n/**\n * The SharedCell distributed data structure can be used to store a single serializable value.\n *\n * @remarks\n * ### Creation\n *\n * To create a `SharedCell`, call the static create method:\n *\n * ```typescript\n * const myCell = SharedCell.create(this.runtime, id);\n * ```\n *\n * ### Usage\n *\n * The value stored in the cell can be set with the `.set()` method and retrieved with the `.get()` method:\n *\n * ```typescript\n * myCell.set(3);\n * console.log(myCell.get()); // 3\n * ```\n *\n * The value must only be plain JS objects or `SharedObject` handles (e.g. to another DDS or Fluid object).\n * In collaborative scenarios, the value is settled with a policy of _last write wins_.\n *\n * The `.delete()` method will delete the stored value from the cell:\n *\n * ```typescript\n * myCell.delete();\n * console.log(myCell.get()); // undefined\n * ```\n *\n * The `.empty()` method will check if the value is undefined.\n *\n * ```typescript\n * if (myCell.empty()) {\n * // myCell.get() will return undefined\n * } else {\n * // myCell.get() will return a non-undefined value\n * }\n * ```\n *\n * ### Eventing\n *\n * `SharedCell` is an `EventEmitter`, and will emit events when other clients make modifications. You should\n * register for these events and respond appropriately as the data is modified. `valueChanged` will be emitted\n * in response to a `set`, and `delete` will be emitted in response to a `delete`.\n */\nexport class SharedCell<T = any> extends SharedObject<ISharedCellEvents<T>>\n implements ISharedCell<T> {\n /**\n * Create a new shared cell\n *\n * @param runtime - data store runtime the new shared map belongs to\n * @param id - optional name of the shared map\n * @returns newly create shared map (but not attached yet)\n */\n public static create(runtime: IFluidDataStoreRuntime, id?: string) {\n return runtime.createChannel(id, CellFactory.Type) as SharedCell;\n }\n\n /**\n * Get a factory for SharedCell to register with the data store.\n *\n * @returns a factory that creates and load SharedCell\n */\n public static getFactory(): IChannelFactory {\n return new CellFactory();\n }\n /**\n * The data held by this cell.\n */\n private data: Serializable<T> | undefined;\n\n /**\n * This is used to assign a unique id to outgoing messages. It is used to track messages until\n * they are ack'd.\n */\n private messageId: number = -1;\n\n /**\n * This keeps track of the messageId of messages that have been ack'd. It is updated every time\n * we a message is ack'd with it's messageId.\n */\n private messageIdObserved: number = -1;\n\n /**\n * Constructs a new shared cell. If the object is non-local an id and service interfaces will\n * be provided\n *\n * @param runtime - data store runtime the shared map belongs to\n * @param id - optional name of the shared map\n */\n constructor(id: string, runtime: IFluidDataStoreRuntime, attributes: IChannelAttributes) {\n super(id, runtime, attributes, \"fluid_cell_\");\n }\n\n /**\n * {@inheritDoc ISharedCell.get}\n */\n public get(): Serializable<T> | undefined {\n return this.data;\n }\n\n /**\n * {@inheritDoc ISharedCell.set}\n */\n public set(value: Serializable<T>) {\n // Serialize the value if required.\n const operationValue: ICellValue = {\n value: this.serializer.encode(value, this.handle),\n };\n\n // Set the value locally.\n this.setCore(value);\n\n // If we are not attached, don't submit the op.\n if (!this.isAttached()) {\n return;\n }\n\n const op: ISetCellOperation = {\n type: \"setCell\",\n value: operationValue,\n };\n this.submitLocalMessage(op, ++this.messageId);\n }\n\n /**\n * {@inheritDoc ISharedCell.delete}\n */\n public delete() {\n // Delete the value locally.\n this.deleteCore();\n\n // If we are not attached, don't submit the op.\n if (!this.isAttached()) {\n return;\n }\n\n const op: IDeleteCellOperation = {\n type: \"deleteCell\",\n };\n this.submitLocalMessage(op, ++this.messageId);\n }\n\n /**\n * {@inheritDoc ISharedCell.empty}\n */\n public empty() {\n return this.data === undefined;\n }\n\n /**\n * Create a summary for the cell\n *\n * @returns the summary of the current state of the cell\n */\n protected summarizeCore(serializer: IFluidSerializer): ISummaryTreeWithStats {\n const content: ICellValue = { value: this.data };\n return createSingleBlobSummary(snapshotFileName, serializer.stringify(content, this.handle));\n }\n\n /**\n * {@inheritDoc @fluidframework/shared-object-base#SharedObject.loadCore}\n */\n protected async loadCore(storage: IChannelStorageService): Promise<void> {\n const content = await readAndParse<ICellValue>(storage, snapshotFileName);\n\n this.data = this.decode(content);\n }\n\n /**\n * Initialize a local instance of cell\n */\n protected initializeLocalCore() {\n this.data = undefined;\n }\n\n /**\n * Call back on disconnect\n */\n protected onDisconnect() { }\n\n /**\n * Apply inner op\n * @param content - ICellOperation content\n */\n private applyInnerOp(content: ICellOperation) {\n switch (content.type) {\n case \"setCell\":\n this.setCore(this.decode(content.value));\n break;\n\n case \"deleteCell\":\n this.deleteCore();\n break;\n\n default:\n throw new Error(\"Unknown operation\");\n }\n }\n\n /**\n * Process a cell operation\n *\n * @param message - the message to prepare\n * @param local - whether the message was sent by the local client\n * @param localOpMetadata - For local client messages, this is the metadata that was submitted with the message.\n * For messages from a remote client, this will be undefined.\n */\n protected processCore(message: ISequencedDocumentMessage, local: boolean, localOpMetadata: unknown) {\n if (this.messageId !== this.messageIdObserved) {\n // We are waiting for an ACK on our change to this cell - we will ignore all messages until we get it.\n if (local) {\n const messageIdReceived = localOpMetadata as number;\n assert(messageIdReceived !== undefined && messageIdReceived <= this.messageId,\n 0x00c /* \"messageId is incorrect from from the local client's ACK\" */);\n\n // We got an ACK. Update messageIdObserved.\n this.messageIdObserved = localOpMetadata as number;\n }\n return;\n }\n\n if (message.type === MessageType.Operation && !local) {\n const op = message.contents as ICellOperation;\n this.applyInnerOp(op);\n }\n }\n\n private setCore(value: Serializable<T>) {\n this.data = value;\n this.emit(\"valueChanged\", value);\n }\n\n private deleteCore() {\n this.data = undefined;\n this.emit(\"delete\");\n }\n\n private decode(cellValue: ICellValue) {\n const value = cellValue.value;\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return this.serializer.decode(value);\n }\n\n /**\n * {@inheritDoc @fluidframework/shared-object-base#SharedObjectCore.applyStashedOp}\n * @internal\n */\n protected applyStashedOp(content: unknown): unknown {\n const cellContent = content as ICellOperation;\n this.applyInnerOp(cellContent);\n ++this.messageId;\n return this.messageId;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"cell.js","sourceRoot":"","sources":["../src/cell.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { assert } from \"@fluidframework/common-utils\";\nimport { ISequencedDocumentMessage, MessageType } from \"@fluidframework/protocol-definitions\";\nimport {\n IChannelAttributes,\n IFluidDataStoreRuntime,\n IChannelStorageService,\n IChannelFactory,\n Serializable,\n} from \"@fluidframework/datastore-definitions\";\nimport { ISummaryTreeWithStats } from \"@fluidframework/runtime-definitions\";\nimport { readAndParse } from \"@fluidframework/driver-utils\";\nimport { createSingleBlobSummary, IFluidSerializer, SharedObject } from \"@fluidframework/shared-object-base\";\nimport { CellFactory } from \"./cellFactory\";\nimport {\n ISharedCell,\n ISharedCellEvents,\n ICellLocalOpMetadata,\n} from \"./interfaces\";\n\n/**\n * Description of a cell delta operation\n */\ntype ICellOperation = ISetCellOperation | IDeleteCellOperation;\n\ninterface ISetCellOperation {\n type: \"setCell\";\n value: ICellValue;\n}\n\ninterface IDeleteCellOperation {\n type: \"deleteCell\";\n}\n\ninterface ICellValue {\n // The actual value contained in the cell which needs to be wrapped to handle undefined\n value: any;\n}\n\nconst snapshotFileName = \"header\";\n\n/**\n * The SharedCell distributed data structure can be used to store a single serializable value.\n *\n * @remarks\n * ### Creation\n *\n * To create a `SharedCell`, call the static create method:\n *\n * ```typescript\n * const myCell = SharedCell.create(this.runtime, id);\n * ```\n *\n * ### Usage\n *\n * The value stored in the cell can be set with the `.set()` method and retrieved with the `.get()` method:\n *\n * ```typescript\n * myCell.set(3);\n * console.log(myCell.get()); // 3\n * ```\n *\n * The value must only be plain JS objects or `SharedObject` handles (e.g. to another DDS or Fluid object).\n * In collaborative scenarios, the value is settled with a policy of _last write wins_.\n *\n * The `.delete()` method will delete the stored value from the cell:\n *\n * ```typescript\n * myCell.delete();\n * console.log(myCell.get()); // undefined\n * ```\n *\n * The `.empty()` method will check if the value is undefined.\n *\n * ```typescript\n * if (myCell.empty()) {\n * // myCell.get() will return undefined\n * } else {\n * // myCell.get() will return a non-undefined value\n * }\n * ```\n *\n * ### Eventing\n *\n * `SharedCell` is an `EventEmitter`, and will emit events when other clients make modifications. You should\n * register for these events and respond appropriately as the data is modified. `valueChanged` will be emitted\n * in response to a `set`, and `delete` will be emitted in response to a `delete`.\n */\nexport class SharedCell<T = any> extends SharedObject<ISharedCellEvents<T>>\n implements ISharedCell<T> {\n /**\n * Create a new shared cell\n *\n * @param runtime - data store runtime the new shared map belongs to\n * @param id - optional name of the shared map\n * @returns newly create shared map (but not attached yet)\n */\n public static create(runtime: IFluidDataStoreRuntime, id?: string) {\n return runtime.createChannel(id, CellFactory.Type) as SharedCell;\n }\n\n /**\n * Get a factory for SharedCell to register with the data store.\n *\n * @returns a factory that creates and load SharedCell\n */\n public static getFactory(): IChannelFactory {\n return new CellFactory();\n }\n /**\n * The data held by this cell.\n */\n private data: Serializable<T> | undefined;\n\n /**\n * This is used to assign a unique id to outgoing messages. It is used to track messages until\n * they are ack'd.\n */\n private messageId: number = -1;\n\n /**\n * This keeps track of the messageId of messages that have been ack'd. It is updated every time\n * we a message is ack'd with it's messageId.\n */\n private messageIdObserved: number = -1;\n\n private readonly pendingMessageIds: number[] = [];\n\n /**\n * Constructs a new shared cell. If the object is non-local an id and service interfaces will\n * be provided\n *\n * @param runtime - data store runtime the shared map belongs to\n * @param id - optional name of the shared map\n */\n constructor(id: string, runtime: IFluidDataStoreRuntime, attributes: IChannelAttributes) {\n super(id, runtime, attributes, \"fluid_cell_\");\n }\n\n /**\n * {@inheritDoc ISharedCell.get}\n */\n public get(): Serializable<T> | undefined {\n return this.data;\n }\n\n /**\n * {@inheritDoc ISharedCell.set}\n */\n public set(value: Serializable<T>) {\n // Serialize the value if required.\n const operationValue: ICellValue = {\n value: this.serializer.encode(value, this.handle),\n };\n\n // Set the value locally.\n const previousValue = this.setCore(value);\n\n // If we are not attached, don't submit the op.\n if (!this.isAttached()) {\n return;\n }\n\n const op: ISetCellOperation = {\n type: \"setCell\",\n value: operationValue,\n };\n this.submitCellMessage(op, previousValue);\n }\n\n /**\n * {@inheritDoc ISharedCell.delete}\n */\n public delete() {\n // Delete the value locally.\n const previousValue = this.deleteCore();\n\n // If we are not attached, don't submit the op.\n if (!this.isAttached()) {\n return;\n }\n\n const op: IDeleteCellOperation = {\n type: \"deleteCell\",\n };\n this.submitCellMessage(op, previousValue);\n }\n\n /**\n * {@inheritDoc ISharedCell.empty}\n */\n public empty() {\n return this.data === undefined;\n }\n\n /**\n * Create a summary for the cell\n *\n * @returns the summary of the current state of the cell\n */\n protected summarizeCore(serializer: IFluidSerializer): ISummaryTreeWithStats {\n const content: ICellValue = { value: this.data };\n return createSingleBlobSummary(snapshotFileName, serializer.stringify(content, this.handle));\n }\n\n /**\n * {@inheritDoc @fluidframework/shared-object-base#SharedObject.loadCore}\n */\n protected async loadCore(storage: IChannelStorageService): Promise<void> {\n const content = await readAndParse<ICellValue>(storage, snapshotFileName);\n\n this.data = this.decode(content);\n }\n\n /**\n * Initialize a local instance of cell\n */\n protected initializeLocalCore() {\n this.data = undefined;\n }\n\n /**\n * Call back on disconnect\n */\n protected onDisconnect() { }\n\n /**\n * Apply inner op\n * @param content - ICellOperation content\n */\n private applyInnerOp(content: ICellOperation) {\n switch (content.type) {\n case \"setCell\":\n return this.setCore(this.decode(content.value));\n\n case \"deleteCell\":\n return this.deleteCore();\n\n default:\n throw new Error(\"Unknown operation\");\n }\n }\n\n /**\n * Process a cell operation\n *\n * @param message - the message to prepare\n * @param local - whether the message was sent by the local client\n * @param localOpMetadata - For local client messages, this is the metadata that was submitted with the message.\n * For messages from a remote client, this will be undefined.\n */\n protected processCore(message: ISequencedDocumentMessage, local: boolean, localOpMetadata: unknown) {\n const cellOpMetadata = localOpMetadata as ICellLocalOpMetadata;\n if (this.messageId !== this.messageIdObserved) {\n // We are waiting for an ACK on our change to this cell - we will ignore all messages until we get it.\n if (local) {\n const messageIdReceived = cellOpMetadata.pendingMessageId;\n assert(messageIdReceived !== undefined && messageIdReceived <= this.messageId,\n 0x00c /* \"messageId is incorrect from from the local client's ACK\" */);\n assert(this.pendingMessageIds !== undefined &&\n this.pendingMessageIds[0] === cellOpMetadata.pendingMessageId,\n 0x471 /* Unexpected pending message received */);\n this.pendingMessageIds.shift();\n // We got an ACK. Update messageIdObserved.\n this.messageIdObserved = cellOpMetadata.pendingMessageId;\n }\n return;\n }\n\n if (message.type === MessageType.Operation && !local) {\n const op = message.contents as ICellOperation;\n this.applyInnerOp(op);\n }\n }\n\n private setCore(value: Serializable<T>): Serializable<T> | undefined {\n const previousLocalValue = this.get();\n this.data = value;\n this.emit(\"valueChanged\", value);\n return previousLocalValue;\n }\n\n private deleteCore(): Serializable<T> | undefined {\n const previousLocalValue = this.get();\n this.data = undefined;\n this.emit(\"delete\");\n return previousLocalValue;\n }\n\n private decode(cellValue: ICellValue) {\n const value = cellValue.value;\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return this.serializer.decode(value);\n }\n\n private createLocalOpMetadata(op: ICellOperation, previousValue?: Serializable<T>): ICellLocalOpMetadata {\n const pendingMessageId = ++this.messageId;\n this.pendingMessageIds.push(pendingMessageId);\n const localMetadata: ICellLocalOpMetadata = {\n pendingMessageId, previousValue,\n };\n return localMetadata;\n }\n\n /**\n * {@inheritDoc @fluidframework/shared-object-base#SharedObjectCore.applyStashedOp}\n * @internal\n */\n protected applyStashedOp(content: unknown): unknown {\n const cellContent = content as ICellOperation;\n const previousValue = this.applyInnerOp(cellContent);\n return this.createLocalOpMetadata(cellContent, previousValue);\n }\n\n /**\n * Rollback a local op\n * @param content - The operation to rollback\n * @param localOpMetadata - The local metadata associated with the op.\n */\n protected rollback(content: any, localOpMetadata: unknown) {\n const cellOpMetadata = localOpMetadata as ICellLocalOpMetadata;\n if (content.type === \"setCell\" || content.type === \"deleteCell\") {\n if (cellOpMetadata.previousValue === undefined) {\n this.deleteCore();\n } else {\n this.setCore(cellOpMetadata.previousValue);\n }\n\n const lastPendingMessageId = this.pendingMessageIds.pop();\n if (lastPendingMessageId !== cellOpMetadata.pendingMessageId) {\n throw new Error(\"Rollback op does not match last pending\");\n }\n } else {\n throw new Error(\"Unsupported op for rollback\");\n }\n }\n\n /**\n * Submit a cell message to remote clients.\n * @param op - The cell message\n * @param previousValue - The value of the cell before this op\n */\n private submitCellMessage(op: ICellOperation, previousValue?: any): void {\n const localMetadata = this.createLocalOpMetadata(op, previousValue);\n this.submitLocalMessage(op, localMetadata);\n }\n}\n"]}
|
package/lib/interfaces.d.ts
CHANGED
package/lib/interfaces.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interfaces.d.ts","sourceRoot":"","sources":["../src/interfaces.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,oCAAoC,CAAC;AACxF,OAAO,EAAE,YAAY,EAAE,MAAM,uCAAuC,CAAC;AAErE,MAAM,WAAW,iBAAiB,CAAC,CAAC,CAAE,SAAQ,mBAAmB;IAC7D,CAAC,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC,KAAK,IAAI,OAAE;IACpE,CAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,IAAI,OAAE;CAC3C;AAED;;GAEG;AAEH,MAAM,WAAW,WAAW,CAAC,CAAC,GAAG,GAAG,CAAE,SAAQ,aAAa,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC;IAC7E;;;;OAIG;IACH,GAAG,IAAI,YAAY,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IAEnC;;;;OAIG;IACH,GAAG,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAElC;;;;OAIG;IACH,KAAK,IAAI,OAAO,CAAC;IAEjB;;OAEG;IACH,MAAM,IAAI,IAAI,CAAC;CAClB"}
|
|
1
|
+
{"version":3,"file":"interfaces.d.ts","sourceRoot":"","sources":["../src/interfaces.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,oCAAoC,CAAC;AACxF,OAAO,EAAE,YAAY,EAAE,MAAM,uCAAuC,CAAC;AAErE,MAAM,WAAW,iBAAiB,CAAC,CAAC,CAAE,SAAQ,mBAAmB;IAC7D,CAAC,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC,KAAK,IAAI,OAAE;IACpE,CAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,IAAI,OAAE;CAC3C;AAED;;GAEG;AAEH,MAAM,WAAW,WAAW,CAAC,CAAC,GAAG,GAAG,CAAE,SAAQ,aAAa,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC;IAC7E;;;;OAIG;IACH,GAAG,IAAI,YAAY,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IAEnC;;;;OAIG;IACH,GAAG,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAElC;;;;OAIG;IACH,KAAK,IAAI,OAAO,CAAC;IAEjB;;OAEG;IACH,MAAM,IAAI,IAAI,CAAC;CAClB;AACD,MAAM,WAAW,oBAAoB;IACjC,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,CAAC,EAAE,GAAG,CAAC;CACvB"}
|
package/lib/interfaces.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interfaces.js","sourceRoot":"","sources":["../src/interfaces.ts"],"names":[],"mappings":"AAAA;;;GAGG","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { ISharedObject, ISharedObjectEvents } from \"@fluidframework/shared-object-base\";\nimport { Serializable } from \"@fluidframework/datastore-definitions\";\n\nexport interface ISharedCellEvents<T> extends ISharedObjectEvents {\n (event: \"valueChanged\", listener: (value: Serializable<T>) => void);\n (event: \"delete\", listener: () => void);\n}\n\n/**\n * Shared cell interface\n */\n\nexport interface ISharedCell<T = any> extends ISharedObject<ISharedCellEvents<T>> {\n /**\n * Retrieves the cell value.\n *\n * @returns - the value of the cell\n */\n get(): Serializable<T> | undefined;\n\n /**\n * Sets the cell value.\n *\n * @param value - a JSON-able or SharedObject value to set the cell to\n */\n set(value: Serializable<T>): void;\n\n /**\n * Checks whether cell is empty or not.\n *\n * @returns - `true` if the value of cell is `undefined`, `false` otherwise\n */\n empty(): boolean;\n\n /**\n * Delete the value from the cell.\n */\n delete(): void;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"interfaces.js","sourceRoot":"","sources":["../src/interfaces.ts"],"names":[],"mappings":"AAAA;;;GAGG","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { ISharedObject, ISharedObjectEvents } from \"@fluidframework/shared-object-base\";\nimport { Serializable } from \"@fluidframework/datastore-definitions\";\n\nexport interface ISharedCellEvents<T> extends ISharedObjectEvents {\n (event: \"valueChanged\", listener: (value: Serializable<T>) => void);\n (event: \"delete\", listener: () => void);\n}\n\n/**\n * Shared cell interface\n */\n\nexport interface ISharedCell<T = any> extends ISharedObject<ISharedCellEvents<T>> {\n /**\n * Retrieves the cell value.\n *\n * @returns - the value of the cell\n */\n get(): Serializable<T> | undefined;\n\n /**\n * Sets the cell value.\n *\n * @param value - a JSON-able or SharedObject value to set the cell to\n */\n set(value: Serializable<T>): void;\n\n /**\n * Checks whether cell is empty or not.\n *\n * @returns - `true` if the value of cell is `undefined`, `false` otherwise\n */\n empty(): boolean;\n\n /**\n * Delete the value from the cell.\n */\n delete(): void;\n}\nexport interface ICellLocalOpMetadata {\n pendingMessageId: number;\n previousValue?: any;\n}\n"]}
|
package/lib/packageVersion.d.ts
CHANGED
|
@@ -5,5 +5,5 @@
|
|
|
5
5
|
* THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY
|
|
6
6
|
*/
|
|
7
7
|
export declare const pkgName = "@fluidframework/cell";
|
|
8
|
-
export declare const pkgVersion = "2.0.0-internal.2.
|
|
8
|
+
export declare const pkgVersion = "2.0.0-internal.2.2.0";
|
|
9
9
|
//# sourceMappingURL=packageVersion.d.ts.map
|
package/lib/packageVersion.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,CAAC,MAAM,OAAO,GAAG,sBAAsB,CAAC;AAC9C,MAAM,CAAC,MAAM,UAAU,GAAG,sBAAsB,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n *\n * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY\n */\n\nexport const pkgName = \"@fluidframework/cell\";\nexport const pkgVersion = \"2.0.0-internal.2.
|
|
1
|
+
{"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,CAAC,MAAM,OAAO,GAAG,sBAAsB,CAAC;AAC9C,MAAM,CAAC,MAAM,UAAU,GAAG,sBAAsB,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n *\n * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY\n */\n\nexport const pkgName = \"@fluidframework/cell\";\nexport const pkgVersion = \"2.0.0-internal.2.2.0\";\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fluidframework/cell",
|
|
3
|
-
"version": "2.0.0-internal.2.
|
|
3
|
+
"version": "2.0.0-internal.2.2.0",
|
|
4
4
|
"description": "Distributed data structure for a single value",
|
|
5
5
|
"homepage": "https://fluidframework.com",
|
|
6
6
|
"repository": {
|
|
@@ -28,8 +28,11 @@
|
|
|
28
28
|
"clean": "rimraf dist lib *.tsbuildinfo *.build.log",
|
|
29
29
|
"eslint": "eslint --format stylish src",
|
|
30
30
|
"eslint:fix": "eslint --format stylish src --fix --fix-type problem,suggestion,layout",
|
|
31
|
+
"format": "npm run prettier:fix",
|
|
31
32
|
"lint": "npm run eslint",
|
|
32
33
|
"lint:fix": "npm run eslint:fix",
|
|
34
|
+
"prettier": "prettier --check . --ignore-path ../../../.prettierignore",
|
|
35
|
+
"prettier:fix": "prettier --write . --ignore-path ../../../.prettierignore",
|
|
33
36
|
"test": "npm run test:mocha",
|
|
34
37
|
"test:coverage": "nyc npm test -- --reporter xunit --reporter-option output=nyc/junit-report.xml",
|
|
35
38
|
"test:mocha": "mocha --ignore 'dist/test/types/*' --recursive dist/test -r node_modules/@fluidframework/mocha-test-setup --unhandled-rejections=strict",
|
|
@@ -60,22 +63,22 @@
|
|
|
60
63
|
},
|
|
61
64
|
"dependencies": {
|
|
62
65
|
"@fluidframework/common-utils": "^1.0.0",
|
|
63
|
-
"@fluidframework/core-interfaces": ">=2.0.0-internal.2.
|
|
64
|
-
"@fluidframework/datastore-definitions": ">=2.0.0-internal.2.
|
|
65
|
-
"@fluidframework/driver-utils": ">=2.0.0-internal.2.
|
|
66
|
+
"@fluidframework/core-interfaces": ">=2.0.0-internal.2.2.0 <2.0.0-internal.3.0.0",
|
|
67
|
+
"@fluidframework/datastore-definitions": ">=2.0.0-internal.2.2.0 <2.0.0-internal.3.0.0",
|
|
68
|
+
"@fluidframework/driver-utils": ">=2.0.0-internal.2.2.0 <2.0.0-internal.3.0.0",
|
|
66
69
|
"@fluidframework/protocol-definitions": "^1.1.0",
|
|
67
|
-
"@fluidframework/runtime-definitions": ">=2.0.0-internal.2.
|
|
68
|
-
"@fluidframework/shared-object-base": ">=2.0.0-internal.2.
|
|
70
|
+
"@fluidframework/runtime-definitions": ">=2.0.0-internal.2.2.0 <2.0.0-internal.3.0.0",
|
|
71
|
+
"@fluidframework/shared-object-base": ">=2.0.0-internal.2.2.0 <2.0.0-internal.3.0.0"
|
|
69
72
|
},
|
|
70
73
|
"devDependencies": {
|
|
71
|
-
"@fluid-internal/test-dds-utils": ">=2.0.0-internal.2.
|
|
72
|
-
"@fluid-tools/build-cli": "^0.
|
|
74
|
+
"@fluid-internal/test-dds-utils": ">=2.0.0-internal.2.2.0 <2.0.0-internal.3.0.0",
|
|
75
|
+
"@fluid-tools/build-cli": "^0.7.0",
|
|
73
76
|
"@fluidframework/build-common": "^1.1.0",
|
|
74
|
-
"@fluidframework/build-tools": "^0.
|
|
75
|
-
"@fluidframework/cell-previous": "npm:@fluidframework/cell@2.0.0-internal.2.1.
|
|
77
|
+
"@fluidframework/build-tools": "^0.7.0",
|
|
78
|
+
"@fluidframework/cell-previous": "npm:@fluidframework/cell@2.0.0-internal.2.1.0",
|
|
76
79
|
"@fluidframework/eslint-config-fluid": "^1.2.0",
|
|
77
|
-
"@fluidframework/mocha-test-setup": ">=2.0.0-internal.2.
|
|
78
|
-
"@fluidframework/test-runtime-utils": ">=2.0.0-internal.2.
|
|
80
|
+
"@fluidframework/mocha-test-setup": ">=2.0.0-internal.2.2.0 <2.0.0-internal.3.0.0",
|
|
81
|
+
"@fluidframework/test-runtime-utils": ">=2.0.0-internal.2.2.0 <2.0.0-internal.3.0.0",
|
|
79
82
|
"@microsoft/api-extractor": "^7.22.2",
|
|
80
83
|
"@rushstack/eslint-config": "^2.5.1",
|
|
81
84
|
"@types/mocha": "^9.1.1",
|
|
@@ -86,12 +89,14 @@
|
|
|
86
89
|
"eslint": "~8.6.0",
|
|
87
90
|
"mocha": "^10.0.0",
|
|
88
91
|
"nyc": "^15.0.0",
|
|
92
|
+
"prettier": "~2.6.2",
|
|
89
93
|
"rimraf": "^2.6.2",
|
|
90
94
|
"typescript": "~4.5.5"
|
|
91
95
|
},
|
|
92
96
|
"typeValidation": {
|
|
93
|
-
"version": "2.0.0-internal.2.
|
|
94
|
-
"baselineRange": "2.0.0-internal.2.1.
|
|
97
|
+
"version": "2.0.0-internal.2.2.0",
|
|
98
|
+
"baselineRange": ">=2.0.0-internal.2.1.0 <2.0.0-internal.2.2.0",
|
|
99
|
+
"baselineVersion": "2.0.0-internal.2.1.0",
|
|
95
100
|
"broken": {}
|
|
96
101
|
}
|
|
97
102
|
}
|
package/src/cell.ts
CHANGED
|
@@ -16,7 +16,11 @@ import { ISummaryTreeWithStats } from "@fluidframework/runtime-definitions";
|
|
|
16
16
|
import { readAndParse } from "@fluidframework/driver-utils";
|
|
17
17
|
import { createSingleBlobSummary, IFluidSerializer, SharedObject } from "@fluidframework/shared-object-base";
|
|
18
18
|
import { CellFactory } from "./cellFactory";
|
|
19
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
ISharedCell,
|
|
21
|
+
ISharedCellEvents,
|
|
22
|
+
ICellLocalOpMetadata,
|
|
23
|
+
} from "./interfaces";
|
|
20
24
|
|
|
21
25
|
/**
|
|
22
26
|
* Description of a cell delta operation
|
|
@@ -124,6 +128,8 @@ export class SharedCell<T = any> extends SharedObject<ISharedCellEvents<T>>
|
|
|
124
128
|
*/
|
|
125
129
|
private messageIdObserved: number = -1;
|
|
126
130
|
|
|
131
|
+
private readonly pendingMessageIds: number[] = [];
|
|
132
|
+
|
|
127
133
|
/**
|
|
128
134
|
* Constructs a new shared cell. If the object is non-local an id and service interfaces will
|
|
129
135
|
* be provided
|
|
@@ -152,7 +158,7 @@ export class SharedCell<T = any> extends SharedObject<ISharedCellEvents<T>>
|
|
|
152
158
|
};
|
|
153
159
|
|
|
154
160
|
// Set the value locally.
|
|
155
|
-
this.setCore(value);
|
|
161
|
+
const previousValue = this.setCore(value);
|
|
156
162
|
|
|
157
163
|
// If we are not attached, don't submit the op.
|
|
158
164
|
if (!this.isAttached()) {
|
|
@@ -163,7 +169,7 @@ export class SharedCell<T = any> extends SharedObject<ISharedCellEvents<T>>
|
|
|
163
169
|
type: "setCell",
|
|
164
170
|
value: operationValue,
|
|
165
171
|
};
|
|
166
|
-
this.
|
|
172
|
+
this.submitCellMessage(op, previousValue);
|
|
167
173
|
}
|
|
168
174
|
|
|
169
175
|
/**
|
|
@@ -171,7 +177,7 @@ export class SharedCell<T = any> extends SharedObject<ISharedCellEvents<T>>
|
|
|
171
177
|
*/
|
|
172
178
|
public delete() {
|
|
173
179
|
// Delete the value locally.
|
|
174
|
-
this.deleteCore();
|
|
180
|
+
const previousValue = this.deleteCore();
|
|
175
181
|
|
|
176
182
|
// If we are not attached, don't submit the op.
|
|
177
183
|
if (!this.isAttached()) {
|
|
@@ -181,7 +187,7 @@ export class SharedCell<T = any> extends SharedObject<ISharedCellEvents<T>>
|
|
|
181
187
|
const op: IDeleteCellOperation = {
|
|
182
188
|
type: "deleteCell",
|
|
183
189
|
};
|
|
184
|
-
this.
|
|
190
|
+
this.submitCellMessage(op, previousValue);
|
|
185
191
|
}
|
|
186
192
|
|
|
187
193
|
/**
|
|
@@ -229,12 +235,10 @@ export class SharedCell<T = any> extends SharedObject<ISharedCellEvents<T>>
|
|
|
229
235
|
private applyInnerOp(content: ICellOperation) {
|
|
230
236
|
switch (content.type) {
|
|
231
237
|
case "setCell":
|
|
232
|
-
this.setCore(this.decode(content.value));
|
|
233
|
-
break;
|
|
238
|
+
return this.setCore(this.decode(content.value));
|
|
234
239
|
|
|
235
240
|
case "deleteCell":
|
|
236
|
-
this.deleteCore();
|
|
237
|
-
break;
|
|
241
|
+
return this.deleteCore();
|
|
238
242
|
|
|
239
243
|
default:
|
|
240
244
|
throw new Error("Unknown operation");
|
|
@@ -250,15 +254,19 @@ export class SharedCell<T = any> extends SharedObject<ISharedCellEvents<T>>
|
|
|
250
254
|
* For messages from a remote client, this will be undefined.
|
|
251
255
|
*/
|
|
252
256
|
protected processCore(message: ISequencedDocumentMessage, local: boolean, localOpMetadata: unknown) {
|
|
257
|
+
const cellOpMetadata = localOpMetadata as ICellLocalOpMetadata;
|
|
253
258
|
if (this.messageId !== this.messageIdObserved) {
|
|
254
259
|
// We are waiting for an ACK on our change to this cell - we will ignore all messages until we get it.
|
|
255
260
|
if (local) {
|
|
256
|
-
const messageIdReceived =
|
|
261
|
+
const messageIdReceived = cellOpMetadata.pendingMessageId;
|
|
257
262
|
assert(messageIdReceived !== undefined && messageIdReceived <= this.messageId,
|
|
258
263
|
0x00c /* "messageId is incorrect from from the local client's ACK" */);
|
|
259
|
-
|
|
264
|
+
assert(this.pendingMessageIds !== undefined &&
|
|
265
|
+
this.pendingMessageIds[0] === cellOpMetadata.pendingMessageId,
|
|
266
|
+
0x471 /* Unexpected pending message received */);
|
|
267
|
+
this.pendingMessageIds.shift();
|
|
260
268
|
// We got an ACK. Update messageIdObserved.
|
|
261
|
-
this.messageIdObserved =
|
|
269
|
+
this.messageIdObserved = cellOpMetadata.pendingMessageId;
|
|
262
270
|
}
|
|
263
271
|
return;
|
|
264
272
|
}
|
|
@@ -269,14 +277,18 @@ export class SharedCell<T = any> extends SharedObject<ISharedCellEvents<T>>
|
|
|
269
277
|
}
|
|
270
278
|
}
|
|
271
279
|
|
|
272
|
-
private setCore(value: Serializable<T>) {
|
|
280
|
+
private setCore(value: Serializable<T>): Serializable<T> | undefined {
|
|
281
|
+
const previousLocalValue = this.get();
|
|
273
282
|
this.data = value;
|
|
274
283
|
this.emit("valueChanged", value);
|
|
284
|
+
return previousLocalValue;
|
|
275
285
|
}
|
|
276
286
|
|
|
277
|
-
private deleteCore() {
|
|
287
|
+
private deleteCore(): Serializable<T> | undefined {
|
|
288
|
+
const previousLocalValue = this.get();
|
|
278
289
|
this.data = undefined;
|
|
279
290
|
this.emit("delete");
|
|
291
|
+
return previousLocalValue;
|
|
280
292
|
}
|
|
281
293
|
|
|
282
294
|
private decode(cellValue: ICellValue) {
|
|
@@ -285,14 +297,55 @@ export class SharedCell<T = any> extends SharedObject<ISharedCellEvents<T>>
|
|
|
285
297
|
return this.serializer.decode(value);
|
|
286
298
|
}
|
|
287
299
|
|
|
300
|
+
private createLocalOpMetadata(op: ICellOperation, previousValue?: Serializable<T>): ICellLocalOpMetadata {
|
|
301
|
+
const pendingMessageId = ++this.messageId;
|
|
302
|
+
this.pendingMessageIds.push(pendingMessageId);
|
|
303
|
+
const localMetadata: ICellLocalOpMetadata = {
|
|
304
|
+
pendingMessageId, previousValue,
|
|
305
|
+
};
|
|
306
|
+
return localMetadata;
|
|
307
|
+
}
|
|
308
|
+
|
|
288
309
|
/**
|
|
289
310
|
* {@inheritDoc @fluidframework/shared-object-base#SharedObjectCore.applyStashedOp}
|
|
290
311
|
* @internal
|
|
291
312
|
*/
|
|
292
313
|
protected applyStashedOp(content: unknown): unknown {
|
|
293
314
|
const cellContent = content as ICellOperation;
|
|
294
|
-
this.applyInnerOp(cellContent);
|
|
295
|
-
|
|
296
|
-
|
|
315
|
+
const previousValue = this.applyInnerOp(cellContent);
|
|
316
|
+
return this.createLocalOpMetadata(cellContent, previousValue);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Rollback a local op
|
|
321
|
+
* @param content - The operation to rollback
|
|
322
|
+
* @param localOpMetadata - The local metadata associated with the op.
|
|
323
|
+
*/
|
|
324
|
+
protected rollback(content: any, localOpMetadata: unknown) {
|
|
325
|
+
const cellOpMetadata = localOpMetadata as ICellLocalOpMetadata;
|
|
326
|
+
if (content.type === "setCell" || content.type === "deleteCell") {
|
|
327
|
+
if (cellOpMetadata.previousValue === undefined) {
|
|
328
|
+
this.deleteCore();
|
|
329
|
+
} else {
|
|
330
|
+
this.setCore(cellOpMetadata.previousValue);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
const lastPendingMessageId = this.pendingMessageIds.pop();
|
|
334
|
+
if (lastPendingMessageId !== cellOpMetadata.pendingMessageId) {
|
|
335
|
+
throw new Error("Rollback op does not match last pending");
|
|
336
|
+
}
|
|
337
|
+
} else {
|
|
338
|
+
throw new Error("Unsupported op for rollback");
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Submit a cell message to remote clients.
|
|
344
|
+
* @param op - The cell message
|
|
345
|
+
* @param previousValue - The value of the cell before this op
|
|
346
|
+
*/
|
|
347
|
+
private submitCellMessage(op: ICellOperation, previousValue?: any): void {
|
|
348
|
+
const localMetadata = this.createLocalOpMetadata(op, previousValue);
|
|
349
|
+
this.submitLocalMessage(op, localMetadata);
|
|
297
350
|
}
|
|
298
351
|
}
|
package/src/interfaces.ts
CHANGED
package/src/packageVersion.ts
CHANGED