@atlaskit/collab-provider 9.3.1 → 9.5.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 (41) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/cjs/analytics/analytics-helper.js +7 -2
  3. package/dist/cjs/channel.js +26 -20
  4. package/dist/cjs/document/document-service.js +60 -11
  5. package/dist/cjs/errors/error-code-mapper.js +7 -0
  6. package/dist/cjs/errors/error-types.js +31 -5
  7. package/dist/cjs/participants/participants-helper.js +1 -0
  8. package/dist/cjs/provider/index.js +52 -38
  9. package/dist/cjs/version-wrapper.js +1 -1
  10. package/dist/cjs/version.json +1 -1
  11. package/dist/es2019/analytics/analytics-helper.js +7 -1
  12. package/dist/es2019/channel.js +13 -7
  13. package/dist/es2019/document/document-service.js +61 -15
  14. package/dist/es2019/errors/error-code-mapper.js +7 -0
  15. package/dist/es2019/errors/error-types.js +20 -2
  16. package/dist/es2019/participants/participants-helper.js +1 -0
  17. package/dist/es2019/provider/index.js +52 -38
  18. package/dist/es2019/version-wrapper.js +1 -1
  19. package/dist/es2019/version.json +1 -1
  20. package/dist/esm/analytics/analytics-helper.js +7 -2
  21. package/dist/esm/channel.js +26 -20
  22. package/dist/esm/document/document-service.js +61 -12
  23. package/dist/esm/errors/error-code-mapper.js +7 -0
  24. package/dist/esm/errors/error-types.js +30 -4
  25. package/dist/esm/participants/participants-helper.js +1 -0
  26. package/dist/esm/provider/index.js +52 -38
  27. package/dist/esm/version-wrapper.js +1 -1
  28. package/dist/esm/version.json +1 -1
  29. package/dist/types/document/document-service.d.ts +4 -1
  30. package/dist/types/errors/error-types.d.ts +47 -7
  31. package/dist/types/helpers/const.d.ts +8 -1
  32. package/dist/types/participants/participants-helper.d.ts +2 -1
  33. package/dist/types/types.d.ts +7 -2
  34. package/dist/types-ts4.5/document/document-service.d.ts +4 -1
  35. package/dist/types-ts4.5/errors/error-types.d.ts +47 -7
  36. package/dist/types-ts4.5/helpers/const.d.ts +8 -1
  37. package/dist/types-ts4.5/participants/participants-helper.d.ts +2 -1
  38. package/dist/types-ts4.5/types.d.ts +7 -2
  39. package/package.json +1 -1
  40. package/report.api.md +24 -4
  41. package/tmp/api-report-tmp.d.ts +17 -2
@@ -1,6 +1,7 @@
1
1
  import { EVENT_ACTION } from '../helpers/const';
2
2
  import { name as packageName, version as packageVersion } from '../version-wrapper';
3
3
  import { network } from '../connectivity/singleton';
4
+ import { CustomError } from '../errors/error-types';
4
5
  const EVENT_SUBJECT = 'collab';
5
6
  var COLLAB_SERVICE = /*#__PURE__*/function (COLLAB_SERVICE) {
6
7
  COLLAB_SERVICE["NCS"] = "ncs";
@@ -59,12 +60,17 @@ export default class AnalyticsHelper {
59
60
  this.getAnalyticsClient = getAnalyticsClient;
60
61
  }
61
62
  sendErrorEvent(error, errorMessage) {
63
+ let errorExtraAttributes = {};
64
+ if (error instanceof CustomError) {
65
+ errorExtraAttributes = error.getExtraErrorEventAttributes() || {};
66
+ }
62
67
  const errorAnalyticsEvent = {
63
68
  eventAction: EVENT_ACTION.ERROR,
64
69
  attributes: {
65
70
  documentAri: this.documentAri,
66
71
  errorMessage,
67
- errorName: error instanceof Error ? error.name : undefined
72
+ errorName: error instanceof Error ? error.name : undefined,
73
+ ...errorExtraAttributes
68
74
  },
69
75
  nonPrivacySafeAttributes: {
70
76
  error
@@ -295,14 +295,15 @@ export class Channel extends Emitter {
295
295
  cacheToken
296
296
  } = this.config;
297
297
  let auth;
298
- const authData = {
299
- // The initialized status. If false, BE will send document, otherwise not.
300
- initialized: this.initialized,
301
- // ESS-1009 Allow to opt-in into 404 response
302
- need404: this.config.need404
303
- };
304
298
  if (permissionTokenRefresh) {
305
299
  auth = async cb => {
300
+ // Rebuild authData to ensure values are current
301
+ const authData = {
302
+ // The initialized status. If false, BE will send document, otherwise not.
303
+ initialized: this.initialized,
304
+ // ESS-1009 Allow to opt-in into 404 response
305
+ need404: this.config.need404
306
+ };
306
307
  // use the cached token if caching in enabled and token valid
307
308
  if (cacheToken && this.token) {
308
309
  authData.token = this.token;
@@ -340,7 +341,12 @@ export class Channel extends Emitter {
340
341
  }
341
342
  };
342
343
  } else {
343
- auth = authData;
344
+ auth = {
345
+ // The initialized status. If false, BE will send document, otherwise not.
346
+ initialized: this.initialized,
347
+ // ESS-1009 Allow to opt-in into 404 response
348
+ need404: this.config.need404
349
+ };
344
350
  }
345
351
  this.socket = createSocket(`${url}/session/${documentAri}`, auth, this.config.productInfo);
346
352
 
@@ -9,7 +9,7 @@ import { JSONTransformer } from '@atlaskit/editor-json-transformer';
9
9
  import { MAX_STEP_REJECTED_ERROR } from '../provider';
10
10
  import { catchup } from './catchup';
11
11
  import { StepQueueState } from './step-queue-state';
12
- import { INTERNAL_ERROR_CODE } from '../errors/error-types';
12
+ import { INTERNAL_ERROR_CODE, UpdateDocumentError } from '../errors/error-types';
13
13
  const CATCHUP_THROTTLE = 1 * 1000; // 1 second
14
14
 
15
15
  const noop = () => {};
@@ -29,8 +29,9 @@ export class DocumentService {
29
29
  * @param onErrorHandled - Callback to handle
30
30
  * @param metadataService
31
31
  * @param failedStepsBeforeCatchupOnPublish - Control MAX_STEP_REJECTED_ERROR during page publishes.
32
+ * @param enableErrorOnFailedDocumentApply - Enable failed document update exceptions.
32
33
  */
33
- constructor(participantsService, analyticsHelper, fetchCatchup, providerEmitCallback, broadcast, getUserId, onErrorHandled, metadataService, failedStepsBeforeCatchupOnPublish = MAX_STEP_REJECTED_ERROR) {
34
+ constructor(participantsService, analyticsHelper, fetchCatchup, providerEmitCallback, broadcast, getUserId, onErrorHandled, metadataService, failedStepsBeforeCatchupOnPublish = MAX_STEP_REJECTED_ERROR, enableErrorOnFailedDocumentApply = false) {
34
35
  // Fires analytics to editor when collab editor cannot sync up
35
36
  _defineProperty(this, "stepRejectCounter", 0);
36
37
  _defineProperty(this, "aggressiveCatchup", false);
@@ -275,6 +276,50 @@ export class DocumentService {
275
276
  reserveCursor
276
277
  } : {})
277
278
  });
279
+ const updatedVersion = this.getCurrentPmVersion();
280
+ if (this.getCurrentPmVersion() !== version) {
281
+ var _doc$content, _this$analyticsHelper17;
282
+ const isDocContentValid = this.validatePMJSONDocument(doc);
283
+ const error = new UpdateDocumentError('Failed to update the document', {
284
+ newVersion: version,
285
+ editorVersion: updatedVersion,
286
+ isDocTruthy: !!doc,
287
+ docHasContent: (doc === null || doc === void 0 ? void 0 : (_doc$content = doc.content) === null || _doc$content === void 0 ? void 0 : _doc$content.length) >= 1,
288
+ isDocContentValid
289
+ });
290
+ (_this$analyticsHelper17 = this.analyticsHelper) === null || _this$analyticsHelper17 === void 0 ? void 0 : _this$analyticsHelper17.sendErrorEvent(error, 'Failed to update the document in document service');
291
+ if (this.enableErrorOnFailedDocumentApply) {
292
+ this.onErrorHandled({
293
+ message: 'The provider failed to apply changes to the editor',
294
+ data: {
295
+ code: INTERNAL_ERROR_CODE.DOCUMENT_UPDATE_ERROR,
296
+ meta: {
297
+ newVersion: version,
298
+ editorVersion: updatedVersion
299
+ },
300
+ status: 500
301
+ }
302
+ });
303
+ throw error;
304
+ }
305
+ // Otherwise just fail silently for now
306
+ }
307
+ });
308
+ _defineProperty(this, "validatePMJSONDocument", doc => {
309
+ try {
310
+ const state = this.getState();
311
+ const content = (doc.content || []).map(child => state.schema.nodeFromJSON(child));
312
+ return content.every(node => {
313
+ try {
314
+ node.check(); // this will throw an error if the node is invalid
315
+ } catch (error) {
316
+ return false;
317
+ }
318
+ return true;
319
+ });
320
+ } catch (e) {
321
+ return false;
322
+ }
278
323
  });
279
324
  /**
280
325
  * Commit the unconfirmed local steps to the back-end service
@@ -284,7 +329,7 @@ export class DocumentService {
284
329
  const unconfirmedSteps = this.getUnconfirmedSteps();
285
330
  try {
286
331
  if (unconfirmedSteps !== null && unconfirmedSteps !== void 0 && unconfirmedSteps.length) {
287
- var _this$analyticsHelper18;
332
+ var _this$analyticsHelper19;
288
333
  startMeasure(MEASURE_NAME.COMMIT_UNCONFIRMED_STEPS, this.analyticsHelper);
289
334
  let count = 0;
290
335
  // We use origins here as steps can be rebased. When steps are rebased a new step is created.
@@ -305,7 +350,7 @@ export class DocumentService {
305
350
  isLastTrConfirmed = true;
306
351
  }
307
352
  if (!isLastTrConfirmed && count++ >= ACK_MAX_TRY) {
308
- var _this$getUnconfirmedS, _this$analyticsHelper17;
353
+ var _this$getUnconfirmedS, _this$analyticsHelper18;
309
354
  if (this.onSyncUpError) {
310
355
  const state = this.getState();
311
356
  this.onSyncUpError({
@@ -317,42 +362,42 @@ export class DocumentService {
317
362
  });
318
363
  }
319
364
  const unconfirmedStepsInfoUGCRemoved = (_this$getUnconfirmedS = this.getUnconfirmedSteps()) === null || _this$getUnconfirmedS === void 0 ? void 0 : _this$getUnconfirmedS.map(step => getStepUGCFreeDetails(step));
320
- (_this$analyticsHelper17 = this.analyticsHelper) === null || _this$analyticsHelper17 === void 0 ? void 0 : _this$analyticsHelper17.sendErrorEvent({
365
+ (_this$analyticsHelper18 = this.analyticsHelper) === null || _this$analyticsHelper18 === void 0 ? void 0 : _this$analyticsHelper18.sendErrorEvent({
321
366
  unconfirmedStepsInfo: unconfirmedStepsInfoUGCRemoved
322
367
  }, "Can't sync up with Collab Service: unable to send unconfirmed steps and max retry reached");
323
368
  throw new Error("Can't sync up with Collab Service");
324
369
  }
325
370
  }
326
371
  const measure = stopMeasure(MEASURE_NAME.COMMIT_UNCONFIRMED_STEPS, this.analyticsHelper);
327
- (_this$analyticsHelper18 = this.analyticsHelper) === null || _this$analyticsHelper18 === void 0 ? void 0 : _this$analyticsHelper18.sendActionEvent(EVENT_ACTION.COMMIT_UNCONFIRMED_STEPS, EVENT_STATUS.SUCCESS, {
372
+ (_this$analyticsHelper19 = this.analyticsHelper) === null || _this$analyticsHelper19 === void 0 ? void 0 : _this$analyticsHelper19.sendActionEvent(EVENT_ACTION.COMMIT_UNCONFIRMED_STEPS, EVENT_STATUS.SUCCESS, {
328
373
  latency: measure === null || measure === void 0 ? void 0 : measure.duration,
329
374
  // upon success, emit the total number of unconfirmed steps we synced
330
375
  numUnconfirmedSteps: unconfirmedSteps === null || unconfirmedSteps === void 0 ? void 0 : unconfirmedSteps.length
331
376
  });
332
377
  }
333
378
  } catch (error) {
334
- var _this$analyticsHelper19, _this$analyticsHelper20;
379
+ var _this$analyticsHelper20, _this$analyticsHelper21;
335
380
  const measure = stopMeasure(MEASURE_NAME.COMMIT_UNCONFIRMED_STEPS, this.analyticsHelper);
336
- (_this$analyticsHelper19 = this.analyticsHelper) === null || _this$analyticsHelper19 === void 0 ? void 0 : _this$analyticsHelper19.sendActionEvent(EVENT_ACTION.COMMIT_UNCONFIRMED_STEPS, EVENT_STATUS.FAILURE, {
381
+ (_this$analyticsHelper20 = this.analyticsHelper) === null || _this$analyticsHelper20 === void 0 ? void 0 : _this$analyticsHelper20.sendActionEvent(EVENT_ACTION.COMMIT_UNCONFIRMED_STEPS, EVENT_STATUS.FAILURE, {
337
382
  latency: measure === null || measure === void 0 ? void 0 : measure.duration,
338
383
  numUnconfirmedSteps: unconfirmedSteps === null || unconfirmedSteps === void 0 ? void 0 : unconfirmedSteps.length
339
384
  });
340
- (_this$analyticsHelper20 = this.analyticsHelper) === null || _this$analyticsHelper20 === void 0 ? void 0 : _this$analyticsHelper20.sendErrorEvent(error, 'Error while committing unconfirmed steps');
385
+ (_this$analyticsHelper21 = this.analyticsHelper) === null || _this$analyticsHelper21 === void 0 ? void 0 : _this$analyticsHelper21.sendErrorEvent(error, 'Error while committing unconfirmed steps');
341
386
  throw error;
342
387
  }
343
388
  });
344
389
  _defineProperty(this, "onStepRejectedError", () => {
345
- var _this$analyticsHelper21;
390
+ var _this$analyticsHelper22;
346
391
  this.stepRejectCounter++;
347
392
  logger(`Steps rejected (tries=${this.stepRejectCounter})`);
348
- (_this$analyticsHelper21 = this.analyticsHelper) === null || _this$analyticsHelper21 === void 0 ? void 0 : _this$analyticsHelper21.sendActionEvent(EVENT_ACTION.SEND_STEPS_RETRY, EVENT_STATUS.INFO, {
393
+ (_this$analyticsHelper22 = this.analyticsHelper) === null || _this$analyticsHelper22 === void 0 ? void 0 : _this$analyticsHelper22.sendActionEvent(EVENT_ACTION.SEND_STEPS_RETRY, EVENT_STATUS.INFO, {
349
394
  count: this.stepRejectCounter
350
395
  });
351
396
  let maxRetries = this.aggressiveCatchup ? this.failedStepsBeforeCatchupOnPublish : MAX_STEP_REJECTED_ERROR;
352
397
  if (this.stepRejectCounter >= maxRetries) {
353
- var _this$analyticsHelper22;
398
+ var _this$analyticsHelper23;
354
399
  logger(`The steps were rejected too many times (tries=${this.stepRejectCounter}, limit=${MAX_STEP_REJECTED_ERROR}). Trying to catch-up.`);
355
- (_this$analyticsHelper22 = this.analyticsHelper) === null || _this$analyticsHelper22 === void 0 ? void 0 : _this$analyticsHelper22.sendActionEvent(EVENT_ACTION.CATCHUP_AFTER_MAX_SEND_STEPS_RETRY, EVENT_STATUS.INFO);
400
+ (_this$analyticsHelper23 = this.analyticsHelper) === null || _this$analyticsHelper23 === void 0 ? void 0 : _this$analyticsHelper23.sendActionEvent(EVENT_ACTION.CATCHUP_AFTER_MAX_SEND_STEPS_RETRY, EVENT_STATUS.INFO);
356
401
  this.throttledCatchup();
357
402
  } else {
358
403
  // If committing steps failed try again automatically in 1s
@@ -371,6 +416,7 @@ export class DocumentService {
371
416
  this.onErrorHandled = onErrorHandled;
372
417
  this.metadataService = metadataService;
373
418
  this.failedStepsBeforeCatchupOnPublish = failedStepsBeforeCatchupOnPublish;
419
+ this.enableErrorOnFailedDocumentApply = enableErrorOnFailedDocumentApply;
374
420
  this.stepQueue = new StepQueueState();
375
421
  }
376
422
  processQueue() {
@@ -416,9 +462,9 @@ export class DocumentService {
416
462
  setTimeout(() => this.sendStepsFromCurrentState(), 100);
417
463
  }
418
464
  } catch (error) {
419
- var _this$analyticsHelper23;
465
+ var _this$analyticsHelper24;
420
466
  logger(`Processing steps failed with error: ${error}. Triggering catch up call.`);
421
- (_this$analyticsHelper23 = this.analyticsHelper) === null || _this$analyticsHelper23 === void 0 ? void 0 : _this$analyticsHelper23.sendErrorEvent(error, 'Error while processing steps');
467
+ (_this$analyticsHelper24 = this.analyticsHelper) === null || _this$analyticsHelper24 === void 0 ? void 0 : _this$analyticsHelper24.sendErrorEvent(error, 'Error while processing steps');
422
468
  this.throttledCatchup();
423
469
  }
424
470
  }
@@ -67,6 +67,13 @@ export const errorCodeMapper = error => {
67
67
  recoverable: false,
68
68
  status: 500
69
69
  };
70
+ case INTERNAL_ERROR_CODE.DOCUMENT_UPDATE_ERROR:
71
+ return {
72
+ code: PROVIDER_ERROR_CODE.DOCUMENT_UPDATE_ERROR,
73
+ message: 'The provider failed to apply changes to the editor',
74
+ recoverable: false,
75
+ status: 500
76
+ };
70
77
  case INTERNAL_ERROR_CODE.RECONNECTION_NETWORK_ISSUE:
71
78
  return {
72
79
  code: PROVIDER_ERROR_CODE.NETWORK_ISSUE,
@@ -1,5 +1,6 @@
1
1
  import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
2
  // Internal error codes (generated by collab provider)
3
+
3
4
  export let INTERNAL_ERROR_CODE = /*#__PURE__*/function (INTERNAL_ERROR_CODE) {
4
5
  INTERNAL_ERROR_CODE["TOKEN_PERMISSION_ERROR"] = "TOKEN_PERMISSION_ERROR";
5
6
  INTERNAL_ERROR_CODE["RECONNECTION_NETWORK_ISSUE"] = "RECONNECTION_NETWORK_ISSUE";
@@ -9,8 +10,10 @@ export let INTERNAL_ERROR_CODE = /*#__PURE__*/function (INTERNAL_ERROR_CODE) {
9
10
  INTERNAL_ERROR_CODE["CATCHUP_FAILED"] = "CATCHUP_FAILED";
10
11
  INTERNAL_ERROR_CODE["DOCUMENT_RESTORE_ERROR"] = "DOCUMENT_RESTORE_ERROR";
11
12
  INTERNAL_ERROR_CODE["ADD_STEPS_ERROR"] = "ADD_STEPS_ERROR";
13
+ INTERNAL_ERROR_CODE["DOCUMENT_UPDATE_ERROR"] = "DOCUMENT_UPDATE_ERROR";
12
14
  return INTERNAL_ERROR_CODE;
13
15
  }({});
16
+
14
17
  // NCS error coded (generated by NCS)
15
18
  export let NCS_ERROR_CODE = /*#__PURE__*/function (NCS_ERROR_CODE) {
16
19
  NCS_ERROR_CODE["HEAD_VERSION_UPDATE_FAILED"] = "HEAD_VERSION_UPDATE_FAILED";
@@ -42,6 +45,10 @@ export let NCS_ERROR_CODE = /*#__PURE__*/function (NCS_ERROR_CODE) {
42
45
  // - Less common back-end errors
43
46
  // Provider Errors
44
47
  // Channel Errors
48
+ /**
49
+ * When we try to apply state updates to the editor, if that fails to apply the user can enter an invalid state where no
50
+ * changes can be saved to NCS.
51
+ */
45
52
  /**
46
53
  * A union of all possible internal errors, that are mapped to another error if being emitted to the editor.
47
54
  */
@@ -57,6 +64,7 @@ export let PROVIDER_ERROR_CODE = /*#__PURE__*/function (PROVIDER_ERROR_CODE) {
57
64
  PROVIDER_ERROR_CODE["NETWORK_ISSUE"] = "NETWORK_ISSUE";
58
65
  PROVIDER_ERROR_CODE["INVALID_PROVIDER_CONFIGURATION"] = "INVALID_PROVIDER_CONFIGURATION";
59
66
  PROVIDER_ERROR_CODE["INTERNAL_SERVICE_ERROR"] = "INTERNAL_SERVICE_ERROR";
67
+ PROVIDER_ERROR_CODE["DOCUMENT_UPDATE_ERROR"] = "DOCUMENT_UPDATE_ERROR";
60
68
  return PROVIDER_ERROR_CODE;
61
69
  }({});
62
70
 
@@ -154,12 +162,16 @@ export let PROVIDER_ERROR_CODE = /*#__PURE__*/function (PROVIDER_ERROR_CODE) {
154
162
  * A union of all possible provider errors that can be emitted back to the editor.
155
163
  */
156
164
  // Custom Errors
157
- class CustomError extends Error {
158
- constructor(message, error) {
165
+ export class CustomError extends Error {
166
+ constructor(message, error, extraEventAttributes) {
159
167
  super(message);
168
+ _defineProperty(this, "getExtraErrorEventAttributes", () => this.extraEventAttributes);
160
169
  if (typeof (error === null || error === void 0 ? void 0 : error.message) === 'string') {
161
170
  this.message = error.message;
162
171
  }
172
+ if (extraEventAttributes) {
173
+ this.extraEventAttributes = extraEventAttributes;
174
+ }
163
175
  }
164
176
  toJSON() {
165
177
  return {
@@ -227,4 +239,10 @@ export class GetFinalAcknowledgedStateError extends CustomError {
227
239
  super(...args);
228
240
  _defineProperty(this, "name", 'GetFinalAcknowledgedStateError');
229
241
  }
242
+ }
243
+ export class UpdateDocumentError extends CustomError {
244
+ constructor(message, extraAttributes) {
245
+ super(message, undefined, extraAttributes);
246
+ _defineProperty(this, "name", 'UpdateDocumentError');
247
+ }
230
248
  }
@@ -13,6 +13,7 @@ export const createParticipantFromPayload = async (payload, getUser) => {
13
13
  const participant = {
14
14
  name: (user === null || user === void 0 ? void 0 : user.name) || '',
15
15
  avatar: (user === null || user === void 0 ? void 0 : user.avatar) || '',
16
+ email: (user === null || user === void 0 ? void 0 : user.email) || '',
16
17
  sessionId,
17
18
  lastActive: timestamp,
18
19
  userId,
@@ -66,12 +66,19 @@ export class Provider extends Emitter {
66
66
  version,
67
67
  metadata
68
68
  } = this.initialDraft;
69
- // Initial document, version, metadata from initial draft
70
- this.documentService.updateDocument({
71
- doc: document,
72
- version,
73
- metadata
74
- });
69
+ try {
70
+ // Initial document, version, metadata from initial draft
71
+ this.documentService.updateDocument({
72
+ doc: document,
73
+ version,
74
+ metadata
75
+ });
76
+ } catch (e) {
77
+ var _this$analyticsHelper;
78
+ (_this$analyticsHelper = this.analyticsHelper) === null || _this$analyticsHelper === void 0 ? void 0 : _this$analyticsHelper.sendErrorEvent(e, 'Failed to update the document on reconnect, destroying provider');
79
+ // Stop events and connections to step us trying to talk to the backend with an invalid state.
80
+ this.destroy();
81
+ }
75
82
  this.metadataService.updateMetadata(metadata);
76
83
  }
77
84
  this.isProviderInitialized = true;
@@ -90,13 +97,20 @@ export class Provider extends Emitter {
90
97
  metadata
91
98
  }) => {
92
99
  // Initial document and version
93
- this.documentService.updateDocument({
94
- doc,
95
- version,
96
- metadata
97
- });
98
- this.metadataService.updateMetadata(metadata);
99
- this.isProviderInitialized = true;
100
+ try {
101
+ this.documentService.updateDocument({
102
+ doc,
103
+ version,
104
+ metadata
105
+ });
106
+ this.metadataService.updateMetadata(metadata);
107
+ this.isProviderInitialized = true;
108
+ } catch (e) {
109
+ var _this$analyticsHelper2;
110
+ (_this$analyticsHelper2 = this.analyticsHelper) === null || _this$analyticsHelper2 === void 0 ? void 0 : _this$analyticsHelper2.sendErrorEvent(e, 'Failed to update with the init document, destroying provider');
111
+ // Stop events and connections to step us trying to talk to the backend with an invalid state.
112
+ this.destroy();
113
+ }
100
114
  }).on('restore', this.documentService.onRestore).on('steps:added', this.documentService.onStepsAdded).on('metadata:changed', this.metadataService.onMetadataChanged).on('participant:telepointer', payload => this.participantsService.onParticipantTelepointer(payload, this.sessionId)).on('presence:joined', this.participantsService.onPresenceJoined).on('presence', this.participantsService.onPresence).on('participant:left', this.participantsService.onParticipantLeft).on('participant:updated', this.participantsService.onParticipantUpdated).on('disconnect', this.onDisconnected.bind(this)).on('error', this.onErrorHandled).on('status', this.namespaceService.onNamespaceStatusChanged).connect(shouldInitialize);
101
115
  });
102
116
  _defineProperty(this, "setUserId", id => {
@@ -120,13 +134,13 @@ export class Provider extends Emitter {
120
134
  if (((_error$data = error.data) === null || _error$data === void 0 ? void 0 : _error$data.code) === NCS_ERROR_CODE.HEAD_VERSION_UPDATE_FAILED || ((_error$data2 = error.data) === null || _error$data2 === void 0 ? void 0 : _error$data2.code) === NCS_ERROR_CODE.VERSION_NUMBER_ALREADY_EXISTS) {
121
135
  this.documentService.onStepRejectedError();
122
136
  } else {
123
- var _this$analyticsHelper;
124
- (_this$analyticsHelper = this.analyticsHelper) === null || _this$analyticsHelper === void 0 ? void 0 : _this$analyticsHelper.sendErrorEvent(error, 'Error handled');
137
+ var _this$analyticsHelper3;
138
+ (_this$analyticsHelper3 = this.analyticsHelper) === null || _this$analyticsHelper3 === void 0 ? void 0 : _this$analyticsHelper3.sendErrorEvent(error, 'Error handled');
125
139
  const mappedError = errorCodeMapper(error);
126
140
  // Temporarily only emit errors to Confluence very intentionally because they will disconnect the collab provider
127
141
  if (mappedError) {
128
- var _this$analyticsHelper2;
129
- (_this$analyticsHelper2 = this.analyticsHelper) === null || _this$analyticsHelper2 === void 0 ? void 0 : _this$analyticsHelper2.sendErrorEvent(mappedError, 'Error emitted');
142
+ var _this$analyticsHelper4;
143
+ (_this$analyticsHelper4 = this.analyticsHelper) === null || _this$analyticsHelper4 === void 0 ? void 0 : _this$analyticsHelper4.sendErrorEvent(mappedError, 'Error emitted');
130
144
  this.emitCallback('error', mappedError);
131
145
  }
132
146
  }
@@ -153,8 +167,8 @@ export class Provider extends Emitter {
153
167
  try {
154
168
  return this.documentService.getCurrentState();
155
169
  } catch (error) {
156
- var _this$analyticsHelper3;
157
- (_this$analyticsHelper3 = this.analyticsHelper) === null || _this$analyticsHelper3 === void 0 ? void 0 : _this$analyticsHelper3.sendErrorEvent(error, 'Error while returning ADF version of current draft document');
170
+ var _this$analyticsHelper5;
171
+ (_this$analyticsHelper5 = this.analyticsHelper) === null || _this$analyticsHelper5 === void 0 ? void 0 : _this$analyticsHelper5.sendErrorEvent(error, 'Error while returning ADF version of current draft document');
158
172
  throw new GetCurrentStateError('Error while returning the current state of the draft document', error); // Reject the promise so the consumer can react to it failing
159
173
  }
160
174
  });
@@ -167,8 +181,8 @@ export class Provider extends Emitter {
167
181
  try {
168
182
  return await this.documentService.getFinalAcknowledgedState();
169
183
  } catch (error) {
170
- var _this$analyticsHelper4;
171
- (_this$analyticsHelper4 = this.analyticsHelper) === null || _this$analyticsHelper4 === void 0 ? void 0 : _this$analyticsHelper4.sendErrorEvent(error, 'Error while returning ADF version of the final draft document');
184
+ var _this$analyticsHelper6;
185
+ (_this$analyticsHelper6 = this.analyticsHelper) === null || _this$analyticsHelper6 === void 0 ? void 0 : _this$analyticsHelper6.sendErrorEvent(error, 'Error while returning ADF version of the final draft document');
172
186
  throw new GetFinalAcknowledgedStateError('Error while returning the final acknowledged state of the draft document', error); // Reject the promise so the consumer can react to it failing
173
187
  }
174
188
  });
@@ -197,7 +211,7 @@ export class Provider extends Emitter {
197
211
  this.isPreinitializing = false;
198
212
  this.participantsService = new ParticipantsService(this.analyticsHelper, undefined, this.emitCallback, this.config.getUser, this.channel.broadcast, this.channel.sendPresenceJoined, this.getPresenceData, this.setUserId);
199
213
  this.metadataService = new MetadataService(this.emitCallback, this.channel.sendMetadata);
200
- this.documentService = new DocumentService(this.participantsService, this.analyticsHelper, this.channel.fetchCatchup, this.emitCallback, this.channel.broadcast, () => this.userId, this.onErrorHandled, this.metadataService, this.config.failedStepLimitBeforeCatchupOnPublish);
214
+ this.documentService = new DocumentService(this.participantsService, this.analyticsHelper, this.channel.fetchCatchup, this.emitCallback, this.channel.broadcast, () => this.userId, this.onErrorHandled, this.metadataService, this.config.failedStepLimitBeforeCatchupOnPublish, this.config.enableErrorOnFailedDocumentApply);
201
215
  this.getStatePromise = new Promise(resolve => {
202
216
  this.getStatePromiseResolve = resolve;
203
217
  });
@@ -259,8 +273,8 @@ export class Provider extends Emitter {
259
273
  this.isChannelInitialized = true;
260
274
  }
261
275
  } catch (initError) {
262
- var _this$analyticsHelper5;
263
- (_this$analyticsHelper5 = this.analyticsHelper) === null || _this$analyticsHelper5 === void 0 ? void 0 : _this$analyticsHelper5.sendErrorEvent(initError, 'Error while initialising the provider');
276
+ var _this$analyticsHelper7;
277
+ (_this$analyticsHelper7 = this.analyticsHelper) === null || _this$analyticsHelper7 === void 0 ? void 0 : _this$analyticsHelper7.sendErrorEvent(initError, 'Error while initialising the provider');
264
278
  // Throw error so consumers are aware the initialisation failed when initialising themselves
265
279
  throw new ProviderInitialisationError('Provider initialisation error', initError);
266
280
  }
@@ -268,9 +282,9 @@ export class Provider extends Emitter {
268
282
  }
269
283
  checkForCookies() {
270
284
  if (!global.navigator.cookieEnabled) {
271
- var _this$analyticsHelper6;
285
+ var _this$analyticsHelper8;
272
286
  const initError = new ProviderInitialisationError('Cookies are not enabled. Please enable cookies to use collaborative editing.');
273
- (_this$analyticsHelper6 = this.analyticsHelper) === null || _this$analyticsHelper6 === void 0 ? void 0 : _this$analyticsHelper6.sendErrorEvent(initError, 'Error while initialising the provider - cookies disabled');
287
+ (_this$analyticsHelper8 = this.analyticsHelper) === null || _this$analyticsHelper8 === void 0 ? void 0 : _this$analyticsHelper8.sendErrorEvent(initError, 'Error while initialising the provider - cookies disabled');
274
288
  throw new ProviderInitialisationError('Provider initialisation error - cookies disabled', initError);
275
289
  }
276
290
  }
@@ -304,8 +318,8 @@ export class Provider extends Emitter {
304
318
  }
305
319
  this.documentService.send(_tr, _oldState, newState);
306
320
  } catch (error) {
307
- var _this$analyticsHelper7;
308
- (_this$analyticsHelper7 = this.analyticsHelper) === null || _this$analyticsHelper7 === void 0 ? void 0 : _this$analyticsHelper7.sendErrorEvent(error, 'Error while sending steps for a transaction');
321
+ var _this$analyticsHelper9;
322
+ (_this$analyticsHelper9 = this.analyticsHelper) === null || _this$analyticsHelper9 === void 0 ? void 0 : _this$analyticsHelper9.sendErrorEvent(error, 'Error while sending steps for a transaction');
309
323
  throw new SendTransactionError('Error while sending steps for a transaction', error);
310
324
  }
311
325
  }
@@ -330,9 +344,9 @@ export class Provider extends Emitter {
330
344
  this.channel.broadcast('participant:telepointer', payload, callback);
331
345
  }
332
346
  } catch (error) {
333
- var _this$analyticsHelper8;
347
+ var _this$analyticsHelper10;
334
348
  // We don't want to throw errors for Presence features as they tend to self-restore
335
- (_this$analyticsHelper8 = this.analyticsHelper) === null || _this$analyticsHelper8 === void 0 ? void 0 : _this$analyticsHelper8.sendErrorEvent(error, 'Error while sending message - telepointer');
349
+ (_this$analyticsHelper10 = this.analyticsHelper) === null || _this$analyticsHelper10 === void 0 ? void 0 : _this$analyticsHelper10.sendErrorEvent(error, 'Error while sending message - telepointer');
336
350
  }
337
351
  }
338
352
  /**
@@ -364,8 +378,8 @@ export class Provider extends Emitter {
364
378
  super.unsubscribeAll();
365
379
  this.channel.disconnect();
366
380
  } catch (error) {
367
- var _this$analyticsHelper9;
368
- (_this$analyticsHelper9 = this.analyticsHelper) === null || _this$analyticsHelper9 === void 0 ? void 0 : _this$analyticsHelper9.sendErrorEvent(error, 'Error while shutting down the collab provider');
381
+ var _this$analyticsHelper11;
382
+ (_this$analyticsHelper11 = this.analyticsHelper) === null || _this$analyticsHelper11 === void 0 ? void 0 : _this$analyticsHelper11.sendErrorEvent(error, 'Error while shutting down the collab provider');
369
383
  throw new DestroyError('Error while shutting down the collab provider', error);
370
384
  }
371
385
  this.clearTimers();
@@ -383,8 +397,8 @@ export class Provider extends Emitter {
383
397
  try {
384
398
  this.metadataService.setTitle(title, broadcast);
385
399
  } catch (error) {
386
- var _this$analyticsHelper10;
387
- (_this$analyticsHelper10 = this.analyticsHelper) === null || _this$analyticsHelper10 === void 0 ? void 0 : _this$analyticsHelper10.sendErrorEvent(error, 'Error while setting title');
400
+ var _this$analyticsHelper12;
401
+ (_this$analyticsHelper12 = this.analyticsHelper) === null || _this$analyticsHelper12 === void 0 ? void 0 : _this$analyticsHelper12.sendErrorEvent(error, 'Error while setting title');
388
402
  throw new SetTitleError('Error while setting title', error);
389
403
  }
390
404
  }
@@ -400,8 +414,8 @@ export class Provider extends Emitter {
400
414
  try {
401
415
  this.metadataService.setEditorWidth(editorWidth, broadcast);
402
416
  } catch (error) {
403
- var _this$analyticsHelper11;
404
- (_this$analyticsHelper11 = this.analyticsHelper) === null || _this$analyticsHelper11 === void 0 ? void 0 : _this$analyticsHelper11.sendErrorEvent(error, 'Error while setting editor width');
417
+ var _this$analyticsHelper13;
418
+ (_this$analyticsHelper13 = this.analyticsHelper) === null || _this$analyticsHelper13 === void 0 ? void 0 : _this$analyticsHelper13.sendErrorEvent(error, 'Error while setting editor width');
405
419
  throw new SetEditorWidthError('Error while setting editor width', error);
406
420
  }
407
421
  }
@@ -415,8 +429,8 @@ export class Provider extends Emitter {
415
429
  try {
416
430
  this.metadataService.setMetadata(metadata);
417
431
  } catch (error) {
418
- var _this$analyticsHelper12;
419
- (_this$analyticsHelper12 = this.analyticsHelper) === null || _this$analyticsHelper12 === void 0 ? void 0 : _this$analyticsHelper12.sendErrorEvent(error, 'Error while setting metadata');
432
+ var _this$analyticsHelper14;
433
+ (_this$analyticsHelper14 = this.analyticsHelper) === null || _this$analyticsHelper14 === void 0 ? void 0 : _this$analyticsHelper14.sendErrorEvent(error, 'Error while setting metadata');
420
434
  throw new SetMetadataError('Error while setting metadata', error);
421
435
  }
422
436
  }
@@ -1,5 +1,5 @@
1
1
  export const name = "@atlaskit/collab-provider";
2
- export const version = "9.3.1";
2
+ export const version = "9.5.0";
3
3
  export const nextMajorVersion = () => {
4
4
  return [Number(version.split('.')[0]) + 1, 0, 0].join('.');
5
5
  };
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@atlaskit/collab-provider",
3
- "version": "9.3.1",
3
+ "version": "9.5.0",
4
4
  "sideEffects": false
5
5
  }
@@ -8,6 +8,7 @@ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { va
8
8
  import { EVENT_ACTION } from '../helpers/const';
9
9
  import { name as packageName, version as packageVersion } from '../version-wrapper';
10
10
  import { network } from '../connectivity/singleton';
11
+ import { CustomError } from '../errors/error-types';
11
12
  var EVENT_SUBJECT = 'collab';
12
13
  var COLLAB_SERVICE = /*#__PURE__*/function (COLLAB_SERVICE) {
13
14
  COLLAB_SERVICE["NCS"] = "ncs";
@@ -68,13 +69,17 @@ var AnalyticsHelper = /*#__PURE__*/function () {
68
69
  _createClass(AnalyticsHelper, [{
69
70
  key: "sendErrorEvent",
70
71
  value: function sendErrorEvent(error, errorMessage) {
72
+ var errorExtraAttributes = {};
73
+ if (error instanceof CustomError) {
74
+ errorExtraAttributes = error.getExtraErrorEventAttributes() || {};
75
+ }
71
76
  var errorAnalyticsEvent = {
72
77
  eventAction: EVENT_ACTION.ERROR,
73
- attributes: {
78
+ attributes: _objectSpread({
74
79
  documentAri: this.documentAri,
75
80
  errorMessage: errorMessage,
76
81
  errorName: error instanceof Error ? error.name : undefined
77
- },
82
+ }, errorExtraAttributes),
78
83
  nonPrivacySafeAttributes: {
79
84
  error: error
80
85
  }
@@ -390,32 +390,33 @@ export var Channel = /*#__PURE__*/function (_Emitter) {
390
390
  permissionTokenRefresh = _this$config2.permissionTokenRefresh,
391
391
  cacheToken = _this$config2.cacheToken;
392
392
  var auth;
393
- var authData = {
394
- // The initialized status. If false, BE will send document, otherwise not.
395
- initialized: this.initialized,
396
- // ESS-1009 Allow to opt-in into 404 response
397
- need404: this.config.need404
398
- };
399
393
  if (permissionTokenRefresh) {
400
394
  auth = /*#__PURE__*/function () {
401
395
  var _ref3 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2(cb) {
402
- var token, _data, _data$meta, authenticationError;
396
+ var authData, token, _data, _data$meta, authenticationError;
403
397
  return _regeneratorRuntime.wrap(function _callee2$(_context2) {
404
398
  while (1) switch (_context2.prev = _context2.next) {
405
399
  case 0:
400
+ // Rebuild authData to ensure values are current
401
+ authData = {
402
+ // The initialized status. If false, BE will send document, otherwise not.
403
+ initialized: _this2.initialized,
404
+ // ESS-1009 Allow to opt-in into 404 response
405
+ need404: _this2.config.need404
406
+ }; // use the cached token if caching in enabled and token valid
406
407
  if (!(cacheToken && _this2.token)) {
407
- _context2.next = 5;
408
+ _context2.next = 6;
408
409
  break;
409
410
  }
410
411
  authData.token = _this2.token;
411
412
  cb(authData);
412
- _context2.next = 17;
413
+ _context2.next = 18;
413
414
  break;
414
- case 5:
415
- _context2.prev = 5;
416
- _context2.next = 8;
415
+ case 6:
416
+ _context2.prev = 6;
417
+ _context2.next = 9;
417
418
  return permissionTokenRefresh();
418
- case 8:
419
+ case 9:
419
420
  token = _context2.sent;
420
421
  if (token) {
421
422
  // save token locally
@@ -426,11 +427,11 @@ export var Channel = /*#__PURE__*/function (_Emitter) {
426
427
  authData.token = undefined;
427
428
  }
428
429
  cb(authData);
429
- _context2.next = 17;
430
+ _context2.next = 18;
430
431
  break;
431
- case 13:
432
- _context2.prev = 13;
433
- _context2.t0 = _context2["catch"](5);
432
+ case 14:
433
+ _context2.prev = 14;
434
+ _context2.t0 = _context2["catch"](6);
434
435
  // Pass the error back to the consumers so they can deal with exceptional cases themselves (eg. no permissions because the page was deleted)
435
436
  authenticationError = {
436
437
  message: 'Insufficient editing permissions',
@@ -446,18 +447,23 @@ export var Channel = /*#__PURE__*/function (_Emitter) {
446
447
  };
447
448
 
448
449
  _this2.emit('error', authenticationError);
449
- case 17:
450
+ case 18:
450
451
  case "end":
451
452
  return _context2.stop();
452
453
  }
453
- }, _callee2, null, [[5, 13]]);
454
+ }, _callee2, null, [[6, 14]]);
454
455
  }));
455
456
  return function auth(_x2) {
456
457
  return _ref3.apply(this, arguments);
457
458
  };
458
459
  }();
459
460
  } else {
460
- auth = authData;
461
+ auth = {
462
+ // The initialized status. If false, BE will send document, otherwise not.
463
+ initialized: this.initialized,
464
+ // ESS-1009 Allow to opt-in into 404 response
465
+ need404: this.config.need404
466
+ };
461
467
  }
462
468
  this.socket = createSocket("".concat(url, "/session/").concat(documentAri), auth, this.config.productInfo);
463
469