@atlaskit/collab-provider 8.0.1 → 8.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.
Files changed (64) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/cjs/analytics/index.js +2 -11
  3. package/dist/cjs/analytics/performance.js +2 -16
  4. package/dist/cjs/channel.js +66 -115
  5. package/dist/cjs/disconnected-reason-mapper.js +0 -2
  6. package/dist/cjs/emitter.js +3 -11
  7. package/dist/cjs/error-code-mapper.js +4 -17
  8. package/dist/cjs/feature-flags/__test__/index.unit.js +27 -0
  9. package/dist/cjs/feature-flags/index.js +52 -0
  10. package/dist/cjs/feature-flags/types.js +12 -0
  11. package/dist/cjs/helpers/const.js +6 -10
  12. package/dist/cjs/helpers/utils.js +0 -12
  13. package/dist/cjs/index.js +0 -1
  14. package/dist/cjs/provider/catchup.js +38 -42
  15. package/dist/cjs/provider/index.js +153 -283
  16. package/dist/cjs/socket-io-provider.js +2 -12
  17. package/dist/cjs/types.js +0 -1
  18. package/dist/cjs/version-wrapper.js +1 -3
  19. package/dist/cjs/version.json +1 -1
  20. package/dist/es2019/analytics/index.js +2 -3
  21. package/dist/es2019/analytics/performance.js +2 -13
  22. package/dist/es2019/channel.js +57 -65
  23. package/dist/es2019/disconnected-reason-mapper.js +1 -2
  24. package/dist/es2019/emitter.js +3 -8
  25. package/dist/es2019/error-code-mapper.js +4 -12
  26. package/dist/es2019/feature-flags/__test__/index.unit.js +25 -0
  27. package/dist/es2019/feature-flags/index.js +31 -0
  28. package/dist/es2019/feature-flags/types.js +5 -0
  29. package/dist/es2019/helpers/const.js +4 -9
  30. package/dist/es2019/helpers/utils.js +0 -2
  31. package/dist/es2019/provider/catchup.js +33 -17
  32. package/dist/es2019/provider/index.js +119 -189
  33. package/dist/es2019/socket-io-provider.js +4 -2
  34. package/dist/es2019/types.js +1 -1
  35. package/dist/es2019/version-wrapper.js +1 -1
  36. package/dist/es2019/version.json +1 -1
  37. package/dist/esm/analytics/index.js +2 -6
  38. package/dist/esm/analytics/performance.js +2 -13
  39. package/dist/esm/channel.js +68 -108
  40. package/dist/esm/disconnected-reason-mapper.js +1 -2
  41. package/dist/esm/emitter.js +3 -6
  42. package/dist/esm/error-code-mapper.js +4 -12
  43. package/dist/esm/feature-flags/__test__/index.unit.js +25 -0
  44. package/dist/esm/feature-flags/index.js +43 -0
  45. package/dist/esm/feature-flags/types.js +5 -0
  46. package/dist/esm/helpers/const.js +4 -9
  47. package/dist/esm/helpers/utils.js +0 -3
  48. package/dist/esm/provider/catchup.js +38 -35
  49. package/dist/esm/provider/index.js +153 -285
  50. package/dist/esm/socket-io-provider.js +2 -5
  51. package/dist/esm/types.js +1 -1
  52. package/dist/esm/version-wrapper.js +1 -1
  53. package/dist/esm/version.json +1 -1
  54. package/dist/types/channel.d.ts +1 -0
  55. package/dist/types/error-code-mapper.d.ts +1 -1
  56. package/dist/types/feature-flags/__test__/index.unit.d.ts +1 -0
  57. package/dist/types/feature-flags/index.d.ts +9 -0
  58. package/dist/types/feature-flags/types.d.ts +11 -0
  59. package/dist/types/helpers/const.d.ts +19 -3
  60. package/dist/types/provider/catchup.d.ts +1 -1
  61. package/dist/types/provider/index.d.ts +1 -0
  62. package/dist/types/types.d.ts +9 -4
  63. package/package.json +10 -9
  64. package/report.api.md +11 -1
@@ -1,5 +1,5 @@
1
1
  import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
- import { getVersion, sendableSteps } from 'prosemirror-collab';
2
+ import { getVersion, sendableSteps } from '@atlaskit/prosemirror-collab';
3
3
  import throttle from 'lodash/throttle';
4
4
  import isEqual from 'lodash/isEqual';
5
5
  import countBy from 'lodash/countBy';
@@ -17,19 +17,12 @@ import { DisconnectReason, socketIOReasons } from '../disconnected-reason-mapper
17
17
  import { MEASURE_NAME, startMeasure, stopMeasure } from '../analytics/performance';
18
18
  const logger = createLogger('Provider', 'black');
19
19
  const PARTICIPANT_UPDATE_INTERVAL = 300 * 1000; // 300 seconds
20
-
21
20
  const SEND_PRESENCE_INTERVAL = 150 * 1000; // 150 seconds
22
-
23
21
  const SEND_STEPS_THROTTLE = 500; // 0.5 second
24
-
25
22
  export const CATCHUP_THROTTLE = 1 * 1000; // 1 second
26
-
27
23
  const OUT_OF_SYNC_PERIOD = 3 * 1000; // 3 seconds
28
-
29
24
  const noop = () => {};
30
-
31
25
  export const MAX_STEP_REJECTED_ERROR = 15;
32
-
33
26
  const commitStep = ({
34
27
  channel,
35
28
  steps,
@@ -41,7 +34,8 @@ const commitStep = ({
41
34
  onStepsAdded,
42
35
  onErrorHandled
43
36
  }) => {
44
- const stepsWithClientAndUserId = steps.map(step => ({ ...step.toJSON(),
37
+ const stepsWithClientAndUserId = steps.map(step => ({
38
+ ...step.toJSON(),
45
39
  clientId,
46
40
  userId
47
41
  }));
@@ -52,7 +46,6 @@ const commitStep = ({
52
46
  userId
53
47
  }, response => {
54
48
  const latency = new Date().getTime() - start;
55
-
56
49
  if (response.type === AcknowledgementResponseTypes.SUCCESS) {
57
50
  onStepsAdded({
58
51
  steps: stepsWithClientAndUserId,
@@ -71,7 +64,6 @@ const commitStep = ({
71
64
  triggerAnalyticsEvent(analyticStepEvent, analyticsClient);
72
65
  } else if (response.type === AcknowledgementResponseTypes.ERROR) {
73
66
  var _response$error, _response$error$data, _response$error2, _response$error2$data;
74
-
75
67
  onErrorHandled(response.error, true);
76
68
  triggerAnalyticsEvent({
77
69
  eventAction: EVENT_ACTION.ADD_STEPS,
@@ -102,41 +94,39 @@ const commitStep = ({
102
94
  }
103
95
  });
104
96
  };
105
-
106
97
  const throttledCommitStep = throttle(commitStep, SEND_STEPS_THROTTLE, {
107
98
  leading: false,
108
99
  trailing: true
109
100
  });
110
101
  export class Provider extends Emitter {
111
102
  // To keep track of the namespace event changes from the server.
103
+
112
104
  constructor(config) {
113
105
  super();
114
-
115
106
  _defineProperty(this, "participants", new Map());
116
-
117
107
  _defineProperty(this, "metadata", {});
118
-
119
108
  _defineProperty(this, "stepRejectCounter", 0);
120
-
121
109
  _defineProperty(this, "isChannelInitialized", false);
122
-
123
110
  _defineProperty(this, "isNamespaceLocked", false);
124
-
125
111
  _defineProperty(this, "initializeChannel", () => {
112
+ this.emit('connecting', {
113
+ initial: true
114
+ });
126
115
  this.channel.on('connected', ({
127
116
  sid,
128
117
  initialized
129
118
  }) => {
130
119
  this.sessionId = sid;
131
120
  this.emit('connected', {
132
- sid
133
- }); // If already initialized, `connected` means reconnected
134
-
135
- if (initialized && this.disconnectedAt && // Offline longer than `OUT_OF_SYNC_PERIOD`
121
+ sid,
122
+ initial: !initialized
123
+ });
124
+ // If already initialized, `connected` means reconnected
125
+ if (initialized && this.disconnectedAt &&
126
+ // Offline longer than `OUT_OF_SYNC_PERIOD`
136
127
  Date.now() - this.disconnectedAt >= OUT_OF_SYNC_PERIOD) {
137
128
  this.throttledCatchup();
138
129
  }
139
-
140
130
  this.disconnectedAt = undefined;
141
131
  }).on('init', ({
142
132
  doc,
@@ -151,7 +141,6 @@ export class Provider extends Emitter {
151
141
  });
152
142
  }).on('restore', this.onRestore.bind(this)).on('steps:added', this.onStepsAdded.bind(this)).on('participant:telepointer', this.onParticipantTelepointer.bind(this)).on('presence:joined', this.onPresenceJoined.bind(this)).on('presence', this.onPresence.bind(this)).on('participant:left', this.onParticipantLeft.bind(this)).on('participant:updated', this.onParticipantUpdated.bind(this)).on('metadata:changed', this.onMetadataChanged.bind(this)).on('disconnect', this.onDisconnected.bind(this)).on('error', this.onErrorHandled.bind(this)).on('status', this.onNamespaceStatusChanged.bind(this)).connect();
153
143
  });
154
-
155
144
  _defineProperty(this, "onRestore", ({
156
145
  doc,
157
146
  version,
@@ -162,12 +151,13 @@ export class Provider extends Emitter {
162
151
  steps: unconfirmedSteps
163
152
  } = this.getUnconfirmedSteps() || {
164
153
  steps: []
165
- }; // Reset the editor,
154
+ };
155
+
156
+ // Reset the editor,
166
157
  // - Replace the document, keep in sync with the server
167
158
  // - Replace the version number, so editor is in sync with NCS server and can commit new changes.
168
159
  // - Replace the metadata
169
160
  // - Reserve the cursor position, in case a cursor jump.
170
-
171
161
  this.updateDocumentWithMetadata({
172
162
  doc,
173
163
  version,
@@ -180,27 +170,24 @@ export class Provider extends Emitter {
180
170
  numUnconfirmedSteps: unconfirmedSteps.length,
181
171
  documentAri: this.config.documentAri
182
172
  }
183
- }, this.analyticsClient); // Re-apply the unconfirmed steps, not 100% of them can be applied, if document is changed significantly.
173
+ }, this.analyticsClient);
184
174
 
175
+ // Re-apply the unconfirmed steps, not 100% of them can be applied, if document is changed significantly.
185
176
  if (unconfirmedSteps.length > 0) {
186
177
  this.applyLocalSteps(unconfirmedSteps);
187
178
  }
188
179
  });
189
-
190
180
  _defineProperty(this, "onStepsAdded", (data, disableAnalytics = false) => {
191
181
  logger(`Received steps`, {
192
182
  steps: data.steps,
193
183
  version: data.version
194
184
  });
195
-
196
185
  if (!data.steps) {
197
186
  logger(`No steps.. waiting..`);
198
187
  return;
199
188
  }
200
-
201
189
  const currentVersion = this.getCurrentPmVersion();
202
190
  const expectedVersion = currentVersion + data.steps.length;
203
-
204
191
  if (data.version === currentVersion) {
205
192
  logger(`Received steps we already have. Ignoring.`);
206
193
  } else if (data.version === expectedVersion) {
@@ -210,21 +197,17 @@ export class Provider extends Emitter {
210
197
  this.queueSteps(data);
211
198
  this.throttledCatchup();
212
199
  }
213
-
214
200
  this.updateParticipants([], data.steps.map(({
215
201
  userId
216
202
  }) => userId));
217
203
  });
218
-
219
204
  _defineProperty(this, "throttledCatchup", throttle(() => this.catchup(), CATCHUP_THROTTLE, {
220
205
  leading: false,
221
206
  trailing: true
222
207
  }));
223
-
224
208
  _defineProperty(this, "filterQueue", condition => {
225
209
  this.queue = this.queue.filter(condition);
226
210
  });
227
-
228
211
  _defineProperty(this, "updateDocumentWithMetadata", ({
229
212
  doc,
230
213
  version,
@@ -239,38 +222,31 @@ export class Provider extends Emitter {
239
222
  reserveCursor
240
223
  } : {})
241
224
  });
242
-
243
225
  if (metadata && Object.keys(metadata).length > 0) {
244
226
  this.metadata = metadata;
245
227
  this.emit('metadata:changed', metadata);
246
228
  }
247
229
  });
248
-
249
230
  _defineProperty(this, "applyLocalSteps", steps => {
250
- // Re-aply local steps
231
+ // Re-apply local steps
251
232
  this.emit('local-steps', {
252
233
  steps
253
234
  });
254
235
  });
255
-
256
236
  _defineProperty(this, "getCurrentPmVersion", () => {
257
237
  return getVersion(this.getState());
258
238
  });
259
-
260
239
  _defineProperty(this, "getUnconfirmedSteps", () => {
261
240
  return sendableSteps(this.getState());
262
241
  });
263
-
264
242
  _defineProperty(this, "catchup", async () => {
265
- const start = new Date().getTime(); // if the queue is already paused, we are busy with something else, so don't proceed.
266
-
243
+ const start = new Date().getTime();
244
+ // if the queue is already paused, we are busy with something else, so don't proceed.
267
245
  if (this.pauseQueue) {
268
246
  logger(`Queue is paused. Aborting.`);
269
247
  return;
270
248
  }
271
-
272
249
  this.pauseQueue = true;
273
-
274
250
  try {
275
251
  await catchup({
276
252
  getCurrentPmVersion: this.getCurrentPmVersion,
@@ -308,52 +284,54 @@ export class Provider extends Emitter {
308
284
  this.stepRejectCounter = 0;
309
285
  }
310
286
  });
311
-
312
287
  _defineProperty(this, "onErrorHandled", (error, disableAnalytics = false) => {
313
- if (error !== null && error !== void 0 && error.data) {
314
- // User tried committing steps but they were rejected because:
315
- // HEAD_VERSION_UPDATE_FAILED: the collab service's latest stored step tail version didn't correspond to the head version of the first step submitted
316
- // VERSION_NUMBER_ALREADY_EXISTS: while storing the steps there was a conflict meaning someone else wrote steps into the database more quickly
317
- if (error.data.code === 'HEAD_VERSION_UPDATE_FAILED' || error.data.code === 'VERSION_NUMBER_ALREADY_EXISTS') {
318
- // TODO: Remove this analytics logic once we have validated the ack messages and aren't likely to go back to a generic error handler
319
- if (!disableAnalytics) {
320
- triggerAnalyticsEvent({
321
- eventAction: EVENT_ACTION.ADD_STEPS,
322
- attributes: {
323
- eventStatus: EVENT_STATUS.FAILURE,
324
- type: ADD_STEPS_TYPE.REJECTED,
325
- error,
326
- documentAri: this.config.documentAri
327
- }
328
- }, this.analyticsClient);
329
- }
330
-
331
- this.stepRejectCounter++;
332
- logger(`Steps rejected (tries=${this.stepRejectCounter})`);
333
-
334
- if (this.stepRejectCounter >= MAX_STEP_REJECTED_ERROR) {
335
- logger(`The steps were rejected too many times (tries=${this.stepRejectCounter}, limit=${MAX_STEP_REJECTED_ERROR}). Trying to catch-up.`);
336
- this.throttledCatchup();
337
- }
288
+ var _error$data, _error$data2;
289
+ // User tried committing steps but they were rejected because:
290
+ // HEAD_VERSION_UPDATE_FAILED: the collab service's latest stored step tail version didn't correspond to the head version of the first step submitted
291
+ // VERSION_NUMBER_ALREADY_EXISTS: while storing the steps there was a conflict meaning someone else wrote steps into the database more quickly
292
+ if (((_error$data = error.data) === null || _error$data === void 0 ? void 0 : _error$data.code) === 'HEAD_VERSION_UPDATE_FAILED' || ((_error$data2 = error.data) === null || _error$data2 === void 0 ? void 0 : _error$data2.code) === 'VERSION_NUMBER_ALREADY_EXISTS') {
293
+ // TODO: Remove this analytics logic once we have validated the ack messages and aren't likely to go back to a generic error handler
294
+ if (!disableAnalytics) {
295
+ triggerAnalyticsEvent({
296
+ eventAction: EVENT_ACTION.ADD_STEPS,
297
+ attributes: {
298
+ eventStatus: EVENT_STATUS.FAILURE,
299
+ type: ADD_STEPS_TYPE.REJECTED,
300
+ error,
301
+ documentAri: this.config.documentAri
302
+ }
303
+ }, this.analyticsClient);
338
304
  }
339
-
340
- const errorToEmit = errorCodeMapper(error);
341
-
342
- if (errorToEmit) {
343
- this.emit('error', errorToEmit);
305
+ this.stepRejectCounter++;
306
+ logger(`Steps rejected (tries=${this.stepRejectCounter})`);
307
+ if (this.stepRejectCounter >= MAX_STEP_REJECTED_ERROR) {
308
+ logger(`The steps were rejected too many times (tries=${this.stepRejectCounter}, limit=${MAX_STEP_REJECTED_ERROR}). Trying to catch-up.`);
309
+ this.throttledCatchup();
310
+ }
311
+ } else {
312
+ const mappedError = errorCodeMapper(error);
313
+ // TODO: Remove this analytics logic once we have validated the ack messages and aren't likely to go back to a generic error handler
314
+ if (!disableAnalytics) {
315
+ triggerAnalyticsEvent({
316
+ eventAction: EVENT_ACTION.ERROR,
317
+ attributes: {
318
+ documentAri: this.config.documentAri,
319
+ mappedError
320
+ },
321
+ nonPrivacySafeAttributes: {
322
+ error
323
+ }
324
+ }, this.analyticsClient);
344
325
  }
326
+ this.emit('error', mappedError);
327
+ logger('Error from collab service', error);
345
328
  }
346
-
347
- logger('Error from collab service', error);
348
329
  });
349
-
350
330
  _defineProperty(this, "queue", []);
351
-
352
331
  _defineProperty(this, "sendPresence", () => {
353
332
  if (this.presenceUpdateTimeout) {
354
333
  clearTimeout(this.presenceUpdateTimeout);
355
334
  }
356
-
357
335
  this.channel.broadcast('participant:updated', {
358
336
  sessionId: this.sessionId,
359
337
  userId: this.userId,
@@ -361,15 +339,14 @@ export class Provider extends Emitter {
361
339
  });
362
340
  this.presenceUpdateTimeout = window.setTimeout(() => this.sendPresence(), SEND_PRESENCE_INTERVAL);
363
341
  });
364
-
365
342
  _defineProperty(this, "onPresenceJoined", ({
366
343
  sessionId
367
344
  }) => {
368
- logger('Participant joined with session: ', sessionId); // This expose existing users to the newly joined user
345
+ logger('Participant joined with session: ', sessionId);
369
346
 
347
+ // This expose existing users to the newly joined user
370
348
  this.sendPresence();
371
349
  });
372
-
373
350
  _defineProperty(this, "onPresence", ({
374
351
  userId
375
352
  }) => {
@@ -378,14 +355,12 @@ export class Provider extends Emitter {
378
355
  this.sendPresence();
379
356
  this.channel.sendPresenceJoined();
380
357
  });
381
-
382
358
  _defineProperty(this, "onMetadataChanged", metadata => {
383
359
  if (metadata !== undefined && !isEqual(this.metadata, metadata)) {
384
360
  this.metadata = metadata;
385
361
  this.emit('metadata:changed', metadata);
386
362
  }
387
363
  });
388
-
389
364
  _defineProperty(this, "onParticipantLeft", ({
390
365
  sessionId
391
366
  }) => {
@@ -397,7 +372,6 @@ export class Provider extends Emitter {
397
372
  }]
398
373
  });
399
374
  });
400
-
401
375
  _defineProperty(this, "onParticipantUpdated", ({
402
376
  sessionId,
403
377
  timestamp,
@@ -411,7 +385,6 @@ export class Provider extends Emitter {
411
385
  clientId
412
386
  });
413
387
  });
414
-
415
388
  _defineProperty(this, "onParticipantTelepointer", ({
416
389
  sessionId,
417
390
  timestamp,
@@ -422,14 +395,14 @@ export class Provider extends Emitter {
422
395
  if (sessionId === this.sessionId) {
423
396
  return;
424
397
  }
398
+ const participant = this.participants.get(sessionId);
425
399
 
426
- const participant = this.participants.get(sessionId); // Ignore old telepointer events
427
-
400
+ // Ignore old telepointer events
428
401
  if (participant && participant.lastActive > timestamp) {
429
402
  return;
430
- } // Set last active
431
-
403
+ }
432
404
 
405
+ // Set last active
433
406
  this.updateParticipant({
434
407
  sessionId,
435
408
  timestamp,
@@ -442,7 +415,6 @@ export class Provider extends Emitter {
442
415
  sessionId
443
416
  });
444
417
  });
445
-
446
418
  _defineProperty(this, "updateParticipant", async ({
447
419
  sessionId,
448
420
  timestamp,
@@ -453,7 +425,6 @@ export class Provider extends Emitter {
453
425
  // If userId does not exsit, does nothing here to prevent duplication.
454
426
  return;
455
427
  }
456
-
457
428
  const {
458
429
  getUser
459
430
  } = this.config;
@@ -463,14 +434,12 @@ export class Provider extends Emitter {
463
434
  avatar = ''
464
435
  } = await (getUser ? getUser(userId) : getParticipant(userId));
465
436
  const isNewParticipant = !this.participants.has(sessionId);
466
-
467
437
  if (isNewParticipant) {
468
438
  logger('new Participant updated', {
469
439
  name,
470
440
  avatar
471
441
  });
472
442
  }
473
-
474
443
  this.participants.set(sessionId, {
475
444
  name,
476
445
  email,
@@ -479,31 +448,30 @@ export class Provider extends Emitter {
479
448
  lastActive: timestamp,
480
449
  userId,
481
450
  clientId
482
- }); // Collab-plugin expects an array of users that joined.
451
+ });
483
452
 
453
+ // Collab-plugin expects an array of users that joined.
484
454
  this.updateParticipants(isNewParticipant ? [this.participants.get(sessionId)] : []);
485
455
  });
486
-
487
456
  _defineProperty(this, "updateParticipants", (joined = [], userIds = []) => {
488
457
  if (this.participantUpdateTimeout) {
489
458
  clearTimeout(this.participantUpdateTimeout);
490
459
  }
491
-
492
460
  const now = new Date().getTime();
493
461
  Array.from(this.participants.values()).forEach(p => {
494
462
  if (userIds.indexOf(p.userId) !== -1) {
495
- this.participants.set(p.sessionId, { ...p,
463
+ this.participants.set(p.sessionId, {
464
+ ...p,
496
465
  lastActive: now
497
466
  });
498
467
  }
499
- }); // Filter out participants that's been inactive for more than 5 minutes.
468
+ });
500
469
 
470
+ // Filter out participants that's been inactive for more than 5 minutes.
501
471
  const left = Array.from(this.participants.values()).filter(p => p.sessionId !== this.sessionId && now - p.lastActive > PARTICIPANT_UPDATE_INTERVAL);
502
472
  left.forEach(p => this.participants.delete(p.sessionId));
503
-
504
473
  if (joined.length || left.length) {
505
474
  var _this$participants$si;
506
-
507
475
  triggerAnalyticsEvent({
508
476
  eventAction: EVENT_ACTION.UPDATE_PARTICIPANTS,
509
477
  attributes: {
@@ -511,7 +479,8 @@ export class Provider extends Emitter {
511
479
  documentAri: this.config.documentAri
512
480
  }
513
481
  }, this.analyticsClient);
514
- this.emit('presence', { ...(joined.length ? {
482
+ this.emit('presence', {
483
+ ...(joined.length ? {
515
484
  joined
516
485
  } : {}),
517
486
  ...(left.length ? {
@@ -519,32 +488,24 @@ export class Provider extends Emitter {
519
488
  } : {})
520
489
  });
521
490
  }
522
-
523
491
  this.participantUpdateTimeout = window.setTimeout(() => this.updateParticipants(), PARTICIPANT_UPDATE_INTERVAL);
524
492
  });
525
-
526
493
  _defineProperty(this, "disconnectedReasonMapper", reason => {
527
494
  switch (reason) {
528
495
  case socketIOReasons.IO_CLIENT_DISCONNECT:
529
496
  return DisconnectReason.CLIENT_DISCONNECT;
530
-
531
497
  case socketIOReasons.IO_SERVER_DISCONNECT:
532
498
  return DisconnectReason.SERVER_DISCONNECT;
533
-
534
499
  case socketIOReasons.TRANSPORT_CLOSED:
535
500
  return DisconnectReason.SOCKET_CLOSED;
536
-
537
501
  case socketIOReasons.TRANSPORT_ERROR:
538
502
  return DisconnectReason.SOCKET_ERROR;
539
-
540
503
  case socketIOReasons.PING_TIMEOUT:
541
504
  return DisconnectReason.SOCKET_TIMEOUT;
542
-
543
505
  default:
544
506
  return DisconnectReason.UNKNOWN_DISCONNECT;
545
507
  }
546
508
  });
547
-
548
509
  _defineProperty(this, "onDisconnected", ({
549
510
  reason
550
511
  }) => {
@@ -555,43 +516,36 @@ export class Provider extends Emitter {
555
516
  reason: this.disconnectedReasonMapper(reason),
556
517
  sid: this.sessionId
557
518
  });
558
-
559
519
  if (left.length) {
560
520
  this.emit('presence', {
561
521
  left
562
522
  });
563
523
  }
564
524
  });
565
-
566
525
  _defineProperty(this, "getFinalAcknowledgedState", async () => {
567
526
  var _this$metadata$title;
568
-
569
527
  const maxAttemptsToSync = ACK_MAX_TRY;
570
528
  let count = 0;
571
529
  let unconfirmedState = this.getUnconfirmedSteps();
572
-
573
530
  if (unconfirmedState && unconfirmedState.steps.length) {
574
- startMeasure(MEASURE_NAME.COMMIT_UNCONFIRMED_STEPS); // We use origins here as steps can be rebased. When steps are rebased a new step is created.
531
+ startMeasure(MEASURE_NAME.COMMIT_UNCONFIRMED_STEPS);
532
+ // We use origins here as steps can be rebased. When steps are rebased a new step is created.
575
533
  // This means that we can not track if it has been removed from the unconfirmed array or not.
576
534
  // Origins points to the original transaction that the step was created in. This is never changed
577
535
  // and gets passed down when a step is rebased.
578
-
579
536
  const unconfirmedTrs = unconfirmedState.origins;
580
537
  const lastTr = unconfirmedTrs[unconfirmedTrs.length - 1];
581
538
  let isLastTrConfirmed = false;
582
-
583
539
  while (!isLastTrConfirmed) {
584
540
  this.sendStepsFromCurrentState();
585
541
  await sleep(1000);
586
542
  const nextUnconfirmedState = this.getUnconfirmedSteps();
587
-
588
543
  if (nextUnconfirmedState && nextUnconfirmedState.steps.length) {
589
544
  const nextUnconfirmedTrs = nextUnconfirmedState.origins;
590
545
  isLastTrConfirmed = !nextUnconfirmedTrs.some(tr => tr === lastTr);
591
546
  } else {
592
547
  isLastTrConfirmed = true;
593
548
  }
594
-
595
549
  if (!isLastTrConfirmed && count++ >= maxAttemptsToSync) {
596
550
  if (this.onSyncUpError) {
597
551
  const state = this.getState();
@@ -603,7 +557,6 @@ export class Provider extends Emitter {
603
557
  version: getVersion(state)
604
558
  });
605
559
  }
606
-
607
560
  const measure = stopMeasure(MEASURE_NAME.COMMIT_UNCONFIRMED_STEPS);
608
561
  triggerAnalyticsEvent({
609
562
  eventAction: EVENT_ACTION.COMMIT_UNCONFIRMED_STEPS,
@@ -618,7 +571,6 @@ export class Provider extends Emitter {
618
571
  throw new Error("Can't sync up with Collab Service");
619
572
  }
620
573
  }
621
-
622
574
  const measure = stopMeasure(MEASURE_NAME.COMMIT_UNCONFIRMED_STEPS);
623
575
  triggerAnalyticsEvent({
624
576
  eventAction: EVENT_ACTION.COMMIT_UNCONFIRMED_STEPS,
@@ -631,11 +583,9 @@ export class Provider extends Emitter {
631
583
  }
632
584
  }, this.analyticsClient);
633
585
  }
634
-
635
- const state = this.getState(); // Convert ProseMirror document in Editor state to ADF document
636
-
586
+ const state = this.getState();
587
+ // Convert ProseMirror document in Editor state to ADF document
637
588
  let adfDocument;
638
-
639
589
  try {
640
590
  startMeasure(MEASURE_NAME.CONVERT_PM_TO_ADF);
641
591
  adfDocument = new JSONTransformer().encode(state.doc);
@@ -661,14 +611,12 @@ export class Provider extends Emitter {
661
611
  }, this.analyticsClient);
662
612
  logger(`Error when converting PM document to ADF: `, error);
663
613
  }
664
-
665
614
  return {
666
615
  content: adfDocument,
667
616
  title: (_this$metadata$title = this.metadata.title) === null || _this$metadata$title === void 0 ? void 0 : _this$metadata$title.toString(),
668
617
  stepVersion: getVersion(state)
669
618
  };
670
619
  });
671
-
672
620
  _defineProperty(this, "onNamespaceStatusChanged", async data => {
673
621
  const {
674
622
  isLocked,
@@ -679,13 +627,13 @@ export class Provider extends Emitter {
679
627
  logger(`Received a namespace status changed event `, {
680
628
  data
681
629
  });
682
-
683
630
  if (isLocked && waitTimeInMs) {
684
631
  this.isNamespaceLocked = true;
685
632
  logger(`Received a namespace status change event `, {
686
633
  isLocked
687
- }); // To protect the collab editing process from locked out due to BE
634
+ });
688
635
 
636
+ // To protect the collab editing process from locked out due to BE
689
637
  setTimeout(() => {
690
638
  logger(`The namespace lock has expired`, {
691
639
  waitTime: Date.now() - start,
@@ -695,20 +643,16 @@ export class Provider extends Emitter {
695
643
  }, waitTimeInMs);
696
644
  return;
697
645
  }
698
-
699
646
  this.isNamespaceLocked = false;
700
647
  logger(`The page lock has expired`);
701
648
  });
702
-
703
649
  this.config = config;
704
650
  this.channel = new Channel(config);
705
651
  this.isChannelInitialized = false;
706
-
707
652
  if (config.analyticsClient) {
708
653
  this.analyticsClient = config.analyticsClient;
709
654
  }
710
655
  }
711
-
712
656
  /**
713
657
  * Called by collab plugin in editor when it's ready to
714
658
  * initialize a collab session.
@@ -718,22 +662,34 @@ export class Provider extends Emitter {
718
662
  getState
719
663
  });
720
664
  }
721
-
722
665
  setup({
723
666
  getState,
724
667
  onSyncUpError
725
668
  }) {
726
- this.getState = getState;
727
- this.onSyncUpError = onSyncUpError || noop;
728
- this.clientId = getState().plugins.find(p => p.key === 'collab$').spec.config.clientID;
729
-
730
- if (!this.isChannelInitialized) {
731
- this.initializeChannel();
732
- this.isChannelInitialized = true;
669
+ try {
670
+ this.getState = getState;
671
+ this.onSyncUpError = onSyncUpError || noop;
672
+ this.clientId = getState().plugins.find(p => p.key === 'collab$').spec.config.clientID;
673
+ if (!this.isChannelInitialized) {
674
+ this.initializeChannel();
675
+ this.isChannelInitialized = true;
676
+ }
677
+ } catch (initError) {
678
+ triggerAnalyticsEvent({
679
+ eventAction: EVENT_ACTION.ERROR,
680
+ attributes: {
681
+ attemptedAction: EVENT_ACTION.INIT_PROVIDER,
682
+ documentAri: this.config.documentAri
683
+ },
684
+ nonPrivacySafeAttributes: {
685
+ error: initError
686
+ }
687
+ }, this.analyticsClient);
688
+ throw initError;
733
689
  }
734
-
735
690
  return this;
736
691
  }
692
+
737
693
  /**
738
694
  * We can use this function to throttle/delay
739
695
  * Any send steps operation
@@ -741,46 +697,40 @@ export class Provider extends Emitter {
741
697
  * The getState function will return the current EditorState
742
698
  * from the EditorView.
743
699
  */
744
-
745
-
746
700
  sendStepsFromCurrentState() {
747
701
  const state = this.getState && this.getState();
748
-
749
702
  if (!state) {
750
703
  return;
751
704
  }
752
-
753
705
  this.send(null, null, state);
754
706
  }
707
+
755
708
  /**
756
709
  * Send steps from transaction to other participants
710
+ * It needs the superfluous arguments because we keep the interface of the send API the same as the Synchrony plugin
757
711
  */
758
-
759
-
760
712
  send(_tr, _oldState, newState) {
761
713
  const sendable = sendableSteps(newState);
762
- const version = getVersion(newState); // Don't send any steps before we're ready.
714
+ const version = getVersion(newState);
763
715
 
716
+ // Don't send any steps before we're ready.
764
717
  if (!sendable) {
765
718
  return;
766
719
  }
767
-
768
720
  if (this.isNamespaceLocked) {
769
721
  logger('The document is temporary locked');
770
722
  return;
771
723
  }
772
-
773
724
  const {
774
725
  steps
775
726
  } = sendable;
776
-
777
727
  if (!steps || !steps.length) {
778
728
  return;
779
- } // Avoid reference issues using a
729
+ }
730
+
731
+ // Avoid reference issues using a
780
732
  // method outside of the provider
781
733
  // scope
782
-
783
-
784
734
  throttledCommitStep({
785
735
  channel: this.channel,
786
736
  userId: this.userId,
@@ -792,44 +742,39 @@ export class Provider extends Emitter {
792
742
  onStepsAdded: this.onStepsAdded.bind(this),
793
743
  onErrorHandled: this.onErrorHandled.bind(this)
794
744
  });
795
- } // Triggered when page recovery has emitted an 'init' event on a page client is currently connected to.
745
+ }
796
746
 
747
+ // Triggered when page recovery has emitted an 'init' event on a page client is currently connected to.
797
748
 
798
749
  queueSteps(data) {
799
750
  logger(`Queueing data for version "${data.version}".`);
800
751
  const orderedQueue = [...this.queue, data].sort((a, b) => a.version > b.version ? 1 : -1);
801
752
  this.queue = orderedQueue;
802
753
  }
803
-
804
754
  processQueue() {
805
755
  if (this.pauseQueue) {
806
756
  logger(`Queue is paused. Aborting.`);
807
757
  return;
808
758
  }
809
-
810
759
  logger(`Looking for processable data.`);
811
-
812
760
  if (this.queue.length > 0) {
813
761
  const firstItem = this.queue.shift();
814
762
  const currentVersion = this.getCurrentPmVersion();
815
763
  const expectedVersion = currentVersion + firstItem.steps.length;
816
-
817
764
  if (firstItem.version === expectedVersion) {
818
765
  logger(`Applying data from queue!`);
819
- this.processSteps(firstItem); // recur
820
-
766
+ this.processSteps(firstItem);
767
+ // recur
821
768
  this.processQueue();
822
769
  }
823
770
  }
824
771
  }
825
-
826
772
  processSteps(data, disableAnalytics = false) {
827
773
  const {
828
774
  version,
829
775
  steps
830
776
  } = data;
831
777
  logger(`Processing data. Version "${version}".`);
832
-
833
778
  if (steps !== null && steps !== void 0 && steps.length) {
834
779
  const clientIds = steps.map(({
835
780
  clientId
@@ -838,10 +783,10 @@ export class Provider extends Emitter {
838
783
  json: steps,
839
784
  version,
840
785
  userIds: clientIds
841
- }); // If steps can apply to local editor successfully, no need to accumulate the error counter.
842
-
843
- this.stepRejectCounter = 0; // TODO: Remove this analytics logic after we've validated the ack call-backs
844
-
786
+ });
787
+ // If steps can apply to local editor successfully, no need to accumulate the error counter.
788
+ this.stepRejectCounter = 0;
789
+ // TODO: Remove this analytics logic after we've validated the ack call-backs
845
790
  if (!disableAnalytics && clientIds.indexOf(this.clientId) >= 0) {
846
791
  let analyticStepEvent = {
847
792
  eventAction: EVENT_ACTION.ADD_STEPS,
@@ -854,24 +799,22 @@ export class Provider extends Emitter {
854
799
  analyticStepEvent.attributes.stepType = countBy(steps, step => step.stepType);
855
800
  triggerAnalyticsEvent(analyticStepEvent, this.analyticsClient);
856
801
  }
802
+ this.emitTelepointersFromSteps(steps);
857
803
 
858
- this.emitTelepointersFromSteps(steps); // Resend local steps if none of the received steps originated with us!
859
-
804
+ // Resend local steps if none of the received steps originated with us!
860
805
  if (clientIds.indexOf(this.clientId) === -1) {
861
806
  setTimeout(() => this.sendStepsFromCurrentState(), 100);
862
807
  }
863
808
  }
864
809
  }
810
+
865
811
  /**
866
812
  * Send messages, such as telepointers, to other participants.
867
813
  */
868
-
869
-
870
814
  sendMessage(data) {
871
815
  if (!data) {
872
816
  return;
873
817
  }
874
-
875
818
  const {
876
819
  type,
877
820
  ...rest
@@ -881,7 +824,6 @@ export class Provider extends Emitter {
881
824
  sessionId,
882
825
  clientId
883
826
  } = this;
884
-
885
827
  switch (type) {
886
828
  case 'telepointer':
887
829
  const telepointerExperience = new UFOExperience('collab-provider.telepointer', {
@@ -925,11 +867,9 @@ export class Provider extends Emitter {
925
867
  break;
926
868
  }
927
869
  }
928
-
929
870
  emitTelepointersFromSteps(steps) {
930
871
  steps.forEach(step => {
931
872
  const [participant] = Array.from(this.participants.values()).filter(p => p.clientId === step.clientId);
932
-
933
873
  if (participant) {
934
874
  const {
935
875
  stepType,
@@ -940,7 +880,6 @@ export class Provider extends Emitter {
940
880
  }
941
881
  } = step;
942
882
  const [node] = slice.content;
943
-
944
883
  if (stepType === 'replace' && to === from && slice.content.length === 1 && node.type === 'text' && node.text.length === 1) {
945
884
  this.emit('telepointer', {
946
885
  sessionId: participant.sessionId,
@@ -955,44 +894,36 @@ export class Provider extends Emitter {
955
894
  }
956
895
  });
957
896
  }
958
-
959
897
  destroy() {
960
898
  return this.disconnect();
961
899
  }
962
-
963
900
  disconnect() {
964
901
  return this.unsubscribeAll();
965
902
  }
966
-
967
903
  setTitle(title, broadcast) {
968
904
  if (broadcast) {
969
905
  this.channel.sendMetadata({
970
906
  title
971
907
  });
972
908
  }
973
-
974
909
  Object.assign(this.metadata, {
975
910
  title
976
911
  });
977
912
  }
978
-
979
913
  setEditorWidth(editorWidth, broadcast) {
980
914
  if (broadcast) {
981
915
  this.channel.sendMetadata({
982
916
  editorWidth
983
917
  });
984
918
  }
985
-
986
919
  Object.assign(this.metadata, {
987
920
  editorWidth
988
921
  });
989
922
  }
990
-
991
923
  setMetadata(metadata) {
992
924
  this.channel.sendMetadata(metadata);
993
925
  Object.assign(this.metadata, metadata);
994
926
  }
995
-
996
927
  /**
997
928
  * Unsubscribe from all events emitted by this provider.
998
929
  */
@@ -1001,9 +932,8 @@ export class Provider extends Emitter {
1001
932
  this.channel.disconnect();
1002
933
  return this;
1003
934
  }
935
+
1004
936
  /**
1005
937
  * ESS-2916 namespace status event- lock/unlock
1006
938
  */
1007
-
1008
-
1009
939
  }