@atlaskit/collab-provider 7.7.0 → 8.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +27 -0
- package/dist/cjs/analytics/index.js +17 -27
- package/dist/cjs/analytics/performance.js +0 -2
- package/dist/cjs/channel.js +74 -35
- package/dist/cjs/helpers/const.js +10 -1
- package/dist/cjs/provider/catchup.js +2 -2
- package/dist/cjs/provider/index.js +174 -65
- package/dist/cjs/types.js +10 -1
- package/dist/cjs/version-wrapper.js +1 -1
- package/dist/cjs/version.json +1 -1
- package/dist/es2019/analytics/index.js +15 -22
- package/dist/es2019/analytics/performance.js +0 -2
- package/dist/es2019/channel.js +28 -10
- package/dist/es2019/helpers/const.js +8 -0
- package/dist/es2019/provider/catchup.js +2 -2
- package/dist/es2019/provider/index.js +165 -65
- package/dist/es2019/types.js +7 -1
- package/dist/es2019/version-wrapper.js +1 -1
- package/dist/es2019/version.json +1 -1
- package/dist/esm/analytics/index.js +15 -22
- package/dist/esm/analytics/performance.js +0 -2
- package/dist/esm/channel.js +75 -36
- package/dist/esm/helpers/const.js +8 -0
- package/dist/esm/provider/catchup.js +2 -2
- package/dist/esm/provider/index.js +175 -69
- package/dist/esm/types.js +7 -1
- package/dist/esm/version-wrapper.js +1 -1
- package/dist/esm/version.json +1 -1
- package/dist/types/analytics/index.d.ts +1 -3
- package/dist/types/analytics/performance.d.ts +0 -2
- package/dist/types/channel.d.ts +2 -1
- package/dist/types/helpers/const.d.ts +30 -2
- package/dist/types/provider/index.d.ts +10 -6
- package/dist/types/types.d.ts +32 -12
- package/package.json +9 -6
- package/report.api.md +24 -9
package/dist/es2019/channel.js
CHANGED
|
@@ -5,8 +5,9 @@ import { Emitter } from './emitter';
|
|
|
5
5
|
import { ErrorCodeMapper } from './error-code-mapper';
|
|
6
6
|
import { createLogger, getProduct, getSubProduct } from './helpers/utils';
|
|
7
7
|
import { MEASURE_NAME, startMeasure, stopMeasure } from './analytics/performance';
|
|
8
|
-
import {
|
|
8
|
+
import { triggerAnalyticsEvent } from './analytics';
|
|
9
9
|
import { EVENT_ACTION, EVENT_STATUS } from './helpers/const';
|
|
10
|
+
import { ExperiencePerformanceTypes, ExperienceTypes, UFOExperience } from '@atlaskit/ufo';
|
|
10
11
|
const logger = createLogger('Channel', 'green');
|
|
11
12
|
export class Channel extends Emitter {
|
|
12
13
|
constructor(config) {
|
|
@@ -18,6 +19,18 @@ export class Channel extends Emitter {
|
|
|
18
19
|
|
|
19
20
|
_defineProperty(this, "initialized", false);
|
|
20
21
|
|
|
22
|
+
_defineProperty(this, "initExperience", new UFOExperience('collab-provider.document-init', {
|
|
23
|
+
type: ExperienceTypes.Load,
|
|
24
|
+
performanceType: ExperiencePerformanceTypes.Custom,
|
|
25
|
+
performanceConfig: {
|
|
26
|
+
histogram: {
|
|
27
|
+
[ExperiencePerformanceTypes.Custom]: {
|
|
28
|
+
duration: '250_500_1000_1500_2000_3000_4000'
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}));
|
|
33
|
+
|
|
21
34
|
_defineProperty(this, "getInitialized", () => this.initialized);
|
|
22
35
|
|
|
23
36
|
_defineProperty(this, "getConnected", () => this.connected);
|
|
@@ -28,7 +41,7 @@ export class Channel extends Emitter {
|
|
|
28
41
|
this.connected = true;
|
|
29
42
|
logger('Connected.', this.socket.id);
|
|
30
43
|
const measure = stopMeasure(MEASURE_NAME.SOCKET_CONNECT);
|
|
31
|
-
|
|
44
|
+
triggerAnalyticsEvent({
|
|
32
45
|
eventAction: EVENT_ACTION.CONNECTION,
|
|
33
46
|
attributes: {
|
|
34
47
|
eventStatus: EVENT_STATUS.SUCCESS,
|
|
@@ -49,7 +62,8 @@ export class Channel extends Emitter {
|
|
|
49
62
|
if (data.type === 'initial') {
|
|
50
63
|
if (!this.initialized) {
|
|
51
64
|
const measure = stopMeasure(MEASURE_NAME.DOCUMENT_INIT);
|
|
52
|
-
|
|
65
|
+
this.initExperience.success();
|
|
66
|
+
triggerAnalyticsEvent({
|
|
53
67
|
eventAction: EVENT_ACTION.DOCUMENT_INIT,
|
|
54
68
|
attributes: {
|
|
55
69
|
eventStatus: EVENT_STATUS.SUCCESS,
|
|
@@ -107,6 +121,7 @@ export class Channel extends Emitter {
|
|
|
107
121
|
|
|
108
122
|
if (!this.initialized) {
|
|
109
123
|
startMeasure(MEASURE_NAME.DOCUMENT_INIT);
|
|
124
|
+
this.initExperience.start();
|
|
110
125
|
}
|
|
111
126
|
|
|
112
127
|
const {
|
|
@@ -124,9 +139,10 @@ export class Channel extends Emitter {
|
|
|
124
139
|
if (permissionTokenRefresh) {
|
|
125
140
|
authCb = cb => {
|
|
126
141
|
permissionTokenRefresh().then(token => {
|
|
127
|
-
cb({
|
|
128
|
-
|
|
129
|
-
|
|
142
|
+
cb({ // The permission token.
|
|
143
|
+
...(token ? {
|
|
144
|
+
token
|
|
145
|
+
} : {}),
|
|
130
146
|
// The initialized status. If false, BE will send document, otherwise not.
|
|
131
147
|
initialized: this.initialized,
|
|
132
148
|
// ESS-1009 Allow to opt-in into 404 response
|
|
@@ -208,7 +224,7 @@ export class Channel extends Emitter {
|
|
|
208
224
|
|
|
209
225
|
this.socket.on('connect_error', error => {
|
|
210
226
|
const measure = stopMeasure(MEASURE_NAME.SOCKET_CONNECT);
|
|
211
|
-
|
|
227
|
+
triggerAnalyticsEvent({
|
|
212
228
|
eventAction: EVENT_ACTION.CONNECTION,
|
|
213
229
|
attributes: {
|
|
214
230
|
eventStatus: EVENT_STATUS.FAILURE,
|
|
@@ -236,6 +252,8 @@ export class Channel extends Emitter {
|
|
|
236
252
|
|
|
237
253
|
async fetchCatchup(fromVersion) {
|
|
238
254
|
try {
|
|
255
|
+
var _await$this$config$pe;
|
|
256
|
+
|
|
239
257
|
const {
|
|
240
258
|
doc,
|
|
241
259
|
version,
|
|
@@ -248,7 +266,7 @@ export class Channel extends Emitter {
|
|
|
248
266
|
},
|
|
249
267
|
requestInit: {
|
|
250
268
|
headers: { ...(this.config.permissionTokenRefresh ? {
|
|
251
|
-
'x-token': await this.config.permissionTokenRefresh()
|
|
269
|
+
'x-token': (_await$this$config$pe = await this.config.permissionTokenRefresh()) !== null && _await$this$config$pe !== void 0 ? _await$this$config$pe : undefined
|
|
252
270
|
} : {}),
|
|
253
271
|
'x-product': getProduct(this.config.productInfo),
|
|
254
272
|
'x-subproduct': getSubProduct(this.config.productInfo)
|
|
@@ -279,7 +297,7 @@ export class Channel extends Emitter {
|
|
|
279
297
|
*/
|
|
280
298
|
|
|
281
299
|
|
|
282
|
-
broadcast(type, data) {
|
|
300
|
+
broadcast(type, data, callback) {
|
|
283
301
|
if (!this.connected || !this.socket) {
|
|
284
302
|
return;
|
|
285
303
|
}
|
|
@@ -287,7 +305,7 @@ export class Channel extends Emitter {
|
|
|
287
305
|
this.socket.emit('broadcast', {
|
|
288
306
|
type,
|
|
289
307
|
...data
|
|
290
|
-
});
|
|
308
|
+
}, callback);
|
|
291
309
|
}
|
|
292
310
|
|
|
293
311
|
sendMetadata(metadata) {
|
|
@@ -26,4 +26,12 @@ export let EVENT_STATUS;
|
|
|
26
26
|
EVENT_STATUS["FAILURE"] = "FAILURE";
|
|
27
27
|
})(EVENT_STATUS || (EVENT_STATUS = {}));
|
|
28
28
|
|
|
29
|
+
export let ADD_STEPS_TYPE;
|
|
30
|
+
|
|
31
|
+
(function (ADD_STEPS_TYPE) {
|
|
32
|
+
ADD_STEPS_TYPE["ACCEPTED"] = "ACCEPTED";
|
|
33
|
+
ADD_STEPS_TYPE["REJECTED"] = "REJECTED";
|
|
34
|
+
ADD_STEPS_TYPE["ERROR"] = "ERROR";
|
|
35
|
+
})(ADD_STEPS_TYPE || (ADD_STEPS_TYPE = {}));
|
|
36
|
+
|
|
29
37
|
export const ACK_MAX_TRY = 30;
|
|
@@ -50,7 +50,7 @@ export const catchup = async opt => {
|
|
|
50
50
|
* newer.
|
|
51
51
|
*/
|
|
52
52
|
|
|
53
|
-
opt.
|
|
53
|
+
opt.filterQueue(data => data.version > serverVersion); // We are too far behind - replace the entire document
|
|
54
54
|
|
|
55
55
|
logger(`Replacing document: ${doc}`);
|
|
56
56
|
logger(`getting metadata: ${metadata}`); // Replace local document and version number
|
|
@@ -84,7 +84,7 @@ export const catchup = async opt => {
|
|
|
84
84
|
const newUnconfirmedSteps = rebaseSteps(unconfirmedSteps, mapping);
|
|
85
85
|
logger(`Re-aply ${newUnconfirmedSteps.length} mapped unconfirmed steps: ${JSON.stringify(newUnconfirmedSteps)}`); // Re-aply local steps
|
|
86
86
|
|
|
87
|
-
opt.
|
|
87
|
+
opt.applyLocalSteps(newUnconfirmedSteps);
|
|
88
88
|
}
|
|
89
89
|
}
|
|
90
90
|
}
|
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
2
|
import { getVersion, sendableSteps } from 'prosemirror-collab';
|
|
3
3
|
import throttle from 'lodash/throttle';
|
|
4
|
-
import
|
|
4
|
+
import isEqual from 'lodash/isEqual';
|
|
5
|
+
import countBy from 'lodash/countBy';
|
|
5
6
|
import { JSONTransformer } from '@atlaskit/editor-json-transformer';
|
|
7
|
+
import { ExperiencePerformanceTypes, ExperienceTypes, UFOExperience } from '@atlaskit/ufo';
|
|
6
8
|
import { Emitter } from '../emitter';
|
|
7
9
|
import { Channel } from '../channel';
|
|
8
10
|
import { createLogger, getParticipant, sleep } from '../helpers/utils';
|
|
9
|
-
import { ACK_MAX_TRY, EVENT_ACTION, EVENT_STATUS } from '../helpers/const';
|
|
10
|
-
import {
|
|
11
|
+
import { ACK_MAX_TRY, EVENT_ACTION, EVENT_STATUS, ADD_STEPS_TYPE } from '../helpers/const';
|
|
12
|
+
import { triggerAnalyticsEvent } from '../analytics';
|
|
11
13
|
import { catchup } from './catchup';
|
|
12
14
|
import { errorCodeMapper } from '../error-code-mapper';
|
|
15
|
+
import { AcknowledgementResponseTypes } from '../types';
|
|
13
16
|
import { DisconnectReason, socketIOReasons } from '../disconnected-reason-mapper';
|
|
14
17
|
import { MEASURE_NAME, startMeasure, stopMeasure } from '../analytics/performance';
|
|
15
18
|
const logger = createLogger('Provider', 'black');
|
|
@@ -32,16 +35,71 @@ const commitStep = ({
|
|
|
32
35
|
steps,
|
|
33
36
|
version,
|
|
34
37
|
userId,
|
|
35
|
-
clientId
|
|
38
|
+
clientId,
|
|
39
|
+
documentAri,
|
|
40
|
+
analyticsClient,
|
|
41
|
+
onStepsAdded,
|
|
42
|
+
onErrorHandled
|
|
36
43
|
}) => {
|
|
37
44
|
const stepsWithClientAndUserId = steps.map(step => ({ ...step.toJSON(),
|
|
38
45
|
clientId,
|
|
39
46
|
userId
|
|
40
47
|
}));
|
|
48
|
+
const start = new Date().getTime();
|
|
41
49
|
channel.broadcast('steps:commit', {
|
|
42
50
|
steps: stepsWithClientAndUserId,
|
|
43
51
|
version,
|
|
44
52
|
userId
|
|
53
|
+
}, response => {
|
|
54
|
+
const latency = new Date().getTime() - start;
|
|
55
|
+
|
|
56
|
+
if (response.type === AcknowledgementResponseTypes.SUCCESS) {
|
|
57
|
+
onStepsAdded({
|
|
58
|
+
steps: stepsWithClientAndUserId,
|
|
59
|
+
version: response.version
|
|
60
|
+
}, true);
|
|
61
|
+
let analyticStepEvent = {
|
|
62
|
+
eventAction: EVENT_ACTION.ADD_STEPS,
|
|
63
|
+
attributes: {
|
|
64
|
+
eventStatus: EVENT_STATUS.SUCCESS,
|
|
65
|
+
type: ADD_STEPS_TYPE.ACCEPTED,
|
|
66
|
+
documentAri,
|
|
67
|
+
latency
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
analyticStepEvent.attributes.stepType = countBy(stepsWithClientAndUserId, stepWithClientAndUserId => stepWithClientAndUserId.stepType);
|
|
71
|
+
triggerAnalyticsEvent(analyticStepEvent, analyticsClient);
|
|
72
|
+
} else if (response.type === AcknowledgementResponseTypes.ERROR) {
|
|
73
|
+
var _response$error, _response$error$data, _response$error2, _response$error2$data;
|
|
74
|
+
|
|
75
|
+
onErrorHandled(response.error, true);
|
|
76
|
+
triggerAnalyticsEvent({
|
|
77
|
+
eventAction: EVENT_ACTION.ADD_STEPS,
|
|
78
|
+
attributes: {
|
|
79
|
+
eventStatus: EVENT_STATUS.FAILURE,
|
|
80
|
+
// User tried committing steps but they were rejected because:
|
|
81
|
+
// - HEAD_VERSION_UPDATE_FAILED: the collab service's latest stored step tail version didn't correspond to the head version of the first step submitted
|
|
82
|
+
// - VERSION_NUMBER_ALREADY_EXISTS: while storing the steps there was a conflict meaning someone else wrote steps into the database more quickly
|
|
83
|
+
type: ((_response$error = response.error) === null || _response$error === void 0 ? void 0 : (_response$error$data = _response$error.data) === null || _response$error$data === void 0 ? void 0 : _response$error$data.code) === 'HEAD_VERSION_UPDATE_FAILED' || ((_response$error2 = response.error) === null || _response$error2 === void 0 ? void 0 : (_response$error2$data = _response$error2.data) === null || _response$error2$data === void 0 ? void 0 : _response$error2$data.code) === 'VERSION_NUMBER_ALREADY_EXISTS' ? ADD_STEPS_TYPE.REJECTED : ADD_STEPS_TYPE.ERROR,
|
|
84
|
+
documentAri,
|
|
85
|
+
latency,
|
|
86
|
+
error: response.error
|
|
87
|
+
}
|
|
88
|
+
}, analyticsClient);
|
|
89
|
+
} else {
|
|
90
|
+
triggerAnalyticsEvent({
|
|
91
|
+
eventAction: EVENT_ACTION.ADD_STEPS,
|
|
92
|
+
attributes: {
|
|
93
|
+
eventStatus: EVENT_STATUS.FAILURE,
|
|
94
|
+
type: ADD_STEPS_TYPE.ERROR,
|
|
95
|
+
documentAri,
|
|
96
|
+
latency,
|
|
97
|
+
error: {
|
|
98
|
+
message: 'Invalid Acknowledgement'
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}, analyticsClient);
|
|
102
|
+
}
|
|
45
103
|
});
|
|
46
104
|
};
|
|
47
105
|
|
|
@@ -99,16 +157,16 @@ export class Provider extends Emitter {
|
|
|
99
157
|
version,
|
|
100
158
|
metadata
|
|
101
159
|
}) => {
|
|
102
|
-
//
|
|
160
|
+
// Preserve the unconfirmed steps to prevent data loss.
|
|
103
161
|
const {
|
|
104
162
|
steps: unconfirmedSteps
|
|
105
163
|
} = this.getUnconfirmedSteps() || {
|
|
106
164
|
steps: []
|
|
107
165
|
}; // Reset the editor,
|
|
108
|
-
// -
|
|
109
|
-
// -
|
|
110
|
-
// -
|
|
111
|
-
// - Reserve the
|
|
166
|
+
// - Replace the document, keep in sync with the server
|
|
167
|
+
// - Replace the version number, so editor is in sync with NCS server and can commit new changes.
|
|
168
|
+
// - Replace the metadata
|
|
169
|
+
// - Reserve the cursor position, in case a cursor jump.
|
|
112
170
|
|
|
113
171
|
this.updateDocumentWithMetadata({
|
|
114
172
|
doc,
|
|
@@ -116,7 +174,7 @@ export class Provider extends Emitter {
|
|
|
116
174
|
metadata,
|
|
117
175
|
reserveCursor: true
|
|
118
176
|
});
|
|
119
|
-
|
|
177
|
+
triggerAnalyticsEvent({
|
|
120
178
|
eventAction: EVENT_ACTION.REINITIALISE_DOCUMENT,
|
|
121
179
|
attributes: {
|
|
122
180
|
numUnconfirmedSteps: unconfirmedSteps.length,
|
|
@@ -125,11 +183,11 @@ export class Provider extends Emitter {
|
|
|
125
183
|
}, this.analyticsClient); // Re-apply the unconfirmed steps, not 100% of them can be applied, if document is changed significantly.
|
|
126
184
|
|
|
127
185
|
if (unconfirmedSteps.length > 0) {
|
|
128
|
-
this.
|
|
186
|
+
this.applyLocalSteps(unconfirmedSteps);
|
|
129
187
|
}
|
|
130
188
|
});
|
|
131
189
|
|
|
132
|
-
_defineProperty(this, "onStepsAdded", data => {
|
|
190
|
+
_defineProperty(this, "onStepsAdded", (data, disableAnalytics = false) => {
|
|
133
191
|
logger(`Received steps`, {
|
|
134
192
|
steps: data.steps,
|
|
135
193
|
version: data.version
|
|
@@ -146,7 +204,7 @@ export class Provider extends Emitter {
|
|
|
146
204
|
if (data.version === currentVersion) {
|
|
147
205
|
logger(`Received steps we already have. Ignoring.`);
|
|
148
206
|
} else if (data.version === expectedVersion) {
|
|
149
|
-
this.processSteps(data);
|
|
207
|
+
this.processSteps(data, disableAnalytics);
|
|
150
208
|
} else if (data.version > expectedVersion) {
|
|
151
209
|
logger(`Version too high. Expected "${expectedVersion}" but got "${data.version}. Current local version is ${currentVersion}.`);
|
|
152
210
|
this.queueSteps(data);
|
|
@@ -163,7 +221,7 @@ export class Provider extends Emitter {
|
|
|
163
221
|
trailing: true
|
|
164
222
|
}));
|
|
165
223
|
|
|
166
|
-
_defineProperty(this, "
|
|
224
|
+
_defineProperty(this, "filterQueue", condition => {
|
|
167
225
|
this.queue = this.queue.filter(condition);
|
|
168
226
|
});
|
|
169
227
|
|
|
@@ -188,7 +246,7 @@ export class Provider extends Emitter {
|
|
|
188
246
|
}
|
|
189
247
|
});
|
|
190
248
|
|
|
191
|
-
_defineProperty(this, "
|
|
249
|
+
_defineProperty(this, "applyLocalSteps", steps => {
|
|
192
250
|
// Re-aply local steps
|
|
193
251
|
this.emit('local-steps', {
|
|
194
252
|
steps
|
|
@@ -204,7 +262,7 @@ export class Provider extends Emitter {
|
|
|
204
262
|
});
|
|
205
263
|
|
|
206
264
|
_defineProperty(this, "catchup", async () => {
|
|
207
|
-
|
|
265
|
+
const start = new Date().getTime(); // if the queue is already paused, we are busy with something else, so don't proceed.
|
|
208
266
|
|
|
209
267
|
if (this.pauseQueue) {
|
|
210
268
|
logger(`Queue is paused. Aborting.`);
|
|
@@ -218,27 +276,27 @@ export class Provider extends Emitter {
|
|
|
218
276
|
getCurrentPmVersion: this.getCurrentPmVersion,
|
|
219
277
|
fetchCatchup: this.channel.fetchCatchup.bind(this.channel),
|
|
220
278
|
getUnconfirmedSteps: this.getUnconfirmedSteps,
|
|
221
|
-
|
|
279
|
+
filterQueue: this.filterQueue,
|
|
222
280
|
updateDocumentWithMetadata: this.updateDocumentWithMetadata,
|
|
223
|
-
|
|
281
|
+
applyLocalSteps: this.applyLocalSteps
|
|
224
282
|
});
|
|
225
|
-
const
|
|
226
|
-
|
|
283
|
+
const latency = new Date().getTime() - start;
|
|
284
|
+
triggerAnalyticsEvent({
|
|
227
285
|
eventAction: EVENT_ACTION.CATCHUP,
|
|
228
286
|
attributes: {
|
|
229
287
|
eventStatus: EVENT_STATUS.SUCCESS,
|
|
230
|
-
latency
|
|
288
|
+
latency,
|
|
231
289
|
documentAri: this.config.documentAri
|
|
232
290
|
}
|
|
233
291
|
}, this.analyticsClient);
|
|
234
292
|
} catch (error) {
|
|
235
|
-
const
|
|
236
|
-
|
|
293
|
+
const latency = new Date().getTime() - start;
|
|
294
|
+
triggerAnalyticsEvent({
|
|
237
295
|
eventAction: EVENT_ACTION.CATCHUP,
|
|
238
296
|
attributes: {
|
|
239
297
|
eventStatus: EVENT_STATUS.FAILURE,
|
|
240
298
|
error: error,
|
|
241
|
-
latency
|
|
299
|
+
latency,
|
|
242
300
|
documentAri: this.config.documentAri
|
|
243
301
|
}
|
|
244
302
|
}, this.analyticsClient);
|
|
@@ -251,27 +309,32 @@ export class Provider extends Emitter {
|
|
|
251
309
|
}
|
|
252
310
|
});
|
|
253
311
|
|
|
254
|
-
_defineProperty(this, "onErrorHandled", error => {
|
|
255
|
-
if (error && error.data) {
|
|
312
|
+
_defineProperty(this, "onErrorHandled", (error, disableAnalytics = false) => {
|
|
313
|
+
if (error !== null && error !== void 0 && error.data) {
|
|
314
|
+
// User tried committing steps but they were rejected because:
|
|
315
|
+
// HEAD_VERSION_UPDATE_FAILED: the collab service's latest stored step tail version didn't correspond to the head version of the first step submitted
|
|
316
|
+
// VERSION_NUMBER_ALREADY_EXISTS: while storing the steps there was a conflict meaning someone else wrote steps into the database more quickly
|
|
256
317
|
if (error.data.code === 'HEAD_VERSION_UPDATE_FAILED' || error.data.code === 'VERSION_NUMBER_ALREADY_EXISTS') {
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
318
|
+
// TODO: Remove this analytics logic once we have validated the ack messages and aren't likely to go back to a generic error handler
|
|
319
|
+
if (!disableAnalytics) {
|
|
320
|
+
triggerAnalyticsEvent({
|
|
321
|
+
eventAction: EVENT_ACTION.ADD_STEPS,
|
|
322
|
+
attributes: {
|
|
323
|
+
eventStatus: EVENT_STATUS.FAILURE,
|
|
324
|
+
type: ADD_STEPS_TYPE.REJECTED,
|
|
325
|
+
error,
|
|
326
|
+
documentAri: this.config.documentAri
|
|
327
|
+
}
|
|
328
|
+
}, this.analyticsClient);
|
|
329
|
+
}
|
|
269
330
|
|
|
270
|
-
|
|
331
|
+
this.stepRejectCounter++;
|
|
332
|
+
logger(`Steps rejected (tries=${this.stepRejectCounter})`);
|
|
271
333
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
334
|
+
if (this.stepRejectCounter >= MAX_STEP_REJECTED_ERROR) {
|
|
335
|
+
logger(`The steps were rejected too many times (tries=${this.stepRejectCounter}, limit=${MAX_STEP_REJECTED_ERROR}). Trying to catch-up.`);
|
|
336
|
+
this.throttledCatchup();
|
|
337
|
+
}
|
|
275
338
|
}
|
|
276
339
|
|
|
277
340
|
const errorToEmit = errorCodeMapper(error);
|
|
@@ -281,7 +344,7 @@ export class Provider extends Emitter {
|
|
|
281
344
|
}
|
|
282
345
|
}
|
|
283
346
|
|
|
284
|
-
logger(
|
|
347
|
+
logger('Error from collab service', error);
|
|
285
348
|
});
|
|
286
349
|
|
|
287
350
|
_defineProperty(this, "queue", []);
|
|
@@ -317,7 +380,7 @@ export class Provider extends Emitter {
|
|
|
317
380
|
});
|
|
318
381
|
|
|
319
382
|
_defineProperty(this, "onMetadataChanged", metadata => {
|
|
320
|
-
if (metadata !== undefined && !
|
|
383
|
+
if (metadata !== undefined && !isEqual(this.metadata, metadata)) {
|
|
321
384
|
this.metadata = metadata;
|
|
322
385
|
this.emit('metadata:changed', metadata);
|
|
323
386
|
}
|
|
@@ -441,7 +504,7 @@ export class Provider extends Emitter {
|
|
|
441
504
|
if (joined.length || left.length) {
|
|
442
505
|
var _this$participants$si;
|
|
443
506
|
|
|
444
|
-
|
|
507
|
+
triggerAnalyticsEvent({
|
|
445
508
|
eventAction: EVENT_ACTION.UPDATE_PARTICIPANTS,
|
|
446
509
|
attributes: {
|
|
447
510
|
participants: (_this$participants$si = this.participants.size) !== null && _this$participants$si !== void 0 ? _this$participants$si : 1,
|
|
@@ -542,7 +605,7 @@ export class Provider extends Emitter {
|
|
|
542
605
|
}
|
|
543
606
|
|
|
544
607
|
const measure = stopMeasure(MEASURE_NAME.COMMIT_UNCONFIRMED_STEPS);
|
|
545
|
-
|
|
608
|
+
triggerAnalyticsEvent({
|
|
546
609
|
eventAction: EVENT_ACTION.COMMIT_UNCONFIRMED_STEPS,
|
|
547
610
|
attributes: {
|
|
548
611
|
eventStatus: EVENT_STATUS.FAILURE,
|
|
@@ -557,7 +620,7 @@ export class Provider extends Emitter {
|
|
|
557
620
|
}
|
|
558
621
|
|
|
559
622
|
const measure = stopMeasure(MEASURE_NAME.COMMIT_UNCONFIRMED_STEPS);
|
|
560
|
-
|
|
623
|
+
triggerAnalyticsEvent({
|
|
561
624
|
eventAction: EVENT_ACTION.COMMIT_UNCONFIRMED_STEPS,
|
|
562
625
|
attributes: {
|
|
563
626
|
eventStatus: EVENT_STATUS.SUCCESS,
|
|
@@ -577,7 +640,7 @@ export class Provider extends Emitter {
|
|
|
577
640
|
startMeasure(MEASURE_NAME.CONVERT_PM_TO_ADF);
|
|
578
641
|
adfDocument = new JSONTransformer().encode(state.doc);
|
|
579
642
|
const measure = stopMeasure(MEASURE_NAME.CONVERT_PM_TO_ADF);
|
|
580
|
-
|
|
643
|
+
triggerAnalyticsEvent({
|
|
581
644
|
eventAction: EVENT_ACTION.CONVERT_PM_TO_ADF,
|
|
582
645
|
attributes: {
|
|
583
646
|
eventStatus: EVENT_STATUS.SUCCESS,
|
|
@@ -587,7 +650,7 @@ export class Provider extends Emitter {
|
|
|
587
650
|
}, this.analyticsClient);
|
|
588
651
|
} catch (error) {
|
|
589
652
|
const measure = stopMeasure(MEASURE_NAME.CONVERT_PM_TO_ADF);
|
|
590
|
-
|
|
653
|
+
triggerAnalyticsEvent({
|
|
591
654
|
eventAction: EVENT_ACTION.CONVERT_PM_TO_ADF,
|
|
592
655
|
attributes: {
|
|
593
656
|
eventStatus: EVENT_STATUS.FAILURE,
|
|
@@ -713,18 +776,21 @@ export class Provider extends Emitter {
|
|
|
713
776
|
|
|
714
777
|
if (!steps || !steps.length) {
|
|
715
778
|
return;
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
startMeasure(MEASURE_NAME.ADD_STEPS); // Avoid reference issues using a
|
|
779
|
+
} // Avoid reference issues using a
|
|
719
780
|
// method outside of the provider
|
|
720
781
|
// scope
|
|
721
782
|
|
|
783
|
+
|
|
722
784
|
throttledCommitStep({
|
|
723
785
|
channel: this.channel,
|
|
724
786
|
userId: this.userId,
|
|
725
787
|
clientId: this.clientId,
|
|
726
788
|
steps,
|
|
727
|
-
version
|
|
789
|
+
version,
|
|
790
|
+
documentAri: this.config.documentAri,
|
|
791
|
+
analyticsClient: this.analyticsClient,
|
|
792
|
+
onStepsAdded: this.onStepsAdded.bind(this),
|
|
793
|
+
onErrorHandled: this.onErrorHandled.bind(this)
|
|
728
794
|
});
|
|
729
795
|
} // Triggered when page recovery has emitted an 'init' event on a page client is currently connected to.
|
|
730
796
|
|
|
@@ -757,14 +823,14 @@ export class Provider extends Emitter {
|
|
|
757
823
|
}
|
|
758
824
|
}
|
|
759
825
|
|
|
760
|
-
processSteps(data) {
|
|
826
|
+
processSteps(data, disableAnalytics = false) {
|
|
761
827
|
const {
|
|
762
828
|
version,
|
|
763
829
|
steps
|
|
764
830
|
} = data;
|
|
765
831
|
logger(`Processing data. Version "${version}".`);
|
|
766
832
|
|
|
767
|
-
if (steps && steps.length) {
|
|
833
|
+
if (steps !== null && steps !== void 0 && steps.length) {
|
|
768
834
|
const clientIds = steps.map(({
|
|
769
835
|
clientId
|
|
770
836
|
}) => clientId);
|
|
@@ -774,16 +840,21 @@ export class Provider extends Emitter {
|
|
|
774
840
|
userIds: clientIds
|
|
775
841
|
}); // If steps can apply to local editor successfully, no need to accumulate the error counter.
|
|
776
842
|
|
|
777
|
-
this.stepRejectCounter = 0;
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
843
|
+
this.stepRejectCounter = 0; // TODO: Remove this analytics logic after we've validated the ack call-backs
|
|
844
|
+
|
|
845
|
+
if (!disableAnalytics && clientIds.indexOf(this.clientId) >= 0) {
|
|
846
|
+
let analyticStepEvent = {
|
|
847
|
+
eventAction: EVENT_ACTION.ADD_STEPS,
|
|
848
|
+
attributes: {
|
|
849
|
+
eventStatus: EVENT_STATUS.SUCCESS,
|
|
850
|
+
type: ADD_STEPS_TYPE.ACCEPTED,
|
|
851
|
+
documentAri: this.config.documentAri
|
|
852
|
+
}
|
|
853
|
+
};
|
|
854
|
+
analyticStepEvent.attributes.stepType = countBy(steps, step => step.stepType);
|
|
855
|
+
triggerAnalyticsEvent(analyticStepEvent, this.analyticsClient);
|
|
856
|
+
}
|
|
857
|
+
|
|
787
858
|
this.emitTelepointersFromSteps(steps); // Resend local steps if none of the received steps originated with us!
|
|
788
859
|
|
|
789
860
|
if (clientIds.indexOf(this.clientId) === -1) {
|
|
@@ -813,6 +884,21 @@ export class Provider extends Emitter {
|
|
|
813
884
|
|
|
814
885
|
switch (type) {
|
|
815
886
|
case 'telepointer':
|
|
887
|
+
const telepointerExperience = new UFOExperience('collab-provider.telepointer', {
|
|
888
|
+
type: ExperienceTypes.Operation,
|
|
889
|
+
performanceType: ExperiencePerformanceTypes.Custom,
|
|
890
|
+
performanceConfig: {
|
|
891
|
+
histogram: {
|
|
892
|
+
[ExperiencePerformanceTypes.Custom]: {
|
|
893
|
+
duration: '250_500_1000_1500_2000_3000_4000'
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
});
|
|
898
|
+
telepointerExperience.addMetadata({
|
|
899
|
+
documentAri: this.config.documentAri
|
|
900
|
+
});
|
|
901
|
+
telepointerExperience.start();
|
|
816
902
|
const {
|
|
817
903
|
selection
|
|
818
904
|
} = rest;
|
|
@@ -821,6 +907,20 @@ export class Provider extends Emitter {
|
|
|
821
907
|
userId: userId,
|
|
822
908
|
sessionId: sessionId,
|
|
823
909
|
clientId: clientId
|
|
910
|
+
}, response => {
|
|
911
|
+
if (response.type === AcknowledgementResponseTypes.SUCCESS) {
|
|
912
|
+
telepointerExperience.success();
|
|
913
|
+
} else if (response.type === AcknowledgementResponseTypes.ERROR) {
|
|
914
|
+
const errorMessage = response.error;
|
|
915
|
+
telepointerExperience.addMetadata({
|
|
916
|
+
error: errorMessage
|
|
917
|
+
});
|
|
918
|
+
logger('Error from collab service with telepointer broadcast', errorMessage);
|
|
919
|
+
telepointerExperience.failure();
|
|
920
|
+
} else {
|
|
921
|
+
// Abort if invalid ACK sent
|
|
922
|
+
telepointerExperience.abort();
|
|
923
|
+
}
|
|
824
924
|
});
|
|
825
925
|
break;
|
|
826
926
|
}
|
package/dist/es2019/types.js
CHANGED
|
@@ -1 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
// Channel
|
|
2
|
+
export let AcknowledgementResponseTypes;
|
|
3
|
+
|
|
4
|
+
(function (AcknowledgementResponseTypes) {
|
|
5
|
+
AcknowledgementResponseTypes["SUCCESS"] = "SUCCESS";
|
|
6
|
+
AcknowledgementResponseTypes["ERROR"] = "ERROR";
|
|
7
|
+
})(AcknowledgementResponseTypes || (AcknowledgementResponseTypes = {}));
|
package/dist/es2019/version.json
CHANGED
|
@@ -6,35 +6,28 @@ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { va
|
|
|
6
6
|
|
|
7
7
|
import { EVENT_SUBJECT, COLLAB_SERVICE } from '../helpers/const';
|
|
8
8
|
import { name as packageName, version as packageVersion } from '../version-wrapper';
|
|
9
|
-
export var
|
|
10
|
-
if (!analyticsClient
|
|
9
|
+
export var triggerAnalyticsEvent = function triggerAnalyticsEvent(analyticsEvent, analyticsClient) {
|
|
10
|
+
if (!analyticsClient) {
|
|
11
11
|
return;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
var client = analyticsClient;
|
|
15
|
-
var requestIdleCallbackFunction = window.requestIdleCallback;
|
|
16
|
-
var runItLater = typeof requestIdleCallbackFunction === 'function' ? requestIdleCallbackFunction : window.requestAnimationFrame; // Let the browser figure out
|
|
17
|
-
// when it should send those events
|
|
18
|
-
|
|
19
|
-
runItLater(function () {
|
|
20
|
-
client.sendOperationalEvent(_objectSpread(_objectSpread({
|
|
21
|
-
action: 'collab'
|
|
22
|
-
}, payload), {}, {
|
|
23
|
-
source: payload.source || 'unknown',
|
|
24
|
-
tags: ['editor']
|
|
25
|
-
}));
|
|
26
|
-
});
|
|
27
|
-
};
|
|
28
|
-
export var triggerCollabAnalyticsEvent = function triggerCollabAnalyticsEvent(analyticsEvent, analyticsClient) {
|
|
29
14
|
var payload = {
|
|
30
|
-
action: analyticsEvent.eventAction,
|
|
31
15
|
actionSubject: EVENT_SUBJECT,
|
|
32
|
-
source: 'unknown',
|
|
33
16
|
attributes: _objectSpread({
|
|
34
17
|
packageName: packageName,
|
|
35
18
|
packageVersion: packageVersion,
|
|
36
19
|
collabService: COLLAB_SERVICE.NCS
|
|
37
|
-
}, analyticsEvent.attributes)
|
|
38
|
-
|
|
39
|
-
|
|
20
|
+
}, analyticsEvent.attributes),
|
|
21
|
+
tags: ['editor'],
|
|
22
|
+
action: analyticsEvent.eventAction,
|
|
23
|
+
source: 'unknown' // Adds zero analytics value, but event validation throws an error if you don't add it :-(
|
|
24
|
+
|
|
25
|
+
}; // Let the browser figure out
|
|
26
|
+
// when it should send those events
|
|
27
|
+
|
|
28
|
+
var requestIdleCallbackFunction = window.requestIdleCallback;
|
|
29
|
+
var runItLater = typeof requestIdleCallbackFunction === 'function' ? requestIdleCallbackFunction : window.requestAnimationFrame;
|
|
30
|
+
runItLater(function () {
|
|
31
|
+
analyticsClient.sendOperationalEvent(payload);
|
|
32
|
+
});
|
|
40
33
|
};
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
export var MEASURE_NAME;
|
|
2
2
|
|
|
3
3
|
(function (MEASURE_NAME) {
|
|
4
|
-
MEASURE_NAME["CALLING_CATCHUP_API"] = "callingCatchupApi";
|
|
5
4
|
MEASURE_NAME["SOCKET_CONNECT"] = "socketConnect";
|
|
6
5
|
MEASURE_NAME["DOCUMENT_INIT"] = "documentInit";
|
|
7
6
|
MEASURE_NAME["CONVERT_PM_TO_ADF"] = "convertPMToADF";
|
|
8
|
-
MEASURE_NAME["ADD_STEPS"] = "addSteps";
|
|
9
7
|
MEASURE_NAME["COMMIT_UNCONFIRMED_STEPS"] = "commitUnconfirmedSteps";
|
|
10
8
|
})(MEASURE_NAME || (MEASURE_NAME = {}));
|
|
11
9
|
|