@fluidframework/replay-driver 2.0.0-internal.3.0.5 → 2.0.0-internal.3.1.1
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 +5 -7
- package/api-extractor.json +2 -2
- package/dist/emptyDeltaStorageService.d.ts.map +1 -1
- package/dist/emptyDeltaStorageService.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/dist/replayController.d.ts.map +1 -1
- package/dist/replayController.js.map +1 -1
- package/dist/replayDocumentDeltaConnection.d.ts.map +1 -1
- package/dist/replayDocumentDeltaConnection.js +12 -9
- package/dist/replayDocumentDeltaConnection.js.map +1 -1
- package/dist/replayDocumentService.d.ts.map +1 -1
- package/dist/replayDocumentService.js.map +1 -1
- package/dist/replayDocumentServiceFactory.d.ts.map +1 -1
- package/dist/replayDocumentServiceFactory.js.map +1 -1
- package/dist/storageImplementations.d.ts.map +1 -1
- package/dist/storageImplementations.js.map +1 -1
- package/lib/emptyDeltaStorageService.d.ts.map +1 -1
- package/lib/emptyDeltaStorageService.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/lib/replayController.d.ts.map +1 -1
- package/lib/replayController.js.map +1 -1
- package/lib/replayDocumentDeltaConnection.d.ts.map +1 -1
- package/lib/replayDocumentDeltaConnection.js +12 -9
- package/lib/replayDocumentDeltaConnection.js.map +1 -1
- package/lib/replayDocumentService.d.ts.map +1 -1
- package/lib/replayDocumentService.js.map +1 -1
- package/lib/replayDocumentServiceFactory.d.ts.map +1 -1
- package/lib/replayDocumentServiceFactory.js.map +1 -1
- package/lib/storageImplementations.d.ts.map +1 -1
- package/lib/storageImplementations.js.map +1 -1
- package/package.json +71 -70
- package/prettier.config.cjs +1 -1
- package/src/emptyDeltaStorageService.ts +14 -13
- package/src/packageVersion.ts +1 -1
- package/src/replayController.ts +59 -55
- package/src/replayDocumentDeltaConnection.ts +323 -310
- package/src/replayDocumentService.ts +46 -44
- package/src/replayDocumentServiceFactory.ts +58 -44
- package/src/storageImplementations.ts +154 -150
- package/tsconfig.esnext.json +6 -6
- package/tsconfig.json +8 -13
|
@@ -5,22 +5,22 @@
|
|
|
5
5
|
|
|
6
6
|
import { IDisposable } from "@fluidframework/common-definitions";
|
|
7
7
|
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
IDocumentDeltaConnection,
|
|
9
|
+
IDocumentDeltaStorageService,
|
|
10
|
+
IDocumentDeltaConnectionEvents,
|
|
11
|
+
IDocumentService,
|
|
12
12
|
} from "@fluidframework/driver-definitions";
|
|
13
13
|
import {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
14
|
+
ConnectionMode,
|
|
15
|
+
IClientConfiguration,
|
|
16
|
+
IConnected,
|
|
17
|
+
IDocumentMessage,
|
|
18
|
+
ISequencedDocumentMessage,
|
|
19
|
+
ISignalClient,
|
|
20
|
+
ISignalMessage,
|
|
21
|
+
ITokenClaims,
|
|
22
|
+
IVersion,
|
|
23
|
+
ScopeType,
|
|
24
24
|
} from "@fluidframework/protocol-definitions";
|
|
25
25
|
import { delay, TypedEventEmitter } from "@fluidframework/common-utils";
|
|
26
26
|
import { ReplayController } from "./replayController";
|
|
@@ -28,303 +28,316 @@ import { ReplayController } from "./replayController";
|
|
|
28
28
|
const ReplayDocumentId = "documentId";
|
|
29
29
|
|
|
30
30
|
export class ReplayControllerStatic extends ReplayController {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
|
|
31
|
+
private static readonly DelayInterval = 50;
|
|
32
|
+
private static readonly ReplayResolution = 15;
|
|
33
|
+
|
|
34
|
+
private firstTimeStamp: number | undefined;
|
|
35
|
+
private replayCurrent = 0;
|
|
36
|
+
// Simulated delay interval for emitting the ops
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Helper class
|
|
40
|
+
*
|
|
41
|
+
* @param replayFrom - First op to be played on socket.
|
|
42
|
+
* @param replayTo - Last op number to be played on socket.
|
|
43
|
+
* @param unitIsTime - True is user want to play ops that are within a replay resolution window.
|
|
44
|
+
*/
|
|
45
|
+
public constructor(
|
|
46
|
+
public readonly replayFrom: number,
|
|
47
|
+
public readonly replayTo: number,
|
|
48
|
+
public readonly unitIsTime?: boolean,
|
|
49
|
+
) {
|
|
50
|
+
super();
|
|
51
|
+
if (unitIsTime !== true) {
|
|
52
|
+
// There is no code in here to start with snapshot, thus we have to start with op #0.
|
|
53
|
+
this.replayTo = 0;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
public async initStorage(documentService: IDocumentService) {
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
public async getVersions(versionId: string | null, count: number): Promise<IVersion[]> {
|
|
62
|
+
return [];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
public async getSnapshotTree(version?: IVersion) {
|
|
66
|
+
return version ? Promise.reject(new Error("Invalid operation")) : null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
public async readBlob(blobId: string): Promise<ArrayBufferLike> {
|
|
70
|
+
return Promise.reject(new Error("Invalid operation"));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
public async getStartingOpSequence(): Promise<number> {
|
|
74
|
+
return 0;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
public fetchTo(currentOp: number) {
|
|
78
|
+
if (!(this.unitIsTime !== true && this.replayTo >= 0)) {
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
return this.replayTo;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
public isDoneFetch(currentOp: number, lastTimeStamp?: number) {
|
|
85
|
+
if (this.replayTo >= 0) {
|
|
86
|
+
if (this.unitIsTime === true) {
|
|
87
|
+
return (
|
|
88
|
+
lastTimeStamp !== undefined &&
|
|
89
|
+
this.firstTimeStamp !== undefined &&
|
|
90
|
+
lastTimeStamp - this.firstTimeStamp >= this.replayTo
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
return currentOp >= this.replayTo;
|
|
94
|
+
}
|
|
95
|
+
return lastTimeStamp === undefined; // No more ops
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
public skipToIndex(fetchedOps: ISequencedDocumentMessage[]) {
|
|
99
|
+
if (this.replayFrom <= 0) {
|
|
100
|
+
return 0;
|
|
101
|
+
}
|
|
102
|
+
if (this.unitIsTime === true) {
|
|
103
|
+
for (let i = 0; i < fetchedOps.length; i += 1) {
|
|
104
|
+
const timeStamp = fetchedOps[i].timestamp;
|
|
105
|
+
if (timeStamp !== undefined) {
|
|
106
|
+
if (this.firstTimeStamp === undefined) {
|
|
107
|
+
this.firstTimeStamp = timeStamp;
|
|
108
|
+
}
|
|
109
|
+
if (timeStamp - this.firstTimeStamp >= this.replayFrom) {
|
|
110
|
+
return i;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
} else if (this.replayFrom > this.replayCurrent) {
|
|
115
|
+
return this.replayFrom - this.replayCurrent;
|
|
116
|
+
}
|
|
117
|
+
return 0;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
public async replay(
|
|
121
|
+
emitter: (op: ISequencedDocumentMessage[]) => void,
|
|
122
|
+
fetchedOps: ISequencedDocumentMessage[],
|
|
123
|
+
): Promise<void> {
|
|
124
|
+
let current = this.skipToIndex(fetchedOps);
|
|
125
|
+
|
|
126
|
+
return new Promise((resolve) => {
|
|
127
|
+
const replayNextOps = () => {
|
|
128
|
+
// Emit the ops from replay to the end every "deltainterval" milliseconds
|
|
129
|
+
// to simulate the socket stream
|
|
130
|
+
const currentOp = fetchedOps[current];
|
|
131
|
+
const playbackOps = [currentOp];
|
|
132
|
+
let nextInterval = ReplayControllerStatic.DelayInterval;
|
|
133
|
+
current += 1;
|
|
134
|
+
|
|
135
|
+
if (this.unitIsTime === true) {
|
|
136
|
+
const currentTimeStamp = currentOp.timestamp;
|
|
137
|
+
if (currentTimeStamp !== undefined) {
|
|
138
|
+
// Emit more ops that is in the ReplayResolution window
|
|
139
|
+
|
|
140
|
+
while (current < fetchedOps.length) {
|
|
141
|
+
const op = fetchedOps[current];
|
|
142
|
+
if (op.timestamp === undefined) {
|
|
143
|
+
// Missing timestamp, just delay the standard amount of time
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
const timeDiff = op.timestamp - currentTimeStamp;
|
|
147
|
+
if (timeDiff >= ReplayControllerStatic.ReplayResolution) {
|
|
148
|
+
// Time exceeded the resolution window, break out the loop
|
|
149
|
+
// and delay for the time difference.
|
|
150
|
+
nextInterval = timeDiff;
|
|
151
|
+
break;
|
|
152
|
+
}
|
|
153
|
+
if (timeDiff < 0) {
|
|
154
|
+
// Time have regressed, just delay the standard amount of time
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// The op is within the ReplayResolution emit it now
|
|
159
|
+
playbackOps.push(op);
|
|
160
|
+
current += 1;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (
|
|
164
|
+
this.firstTimeStamp !== undefined &&
|
|
165
|
+
this.replayTo >= 0 &&
|
|
166
|
+
currentTimeStamp + nextInterval - this.firstTimeStamp > this.replayTo
|
|
167
|
+
) {
|
|
168
|
+
nextInterval = -1;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
scheduleNext(nextInterval);
|
|
173
|
+
emitter(playbackOps);
|
|
174
|
+
};
|
|
175
|
+
const scheduleNext = (nextInterval: number) => {
|
|
176
|
+
if (nextInterval >= 0 && current < fetchedOps.length) {
|
|
177
|
+
setTimeout(replayNextOps, nextInterval);
|
|
178
|
+
} else {
|
|
179
|
+
this.replayCurrent += current;
|
|
180
|
+
resolve();
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
scheduleNext(ReplayControllerStatic.DelayInterval);
|
|
184
|
+
});
|
|
185
|
+
}
|
|
181
186
|
}
|
|
182
187
|
|
|
183
188
|
export class ReplayDocumentDeltaConnection
|
|
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
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
189
|
+
extends TypedEventEmitter<IDocumentDeltaConnectionEvents>
|
|
190
|
+
implements IDocumentDeltaConnection, IDisposable
|
|
191
|
+
{
|
|
192
|
+
/**
|
|
193
|
+
* Creates a new delta connection and mimics the delta connection to replay ops on it.
|
|
194
|
+
* @param documentService - The document service to be used to get underlying endpoints.
|
|
195
|
+
*/
|
|
196
|
+
public static create(
|
|
197
|
+
documentStorageService: IDocumentDeltaStorageService,
|
|
198
|
+
controller: ReplayController,
|
|
199
|
+
): IDocumentDeltaConnection {
|
|
200
|
+
const connection: IConnected = {
|
|
201
|
+
claims: ReplayDocumentDeltaConnection.claims,
|
|
202
|
+
clientId: "PseudoClientId",
|
|
203
|
+
existing: true,
|
|
204
|
+
initialMessages: [],
|
|
205
|
+
initialSignals: [],
|
|
206
|
+
initialClients: [],
|
|
207
|
+
maxMessageSize: ReplayDocumentDeltaConnection.ReplayMaxMessageSize,
|
|
208
|
+
mode: "read",
|
|
209
|
+
serviceConfiguration: {
|
|
210
|
+
blockSize: 64436,
|
|
211
|
+
maxMessageSize: ReplayDocumentDeltaConnection.ReplayMaxMessageSize,
|
|
212
|
+
},
|
|
213
|
+
supportedVersions: [ReplayDocumentDeltaConnection.replayProtocolVersion],
|
|
214
|
+
version: ReplayDocumentDeltaConnection.replayProtocolVersion,
|
|
215
|
+
};
|
|
216
|
+
const deltaConnection = new ReplayDocumentDeltaConnection(connection);
|
|
217
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
218
|
+
deltaConnection.fetchAndEmitOps(documentStorageService, controller);
|
|
219
|
+
|
|
220
|
+
return deltaConnection;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
private static readonly replayProtocolVersion = "^0.1.0";
|
|
224
|
+
// Since the replay service never actually sends messages the size below is arbitrary
|
|
225
|
+
private static readonly ReplayMaxMessageSize = 16 * 1024;
|
|
226
|
+
|
|
227
|
+
private static readonly claims: ITokenClaims = {
|
|
228
|
+
documentId: ReplayDocumentId,
|
|
229
|
+
scopes: [ScopeType.DocRead],
|
|
230
|
+
tenantId: "",
|
|
231
|
+
user: {
|
|
232
|
+
id: "",
|
|
233
|
+
},
|
|
234
|
+
iat: Math.round(new Date().getTime() / 1000),
|
|
235
|
+
exp: Math.round(new Date().getTime() / 1000) + 60 * 60, // 1 hour expiration
|
|
236
|
+
ver: "1.0",
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
public get clientId(): string {
|
|
240
|
+
return this.details.clientId;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
public get mode(): ConnectionMode {
|
|
244
|
+
return this.details.mode;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
public get claims(): ITokenClaims {
|
|
248
|
+
return this.details.claims;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
public get existing(): boolean {
|
|
252
|
+
return this.details.existing;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
public get version(): string {
|
|
256
|
+
return this.details.version;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
public get initialMessages(): ISequencedDocumentMessage[] {
|
|
260
|
+
return this.details.initialMessages;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
public get initialSignals(): ISignalMessage[] {
|
|
264
|
+
return this.details.initialSignals;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
public get initialClients(): ISignalClient[] {
|
|
268
|
+
return this.details.initialClients;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
public get serviceConfiguration(): IClientConfiguration {
|
|
272
|
+
return this.details.serviceConfiguration;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
public readonly maxMessageSize = ReplayDocumentDeltaConnection.ReplayMaxMessageSize;
|
|
276
|
+
|
|
277
|
+
constructor(public details: IConnected) {
|
|
278
|
+
super();
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
public submit(documentMessage: IDocumentMessage[]): void {
|
|
282
|
+
// ReplayDocumentDeltaConnection.submit() can't be called - client never sees its own join on,
|
|
283
|
+
// and thus can never move to sending ops.
|
|
284
|
+
throw new Error("ReplayDocumentDeltaConnection.submit() can't be called");
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
public async submitSignal(message: any) {}
|
|
288
|
+
|
|
289
|
+
private _disposed = false;
|
|
290
|
+
public get disposed() {
|
|
291
|
+
return this._disposed;
|
|
292
|
+
}
|
|
293
|
+
public dispose() {
|
|
294
|
+
this._disposed = true;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* This gets the specified ops from the delta storage endpoint and replays them in the replayer.
|
|
299
|
+
*/
|
|
300
|
+
private async fetchAndEmitOps(
|
|
301
|
+
documentStorageService: IDocumentDeltaStorageService,
|
|
302
|
+
controller: ReplayController,
|
|
303
|
+
): Promise<void> {
|
|
304
|
+
let done;
|
|
305
|
+
let replayPromiseChain = Promise.resolve();
|
|
306
|
+
|
|
307
|
+
let currentOp = await controller.getStartingOpSequence();
|
|
308
|
+
|
|
309
|
+
do {
|
|
310
|
+
const fetchTo = controller.fetchTo(currentOp);
|
|
311
|
+
|
|
312
|
+
const abortController = new AbortController();
|
|
313
|
+
const stream = documentStorageService.fetchMessages(
|
|
314
|
+
currentOp + 1,
|
|
315
|
+
fetchTo,
|
|
316
|
+
abortController.signal,
|
|
317
|
+
);
|
|
318
|
+
do {
|
|
319
|
+
const result = await stream.read();
|
|
320
|
+
|
|
321
|
+
if (result.done) {
|
|
322
|
+
// No more ops. But, they can show up later, either because document was just created,
|
|
323
|
+
// or because another client keeps submitting new ops.
|
|
324
|
+
done = controller.isDoneFetch(currentOp, undefined);
|
|
325
|
+
if (!done) {
|
|
326
|
+
await delay(2000);
|
|
327
|
+
}
|
|
328
|
+
break;
|
|
329
|
+
}
|
|
330
|
+
replayPromiseChain = replayPromiseChain.then(async () =>
|
|
331
|
+
controller.replay((ops) => this.emit("op", ReplayDocumentId, ops), messages),
|
|
332
|
+
);
|
|
333
|
+
|
|
334
|
+
const messages = result.value;
|
|
335
|
+
currentOp += messages.length;
|
|
336
|
+
done = controller.isDoneFetch(currentOp, messages[messages.length - 1].timestamp);
|
|
337
|
+
} while (!done);
|
|
338
|
+
|
|
339
|
+
abortController.abort();
|
|
340
|
+
} while (!done);
|
|
341
|
+
return replayPromiseChain;
|
|
342
|
+
}
|
|
330
343
|
}
|