@atlaskit/collab-provider 8.2.0 → 8.3.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 (67) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/dist/cjs/analytics/index.js +66 -9
  3. package/dist/cjs/analytics/performance.js +45 -35
  4. package/dist/cjs/analytics/ufo.js +33 -0
  5. package/dist/cjs/channel.js +89 -54
  6. package/dist/cjs/connectivity/network.js +53 -0
  7. package/dist/cjs/connectivity/reconnect-helper.js +48 -0
  8. package/dist/cjs/connectivity/singleton.js +15 -0
  9. package/dist/cjs/disconnected-reason-mapper.js +19 -2
  10. package/dist/cjs/error-code-mapper.js +16 -1
  11. package/dist/cjs/helpers/const.js +5 -10
  12. package/dist/cjs/provider/catchup.js +8 -12
  13. package/dist/cjs/provider/commit-step.js +66 -0
  14. package/dist/cjs/provider/index.js +480 -558
  15. package/dist/cjs/provider/telepointers.js +78 -0
  16. package/dist/cjs/version-wrapper.js +1 -1
  17. package/dist/cjs/version.json +1 -1
  18. package/dist/es2019/analytics/index.js +57 -8
  19. package/dist/es2019/analytics/performance.js +46 -35
  20. package/dist/es2019/analytics/ufo.js +22 -0
  21. package/dist/es2019/channel.js +89 -58
  22. package/dist/es2019/connectivity/network.js +34 -0
  23. package/dist/es2019/connectivity/reconnect-helper.js +29 -0
  24. package/dist/es2019/connectivity/singleton.js +7 -0
  25. package/dist/es2019/disconnected-reason-mapper.js +17 -1
  26. package/dist/es2019/error-code-mapper.js +16 -1
  27. package/dist/es2019/helpers/const.js +4 -7
  28. package/dist/es2019/provider/catchup.js +5 -12
  29. package/dist/es2019/provider/commit-step.js +53 -0
  30. package/dist/es2019/provider/index.js +397 -496
  31. package/dist/es2019/provider/telepointers.js +65 -0
  32. package/dist/es2019/version-wrapper.js +1 -1
  33. package/dist/es2019/version.json +1 -1
  34. package/dist/esm/analytics/index.js +67 -9
  35. package/dist/esm/analytics/performance.js +46 -35
  36. package/dist/esm/analytics/ufo.js +25 -0
  37. package/dist/esm/channel.js +89 -56
  38. package/dist/esm/connectivity/network.js +45 -0
  39. package/dist/esm/connectivity/reconnect-helper.js +42 -0
  40. package/dist/esm/connectivity/singleton.js +7 -0
  41. package/dist/esm/disconnected-reason-mapper.js +17 -1
  42. package/dist/esm/error-code-mapper.js +16 -1
  43. package/dist/esm/helpers/const.js +4 -7
  44. package/dist/esm/provider/catchup.js +8 -12
  45. package/dist/esm/provider/commit-step.js +58 -0
  46. package/dist/esm/provider/index.js +482 -559
  47. package/dist/esm/provider/telepointers.js +69 -0
  48. package/dist/esm/version-wrapper.js +1 -1
  49. package/dist/esm/version.json +1 -1
  50. package/dist/types/analytics/index.d.ts +8 -2
  51. package/dist/types/analytics/performance.d.ts +5 -5
  52. package/dist/types/analytics/ufo.d.ts +3 -0
  53. package/dist/types/channel.d.ts +11 -5
  54. package/dist/types/connectivity/network.d.ts +17 -0
  55. package/dist/types/connectivity/reconnect-helper.d.ts +8 -0
  56. package/dist/types/connectivity/singleton.d.ts +3 -0
  57. package/dist/types/disconnected-reason-mapper.d.ts +1 -0
  58. package/dist/types/error-code-mapper.d.ts +5 -1
  59. package/dist/types/helpers/const.d.ts +109 -25
  60. package/dist/types/provider/commit-step.d.ts +14 -0
  61. package/dist/types/provider/index.d.ts +9 -5
  62. package/dist/types/provider/telepointers.d.ts +5 -0
  63. package/dist/types/socket-io-provider.d.ts +2 -1
  64. package/dist/types/types.d.ts +27 -10
  65. package/package.json +4 -5
  66. package/report.api.md +29 -0
  67. package/.vscode/settings.json +0 -3
package/CHANGELOG.md CHANGED
@@ -1,5 +1,38 @@
1
1
  # @atlaskit/collab-provider
2
2
 
3
+ ## 8.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`1d36e909618`](https://bitbucket.org/atlassian/atlassian-frontend/commits/1d36e909618) - Log error on document restore
8
+ - [`0529b1b833c`](https://bitbucket.org/atlassian/atlassian-frontend/commits/0529b1b833c) - Trigger catch up call on process steps failing
9
+ - [`4f005660ce2`](https://bitbucket.org/atlassian/atlassian-frontend/commits/4f005660ce2) - Log step commit errors to analytics
10
+ - [`6034004a812`](https://bitbucket.org/atlassian/atlassian-frontend/commits/6034004a812) - Log errors on reconnection failure
11
+ - [`a261b2a9e6a`](https://bitbucket.org/atlassian/atlassian-frontend/commits/a261b2a9e6a) - [ESS-3183] Catch any errors in Presence functionality so an uncaught error there doesn't impact regular operation of the collab provider
12
+ - [`7f35ae7d99c`](https://bitbucket.org/atlassian/atlassian-frontend/commits/7f35ae7d99c) - [ESS-2815] Added network status to analytics events
13
+ - [`80feb6de229`](https://bitbucket.org/atlassian/atlassian-frontend/commits/80feb6de229) - [ESS-2815] Emit an error to consumers if the reconnections fails 8 times due to the network issues
14
+ - [`611d9c643c6`](https://bitbucket.org/atlassian/atlassian-frontend/commits/611d9c643c6) - [ESS-3183] Retry syncing unconfirmed steps
15
+
16
+ ### Patch Changes
17
+
18
+ - [`fccc5952d49`](https://bitbucket.org/atlassian/atlassian-frontend/commits/fccc5952d49) - Revert emitting errors to Confluence by default
19
+ - [`0d25bcca6bb`](https://bitbucket.org/atlassian/atlassian-frontend/commits/0d25bcca6bb) - Added more comprehensive error handling for performance/analytics/ufo events in collab provider
20
+ - [`e97495c5748`](https://bitbucket.org/atlassian/atlassian-frontend/commits/e97495c5748) - Extract emitTelepointer logic from Provider
21
+ - [`260d1355cc6`](https://bitbucket.org/atlassian/atlassian-frontend/commits/260d1355cc6) - Reconnect collab provider immediately when browser emits online event.
22
+ - [`5725fb45955`](https://bitbucket.org/atlassian/atlassian-frontend/commits/5725fb45955) - Introduce a file for UFO.
23
+
24
+ Introduce a file for commit step logic, which was already separate from the provider class
25
+
26
+ - [`2b648e4db70`](https://bitbucket.org/atlassian/atlassian-frontend/commits/2b648e4db70) - NO-ISSUE Remove the analytics fall-back after validating acks work as expected
27
+ - [`35c5e7dd9d5`](https://bitbucket.org/atlassian/atlassian-frontend/commits/35c5e7dd9d5) - NO-ISSUE deduplicate the analytics types dependency (again)
28
+ - [`1c255047a29`](https://bitbucket.org/atlassian/atlassian-frontend/commits/1c255047a29) - improve catchup error handling
29
+ - [`c9ad25cf224`](https://bitbucket.org/atlassian/atlassian-frontend/commits/c9ad25cf224) - [ESS-3183] Create abstraction around sending analytics events for errors or action events
30
+ - [`607a34f4426`](https://bitbucket.org/atlassian/atlassian-frontend/commits/607a34f4426) - Pass through the analytics web client in places it was missing
31
+ - [`efb112b06ab`](https://bitbucket.org/atlassian/atlassian-frontend/commits/efb112b06ab) - Move disonnected reason mapper
32
+ - [`9e6ceda8977`](https://bitbucket.org/atlassian/atlassian-frontend/commits/9e6ceda8977) - Provide the reason for a page reset
33
+ - [`6956eedc944`](https://bitbucket.org/atlassian/atlassian-frontend/commits/6956eedc944) - Tighten type definitions on analytics events
34
+ - Updated dependencies
35
+
3
36
  ## 8.2.0
4
37
 
5
38
  ### Minor Changes
@@ -4,34 +4,91 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
4
4
  Object.defineProperty(exports, "__esModule", {
5
5
  value: true
6
6
  });
7
- exports.triggerAnalyticsEvent = void 0;
7
+ exports.default = void 0;
8
+ var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
9
+ var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
8
10
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
9
11
  var _const = require("../helpers/const");
10
12
  var _versionWrapper = require("../version-wrapper");
13
+ var _singleton = require("../connectivity/singleton");
11
14
  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; }
12
15
  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; }
16
+ var EVENT_SUBJECT = 'collab';
17
+ var COLLAB_SERVICE;
18
+ (function (COLLAB_SERVICE) {
19
+ COLLAB_SERVICE["NCS"] = "ncs";
20
+ COLLAB_SERVICE["SYNCHRONY"] = "synchrony";
21
+ })(COLLAB_SERVICE || (COLLAB_SERVICE = {}));
13
22
  var triggerAnalyticsEvent = function triggerAnalyticsEvent(analyticsEvent, analyticsClient) {
14
23
  if (!analyticsClient) {
15
24
  return;
16
25
  }
17
26
  var payload = {
18
- actionSubject: _const.EVENT_SUBJECT,
27
+ actionSubject: EVENT_SUBJECT,
19
28
  attributes: _objectSpread({
20
29
  packageName: _versionWrapper.name,
21
30
  packageVersion: _versionWrapper.version,
22
- collabService: _const.COLLAB_SERVICE.NCS
31
+ collabService: COLLAB_SERVICE.NCS,
32
+ network: {
33
+ status: _singleton.network.getStatus()
34
+ }
23
35
  }, analyticsEvent.attributes),
24
36
  tags: ['editor'],
25
37
  action: analyticsEvent.eventAction,
26
38
  source: 'unknown' // Adds zero analytics value, but event validation throws an error if you don't add it :-(
27
39
  };
28
40
 
41
+ if (analyticsEvent.eventAction === _const.EVENT_ACTION.ERROR) {
42
+ payload.nonPrivacySafeAttributes = analyticsEvent.nonPrivacySafeAttributes;
43
+ }
44
+
29
45
  // Let the browser figure out
30
46
  // when it should send those events
31
- var requestIdleCallbackFunction = window.requestIdleCallback;
32
- var runItLater = typeof requestIdleCallbackFunction === 'function' ? requestIdleCallbackFunction : window.requestAnimationFrame;
33
- runItLater(function () {
34
- analyticsClient.sendOperationalEvent(payload);
35
- });
47
+ try {
48
+ var requestIdleCallbackFunction = window.requestIdleCallback;
49
+ var runItLater = typeof requestIdleCallbackFunction === 'function' ? requestIdleCallbackFunction : window.requestAnimationFrame;
50
+ runItLater(function () {
51
+ analyticsClient.sendOperationalEvent(payload);
52
+ });
53
+ } catch (error) {
54
+ // silently fail for now https://product-fabric.atlassian.net/browse/ESS-3112
55
+ }
36
56
  };
37
- exports.triggerAnalyticsEvent = triggerAnalyticsEvent;
57
+ var AnalyticsHelper = /*#__PURE__*/function () {
58
+ function AnalyticsHelper(documentAri, analyticsClient) {
59
+ (0, _classCallCheck2.default)(this, AnalyticsHelper);
60
+ this.analyticsClient = analyticsClient;
61
+ this.documentAri = documentAri;
62
+ }
63
+ (0, _createClass2.default)(AnalyticsHelper, [{
64
+ key: "sendErrorEvent",
65
+ value: function sendErrorEvent(error, errorMessage) {
66
+ var errorAnalyticsEvent = {
67
+ eventAction: _const.EVENT_ACTION.ERROR,
68
+ attributes: {
69
+ documentAri: this.documentAri,
70
+ errorMessage: errorMessage
71
+ },
72
+ nonPrivacySafeAttributes: {
73
+ error: error
74
+ }
75
+ };
76
+ triggerAnalyticsEvent(errorAnalyticsEvent, this.analyticsClient);
77
+ }
78
+ }, {
79
+ key: "sendActionEvent",
80
+ value: function sendActionEvent(action, status, attributes // This breaks discriminated unions, because there is no obvious field to discriminate against any more
81
+ ) {
82
+ var analyticsEvent = {
83
+ eventAction: action,
84
+ attributes: _objectSpread({
85
+ documentAri: this.documentAri,
86
+ eventStatus: status
87
+ }, attributes)
88
+ };
89
+ triggerAnalyticsEvent(analyticsEvent, this.analyticsClient);
90
+ }
91
+ }]);
92
+ return AnalyticsHelper;
93
+ }();
94
+ exports.default = AnalyticsHelper;
@@ -4,7 +4,6 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.MEASURE_NAME = void 0;
7
- exports.clearMeasure = clearMeasure;
8
7
  exports.startMeasure = startMeasure;
9
8
  exports.stopMeasure = stopMeasure;
10
9
  var MEASURE_NAME;
@@ -12,8 +11,8 @@ exports.MEASURE_NAME = MEASURE_NAME;
12
11
  (function (MEASURE_NAME) {
13
12
  MEASURE_NAME["SOCKET_CONNECT"] = "socketConnect";
14
13
  MEASURE_NAME["DOCUMENT_INIT"] = "documentInit";
15
- MEASURE_NAME["CONVERT_PM_TO_ADF"] = "convertPMToADF";
16
14
  MEASURE_NAME["COMMIT_UNCONFIRMED_STEPS"] = "commitUnconfirmedSteps";
15
+ MEASURE_NAME["PUBLISH_PAGE"] = "publishPage";
17
16
  })(MEASURE_NAME || (exports.MEASURE_NAME = MEASURE_NAME = {}));
18
17
  var isPerformanceAPIAvailable = function isPerformanceAPIAvailable() {
19
18
  return typeof window !== 'undefined' && 'performance' in window && ['measure', 'clearMeasures', 'clearMarks', 'getEntriesByName', 'getEntriesByType'].every(function (api) {
@@ -22,45 +21,56 @@ var isPerformanceAPIAvailable = function isPerformanceAPIAvailable() {
22
21
  };
23
22
  var hasPerformanceAPIAvailable = isPerformanceAPIAvailable();
24
23
  var measureMap = new Map();
25
- function startMeasure(measureName) {
26
- if (!hasPerformanceAPIAvailable) {
27
- return;
24
+ function startMeasure(measureName, analyticsHelper) {
25
+ try {
26
+ if (!hasPerformanceAPIAvailable) {
27
+ return;
28
+ }
29
+ performance.mark("".concat(measureName, "::start"));
30
+ measureMap.set(measureName, performance.now());
31
+ } catch (error) {
32
+ analyticsHelper === null || analyticsHelper === void 0 ? void 0 : analyticsHelper.sendErrorEvent(error, 'Error while measuring performance when marking the start');
28
33
  }
29
- performance.mark("".concat(measureName, "::start"));
30
- measureMap.set(measureName, performance.now());
31
34
  }
32
- function stopMeasure(measureName, onMeasureComplete) {
33
- if (!hasPerformanceAPIAvailable) {
34
- return;
35
- }
36
-
37
- // `startMeasure` is not called with `measureName` before.
38
- if (!measureMap.get(measureName)) {
39
- return;
40
- }
41
- performance.mark("".concat(measureName, "::end"));
42
- var start = onMeasureComplete ? measureMap.get(measureName) : undefined;
35
+ function stopMeasure(measureName, analyticsHelper, onMeasureComplete) {
36
+ var start;
43
37
  try {
38
+ if (!hasPerformanceAPIAvailable) {
39
+ return;
40
+ }
41
+
42
+ // `startMeasure` is not called with `measureName` before.
43
+ if (!measureMap.get(measureName)) {
44
+ return;
45
+ }
46
+ performance.mark("".concat(measureName, "::end"));
47
+ start = onMeasureComplete ? measureMap.get(measureName) : undefined;
44
48
  performance.measure(measureName, "".concat(measureName, "::start"), "".concat(measureName, "::end"));
45
- } catch (e) {}
46
- var entry = performance.getEntriesByName(measureName).pop();
47
- clearMeasure(measureName);
48
- var measure;
49
- if (entry) {
50
- measure = {
51
- duration: entry.duration,
52
- startTime: entry.startTime
53
- };
54
- } else if (start) {
55
- measure = {
56
- duration: performance.now() - start,
57
- startTime: start
58
- };
49
+ } catch (error) {
50
+ analyticsHelper === null || analyticsHelper === void 0 ? void 0 : analyticsHelper.sendErrorEvent(error, 'Error while measuring performance when marking the end');
59
51
  }
60
- if (measure && onMeasureComplete) {
61
- onMeasureComplete(measure.duration, measure.startTime);
52
+ try {
53
+ var entry = performance.getEntriesByName(measureName).pop();
54
+ clearMeasure(measureName);
55
+ var measure;
56
+ if (entry) {
57
+ measure = {
58
+ duration: entry.duration,
59
+ startTime: entry.startTime
60
+ };
61
+ } else if (start) {
62
+ measure = {
63
+ duration: performance.now() - start,
64
+ startTime: start
65
+ };
66
+ }
67
+ if (measure && onMeasureComplete) {
68
+ onMeasureComplete(measure.duration, measure.startTime);
69
+ }
70
+ return measure;
71
+ } catch (error) {
72
+ analyticsHelper === null || analyticsHelper === void 0 ? void 0 : analyticsHelper.sendErrorEvent(error, 'Error while measuring performance when completing the measurement');
62
73
  }
63
- return measure;
64
74
  }
65
75
  function clearMeasure(measureName) {
66
76
  if (!hasPerformanceAPIAvailable) {
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.createDocInitExp = void 0;
8
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
9
+ var _ufo = require("@atlaskit/ufo");
10
+ var createDocumentInitExperience = function createDocumentInitExperience() {
11
+ return new _ufo.UFOExperience('collab-provider.document-init', {
12
+ type: _ufo.ExperienceTypes.Load,
13
+ performanceType: _ufo.ExperiencePerformanceTypes.Custom,
14
+ performanceConfig: {
15
+ histogram: (0, _defineProperty2.default)({}, _ufo.ExperiencePerformanceTypes.Custom, {
16
+ duration: '250_500_1000_1500_2000_3000_4000'
17
+ })
18
+ }
19
+ });
20
+ };
21
+ var withErrorHandling = function withErrorHandling(createExperience, analyticsHelper) {
22
+ var initExperience;
23
+ try {
24
+ initExperience = createExperience();
25
+ } catch (error) {
26
+ analyticsHelper === null || analyticsHelper === void 0 ? void 0 : analyticsHelper.sendErrorEvent(error, 'Error while initialising a UFO experience');
27
+ }
28
+ return initExperience;
29
+ };
30
+ var createDocInitExp = function createDocInitExp(analyticsHelper) {
31
+ return withErrorHandling(createDocumentInitExperience, analyticsHelper);
32
+ };
33
+ exports.createDocInitExp = createDocInitExp;
@@ -19,9 +19,10 @@ var _emitter = require("./emitter");
19
19
  var _errorCodeMapper = require("./error-code-mapper");
20
20
  var _utils = require("./helpers/utils");
21
21
  var _performance = require("./analytics/performance");
22
- var _analytics = require("./analytics");
23
22
  var _const = require("./helpers/const");
24
- var _ufo = require("@atlaskit/ufo");
23
+ var _reconnectHelper = _interopRequireDefault(require("./connectivity/reconnect-helper"));
24
+ var _ufo = require("./analytics/ufo");
25
+ var _network = _interopRequireDefault(require("./connectivity/network"));
25
26
  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; }
26
27
  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; }
27
28
  function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = (0, _getPrototypeOf2.default)(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = (0, _getPrototypeOf2.default)(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return (0, _possibleConstructorReturn2.default)(this, result); }; }
@@ -30,22 +31,15 @@ var logger = (0, _utils.createLogger)('Channel', 'green');
30
31
  var Channel = /*#__PURE__*/function (_Emitter) {
31
32
  (0, _inherits2.default)(Channel, _Emitter);
32
33
  var _super = _createSuper(Channel);
33
- function Channel(config) {
34
+ function Channel(config, analyticsHelper) {
34
35
  var _this;
35
36
  (0, _classCallCheck2.default)(this, Channel);
36
37
  _this = _super.call(this);
37
38
  (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "connected", false);
38
39
  (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "socket", null);
40
+ (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "reconnectHelper", null);
39
41
  (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "initialized", false);
40
- (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "initExperience", new _ufo.UFOExperience('collab-provider.document-init', {
41
- type: _ufo.ExperienceTypes.Load,
42
- performanceType: _ufo.ExperiencePerformanceTypes.Custom,
43
- performanceConfig: {
44
- histogram: (0, _defineProperty2.default)({}, _ufo.ExperiencePerformanceTypes.Custom, {
45
- duration: '250_500_1000_1500_2000_3000_4000'
46
- })
47
- }
48
- }));
42
+ (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "network", null);
49
43
  (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "getInitialized", function () {
50
44
  return _this.initialized;
51
45
  });
@@ -56,16 +50,12 @@ var Channel = /*#__PURE__*/function (_Emitter) {
56
50
  return _this.socket;
57
51
  });
58
52
  (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "onConnectError", function (error) {
59
- var measure = (0, _performance.stopMeasure)(_performance.MEASURE_NAME.SOCKET_CONNECT);
60
- (0, _analytics.triggerAnalyticsEvent)({
61
- eventAction: _const.EVENT_ACTION.CONNECTION,
62
- attributes: {
63
- eventStatus: _const.EVENT_STATUS.FAILURE,
64
- error: error,
65
- latency: measure === null || measure === void 0 ? void 0 : measure.duration,
66
- documentAri: _this.config.documentAri
67
- }
68
- }, _this.analyticsClient);
53
+ var _this$analyticsHelper, _this$analyticsHelper2;
54
+ var measure = (0, _performance.stopMeasure)(_performance.MEASURE_NAME.SOCKET_CONNECT, _this.analyticsHelper);
55
+ (_this$analyticsHelper = _this.analyticsHelper) === null || _this$analyticsHelper === void 0 ? void 0 : _this$analyticsHelper.sendActionEvent(_const.EVENT_ACTION.CONNECTION, _const.EVENT_STATUS.FAILURE, {
56
+ latency: measure === null || measure === void 0 ? void 0 : measure.duration
57
+ });
58
+ (_this$analyticsHelper2 = _this.analyticsHelper) === null || _this$analyticsHelper2 === void 0 ? void 0 : _this$analyticsHelper2.sendErrorEvent(error, 'Error while establishing connection');
69
59
  // If error received with `data`, it means the connection is rejected
70
60
  // by the server on purpose for example no permission, so no need to
71
61
  // keep the underneath connection, need to close. But some error like
@@ -79,18 +69,29 @@ var Channel = /*#__PURE__*/function (_Emitter) {
79
69
  data: error.data
80
70
  });
81
71
  });
72
+ (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "onReconnectError", function (error) {
73
+ var _this$reconnectHelper, _this$reconnectHelper2;
74
+ (_this$reconnectHelper = _this.reconnectHelper) === null || _this$reconnectHelper === void 0 ? void 0 : _this$reconnectHelper.countReconnectError();
75
+ if ((_this$reconnectHelper2 = _this.reconnectHelper) !== null && _this$reconnectHelper2 !== void 0 && _this$reconnectHelper2.isLikelyNetworkIssue()) {
76
+ var _this$analyticsHelper3;
77
+ (_this$analyticsHelper3 = _this.analyticsHelper) === null || _this$analyticsHelper3 === void 0 ? void 0 : _this$analyticsHelper3.sendErrorEvent(error, 'Likely network issue while reconnecting the channel');
78
+ _this.emit('error', {
79
+ message: 'Reconnection failed 8 times when browser was offline, likely there was a network issue.',
80
+ data: {
81
+ status: 400,
82
+ code: 'RECONNECTION_NETWORK_ISSUE'
83
+ }
84
+ });
85
+ }
86
+ });
82
87
  (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "onConnect", function () {
88
+ var _this$analyticsHelper4;
83
89
  _this.connected = true;
84
90
  logger('Connected.', _this.socket.id);
85
- var measure = (0, _performance.stopMeasure)(_performance.MEASURE_NAME.SOCKET_CONNECT);
86
- (0, _analytics.triggerAnalyticsEvent)({
87
- eventAction: _const.EVENT_ACTION.CONNECTION,
88
- attributes: {
89
- eventStatus: _const.EVENT_STATUS.SUCCESS,
90
- latency: measure === null || measure === void 0 ? void 0 : measure.duration,
91
- documentAri: _this.config.documentAri
92
- }
93
- }, _this.analyticsClient);
91
+ var measure = (0, _performance.stopMeasure)(_performance.MEASURE_NAME.SOCKET_CONNECT, _this.analyticsHelper);
92
+ (_this$analyticsHelper4 = _this.analyticsHelper) === null || _this$analyticsHelper4 === void 0 ? void 0 : _this$analyticsHelper4.sendActionEvent(_const.EVENT_ACTION.CONNECTION, _const.EVENT_STATUS.SUCCESS, {
93
+ latency: measure === null || measure === void 0 ? void 0 : measure.duration
94
+ });
94
95
  _this.emit('connected', {
95
96
  sid: _this.socket.id,
96
97
  initialized: _this.initialized
@@ -101,19 +102,16 @@ var Channel = /*#__PURE__*/function (_Emitter) {
101
102
  logger('Session ID is', _this.socket.id);
102
103
  if (data.type === 'initial') {
103
104
  if (!_this.initialized) {
104
- var measure = (0, _performance.stopMeasure)(_performance.MEASURE_NAME.DOCUMENT_INIT);
105
- _this.initExperience.success();
106
- (0, _analytics.triggerAnalyticsEvent)({
107
- eventAction: _const.EVENT_ACTION.DOCUMENT_INIT,
108
- attributes: {
109
- eventStatus: _const.EVENT_STATUS.SUCCESS,
110
- // TODO: detect when document init fails and fire corresponding event for it
111
- latency: measure === null || measure === void 0 ? void 0 : measure.duration,
112
- documentAri: _this.config.documentAri,
113
- requiredPageRecovery: data === null || data === void 0 ? void 0 : data.requiredPageRecovery,
114
- ttlEnabled: data === null || data === void 0 ? void 0 : data.ttlEnabled
115
- }
116
- }, _this.analyticsClient);
105
+ var _this$initExperience, _this$analyticsHelper5;
106
+ var measure = (0, _performance.stopMeasure)(_performance.MEASURE_NAME.DOCUMENT_INIT, _this.analyticsHelper);
107
+ (_this$initExperience = _this.initExperience) === null || _this$initExperience === void 0 ? void 0 : _this$initExperience.success();
108
+ (_this$analyticsHelper5 = _this.analyticsHelper) === null || _this$analyticsHelper5 === void 0 ? void 0 : _this$analyticsHelper5.sendActionEvent(_const.EVENT_ACTION.DOCUMENT_INIT,
109
+ // TODO: detect when document init fails and fire corresponding event for it
110
+ _const.EVENT_STATUS.SUCCESS, {
111
+ latency: measure === null || measure === void 0 ? void 0 : measure.duration,
112
+ resetReason: data === null || data === void 0 ? void 0 : data.resetReason,
113
+ ttlEnabled: data === null || data === void 0 ? void 0 : data.ttlEnabled
114
+ });
117
115
  var doc = data.doc,
118
116
  version = data.version,
119
117
  userId = data.userId,
@@ -142,10 +140,17 @@ var Channel = /*#__PURE__*/function (_Emitter) {
142
140
  _this.emit('steps:added', data);
143
141
  }
144
142
  });
143
+ (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "onOnlineHandler", function () {
144
+ // Force an immediate reconnect, the socket must first be closed to reset reconnection delay logic
145
+ if (!_this.connected) {
146
+ var _this$socket2, _this$socket3;
147
+ (_this$socket2 = _this.socket) === null || _this$socket2 === void 0 ? void 0 : _this$socket2.close();
148
+ (_this$socket3 = _this.socket) === null || _this$socket3 === void 0 ? void 0 : _this$socket3.connect();
149
+ }
150
+ });
145
151
  _this.config = config;
146
- if (config.analyticsClient) {
147
- _this.analyticsClient = config.analyticsClient;
148
- }
152
+ _this.analyticsHelper = analyticsHelper;
153
+ _this.initExperience = (0, _ufo.createDocInitExp)(_this.analyticsHelper);
149
154
  return _this;
150
155
  }
151
156
 
@@ -158,10 +163,11 @@ var Channel = /*#__PURE__*/function (_Emitter) {
158
163
  */
159
164
  function connect() {
160
165
  var _this2 = this;
161
- (0, _performance.startMeasure)(_performance.MEASURE_NAME.SOCKET_CONNECT);
166
+ (0, _performance.startMeasure)(_performance.MEASURE_NAME.SOCKET_CONNECT, this.analyticsHelper);
162
167
  if (!this.initialized) {
163
- (0, _performance.startMeasure)(_performance.MEASURE_NAME.DOCUMENT_INIT);
164
- this.initExperience.start();
168
+ var _this$initExperience2;
169
+ (0, _performance.startMeasure)(_performance.MEASURE_NAME.DOCUMENT_INIT, this.analyticsHelper);
170
+ (_this$initExperience2 = this.initExperience) === null || _this$initExperience2 === void 0 ? void 0 : _this$initExperience2.start();
165
171
  }
166
172
  var _this$config = this.config,
167
173
  documentAri = _this$config.documentAri,
@@ -237,6 +243,7 @@ var Channel = /*#__PURE__*/function (_Emitter) {
237
243
  });
238
244
  this.socket.on('disconnect', /*#__PURE__*/function () {
239
245
  var _ref2 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(reason) {
246
+ var _this2$analyticsHelpe;
240
247
  return _regenerator.default.wrap(function _callee$(_context) {
241
248
  while (1) {
242
249
  switch (_context.prev = _context.next) {
@@ -248,7 +255,18 @@ var Channel = /*#__PURE__*/function (_Emitter) {
248
255
  });
249
256
  if (reason === 'io server disconnect' && _this2.socket) {
250
257
  // The disconnection was initiated by the server, we need to reconnect manually.
251
- _this2.socket.connect();
258
+ try {
259
+ _this2.socket.connect();
260
+ } catch (error) {
261
+ (_this2$analyticsHelpe = _this2.analyticsHelper) === null || _this2$analyticsHelpe === void 0 ? void 0 : _this2$analyticsHelpe.sendErrorEvent(error, 'Error while reconnecting the channel');
262
+ _this2.emit('error', {
263
+ message: 'Caught error during reconnection.',
264
+ data: {
265
+ status: 500,
266
+ code: 'RECONNECTION_ERROR'
267
+ }
268
+ });
269
+ }
252
270
  }
253
271
  case 4:
254
272
  case "end":
@@ -262,14 +280,26 @@ var Channel = /*#__PURE__*/function (_Emitter) {
262
280
  };
263
281
  }());
264
282
 
265
- // Socket error, including errors from `packetMiddleware`, `controllers` and `onconnect` and more. Paramter is a plain JSON object.
283
+ // Socket error, including errors from `packetMiddleware`, `controllers` and `onconnect` and more. Parameter is a plain JSON object.
266
284
  this.socket.on('error', function (error) {
267
285
  _this2.emit('error', error);
268
286
  });
269
287
 
270
- // `connect_error`'s paramter type is `Error`.
288
+ // `connect_error`'s parameter type is `Error`.
271
289
  // Ensure the error emit to the provider has the same structure, so we can handle them unified.
272
290
  this.socket.on('connect_error', this.onConnectError);
291
+
292
+ // To trigger reconnection when browser comes back online
293
+ if (!this.network) {
294
+ this.network = new _network.default({
295
+ onlineCallback: this.onOnlineHandler
296
+ });
297
+ }
298
+
299
+ // Helper class to track reconnection issues, to emit an error if too many failed reconnection attempts happen
300
+ this.reconnectHelper = new _reconnectHelper.default();
301
+ // Fired upon a reconnection attempt error (from Socket.IO Manager)
302
+ this.socket.io.on('reconnect_error', this.onReconnectError);
273
303
  }
274
304
  }, {
275
305
  key: "fetchCatchup",
@@ -380,7 +410,7 @@ var Channel = /*#__PURE__*/function (_Emitter) {
380
410
  }
381
411
  };
382
412
  this.emit('error', errorCatchup);
383
- return _context2.abrupt("return", {});
413
+ throw _context2.t19;
384
414
  case 52:
385
415
  case "end":
386
416
  return _context2.stop();
@@ -425,10 +455,15 @@ var Channel = /*#__PURE__*/function (_Emitter) {
425
455
  }, {
426
456
  key: "disconnect",
427
457
  value: function disconnect() {
458
+ var _this$network;
428
459
  this.unsubscribeAll();
460
+ (_this$network = this.network) === null || _this$network === void 0 ? void 0 : _this$network.destroy();
461
+ this.network = null;
429
462
  if (this.socket) {
463
+ var _this$reconnectHelper3;
430
464
  this.socket.close();
431
465
  this.socket = null;
466
+ (_this$reconnectHelper3 = this.reconnectHelper) === null || _this$reconnectHelper3 === void 0 ? void 0 : _this$reconnectHelper3.destroy();
432
467
  }
433
468
  }
434
469
  }]);
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.default = exports.NetworkStatus = void 0;
8
+ var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
9
+ var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
10
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
11
+ var NetworkStatus;
12
+ exports.NetworkStatus = NetworkStatus;
13
+ (function (NetworkStatus) {
14
+ NetworkStatus["ONLINE"] = "ONLINE";
15
+ NetworkStatus["OFFLINE"] = "OFFLINE";
16
+ })(NetworkStatus || (exports.NetworkStatus = NetworkStatus = {}));
17
+ var Network = /*#__PURE__*/function () {
18
+ function Network(props) {
19
+ var _this = this;
20
+ (0, _classCallCheck2.default)(this, Network);
21
+ (0, _defineProperty2.default)(this, "offlineHandler", function () {
22
+ _this.status = NetworkStatus.OFFLINE;
23
+ });
24
+ (0, _defineProperty2.default)(this, "onlineHandler", function () {
25
+ _this.status = NetworkStatus.ONLINE;
26
+ if (_this.onlineCallback) {
27
+ _this.onlineCallback();
28
+ }
29
+ });
30
+ if (props !== null && props !== void 0 && props.initialStatus) {
31
+ this.status = props.initialStatus;
32
+ }
33
+ if (props !== null && props !== void 0 && props.onlineCallback) {
34
+ this.onlineCallback = props.onlineCallback;
35
+ }
36
+ window.addEventListener('offline', this.offlineHandler);
37
+ window.addEventListener('online', this.onlineHandler);
38
+ }
39
+ (0, _createClass2.default)(Network, [{
40
+ key: "getStatus",
41
+ value: function getStatus() {
42
+ return this.status || null;
43
+ }
44
+ }, {
45
+ key: "destroy",
46
+ value: function destroy() {
47
+ window.removeEventListener('offline', this.offlineHandler);
48
+ window.removeEventListener('online', this.onlineHandler);
49
+ }
50
+ }]);
51
+ return Network;
52
+ }();
53
+ exports.default = Network;
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.default = void 0;
8
+ var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
9
+ var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
10
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
11
+ var _network = require("./network");
12
+ var _singleton = require("./singleton");
13
+ // Calculation for max 8 offline reconnect attempts, with reconnection delay 200ms, randomization factor 0.5, reconnection delay max 128s
14
+ // Min: 800ms, Avg: 51s, Max: 6m
15
+ var FAILED_RECONNECTS_WHILE_OFFLINE_THRESHOLD = 8;
16
+ var ReconnectHelper = /*#__PURE__*/function () {
17
+ function ReconnectHelper() {
18
+ var _this = this;
19
+ (0, _classCallCheck2.default)(this, ReconnectHelper);
20
+ (0, _defineProperty2.default)(this, "failedReconnectCount", 0);
21
+ (0, _defineProperty2.default)(this, "onlineHandler", function () {
22
+ _this.failedReconnectCount = 0;
23
+ });
24
+ window.addEventListener('online', this.onlineHandler);
25
+ }
26
+ (0, _createClass2.default)(ReconnectHelper, [{
27
+ key: "countReconnectError",
28
+ value: function countReconnectError() {
29
+ // Only count the reconnection attempts when offline
30
+ if (_singleton.network.getStatus() === _network.NetworkStatus.OFFLINE) {
31
+ this.failedReconnectCount++;
32
+ }
33
+ }
34
+ }, {
35
+ key: "isLikelyNetworkIssue",
36
+ value: function isLikelyNetworkIssue() {
37
+ var isLikelyNetworkIssue = this.failedReconnectCount >= FAILED_RECONNECTS_WHILE_OFFLINE_THRESHOLD;
38
+ return isLikelyNetworkIssue;
39
+ }
40
+ }, {
41
+ key: "destroy",
42
+ value: function destroy() {
43
+ window.removeEventListener('online', this.onlineHandler);
44
+ }
45
+ }]);
46
+ return ReconnectHelper;
47
+ }();
48
+ exports.default = ReconnectHelper;
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+
3
+ var _typeof = require("@babel/runtime/helpers/typeof");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.network = void 0;
8
+ var _network = _interopRequireWildcard(require("./network"));
9
+ function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
10
+ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
11
+ // Assume the connection is established at first
12
+ var network = new _network.default({
13
+ initialStatus: _network.NetworkStatus.ONLINE
14
+ });
15
+ exports.network = network;