@atlaskit/collab-provider 9.0.1 → 9.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.
@@ -30,16 +30,19 @@ export var DocumentService = /*#__PURE__*/function () {
30
30
  * @param analyticsHelper - Helper for analytics events
31
31
  * @param fetchCatchup - Function to fetch "catchup" data, data required to rebase current steps to the latest version.
32
32
  * @param providerEmitCallback - Callback for emitting events to listeners on the provider
33
- * @param broadcastMetadata - Callback for broadcasting metadata changes to other clients
34
33
  * @param broadcast - Callback for broadcasting events to other clients
35
34
  * @param getUserId - Callback to fetch the current user's ID
36
35
  * @param onErrorHandled - Callback to handle
36
+ * @param metadataService
37
+ * @param failedStepsBeforeCatchupOnPublish - Control MAX_STEP_REJECTED_ERROR during page publishes.
37
38
  */
38
39
  function DocumentService(participantsService, analyticsHelper, fetchCatchup, providerEmitCallback, broadcast, getUserId, onErrorHandled, metadataService) {
39
40
  var _this = this;
41
+ var failedStepsBeforeCatchupOnPublish = arguments.length > 8 && arguments[8] !== undefined ? arguments[8] : MAX_STEP_REJECTED_ERROR;
40
42
  _classCallCheck(this, DocumentService);
41
43
  // Fires analytics to editor when collab editor cannot sync up
42
44
  _defineProperty(this, "stepRejectCounter", 0);
45
+ _defineProperty(this, "aggressiveCatchup", false);
43
46
  /**
44
47
  * To prevent calling catchup to often, use lodash throttle to reduce the frequency
45
48
  */
@@ -274,34 +277,37 @@ export var DocumentService = /*#__PURE__*/function () {
274
277
  return _regeneratorRuntime.wrap(function _callee3$(_context3) {
275
278
  while (1) switch (_context3.prev = _context3.next) {
276
279
  case 0:
277
- _context3.prev = 0;
280
+ _this.aggressiveCatchup = true;
281
+ _context3.prev = 1;
278
282
  startMeasure(MEASURE_NAME.PUBLISH_PAGE, _this.analyticsHelper);
279
- _context3.next = 4;
283
+ _context3.next = 5;
280
284
  return _this.commitUnconfirmedSteps();
281
- case 4:
282
- _context3.next = 6;
285
+ case 5:
286
+ _context3.next = 7;
283
287
  return _this.getCurrentState();
284
- case 6:
288
+ case 7:
285
289
  currentState = _context3.sent;
286
290
  measure = stopMeasure(MEASURE_NAME.PUBLISH_PAGE, _this.analyticsHelper);
287
291
  (_this$analyticsHelper14 = _this.analyticsHelper) === null || _this$analyticsHelper14 === void 0 ? void 0 : _this$analyticsHelper14.sendActionEvent(EVENT_ACTION.PUBLISH_PAGE, EVENT_STATUS.SUCCESS, {
288
292
  latency: measure === null || measure === void 0 ? void 0 : measure.duration
289
293
  });
294
+ _this.aggressiveCatchup = false;
290
295
  return _context3.abrupt("return", currentState);
291
- case 12:
292
- _context3.prev = 12;
293
- _context3.t0 = _context3["catch"](0);
296
+ case 14:
297
+ _context3.prev = 14;
298
+ _context3.t0 = _context3["catch"](1);
299
+ _this.aggressiveCatchup = false;
294
300
  _measure2 = stopMeasure(MEASURE_NAME.PUBLISH_PAGE, _this.analyticsHelper);
295
301
  (_this$analyticsHelper15 = _this.analyticsHelper) === null || _this$analyticsHelper15 === void 0 ? void 0 : _this$analyticsHelper15.sendActionEvent(EVENT_ACTION.PUBLISH_PAGE, EVENT_STATUS.FAILURE, {
296
302
  latency: _measure2 === null || _measure2 === void 0 ? void 0 : _measure2.duration
297
303
  });
298
304
  (_this$analyticsHelper16 = _this.analyticsHelper) === null || _this$analyticsHelper16 === void 0 ? void 0 : _this$analyticsHelper16.sendErrorEvent(_context3.t0, 'Error while returning ADF version of the final draft document');
299
305
  throw _context3.t0;
300
- case 18:
306
+ case 21:
301
307
  case "end":
302
308
  return _context3.stop();
303
309
  }
304
- }, _callee3, null, [[0, 12]]);
310
+ }, _callee3, null, [[1, 14]]);
305
311
  })));
306
312
  _defineProperty(this, "updateDocument", function (_ref6) {
307
313
  var doc = _ref6.doc,
@@ -402,10 +408,17 @@ export var DocumentService = /*#__PURE__*/function () {
402
408
  }, _callee4, null, [[1, 23]]);
403
409
  })));
404
410
  _defineProperty(this, "onStepRejectedError", function () {
411
+ var _this$analyticsHelper20;
405
412
  _this.stepRejectCounter++;
406
413
  logger("Steps rejected (tries=".concat(_this.stepRejectCounter, ")"));
407
- if (_this.stepRejectCounter >= MAX_STEP_REJECTED_ERROR) {
414
+ (_this$analyticsHelper20 = _this.analyticsHelper) === null || _this$analyticsHelper20 === void 0 ? void 0 : _this$analyticsHelper20.sendActionEvent(EVENT_ACTION.SEND_STEPS_RETRY, EVENT_STATUS.INFO, {
415
+ count: _this.stepRejectCounter
416
+ });
417
+ var maxRetries = _this.aggressiveCatchup ? _this.failedStepsBeforeCatchupOnPublish : MAX_STEP_REJECTED_ERROR;
418
+ if (_this.stepRejectCounter >= maxRetries) {
419
+ var _this$analyticsHelper21;
408
420
  logger("The steps were rejected too many times (tries=".concat(_this.stepRejectCounter, ", limit=").concat(MAX_STEP_REJECTED_ERROR, "). Trying to catch-up."));
421
+ (_this$analyticsHelper21 = _this.analyticsHelper) === null || _this$analyticsHelper21 === void 0 ? void 0 : _this$analyticsHelper21.sendActionEvent(EVENT_ACTION.CATCHUP_AFTER_MAX_SEND_STEPS_RETRY, EVENT_STATUS.INFO);
409
422
  _this.throttledCatchup();
410
423
  } else {
411
424
  // If committing steps failed try again automatically in 1s
@@ -425,6 +438,7 @@ export var DocumentService = /*#__PURE__*/function () {
425
438
  this.getUserId = getUserId;
426
439
  this.onErrorHandled = onErrorHandled;
427
440
  this.metadataService = metadataService;
441
+ this.failedStepsBeforeCatchupOnPublish = failedStepsBeforeCatchupOnPublish;
428
442
  this.stepQueue = new StepQueueState();
429
443
  }
430
444
  _createClass(DocumentService, [{
@@ -476,9 +490,9 @@ export var DocumentService = /*#__PURE__*/function () {
476
490
  }, 100);
477
491
  }
478
492
  } catch (error) {
479
- var _this$analyticsHelper20;
493
+ var _this$analyticsHelper22;
480
494
  logger("Processing steps failed with error: ".concat(error, ". Triggering catch up call."));
481
- (_this$analyticsHelper20 = this.analyticsHelper) === null || _this$analyticsHelper20 === void 0 ? void 0 : _this$analyticsHelper20.sendErrorEvent(error, 'Error while processing steps');
495
+ (_this$analyticsHelper22 = this.analyticsHelper) === null || _this$analyticsHelper22 === void 0 ? void 0 : _this$analyticsHelper22.sendErrorEvent(error, 'Error while processing steps');
482
496
  this.throttledCatchup();
483
497
  }
484
498
  }
@@ -543,7 +557,8 @@ export var DocumentService = /*#__PURE__*/function () {
543
557
  version: version,
544
558
  onStepsAdded: this.onStepsAdded,
545
559
  onErrorHandled: this.onErrorHandled,
546
- analyticsHelper: this.analyticsHelper
560
+ analyticsHelper: this.analyticsHelper,
561
+ emit: this.providerEmitCallback
547
562
  });
548
563
  }
549
564
  }]);
@@ -10,12 +10,15 @@ export var EVENT_ACTION = /*#__PURE__*/function (EVENT_ACTION) {
10
10
  EVENT_ACTION["PUBLISH_PAGE"] = "publishPage";
11
11
  EVENT_ACTION["GET_CURRENT_STATE"] = "getCurrentState";
12
12
  EVENT_ACTION["INVALIDATE_TOKEN"] = "invalidateToken";
13
+ EVENT_ACTION["SEND_STEPS_RETRY"] = "sendStepsRetry";
14
+ EVENT_ACTION["CATCHUP_AFTER_MAX_SEND_STEPS_RETRY"] = "catchupAfterMaxSendStepsRetry";
13
15
  EVENT_ACTION["DROPPED_STEPS"] = "droppedStepInCatchup";
14
16
  return EVENT_ACTION;
15
17
  }({});
16
18
  export var EVENT_STATUS = /*#__PURE__*/function (EVENT_STATUS) {
17
19
  EVENT_STATUS["SUCCESS"] = "SUCCESS";
18
20
  EVENT_STATUS["FAILURE"] = "FAILURE";
21
+ EVENT_STATUS["INFO"] = "INFO";
19
22
  return EVENT_STATUS;
20
23
  }({});
21
24
  export var ADD_STEPS_TYPE = /*#__PURE__*/function (ADD_STEPS_TYPE) {
@@ -16,7 +16,8 @@ export var commitStep = function commitStep(_ref) {
16
16
  clientId = _ref.clientId,
17
17
  onStepsAdded = _ref.onStepsAdded,
18
18
  onErrorHandled = _ref.onErrorHandled,
19
- analyticsHelper = _ref.analyticsHelper;
19
+ analyticsHelper = _ref.analyticsHelper,
20
+ emit = _ref.emit;
20
21
  var stepsWithClientAndUserId = steps.map(function (step) {
21
22
  return _objectSpread(_objectSpread({}, step.toJSON()), {}, {
22
23
  clientId: clientId,
@@ -24,6 +25,10 @@ export var commitStep = function commitStep(_ref) {
24
25
  });
25
26
  });
26
27
  var start = new Date().getTime();
28
+ emit('commit-status', {
29
+ status: 'attempt',
30
+ version: version
31
+ });
27
32
  try {
28
33
  broadcast('steps:commit', {
29
34
  steps: stepsWithClientAndUserId,
@@ -43,6 +48,10 @@ export var commitStep = function commitStep(_ref) {
43
48
  return stepWithClientAndUserId.stepType;
44
49
  })
45
50
  });
51
+ emit('commit-status', {
52
+ status: 'success',
53
+ version: response.version
54
+ });
46
55
  } else if (response.type === AcknowledgementResponseTypes.ERROR) {
47
56
  onErrorHandled(response.error);
48
57
  analyticsHelper === null || analyticsHelper === void 0 ? void 0 : analyticsHelper.sendActionEvent(EVENT_ACTION.ADD_STEPS, EVENT_STATUS.FAILURE, {
@@ -53,14 +62,26 @@ export var commitStep = function commitStep(_ref) {
53
62
  latency: latency
54
63
  });
55
64
  analyticsHelper === null || analyticsHelper === void 0 ? void 0 : analyticsHelper.sendErrorEvent(response.error, 'Error while adding steps - Acknowledgement Error');
65
+ emit('commit-status', {
66
+ status: 'failure',
67
+ version: version
68
+ });
56
69
  } else {
57
70
  analyticsHelper === null || analyticsHelper === void 0 ? void 0 : analyticsHelper.sendErrorEvent(
58
71
  // @ts-expect-error We didn't type the invalid type case
59
72
  new Error("Response type: ".concat((response === null || response === void 0 ? void 0 : response.type) || 'No response type')), 'Error while adding steps - Invalid Acknowledgement');
73
+ emit('commit-status', {
74
+ status: 'failure',
75
+ version: version
76
+ });
60
77
  }
61
78
  });
62
79
  } catch (error) {
63
80
  analyticsHelper === null || analyticsHelper === void 0 ? void 0 : analyticsHelper.sendErrorEvent(error, 'Error while adding steps - Broadcast threw exception');
81
+ emit('commit-status', {
82
+ status: 'failure',
83
+ version: version
84
+ });
64
85
  }
65
86
  };
66
87
  export var throttledCommitStep = throttle(commitStep, SEND_STEPS_THROTTLE, {
@@ -23,6 +23,7 @@ import { ParticipantsService } from '../participants/participants-service';
23
23
  import { errorCodeMapper } from '../errors/error-code-mapper';
24
24
  var logger = createLogger('Provider', 'black');
25
25
  var OUT_OF_SYNC_PERIOD = 3 * 1000; // 3 seconds
26
+ var PRELOAD_DRAFT_SYNC_PERIOD = 15 * 1000; // 15 seconds
26
27
 
27
28
  export var MAX_STEP_REJECTED_ERROR = 15;
28
29
  export var Provider = /*#__PURE__*/function (_Emitter) {
@@ -67,20 +68,27 @@ export var Provider = /*#__PURE__*/function (_Emitter) {
67
68
  sid: sid,
68
69
  initial: !initialized
69
70
  });
70
- // If initial draft is already present and the channel is initialized,
71
- // fire the provider's init event with initial draft document and version
71
+
72
+ // Early initialization with initial draft passed via provider
72
73
  if (_this.initialDraft && initialized && !_this.isProviderInitialized) {
73
- var _this$initialDraft = _this.initialDraft,
74
- document = _this$initialDraft.document,
75
- version = _this$initialDraft.version,
76
- metadata = _this$initialDraft.metadata;
77
- // Initial document, version, metadata from initial draft
78
- _this.documentService.updateDocument({
79
- doc: document,
80
- version: version,
81
- metadata: metadata
82
- });
83
- _this.metadataService.updateMetadata(metadata);
74
+ // Call catchup if the draft has become stale since being passed to provider
75
+ if (_this.isDraftTimestampStale()) {
76
+ _this.documentService.throttledCatchup();
77
+ }
78
+ // If the initial draft is already up to date, update the document with that of the initial draft
79
+ else {
80
+ var _this$initialDraft = _this.initialDraft,
81
+ document = _this$initialDraft.document,
82
+ version = _this$initialDraft.version,
83
+ metadata = _this$initialDraft.metadata;
84
+ // Initial document, version, metadata from initial draft
85
+ _this.documentService.updateDocument({
86
+ doc: document,
87
+ version: version,
88
+ metadata: metadata
89
+ });
90
+ _this.metadataService.updateMetadata(metadata);
91
+ }
84
92
  _this.isProviderInitialized = true;
85
93
  }
86
94
  // If already initialized, `connected` means reconnected
@@ -221,7 +229,7 @@ export var Provider = /*#__PURE__*/function (_Emitter) {
221
229
  _this.metadataService = new MetadataService(_this.emitCallback, _this.channel.sendMetadata);
222
230
  _this.documentService = new DocumentService(_this.participantsService, _this.analyticsHelper, _this.channel.fetchCatchup, _this.emitCallback, _this.channel.broadcast, function () {
223
231
  return _this.userId;
224
- }, _this.onErrorHandled, _this.metadataService);
232
+ }, _this.onErrorHandled, _this.metadataService, _this.config.failedStepLimitBeforeCatchupOnPublish);
225
233
  _this.getStatePromise = new Promise(function (resolve) {
226
234
  _this.getStatePromiseResolve = resolve;
227
235
  });
@@ -308,6 +316,21 @@ export var Provider = /*#__PURE__*/function (_Emitter) {
308
316
  }
309
317
  }
310
318
 
319
+ /**
320
+ * Checks the provider's initial draft timestamp to determine if it is stale.
321
+ * Returns true only if the time elapsed since the draft timestamp is greater than or
322
+ * equal to a predetermined timeout. Returns false in all other cases.
323
+ */
324
+ }, {
325
+ key: "isDraftTimestampStale",
326
+ value: function isDraftTimestampStale() {
327
+ var _this$initialDraft2;
328
+ if (!((_this$initialDraft2 = this.initialDraft) !== null && _this$initialDraft2 !== void 0 && _this$initialDraft2.timestamp)) {
329
+ return false;
330
+ }
331
+ return Date.now() - this.initialDraft.timestamp >= PRELOAD_DRAFT_SYNC_PERIOD;
332
+ }
333
+
311
334
  /**
312
335
  * Send steps from transaction to NCS (and as a consequence to other participants), called from the collab-edit plugin in the editor
313
336
  * @param {Transaction} _tr Deprecated, included to keep API consistent with Synchrony provider
@@ -1,5 +1,5 @@
1
1
  export var name = "@atlaskit/collab-provider";
2
- export var version = "9.0.1";
2
+ export var version = "9.2.0";
3
3
  export var nextMajorVersion = function 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.0.1",
3
+ "version": "9.2.0",
4
4
  "sideEffects": false
5
5
  }
@@ -16,10 +16,12 @@ export declare class DocumentService {
16
16
  private getUserId;
17
17
  private onErrorHandled;
18
18
  private metadataService;
19
+ private failedStepsBeforeCatchupOnPublish;
19
20
  private getState;
20
21
  private onSyncUpError?;
21
22
  private stepQueue;
22
23
  private stepRejectCounter;
24
+ private aggressiveCatchup;
23
25
  private clientId?;
24
26
  /**
25
27
  *
@@ -28,12 +30,13 @@ export declare class DocumentService {
28
30
  * @param analyticsHelper - Helper for analytics events
29
31
  * @param fetchCatchup - Function to fetch "catchup" data, data required to rebase current steps to the latest version.
30
32
  * @param providerEmitCallback - Callback for emitting events to listeners on the provider
31
- * @param broadcastMetadata - Callback for broadcasting metadata changes to other clients
32
33
  * @param broadcast - Callback for broadcasting events to other clients
33
34
  * @param getUserId - Callback to fetch the current user's ID
34
35
  * @param onErrorHandled - Callback to handle
36
+ * @param metadataService
37
+ * @param failedStepsBeforeCatchupOnPublish - Control MAX_STEP_REJECTED_ERROR during page publishes.
35
38
  */
36
- constructor(participantsService: ParticipantsService, analyticsHelper: AnalyticsHelper | undefined, fetchCatchup: (fromVersion: number) => Promise<CatchupResponse>, providerEmitCallback: (evt: keyof CollabEvents, data: any) => void, broadcast: <K extends keyof ChannelEvent>(type: K, data: Omit<ChannelEvent[K], 'timestamp'>, callback?: Function) => void, getUserId: () => string | undefined, onErrorHandled: (error: InternalError) => void, metadataService: MetadataService);
39
+ constructor(participantsService: ParticipantsService, analyticsHelper: AnalyticsHelper | undefined, fetchCatchup: (fromVersion: number) => Promise<CatchupResponse>, providerEmitCallback: (evt: keyof CollabEvents, data: any) => void, broadcast: <K extends keyof ChannelEvent>(type: K, data: Omit<ChannelEvent[K], 'timestamp'>, callback?: Function) => void, getUserId: () => string | undefined, onErrorHandled: (error: InternalError) => void, metadataService: MetadataService, failedStepsBeforeCatchupOnPublish?: number);
37
40
  /**
38
41
  * To prevent calling catchup to often, use lodash throttle to reduce the frequency
39
42
  */
@@ -11,11 +11,14 @@ export declare enum EVENT_ACTION {
11
11
  PUBLISH_PAGE = "publishPage",
12
12
  GET_CURRENT_STATE = "getCurrentState",
13
13
  INVALIDATE_TOKEN = "invalidateToken",
14
+ SEND_STEPS_RETRY = "sendStepsRetry",
15
+ CATCHUP_AFTER_MAX_SEND_STEPS_RETRY = "catchupAfterMaxSendStepsRetry",
14
16
  DROPPED_STEPS = "droppedStepInCatchup"
15
17
  }
16
18
  export declare enum EVENT_STATUS {
17
19
  SUCCESS = "SUCCESS",
18
- FAILURE = "FAILURE"
20
+ FAILURE = "FAILURE",
21
+ INFO = "INFO"
19
22
  }
20
23
  export declare enum ADD_STEPS_TYPE {
21
24
  ACCEPTED = "ACCEPTED",
@@ -185,7 +188,22 @@ type GetCurrentStateFailureAnalyticsEvent = {
185
188
  latency?: number;
186
189
  };
187
190
  };
188
- export type ActionAnalyticsEvent = AddStepsSuccessAnalyticsEvent | AddStepsFailureAnalyticsEvent | ReInitDocFailAnalyticsEvent | ReInitDocSuccessAnalyticsEvent | ConnectionSuccessAnalyticsEvent | ConnectionFailureAnalyticsEvent | CatchUpSuccessAnalyticsEvent | CatchUpFailureAnalyticsEvent | DocumentInitSuccessAnalyticsEvent | UpdateParticipantsSuccessAnalyticsEvent | CommitUnconfirmedStepsSuccessAnalyticsEvent | CommitUnconfirmedStepsFailureAnalyticsEvent | PublishPageSuccessAnalyticsEvent | PublishPageFailureAnalyticsEvent | GetCurrentStateSuccessAnalyticsEvent | GetCurrentStateFailureAnalyticsEvent | InvalidateTokenAnalyticsEvent | CatchUpDroppedStepsEvent;
191
+ type SendStepsRetryAnalyticsEvent = {
192
+ eventAction: EVENT_ACTION.SEND_STEPS_RETRY;
193
+ attributes: {
194
+ documentAri: string;
195
+ eventStatus: EVENT_STATUS.SUCCESS;
196
+ count: number;
197
+ };
198
+ };
199
+ type CatchupAfterMaxSendStepsRetryAnalyticsEvent = {
200
+ eventAction: EVENT_ACTION.CATCHUP_AFTER_MAX_SEND_STEPS_RETRY;
201
+ attributes: {
202
+ documentAri: string;
203
+ eventStatus: EVENT_STATUS.SUCCESS;
204
+ };
205
+ };
206
+ export type ActionAnalyticsEvent = AddStepsSuccessAnalyticsEvent | AddStepsFailureAnalyticsEvent | ReInitDocFailAnalyticsEvent | ReInitDocSuccessAnalyticsEvent | ConnectionSuccessAnalyticsEvent | ConnectionFailureAnalyticsEvent | CatchUpSuccessAnalyticsEvent | CatchUpFailureAnalyticsEvent | DocumentInitSuccessAnalyticsEvent | UpdateParticipantsSuccessAnalyticsEvent | CommitUnconfirmedStepsSuccessAnalyticsEvent | CommitUnconfirmedStepsFailureAnalyticsEvent | PublishPageSuccessAnalyticsEvent | PublishPageFailureAnalyticsEvent | GetCurrentStateSuccessAnalyticsEvent | GetCurrentStateFailureAnalyticsEvent | InvalidateTokenAnalyticsEvent | SendStepsRetryAnalyticsEvent | CatchupAfterMaxSendStepsRetryAnalyticsEvent | CatchUpDroppedStepsEvent;
189
207
  export declare const ACK_MAX_TRY = 60;
190
208
  export declare const CONFLUENCE = "confluence";
191
209
  export {};
@@ -1,9 +1,9 @@
1
1
  /// <reference types="lodash" />
2
- import { ChannelEvent, StepsPayload } from '../types';
2
+ import { ChannelEvent, CollabCommitStatusEventPayload, CollabEvents, StepsPayload } from '../types';
3
3
  import type { Step as ProseMirrorStep } from 'prosemirror-transform';
4
4
  import AnalyticsHelper from '../analytics/analytics-helper';
5
5
  import type { InternalError } from '../errors/error-types';
6
- export declare const commitStep: ({ broadcast, steps, version, userId, clientId, onStepsAdded, onErrorHandled, analyticsHelper, }: {
6
+ export declare const commitStep: ({ broadcast, steps, version, userId, clientId, onStepsAdded, onErrorHandled, analyticsHelper, emit, }: {
7
7
  broadcast: <K extends keyof ChannelEvent>(type: K, data: Omit<ChannelEvent[K], "timestamp">, callback?: Function) => void;
8
8
  steps: readonly ProseMirrorStep[];
9
9
  version: number;
@@ -12,8 +12,9 @@ export declare const commitStep: ({ broadcast, steps, version, userId, clientId,
12
12
  onStepsAdded: (data: StepsPayload) => void;
13
13
  onErrorHandled: (error: InternalError) => void;
14
14
  analyticsHelper?: AnalyticsHelper | undefined;
15
+ emit: (evt: keyof CollabEvents, data: CollabCommitStatusEventPayload) => void;
15
16
  }) => void;
16
- export declare const throttledCommitStep: import("lodash").DebouncedFunc<({ broadcast, steps, version, userId, clientId, onStepsAdded, onErrorHandled, analyticsHelper, }: {
17
+ export declare const throttledCommitStep: import("lodash").DebouncedFunc<({ broadcast, steps, version, userId, clientId, onStepsAdded, onErrorHandled, analyticsHelper, emit, }: {
17
18
  broadcast: <K extends keyof ChannelEvent>(type: K, data: Omit<ChannelEvent[K], "timestamp">, callback?: Function) => void;
18
19
  steps: readonly ProseMirrorStep[];
19
20
  version: number;
@@ -22,4 +23,5 @@ export declare const throttledCommitStep: import("lodash").DebouncedFunc<({ broa
22
23
  onStepsAdded: (data: StepsPayload) => void;
23
24
  onErrorHandled: (error: InternalError) => void;
24
25
  analyticsHelper?: AnalyticsHelper | undefined;
26
+ emit: (evt: keyof CollabEvents, data: CollabCommitStatusEventPayload) => void;
25
27
  }) => void>;
@@ -59,6 +59,12 @@ export declare class Provider extends Emitter<CollabEvents> implements BaseEvent
59
59
  onSyncUpError?: SyncUpErrorFunction;
60
60
  }): this;
61
61
  private checkForCookies;
62
+ /**
63
+ * Checks the provider's initial draft timestamp to determine if it is stale.
64
+ * Returns true only if the time elapsed since the draft timestamp is greater than or
65
+ * equal to a predetermined timeout. Returns false in all other cases.
66
+ */
67
+ private isDraftTimestampStale;
62
68
  /**
63
69
  * Send steps from transaction to NCS (and as a consequence to other participants), called from the collab-edit plugin in the editor
64
70
  * @param {Transaction} _tr Deprecated, included to keep API consistent with Synchrony provider
@@ -61,6 +61,7 @@ export interface InitialDraft {
61
61
  document: JSONDocNode;
62
62
  version: number;
63
63
  metadata?: Metadata;
64
+ timestamp?: number;
64
65
  }
65
66
  export interface Config {
66
67
  url: string;
@@ -90,6 +91,11 @@ export interface Config {
90
91
  */
91
92
  throwOnNotConnected?: boolean;
92
93
  initialDraft?: InitialDraft;
94
+ /**
95
+ * When a page is being published this number can control the number of failed steps until a catchup is triggered.
96
+ * The default value is MAX_STEP_REJECTED_ERROR (15).
97
+ */
98
+ failedStepLimitBeforeCatchupOnPublish?: number;
93
99
  }
94
100
  export interface InitAndAuthData {
95
101
  initialized: boolean;
@@ -135,6 +141,10 @@ export type CollabMetadataPayload = Metadata;
135
141
  export type CollabLocalStepsPayload = {
136
142
  steps: readonly Step[];
137
143
  };
144
+ export type CollabCommitStatusEventPayload = {
145
+ status: 'attempt' | 'success' | 'failure';
146
+ version: number;
147
+ };
138
148
  export interface CollabEvents {
139
149
  'metadata:changed': CollabMetadataPayload;
140
150
  init: CollabInitPayload;
@@ -147,6 +157,7 @@ export interface CollabEvents {
147
157
  error: ProviderError;
148
158
  entity: any;
149
159
  connecting: CollabConnectingPayload;
160
+ 'commit-status': CollabCommitStatusEventPayload;
150
161
  }
151
162
  export interface Metadata {
152
163
  [key: string]: string | number | boolean;
@@ -16,10 +16,12 @@ export declare class DocumentService {
16
16
  private getUserId;
17
17
  private onErrorHandled;
18
18
  private metadataService;
19
+ private failedStepsBeforeCatchupOnPublish;
19
20
  private getState;
20
21
  private onSyncUpError?;
21
22
  private stepQueue;
22
23
  private stepRejectCounter;
24
+ private aggressiveCatchup;
23
25
  private clientId?;
24
26
  /**
25
27
  *
@@ -28,12 +30,13 @@ export declare class DocumentService {
28
30
  * @param analyticsHelper - Helper for analytics events
29
31
  * @param fetchCatchup - Function to fetch "catchup" data, data required to rebase current steps to the latest version.
30
32
  * @param providerEmitCallback - Callback for emitting events to listeners on the provider
31
- * @param broadcastMetadata - Callback for broadcasting metadata changes to other clients
32
33
  * @param broadcast - Callback for broadcasting events to other clients
33
34
  * @param getUserId - Callback to fetch the current user's ID
34
35
  * @param onErrorHandled - Callback to handle
36
+ * @param metadataService
37
+ * @param failedStepsBeforeCatchupOnPublish - Control MAX_STEP_REJECTED_ERROR during page publishes.
35
38
  */
36
- constructor(participantsService: ParticipantsService, analyticsHelper: AnalyticsHelper | undefined, fetchCatchup: (fromVersion: number) => Promise<CatchupResponse>, providerEmitCallback: (evt: keyof CollabEvents, data: any) => void, broadcast: <K extends keyof ChannelEvent>(type: K, data: Omit<ChannelEvent[K], 'timestamp'>, callback?: Function) => void, getUserId: () => string | undefined, onErrorHandled: (error: InternalError) => void, metadataService: MetadataService);
39
+ constructor(participantsService: ParticipantsService, analyticsHelper: AnalyticsHelper | undefined, fetchCatchup: (fromVersion: number) => Promise<CatchupResponse>, providerEmitCallback: (evt: keyof CollabEvents, data: any) => void, broadcast: <K extends keyof ChannelEvent>(type: K, data: Omit<ChannelEvent[K], 'timestamp'>, callback?: Function) => void, getUserId: () => string | undefined, onErrorHandled: (error: InternalError) => void, metadataService: MetadataService, failedStepsBeforeCatchupOnPublish?: number);
37
40
  /**
38
41
  * To prevent calling catchup to often, use lodash throttle to reduce the frequency
39
42
  */
@@ -11,11 +11,14 @@ export declare enum EVENT_ACTION {
11
11
  PUBLISH_PAGE = "publishPage",
12
12
  GET_CURRENT_STATE = "getCurrentState",
13
13
  INVALIDATE_TOKEN = "invalidateToken",
14
+ SEND_STEPS_RETRY = "sendStepsRetry",
15
+ CATCHUP_AFTER_MAX_SEND_STEPS_RETRY = "catchupAfterMaxSendStepsRetry",
14
16
  DROPPED_STEPS = "droppedStepInCatchup"
15
17
  }
16
18
  export declare enum EVENT_STATUS {
17
19
  SUCCESS = "SUCCESS",
18
- FAILURE = "FAILURE"
20
+ FAILURE = "FAILURE",
21
+ INFO = "INFO"
19
22
  }
20
23
  export declare enum ADD_STEPS_TYPE {
21
24
  ACCEPTED = "ACCEPTED",
@@ -185,7 +188,22 @@ type GetCurrentStateFailureAnalyticsEvent = {
185
188
  latency?: number;
186
189
  };
187
190
  };
188
- export type ActionAnalyticsEvent = AddStepsSuccessAnalyticsEvent | AddStepsFailureAnalyticsEvent | ReInitDocFailAnalyticsEvent | ReInitDocSuccessAnalyticsEvent | ConnectionSuccessAnalyticsEvent | ConnectionFailureAnalyticsEvent | CatchUpSuccessAnalyticsEvent | CatchUpFailureAnalyticsEvent | DocumentInitSuccessAnalyticsEvent | UpdateParticipantsSuccessAnalyticsEvent | CommitUnconfirmedStepsSuccessAnalyticsEvent | CommitUnconfirmedStepsFailureAnalyticsEvent | PublishPageSuccessAnalyticsEvent | PublishPageFailureAnalyticsEvent | GetCurrentStateSuccessAnalyticsEvent | GetCurrentStateFailureAnalyticsEvent | InvalidateTokenAnalyticsEvent | CatchUpDroppedStepsEvent;
191
+ type SendStepsRetryAnalyticsEvent = {
192
+ eventAction: EVENT_ACTION.SEND_STEPS_RETRY;
193
+ attributes: {
194
+ documentAri: string;
195
+ eventStatus: EVENT_STATUS.SUCCESS;
196
+ count: number;
197
+ };
198
+ };
199
+ type CatchupAfterMaxSendStepsRetryAnalyticsEvent = {
200
+ eventAction: EVENT_ACTION.CATCHUP_AFTER_MAX_SEND_STEPS_RETRY;
201
+ attributes: {
202
+ documentAri: string;
203
+ eventStatus: EVENT_STATUS.SUCCESS;
204
+ };
205
+ };
206
+ export type ActionAnalyticsEvent = AddStepsSuccessAnalyticsEvent | AddStepsFailureAnalyticsEvent | ReInitDocFailAnalyticsEvent | ReInitDocSuccessAnalyticsEvent | ConnectionSuccessAnalyticsEvent | ConnectionFailureAnalyticsEvent | CatchUpSuccessAnalyticsEvent | CatchUpFailureAnalyticsEvent | DocumentInitSuccessAnalyticsEvent | UpdateParticipantsSuccessAnalyticsEvent | CommitUnconfirmedStepsSuccessAnalyticsEvent | CommitUnconfirmedStepsFailureAnalyticsEvent | PublishPageSuccessAnalyticsEvent | PublishPageFailureAnalyticsEvent | GetCurrentStateSuccessAnalyticsEvent | GetCurrentStateFailureAnalyticsEvent | InvalidateTokenAnalyticsEvent | SendStepsRetryAnalyticsEvent | CatchupAfterMaxSendStepsRetryAnalyticsEvent | CatchUpDroppedStepsEvent;
189
207
  export declare const ACK_MAX_TRY = 60;
190
208
  export declare const CONFLUENCE = "confluence";
191
209
  export {};
@@ -1,9 +1,9 @@
1
1
  /// <reference types="lodash" />
2
- import { ChannelEvent, StepsPayload } from '../types';
2
+ import { ChannelEvent, CollabCommitStatusEventPayload, CollabEvents, StepsPayload } from '../types';
3
3
  import type { Step as ProseMirrorStep } from 'prosemirror-transform';
4
4
  import AnalyticsHelper from '../analytics/analytics-helper';
5
5
  import type { InternalError } from '../errors/error-types';
6
- export declare const commitStep: ({ broadcast, steps, version, userId, clientId, onStepsAdded, onErrorHandled, analyticsHelper, }: {
6
+ export declare const commitStep: ({ broadcast, steps, version, userId, clientId, onStepsAdded, onErrorHandled, analyticsHelper, emit, }: {
7
7
  broadcast: <K extends keyof ChannelEvent>(type: K, data: Omit<ChannelEvent[K], "timestamp">, callback?: Function) => void;
8
8
  steps: readonly ProseMirrorStep[];
9
9
  version: number;
@@ -12,8 +12,9 @@ export declare const commitStep: ({ broadcast, steps, version, userId, clientId,
12
12
  onStepsAdded: (data: StepsPayload) => void;
13
13
  onErrorHandled: (error: InternalError) => void;
14
14
  analyticsHelper?: AnalyticsHelper | undefined;
15
+ emit: (evt: keyof CollabEvents, data: CollabCommitStatusEventPayload) => void;
15
16
  }) => void;
16
- export declare const throttledCommitStep: import("lodash").DebouncedFunc<({ broadcast, steps, version, userId, clientId, onStepsAdded, onErrorHandled, analyticsHelper, }: {
17
+ export declare const throttledCommitStep: import("lodash").DebouncedFunc<({ broadcast, steps, version, userId, clientId, onStepsAdded, onErrorHandled, analyticsHelper, emit, }: {
17
18
  broadcast: <K extends keyof ChannelEvent>(type: K, data: Omit<ChannelEvent[K], "timestamp">, callback?: Function) => void;
18
19
  steps: readonly ProseMirrorStep[];
19
20
  version: number;
@@ -22,4 +23,5 @@ export declare const throttledCommitStep: import("lodash").DebouncedFunc<({ broa
22
23
  onStepsAdded: (data: StepsPayload) => void;
23
24
  onErrorHandled: (error: InternalError) => void;
24
25
  analyticsHelper?: AnalyticsHelper | undefined;
26
+ emit: (evt: keyof CollabEvents, data: CollabCommitStatusEventPayload) => void;
25
27
  }) => void>;
@@ -59,6 +59,12 @@ export declare class Provider extends Emitter<CollabEvents> implements BaseEvent
59
59
  onSyncUpError?: SyncUpErrorFunction;
60
60
  }): this;
61
61
  private checkForCookies;
62
+ /**
63
+ * Checks the provider's initial draft timestamp to determine if it is stale.
64
+ * Returns true only if the time elapsed since the draft timestamp is greater than or
65
+ * equal to a predetermined timeout. Returns false in all other cases.
66
+ */
67
+ private isDraftTimestampStale;
62
68
  /**
63
69
  * Send steps from transaction to NCS (and as a consequence to other participants), called from the collab-edit plugin in the editor
64
70
  * @param {Transaction} _tr Deprecated, included to keep API consistent with Synchrony provider
@@ -61,6 +61,7 @@ export interface InitialDraft {
61
61
  document: JSONDocNode;
62
62
  version: number;
63
63
  metadata?: Metadata;
64
+ timestamp?: number;
64
65
  }
65
66
  export interface Config {
66
67
  url: string;
@@ -90,6 +91,11 @@ export interface Config {
90
91
  */
91
92
  throwOnNotConnected?: boolean;
92
93
  initialDraft?: InitialDraft;
94
+ /**
95
+ * When a page is being published this number can control the number of failed steps until a catchup is triggered.
96
+ * The default value is MAX_STEP_REJECTED_ERROR (15).
97
+ */
98
+ failedStepLimitBeforeCatchupOnPublish?: number;
93
99
  }
94
100
  export interface InitAndAuthData {
95
101
  initialized: boolean;
@@ -135,6 +141,10 @@ export type CollabMetadataPayload = Metadata;
135
141
  export type CollabLocalStepsPayload = {
136
142
  steps: readonly Step[];
137
143
  };
144
+ export type CollabCommitStatusEventPayload = {
145
+ status: 'attempt' | 'success' | 'failure';
146
+ version: number;
147
+ };
138
148
  export interface CollabEvents {
139
149
  'metadata:changed': CollabMetadataPayload;
140
150
  init: CollabInitPayload;
@@ -147,6 +157,7 @@ export interface CollabEvents {
147
157
  error: ProviderError;
148
158
  entity: any;
149
159
  connecting: CollabConnectingPayload;
160
+ 'commit-status': CollabCommitStatusEventPayload;
150
161
  }
151
162
  export interface Metadata {
152
163
  [key: string]: string | number | boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/collab-provider",
3
- "version": "9.0.1",
3
+ "version": "9.2.0",
4
4
  "description": "A provider for collaborative editing.",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org/"
@@ -25,7 +25,7 @@
25
25
  "atlassian": {
26
26
  "team": "Editor Services",
27
27
  "inPublicMirror": true,
28
- "releaseModel": "scheduled"
28
+ "releaseModel": "continuous"
29
29
  },
30
30
  "af:exports": {
31
31
  ".": "./src/index.ts",
@@ -61,7 +61,7 @@
61
61
  }
62
62
  },
63
63
  "devDependencies": {
64
- "@atlaskit/adf-schema": "^25.7.0",
64
+ "@atlaskit/adf-schema": "^25.8.0",
65
65
  "@atlaskit/analytics-listeners": "^8.7.0",
66
66
  "@atlaskit/editor-test-helpers": "^18.5.0",
67
67
  "@atlassian/atlassian-frontend-prettier-config-1.0.1": "npm:@atlassian/atlassian-frontend-prettier-config@1.0.1",