@webex/plugin-meetings 3.12.0-next.7 → 3.12.0-next.70

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 (178) hide show
  1. package/AGENTS.md +9 -0
  2. package/dist/aiEnableRequest/index.js +15 -2
  3. package/dist/aiEnableRequest/index.js.map +1 -1
  4. package/dist/breakouts/breakout.js +8 -3
  5. package/dist/breakouts/breakout.js.map +1 -1
  6. package/dist/breakouts/index.js +26 -2
  7. package/dist/breakouts/index.js.map +1 -1
  8. package/dist/config.js +2 -0
  9. package/dist/config.js.map +1 -1
  10. package/dist/constants.js +30 -7
  11. package/dist/constants.js.map +1 -1
  12. package/dist/controls-options-manager/constants.js +11 -1
  13. package/dist/controls-options-manager/constants.js.map +1 -1
  14. package/dist/controls-options-manager/index.js +38 -24
  15. package/dist/controls-options-manager/index.js.map +1 -1
  16. package/dist/controls-options-manager/util.js +91 -0
  17. package/dist/controls-options-manager/util.js.map +1 -1
  18. package/dist/hashTree/constants.js +13 -1
  19. package/dist/hashTree/constants.js.map +1 -1
  20. package/dist/hashTree/hashTreeParser.js +880 -382
  21. package/dist/hashTree/hashTreeParser.js.map +1 -1
  22. package/dist/hashTree/utils.js +42 -0
  23. package/dist/hashTree/utils.js.map +1 -1
  24. package/dist/index.js +7 -0
  25. package/dist/index.js.map +1 -1
  26. package/dist/interceptors/dataChannelAuthToken.js +75 -15
  27. package/dist/interceptors/dataChannelAuthToken.js.map +1 -1
  28. package/dist/interceptors/locusRetry.js +23 -8
  29. package/dist/interceptors/locusRetry.js.map +1 -1
  30. package/dist/interpretation/index.js +10 -1
  31. package/dist/interpretation/index.js.map +1 -1
  32. package/dist/interpretation/interpretation.types.js +7 -0
  33. package/dist/interpretation/interpretation.types.js.map +1 -0
  34. package/dist/interpretation/siLanguage.js +1 -1
  35. package/dist/locus-info/controlsUtils.js +4 -1
  36. package/dist/locus-info/controlsUtils.js.map +1 -1
  37. package/dist/locus-info/index.js +298 -87
  38. package/dist/locus-info/index.js.map +1 -1
  39. package/dist/locus-info/types.js +19 -0
  40. package/dist/locus-info/types.js.map +1 -1
  41. package/dist/media/index.js +3 -1
  42. package/dist/media/index.js.map +1 -1
  43. package/dist/media/properties.js +1 -0
  44. package/dist/media/properties.js.map +1 -1
  45. package/dist/meeting/in-meeting-actions.js +3 -1
  46. package/dist/meeting/in-meeting-actions.js.map +1 -1
  47. package/dist/meeting/index.js +1046 -689
  48. package/dist/meeting/index.js.map +1 -1
  49. package/dist/meeting/muteState.js +10 -1
  50. package/dist/meeting/muteState.js.map +1 -1
  51. package/dist/meeting/request.js +5 -2
  52. package/dist/meeting/request.js.map +1 -1
  53. package/dist/meeting/util.js +20 -2
  54. package/dist/meeting/util.js.map +1 -1
  55. package/dist/meeting-info/meeting-info-v2.js +2 -2
  56. package/dist/meeting-info/meeting-info-v2.js.map +1 -1
  57. package/dist/meetings/index.js +231 -78
  58. package/dist/meetings/index.js.map +1 -1
  59. package/dist/meetings/meetings.types.js +6 -1
  60. package/dist/meetings/meetings.types.js.map +1 -1
  61. package/dist/meetings/request.js +39 -0
  62. package/dist/meetings/request.js.map +1 -1
  63. package/dist/meetings/util.js +79 -5
  64. package/dist/meetings/util.js.map +1 -1
  65. package/dist/member/index.js +10 -0
  66. package/dist/member/index.js.map +1 -1
  67. package/dist/member/types.js.map +1 -1
  68. package/dist/member/util.js +3 -0
  69. package/dist/member/util.js.map +1 -1
  70. package/dist/metrics/constants.js +4 -1
  71. package/dist/metrics/constants.js.map +1 -1
  72. package/dist/multistream/codec/constants.js +63 -0
  73. package/dist/multistream/codec/constants.js.map +1 -0
  74. package/dist/multistream/mediaRequestManager.js +62 -15
  75. package/dist/multistream/mediaRequestManager.js.map +1 -1
  76. package/dist/multistream/receiveSlot.js +9 -0
  77. package/dist/multistream/receiveSlot.js.map +1 -1
  78. package/dist/reactions/reactions.type.js.map +1 -1
  79. package/dist/recording-controller/index.js +1 -3
  80. package/dist/recording-controller/index.js.map +1 -1
  81. package/dist/types/config.d.ts +2 -0
  82. package/dist/types/constants.d.ts +9 -1
  83. package/dist/types/controls-options-manager/constants.d.ts +6 -1
  84. package/dist/types/controls-options-manager/index.d.ts +10 -0
  85. package/dist/types/hashTree/constants.d.ts +2 -0
  86. package/dist/types/hashTree/hashTreeParser.d.ts +146 -17
  87. package/dist/types/hashTree/utils.d.ts +18 -0
  88. package/dist/types/index.d.ts +3 -0
  89. package/dist/types/interceptors/locusRetry.d.ts +4 -4
  90. package/dist/types/interpretation/interpretation.types.d.ts +10 -0
  91. package/dist/types/locus-info/index.d.ts +50 -6
  92. package/dist/types/locus-info/types.d.ts +21 -1
  93. package/dist/types/media/properties.d.ts +1 -0
  94. package/dist/types/meeting/in-meeting-actions.d.ts +2 -0
  95. package/dist/types/meeting/index.d.ts +78 -5
  96. package/dist/types/meeting/request.d.ts +1 -0
  97. package/dist/types/meeting/util.d.ts +8 -0
  98. package/dist/types/meetings/index.d.ts +30 -2
  99. package/dist/types/meetings/meetings.types.d.ts +15 -0
  100. package/dist/types/meetings/request.d.ts +14 -0
  101. package/dist/types/member/index.d.ts +1 -0
  102. package/dist/types/member/types.d.ts +1 -0
  103. package/dist/types/member/util.d.ts +1 -0
  104. package/dist/types/metrics/constants.d.ts +3 -0
  105. package/dist/types/multistream/codec/constants.d.ts +7 -0
  106. package/dist/types/multistream/mediaRequestManager.d.ts +22 -5
  107. package/dist/types/reactions/reactions.type.d.ts +3 -0
  108. package/dist/webinar/index.js +305 -159
  109. package/dist/webinar/index.js.map +1 -1
  110. package/package.json +22 -22
  111. package/src/aiEnableRequest/index.ts +16 -0
  112. package/src/breakouts/breakout.ts +3 -1
  113. package/src/breakouts/index.ts +31 -0
  114. package/src/config.ts +2 -0
  115. package/src/constants.ts +13 -2
  116. package/src/controls-options-manager/constants.ts +14 -1
  117. package/src/controls-options-manager/index.ts +47 -24
  118. package/src/controls-options-manager/util.ts +81 -1
  119. package/src/hashTree/constants.ts +16 -0
  120. package/src/hashTree/hashTreeParser.ts +580 -196
  121. package/src/hashTree/utils.ts +36 -0
  122. package/src/index.ts +6 -0
  123. package/src/interceptors/dataChannelAuthToken.ts +88 -12
  124. package/src/interceptors/locusRetry.ts +25 -4
  125. package/src/interpretation/index.ts +27 -9
  126. package/src/interpretation/interpretation.types.ts +11 -0
  127. package/src/locus-info/controlsUtils.ts +3 -1
  128. package/src/locus-info/index.ts +293 -97
  129. package/src/locus-info/types.ts +25 -1
  130. package/src/media/index.ts +3 -0
  131. package/src/media/properties.ts +1 -0
  132. package/src/meeting/in-meeting-actions.ts +4 -0
  133. package/src/meeting/index.ts +386 -48
  134. package/src/meeting/muteState.ts +10 -1
  135. package/src/meeting/request.ts +11 -0
  136. package/src/meeting/util.ts +21 -2
  137. package/src/meeting-info/meeting-info-v2.ts +4 -2
  138. package/src/meetings/index.ts +134 -44
  139. package/src/meetings/meetings.types.ts +19 -0
  140. package/src/meetings/request.ts +43 -0
  141. package/src/meetings/util.ts +97 -1
  142. package/src/member/index.ts +10 -0
  143. package/src/member/types.ts +1 -0
  144. package/src/member/util.ts +3 -0
  145. package/src/metrics/constants.ts +3 -0
  146. package/src/multistream/codec/constants.ts +58 -0
  147. package/src/multistream/mediaRequestManager.ts +119 -28
  148. package/src/multistream/receiveSlot.ts +18 -0
  149. package/src/reactions/reactions.type.ts +3 -0
  150. package/src/recording-controller/index.ts +1 -2
  151. package/src/webinar/index.ts +214 -36
  152. package/test/unit/spec/aiEnableRequest/index.ts +86 -0
  153. package/test/unit/spec/breakouts/breakout.ts +9 -3
  154. package/test/unit/spec/breakouts/index.ts +49 -0
  155. package/test/unit/spec/controls-options-manager/index.js +140 -29
  156. package/test/unit/spec/controls-options-manager/util.js +165 -0
  157. package/test/unit/spec/hashTree/hashTreeParser.ts +1838 -180
  158. package/test/unit/spec/hashTree/utils.ts +125 -1
  159. package/test/unit/spec/interceptors/dataChannelAuthToken.ts +196 -0
  160. package/test/unit/spec/interceptors/locusRetry.ts +205 -4
  161. package/test/unit/spec/interpretation/index.ts +26 -4
  162. package/test/unit/spec/locus-info/controlsUtils.js +172 -57
  163. package/test/unit/spec/locus-info/index.js +487 -81
  164. package/test/unit/spec/media/index.ts +31 -0
  165. package/test/unit/spec/meeting/in-meeting-actions.ts +2 -0
  166. package/test/unit/spec/meeting/index.js +1240 -37
  167. package/test/unit/spec/meeting/muteState.js +81 -0
  168. package/test/unit/spec/meeting/request.js +12 -0
  169. package/test/unit/spec/meeting/utils.js +33 -0
  170. package/test/unit/spec/meeting-info/meetinginfov2.js +19 -10
  171. package/test/unit/spec/meetings/index.js +360 -10
  172. package/test/unit/spec/meetings/request.js +141 -0
  173. package/test/unit/spec/meetings/utils.js +189 -0
  174. package/test/unit/spec/member/index.js +7 -0
  175. package/test/unit/spec/member/util.js +24 -0
  176. package/test/unit/spec/multistream/mediaRequestManager.ts +501 -37
  177. package/test/unit/spec/recording-controller/index.js +9 -8
  178. package/test/unit/spec/webinar/index.ts +329 -28
@@ -15,17 +15,18 @@ var _interopRequireDefault = require("@babel/runtime-corejs2/helpers/interopRequ
15
15
  _Object$defineProperty(exports, "__esModule", {
16
16
  value: true
17
17
  });
18
- exports.default = exports.MeetingEndedError = exports.LocusInfoUpdateType = void 0;
18
+ exports.default = exports.SyncAllBackoffType = exports.MeetingEndedError = exports.LocusNotFoundError = exports.LocusInfoUpdateType = void 0;
19
19
  var _regenerator = _interopRequireDefault(require("@babel/runtime-corejs2/regenerator"));
20
- var _stringify = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/json/stringify"));
20
+ var _set = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/set"));
21
21
  var _promise = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/promise"));
22
+ var _stringify = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/json/stringify"));
22
23
  var _isArray = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/array/is-array"));
23
24
  var _keys = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/object/keys"));
24
25
  var _values = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/object/values"));
25
26
  var _parseInt2 = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/parse-int"));
27
+ var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/toConsumableArray"));
26
28
  var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/slicedToArray"));
27
29
  var _typeof2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/typeof"));
28
- var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/toConsumableArray"));
29
30
  var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/asyncToGenerator"));
30
31
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/defineProperty"));
31
32
  var _createClass2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/createClass"));
@@ -37,9 +38,12 @@ var _wrapNativeSuper2 = _interopRequireDefault(require("@babel/runtime-corejs2/h
37
38
  var _lodash = require("lodash");
38
39
  var _hashTree = _interopRequireDefault(require("./hashTree"));
39
40
  var _loggerProxy = _interopRequireDefault(require("../common/logs/logger-proxy"));
40
- var _constants = require("../constants");
41
- var _constants2 = require("./constants");
41
+ var _metrics = _interopRequireDefault(require("../metrics"));
42
+ var _constants = _interopRequireDefault(require("../metrics/constants"));
43
+ var _constants2 = require("../constants");
44
+ var _constants3 = require("./constants");
42
45
  var _types = require("./types");
46
+ var _types2 = require("../locus-info/types");
43
47
  var _utils = require("./utils");
44
48
  function ownKeys(e, r) { var t = _Object$keys4(e); if (_Object$getOwnPropertySymbols) { var o = _Object$getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return _Object$getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
45
49
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : _Object$getOwnPropertyDescriptors ? _Object$defineProperties(e, _Object$getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { _Object$defineProperty(e, r, _Object$getOwnPropertyDescriptor(t, r)); }); } return e; }
@@ -48,9 +52,16 @@ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r)
48
52
  function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
49
53
  function _callSuper(t, o, e) { return o = (0, _getPrototypeOf2.default)(o), (0, _possibleConstructorReturn2.default)(t, _isNativeReflectConstruct() ? _Reflect$construct(o, e || [], (0, _getPrototypeOf2.default)(t).constructor) : o.apply(t, e)); }
50
54
  function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(_Reflect$construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
55
+ var SyncAllBackoffType = exports.SyncAllBackoffType = /*#__PURE__*/function (SyncAllBackoffType) {
56
+ SyncAllBackoffType["NONE"] = "none";
57
+ SyncAllBackoffType["ONLY_LLM"] = "onlyLLM";
58
+ SyncAllBackoffType["ALL"] = "all";
59
+ return SyncAllBackoffType;
60
+ }({});
51
61
  var LocusInfoUpdateType = exports.LocusInfoUpdateType = {
52
62
  OBJECTS_UPDATED: 'OBJECTS_UPDATED',
53
- MEETING_ENDED: 'MEETING_ENDED'
63
+ MEETING_ENDED: 'MEETING_ENDED',
64
+ LOCUS_NOT_FOUND: 'LOCUS_NOT_FOUND'
54
65
  };
55
66
  /**
56
67
  * This error is thrown if we receive information that the meeting has ended while we're processing some hash messages.
@@ -64,11 +75,24 @@ var MeetingEndedError = exports.MeetingEndedError = /*#__PURE__*/function (_Erro
64
75
  (0, _inherits2.default)(MeetingEndedError, _Error);
65
76
  return (0, _createClass2.default)(MeetingEndedError);
66
77
  }(/*#__PURE__*/(0, _wrapNativeSuper2.default)(Error));
78
+ /**
79
+ * This error is thrown when a 404 is received from Locus hash tree endpoints, indicating that the locus URL
80
+ * is no longer valid (e.g. participant moved to a breakout room, or meeting ended).
81
+ * It's handled internally by HashTreeParser and results in LOCUS_NOT_FOUND being sent up.
82
+ */
83
+ var LocusNotFoundError = exports.LocusNotFoundError = /*#__PURE__*/function (_Error2) {
84
+ function LocusNotFoundError() {
85
+ (0, _classCallCheck2.default)(this, LocusNotFoundError);
86
+ return _callSuper(this, LocusNotFoundError, arguments);
87
+ }
88
+ (0, _inherits2.default)(LocusNotFoundError, _Error2);
89
+ return (0, _createClass2.default)(LocusNotFoundError);
90
+ }(/*#__PURE__*/(0, _wrapNativeSuper2.default)(Error));
67
91
  /* Currently Locus always sends Metadata objects only in the "self" dataset.
68
92
  * If this ever changes, update all the code that relies on this constant.
69
93
  */
70
- var MetadataDataSetName = _constants2.DataSetNames.SELF;
71
- var PossibleSentinelMessageDataSetNames = [_constants2.DataSetNames.MAIN, _constants2.DataSetNames.SELF, _constants2.DataSetNames.UNJOINED];
94
+ var MetadataDataSetName = _constants3.DataSetNames.SELF;
95
+ var PossibleSentinelMessageDataSetNames = [_constants3.DataSetNames.MAIN, _constants3.DataSetNames.SELF, _constants3.DataSetNames.UNJOINED];
72
96
 
73
97
  /**
74
98
  * Parses hash tree eventing locus data
@@ -89,9 +113,17 @@ var HashTreeParser = /*#__PURE__*/function () {
89
113
  (0, _defineProperty2.default)(this, "locusInfoUpdateCallback", void 0);
90
114
  (0, _defineProperty2.default)(this, "visibleDataSets", void 0);
91
115
  (0, _defineProperty2.default)(this, "debugId", void 0);
92
- (0, _defineProperty2.default)(this, "heartbeatIntervalMs", void 0);
93
116
  (0, _defineProperty2.default)(this, "excludedDataSets", void 0);
94
117
  (0, _defineProperty2.default)(this, "state", void 0);
118
+ (0, _defineProperty2.default)(this, "syncQueue", []);
119
+ (0, _defineProperty2.default)(this, "isSyncInProgress", false);
120
+ // tracks whether syncAllDatasets is currently in its backoff delay phase and with what scope
121
+ (0, _defineProperty2.default)(this, "syncAllBackoffType", SyncAllBackoffType.NONE);
122
+ // datasets that received messages during the syncAllDatasets backoff sleep and should be skipped
123
+ (0, _defineProperty2.default)(this, "dataSetsSyncedDuringBackoff", new _set.default());
124
+ (0, _defineProperty2.default)(this, "syncQueueProcessingPromise", _promise.default.resolve());
125
+ // top-level heartbeat interval from the most recent message, used as fallback when dataset-level value is missing
126
+ (0, _defineProperty2.default)(this, "topLevelHeartbeatIntervalMs", void 0);
95
127
  var _options$initialLocus = options.initialLocus,
96
128
  dataSets = _options$initialLocus.dataSets,
97
129
  locus = _options$initialLocus.locus; // extract dataSets from initialLocus
@@ -212,80 +244,65 @@ var HashTreeParser = /*#__PURE__*/function () {
212
244
  */
213
245
  }, {
214
246
  key: "initializeNewVisibleDataSet",
215
- value: function initializeNewVisibleDataSet(visibleDataSetInfo, dataSetInfo) {
216
- if (this.isVisibleDataSet(dataSetInfo.name)) {
217
- _loggerProxy.default.logger.info("HashTreeParser#initializeNewVisibleDataSet --> ".concat(this.debugId, " Data set \"").concat(dataSetInfo.name, "\" already exists, skipping init"));
218
- return _promise.default.resolve({
219
- updateType: LocusInfoUpdateType.OBJECTS_UPDATED,
220
- updatedObjects: []
221
- });
222
- }
223
- _loggerProxy.default.logger.info("HashTreeParser#initializeNewVisibleDataSet --> ".concat(this.debugId, " Adding visible data set \"").concat(dataSetInfo.name, "\""));
224
- if (!this.addToVisibleDataSetsList(visibleDataSetInfo)) {
225
- return _promise.default.resolve({
226
- updateType: LocusInfoUpdateType.OBJECTS_UPDATED,
227
- updatedObjects: []
228
- });
229
- }
230
- var hashTree = new _hashTree.default([], dataSetInfo.leafCount);
231
- this.dataSets[dataSetInfo.name] = _objectSpread(_objectSpread({}, dataSetInfo), {}, {
232
- hashTree: hashTree
233
- });
234
- return this.sendInitializationSyncRequestToLocus(dataSetInfo.name, 'new visible data set');
235
- }
236
-
237
- /**
238
- * Sends a special sync request to Locus with all leaves empty - this is a way to get all the data for a given dataset.
239
- *
240
- * @param {string} datasetName - name of the dataset for which to send the request
241
- * @param {string} debugText - text to include in logs
242
- * @returns {Promise}
243
- */
244
- }, {
245
- key: "sendInitializationSyncRequestToLocus",
246
- value: function sendInitializationSyncRequestToLocus(datasetName, debugText) {
247
- var _this2 = this;
248
- var dataset = this.dataSets[datasetName];
249
- if (!dataset) {
250
- _loggerProxy.default.logger.warn("HashTreeParser#sendInitializationSyncRequestToLocus --> ".concat(this.debugId, " No data set found for ").concat(datasetName, ", cannot send the request for leaf data"));
251
- return _promise.default.resolve(null);
247
+ value: (function () {
248
+ var _initializeNewVisibleDataSet = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee(visibleDataSetInfo, dataSetInfo) {
249
+ var hashTree;
250
+ return _regenerator.default.wrap(function (_context) {
251
+ while (1) switch (_context.prev = _context.next) {
252
+ case 0:
253
+ if (!this.isVisibleDataSet(dataSetInfo.name)) {
254
+ _context.next = 1;
255
+ break;
256
+ }
257
+ _loggerProxy.default.logger.info("HashTreeParser#initializeNewVisibleDataSet --> ".concat(this.debugId, " Data set \"").concat(dataSetInfo.name, "\" already exists, skipping init"));
258
+ return _context.abrupt("return");
259
+ case 1:
260
+ _loggerProxy.default.logger.info("HashTreeParser#initializeNewVisibleDataSet --> ".concat(this.debugId, " Adding visible data set \"").concat(dataSetInfo.name, "\""));
261
+ if (this.addToVisibleDataSetsList(visibleDataSetInfo)) {
262
+ _context.next = 2;
263
+ break;
264
+ }
265
+ return _context.abrupt("return");
266
+ case 2:
267
+ hashTree = new _hashTree.default([], dataSetInfo.leafCount);
268
+ this.dataSets[dataSetInfo.name] = _objectSpread(_objectSpread({}, dataSetInfo), {}, {
269
+ hashTree: hashTree
270
+ });
271
+ this.enqueueSyncForDataset(dataSetInfo.name, 'new visible data set initialization', true);
272
+ _context.next = 3;
273
+ return this.syncQueueProcessingPromise;
274
+ case 3:
275
+ case "end":
276
+ return _context.stop();
277
+ }
278
+ }, _callee, this);
279
+ }));
280
+ function initializeNewVisibleDataSet(_x, _x2) {
281
+ return _initializeNewVisibleDataSet.apply(this, arguments);
252
282
  }
253
- var emptyLeavesData = new Array(dataset.leafCount).fill([]);
254
- _loggerProxy.default.logger.info("HashTreeParser#sendInitializationSyncRequestToLocus --> ".concat(this.debugId, " Sending initial sync request to Locus for data set \"").concat(datasetName, "\" with empty leaf data"));
255
- return this.sendSyncRequestToLocus(this.dataSets[datasetName], emptyLeavesData).then(function (syncResponse) {
256
- if (syncResponse) {
257
- return {
258
- updateType: LocusInfoUpdateType.OBJECTS_UPDATED,
259
- updatedObjects: _this2.parseMessage(syncResponse, "via empty leaves /sync API call for ".concat(debugText))
260
- };
261
- }
262
- return {
263
- updateType: LocusInfoUpdateType.OBJECTS_UPDATED,
264
- updatedObjects: []
265
- };
266
- });
267
- }
268
-
283
+ return initializeNewVisibleDataSet;
284
+ }()
269
285
  /**
270
286
  * Queries Locus for all up-to-date information about all visible data sets
271
287
  *
272
288
  * @returns {Promise}
273
289
  */
290
+ )
274
291
  }, {
275
292
  key: "getAllVisibleDataSetsFromLocus",
276
293
  value: function getAllVisibleDataSetsFromLocus() {
277
- var _this3 = this;
294
+ var _this2 = this;
278
295
  if (!this.visibleDataSetsUrl) {
279
296
  _loggerProxy.default.logger.warn("HashTreeParser#getAllVisibleDataSetsFromLocus --> ".concat(this.debugId, " No visibleDataSetsUrl, cannot get data sets information"));
280
297
  return _promise.default.resolve([]);
281
298
  }
282
299
  return this.webexRequest({
283
- method: _constants.HTTP_VERBS.GET,
300
+ method: _constants2.HTTP_VERBS.GET,
284
301
  uri: this.visibleDataSetsUrl
285
302
  }).then(function (response) {
286
303
  return response.body.dataSets;
287
304
  }).catch(function (error) {
288
- _this3.checkForSentinelHttpResponse(error);
305
+ _this2.checkForSentinelHttpResponse(error);
289
306
  throw error;
290
307
  });
291
308
  }
@@ -299,26 +316,26 @@ var HashTreeParser = /*#__PURE__*/function () {
299
316
  }, {
300
317
  key: "initializeFromMessage",
301
318
  value: (function () {
302
- var _initializeFromMessage = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee(message) {
319
+ var _initializeFromMessage = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee2(message) {
303
320
  var visibleDataSets;
304
- return _regenerator.default.wrap(function (_context) {
305
- while (1) switch (_context.prev = _context.next) {
321
+ return _regenerator.default.wrap(function (_context2) {
322
+ while (1) switch (_context2.prev = _context2.next) {
306
323
  case 0:
307
324
  this.visibleDataSetsUrl = message.visibleDataSetsUrl;
308
325
  _loggerProxy.default.logger.info("HashTreeParser#initializeFromMessage --> ".concat(this.debugId, " visibleDataSetsUrl=").concat(this.visibleDataSetsUrl));
309
- _context.next = 1;
326
+ _context2.next = 1;
310
327
  return this.getAllVisibleDataSetsFromLocus();
311
328
  case 1:
312
- visibleDataSets = _context.sent;
313
- _context.next = 2;
329
+ visibleDataSets = _context2.sent;
330
+ _context2.next = 2;
314
331
  return this.initializeDataSets(visibleDataSets, 'initialization from message');
315
332
  case 2:
316
333
  case "end":
317
- return _context.stop();
334
+ return _context2.stop();
318
335
  }
319
- }, _callee, this);
336
+ }, _callee2, this);
320
337
  }));
321
- function initializeFromMessage(_x) {
338
+ function initializeFromMessage(_x3) {
322
339
  return _initializeFromMessage.apply(this, arguments);
323
340
  }
324
341
  return initializeFromMessage;
@@ -336,34 +353,34 @@ var HashTreeParser = /*#__PURE__*/function () {
336
353
  }, {
337
354
  key: "initializeFromGetLociResponse",
338
355
  value: (function () {
339
- var _initializeFromGetLociResponse = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee2(locus) {
356
+ var _initializeFromGetLociResponse = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee3(locus) {
340
357
  var _locus$links2, _locus$links2$resourc, _locus$links2$resourc2;
341
358
  var visibleDataSets;
342
- return _regenerator.default.wrap(function (_context2) {
343
- while (1) switch (_context2.prev = _context2.next) {
359
+ return _regenerator.default.wrap(function (_context3) {
360
+ while (1) switch (_context3.prev = _context3.next) {
344
361
  case 0:
345
362
  if (locus !== null && locus !== void 0 && (_locus$links2 = locus.links) !== null && _locus$links2 !== void 0 && (_locus$links2$resourc = _locus$links2.resources) !== null && _locus$links2$resourc !== void 0 && (_locus$links2$resourc2 = _locus$links2$resourc.visibleDataSets) !== null && _locus$links2$resourc2 !== void 0 && _locus$links2$resourc2.url) {
346
- _context2.next = 1;
363
+ _context3.next = 1;
347
364
  break;
348
365
  }
349
366
  _loggerProxy.default.logger.warn("HashTreeParser#initializeFromGetLociResponse --> ".concat(this.debugId, " missing visibleDataSets url in GET Loci response, cannot initialize hash trees"));
350
- return _context2.abrupt("return");
367
+ return _context3.abrupt("return");
351
368
  case 1:
352
369
  this.visibleDataSetsUrl = locus.links.resources.visibleDataSets.url;
353
370
  _loggerProxy.default.logger.info("HashTreeParser#initializeFromGetLociResponse --> ".concat(this.debugId, " visibleDataSets url: ").concat(this.visibleDataSetsUrl));
354
- _context2.next = 2;
371
+ _context3.next = 2;
355
372
  return this.getAllVisibleDataSetsFromLocus();
356
373
  case 2:
357
- visibleDataSets = _context2.sent;
358
- _context2.next = 3;
374
+ visibleDataSets = _context3.sent;
375
+ _context3.next = 3;
359
376
  return this.initializeDataSets(visibleDataSets, 'initialization from GET /loci response');
360
377
  case 3:
361
378
  case "end":
362
- return _context2.stop();
379
+ return _context3.stop();
363
380
  }
364
- }, _callee2, this);
381
+ }, _callee3, this);
365
382
  }));
366
- function initializeFromGetLociResponse(_x2) {
383
+ function initializeFromGetLociResponse(_x4) {
367
384
  return _initializeFromGetLociResponse.apply(this, arguments);
368
385
  }
369
386
  return initializeFromGetLociResponse;
@@ -379,24 +396,23 @@ var HashTreeParser = /*#__PURE__*/function () {
379
396
  }, {
380
397
  key: "initializeDataSets",
381
398
  value: (function () {
382
- var _initializeDataSets = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee3(visibleDataSets, debugText) {
383
- var updatedObjects, _iterator2, _step2, dataSet, name, leafCount, url, _data, _t;
384
- return _regenerator.default.wrap(function (_context3) {
385
- while (1) switch (_context3.prev = _context3.next) {
399
+ var _initializeDataSets = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee4(visibleDataSets, debugText) {
400
+ var _iterator2, _step2, dataSet, name, leafCount, url, _t;
401
+ return _regenerator.default.wrap(function (_context4) {
402
+ while (1) switch (_context4.prev = _context4.next) {
386
403
  case 0:
387
404
  if (!(this.state === 'stopped')) {
388
- _context3.next = 1;
405
+ _context4.next = 1;
389
406
  break;
390
407
  }
391
- return _context3.abrupt("return");
408
+ return _context4.abrupt("return");
392
409
  case 1:
393
- updatedObjects = [];
394
- _iterator2 = _createForOfIteratorHelper(visibleDataSets);
395
- _context3.prev = 2;
410
+ _iterator2 = _createForOfIteratorHelper((0, _utils.sortByInitPriority)(visibleDataSets, _constants3.DATA_SET_INIT_PRIORITY));
411
+ _context4.prev = 2;
396
412
  _iterator2.s();
397
413
  case 3:
398
414
  if ((_step2 = _iterator2.n()).done) {
399
- _context3.next = 7;
415
+ _context4.next = 6;
400
416
  break;
401
417
  }
402
418
  dataSet = _step2.value;
@@ -408,59 +424,47 @@ var HashTreeParser = /*#__PURE__*/function () {
408
424
  _loggerProxy.default.logger.info("HashTreeParser#initializeDataSets --> ".concat(this.debugId, " dataset \"").concat(name, "\" already exists (").concat(debugText, ")"));
409
425
  }
410
426
  if (this.isVisibleDataSet(name)) {
411
- _context3.next = 4;
427
+ _context4.next = 4;
412
428
  break;
413
429
  }
414
430
  if (this.addToVisibleDataSetsList({
415
431
  name: name,
416
432
  url: url
417
433
  })) {
418
- _context3.next = 4;
434
+ _context4.next = 4;
419
435
  break;
420
436
  }
421
- return _context3.abrupt("continue", 6);
437
+ return _context4.abrupt("continue", 5);
422
438
  case 4:
423
- if (this.dataSets[name].hashTree) {
424
- _context3.next = 6;
425
- break;
439
+ if (!this.dataSets[name].hashTree) {
440
+ _loggerProxy.default.logger.info("HashTreeParser#initializeDataSets --> ".concat(this.debugId, " creating hash tree for visible dataset \"").concat(name, "\" (").concat(debugText, ")"));
441
+ this.dataSets[name].hashTree = new _hashTree.default([], leafCount);
442
+ this.enqueueSyncForDataset(name, "initialization sync for ".concat(debugText), true);
426
443
  }
427
- _loggerProxy.default.logger.info("HashTreeParser#initializeDataSets --> ".concat(this.debugId, " creating hash tree for visible dataset \"").concat(name, "\" (").concat(debugText, ")"));
428
- this.dataSets[name].hashTree = new _hashTree.default([], leafCount);
429
-
430
- // eslint-disable-next-line no-await-in-loop
431
- _context3.next = 5;
432
- return this.sendInitializationSyncRequestToLocus(name, debugText);
433
444
  case 5:
434
- _data = _context3.sent;
435
- if (_data.updateType === LocusInfoUpdateType.OBJECTS_UPDATED) {
436
- updatedObjects.push.apply(updatedObjects, (0, _toConsumableArray2.default)(_data.updatedObjects || []));
437
- }
445
+ _context4.next = 3;
446
+ break;
438
447
  case 6:
439
- _context3.next = 3;
448
+ _context4.next = 8;
440
449
  break;
441
450
  case 7:
442
- _context3.next = 9;
443
- break;
444
- case 8:
445
- _context3.prev = 8;
446
- _t = _context3["catch"](2);
451
+ _context4.prev = 7;
452
+ _t = _context4["catch"](2);
447
453
  _iterator2.e(_t);
448
- case 9:
449
- _context3.prev = 9;
454
+ case 8:
455
+ _context4.prev = 8;
450
456
  _iterator2.f();
451
- return _context3.finish(9);
457
+ return _context4.finish(8);
458
+ case 9:
459
+ _context4.next = 10;
460
+ return this.syncQueueProcessingPromise;
452
461
  case 10:
453
- this.callLocusInfoUpdateCallback({
454
- updateType: LocusInfoUpdateType.OBJECTS_UPDATED,
455
- updatedObjects: updatedObjects
456
- });
457
- case 11:
458
462
  case "end":
459
- return _context3.stop();
463
+ return _context4.stop();
460
464
  }
461
- }, _callee3, this, [[2, 8, 9, 10]]);
465
+ }, _callee4, this, [[2, 7, 8, 9]]);
462
466
  }));
463
- function initializeDataSets(_x3, _x4) {
467
+ function initializeDataSets(_x5, _x6) {
464
468
  return _initializeDataSets.apply(this, arguments);
465
469
  }
466
470
  return initializeDataSets;
@@ -596,9 +600,9 @@ var HashTreeParser = /*#__PURE__*/function () {
596
600
  }, {
597
601
  key: "isEndMessage",
598
602
  value: function isEndMessage(message) {
599
- var _this4 = this;
603
+ var _this3 = this;
600
604
  return message.dataSets.some(function (dataSet) {
601
- if (dataSet.leafCount === 1 && dataSet.root === _constants2.EMPTY_HASH && (!_this4.dataSets[dataSet.name] || _this4.dataSets[dataSet.name].version < dataSet.version) && PossibleSentinelMessageDataSetNames.includes(dataSet.name.toLowerCase())) {
605
+ if (dataSet.leafCount === 1 && dataSet.root === _constants3.EMPTY_HASH && (!_this3.dataSets[dataSet.name] || _this3.dataSets[dataSet.name].version < dataSet.version) && PossibleSentinelMessageDataSetNames.includes(dataSet.name.toLowerCase())) {
602
606
  // this is a special way for Locus to indicate that this meeting has ended
603
607
  return true;
604
608
  }
@@ -615,7 +619,7 @@ var HashTreeParser = /*#__PURE__*/function () {
615
619
  }, {
616
620
  key: "handleRootHashHeartBeatMessage",
617
621
  value: function handleRootHashHeartBeatMessage(message) {
618
- var _this5 = this;
622
+ var _this4 = this;
619
623
  var dataSets = message.dataSets;
620
624
  _loggerProxy.default.logger.info("HashTreeParser#handleRootHashMessage --> ".concat(this.debugId, " Received heartbeat root hash message with data sets: ").concat((0, _stringify.default)(dataSets.map(function (_ref2) {
621
625
  var name = _ref2.name,
@@ -629,12 +633,40 @@ var HashTreeParser = /*#__PURE__*/function () {
629
633
  version: version
630
634
  };
631
635
  }))));
636
+ this.cancelPendingSyncsForDataSets(dataSets.map(function (ds) {
637
+ return ds.name;
638
+ }));
632
639
  dataSets.forEach(function (dataSet) {
633
- _this5.updateDataSetInfo(dataSet);
634
- _this5.runSyncAlgorithm(dataSet);
640
+ _this4.updateDataSetInfo(dataSet);
641
+ _this4.runSyncAlgorithm(dataSet);
635
642
  });
636
643
  }
637
644
 
645
+ /**
646
+ * Handles known errors that can happen during syncs
647
+ *
648
+ * @param {any} error - The error to handle
649
+ * @returns {boolean} true if the error was recognized and handled, false otherwise
650
+ */
651
+ }, {
652
+ key: "handleSyncErrors",
653
+ value: function handleSyncErrors(error) {
654
+ if (error instanceof MeetingEndedError) {
655
+ this.callLocusInfoUpdateCallback({
656
+ updateType: LocusInfoUpdateType.MEETING_ENDED
657
+ });
658
+ return true;
659
+ }
660
+ if (error instanceof LocusNotFoundError) {
661
+ this.callLocusInfoUpdateCallback({
662
+ updateType: LocusInfoUpdateType.LOCUS_NOT_FOUND
663
+ });
664
+ this.stop();
665
+ return true;
666
+ }
667
+ return false;
668
+ }
669
+
638
670
  /**
639
671
  * Asynchronously initializes new visible data sets
640
672
  *
@@ -644,18 +676,14 @@ var HashTreeParser = /*#__PURE__*/function () {
644
676
  }, {
645
677
  key: "queueInitForNewVisibleDataSets",
646
678
  value: function queueInitForNewVisibleDataSets(dataSetsRequiringInitialization) {
647
- var _this6 = this;
679
+ var _this5 = this;
648
680
  _loggerProxy.default.logger.info("HashTreeParser#queueInitForNewVisibleDataSets --> ".concat(this.debugId, " queuing initialization of new visible datasets: ").concat(dataSetsRequiringInitialization.map(function (ds) {
649
681
  return ds.name;
650
682
  }).join(', ')));
651
683
  queueMicrotask(function () {
652
- _this6.initializeNewVisibleDataSets(dataSetsRequiringInitialization).catch(function (error) {
653
- if (error instanceof MeetingEndedError) {
654
- _this6.callLocusInfoUpdateCallback({
655
- updateType: LocusInfoUpdateType.MEETING_ENDED
656
- });
657
- } else {
658
- _loggerProxy.default.logger.warn("HashTreeParser#queueInitForNewVisibleDataSets --> ".concat(_this6.debugId, " error while initializing new visible datasets: ").concat(dataSetsRequiringInitialization.map(function (ds) {
684
+ _this5.initializeNewVisibleDataSets(dataSetsRequiringInitialization).catch(function (error) {
685
+ if (!_this5.handleSyncErrors(error)) {
686
+ _loggerProxy.default.logger.warn("HashTreeParser#queueInitForNewVisibleDataSets --> ".concat(_this5.debugId, " error while initializing new visible datasets: ").concat(dataSetsRequiringInitialization.map(function (ds) {
659
687
  return ds.name;
660
688
  }).join(', '), ": "), error);
661
689
  }
@@ -715,14 +743,18 @@ var HashTreeParser = /*#__PURE__*/function () {
715
743
  }, {
716
744
  key: "handleLocusUpdate",
717
745
  value: function handleLocusUpdate(update) {
718
- var _this7 = this;
746
+ var _this6 = this;
719
747
  if (this.state === 'stopped') {
720
748
  return;
721
749
  }
722
750
  var dataSets = update.dataSets,
723
751
  locus = update.locus,
724
752
  metadata = update.metadata;
753
+ _loggerProxy.default.logger.info("HashTreeParser#handleLocusUpdate --> ".concat(this.debugId, " received update with dataSets=").concat(dataSets === null || dataSets === void 0 ? void 0 : dataSets.map(function (ds) {
754
+ return ds.name;
755
+ }).join(','), " metadata=").concat(metadata ? 'yes' : 'no'));
725
756
  if (!dataSets) {
757
+ // this happens for example when we handle GET /loci response
726
758
  _loggerProxy.default.logger.info("HashTreeParser#handleLocusUpdate --> ".concat(this.debugId, " received hash tree update without dataSets"));
727
759
  } else {
728
760
  var _iterator5 = _createForOfIteratorHelper(dataSets),
@@ -752,9 +784,9 @@ var HashTreeParser = /*#__PURE__*/function () {
752
784
 
753
785
  // then process the data in hash trees, if it is a new version, then add it to updatedObjects
754
786
  (0, _keys.default)(leafInfo).forEach(function (dataSetName) {
755
- if (_this7.dataSets[dataSetName]) {
756
- if (_this7.dataSets[dataSetName].hashTree) {
757
- var appliedChangesList = _this7.dataSets[dataSetName].hashTree.putItems(leafInfo[dataSetName].map(function (leaf) {
787
+ if (_this6.dataSets[dataSetName]) {
788
+ if (_this6.dataSets[dataSetName].hashTree) {
789
+ var appliedChangesList = _this6.dataSets[dataSetName].hashTree.putItems(leafInfo[dataSetName].map(function (leaf) {
758
790
  return {
759
791
  id: leaf.id,
760
792
  type: leaf.type,
@@ -781,10 +813,10 @@ var HashTreeParser = /*#__PURE__*/function () {
781
813
  });
782
814
  } else {
783
815
  // no hash tree means that the data set is not visible
784
- _loggerProxy.default.logger.warn("HashTreeParser#handleLocusUpdate --> ".concat(_this7.debugId, " received leaf data for data set \"").concat(dataSetName, "\" that has no hash tree created, ignoring"));
816
+ _loggerProxy.default.logger.warn("HashTreeParser#handleLocusUpdate --> ".concat(_this6.debugId, " received leaf data for data set \"").concat(dataSetName, "\" that has no hash tree created, ignoring"));
785
817
  }
786
818
  } else {
787
- _loggerProxy.default.logger.info("HashTreeParser#handleLocusUpdate --> ".concat(_this7.debugId, " received leaf data for unknown data set \"").concat(dataSetName, "\", ignoring"));
819
+ _loggerProxy.default.logger.info("HashTreeParser#handleLocusUpdate --> ".concat(_this6.debugId, " received leaf data for unknown data set \"").concat(dataSetName, "\", ignoring"));
788
820
  }
789
821
  });
790
822
  if (updatedObjects.length === 0) {
@@ -820,10 +852,26 @@ var HashTreeParser = /*#__PURE__*/function () {
820
852
  maxMs: receivedDataSet.backoff.maxMs,
821
853
  exponent: receivedDataSet.backoff.exponent
822
854
  };
855
+ this.dataSets[receivedDataSet.name].heartbeatIntervalMs = receivedDataSet.heartbeatIntervalMs;
823
856
  _loggerProxy.default.logger.info("HashTreeParser#updateDataSetInfo --> ".concat(this.debugId, " updated \"").concat(receivedDataSet.name, "\" dataset to version=").concat(receivedDataSet.version, ", root=").concat(receivedDataSet.root));
824
857
  }
825
858
  }
826
859
 
860
+ /**
861
+ * Updates the leaf count for a data set, resizing its hash tree accordingly.
862
+ *
863
+ * @param {InternalDataSet} dataSet - The data set to update
864
+ * @param {number} newLeafCount - The new leaf count
865
+ * @returns {void}
866
+ */
867
+ }, {
868
+ key: "updateDataSetLeafCount",
869
+ value: function updateDataSetLeafCount(dataSet, newLeafCount) {
870
+ var _dataSet$hashTree;
871
+ (_dataSet$hashTree = dataSet.hashTree) === null || _dataSet$hashTree === void 0 ? void 0 : _dataSet$hashTree.resize(newLeafCount);
872
+ dataSet.leafCount = newLeafCount;
873
+ }
874
+
827
875
  /**
828
876
  * Checks for changes in the visible data sets based on the updated objects.
829
877
  * @param {HashTreeObject[]} updatedObjects - The list of updated hash tree objects.
@@ -832,7 +880,7 @@ var HashTreeParser = /*#__PURE__*/function () {
832
880
  }, {
833
881
  key: "checkForVisibleDataSetChanges",
834
882
  value: function checkForVisibleDataSetChanges(updatedObjects) {
835
- var _this8 = this;
883
+ var _this7 = this;
836
884
  var removedDataSets = [];
837
885
  var addedDataSets = [];
838
886
 
@@ -841,20 +889,20 @@ var HashTreeParser = /*#__PURE__*/function () {
841
889
  var _object$data;
842
890
  if ((0, _utils.isMetadata)(object) && (_object$data = object.data) !== null && _object$data !== void 0 && _object$data.visibleDataSets) {
843
891
  var newVisibleDataSets = object.data.visibleDataSets.filter(function (vds) {
844
- return !_this8.isExcludedDataSet(vds.name);
892
+ return !_this7.isExcludedDataSet(vds.name);
845
893
  });
846
- removedDataSets = _this8.visibleDataSets.filter(function (ds) {
894
+ removedDataSets = _this7.visibleDataSets.filter(function (ds) {
847
895
  return !newVisibleDataSets.some(function (nvs) {
848
896
  return nvs.name === ds.name;
849
897
  });
850
898
  });
851
899
  addedDataSets = newVisibleDataSets.filter(function (nvs) {
852
- return _this8.visibleDataSets.every(function (ds) {
900
+ return _this7.visibleDataSets.every(function (ds) {
853
901
  return ds.name !== nvs.name;
854
902
  });
855
903
  });
856
904
  if (removedDataSets.length > 0 || addedDataSets.length > 0) {
857
- _loggerProxy.default.logger.info("HashTreeParser#checkForVisibleDataSetChanges --> ".concat(_this8.debugId, " visible data sets change: removed: ").concat(removedDataSets.map(function (ds) {
905
+ _loggerProxy.default.logger.info("HashTreeParser#checkForVisibleDataSetChanges --> ".concat(_this7.debugId, " visible data sets change: removed: ").concat(removedDataSets.map(function (ds) {
858
906
  return ds.name;
859
907
  }).join(', '), ", added: ").concat(addedDataSets.map(function (ds) {
860
908
  return ds.name;
@@ -878,7 +926,10 @@ var HashTreeParser = /*#__PURE__*/function () {
878
926
  }, {
879
927
  key: "deleteHashTree",
880
928
  value: function deleteHashTree(dataSetName) {
929
+ var _this$dataSets$dataSe;
881
930
  this.dataSets[dataSetName].hashTree = undefined;
931
+ (_this$dataSets$dataSe = this.dataSets[dataSetName].syncAbortController) === null || _this$dataSets$dataSe === void 0 ? void 0 : _this$dataSets$dataSe.abort();
932
+ this.dataSets[dataSetName].syncAbortController = undefined;
882
933
 
883
934
  // we also need to stop the timers as there is no hash tree anymore to sync
884
935
  if (this.dataSets[dataSetName].timer) {
@@ -908,16 +959,16 @@ var HashTreeParser = /*#__PURE__*/function () {
908
959
  }, {
909
960
  key: "processVisibleDataSetChanges",
910
961
  value: function processVisibleDataSetChanges(removedDataSets, addedDataSets, updatedObjects) {
911
- var _this9 = this;
962
+ var _this8 = this;
912
963
  var dataSetsRequiringInitialization = [];
913
964
 
914
965
  // if a visible data set was removed, we need to tell our client that all objects from it are removed
915
966
  var removedObjects = [];
916
967
  removedDataSets.forEach(function (ds) {
917
- var _this9$dataSets$ds$na;
918
- if ((_this9$dataSets$ds$na = _this9.dataSets[ds.name]) !== null && _this9$dataSets$ds$na !== void 0 && _this9$dataSets$ds$na.hashTree) {
919
- for (var i = 0; i < _this9.dataSets[ds.name].hashTree.numLeaves; i += 1) {
920
- removedObjects.push.apply(removedObjects, (0, _toConsumableArray2.default)(_this9.dataSets[ds.name].hashTree.getLeafData(i).map(function (elementId) {
968
+ var _this8$dataSets$ds$na;
969
+ if ((_this8$dataSets$ds$na = _this8.dataSets[ds.name]) !== null && _this8$dataSets$ds$na !== void 0 && _this8$dataSets$ds$na.hashTree) {
970
+ for (var i = 0; i < _this8.dataSets[ds.name].hashTree.numLeaves; i += 1) {
971
+ removedObjects.push.apply(removedObjects, (0, _toConsumableArray2.default)(_this8.dataSets[ds.name].hashTree.getLeafData(i).map(function (elementId) {
921
972
  return {
922
973
  htMeta: {
923
974
  elementId: elementId,
@@ -927,7 +978,7 @@ var HashTreeParser = /*#__PURE__*/function () {
927
978
  };
928
979
  })));
929
980
  }
930
- _this9.deleteHashTree(ds.name);
981
+ _this8.deleteHashTree(ds.name);
931
982
  }
932
983
  });
933
984
  this.visibleDataSets = this.visibleDataSets.filter(function (vds) {
@@ -988,81 +1039,78 @@ var HashTreeParser = /*#__PURE__*/function () {
988
1039
  }, {
989
1040
  key: "initializeNewVisibleDataSets",
990
1041
  value: (function () {
991
- var _initializeNewVisibleDataSets = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee4(addedDataSets) {
992
- var _this0 = this;
1042
+ var _initializeNewVisibleDataSets = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee5(addedDataSets) {
1043
+ var _this9 = this;
993
1044
  var allDataSets, _iterator7, _step7, _loop, _t2;
994
- return _regenerator.default.wrap(function (_context5) {
995
- while (1) switch (_context5.prev = _context5.next) {
1045
+ return _regenerator.default.wrap(function (_context6) {
1046
+ while (1) switch (_context6.prev = _context6.next) {
996
1047
  case 0:
997
1048
  if (!(this.state === 'stopped')) {
998
- _context5.next = 1;
1049
+ _context6.next = 1;
999
1050
  break;
1000
1051
  }
1001
- return _context5.abrupt("return");
1052
+ return _context6.abrupt("return");
1002
1053
  case 1:
1003
- _context5.next = 2;
1054
+ _context6.next = 2;
1004
1055
  return this.getAllVisibleDataSetsFromLocus();
1005
1056
  case 2:
1006
- allDataSets = _context5.sent;
1007
- _iterator7 = _createForOfIteratorHelper(addedDataSets);
1008
- _context5.prev = 3;
1057
+ allDataSets = _context6.sent;
1058
+ _iterator7 = _createForOfIteratorHelper((0, _utils.sortByInitPriority)(addedDataSets, _constants3.DATA_SET_INIT_PRIORITY));
1059
+ _context6.prev = 3;
1009
1060
  _loop = /*#__PURE__*/_regenerator.default.mark(function _loop() {
1010
- var ds, dataSetInfo, updates;
1011
- return _regenerator.default.wrap(function (_context4) {
1012
- while (1) switch (_context4.prev = _context4.next) {
1061
+ var ds, dataSetInfo;
1062
+ return _regenerator.default.wrap(function (_context5) {
1063
+ while (1) switch (_context5.prev = _context5.next) {
1013
1064
  case 0:
1014
1065
  ds = _step7.value;
1015
1066
  dataSetInfo = allDataSets.find(function (d) {
1016
1067
  return d.name === ds.name;
1017
1068
  });
1018
- _loggerProxy.default.logger.info("HashTreeParser#initializeNewVisibleDataSets --> ".concat(_this0.debugId, " initializing data set \"").concat(ds.name, "\""));
1069
+ _loggerProxy.default.logger.info("HashTreeParser#initializeNewVisibleDataSets --> ".concat(_this9.debugId, " initializing data set \"").concat(ds.name, "\""));
1019
1070
  if (dataSetInfo) {
1020
- _context4.next = 1;
1071
+ _context5.next = 1;
1021
1072
  break;
1022
1073
  }
1023
- _loggerProxy.default.logger.warn("HashTreeParser#initializeNewVisibleDataSets --> ".concat(_this0.debugId, " missing info about data set \"").concat(ds.name, "\" in Locus response from visibleDataSetsUrl"));
1024
- _context4.next = 3;
1074
+ _loggerProxy.default.logger.warn("HashTreeParser#initializeNewVisibleDataSets --> ".concat(_this9.debugId, " missing info about data set \"").concat(ds.name, "\" in Locus response from visibleDataSetsUrl"));
1075
+ _context5.next = 2;
1025
1076
  break;
1026
1077
  case 1:
1027
- _context4.next = 2;
1028
- return _this0.initializeNewVisibleDataSet(ds, dataSetInfo);
1078
+ _context5.next = 2;
1079
+ return _this9.initializeNewVisibleDataSet(ds, dataSetInfo);
1029
1080
  case 2:
1030
- updates = _context4.sent;
1031
- _this0.callLocusInfoUpdateCallback(updates);
1032
- case 3:
1033
1081
  case "end":
1034
- return _context4.stop();
1082
+ return _context5.stop();
1035
1083
  }
1036
1084
  }, _loop);
1037
1085
  });
1038
1086
  _iterator7.s();
1039
1087
  case 4:
1040
1088
  if ((_step7 = _iterator7.n()).done) {
1041
- _context5.next = 6;
1089
+ _context6.next = 6;
1042
1090
  break;
1043
1091
  }
1044
- return _context5.delegateYield(_loop(), "t0", 5);
1092
+ return _context6.delegateYield(_loop(), "t0", 5);
1045
1093
  case 5:
1046
- _context5.next = 4;
1094
+ _context6.next = 4;
1047
1095
  break;
1048
1096
  case 6:
1049
- _context5.next = 8;
1097
+ _context6.next = 8;
1050
1098
  break;
1051
1099
  case 7:
1052
- _context5.prev = 7;
1053
- _t2 = _context5["catch"](3);
1100
+ _context6.prev = 7;
1101
+ _t2 = _context6["catch"](3);
1054
1102
  _iterator7.e(_t2);
1055
1103
  case 8:
1056
- _context5.prev = 8;
1104
+ _context6.prev = 8;
1057
1105
  _iterator7.f();
1058
- return _context5.finish(8);
1106
+ return _context6.finish(8);
1059
1107
  case 9:
1060
1108
  case "end":
1061
- return _context5.stop();
1109
+ return _context6.stop();
1062
1110
  }
1063
- }, _callee4, this, [[3, 7, 8, 9]]);
1111
+ }, _callee5, this, [[3, 7, 8, 9]]);
1064
1112
  }));
1065
- function initializeNewVisibleDataSets(_x5) {
1113
+ function initializeNewVisibleDataSets(_x7) {
1066
1114
  return _initializeNewVisibleDataSets.apply(this, arguments);
1067
1115
  }
1068
1116
  return initializeNewVisibleDataSets;
@@ -1078,25 +1126,37 @@ var HashTreeParser = /*#__PURE__*/function () {
1078
1126
  }, {
1079
1127
  key: "parseMessage",
1080
1128
  value: function parseMessage(message, debugText) {
1081
- var _message$locusStateEl,
1082
- _this1 = this,
1083
- _message$locusStateEl2;
1129
+ var _message$dataSets,
1130
+ _message$locusStateEl,
1131
+ _message$locusStateEl2,
1132
+ _this0 = this;
1084
1133
  if (this.state === 'stopped') {
1085
1134
  return [];
1086
1135
  }
1087
1136
  var dataSets = message.dataSets,
1088
1137
  visibleDataSetsUrl = message.visibleDataSetsUrl;
1089
- _loggerProxy.default.logger.info("HashTreeParser#parseMessage --> ".concat(this.debugId, " received message ").concat(debugText || '', ":"), message);
1090
- if (((_message$locusStateEl = message.locusStateElements) === null || _message$locusStateEl === void 0 ? void 0 : _message$locusStateEl.length) === 0) {
1138
+ _loggerProxy.default.logger.info("HashTreeParser#parseMessage --> ".concat(this.debugId, " ").concat(debugText || '', " dataSets: ").concat((_message$dataSets = message.dataSets) === null || _message$dataSets === void 0 ? void 0 : _message$dataSets.map(function (_ref5) {
1139
+ var name = _ref5.name,
1140
+ version = _ref5.version;
1141
+ return "".concat(name, ":").concat(version);
1142
+ }).join(','), ", elements: ").concat((_message$locusStateEl = message.locusStateElements) === null || _message$locusStateEl === void 0 ? void 0 : _message$locusStateEl.map(function (el) {
1143
+ return "".concat(el.htMeta.elementId.type, ":").concat(el.htMeta.elementId.id, ":").concat(el.htMeta.elementId.version).concat(el.data ? '+' : '-');
1144
+ }).join(',')));
1145
+ if (((_message$locusStateEl2 = message.locusStateElements) === null || _message$locusStateEl2 === void 0 ? void 0 : _message$locusStateEl2.length) === 0) {
1091
1146
  _loggerProxy.default.logger.warn("HashTreeParser#parseMessage --> ".concat(this.debugId, " got empty locusStateElements!!!"));
1092
- // todo: send a metric
1147
+ _metrics.default.sendBehavioralMetric(_constants.default.HASH_TREE_EMPTY_LOCUS_STATE_ELEMENTS, {
1148
+ debugId: this.debugId
1149
+ });
1093
1150
  }
1094
1151
 
1095
1152
  // first, update our metadata about the datasets with info from the message
1096
1153
  this.visibleDataSetsUrl = visibleDataSetsUrl;
1097
1154
  dataSets.forEach(function (dataSet) {
1098
- return _this1.updateDataSetInfo(dataSet);
1155
+ return _this0.updateDataSetInfo(dataSet);
1099
1156
  });
1157
+ this.cancelPendingSyncsForDataSets(dataSets.map(function (ds) {
1158
+ return ds.name;
1159
+ }));
1100
1160
  var updatedObjects = [];
1101
1161
 
1102
1162
  // when we detect new visible datasets, it may be that the metadata about them is not
@@ -1115,9 +1175,9 @@ var HashTreeParser = /*#__PURE__*/function () {
1115
1175
  _step8;
1116
1176
  try {
1117
1177
  for (_iterator8.s(); !(_step8 = _iterator8.n()).done;) {
1118
- var _this1$dataSets$dataS;
1178
+ var _this0$dataSets$dataS;
1119
1179
  var dataSetName = _step8.value;
1120
- var hashTree = (_this1$dataSets$dataS = _this1.dataSets[dataSetName]) === null || _this1$dataSets$dataS === void 0 ? void 0 : _this1$dataSets$dataS.hashTree;
1180
+ var hashTree = (_this0$dataSets$dataS = _this0.dataSets[dataSetName]) === null || _this0$dataSets$dataS === void 0 ? void 0 : _this0$dataSets$dataS.hashTree;
1121
1181
  if (hashTree && object.data) {
1122
1182
  if (hashTree.putItem(object.htMeta.elementId)) {
1123
1183
  updatedMetadataObjects.push(object);
@@ -1139,13 +1199,13 @@ var HashTreeParser = /*#__PURE__*/function () {
1139
1199
  dataSetsRequiringInitialization = this.processVisibleDataSetChanges(removedDataSets, addedDataSets, updatedObjects);
1140
1200
  }
1141
1201
  }
1142
- if (((_message$locusStateEl2 = message.locusStateElements) === null || _message$locusStateEl2 === void 0 ? void 0 : _message$locusStateEl2.length) > 0) {
1202
+ if (message.locusStateElements && message.locusStateElements.length > 0) {
1143
1203
  // by this point we now have this.dataSets setup for data sets from this message
1144
1204
  // and hash trees created for the new visible data sets,
1145
1205
  // so we can now process all the updates from the message
1146
1206
  dataSets.forEach(function (dataSet) {
1147
- if (_this1.dataSets[dataSet.name]) {
1148
- var hashTree = _this1.dataSets[dataSet.name].hashTree;
1207
+ if (_this0.dataSets[dataSet.name]) {
1208
+ var hashTree = _this0.dataSets[dataSet.name].hashTree;
1149
1209
  if (hashTree) {
1150
1210
  var locusStateElementsForThisSet = message.locusStateElements.filter(function (object) {
1151
1211
  return object.htMeta.dataSetNames.includes(dataSet.name);
@@ -1159,20 +1219,20 @@ var HashTreeParser = /*#__PURE__*/function () {
1159
1219
  item: object.htMeta.elementId
1160
1220
  };
1161
1221
  }));
1162
- (0, _lodash.zip)(appliedChangesList, locusStateElementsForThisSet).forEach(function (_ref5) {
1163
- var _ref6 = (0, _slicedToArray2.default)(_ref5, 2),
1164
- changeApplied = _ref6[0],
1165
- object = _ref6[1];
1222
+ (0, _lodash.zip)(appliedChangesList, locusStateElementsForThisSet).forEach(function (_ref6) {
1223
+ var _ref7 = (0, _slicedToArray2.default)(_ref6, 2),
1224
+ changeApplied = _ref7[0],
1225
+ object = _ref7[1];
1166
1226
  if (changeApplied) {
1167
1227
  // add to updatedObjects so that our locus DTO will get updated with the new object
1168
1228
  updatedObjects.push(object);
1169
1229
  }
1170
1230
  });
1171
1231
  } else {
1172
- _loggerProxy.default.logger.info("Locus-info:index#parseMessage --> ".concat(_this1.debugId, " unexpected (not visible) dataSet ").concat(dataSet.name, " received in hash tree message"));
1232
+ _loggerProxy.default.logger.info("Locus-info:index#parseMessage --> ".concat(_this0.debugId, " unexpected (not visible) dataSet ").concat(dataSet.name, " received in hash tree message"));
1173
1233
  }
1174
1234
  }
1175
- _this1.runSyncAlgorithm(dataSet);
1235
+ _this0.runSyncAlgorithm(dataSet);
1176
1236
  });
1177
1237
  }
1178
1238
  if (dataSetsRequiringInitialization.length > 0) {
@@ -1198,8 +1258,8 @@ var HashTreeParser = /*#__PURE__*/function () {
1198
1258
  if (this.state === 'stopped') {
1199
1259
  return;
1200
1260
  }
1201
- if (message.heartbeatIntervalMs) {
1202
- this.heartbeatIntervalMs = message.heartbeatIntervalMs;
1261
+ if (message.heartbeatIntervalMs !== undefined) {
1262
+ this.topLevelHeartbeatIntervalMs = message.heartbeatIntervalMs;
1203
1263
  }
1204
1264
  if (this.isEndMessage(message)) {
1205
1265
  _loggerProxy.default.logger.info("HashTreeParser#handleMessage --> ".concat(this.debugId, " received sentinel END MEETING message"));
@@ -1229,25 +1289,25 @@ var HashTreeParser = /*#__PURE__*/function () {
1229
1289
  }, {
1230
1290
  key: "callLocusInfoUpdateCallback",
1231
1291
  value: function callLocusInfoUpdateCallback(updates) {
1232
- var _this10 = this;
1292
+ var _updates$updatedObjec,
1293
+ _this1 = this;
1233
1294
  if (this.state === 'stopped') {
1234
1295
  return;
1235
1296
  }
1236
- var updateType = updates.updateType,
1237
- updatedObjects = updates.updatedObjects;
1238
- if (updateType === LocusInfoUpdateType.OBJECTS_UPDATED && (updatedObjects === null || updatedObjects === void 0 ? void 0 : updatedObjects.length) > 0) {
1297
+ var updateType = updates.updateType;
1298
+ if (updateType === LocusInfoUpdateType.OBJECTS_UPDATED && ((_updates$updatedObjec = updates.updatedObjects) === null || _updates$updatedObjec === void 0 ? void 0 : _updates$updatedObjec.length) > 0) {
1239
1299
  // Filter out updates for objects that already have a higher version in their datasets,
1240
1300
  // or removals for objects that still exist in any of their datasets
1241
- var filteredUpdates = updatedObjects.filter(function (object) {
1301
+ var filteredUpdates = updates.updatedObjects.filter(function (object) {
1242
1302
  var elementId = object.htMeta.elementId;
1243
1303
  var type = elementId.type,
1244
1304
  id = elementId.id,
1245
1305
  version = elementId.version;
1246
1306
 
1247
1307
  // Check all datasets
1248
- for (var _i2 = 0, _Object$keys3 = (0, _keys.default)(_this10.dataSets); _i2 < _Object$keys3.length; _i2++) {
1308
+ for (var _i2 = 0, _Object$keys3 = (0, _keys.default)(_this1.dataSets); _i2 < _Object$keys3.length; _i2++) {
1249
1309
  var dataSetName = _Object$keys3[_i2];
1250
- var dataSet = _this10.dataSets[dataSetName];
1310
+ var dataSet = _this1.dataSets[dataSetName];
1251
1311
 
1252
1312
  // only visible datasets have hash trees set
1253
1313
  if (dataSet !== null && dataSet !== void 0 && dataSet.hashTree) {
@@ -1256,12 +1316,12 @@ var HashTreeParser = /*#__PURE__*/function () {
1256
1316
  if (object.data) {
1257
1317
  // For updates: filter out if any dataset has a higher version
1258
1318
  if (existingVersion > version) {
1259
- _loggerProxy.default.logger.info("HashTreeParser#callLocusInfoUpdateCallback --> ".concat(_this10.debugId, " Filtering out update for ").concat(type, ":").concat(id, " v").concat(version, " because dataset \"").concat(dataSetName, "\" has v").concat(existingVersion));
1319
+ _loggerProxy.default.logger.info("HashTreeParser#callLocusInfoUpdateCallback --> ".concat(_this1.debugId, " Filtering out update for ").concat(type, ":").concat(id, " v").concat(version, " because dataset \"").concat(dataSetName, "\" has v").concat(existingVersion));
1260
1320
  return false;
1261
1321
  }
1262
1322
  } else if (existingVersion >= version) {
1263
1323
  // For removals: filter out if the object still exists in any dataset
1264
- _loggerProxy.default.logger.info("HashTreeParser#callLocusInfoUpdateCallback --> ".concat(_this10.debugId, " Filtering out removal for ").concat(type, ":").concat(id, " v").concat(version, " because dataset \"").concat(dataSetName, "\" still has v").concat(existingVersion));
1324
+ _loggerProxy.default.logger.info("HashTreeParser#callLocusInfoUpdateCallback --> ".concat(_this1.debugId, " Filtering out removal for ").concat(type, ":").concat(id, " v").concat(version, " because dataset \"").concat(dataSetName, "\" still has v").concat(existingVersion));
1265
1325
  return false;
1266
1326
  }
1267
1327
  }
@@ -1270,13 +1330,14 @@ var HashTreeParser = /*#__PURE__*/function () {
1270
1330
  return true;
1271
1331
  });
1272
1332
  if (filteredUpdates.length > 0) {
1273
- this.locusInfoUpdateCallback(updateType, {
1333
+ this.locusInfoUpdateCallback({
1334
+ updateType: updateType,
1274
1335
  updatedObjects: filteredUpdates
1275
1336
  });
1276
1337
  }
1277
1338
  } else if (updateType !== LocusInfoUpdateType.OBJECTS_UPDATED) {
1278
- this.locusInfoUpdateCallback(updateType, {
1279
- updatedObjects: updatedObjects
1339
+ this.locusInfoUpdateCallback({
1340
+ updateType: updateType
1280
1341
  });
1281
1342
  }
1282
1343
  }
@@ -1300,164 +1361,517 @@ var HashTreeParser = /*#__PURE__*/function () {
1300
1361
  * Performs a sync for the given data set.
1301
1362
  *
1302
1363
  * @param {InternalDataSet} dataSet - The data set to sync
1303
- * @param {string} rootHash - Our current root hash for this data set
1304
1364
  * @param {string} reason - The reason for the sync (used for logging)
1365
+ * @param {boolean} [isInitialization] - Whether this is an initialization sync (sends empty leaves data instead of comparing hashes)
1305
1366
  * @returns {Promise<void>}
1306
1367
  */
1307
1368
  }, {
1308
1369
  key: "performSync",
1309
1370
  value: (function () {
1310
- var _performSync = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee5(dataSet, rootHash, reason) {
1311
- var mismatchedLeavesData, receivedHashes, _yield$this$getHashes, hashes, latestDataSetInfo, mismatchedLeaveIndexes, syncResponse, _t3, _t4;
1312
- return _regenerator.default.wrap(function (_context6) {
1313
- while (1) switch (_context6.prev = _context6.next) {
1371
+ var _performSync = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee6(dataSet, reason, isInitialization) {
1372
+ var _dataSet$syncAbortCon;
1373
+ var abortController, hashTree, rootHash, leavesData, receivedHashes, hashesResult, mismatchedLeaveIndexes, syncResponse, _t3, _t4;
1374
+ return _regenerator.default.wrap(function (_context7) {
1375
+ while (1) switch (_context7.prev = _context7.next) {
1314
1376
  case 0:
1315
1377
  if (dataSet.hashTree) {
1316
- _context6.next = 1;
1378
+ _context7.next = 1;
1317
1379
  break;
1318
1380
  }
1319
- return _context6.abrupt("return");
1381
+ return _context7.abrupt("return");
1320
1382
  case 1:
1321
- _context6.prev = 1;
1383
+ abortController = (_dataSet$syncAbortCon = dataSet.syncAbortController) !== null && _dataSet$syncAbortCon !== void 0 ? _dataSet$syncAbortCon : new AbortController();
1384
+ dataSet.syncAbortController = abortController;
1385
+ hashTree = dataSet.hashTree;
1386
+ rootHash = hashTree.getRootHash();
1387
+ _context7.prev = 2;
1322
1388
  _loggerProxy.default.logger.info("HashTreeParser#performSync --> ".concat(this.debugId, " ").concat(reason, ", syncing data set \"").concat(dataSet.name, "\""));
1323
- mismatchedLeavesData = {};
1389
+ leavesData = {};
1390
+ if (isInitialization) {
1391
+ _context7.next = 10;
1392
+ break;
1393
+ }
1324
1394
  if (!(dataSet.leafCount !== 1)) {
1325
- _context6.next = 7;
1395
+ _context7.next = 9;
1326
1396
  break;
1327
1397
  }
1328
- _context6.prev = 2;
1329
- _context6.next = 3;
1398
+ _context7.prev = 3;
1399
+ _context7.next = 4;
1330
1400
  return this.getHashesFromLocus(dataSet.name, rootHash);
1331
- case 3:
1332
- _yield$this$getHashes = _context6.sent;
1333
- hashes = _yield$this$getHashes.hashes;
1334
- latestDataSetInfo = _yield$this$getHashes.dataSet;
1335
- receivedHashes = hashes;
1336
- dataSet.hashTree.resize(latestDataSetInfo.leafCount);
1337
- _context6.next = 6;
1338
- break;
1339
1401
  case 4:
1340
- _context6.prev = 4;
1341
- _t3 = _context6["catch"](2);
1342
- if (!(_t3.statusCode === 409)) {
1343
- _context6.next = 5;
1402
+ hashesResult = _context7.sent;
1403
+ if (hashesResult) {
1404
+ _context7.next = 5;
1405
+ break;
1406
+ }
1407
+ return _context7.abrupt("return");
1408
+ case 5:
1409
+ receivedHashes = hashesResult.hashes;
1410
+ this.updateDataSetLeafCount(dataSet, hashesResult.dataSet.leafCount);
1411
+ _context7.next = 8;
1412
+ break;
1413
+ case 6:
1414
+ _context7.prev = 6;
1415
+ _t3 = _context7["catch"](3);
1416
+ if (!((_t3 === null || _t3 === void 0 ? void 0 : _t3.statusCode) === 409)) {
1417
+ _context7.next = 7;
1344
1418
  break;
1345
1419
  }
1346
1420
  // this is a leaf count mismatch, we should do nothing, just wait for another heartbeat message from Locus
1347
1421
  _loggerProxy.default.logger.info("HashTreeParser#getHashesFromLocus --> ".concat(this.debugId, " Got 409 when fetching hashes for data set \"").concat(dataSet.name, "\": ").concat(_t3.message));
1348
- return _context6.abrupt("return");
1349
- case 5:
1422
+ return _context7.abrupt("return");
1423
+ case 7:
1350
1424
  throw _t3;
1351
- case 6:
1425
+ case 8:
1352
1426
  // identify mismatched leaves
1353
- mismatchedLeaveIndexes = dataSet.hashTree.diffHashes(receivedHashes);
1427
+ mismatchedLeaveIndexes = hashTree.diffHashes(receivedHashes);
1354
1428
  mismatchedLeaveIndexes.forEach(function (index) {
1355
- mismatchedLeavesData[index] = dataSet.hashTree.getLeafData(index);
1429
+ leavesData[index] = hashTree.getLeafData(index);
1356
1430
  });
1357
- _context6.next = 8;
1431
+ _context7.next = 10;
1358
1432
  break;
1359
- case 7:
1360
- mismatchedLeavesData[0] = dataSet.hashTree.getLeafData(0);
1361
- case 8:
1362
- if (!((0, _keys.default)(mismatchedLeavesData).length > 0)) {
1363
- _context6.next = 10;
1433
+ case 9:
1434
+ leavesData = {
1435
+ 0: hashTree.getLeafData(0)
1436
+ };
1437
+ case 10:
1438
+ if (!abortController.signal.aborted) {
1439
+ _context7.next = 11;
1364
1440
  break;
1365
1441
  }
1366
- _context6.next = 9;
1367
- return this.sendSyncRequestToLocus(dataSet, mismatchedLeavesData);
1368
- case 9:
1369
- syncResponse = _context6.sent;
1442
+ _loggerProxy.default.logger.info("HashTreeParser#performSync --> ".concat(this.debugId, " abandoning sync for \"").concat(dataSet.name, "\" before /sync - message received during sync"));
1443
+ return _context7.abrupt("return");
1444
+ case 11:
1445
+ // request sync for mismatched leaves
1446
+ syncResponse = null;
1447
+ if (!isInitialization) {
1448
+ _context7.next = 13;
1449
+ break;
1450
+ }
1451
+ _context7.next = 12;
1452
+ return this.sendSyncRequestToLocus(dataSet, {
1453
+ isInitialization: true
1454
+ });
1455
+ case 12:
1456
+ syncResponse = _context7.sent;
1457
+ _context7.next = 15;
1458
+ break;
1459
+ case 13:
1460
+ if (!((0, _keys.default)(leavesData).length > 0)) {
1461
+ _context7.next = 15;
1462
+ break;
1463
+ }
1464
+ _context7.next = 14;
1465
+ return this.sendSyncRequestToLocus(dataSet, {
1466
+ mismatchedLeavesData: leavesData
1467
+ });
1468
+ case 14:
1469
+ syncResponse = _context7.sent;
1470
+ case 15:
1370
1471
  // sync API may return nothing (in that case data will arrive via messages)
1371
1472
  // or it may return a response in the same format as messages
1473
+ // We still need to restart the sync timer as a safety net in case the messages don't arrive.
1474
+ this.runSyncAlgorithm(dataSet);
1372
1475
  if (syncResponse) {
1373
- this.handleMessage(syncResponse, 'via sync API');
1476
+ // clear the abort controller before processing the response so that
1477
+ // parseMessage() -> cancelPendingSyncsForDataSets() doesn't log a
1478
+ // misleading "aborting sync" message for this already-completed sync
1479
+ dataSet.syncAbortController = undefined;
1480
+
1481
+ // the format of sync response is the same as messages, so we can reuse the same handler
1482
+ this.handleMessage(syncResponse, "via sync API (".concat(isInitialization ? 'init' : "".concat((0, _keys.default)(leavesData).length, " mismatched leaves"), ")"));
1374
1483
  }
1375
- case 10:
1376
- _context6.next = 12;
1484
+ _context7.next = 17;
1377
1485
  break;
1378
- case 11:
1379
- _context6.prev = 11;
1380
- _t4 = _context6["catch"](1);
1381
- if (_t4 instanceof MeetingEndedError) {
1382
- this.callLocusInfoUpdateCallback({
1383
- updateType: LocusInfoUpdateType.MEETING_ENDED
1384
- });
1385
- } else {
1486
+ case 16:
1487
+ _context7.prev = 16;
1488
+ _t4 = _context7["catch"](2);
1489
+ if (!this.handleSyncErrors(_t4)) {
1386
1490
  _loggerProxy.default.logger.warn("HashTreeParser#performSync --> ".concat(this.debugId, " error during sync for data set \"").concat(dataSet.name, "\":"), _t4);
1387
1491
  }
1388
- case 12:
1492
+ case 17:
1493
+ _context7.prev = 17;
1494
+ dataSet.syncAbortController = undefined;
1495
+ return _context7.finish(17);
1496
+ case 18:
1389
1497
  case "end":
1390
- return _context6.stop();
1498
+ return _context7.stop();
1391
1499
  }
1392
- }, _callee5, this, [[1, 11], [2, 4]]);
1500
+ }, _callee6, this, [[2, 16, 17, 18], [3, 6]]);
1393
1501
  }));
1394
- function performSync(_x6, _x7, _x8) {
1502
+ function performSync(_x8, _x9, _x0) {
1395
1503
  return _performSync.apply(this, arguments);
1396
1504
  }
1397
1505
  return performSync;
1398
1506
  }()
1507
+ /**
1508
+ * Cancels any pending or in-flight syncs for the specified data sets.
1509
+ * This removes matching entries from the sync queue and aborts any in-flight sync HTTP requests.
1510
+ *
1511
+ * @param {string[]} dataSetNames - The names of the data sets to cancel syncs for
1512
+ * @returns {void}
1513
+ */
1514
+ )
1515
+ }, {
1516
+ key: "cancelPendingSyncsForDataSets",
1517
+ value: function cancelPendingSyncsForDataSets(dataSetNames) {
1518
+ var previousLength = this.syncQueue.length;
1519
+ this.syncQueue = this.syncQueue.filter(function (entry) {
1520
+ return !dataSetNames.includes(entry.dataSetName);
1521
+ });
1522
+ if (previousLength !== this.syncQueue.length) {
1523
+ _loggerProxy.default.logger.info("HashTreeParser#cancelPendingSyncsForDataSets --> ".concat(this.debugId, " removed ").concat(previousLength - this.syncQueue.length, " entries from sync queue for data sets: ").concat(dataSetNames.join(', ')));
1524
+ }
1525
+ this.markDataSetsForSyncAllBackoffSkip(dataSetNames);
1526
+ this.abortInFlightSyncs(dataSetNames);
1527
+ }
1528
+
1529
+ /**
1530
+ * If a syncAllDatasets backoff sleep is in progress, marks the given data sets to be skipped
1531
+ * after the sleep completes.
1532
+ *
1533
+ * @param {string[]} dataSetNames - The names of the data sets to mark
1534
+ * @returns {void}
1535
+ */
1536
+ }, {
1537
+ key: "markDataSetsForSyncAllBackoffSkip",
1538
+ value: function markDataSetsForSyncAllBackoffSkip(dataSetNames) {
1539
+ if (this.syncAllBackoffType !== SyncAllBackoffType.NONE) {
1540
+ var _iterator9 = _createForOfIteratorHelper(dataSetNames),
1541
+ _step9;
1542
+ try {
1543
+ for (_iterator9.s(); !(_step9 = _iterator9.n()).done;) {
1544
+ var name = _step9.value;
1545
+ this.dataSetsSyncedDuringBackoff.add(name);
1546
+ }
1547
+ } catch (err) {
1548
+ _iterator9.e(err);
1549
+ } finally {
1550
+ _iterator9.f();
1551
+ }
1552
+ }
1553
+ }
1554
+
1555
+ /**
1556
+ * Aborts any in-flight sync HTTP requests for the specified data sets.
1557
+ *
1558
+ * @param {string[]} dataSetNames - The names of the data sets whose syncs should be aborted
1559
+ * @returns {void}
1560
+ */
1561
+ }, {
1562
+ key: "abortInFlightSyncs",
1563
+ value: function abortInFlightSyncs(dataSetNames) {
1564
+ var _iterator0 = _createForOfIteratorHelper(dataSetNames),
1565
+ _step0;
1566
+ try {
1567
+ for (_iterator0.s(); !(_step0 = _iterator0.n()).done;) {
1568
+ var _this$dataSets$name;
1569
+ var name = _step0.value;
1570
+ if ((_this$dataSets$name = this.dataSets[name]) !== null && _this$dataSets$name !== void 0 && _this$dataSets$name.syncAbortController) {
1571
+ _loggerProxy.default.logger.info("HashTreeParser#cancelPendingSyncsForDataSets --> ".concat(this.debugId, " aborting in-flight sync for data set \"").concat(name, "\""));
1572
+ this.dataSets[name].syncAbortController.abort();
1573
+ }
1574
+ }
1575
+ } catch (err) {
1576
+ _iterator0.e(err);
1577
+ } finally {
1578
+ _iterator0.f();
1579
+ }
1580
+ }
1581
+
1582
+ /**
1583
+ * Enqueues a sync for the given data set. If the data set is already in the queue, the request is ignored.
1584
+ * This ensures that all syncs are executed sequentially and no more than 1 sync runs at a time.
1585
+ *
1586
+ * @param {string} dataSetName - The name of the data set to sync
1587
+ * @param {string} reason - The reason for the sync (used for logging)
1588
+ * @param {boolean} [isInitialization=false] - Whether this is an initialization sync (uses empty leaves data instead of hash comparison)
1589
+ * @returns {void}
1590
+ */
1591
+ }, {
1592
+ key: "enqueueSyncForDataset",
1593
+ value: function enqueueSyncForDataset(dataSetName, reason) {
1594
+ var isInitialization = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
1595
+ if (this.state === 'stopped') return;
1596
+ var existingEntry = this.syncQueue.find(function (entry) {
1597
+ return entry.dataSetName === dataSetName;
1598
+ });
1599
+ if (existingEntry) {
1600
+ if (isInitialization) {
1601
+ existingEntry.isInitialization = true;
1602
+ }
1603
+ _loggerProxy.default.logger.info("HashTreeParser#enqueueSyncForDataset --> ".concat(this.debugId, " data set \"").concat(dataSetName, "\" already in sync queue, skipping"));
1604
+ return;
1605
+ }
1606
+ this.syncQueue.push({
1607
+ dataSetName: dataSetName,
1608
+ reason: reason,
1609
+ isInitialization: isInitialization
1610
+ });
1611
+ if (!this.isSyncInProgress) {
1612
+ this.syncQueueProcessingPromise = this.processSyncQueue();
1613
+ }
1614
+ }
1615
+
1616
+ /**
1617
+ * Processes the sync queue sequentially. Only one instance of this method runs at a time.
1618
+ *
1619
+ * @returns {Promise<void>}
1620
+ */
1621
+ }, {
1622
+ key: "processSyncQueue",
1623
+ value: (function () {
1624
+ var _processSyncQueue = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee7() {
1625
+ var _ref8, dataSetName, reason, isInitialization, dataSet;
1626
+ return _regenerator.default.wrap(function (_context8) {
1627
+ while (1) switch (_context8.prev = _context8.next) {
1628
+ case 0:
1629
+ if (!this.isSyncInProgress) {
1630
+ _context8.next = 1;
1631
+ break;
1632
+ }
1633
+ return _context8.abrupt("return");
1634
+ case 1:
1635
+ this.isSyncInProgress = true;
1636
+ _context8.prev = 2;
1637
+ case 3:
1638
+ if (!(this.syncQueue.length > 0 && this.state !== 'stopped')) {
1639
+ _context8.next = 6;
1640
+ break;
1641
+ }
1642
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1643
+ _ref8 = this.syncQueue.shift(), dataSetName = _ref8.dataSetName, reason = _ref8.reason, isInitialization = _ref8.isInitialization;
1644
+ dataSet = this.dataSets[dataSetName];
1645
+ if (dataSet !== null && dataSet !== void 0 && dataSet.hashTree) {
1646
+ _context8.next = 4;
1647
+ break;
1648
+ }
1649
+ return _context8.abrupt("continue", 3);
1650
+ case 4:
1651
+ _context8.next = 5;
1652
+ return this.performSync(dataSet, reason, isInitialization);
1653
+ case 5:
1654
+ _context8.next = 3;
1655
+ break;
1656
+ case 6:
1657
+ _context8.prev = 6;
1658
+ this.isSyncInProgress = false;
1659
+ return _context8.finish(6);
1660
+ case 7:
1661
+ case "end":
1662
+ return _context8.stop();
1663
+ }
1664
+ }, _callee7, this, [[2,, 6, 7]]);
1665
+ }));
1666
+ function processSyncQueue() {
1667
+ return _processSyncQueue.apply(this, arguments);
1668
+ }
1669
+ return processSyncQueue;
1670
+ }()
1671
+ /**
1672
+ * sets the backoff type for syncAllDatasets calls, which determines the scope of datasets that will be synced after the backoff delay.
1673
+ *
1674
+ * @param {boolean} onlyLLM - Whether the backoff is for a syncAllDatasets call that is syncing only LLM datasets
1675
+ * @returns {void}
1676
+ */
1677
+ )
1678
+ }, {
1679
+ key: "setSyncAllBackoffType",
1680
+ value: function setSyncAllBackoffType(onlyLLM) {
1681
+ this.syncAllBackoffType = onlyLLM ? SyncAllBackoffType.ONLY_LLM : SyncAllBackoffType.ALL;
1682
+ }
1683
+
1684
+ /**
1685
+ * Checks if a syncAll backoff is already in progress. If so, upgrades the scope from
1686
+ * onlyLLM to all datasets when the new call has a broader scope.
1687
+ *
1688
+ * @param {boolean} onlyLLM - Whether the current call is for LLM datasets only
1689
+ * @returns {boolean} true if a backoff is already pending (caller should return early)
1690
+ */
1691
+ }, {
1692
+ key: "tryUpgradePendingBackoff",
1693
+ value: function tryUpgradePendingBackoff(onlyLLM) {
1694
+ if (this.syncAllBackoffType !== SyncAllBackoffType.NONE) {
1695
+ if (!onlyLLM && this.syncAllBackoffType === SyncAllBackoffType.ONLY_LLM) {
1696
+ this.setSyncAllBackoffType(false);
1697
+ _loggerProxy.default.logger.info("HashTreeParser#syncAllDatasets --> ".concat(this.debugId, " upgraded pending syncAll from onlyLLM to all datasets"));
1698
+ }
1699
+ return true;
1700
+ }
1701
+ return false;
1702
+ }
1703
+
1704
+ /**
1705
+ * Syncs all data sets that have hash trees, one by one in sequence, using the priority order
1706
+ * provided by sortByInitPriority().
1707
+ *
1708
+ * If a call is already waiting in the backoff delay phase, a new call with a broader scope
1709
+ * (onlyLLM=false) will upgrade the pending scope, and the dataset list will be computed after
1710
+ * the backoff using the upgraded scope. After the backoff, the sync queue handles deduplication
1711
+ * so no guard is needed.
1712
+ *
1713
+ * @param {Object} [options={}] - Options for syncing
1714
+ * @param {boolean} [options.onlyLLM=false] - Whether to sync only LLM based data sets
1715
+ * @returns {Promise<void>}
1716
+ */
1717
+ }, {
1718
+ key: "syncAllDatasets",
1719
+ value: (function () {
1720
+ var _syncAllDatasets = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee8() {
1721
+ var options,
1722
+ _options$onlyLLM,
1723
+ onlyLLM,
1724
+ dataSetsToSync,
1725
+ delay,
1726
+ effectiveBackoffType,
1727
+ skippedDataSets,
1728
+ effectiveDataSetsToSync,
1729
+ _iterator1,
1730
+ _step1,
1731
+ ds,
1732
+ _args9 = arguments;
1733
+ return _regenerator.default.wrap(function (_context9) {
1734
+ while (1) switch (_context9.prev = _context9.next) {
1735
+ case 0:
1736
+ options = _args9.length > 0 && _args9[0] !== undefined ? _args9[0] : {};
1737
+ _options$onlyLLM = options.onlyLLM, onlyLLM = _options$onlyLLM === void 0 ? false : _options$onlyLLM;
1738
+ if (!(this.state === 'stopped')) {
1739
+ _context9.next = 1;
1740
+ break;
1741
+ }
1742
+ return _context9.abrupt("return");
1743
+ case 1:
1744
+ if (!this.tryUpgradePendingBackoff(onlyLLM)) {
1745
+ _context9.next = 2;
1746
+ break;
1747
+ }
1748
+ return _context9.abrupt("return");
1749
+ case 2:
1750
+ dataSetsToSync = this.getSortedDataSetsWithHashTrees(onlyLLM);
1751
+ if (!(dataSetsToSync.length === 0)) {
1752
+ _context9.next = 3;
1753
+ break;
1754
+ }
1755
+ return _context9.abrupt("return");
1756
+ case 3:
1757
+ this.setSyncAllBackoffType(onlyLLM);
1758
+ delay = this.getWeightedBackoffTime(dataSetsToSync[0].backoff);
1759
+ _loggerProxy.default.logger.info("HashTreeParser#syncAllDatasets --> ".concat(this.debugId, " starting backoff delay of ").concat(delay, "ms (onlyLLM=").concat(onlyLLM, ")"));
1760
+
1761
+ // delay the start of the syncs - this is a Locus requirement to avoid thundering herd issues
1762
+ _context9.next = 4;
1763
+ return (0, _utils.sleep)(delay);
1764
+ case 4:
1765
+ // read the (possibly upgraded) scope and clear the backoff flag
1766
+ effectiveBackoffType = this.syncAllBackoffType;
1767
+ skippedDataSets = this.dataSetsSyncedDuringBackoff;
1768
+ this.syncAllBackoffType = SyncAllBackoffType.NONE;
1769
+ this.dataSetsSyncedDuringBackoff = new _set.default();
1770
+ if (!(this.state === 'stopped')) {
1771
+ _context9.next = 5;
1772
+ break;
1773
+ }
1774
+ return _context9.abrupt("return");
1775
+ case 5:
1776
+ // re-evaluate the dataset list after the sleep, since the scope may have been upgraded
1777
+ // and exclude datasets that received messages during the backoff sleep
1778
+ effectiveDataSetsToSync = this.getSortedDataSetsWithHashTrees(effectiveBackoffType === SyncAllBackoffType.ONLY_LLM).filter(function (ds) {
1779
+ return !skippedDataSets.has(ds.name);
1780
+ });
1781
+ if (skippedDataSets.size > 0) {
1782
+ _loggerProxy.default.logger.info("HashTreeParser#syncAllDatasets --> ".concat(this.debugId, " skipping datasets that received messages during backoff: ").concat((0, _toConsumableArray2.default)(skippedDataSets).join(', ')));
1783
+ }
1784
+ _loggerProxy.default.logger.info("HashTreeParser#syncAllDatasets --> ".concat(this.debugId, " syncing ").concat(effectiveBackoffType === SyncAllBackoffType.ONLY_LLM ? 'only LLM' : 'all', " datasets: ").concat(effectiveDataSetsToSync.map(function (ds) {
1785
+ return ds.name;
1786
+ }).join(', ')));
1787
+ _iterator1 = _createForOfIteratorHelper(effectiveDataSetsToSync);
1788
+ try {
1789
+ for (_iterator1.s(); !(_step1 = _iterator1.n()).done;) {
1790
+ ds = _step1.value;
1791
+ this.enqueueSyncForDataset(ds.name, 'syncAllDatasets');
1792
+ }
1793
+ } catch (err) {
1794
+ _iterator1.e(err);
1795
+ } finally {
1796
+ _iterator1.f();
1797
+ }
1798
+ _context9.next = 6;
1799
+ return this.syncQueueProcessingPromise;
1800
+ case 6:
1801
+ case "end":
1802
+ return _context9.stop();
1803
+ }
1804
+ }, _callee8, this);
1805
+ }));
1806
+ function syncAllDatasets() {
1807
+ return _syncAllDatasets.apply(this, arguments);
1808
+ }
1809
+ return syncAllDatasets;
1810
+ }()
1811
+ /**
1812
+ * Returns the list of data sets that have hash trees, sorted by the priority order provided by sortByInitPriority().
1813
+ *
1814
+ * @param {boolean} onlyLLM - Whether to include only LLM based data sets
1815
+ * @returns {Array<{name: string, backoff: {maxMs: number, exponent: number}}>} The sorted list of data sets with their backoff configurations
1816
+ */
1817
+ )
1818
+ }, {
1819
+ key: "getSortedDataSetsWithHashTrees",
1820
+ value: function getSortedDataSetsWithHashTrees(onlyLLM) {
1821
+ var dataSets = (0, _values.default)(this.dataSets).filter(function (dataSet) {
1822
+ return dataSet === null || dataSet === void 0 ? void 0 : dataSet.hashTree;
1823
+ }).map(function (dataSet) {
1824
+ return {
1825
+ name: dataSet.name,
1826
+ backoff: dataSet.backoff
1827
+ };
1828
+ });
1829
+ if (onlyLLM) {
1830
+ dataSets = dataSets.filter(function (ds) {
1831
+ return _constants3.LLM_DATASET_NAMES.includes(ds.name);
1832
+ });
1833
+ }
1834
+ return (0, _utils.sortByInitPriority)(dataSets, _constants3.DATA_SET_INIT_PRIORITY);
1835
+ }
1836
+
1399
1837
  /**
1400
1838
  * Runs the sync algorithm for the given data set.
1401
1839
  *
1402
1840
  * @param {DataSet} receivedDataSet - The data set to run the sync algorithm for.
1403
1841
  * @returns {void}
1404
1842
  */
1405
- )
1406
1843
  }, {
1407
1844
  key: "runSyncAlgorithm",
1408
1845
  value: function runSyncAlgorithm(receivedDataSet) {
1409
- var _this11 = this;
1846
+ var _this10 = this;
1410
1847
  var dataSet = this.dataSets[receivedDataSet.name];
1411
1848
  if (!dataSet) {
1412
1849
  _loggerProxy.default.logger.warn("HashTreeParser#runSyncAlgorithm --> ".concat(this.debugId, " No data set found for ").concat(receivedDataSet.name, ", skipping sync algorithm"));
1413
1850
  return;
1414
1851
  }
1415
1852
  if (!dataSet.hashTree) {
1416
- _loggerProxy.default.logger.info("HashTreeParser#runSyncAlgorithm --> ".concat(this.debugId, " Data set \"").concat(dataSet.name, "\" has no hash tree, skipping sync algorithm"));
1853
+ // no hash tree, so no need to do any syncing
1854
+ // we fall into this branch often, because Locus sends dataSets in messages that are not visible to us
1855
+
1417
1856
  return;
1418
1857
  }
1419
1858
  dataSet.hashTree.resize(receivedDataSet.leafCount);
1420
-
1421
- // temporary log for the workshop // todo: remove
1422
- var ourCurrentRootHash = dataSet.hashTree.getRootHash();
1423
- _loggerProxy.default.logger.info("HashTreeParser#runSyncAlgorithm --> ".concat(this.debugId, " dataSet=\"").concat(dataSet.name, "\" version=").concat(dataSet.version, " hashes before starting timer: ours=").concat(ourCurrentRootHash, " Locus=").concat(dataSet.root));
1424
1859
  var delay = dataSet.idleMs + this.getWeightedBackoffTime(dataSet.backoff);
1425
1860
  if (delay > 0) {
1426
1861
  if (dataSet.timer) {
1427
1862
  clearTimeout(dataSet.timer);
1428
1863
  }
1429
- _loggerProxy.default.logger.info("HashTreeParser#runSyncAlgorithm --> ".concat(this.debugId, " setting \"").concat(dataSet.name, "\" sync timer for ").concat(delay));
1430
- dataSet.timer = setTimeout(/*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee6() {
1431
- var rootHash;
1432
- return _regenerator.default.wrap(function (_context7) {
1433
- while (1) switch (_context7.prev = _context7.next) {
1434
- case 0:
1435
- dataSet.timer = undefined;
1436
- if (dataSet.hashTree) {
1437
- _context7.next = 1;
1438
- break;
1439
- }
1440
- _loggerProxy.default.logger.warn("HashTreeParser#runSyncAlgorithm --> ".concat(_this11.debugId, " Data set \"").concat(dataSet.name, "\" no longer has a hash tree, cannot run sync algorithm"));
1441
- return _context7.abrupt("return");
1442
- case 1:
1443
- rootHash = dataSet.hashTree.getRootHash();
1444
- if (!(dataSet.root !== rootHash)) {
1445
- _context7.next = 3;
1446
- break;
1447
- }
1448
- _context7.next = 2;
1449
- return _this11.performSync(dataSet, rootHash, "Root hash mismatch: received=".concat(dataSet.root, ", ours=").concat(rootHash));
1450
- case 2:
1451
- _context7.next = 4;
1452
- break;
1453
- case 3:
1454
- _loggerProxy.default.logger.info("HashTreeParser#runSyncAlgorithm --> ".concat(_this11.debugId, " \"").concat(dataSet.name, "\" root hash matching: ").concat(rootHash, ", version=").concat(dataSet.version));
1455
- case 4:
1456
- case "end":
1457
- return _context7.stop();
1458
- }
1459
- }, _callee6);
1460
- })), delay);
1864
+ dataSet.timer = setTimeout(function () {
1865
+ dataSet.timer = undefined;
1866
+ if (!dataSet.hashTree) {
1867
+ _loggerProxy.default.logger.warn("HashTreeParser#runSyncAlgorithm --> ".concat(_this10.debugId, " Data set \"").concat(dataSet.name, "\" no longer has a hash tree, cannot run sync algorithm"));
1868
+ return;
1869
+ }
1870
+ var rootHash = dataSet.hashTree.getRootHash();
1871
+ if (dataSet.root !== rootHash) {
1872
+ _this10.enqueueSyncForDataset(dataSet.name, "Root hash mismatch: received=".concat(dataSet.root, ", ours=").concat(rootHash));
1873
+ }
1874
+ }, delay);
1461
1875
  } else {
1462
1876
  _loggerProxy.default.logger.info("HashTreeParser#runSyncAlgorithm --> ".concat(this.debugId, " No delay for \"").concat(dataSet.name, "\" data set, skipping sync timer reset/setup"));
1463
1877
  }
@@ -1475,48 +1889,45 @@ var HashTreeParser = /*#__PURE__*/function () {
1475
1889
  }, {
1476
1890
  key: "resetHeartbeatWatchdogs",
1477
1891
  value: function resetHeartbeatWatchdogs(receivedDataSets) {
1478
- var _this12 = this;
1479
- if (!this.heartbeatIntervalMs) {
1480
- return;
1481
- }
1482
- var _iterator9 = _createForOfIteratorHelper(receivedDataSets),
1483
- _step9;
1892
+ var _this11 = this;
1893
+ var _iterator10 = _createForOfIteratorHelper(receivedDataSets),
1894
+ _step10;
1484
1895
  try {
1485
1896
  var _loop2 = function _loop2() {
1486
- var receivedDataSet = _step9.value;
1487
- var dataSet = _this12.dataSets[receivedDataSet.name];
1488
- if (!(dataSet !== null && dataSet !== void 0 && dataSet.hashTree)) {
1489
- // eslint-disable-next-line no-continue
1490
- return 1; // continue
1491
- }
1897
+ var _dataSet$heartbeatInt;
1898
+ var receivedDataSet = _step10.value;
1899
+ var dataSet = _this11.dataSets[receivedDataSet.name];
1492
1900
  if (dataSet.heartbeatWatchdogTimer) {
1493
1901
  clearTimeout(dataSet.heartbeatWatchdogTimer);
1494
1902
  dataSet.heartbeatWatchdogTimer = undefined;
1495
1903
  }
1496
- var backoffTime = _this12.getWeightedBackoffTime(dataSet.backoff);
1497
- var delay = _this12.heartbeatIntervalMs + backoffTime;
1498
- dataSet.heartbeatWatchdogTimer = setTimeout(/*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee7() {
1499
- return _regenerator.default.wrap(function (_context8) {
1500
- while (1) switch (_context8.prev = _context8.next) {
1501
- case 0:
1502
- dataSet.heartbeatWatchdogTimer = undefined;
1503
- _loggerProxy.default.logger.warn("HashTreeParser#resetHeartbeatWatchdogs --> ".concat(_this12.debugId, " Heartbeat watchdog fired for data set \"").concat(dataSet.name, "\" - no heartbeat received within expected interval, initiating sync"));
1504
- _context8.next = 1;
1505
- return _this12.performSync(dataSet, dataSet.hashTree.getRootHash(), "heartbeat watchdog expired");
1506
- case 1:
1507
- case "end":
1508
- return _context8.stop();
1509
- }
1510
- }, _callee7);
1511
- })), delay);
1904
+
1905
+ // dataset-level heartbeatIntervalMs takes priority; fall back to top-level common value
1906
+ var heartbeatIntervalMs = (_dataSet$heartbeatInt = dataSet === null || dataSet === void 0 ? void 0 : dataSet.heartbeatIntervalMs) !== null && _dataSet$heartbeatInt !== void 0 ? _dataSet$heartbeatInt : _this11.topLevelHeartbeatIntervalMs;
1907
+ if (!(dataSet !== null && dataSet !== void 0 && dataSet.hashTree) || !heartbeatIntervalMs) {
1908
+ // eslint-disable-next-line no-continue
1909
+ return 1; // continue
1910
+ }
1911
+ var backoffTime = _this11.getWeightedBackoffTime(dataSet.backoff);
1912
+ var delay = heartbeatIntervalMs + backoffTime;
1913
+ dataSet.heartbeatWatchdogTimer = setTimeout(function () {
1914
+ dataSet.heartbeatWatchdogTimer = undefined;
1915
+ _loggerProxy.default.logger.warn("HashTreeParser#resetHeartbeatWatchdogs --> ".concat(_this11.debugId, " Heartbeat watchdog fired for data set \"").concat(dataSet.name, "\" - no heartbeat received within expected interval, initiating sync"));
1916
+ _metrics.default.sendBehavioralMetric(_constants.default.HASH_TREE_HEARTBEAT_WATCHDOG_EXPIRED, {
1917
+ debugId: _this11.debugId,
1918
+ dataSetName: dataSet.name
1919
+ });
1920
+ _this11.enqueueSyncForDataset(dataSet.name, "heartbeat watchdog expired");
1921
+ _this11.resetHeartbeatWatchdogs([dataSet]);
1922
+ }, delay);
1512
1923
  };
1513
- for (_iterator9.s(); !(_step9 = _iterator9.n()).done;) {
1924
+ for (_iterator10.s(); !(_step10 = _iterator10.n()).done;) {
1514
1925
  if (_loop2()) continue;
1515
1926
  }
1516
1927
  } catch (err) {
1517
- _iterator9.e(err);
1928
+ _iterator10.e(err);
1518
1929
  } finally {
1519
- _iterator9.f();
1930
+ _iterator10.f();
1520
1931
  }
1521
1932
  }
1522
1933
 
@@ -1550,7 +1961,14 @@ var HashTreeParser = /*#__PURE__*/function () {
1550
1961
  value: function stop() {
1551
1962
  _loggerProxy.default.logger.info("HashTreeParser#stop --> ".concat(this.debugId, " Stopping HashTreeParser, clearing timers and hash trees"));
1552
1963
  this.stopAllTimers();
1964
+ this.syncQueue = [];
1965
+ this.topLevelHeartbeatIntervalMs = undefined;
1966
+ this.syncAllBackoffType = SyncAllBackoffType.NONE;
1967
+ this.dataSetsSyncedDuringBackoff = new _set.default();
1553
1968
  (0, _values.default)(this.dataSets).forEach(function (dataSet) {
1969
+ var _dataSet$syncAbortCon2;
1970
+ (_dataSet$syncAbortCon2 = dataSet.syncAbortController) === null || _dataSet$syncAbortCon2 === void 0 ? void 0 : _dataSet$syncAbortCon2.abort();
1971
+ dataSet.syncAbortController = undefined;
1554
1972
  dataSet.hashTree = undefined;
1555
1973
  });
1556
1974
  this.visibleDataSets = [];
@@ -1558,29 +1976,41 @@ var HashTreeParser = /*#__PURE__*/function () {
1558
1976
  }
1559
1977
 
1560
1978
  /**
1561
- * Resumes the HashTreeParser that was previously stopped.
1979
+ * Cleans up the HashTreeParser, stopping all timers and clearing all internal state.
1980
+ * After calling this, the parser should not be used anymore.
1981
+ * @returns {void}
1982
+ */
1983
+ }, {
1984
+ key: "cleanUp",
1985
+ value: function cleanUp() {
1986
+ this.stop();
1987
+ this.dataSets = {};
1988
+ }
1989
+
1990
+ /**
1991
+ * Resumes the HashTreeParser that was previously stopped, using a hash tree message.
1562
1992
  * @param {HashTreeMessage} message - The message to resume with, it must contain metadata with visible data sets info
1563
1993
  * @returns {void}
1564
1994
  */
1565
1995
  }, {
1566
- key: "resume",
1567
- value: function resume(message) {
1996
+ key: "resumeFromMessage",
1997
+ value: function resumeFromMessage(message) {
1568
1998
  var _message$locusStateEl3, _metadataObject$data;
1569
1999
  // check that message contains metadata with visible data sets - this is essential to be able to resume
1570
2000
  var metadataObject = (_message$locusStateEl3 = message.locusStateElements) === null || _message$locusStateEl3 === void 0 ? void 0 : _message$locusStateEl3.find(function (el) {
1571
2001
  return (0, _utils.isMetadata)(el);
1572
2002
  });
1573
2003
  if (!(metadataObject !== null && metadataObject !== void 0 && (_metadataObject$data = metadataObject.data) !== null && _metadataObject$data !== void 0 && _metadataObject$data.visibleDataSets)) {
1574
- _loggerProxy.default.logger.warn("HashTreeParser#resume --> ".concat(this.debugId, " Cannot resume HashTreeParser because the message is missing metadata with visible data sets info"));
2004
+ _loggerProxy.default.logger.warn("HashTreeParser#resumeFromMessage --> ".concat(this.debugId, " Cannot resume HashTreeParser because the message is missing metadata with visible data sets info"));
1575
2005
  return;
1576
2006
  }
1577
2007
  this.setVisibleDataSets(metadataObject.data.visibleDataSets, message.dataSets);
1578
2008
  this.dataSets = {};
1579
- var _iterator0 = _createForOfIteratorHelper(message.dataSets),
1580
- _step0;
2009
+ var _iterator11 = _createForOfIteratorHelper(message.dataSets),
2010
+ _step11;
1581
2011
  try {
1582
- for (_iterator0.s(); !(_step0 = _iterator0.n()).done;) {
1583
- var dataSet = _step0.value;
2012
+ for (_iterator11.s(); !(_step11 = _iterator11.n()).done;) {
2013
+ var dataSet = _step11.value;
1584
2014
  var name = dataSet.name,
1585
2015
  leafCount = dataSet.leafCount;
1586
2016
  this.dataSets[name] = _objectSpread(_objectSpread({}, dataSet), {}, {
@@ -1588,23 +2018,62 @@ var HashTreeParser = /*#__PURE__*/function () {
1588
2018
  });
1589
2019
  }
1590
2020
  } catch (err) {
1591
- _iterator0.e(err);
2021
+ _iterator11.e(err);
1592
2022
  } finally {
1593
- _iterator0.f();
2023
+ _iterator11.f();
1594
2024
  }
1595
- _loggerProxy.default.logger.info("HashTreeParser#resume --> ".concat(this.debugId, " Resuming HashTreeParser with data sets: ").concat((0, _keys.default)(this.dataSets).join(', '), ", visible data sets: ").concat(this.visibleDataSets.map(function (ds) {
2025
+ _loggerProxy.default.logger.info("HashTreeParser#resumeFromMessage --> ".concat(this.debugId, " Resuming HashTreeParser with data sets: ").concat((0, _keys.default)(this.dataSets).join(', '), ", visible data sets: ").concat(this.visibleDataSets.map(function (ds) {
1596
2026
  return ds.name;
1597
2027
  }).join(', ')));
1598
2028
  this.state = 'active';
1599
2029
  this.handleMessage(message, 'on resume');
1600
2030
  }
2031
+
2032
+ /**
2033
+ * Resumes the HashTreeParser that was previously stopped, using a Locus API response.
2034
+ * Unlike resumeFromMessage(), this does not require metadata/dataSets in the input,
2035
+ * as it fetches all necessary information from Locus via initializeFromGetLociResponse.
2036
+ * @param {LocusDTO} locus - locus object from an API response
2037
+ * @returns {Promise}
2038
+ */
2039
+ }, {
2040
+ key: "resumeFromApiResponse",
2041
+ value: (function () {
2042
+ var _resumeFromApiResponse = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee9(locus) {
2043
+ return _regenerator.default.wrap(function (_context0) {
2044
+ while (1) switch (_context0.prev = _context0.next) {
2045
+ case 0:
2046
+ this.state = 'active';
2047
+ this.dataSets = {};
2048
+ _loggerProxy.default.logger.info("HashTreeParser#resumeFromApiResponse --> ".concat(this.debugId, " Resuming HashTreeParser from API response"));
2049
+ _context0.next = 1;
2050
+ return this.initializeFromGetLociResponse(locus);
2051
+ case 1:
2052
+ case "end":
2053
+ return _context0.stop();
2054
+ }
2055
+ }, _callee9, this);
2056
+ }));
2057
+ function resumeFromApiResponse(_x1) {
2058
+ return _resumeFromApiResponse.apply(this, arguments);
2059
+ }
2060
+ return resumeFromApiResponse;
2061
+ }())
1601
2062
  }, {
1602
2063
  key: "checkForSentinelHttpResponse",
1603
2064
  value: function checkForSentinelHttpResponse(error, dataSetName) {
1604
2065
  var _error$body;
2066
+ // 404 for any dataset means the locus is no longer available at this URL - could be replaced or ended
2067
+ // if a dataset is just not visible, we would get a 400
2068
+ if (error.statusCode === 404) {
2069
+ _loggerProxy.default.logger.info("HashTreeParser#checkForSentinelHttpResponse --> ".concat(this.debugId, " Received 404 for data set \"").concat(dataSetName, "\", locus not found"));
2070
+ this.stopAllTimers();
2071
+ throw new LocusNotFoundError();
2072
+ }
1605
2073
  var isValidDataSetForSentinel = dataSetName === undefined || PossibleSentinelMessageDataSetNames.includes(dataSetName.toLowerCase());
1606
- if ((error.statusCode === 409 && ((_error$body = error.body) === null || _error$body === void 0 ? void 0 : _error$body.errorCode) === 2403004 || error.statusCode === 404) && isValidDataSetForSentinel) {
1607
- _loggerProxy.default.logger.info("HashTreeParser#checkForSentinelHttpResponse --> ".concat(this.debugId, " Received ").concat(error.statusCode, " for data set \"").concat(dataSetName, "\", indicating that the meeting has ended"));
2074
+ if (error.statusCode === 409 && ((_error$body = error.body) === null || _error$body === void 0 ? void 0 : _error$body.errorCode) === _types2.LocusErrorCodes.LOCUS_INACTIVE && isValidDataSetForSentinel) {
2075
+ var _error$body2;
2076
+ _loggerProxy.default.logger.info("HashTreeParser#checkForSentinelHttpResponse --> ".concat(this.debugId, " Received ").concat(error.statusCode, "/").concat((_error$body2 = error.body) === null || _error$body2 === void 0 ? void 0 : _error$body2.errorCode, " for data set \"").concat(dataSetName, "\", indicating that the meeting has ended"));
1608
2077
  this.stopAllTimers();
1609
2078
  throw new MeetingEndedError();
1610
2079
  }
@@ -1614,37 +2083,49 @@ var HashTreeParser = /*#__PURE__*/function () {
1614
2083
  * Gets the current hashes from the locus for a specific data set.
1615
2084
  * @param {string} dataSetName
1616
2085
  * @param {string} currentRootHash
1617
- * @returns {string[]}
2086
+ * @returns {Object|null} An object containing the hashes and leaf count, or null if the hashes match and no sync is needed
1618
2087
  */
1619
2088
  }, {
1620
2089
  key: "getHashesFromLocus",
1621
2090
  value: function getHashesFromLocus(dataSetName, currentRootHash) {
1622
- var _this13 = this;
2091
+ var _this12 = this;
1623
2092
  _loggerProxy.default.logger.info("HashTreeParser#getHashesFromLocus --> ".concat(this.debugId, " Requesting hashes for data set \"").concat(dataSetName, "\""));
1624
2093
  var dataSet = this.dataSets[dataSetName];
1625
2094
  var url = "".concat(dataSet.url, "/hashtree");
1626
2095
  return this.webexRequest({
1627
- method: _constants.HTTP_VERBS.GET,
2096
+ method: _constants2.HTTP_VERBS.GET,
1628
2097
  uri: url,
1629
2098
  qs: {
1630
2099
  rootHash: currentRootHash
1631
2100
  }
1632
2101
  }).then(function (response) {
1633
2102
  var _response$body, _response$body2;
2103
+ if (!response.body || (0, _lodash.isEmpty)(response.body)) {
2104
+ // 204 with empty body means our hashes match Locus, no sync needed
2105
+ _loggerProxy.default.logger.info("HashTreeParser#getHashesFromLocus --> ".concat(_this12.debugId, " Got ").concat(response.statusCode, " with empty body for data set \"").concat(dataSetName, "\", hashes match - no sync needed"));
2106
+ return null;
2107
+ }
1634
2108
  var hashes = (_response$body = response.body) === null || _response$body === void 0 ? void 0 : _response$body.hashes;
1635
2109
  var dataSetFromResponse = (_response$body2 = response.body) === null || _response$body2 === void 0 ? void 0 : _response$body2.dataSet;
1636
2110
  if (!hashes || !(0, _isArray.default)(hashes)) {
1637
- _loggerProxy.default.logger.warn("HashTreeParser#getHashesFromLocus --> ".concat(_this13.debugId, " Locus returned invalid hashes, response body="), response.body);
2111
+ _loggerProxy.default.logger.warn("HashTreeParser#getHashesFromLocus --> ".concat(_this12.debugId, " Locus returned invalid hashes, response body="), response.body);
1638
2112
  throw new Error("Locus returned invalid hashes: ".concat(hashes));
1639
2113
  }
1640
- _loggerProxy.default.logger.info("HashTreeParser#getHashesFromLocus --> ".concat(_this13.debugId, " Received hashes for data set \"").concat(dataSetName, "\": ").concat((0, _stringify.default)(hashes)));
2114
+ _loggerProxy.default.logger.info("HashTreeParser#getHashesFromLocus --> ".concat(_this12.debugId, " Received hashes for data set \"").concat(dataSetName, "\": ").concat((0, _stringify.default)(hashes)));
1641
2115
  return {
1642
2116
  hashes: hashes,
1643
2117
  dataSet: dataSetFromResponse
1644
2118
  };
1645
2119
  }).catch(function (error) {
1646
- _loggerProxy.default.logger.error("HashTreeParser#getHashesFromLocus --> ".concat(_this13.debugId, " Error ").concat(error.statusCode, " fetching hashes for data set \"").concat(dataSetName, "\":"), error);
1647
- _this13.checkForSentinelHttpResponse(error, dataSet.name);
2120
+ _loggerProxy.default.logger.error("HashTreeParser#getHashesFromLocus --> ".concat(_this12.debugId, " Error ").concat(error.statusCode, " fetching hashes for data set \"").concat(dataSetName, "\":"), error);
2121
+ _this12.checkForSentinelHttpResponse(error, dataSet.name);
2122
+ _metrics.default.sendBehavioralMetric(_constants.default.HASH_TREE_SYNC_FAILURE, {
2123
+ debugId: _this12.debugId,
2124
+ dataSetName: dataSetName,
2125
+ request: 'GET /hashtree',
2126
+ statusCode: error.statusCode,
2127
+ reason: error.message
2128
+ });
1648
2129
  throw error;
1649
2130
  });
1650
2131
  }
@@ -1653,43 +2134,60 @@ var HashTreeParser = /*#__PURE__*/function () {
1653
2134
  * Sends a sync request to Locus for the specified data set.
1654
2135
  *
1655
2136
  * @param {InternalDataSet} dataSet The data set to sync.
1656
- * @param {Record<number, LeafDataItem[]>} mismatchedLeavesData The mismatched leaves data to include in the sync request.
2137
+ * @param {Object} options Either `{ isInitialization: true }` for init syncs (uses leafCount=1 with empty leaf data) or `{ mismatchedLeavesData }` for normal syncs.
1657
2138
  * @returns {Promise<HashTreeMessage|null>}
1658
2139
  */
1659
2140
  }, {
1660
2141
  key: "sendSyncRequestToLocus",
1661
- value: function sendSyncRequestToLocus(dataSet, mismatchedLeavesData) {
1662
- var _this14 = this;
2142
+ value: function sendSyncRequestToLocus(dataSet, options) {
2143
+ var _this13 = this;
1663
2144
  _loggerProxy.default.logger.info("HashTreeParser#sendSyncRequestToLocus --> ".concat(this.debugId, " Sending sync request for data set \"").concat(dataSet.name, "\""));
2145
+ var isInitialization = 'isInitialization' in options;
1664
2146
  var url = "".concat(dataSet.url, "/sync");
1665
2147
  var body = {
1666
- leafCount: dataSet.leafCount,
2148
+ leafCount: isInitialization ? 1 : dataSet.leafCount,
1667
2149
  leafDataEntries: []
1668
2150
  };
1669
- (0, _keys.default)(mismatchedLeavesData).forEach(function (index) {
2151
+ if (isInitialization) {
2152
+ // initialization sync: Locus requires leafCount=1 with a single empty leaf
1670
2153
  body.leafDataEntries.push({
1671
- leafIndex: (0, _parseInt2.default)(index, 10),
1672
- elementIds: mismatchedLeavesData[index]
2154
+ leafIndex: 0,
2155
+ elementIds: []
1673
2156
  });
1674
- });
1675
- var ourCurrentRootHash = dataSet.hashTree ? dataSet.hashTree.getRootHash() : _constants2.EMPTY_HASH;
2157
+ } else {
2158
+ var mismatchedLeavesData = options.mismatchedLeavesData;
2159
+ (0, _keys.default)(mismatchedLeavesData).forEach(function (index) {
2160
+ var leafIndex = (0, _parseInt2.default)(index, 10);
2161
+ body.leafDataEntries.push({
2162
+ leafIndex: leafIndex,
2163
+ elementIds: mismatchedLeavesData[leafIndex]
2164
+ });
2165
+ });
2166
+ }
2167
+ var ourCurrentRootHash = dataSet.hashTree ? dataSet.hashTree.getRootHash() : _constants3.EMPTY_HASH;
1676
2168
  return this.webexRequest({
1677
- method: _constants.HTTP_VERBS.POST,
2169
+ method: _constants2.HTTP_VERBS.POST,
1678
2170
  uri: url,
1679
2171
  qs: {
1680
2172
  rootHash: ourCurrentRootHash
1681
2173
  },
1682
2174
  body: body
1683
2175
  }).then(function (resp) {
1684
- _loggerProxy.default.logger.info("HashTreeParser#sendSyncRequestToLocus --> ".concat(_this14.debugId, " Sync request succeeded for \"").concat(dataSet.name, "\""));
1685
2176
  if (!resp.body || (0, _lodash.isEmpty)(resp.body)) {
1686
- _loggerProxy.default.logger.info("HashTreeParser#sendSyncRequestToLocus --> ".concat(_this14.debugId, " Got ").concat(resp.statusCode, " with empty body for sync request for data set \"").concat(dataSet.name, "\", data should arrive via messages"));
2177
+ _loggerProxy.default.logger.info("HashTreeParser#sendSyncRequestToLocus --> ".concat(_this13.debugId, " Got ").concat(resp.statusCode, " with empty body for sync request for data set \"").concat(dataSet.name, "\", data should arrive via messages"));
1687
2178
  return null;
1688
2179
  }
1689
2180
  return resp.body;
1690
2181
  }).catch(function (error) {
1691
- _loggerProxy.default.logger.error("HashTreeParser#sendSyncRequestToLocus --> ".concat(_this14.debugId, " Error ").concat(error.statusCode, " sending sync request for data set \"").concat(dataSet.name, "\":"), error);
1692
- _this14.checkForSentinelHttpResponse(error, dataSet.name);
2182
+ _loggerProxy.default.logger.error("HashTreeParser#sendSyncRequestToLocus --> ".concat(_this13.debugId, " Error ").concat(error.statusCode, " sending sync request for data set \"").concat(dataSet.name, "\":"), error);
2183
+ _this13.checkForSentinelHttpResponse(error, dataSet.name);
2184
+ _metrics.default.sendBehavioralMetric(_constants.default.HASH_TREE_SYNC_FAILURE, {
2185
+ debugId: _this13.debugId,
2186
+ dataSetName: dataSet.name,
2187
+ request: 'POST /sync',
2188
+ statusCode: error.statusCode,
2189
+ reason: error.message
2190
+ });
1693
2191
  throw error;
1694
2192
  });
1695
2193
  }