@fluidframework/cell 2.0.0-internal.3.0.2 → 2.0.0-internal.3.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 +9 -11
- package/.mocharc.js +2 -2
- package/README.md +1 -1
- package/api-extractor.json +2 -2
- package/dist/cell.d.ts +19 -6
- package/dist/cell.d.ts.map +1 -1
- package/dist/cell.js +33 -7
- package/dist/cell.js.map +1 -1
- package/dist/cellFactory.d.ts.map +1 -1
- package/dist/cellFactory.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/interfaces.d.ts +25 -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 +19 -6
- package/lib/cell.d.ts.map +1 -1
- package/lib/cell.js +34 -8
- package/lib/cell.js.map +1 -1
- package/lib/cellFactory.d.ts.map +1 -1
- package/lib/cellFactory.js.map +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js.map +1 -1
- package/lib/interfaces.d.ts +25 -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 +46 -44
- package/prettier.config.cjs +1 -1
- package/src/cell.ts +338 -284
- package/src/cellFactory.ts +49 -48
- package/src/index.ts +6 -1
- package/src/interfaces.ts +70 -42
- package/src/packageVersion.ts +1 -1
- package/tsconfig.esnext.json +6 -6
- package/tsconfig.json +9 -13
package/src/cell.ts
CHANGED
|
@@ -6,21 +6,21 @@
|
|
|
6
6
|
import { assert } from "@fluidframework/common-utils";
|
|
7
7
|
import { ISequencedDocumentMessage, MessageType } from "@fluidframework/protocol-definitions";
|
|
8
8
|
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
IChannelAttributes,
|
|
10
|
+
IFluidDataStoreRuntime,
|
|
11
|
+
IChannelStorageService,
|
|
12
|
+
IChannelFactory,
|
|
13
|
+
Serializable,
|
|
14
14
|
} from "@fluidframework/datastore-definitions";
|
|
15
|
-
import { ISummaryTreeWithStats } from "@fluidframework/runtime-definitions";
|
|
15
|
+
import { AttributionKey, ISummaryTreeWithStats } from "@fluidframework/runtime-definitions";
|
|
16
16
|
import { readAndParse } from "@fluidframework/driver-utils";
|
|
17
|
-
import { createSingleBlobSummary, IFluidSerializer, SharedObject } from "@fluidframework/shared-object-base";
|
|
18
|
-
import { CellFactory } from "./cellFactory";
|
|
19
17
|
import {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
} from "
|
|
18
|
+
createSingleBlobSummary,
|
|
19
|
+
IFluidSerializer,
|
|
20
|
+
SharedObject,
|
|
21
|
+
} from "@fluidframework/shared-object-base";
|
|
22
|
+
import { CellFactory } from "./cellFactory";
|
|
23
|
+
import { ISharedCell, ISharedCellEvents, ICellLocalOpMetadata, ICellOptions } from "./interfaces";
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
26
|
* Description of a cell delta operation
|
|
@@ -28,19 +28,24 @@ import {
|
|
|
28
28
|
type ICellOperation = ISetCellOperation | IDeleteCellOperation;
|
|
29
29
|
|
|
30
30
|
interface ISetCellOperation {
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
type: "setCell";
|
|
32
|
+
value: ICellValue;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
interface IDeleteCellOperation {
|
|
36
|
-
|
|
36
|
+
type: "deleteCell";
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
interface ICellValue {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
/**
|
|
41
|
+
* The actual value contained in the `Cell`, which needs to be wrapped to handle `undefined`.
|
|
42
|
+
*/
|
|
43
|
+
value: unknown;
|
|
44
|
+
/**
|
|
45
|
+
* The attribution key contained in the `Cell`.
|
|
46
|
+
* @alpha
|
|
47
|
+
*/
|
|
48
|
+
attribution?: AttributionKey;
|
|
44
49
|
}
|
|
45
50
|
|
|
46
51
|
const snapshotFileName = "header";
|
|
@@ -50,269 +55,318 @@ const snapshotFileName = "header";
|
|
|
50
55
|
*/
|
|
51
56
|
// TODO: use `unknown` instead (breaking change).
|
|
52
57
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
53
|
-
export class SharedCell<T = any>
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
58
|
+
export class SharedCell<T = any>
|
|
59
|
+
extends SharedObject<ISharedCellEvents<T>>
|
|
60
|
+
implements ISharedCell<T>
|
|
61
|
+
{
|
|
62
|
+
/**
|
|
63
|
+
* Create a new `SharedCell`.
|
|
64
|
+
*
|
|
65
|
+
* @param runtime - The data store runtime to which the `SharedCell` belongs.
|
|
66
|
+
* @param id - Unique identifier for the `SharedCell`.
|
|
67
|
+
*
|
|
68
|
+
* @returns The newly create `SharedCell`. Note that it will not yet be attached.
|
|
69
|
+
*/
|
|
70
|
+
public static create(runtime: IFluidDataStoreRuntime, id?: string): SharedCell {
|
|
71
|
+
return runtime.createChannel(id, CellFactory.Type) as SharedCell;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Gets the factory for the `SharedCell` to register with the data store.
|
|
76
|
+
*
|
|
77
|
+
* @returns A factory that creates and loads `SharedCell`s.
|
|
78
|
+
*/
|
|
79
|
+
public static getFactory(): IChannelFactory {
|
|
80
|
+
return new CellFactory();
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* The data held by this cell.
|
|
85
|
+
*/
|
|
86
|
+
private data: Serializable<T> | undefined;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* This is used to assign a unique id to outgoing messages. It is used to track messages until
|
|
90
|
+
* they are ack'd.
|
|
91
|
+
*/
|
|
92
|
+
private messageId: number = -1;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* This keeps track of the messageId of messages that have been ack'd. It is updated every time
|
|
96
|
+
* we a message is ack'd with it's messageId.
|
|
97
|
+
*/
|
|
98
|
+
private messageIdObserved: number = -1;
|
|
99
|
+
|
|
100
|
+
private readonly pendingMessageIds: number[] = [];
|
|
101
|
+
|
|
102
|
+
private attribution: AttributionKey | undefined;
|
|
103
|
+
|
|
104
|
+
private readonly options: ICellOptions | undefined;
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Constructs a new `SharedCell`.
|
|
108
|
+
* If the object is non-local an id and service interfaces will be provided.
|
|
109
|
+
*
|
|
110
|
+
* @alpha
|
|
111
|
+
*
|
|
112
|
+
* @param runtime - The data store runtime to which the `SharedCell` belongs.
|
|
113
|
+
* @param id - Unique identifier for the `SharedCell`.
|
|
114
|
+
*/
|
|
115
|
+
// eslint-disable-next-line @typescript-eslint/explicit-member-accessibility
|
|
116
|
+
constructor(id: string, runtime: IFluidDataStoreRuntime, attributes: IChannelAttributes) {
|
|
117
|
+
super(id, runtime, attributes, "fluid_cell_");
|
|
118
|
+
|
|
119
|
+
this.options = runtime.options as ICellOptions;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* {@inheritDoc ISharedCell.get}
|
|
124
|
+
*/
|
|
125
|
+
public get(): Serializable<T> | undefined {
|
|
126
|
+
return this.data;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* {@inheritDoc ISharedCell.set}
|
|
131
|
+
*/
|
|
132
|
+
public set(value: Serializable<T>): void {
|
|
133
|
+
// Serialize the value if required.
|
|
134
|
+
const operationValue: ICellValue = {
|
|
135
|
+
value: this.serializer.encode(value, this.handle),
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
// Set the value locally.
|
|
139
|
+
const previousValue = this.setCore(value);
|
|
140
|
+
|
|
141
|
+
// If we are not attached, don't submit the op.
|
|
142
|
+
if (!this.isAttached()) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const op: ISetCellOperation = {
|
|
147
|
+
type: "setCell",
|
|
148
|
+
value: operationValue,
|
|
149
|
+
};
|
|
150
|
+
this.submitCellMessage(op, previousValue);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* {@inheritDoc ISharedCell.delete}
|
|
155
|
+
*/
|
|
156
|
+
public delete(): void {
|
|
157
|
+
// Delete the value locally.
|
|
158
|
+
const previousValue = this.deleteCore();
|
|
159
|
+
|
|
160
|
+
// If we are not attached, don't submit the op.
|
|
161
|
+
if (!this.isAttached()) {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const op: IDeleteCellOperation = {
|
|
166
|
+
type: "deleteCell",
|
|
167
|
+
};
|
|
168
|
+
this.submitCellMessage(op, previousValue);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* {@inheritDoc ISharedCell.empty}
|
|
173
|
+
*/
|
|
174
|
+
public empty(): boolean {
|
|
175
|
+
return this.data === undefined;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* {@inheritDoc ISharedCell.getAttribution}
|
|
180
|
+
* @alpha
|
|
181
|
+
*/
|
|
182
|
+
public getAttribution(): AttributionKey | undefined {
|
|
183
|
+
return this.attribution;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Set the attribution through the SequencedDocumentMessage
|
|
188
|
+
*/
|
|
189
|
+
private setAttribution(message: ISequencedDocumentMessage): void {
|
|
190
|
+
if (this.options?.attribution?.track ?? false) {
|
|
191
|
+
this.attribution = { type: "op", seq: message.sequenceNumber };
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Creates a summary for the Cell.
|
|
197
|
+
*
|
|
198
|
+
* @returns The summary of the current state of the Cell.
|
|
199
|
+
*/
|
|
200
|
+
protected summarizeCore(serializer: IFluidSerializer): ISummaryTreeWithStats {
|
|
201
|
+
const content: ICellValue = { value: this.data, attribution: this.attribution };
|
|
202
|
+
return createSingleBlobSummary(
|
|
203
|
+
snapshotFileName,
|
|
204
|
+
serializer.stringify(content, this.handle),
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* {@inheritDoc @fluidframework/shared-object-base#SharedObject.loadCore}
|
|
210
|
+
*/
|
|
211
|
+
protected async loadCore(storage: IChannelStorageService): Promise<void> {
|
|
212
|
+
const content = await readAndParse<ICellValue>(storage, snapshotFileName);
|
|
213
|
+
|
|
214
|
+
this.data = this.decode(content);
|
|
215
|
+
this.attribution = content.attribution;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Initialize a local instance of cell.
|
|
220
|
+
*/
|
|
221
|
+
protected initializeLocalCore(): void {
|
|
222
|
+
this.data = undefined;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Call back on disconnect.
|
|
227
|
+
*/
|
|
228
|
+
protected onDisconnect(): void {}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Apply inner op.
|
|
232
|
+
*
|
|
233
|
+
* @param content - ICellOperation content
|
|
234
|
+
*/
|
|
235
|
+
private applyInnerOp(content: ICellOperation): Serializable<T> | undefined {
|
|
236
|
+
switch (content.type) {
|
|
237
|
+
case "setCell":
|
|
238
|
+
return this.setCore(this.decode(content.value));
|
|
239
|
+
|
|
240
|
+
case "deleteCell":
|
|
241
|
+
return this.deleteCore();
|
|
242
|
+
|
|
243
|
+
default:
|
|
244
|
+
throw new Error("Unknown operation");
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Process a cell operation (op).
|
|
250
|
+
*
|
|
251
|
+
* @param message - The message to prepare.
|
|
252
|
+
* @param local - Whether or not the message was sent by the local client.
|
|
253
|
+
* @param localOpMetadata - For local client messages, this is the metadata that was submitted with the message.
|
|
254
|
+
* For messages from a remote client, this will be `undefined`.
|
|
255
|
+
*/
|
|
256
|
+
protected processCore(
|
|
257
|
+
message: ISequencedDocumentMessage,
|
|
258
|
+
local: boolean,
|
|
259
|
+
localOpMetadata: unknown,
|
|
260
|
+
): void {
|
|
261
|
+
const cellOpMetadata = localOpMetadata as ICellLocalOpMetadata;
|
|
262
|
+
if (this.messageId !== this.messageIdObserved) {
|
|
263
|
+
// We are waiting for an ACK on our change to this cell - we will ignore all messages until we get it.
|
|
264
|
+
if (local) {
|
|
265
|
+
const messageIdReceived = cellOpMetadata.pendingMessageId;
|
|
266
|
+
assert(
|
|
267
|
+
messageIdReceived !== undefined && messageIdReceived <= this.messageId,
|
|
268
|
+
0x00c /* "messageId is incorrect from from the local client's ACK" */,
|
|
269
|
+
);
|
|
270
|
+
assert(
|
|
271
|
+
this.pendingMessageIds !== undefined &&
|
|
272
|
+
this.pendingMessageIds[0] === cellOpMetadata.pendingMessageId,
|
|
273
|
+
0x471 /* Unexpected pending message received */,
|
|
274
|
+
);
|
|
275
|
+
this.pendingMessageIds.shift();
|
|
276
|
+
// We got an ACK. Update messageIdObserved.
|
|
277
|
+
this.messageIdObserved = cellOpMetadata.pendingMessageId;
|
|
278
|
+
// update the attributor
|
|
279
|
+
this.setAttribution(message);
|
|
280
|
+
}
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
if (message.type === MessageType.Operation && !local) {
|
|
285
|
+
const op = message.contents as ICellOperation;
|
|
286
|
+
// update the attributor
|
|
287
|
+
this.setAttribution(message);
|
|
288
|
+
this.applyInnerOp(op);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
private setCore(value: Serializable<T>): Serializable<T> | undefined {
|
|
293
|
+
const previousLocalValue = this.get();
|
|
294
|
+
this.data = value;
|
|
295
|
+
this.emit("valueChanged", value);
|
|
296
|
+
return previousLocalValue;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
private deleteCore(): Serializable<T> | undefined {
|
|
300
|
+
const previousLocalValue = this.get();
|
|
301
|
+
this.data = undefined;
|
|
302
|
+
this.emit("delete");
|
|
303
|
+
return previousLocalValue;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
private decode(cellValue: ICellValue): Serializable<T> {
|
|
307
|
+
const value = cellValue.value;
|
|
308
|
+
return this.serializer.decode(value) as Serializable<T>;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
private createLocalOpMetadata(
|
|
312
|
+
op: ICellOperation,
|
|
313
|
+
previousValue?: Serializable<T>,
|
|
314
|
+
): ICellLocalOpMetadata {
|
|
315
|
+
const pendingMessageId = ++this.messageId;
|
|
316
|
+
this.pendingMessageIds.push(pendingMessageId);
|
|
317
|
+
const localMetadata: ICellLocalOpMetadata = {
|
|
318
|
+
pendingMessageId,
|
|
319
|
+
previousValue,
|
|
320
|
+
};
|
|
321
|
+
return localMetadata;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* {@inheritDoc @fluidframework/shared-object-base#SharedObjectCore.applyStashedOp}
|
|
326
|
+
*
|
|
327
|
+
* @internal
|
|
328
|
+
*/
|
|
329
|
+
protected applyStashedOp(content: unknown): unknown {
|
|
330
|
+
const cellContent = content as ICellOperation;
|
|
331
|
+
const previousValue = this.applyInnerOp(cellContent);
|
|
332
|
+
return this.createLocalOpMetadata(cellContent, previousValue);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Rollback a local op.
|
|
337
|
+
*
|
|
338
|
+
* @param content - The operation to rollback.
|
|
339
|
+
* @param localOpMetadata - The local metadata associated with the op.
|
|
340
|
+
*/
|
|
341
|
+
// TODO: use `unknown` instead (breaking change).
|
|
342
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
|
|
343
|
+
protected rollback(content: any, localOpMetadata: unknown): void {
|
|
344
|
+
const cellOpMetadata = localOpMetadata as ICellLocalOpMetadata;
|
|
345
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
346
|
+
if (content.type === "setCell" || content.type === "deleteCell") {
|
|
347
|
+
if (cellOpMetadata.previousValue === undefined) {
|
|
348
|
+
this.deleteCore();
|
|
349
|
+
} else {
|
|
350
|
+
this.setCore(cellOpMetadata.previousValue as Serializable<T>);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
const lastPendingMessageId = this.pendingMessageIds.pop();
|
|
354
|
+
if (lastPendingMessageId !== cellOpMetadata.pendingMessageId) {
|
|
355
|
+
throw new Error("Rollback op does not match last pending");
|
|
356
|
+
}
|
|
357
|
+
} else {
|
|
358
|
+
throw new Error("Unsupported op for rollback");
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Submit a cell message to remote clients.
|
|
364
|
+
*
|
|
365
|
+
* @param op - The cell message.
|
|
366
|
+
* @param previousValue - The value of the cell before this op.
|
|
367
|
+
*/
|
|
368
|
+
private submitCellMessage(op: ICellOperation, previousValue?: Serializable<T>): void {
|
|
369
|
+
const localMetadata = this.createLocalOpMetadata(op, previousValue);
|
|
370
|
+
this.submitLocalMessage(op, localMetadata);
|
|
371
|
+
}
|
|
318
372
|
}
|