@atlaskit/collab-provider 8.4.0 → 8.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 (77) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/dist/cjs/analytics/{index.js → analytics-helper.js} +43 -4
  3. package/dist/cjs/analytics/performance.js +6 -5
  4. package/dist/cjs/channel.js +222 -224
  5. package/dist/cjs/{provider → document}/catchup.js +2 -2
  6. package/dist/cjs/document/document-service.js +617 -0
  7. package/dist/cjs/document/step-queue-state.js +51 -0
  8. package/dist/cjs/errors/error-code-mapper.js +86 -67
  9. package/dist/cjs/errors/error-types.js +251 -21
  10. package/dist/cjs/helpers/utils.js +1 -12
  11. package/dist/cjs/participants/participants-helper.js +51 -0
  12. package/dist/cjs/participants/participants-service.js +217 -0
  13. package/dist/cjs/participants/participants-state.js +53 -0
  14. package/dist/cjs/{provider/telepointers.js → participants/telepointers-helper.js} +6 -6
  15. package/dist/cjs/provider/commit-step.js +4 -4
  16. package/dist/cjs/provider/index.js +212 -774
  17. package/dist/cjs/types.js +3 -0
  18. package/dist/cjs/version-wrapper.js +1 -1
  19. package/dist/cjs/version.json +1 -1
  20. package/dist/es2019/analytics/{index.js → analytics-helper.js} +15 -4
  21. package/dist/es2019/analytics/performance.js +5 -6
  22. package/dist/es2019/channel.js +123 -116
  23. package/dist/es2019/{provider → document}/catchup.js +2 -2
  24. package/dist/es2019/document/document-service.js +495 -0
  25. package/dist/es2019/document/step-queue-state.js +30 -0
  26. package/dist/es2019/errors/error-code-mapper.js +87 -63
  27. package/dist/es2019/errors/error-types.js +143 -5
  28. package/dist/es2019/helpers/utils.js +0 -10
  29. package/dist/es2019/participants/participants-helper.js +25 -0
  30. package/dist/es2019/participants/participants-service.js +166 -0
  31. package/dist/es2019/participants/participants-state.js +28 -0
  32. package/dist/es2019/{provider/telepointers.js → participants/telepointers-helper.js} +2 -2
  33. package/dist/es2019/provider/commit-step.js +4 -4
  34. package/dist/es2019/provider/index.js +162 -637
  35. package/dist/es2019/types.js +4 -0
  36. package/dist/es2019/version-wrapper.js +1 -1
  37. package/dist/es2019/version.json +1 -1
  38. package/dist/esm/analytics/{index.js → analytics-helper.js} +43 -4
  39. package/dist/esm/analytics/performance.js +5 -6
  40. package/dist/esm/channel.js +224 -226
  41. package/dist/esm/{provider → document}/catchup.js +2 -2
  42. package/dist/esm/document/document-service.js +609 -0
  43. package/dist/esm/document/step-queue-state.js +43 -0
  44. package/dist/esm/errors/error-code-mapper.js +87 -64
  45. package/dist/esm/errors/error-types.js +243 -18
  46. package/dist/esm/helpers/utils.js +0 -10
  47. package/dist/esm/participants/participants-helper.js +43 -0
  48. package/dist/esm/participants/participants-service.js +209 -0
  49. package/dist/esm/participants/participants-state.js +45 -0
  50. package/dist/esm/{provider/telepointers.js → participants/telepointers-helper.js} +4 -4
  51. package/dist/esm/provider/commit-step.js +4 -4
  52. package/dist/esm/provider/index.js +212 -774
  53. package/dist/esm/types.js +4 -0
  54. package/dist/esm/version-wrapper.js +1 -1
  55. package/dist/esm/version.json +1 -1
  56. package/dist/types/analytics/{index.d.ts → analytics-helper.d.ts} +3 -1
  57. package/dist/types/analytics/performance.d.ts +3 -1
  58. package/dist/types/analytics/ufo.d.ts +1 -1
  59. package/dist/types/channel.d.ts +12 -5
  60. package/dist/types/document/document-service.d.ts +105 -0
  61. package/dist/types/document/step-queue-state.d.ts +16 -0
  62. package/dist/types/errors/error-code-mapper.d.ts +2 -36
  63. package/dist/types/errors/error-types.d.ts +439 -4
  64. package/dist/types/helpers/const.d.ts +2 -2
  65. package/dist/types/helpers/utils.d.ts +0 -6
  66. package/dist/types/index.d.ts +2 -1
  67. package/dist/types/participants/participants-helper.d.ts +15 -0
  68. package/dist/types/participants/participants-service.d.ts +70 -0
  69. package/dist/types/participants/participants-state.d.ts +13 -0
  70. package/dist/types/participants/telepointers-helper.d.ts +4 -0
  71. package/dist/types/provider/commit-step.d.ts +6 -6
  72. package/dist/types/provider/index.d.ts +80 -73
  73. package/dist/types/types.d.ts +47 -30
  74. package/package.json +4 -4
  75. package/report.api.md +172 -19
  76. package/dist/types/provider/telepointers.d.ts +0 -5
  77. /package/dist/types/{provider → document}/catchup.d.ts +0 -0
@@ -0,0 +1,617 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.DocumentService = void 0;
8
+ var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
9
+ var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
10
+ var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
11
+ var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
12
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
13
+ var _const = require("../helpers/const");
14
+ var _prosemirrorCollab = require("@atlaskit/prosemirror-collab");
15
+ var _utils = require("../helpers/utils");
16
+ var _throttle = _interopRequireDefault(require("lodash/throttle"));
17
+ var _performance = require("../analytics/performance");
18
+ var _editorJsonTransformer = require("@atlaskit/editor-json-transformer");
19
+ var _provider = require("../provider");
20
+ var _catchup = require("./catchup");
21
+ var _isEqual = _interopRequireDefault(require("lodash/isEqual"));
22
+ var _stepQueueState = require("./step-queue-state");
23
+ var _errorTypes = require("../errors/error-types");
24
+ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
25
+ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { (0, _defineProperty2.default)(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
26
+ var CATCHUP_THROTTLE = 1 * 1000; // 1 second
27
+
28
+ var noop = function noop() {};
29
+ var logger = (0, _utils.createLogger)('documentService', 'black');
30
+ var DocumentService = /*#__PURE__*/function () {
31
+ // Fires analytics to editor when collab editor cannot sync up
32
+
33
+ /**
34
+ *
35
+ * @param participantsService - The participants service, used when users are detected active when making changes to the document
36
+ * and to emit their telepointers from steps they add
37
+ * @param analyticsHelper - Helper for analytics events
38
+ * @param fetchCatchup - Function to fetch "catchup" data, data required to rebase current steps to the latest version.
39
+ * @param providerEmitCallback - Callback for emitting events to listeners on the provider
40
+ * @param broadcastMetadata - Callback for broadcasting metadata changes to other clients
41
+ * @param broadcast - Callback for broadcasting events to other clients
42
+ * @param getUserId - Callback to fetch the current user's ID
43
+ * @param onErrorHandled - Callback to handle
44
+ */
45
+ function DocumentService(participantsService, analyticsHelper, fetchCatchup, providerEmitCallback, broadcastMetadata, broadcast, getUserId, onErrorHandled) {
46
+ var _this = this;
47
+ (0, _classCallCheck2.default)(this, DocumentService);
48
+ (0, _defineProperty2.default)(this, "stepRejectCounter", 0);
49
+ (0, _defineProperty2.default)(this, "metadata", {});
50
+ (0, _defineProperty2.default)(this, "onMetadataChanged", function (metadata) {
51
+ if (metadata !== undefined && !(0, _isEqual.default)(_this.metadata, metadata)) {
52
+ _this.metadata = metadata;
53
+ _this.providerEmitCallback('metadata:changed', metadata);
54
+ }
55
+ });
56
+ (0, _defineProperty2.default)(this, "getMetaData", function () {
57
+ return _this.metadata;
58
+ });
59
+ (0, _defineProperty2.default)(this, "throttledCatchup", (0, _throttle.default)(function () {
60
+ return _this.catchup();
61
+ }, CATCHUP_THROTTLE, {
62
+ leading: false,
63
+ // TODO: why shouldn't this be leading?
64
+ trailing: true
65
+ }));
66
+ (0, _defineProperty2.default)(this, "catchup", /*#__PURE__*/(0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee() {
67
+ var start, _this$analyticsHelper, latency, _this$analyticsHelper2, _this$analyticsHelper3, _latency;
68
+ return _regenerator.default.wrap(function _callee$(_context) {
69
+ while (1) {
70
+ switch (_context.prev = _context.next) {
71
+ case 0:
72
+ start = new Date().getTime(); // if the queue is already paused, we are busy with something else, so don't proceed.
73
+ if (!_this.stepQueue.isPaused()) {
74
+ _context.next = 4;
75
+ break;
76
+ }
77
+ logger("Queue is paused. Aborting.");
78
+ return _context.abrupt("return");
79
+ case 4:
80
+ _this.stepQueue.pauseQueue();
81
+ _context.prev = 5;
82
+ _context.next = 8;
83
+ return (0, _catchup.catchup)({
84
+ getCurrentPmVersion: _this.getCurrentPmVersion,
85
+ fetchCatchup: _this.fetchCatchup,
86
+ getUnconfirmedSteps: _this.getUnconfirmedSteps,
87
+ filterQueue: _this.stepQueue.filterQueue,
88
+ updateDocumentWithMetadata: _this.updateDocumentWithMetadata,
89
+ applyLocalSteps: _this.applyLocalSteps
90
+ });
91
+ case 8:
92
+ latency = new Date().getTime() - start;
93
+ (_this$analyticsHelper = _this.analyticsHelper) === null || _this$analyticsHelper === void 0 ? void 0 : _this$analyticsHelper.sendActionEvent(_const.EVENT_ACTION.CATCHUP, _const.EVENT_STATUS.SUCCESS, {
94
+ latency: latency
95
+ });
96
+ _context.next = 18;
97
+ break;
98
+ case 12:
99
+ _context.prev = 12;
100
+ _context.t0 = _context["catch"](5);
101
+ _latency = new Date().getTime() - start;
102
+ (_this$analyticsHelper2 = _this.analyticsHelper) === null || _this$analyticsHelper2 === void 0 ? void 0 : _this$analyticsHelper2.sendActionEvent(_const.EVENT_ACTION.CATCHUP, _const.EVENT_STATUS.FAILURE, {
103
+ latency: _latency
104
+ });
105
+ (_this$analyticsHelper3 = _this.analyticsHelper) === null || _this$analyticsHelper3 === void 0 ? void 0 : _this$analyticsHelper3.sendErrorEvent(_context.t0, 'Error while catching up');
106
+ logger("Catch-Up Failed:", _context.t0.message);
107
+ case 18:
108
+ _context.prev = 18;
109
+ _this.stepQueue.resumeQueue();
110
+ _this.processQueue();
111
+ _this.sendStepsFromCurrentState(); // this will eventually retry catchup as it calls commitStep which will either catchup on onStepsAdded or onErrorHandled
112
+ _this.stepRejectCounter = 0;
113
+ return _context.finish(18);
114
+ case 24:
115
+ case "end":
116
+ return _context.stop();
117
+ }
118
+ }
119
+ }, _callee, null, [[5, 12, 18, 24]]);
120
+ })));
121
+ (0, _defineProperty2.default)(this, "getCurrentPmVersion", function () {
122
+ var _this$getState;
123
+ var state = (_this$getState = _this.getState) === null || _this$getState === void 0 ? void 0 : _this$getState.call(_this);
124
+ if (!state) {
125
+ var _this$analyticsHelper4;
126
+ (_this$analyticsHelper4 = _this.analyticsHelper) === null || _this$analyticsHelper4 === void 0 ? void 0 : _this$analyticsHelper4.sendErrorEvent(new Error('No editor state when calling ProseMirror function'), 'getCurrentPmVersion called without state');
127
+ return 0;
128
+ }
129
+ return (0, _prosemirrorCollab.getVersion)(state);
130
+ });
131
+ (0, _defineProperty2.default)(this, "getCurrentState", /*#__PURE__*/(0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee2() {
132
+ var _this$metadata$title, _this$analyticsHelper5, state, adfDocument, currentState, measure, _this$analyticsHelper6, _this$analyticsHelper7, _measure;
133
+ return _regenerator.default.wrap(function _callee2$(_context2) {
134
+ while (1) {
135
+ switch (_context2.prev = _context2.next) {
136
+ case 0:
137
+ _context2.prev = 0;
138
+ (0, _performance.startMeasure)(_performance.MEASURE_NAME.GET_CURRENT_STATE, _this.analyticsHelper);
139
+
140
+ // Convert ProseMirror document in Editor state to ADF document
141
+ state = _this.getState();
142
+ adfDocument = new _editorJsonTransformer.JSONTransformer().encode(state.doc);
143
+ currentState = {
144
+ content: adfDocument,
145
+ title: (_this$metadata$title = _this.metadata.title) === null || _this$metadata$title === void 0 ? void 0 : _this$metadata$title.toString(),
146
+ stepVersion: (0, _prosemirrorCollab.getVersion)(state)
147
+ };
148
+ measure = (0, _performance.stopMeasure)(_performance.MEASURE_NAME.GET_CURRENT_STATE, _this.analyticsHelper);
149
+ (_this$analyticsHelper5 = _this.analyticsHelper) === null || _this$analyticsHelper5 === void 0 ? void 0 : _this$analyticsHelper5.sendActionEvent(_const.EVENT_ACTION.GET_CURRENT_STATE, _const.EVENT_STATUS.SUCCESS, {
150
+ latency: measure === null || measure === void 0 ? void 0 : measure.duration
151
+ });
152
+ return _context2.abrupt("return", currentState);
153
+ case 10:
154
+ _context2.prev = 10;
155
+ _context2.t0 = _context2["catch"](0);
156
+ _measure = (0, _performance.stopMeasure)(_performance.MEASURE_NAME.GET_CURRENT_STATE, _this.analyticsHelper);
157
+ (_this$analyticsHelper6 = _this.analyticsHelper) === null || _this$analyticsHelper6 === void 0 ? void 0 : _this$analyticsHelper6.sendActionEvent(_const.EVENT_ACTION.GET_CURRENT_STATE, _const.EVENT_STATUS.FAILURE, {
158
+ latency: _measure === null || _measure === void 0 ? void 0 : _measure.duration
159
+ });
160
+ (_this$analyticsHelper7 = _this.analyticsHelper) === null || _this$analyticsHelper7 === void 0 ? void 0 : _this$analyticsHelper7.sendErrorEvent(_context2.t0, 'Error while returning ADF version of current draft document');
161
+ throw _context2.t0;
162
+ case 16:
163
+ case "end":
164
+ return _context2.stop();
165
+ }
166
+ }
167
+ }, _callee2, null, [[0, 10]]);
168
+ })));
169
+ (0, _defineProperty2.default)(this, "getUnconfirmedStepsOrigins", function () {
170
+ var _this$getState2, _sendableSteps;
171
+ var state = (_this$getState2 = _this.getState) === null || _this$getState2 === void 0 ? void 0 : _this$getState2.call(_this);
172
+ if (!state) {
173
+ var _this$analyticsHelper8;
174
+ (_this$analyticsHelper8 = _this.analyticsHelper) === null || _this$analyticsHelper8 === void 0 ? void 0 : _this$analyticsHelper8.sendErrorEvent(new Error('No editor state when calling ProseMirror function'), 'getUnconfirmedStepsOrigins called without state');
175
+ return;
176
+ }
177
+ return (_sendableSteps = (0, _prosemirrorCollab.sendableSteps)(state)) === null || _sendableSteps === void 0 ? void 0 : _sendableSteps.origins;
178
+ });
179
+ (0, _defineProperty2.default)(this, "getUnconfirmedSteps", function () {
180
+ var _this$getState3, _sendableSteps2;
181
+ var state = (_this$getState3 = _this.getState) === null || _this$getState3 === void 0 ? void 0 : _this$getState3.call(_this);
182
+ if (!state) {
183
+ var _this$analyticsHelper9;
184
+ (_this$analyticsHelper9 = _this.analyticsHelper) === null || _this$analyticsHelper9 === void 0 ? void 0 : _this$analyticsHelper9.sendErrorEvent(new Error('No editor state when calling ProseMirror function'), 'getUnconfirmedSteps called without state');
185
+ return;
186
+ }
187
+ return (_sendableSteps2 = (0, _prosemirrorCollab.sendableSteps)(state)) === null || _sendableSteps2 === void 0 ? void 0 : _sendableSteps2.steps;
188
+ });
189
+ (0, _defineProperty2.default)(this, "applyLocalSteps", function (steps) {
190
+ // Re-apply local steps
191
+ _this.providerEmitCallback('local-steps', {
192
+ steps: steps
193
+ });
194
+ });
195
+ (0, _defineProperty2.default)(this, "updateDocumentWithMetadata", function (_ref3) {
196
+ var doc = _ref3.doc,
197
+ version = _ref3.version,
198
+ metadata = _ref3.metadata,
199
+ reserveCursor = _ref3.reserveCursor;
200
+ _this.providerEmitCallback('init', _objectSpread({
201
+ doc: doc,
202
+ version: version,
203
+ metadata: metadata
204
+ }, reserveCursor ? {
205
+ reserveCursor: reserveCursor
206
+ } : {}));
207
+ if (metadata && Object.keys(metadata).length > 0) {
208
+ _this.metadata = metadata;
209
+ _this.providerEmitCallback('metadata:changed', metadata);
210
+ }
211
+ });
212
+ (0, _defineProperty2.default)(this, "onStepsAdded", function (data) {
213
+ logger("Received steps", {
214
+ steps: data.steps,
215
+ version: data.version
216
+ });
217
+ if (!data.steps) {
218
+ logger("No steps.. waiting..");
219
+ return;
220
+ }
221
+ try {
222
+ var currentVersion = _this.getCurrentPmVersion();
223
+ var expectedVersion = currentVersion + data.steps.length;
224
+ if (data.version <= currentVersion) {
225
+ logger("Received steps we already have. Ignoring.");
226
+ } else if (data.version === expectedVersion) {
227
+ _this.processSteps(data);
228
+ } else if (data.version > expectedVersion) {
229
+ logger("Version too high. Expected \"".concat(expectedVersion, "\" but got \"").concat(data.version, ". Current local version is ").concat(currentVersion, "."));
230
+ _this.stepQueue.queueSteps(data);
231
+ _this.throttledCatchup();
232
+ }
233
+ _this.participantsService.updateLastActive(data.steps.map(function (_ref4) {
234
+ var userId = _ref4.userId;
235
+ return userId;
236
+ }));
237
+ } catch (stepsAddedError) {
238
+ var _this$analyticsHelper10;
239
+ (_this$analyticsHelper10 = _this.analyticsHelper) === null || _this$analyticsHelper10 === void 0 ? void 0 : _this$analyticsHelper10.sendErrorEvent(stepsAddedError, 'Error while adding steps in the provider');
240
+ _this.onErrorHandled({
241
+ message: 'Error while adding steps in the provider',
242
+ data: {
243
+ status: 500,
244
+ // Meaningless, remove when we review error structure
245
+ code: _errorTypes.INTERNAL_ERROR_CODE.ADD_STEPS_ERROR
246
+ }
247
+ });
248
+ }
249
+ });
250
+ (0, _defineProperty2.default)(this, "onRestore", function (_ref5) {
251
+ var doc = _ref5.doc,
252
+ version = _ref5.version,
253
+ metadata = _ref5.metadata;
254
+ // Preserve the unconfirmed steps to prevent data loss.
255
+ var unconfirmedSteps = _this.getUnconfirmedSteps();
256
+ try {
257
+ var _this$analyticsHelper11;
258
+ // Reset the editor,
259
+ // - Replace the document, keep in sync with the server
260
+ // - Replace the version number, so editor is in sync with NCS server and can commit new changes.
261
+ // - Replace the metadata
262
+ // - Reserve the cursor position, in case a cursor jump.
263
+ _this.updateDocumentWithMetadata({
264
+ doc: doc,
265
+ version: version,
266
+ metadata: metadata,
267
+ reserveCursor: true
268
+ });
269
+
270
+ // Re-apply the unconfirmed steps, not 100% of them can be applied, if document is changed significantly.
271
+ if (unconfirmedSteps !== null && unconfirmedSteps !== void 0 && unconfirmedSteps.length) {
272
+ _this.applyLocalSteps(unconfirmedSteps);
273
+ }
274
+ (_this$analyticsHelper11 = _this.analyticsHelper) === null || _this$analyticsHelper11 === void 0 ? void 0 : _this$analyticsHelper11.sendActionEvent(_const.EVENT_ACTION.REINITIALISE_DOCUMENT, _const.EVENT_STATUS.SUCCESS, {
275
+ numUnconfirmedSteps: unconfirmedSteps === null || unconfirmedSteps === void 0 ? void 0 : unconfirmedSteps.length
276
+ });
277
+ } catch (restoreError) {
278
+ var _this$analyticsHelper12, _this$analyticsHelper13;
279
+ (_this$analyticsHelper12 = _this.analyticsHelper) === null || _this$analyticsHelper12 === void 0 ? void 0 : _this$analyticsHelper12.sendActionEvent(_const.EVENT_ACTION.REINITIALISE_DOCUMENT, _const.EVENT_STATUS.FAILURE, {
280
+ numUnconfirmedSteps: unconfirmedSteps === null || unconfirmedSteps === void 0 ? void 0 : unconfirmedSteps.length
281
+ });
282
+ (_this$analyticsHelper13 = _this.analyticsHelper) === null || _this$analyticsHelper13 === void 0 ? void 0 : _this$analyticsHelper13.sendErrorEvent(restoreError, 'Error while reinitialising document');
283
+ _this.onErrorHandled({
284
+ message: 'Caught error while trying to recover the document',
285
+ data: {
286
+ status: 500,
287
+ // Meaningless, remove when we review error structure
288
+ code: _errorTypes.INTERNAL_ERROR_CODE.DOCUMENT_RESTORE_ERROR
289
+ }
290
+ });
291
+ }
292
+ });
293
+ (0, _defineProperty2.default)(this, "getFinalAcknowledgedState", /*#__PURE__*/(0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee3() {
294
+ var _this$analyticsHelper14, currentState, measure, _this$analyticsHelper15, _this$analyticsHelper16, _measure2;
295
+ return _regenerator.default.wrap(function _callee3$(_context3) {
296
+ while (1) {
297
+ switch (_context3.prev = _context3.next) {
298
+ case 0:
299
+ _context3.prev = 0;
300
+ (0, _performance.startMeasure)(_performance.MEASURE_NAME.PUBLISH_PAGE, _this.analyticsHelper);
301
+ _context3.next = 4;
302
+ return _this.commitUnconfirmedSteps();
303
+ case 4:
304
+ _context3.next = 6;
305
+ return _this.getCurrentState();
306
+ case 6:
307
+ currentState = _context3.sent;
308
+ measure = (0, _performance.stopMeasure)(_performance.MEASURE_NAME.PUBLISH_PAGE, _this.analyticsHelper);
309
+ (_this$analyticsHelper14 = _this.analyticsHelper) === null || _this$analyticsHelper14 === void 0 ? void 0 : _this$analyticsHelper14.sendActionEvent(_const.EVENT_ACTION.PUBLISH_PAGE, _const.EVENT_STATUS.SUCCESS, {
310
+ latency: measure === null || measure === void 0 ? void 0 : measure.duration
311
+ });
312
+ return _context3.abrupt("return", currentState);
313
+ case 12:
314
+ _context3.prev = 12;
315
+ _context3.t0 = _context3["catch"](0);
316
+ _measure2 = (0, _performance.stopMeasure)(_performance.MEASURE_NAME.PUBLISH_PAGE, _this.analyticsHelper);
317
+ (_this$analyticsHelper15 = _this.analyticsHelper) === null || _this$analyticsHelper15 === void 0 ? void 0 : _this$analyticsHelper15.sendActionEvent(_const.EVENT_ACTION.PUBLISH_PAGE, _const.EVENT_STATUS.FAILURE, {
318
+ latency: _measure2 === null || _measure2 === void 0 ? void 0 : _measure2.duration
319
+ });
320
+ (_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');
321
+ throw _context3.t0;
322
+ case 18:
323
+ case "end":
324
+ return _context3.stop();
325
+ }
326
+ }
327
+ }, _callee3, null, [[0, 12]]);
328
+ })));
329
+ (0, _defineProperty2.default)(this, "commitUnconfirmedSteps", /*#__PURE__*/(0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee5() {
330
+ var unconfirmedSteps, _this$analyticsHelper18, _this$analyticsHelper19, measure;
331
+ return _regenerator.default.wrap(function _callee5$(_context5) {
332
+ while (1) {
333
+ switch (_context5.prev = _context5.next) {
334
+ case 0:
335
+ unconfirmedSteps = _this.getUnconfirmedSteps();
336
+ _context5.prev = 1;
337
+ if (!(unconfirmedSteps !== null && unconfirmedSteps !== void 0 && unconfirmedSteps.length)) {
338
+ _context5.next = 4;
339
+ break;
340
+ }
341
+ return _context5.delegateYield( /*#__PURE__*/_regenerator.default.mark(function _callee4() {
342
+ var _this$analyticsHelper17;
343
+ var count, unconfirmedTrs, lastTr, isLastTrConfirmed, nextUnconfirmedSteps, nextUnconfirmedTrs, state, measure;
344
+ return _regenerator.default.wrap(function _callee4$(_context4) {
345
+ while (1) {
346
+ switch (_context4.prev = _context4.next) {
347
+ case 0:
348
+ (0, _performance.startMeasure)(_performance.MEASURE_NAME.COMMIT_UNCONFIRMED_STEPS, _this.analyticsHelper);
349
+ count = 0; // We use origins here as steps can be rebased. When steps are rebased a new step is created.
350
+ // This means that we can not track if it has been removed from the unconfirmed array or not.
351
+ // Origins points to the original transaction that the step was created in. This is never changed
352
+ // and gets passed down when a step is rebased.
353
+ unconfirmedTrs = _this.getUnconfirmedStepsOrigins();
354
+ lastTr = unconfirmedTrs === null || unconfirmedTrs === void 0 ? void 0 : unconfirmedTrs[unconfirmedTrs.length - 1];
355
+ isLastTrConfirmed = false;
356
+ case 5:
357
+ if (isLastTrConfirmed) {
358
+ _context4.next = 16;
359
+ break;
360
+ }
361
+ _this.sendStepsFromCurrentState();
362
+ _context4.next = 9;
363
+ return (0, _utils.sleep)(1000);
364
+ case 9:
365
+ nextUnconfirmedSteps = _this.getUnconfirmedSteps();
366
+ if (nextUnconfirmedSteps !== null && nextUnconfirmedSteps !== void 0 && nextUnconfirmedSteps.length) {
367
+ nextUnconfirmedTrs = _this.getUnconfirmedStepsOrigins();
368
+ isLastTrConfirmed = !(nextUnconfirmedTrs !== null && nextUnconfirmedTrs !== void 0 && nextUnconfirmedTrs.some(function (tr) {
369
+ return tr === lastTr;
370
+ }));
371
+ } else {
372
+ isLastTrConfirmed = true;
373
+ }
374
+ if (!(!isLastTrConfirmed && count++ >= _const.ACK_MAX_TRY)) {
375
+ _context4.next = 14;
376
+ break;
377
+ }
378
+ if (_this.onSyncUpError) {
379
+ state = _this.getState();
380
+ _this.onSyncUpError({
381
+ lengthOfUnconfirmedSteps: nextUnconfirmedSteps === null || nextUnconfirmedSteps === void 0 ? void 0 : nextUnconfirmedSteps.length,
382
+ tries: count,
383
+ maxRetries: _const.ACK_MAX_TRY,
384
+ clientId: _this.clientId,
385
+ version: (0, _prosemirrorCollab.getVersion)(state)
386
+ });
387
+ }
388
+ throw new Error("Can't sync up with Collab Service");
389
+ case 14:
390
+ _context4.next = 5;
391
+ break;
392
+ case 16:
393
+ measure = (0, _performance.stopMeasure)(_performance.MEASURE_NAME.COMMIT_UNCONFIRMED_STEPS, _this.analyticsHelper);
394
+ (_this$analyticsHelper17 = _this.analyticsHelper) === null || _this$analyticsHelper17 === void 0 ? void 0 : _this$analyticsHelper17.sendActionEvent(_const.EVENT_ACTION.COMMIT_UNCONFIRMED_STEPS, _const.EVENT_STATUS.SUCCESS, {
395
+ latency: measure === null || measure === void 0 ? void 0 : measure.duration,
396
+ // upon success, emit the total number of unconfirmed steps we synced
397
+ numUnconfirmedSteps: unconfirmedSteps === null || unconfirmedSteps === void 0 ? void 0 : unconfirmedSteps.length
398
+ });
399
+ case 18:
400
+ case "end":
401
+ return _context4.stop();
402
+ }
403
+ }
404
+ }, _callee4);
405
+ })(), "t0", 4);
406
+ case 4:
407
+ _context5.next = 12;
408
+ break;
409
+ case 6:
410
+ _context5.prev = 6;
411
+ _context5.t1 = _context5["catch"](1);
412
+ measure = (0, _performance.stopMeasure)(_performance.MEASURE_NAME.COMMIT_UNCONFIRMED_STEPS, _this.analyticsHelper);
413
+ (_this$analyticsHelper18 = _this.analyticsHelper) === null || _this$analyticsHelper18 === void 0 ? void 0 : _this$analyticsHelper18.sendActionEvent(_const.EVENT_ACTION.COMMIT_UNCONFIRMED_STEPS, _const.EVENT_STATUS.FAILURE, {
414
+ latency: measure === null || measure === void 0 ? void 0 : measure.duration,
415
+ numUnconfirmedSteps: unconfirmedSteps === null || unconfirmedSteps === void 0 ? void 0 : unconfirmedSteps.length
416
+ });
417
+ (_this$analyticsHelper19 = _this.analyticsHelper) === null || _this$analyticsHelper19 === void 0 ? void 0 : _this$analyticsHelper19.sendErrorEvent(_context5.t1, 'Error while committing unconfirmed steps');
418
+ throw _context5.t1;
419
+ case 12:
420
+ case "end":
421
+ return _context5.stop();
422
+ }
423
+ }
424
+ }, _callee5, null, [[1, 6]]);
425
+ })));
426
+ (0, _defineProperty2.default)(this, "onStepRejectedError", function () {
427
+ _this.stepRejectCounter++;
428
+ logger("Steps rejected (tries=".concat(_this.stepRejectCounter, ")"));
429
+ if (_this.stepRejectCounter >= _provider.MAX_STEP_REJECTED_ERROR) {
430
+ logger("The steps were rejected too many times (tries=".concat(_this.stepRejectCounter, ", limit=").concat(_provider.MAX_STEP_REJECTED_ERROR, "). Trying to catch-up."));
431
+ _this.throttledCatchup();
432
+ } else {
433
+ // If committing steps failed try again automatically in 1s
434
+ // This makes it more likely that unconfirmed steps trigger a catch-up
435
+ // within 15s even if there is no one editing actively (or draft sync polling)
436
+ // reducing the risk of data loss at the expense of step commits
437
+ setTimeout(function () {
438
+ return _this.sendStepsFromCurrentState();
439
+ }, 1000);
440
+ }
441
+ });
442
+ this.participantsService = participantsService;
443
+ this.analyticsHelper = analyticsHelper;
444
+ this.fetchCatchup = fetchCatchup;
445
+ this.providerEmitCallback = providerEmitCallback;
446
+ this.broadcastMetadata = broadcastMetadata;
447
+ this.broadcast = broadcast;
448
+ this.getUserId = getUserId;
449
+ this.onErrorHandled = onErrorHandled;
450
+ this.stepQueue = new _stepQueueState.StepQueueState();
451
+ }
452
+
453
+ /**
454
+ * Called when a metadata is changed externally from other clients/backend.
455
+ */
456
+ (0, _createClass2.default)(DocumentService, [{
457
+ key: "setTitle",
458
+ value: function setTitle(title, broadcast) {
459
+ if (broadcast) {
460
+ this.broadcastMetadata({
461
+ title: title
462
+ });
463
+ }
464
+ Object.assign(this.metadata, {
465
+ title: title
466
+ });
467
+ }
468
+ }, {
469
+ key: "setEditorWidth",
470
+ value: function setEditorWidth(editorWidth, broadcast) {
471
+ if (broadcast) {
472
+ this.broadcastMetadata({
473
+ editorWidth: editorWidth
474
+ });
475
+ }
476
+ Object.assign(this.metadata, {
477
+ editorWidth: editorWidth
478
+ });
479
+ }
480
+
481
+ /**
482
+ * Updates the local metadata and broadcasts the metadata to other clients/backend.
483
+ * @param metadata
484
+ */
485
+ }, {
486
+ key: "setMetadata",
487
+ value: function setMetadata(metadata) {
488
+ this.broadcastMetadata(metadata);
489
+ Object.assign(this.metadata, metadata);
490
+ }
491
+
492
+ /**
493
+ * To prevent calling catchup to often, use lodash throttle to reduce the frequency
494
+ */
495
+ }, {
496
+ key: "processQueue",
497
+ value: function processQueue() {
498
+ if (this.stepQueue.isPaused()) {
499
+ logger("Queue is paused. Aborting.");
500
+ return;
501
+ }
502
+ logger("Looking for processable data.");
503
+ if (this.stepQueue.getQueue().length > 0) {
504
+ var firstItem = this.stepQueue.shift();
505
+ var currentVersion = this.getCurrentPmVersion();
506
+ var expectedVersion = currentVersion + firstItem.steps.length;
507
+ if (firstItem.version === expectedVersion) {
508
+ logger("Applying data from queue!");
509
+ this.processSteps(firstItem);
510
+ // recur
511
+ this.processQueue();
512
+ }
513
+ }
514
+ }
515
+ }, {
516
+ key: "processSteps",
517
+ value: function processSteps(data) {
518
+ var _this2 = this;
519
+ var version = data.version,
520
+ steps = data.steps;
521
+ logger("Processing data. Version \"".concat(version, "\"."));
522
+ if (steps !== null && steps !== void 0 && steps.length) {
523
+ try {
524
+ var clientIds = steps.map(function (_ref8) {
525
+ var clientId = _ref8.clientId;
526
+ return clientId;
527
+ });
528
+ this.providerEmitCallback('data', {
529
+ json: steps,
530
+ version: version,
531
+ userIds: clientIds
532
+ });
533
+ // If steps can apply to local editor successfully, no need to accumulate the error counter.
534
+ this.stepRejectCounter = 0;
535
+ this.participantsService.emitTelepointersFromSteps(steps, this.providerEmitCallback);
536
+
537
+ // Resend local steps if none of the received steps originated with us!
538
+ if (clientIds.indexOf(this.clientId) === -1) {
539
+ setTimeout(function () {
540
+ return _this2.sendStepsFromCurrentState();
541
+ }, 100);
542
+ }
543
+ } catch (error) {
544
+ var _this$analyticsHelper20;
545
+ logger("Processing steps failed with error: ".concat(error, ". Triggering catch up call."));
546
+ (_this$analyticsHelper20 = this.analyticsHelper) === null || _this$analyticsHelper20 === void 0 ? void 0 : _this$analyticsHelper20.sendErrorEvent(error, 'Error while processing steps');
547
+ this.throttledCatchup();
548
+ }
549
+ }
550
+ }
551
+ }, {
552
+ key: "setup",
553
+ value: function setup(_ref9) {
554
+ var getState = _ref9.getState,
555
+ onSyncUpError = _ref9.onSyncUpError,
556
+ clientId = _ref9.clientId;
557
+ this.getState = getState;
558
+ this.onSyncUpError = onSyncUpError || noop;
559
+ this.clientId = clientId;
560
+ return this;
561
+ }
562
+
563
+ /**
564
+ * We can use this function to throttle/delay
565
+ * Any send steps operation
566
+ *
567
+ * The getState function will return the current EditorState
568
+ * from the EditorView.
569
+ */
570
+ }, {
571
+ key: "sendStepsFromCurrentState",
572
+ value: function sendStepsFromCurrentState() {
573
+ var _this$getState4;
574
+ var state = (_this$getState4 = this.getState) === null || _this$getState4 === void 0 ? void 0 : _this$getState4.call(this);
575
+ if (!state) {
576
+ return;
577
+ }
578
+ this.send(null, null, state);
579
+ }
580
+ }, {
581
+ key: "send",
582
+ value:
583
+ /**
584
+ * Send steps from transaction to other participants
585
+ * It needs the superfluous arguments because we keep the interface of the send API the same as the Synchrony plugin
586
+ */
587
+ function send(_tr, _oldState, newState) {
588
+ var unconfirmedStepsData = (0, _prosemirrorCollab.sendableSteps)(newState);
589
+ var version = (0, _prosemirrorCollab.getVersion)(newState);
590
+
591
+ // Don't send any steps before we're ready.
592
+ if (!unconfirmedStepsData) {
593
+ return;
594
+ }
595
+ var unconfirmedSteps = unconfirmedStepsData.steps;
596
+ if (!(unconfirmedSteps !== null && unconfirmedSteps !== void 0 && unconfirmedSteps.length)) {
597
+ return;
598
+ }
599
+
600
+ // Avoid reference issues using a
601
+ // method outside of the provider
602
+ // scope
603
+ (0, _provider.throttledCommitStep)({
604
+ broadcast: this.broadcast,
605
+ userId: this.getUserId(),
606
+ clientId: this.clientId,
607
+ steps: unconfirmedSteps,
608
+ version: version,
609
+ onStepsAdded: this.onStepsAdded,
610
+ onErrorHandled: this.onErrorHandled,
611
+ analyticsHelper: this.analyticsHelper
612
+ });
613
+ }
614
+ }]);
615
+ return DocumentService;
616
+ }();
617
+ exports.DocumentService = DocumentService;
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.StepQueueState = void 0;
8
+ var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
9
+ var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
10
+ var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
11
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
12
+ var _utils = require("../helpers/utils");
13
+ var logger = (0, _utils.createLogger)('documentService-queue', 'black');
14
+ var StepQueueState = /*#__PURE__*/function () {
15
+ function StepQueueState() {
16
+ var _this = this;
17
+ (0, _classCallCheck2.default)(this, StepQueueState);
18
+ (0, _defineProperty2.default)(this, "queuePaused", false);
19
+ (0, _defineProperty2.default)(this, "queue", []);
20
+ (0, _defineProperty2.default)(this, "getQueue", function () {
21
+ return _this.queue;
22
+ });
23
+ (0, _defineProperty2.default)(this, "filterQueue", function (condition) {
24
+ _this.queue = _this.queue.filter(condition);
25
+ });
26
+ (0, _defineProperty2.default)(this, "isPaused", function () {
27
+ return _this.queuePaused;
28
+ });
29
+ (0, _defineProperty2.default)(this, "pauseQueue", function () {
30
+ _this.queuePaused = true;
31
+ });
32
+ (0, _defineProperty2.default)(this, "resumeQueue", function () {
33
+ _this.queuePaused = false;
34
+ });
35
+ (0, _defineProperty2.default)(this, "shift", function () {
36
+ return _this.queue.shift();
37
+ });
38
+ }
39
+ (0, _createClass2.default)(StepQueueState, [{
40
+ key: "queueSteps",
41
+ value: function queueSteps(data) {
42
+ logger("Queueing data for version \"".concat(data.version, "\"."));
43
+ var orderedQueue = [].concat((0, _toConsumableArray2.default)(this.queue), [data]).sort(function (a, b) {
44
+ return a.version > b.version ? 1 : -1;
45
+ });
46
+ this.queue = orderedQueue;
47
+ }
48
+ }]);
49
+ return StepQueueState;
50
+ }();
51
+ exports.StepQueueState = StepQueueState;