@webex/plugin-meetings 3.12.0-next.4 → 3.12.0-next.40

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 (90) 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 +6 -2
  5. package/dist/breakouts/breakout.js.map +1 -1
  6. package/dist/breakouts/index.js +1 -1
  7. package/dist/constants.js +1 -1
  8. package/dist/constants.js.map +1 -1
  9. package/dist/controls-options-manager/constants.js +11 -1
  10. package/dist/controls-options-manager/constants.js.map +1 -1
  11. package/dist/controls-options-manager/index.js +23 -21
  12. package/dist/controls-options-manager/index.js.map +1 -1
  13. package/dist/controls-options-manager/util.js +91 -0
  14. package/dist/controls-options-manager/util.js.map +1 -1
  15. package/dist/hashTree/constants.js +10 -1
  16. package/dist/hashTree/constants.js.map +1 -1
  17. package/dist/hashTree/hashTreeParser.js +554 -350
  18. package/dist/hashTree/hashTreeParser.js.map +1 -1
  19. package/dist/hashTree/utils.js +22 -0
  20. package/dist/hashTree/utils.js.map +1 -1
  21. package/dist/interceptors/locusRetry.js +23 -8
  22. package/dist/interceptors/locusRetry.js.map +1 -1
  23. package/dist/interpretation/index.js +1 -1
  24. package/dist/interpretation/siLanguage.js +1 -1
  25. package/dist/locus-info/index.js +274 -85
  26. package/dist/locus-info/index.js.map +1 -1
  27. package/dist/locus-info/types.js +16 -0
  28. package/dist/locus-info/types.js.map +1 -1
  29. package/dist/meeting/index.js +710 -499
  30. package/dist/meeting/index.js.map +1 -1
  31. package/dist/meeting/util.js +1 -0
  32. package/dist/meeting/util.js.map +1 -1
  33. package/dist/meetings/index.js +174 -77
  34. package/dist/meetings/index.js.map +1 -1
  35. package/dist/meetings/util.js +49 -5
  36. package/dist/meetings/util.js.map +1 -1
  37. package/dist/member/index.js +10 -0
  38. package/dist/member/index.js.map +1 -1
  39. package/dist/member/types.js.map +1 -1
  40. package/dist/member/util.js +3 -0
  41. package/dist/member/util.js.map +1 -1
  42. package/dist/types/controls-options-manager/constants.d.ts +6 -1
  43. package/dist/types/hashTree/constants.d.ts +1 -0
  44. package/dist/types/hashTree/hashTreeParser.d.ts +53 -15
  45. package/dist/types/hashTree/utils.d.ts +11 -0
  46. package/dist/types/interceptors/locusRetry.d.ts +4 -4
  47. package/dist/types/locus-info/index.d.ts +46 -6
  48. package/dist/types/locus-info/types.d.ts +17 -1
  49. package/dist/types/meeting/index.d.ts +64 -1
  50. package/dist/types/member/index.d.ts +1 -0
  51. package/dist/types/member/types.d.ts +1 -0
  52. package/dist/types/member/util.d.ts +1 -0
  53. package/dist/webinar/index.js +301 -226
  54. package/dist/webinar/index.js.map +1 -1
  55. package/package.json +22 -22
  56. package/src/aiEnableRequest/index.ts +16 -0
  57. package/src/breakouts/breakout.ts +2 -1
  58. package/src/constants.ts +1 -1
  59. package/src/controls-options-manager/constants.ts +14 -1
  60. package/src/controls-options-manager/index.ts +26 -19
  61. package/src/controls-options-manager/util.ts +81 -1
  62. package/src/hashTree/constants.ts +9 -0
  63. package/src/hashTree/hashTreeParser.ts +278 -160
  64. package/src/hashTree/utils.ts +17 -0
  65. package/src/interceptors/locusRetry.ts +25 -4
  66. package/src/locus-info/index.ts +274 -93
  67. package/src/locus-info/types.ts +19 -1
  68. package/src/meeting/index.ts +206 -22
  69. package/src/meeting/util.ts +1 -0
  70. package/src/meetings/index.ts +77 -43
  71. package/src/meetings/util.ts +56 -1
  72. package/src/member/index.ts +10 -0
  73. package/src/member/types.ts +1 -0
  74. package/src/member/util.ts +3 -0
  75. package/src/webinar/index.ts +75 -1
  76. package/test/unit/spec/aiEnableRequest/index.ts +86 -0
  77. package/test/unit/spec/breakouts/breakout.ts +7 -3
  78. package/test/unit/spec/controls-options-manager/index.js +114 -6
  79. package/test/unit/spec/controls-options-manager/util.js +165 -0
  80. package/test/unit/spec/hashTree/hashTreeParser.ts +996 -51
  81. package/test/unit/spec/hashTree/utils.ts +88 -1
  82. package/test/unit/spec/interceptors/locusRetry.ts +205 -4
  83. package/test/unit/spec/locus-info/index.js +397 -81
  84. package/test/unit/spec/meeting/index.js +271 -44
  85. package/test/unit/spec/meeting/utils.js +4 -0
  86. package/test/unit/spec/meetings/index.js +195 -13
  87. package/test/unit/spec/meetings/utils.js +137 -0
  88. package/test/unit/spec/member/index.js +7 -0
  89. package/test/unit/spec/member/util.js +24 -0
  90. package/test/unit/spec/webinar/index.ts +60 -0
@@ -17,15 +17,15 @@ _Object$defineProperty(exports, "__esModule", {
17
17
  });
18
18
  exports.default = exports.MeetingEndedError = 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"));
21
20
  var _promise = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/promise"));
21
+ var _stringify = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/json/stringify"));
22
22
  var _isArray = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/array/is-array"));
23
23
  var _keys = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/object/keys"));
24
24
  var _values = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/object/values"));
25
25
  var _parseInt2 = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/parse-int"));
26
+ var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/toConsumableArray"));
26
27
  var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/slicedToArray"));
27
28
  var _typeof2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/typeof"));
28
- var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/toConsumableArray"));
29
29
  var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/asyncToGenerator"));
30
30
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/defineProperty"));
31
31
  var _createClass2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/createClass"));
@@ -39,6 +39,7 @@ var _hashTree = _interopRequireDefault(require("./hashTree"));
39
39
  var _loggerProxy = _interopRequireDefault(require("../common/logs/logger-proxy"));
40
40
  var _constants = require("../constants");
41
41
  var _constants2 = require("./constants");
42
+ var _types = require("./types");
42
43
  var _utils = require("./utils");
43
44
  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; }
44
45
  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; }
@@ -91,6 +92,10 @@ var HashTreeParser = /*#__PURE__*/function () {
91
92
  (0, _defineProperty2.default)(this, "heartbeatIntervalMs", void 0);
92
93
  (0, _defineProperty2.default)(this, "excludedDataSets", void 0);
93
94
  (0, _defineProperty2.default)(this, "state", void 0);
95
+ (0, _defineProperty2.default)(this, "syncQueue", []);
96
+ (0, _defineProperty2.default)(this, "isSyncInProgress", false);
97
+ (0, _defineProperty2.default)(this, "isSyncAllInProgress", false);
98
+ (0, _defineProperty2.default)(this, "syncQueueProcessingPromise", _promise.default.resolve());
94
99
  var _options$initialLocus = options.initialLocus,
95
100
  dataSets = _options$initialLocus.dataSets,
96
101
  locus = _options$initialLocus.locus; // extract dataSets from initialLocus
@@ -211,69 +216,54 @@ var HashTreeParser = /*#__PURE__*/function () {
211
216
  */
212
217
  }, {
213
218
  key: "initializeNewVisibleDataSet",
214
- value: function initializeNewVisibleDataSet(visibleDataSetInfo, dataSetInfo) {
215
- if (this.isVisibleDataSet(dataSetInfo.name)) {
216
- _loggerProxy.default.logger.info("HashTreeParser#initializeNewVisibleDataSet --> ".concat(this.debugId, " Data set \"").concat(dataSetInfo.name, "\" already exists, skipping init"));
217
- return _promise.default.resolve({
218
- updateType: LocusInfoUpdateType.OBJECTS_UPDATED,
219
- updatedObjects: []
220
- });
221
- }
222
- _loggerProxy.default.logger.info("HashTreeParser#initializeNewVisibleDataSet --> ".concat(this.debugId, " Adding visible data set \"").concat(dataSetInfo.name, "\""));
223
- if (!this.addToVisibleDataSetsList(visibleDataSetInfo)) {
224
- return _promise.default.resolve({
225
- updateType: LocusInfoUpdateType.OBJECTS_UPDATED,
226
- updatedObjects: []
227
- });
228
- }
229
- var hashTree = new _hashTree.default([], dataSetInfo.leafCount);
230
- this.dataSets[dataSetInfo.name] = _objectSpread(_objectSpread({}, dataSetInfo), {}, {
231
- hashTree: hashTree
232
- });
233
- return this.sendInitializationSyncRequestToLocus(dataSetInfo.name, 'new visible data set');
234
- }
235
-
236
- /**
237
- * Sends a special sync request to Locus with all leaves empty - this is a way to get all the data for a given dataset.
238
- *
239
- * @param {string} datasetName - name of the dataset for which to send the request
240
- * @param {string} debugText - text to include in logs
241
- * @returns {Promise}
242
- */
243
- }, {
244
- key: "sendInitializationSyncRequestToLocus",
245
- value: function sendInitializationSyncRequestToLocus(datasetName, debugText) {
246
- var _this2 = this;
247
- var dataset = this.dataSets[datasetName];
248
- if (!dataset) {
249
- _loggerProxy.default.logger.warn("HashTreeParser#sendInitializationSyncRequestToLocus --> ".concat(this.debugId, " No data set found for ").concat(datasetName, ", cannot send the request for leaf data"));
250
- return _promise.default.resolve(null);
219
+ value: (function () {
220
+ var _initializeNewVisibleDataSet = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee(visibleDataSetInfo, dataSetInfo) {
221
+ var hashTree;
222
+ return _regenerator.default.wrap(function (_context) {
223
+ while (1) switch (_context.prev = _context.next) {
224
+ case 0:
225
+ if (!this.isVisibleDataSet(dataSetInfo.name)) {
226
+ _context.next = 1;
227
+ break;
228
+ }
229
+ _loggerProxy.default.logger.info("HashTreeParser#initializeNewVisibleDataSet --> ".concat(this.debugId, " Data set \"").concat(dataSetInfo.name, "\" already exists, skipping init"));
230
+ return _context.abrupt("return");
231
+ case 1:
232
+ _loggerProxy.default.logger.info("HashTreeParser#initializeNewVisibleDataSet --> ".concat(this.debugId, " Adding visible data set \"").concat(dataSetInfo.name, "\""));
233
+ if (this.addToVisibleDataSetsList(visibleDataSetInfo)) {
234
+ _context.next = 2;
235
+ break;
236
+ }
237
+ return _context.abrupt("return");
238
+ case 2:
239
+ hashTree = new _hashTree.default([], dataSetInfo.leafCount);
240
+ this.dataSets[dataSetInfo.name] = _objectSpread(_objectSpread({}, dataSetInfo), {}, {
241
+ hashTree: hashTree
242
+ });
243
+ this.enqueueSyncForDataset(dataSetInfo.name, 'new visible data set initialization', true);
244
+ _context.next = 3;
245
+ return this.syncQueueProcessingPromise;
246
+ case 3:
247
+ case "end":
248
+ return _context.stop();
249
+ }
250
+ }, _callee, this);
251
+ }));
252
+ function initializeNewVisibleDataSet(_x, _x2) {
253
+ return _initializeNewVisibleDataSet.apply(this, arguments);
251
254
  }
252
- var emptyLeavesData = new Array(dataset.leafCount).fill([]);
253
- _loggerProxy.default.logger.info("HashTreeParser#sendInitializationSyncRequestToLocus --> ".concat(this.debugId, " Sending initial sync request to Locus for data set \"").concat(datasetName, "\" with empty leaf data"));
254
- return this.sendSyncRequestToLocus(this.dataSets[datasetName], emptyLeavesData).then(function (syncResponse) {
255
- if (syncResponse) {
256
- return {
257
- updateType: LocusInfoUpdateType.OBJECTS_UPDATED,
258
- updatedObjects: _this2.parseMessage(syncResponse, "via empty leaves /sync API call for ".concat(debugText))
259
- };
260
- }
261
- return {
262
- updateType: LocusInfoUpdateType.OBJECTS_UPDATED,
263
- updatedObjects: []
264
- };
265
- });
266
- }
267
-
255
+ return initializeNewVisibleDataSet;
256
+ }()
268
257
  /**
269
258
  * Queries Locus for all up-to-date information about all visible data sets
270
259
  *
271
260
  * @returns {Promise}
272
261
  */
262
+ )
273
263
  }, {
274
264
  key: "getAllVisibleDataSetsFromLocus",
275
265
  value: function getAllVisibleDataSetsFromLocus() {
276
- var _this3 = this;
266
+ var _this2 = this;
277
267
  if (!this.visibleDataSetsUrl) {
278
268
  _loggerProxy.default.logger.warn("HashTreeParser#getAllVisibleDataSetsFromLocus --> ".concat(this.debugId, " No visibleDataSetsUrl, cannot get data sets information"));
279
269
  return _promise.default.resolve([]);
@@ -284,7 +274,7 @@ var HashTreeParser = /*#__PURE__*/function () {
284
274
  }).then(function (response) {
285
275
  return response.body.dataSets;
286
276
  }).catch(function (error) {
287
- _this3.checkForSentinelHttpResponse(error);
277
+ _this2.checkForSentinelHttpResponse(error);
288
278
  throw error;
289
279
  });
290
280
  }
@@ -298,26 +288,26 @@ var HashTreeParser = /*#__PURE__*/function () {
298
288
  }, {
299
289
  key: "initializeFromMessage",
300
290
  value: (function () {
301
- var _initializeFromMessage = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee(message) {
291
+ var _initializeFromMessage = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee2(message) {
302
292
  var visibleDataSets;
303
- return _regenerator.default.wrap(function (_context) {
304
- while (1) switch (_context.prev = _context.next) {
293
+ return _regenerator.default.wrap(function (_context2) {
294
+ while (1) switch (_context2.prev = _context2.next) {
305
295
  case 0:
306
296
  this.visibleDataSetsUrl = message.visibleDataSetsUrl;
307
297
  _loggerProxy.default.logger.info("HashTreeParser#initializeFromMessage --> ".concat(this.debugId, " visibleDataSetsUrl=").concat(this.visibleDataSetsUrl));
308
- _context.next = 1;
298
+ _context2.next = 1;
309
299
  return this.getAllVisibleDataSetsFromLocus();
310
300
  case 1:
311
- visibleDataSets = _context.sent;
312
- _context.next = 2;
301
+ visibleDataSets = _context2.sent;
302
+ _context2.next = 2;
313
303
  return this.initializeDataSets(visibleDataSets, 'initialization from message');
314
304
  case 2:
315
305
  case "end":
316
- return _context.stop();
306
+ return _context2.stop();
317
307
  }
318
- }, _callee, this);
308
+ }, _callee2, this);
319
309
  }));
320
- function initializeFromMessage(_x) {
310
+ function initializeFromMessage(_x3) {
321
311
  return _initializeFromMessage.apply(this, arguments);
322
312
  }
323
313
  return initializeFromMessage;
@@ -335,34 +325,34 @@ var HashTreeParser = /*#__PURE__*/function () {
335
325
  }, {
336
326
  key: "initializeFromGetLociResponse",
337
327
  value: (function () {
338
- var _initializeFromGetLociResponse = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee2(locus) {
328
+ var _initializeFromGetLociResponse = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee3(locus) {
339
329
  var _locus$links2, _locus$links2$resourc, _locus$links2$resourc2;
340
330
  var visibleDataSets;
341
- return _regenerator.default.wrap(function (_context2) {
342
- while (1) switch (_context2.prev = _context2.next) {
331
+ return _regenerator.default.wrap(function (_context3) {
332
+ while (1) switch (_context3.prev = _context3.next) {
343
333
  case 0:
344
334
  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) {
345
- _context2.next = 1;
335
+ _context3.next = 1;
346
336
  break;
347
337
  }
348
338
  _loggerProxy.default.logger.warn("HashTreeParser#initializeFromGetLociResponse --> ".concat(this.debugId, " missing visibleDataSets url in GET Loci response, cannot initialize hash trees"));
349
- return _context2.abrupt("return");
339
+ return _context3.abrupt("return");
350
340
  case 1:
351
341
  this.visibleDataSetsUrl = locus.links.resources.visibleDataSets.url;
352
342
  _loggerProxy.default.logger.info("HashTreeParser#initializeFromGetLociResponse --> ".concat(this.debugId, " visibleDataSets url: ").concat(this.visibleDataSetsUrl));
353
- _context2.next = 2;
343
+ _context3.next = 2;
354
344
  return this.getAllVisibleDataSetsFromLocus();
355
345
  case 2:
356
- visibleDataSets = _context2.sent;
357
- _context2.next = 3;
346
+ visibleDataSets = _context3.sent;
347
+ _context3.next = 3;
358
348
  return this.initializeDataSets(visibleDataSets, 'initialization from GET /loci response');
359
349
  case 3:
360
350
  case "end":
361
- return _context2.stop();
351
+ return _context3.stop();
362
352
  }
363
- }, _callee2, this);
353
+ }, _callee3, this);
364
354
  }));
365
- function initializeFromGetLociResponse(_x2) {
355
+ function initializeFromGetLociResponse(_x4) {
366
356
  return _initializeFromGetLociResponse.apply(this, arguments);
367
357
  }
368
358
  return initializeFromGetLociResponse;
@@ -378,24 +368,23 @@ var HashTreeParser = /*#__PURE__*/function () {
378
368
  }, {
379
369
  key: "initializeDataSets",
380
370
  value: (function () {
381
- var _initializeDataSets = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee3(visibleDataSets, debugText) {
382
- var updatedObjects, _iterator2, _step2, dataSet, name, leafCount, url, _data, _t;
383
- return _regenerator.default.wrap(function (_context3) {
384
- while (1) switch (_context3.prev = _context3.next) {
371
+ var _initializeDataSets = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee4(visibleDataSets, debugText) {
372
+ var _iterator2, _step2, dataSet, name, leafCount, url, _t;
373
+ return _regenerator.default.wrap(function (_context4) {
374
+ while (1) switch (_context4.prev = _context4.next) {
385
375
  case 0:
386
376
  if (!(this.state === 'stopped')) {
387
- _context3.next = 1;
377
+ _context4.next = 1;
388
378
  break;
389
379
  }
390
- return _context3.abrupt("return");
380
+ return _context4.abrupt("return");
391
381
  case 1:
392
- updatedObjects = [];
393
- _iterator2 = _createForOfIteratorHelper(visibleDataSets);
394
- _context3.prev = 2;
382
+ _iterator2 = _createForOfIteratorHelper((0, _utils.sortByInitPriority)(visibleDataSets, _constants2.DATA_SET_INIT_PRIORITY));
383
+ _context4.prev = 2;
395
384
  _iterator2.s();
396
385
  case 3:
397
386
  if ((_step2 = _iterator2.n()).done) {
398
- _context3.next = 7;
387
+ _context4.next = 6;
399
388
  break;
400
389
  }
401
390
  dataSet = _step2.value;
@@ -407,59 +396,47 @@ var HashTreeParser = /*#__PURE__*/function () {
407
396
  _loggerProxy.default.logger.info("HashTreeParser#initializeDataSets --> ".concat(this.debugId, " dataset \"").concat(name, "\" already exists (").concat(debugText, ")"));
408
397
  }
409
398
  if (this.isVisibleDataSet(name)) {
410
- _context3.next = 4;
399
+ _context4.next = 4;
411
400
  break;
412
401
  }
413
402
  if (this.addToVisibleDataSetsList({
414
403
  name: name,
415
404
  url: url
416
405
  })) {
417
- _context3.next = 4;
406
+ _context4.next = 4;
418
407
  break;
419
408
  }
420
- return _context3.abrupt("continue", 6);
409
+ return _context4.abrupt("continue", 5);
421
410
  case 4:
422
- if (this.dataSets[name].hashTree) {
423
- _context3.next = 6;
424
- break;
411
+ if (!this.dataSets[name].hashTree) {
412
+ _loggerProxy.default.logger.info("HashTreeParser#initializeDataSets --> ".concat(this.debugId, " creating hash tree for visible dataset \"").concat(name, "\" (").concat(debugText, ")"));
413
+ this.dataSets[name].hashTree = new _hashTree.default([], leafCount);
414
+ this.enqueueSyncForDataset(name, "initialization sync for ".concat(debugText), true);
425
415
  }
426
- _loggerProxy.default.logger.info("HashTreeParser#initializeDataSets --> ".concat(this.debugId, " creating hash tree for visible dataset \"").concat(name, "\" (").concat(debugText, ")"));
427
- this.dataSets[name].hashTree = new _hashTree.default([], leafCount);
428
-
429
- // eslint-disable-next-line no-await-in-loop
430
- _context3.next = 5;
431
- return this.sendInitializationSyncRequestToLocus(name, debugText);
432
416
  case 5:
433
- _data = _context3.sent;
434
- if (_data.updateType === LocusInfoUpdateType.OBJECTS_UPDATED) {
435
- updatedObjects.push.apply(updatedObjects, (0, _toConsumableArray2.default)(_data.updatedObjects || []));
436
- }
417
+ _context4.next = 3;
418
+ break;
437
419
  case 6:
438
- _context3.next = 3;
420
+ _context4.next = 8;
439
421
  break;
440
422
  case 7:
441
- _context3.next = 9;
442
- break;
443
- case 8:
444
- _context3.prev = 8;
445
- _t = _context3["catch"](2);
423
+ _context4.prev = 7;
424
+ _t = _context4["catch"](2);
446
425
  _iterator2.e(_t);
447
- case 9:
448
- _context3.prev = 9;
426
+ case 8:
427
+ _context4.prev = 8;
449
428
  _iterator2.f();
450
- return _context3.finish(9);
429
+ return _context4.finish(8);
430
+ case 9:
431
+ _context4.next = 10;
432
+ return this.syncQueueProcessingPromise;
451
433
  case 10:
452
- this.callLocusInfoUpdateCallback({
453
- updateType: LocusInfoUpdateType.OBJECTS_UPDATED,
454
- updatedObjects: updatedObjects
455
- });
456
- case 11:
457
434
  case "end":
458
- return _context3.stop();
435
+ return _context4.stop();
459
436
  }
460
- }, _callee3, this, [[2, 8, 9, 10]]);
437
+ }, _callee4, this, [[2, 7, 8, 9]]);
461
438
  }));
462
- function initializeDataSets(_x3, _x4) {
439
+ function initializeDataSets(_x5, _x6) {
463
440
  return _initializeDataSets.apply(this, arguments);
464
441
  }
465
442
  return initializeDataSets;
@@ -483,7 +460,7 @@ var HashTreeParser = /*#__PURE__*/function () {
483
460
  copyData = _ref$copyData === void 0 ? false : _ref$copyData;
484
461
  // object mapping dataset names to arrays of leaf data
485
462
  var leafInfo = {};
486
- var _findAndStoreMetaData = function findAndStoreMetaData(currentLocusPart) {
463
+ var _findAndStoreMetaData = function findAndStoreMetaData(currentLocusPart, currentLocusPartName) {
487
464
  if ((0, _typeof2.default)(currentLocusPart) !== 'object' || currentLocusPart === null) {
488
465
  return;
489
466
  }
@@ -499,10 +476,16 @@ var HashTreeParser = /*#__PURE__*/function () {
499
476
  version: version
500
477
  };
501
478
  if (copyData) {
502
- newLeafInfo.data = (0, _lodash.cloneDeep)(currentLocusPart);
479
+ if (type.toLowerCase() === _types.ObjectType.control) {
480
+ // control entries require special handling, because they are signalled by Locus
481
+ // differently when coming in messages vs API responses
482
+ newLeafInfo.data = (0, _defineProperty2.default)({}, currentLocusPartName, (0, _lodash.cloneDeep)(currentLocusPart));
483
+ } else {
484
+ newLeafInfo.data = (0, _lodash.cloneDeep)(currentLocusPart);
503
485
 
504
- // remove any nested other objects that have their own htMeta
505
- (0, _utils.deleteNestedObjectsWithHtMeta)(newLeafInfo.data);
486
+ // remove any nested other objects that have their own htMeta
487
+ (0, _utils.deleteNestedObjectsWithHtMeta)(newLeafInfo.data);
488
+ }
506
489
  }
507
490
  var _iterator3 = _createForOfIteratorHelper(dataSetNames),
508
491
  _step3;
@@ -521,12 +504,14 @@ var HashTreeParser = /*#__PURE__*/function () {
521
504
  }
522
505
  }
523
506
  if ((0, _isArray.default)(currentLocusPart)) {
524
- var _iterator4 = _createForOfIteratorHelper(currentLocusPart),
507
+ var _iterator4 = _createForOfIteratorHelper(currentLocusPart.entries()),
525
508
  _step4;
526
509
  try {
527
510
  for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
528
- var item = _step4.value;
529
- _findAndStoreMetaData(item);
511
+ var _step4$value = (0, _slicedToArray2.default)(_step4.value, 2),
512
+ index = _step4$value[0],
513
+ item = _step4$value[1];
514
+ _findAndStoreMetaData(item, index.toString());
530
515
  }
531
516
  } catch (err) {
532
517
  _iterator4.e(err);
@@ -537,12 +522,12 @@ var HashTreeParser = /*#__PURE__*/function () {
537
522
  for (var _i = 0, _Object$keys = (0, _keys.default)(currentLocusPart); _i < _Object$keys.length; _i++) {
538
523
  var key = _Object$keys[_i];
539
524
  if (Object.prototype.hasOwnProperty.call(currentLocusPart, key)) {
540
- _findAndStoreMetaData(currentLocusPart[key]);
525
+ _findAndStoreMetaData(currentLocusPart[key], key);
541
526
  }
542
527
  }
543
528
  }
544
529
  };
545
- _findAndStoreMetaData(locus);
530
+ _findAndStoreMetaData(locus, 'locus');
546
531
  return leafInfo;
547
532
  }
548
533
 
@@ -587,9 +572,9 @@ var HashTreeParser = /*#__PURE__*/function () {
587
572
  }, {
588
573
  key: "isEndMessage",
589
574
  value: function isEndMessage(message) {
590
- var _this4 = this;
575
+ var _this3 = this;
591
576
  return message.dataSets.some(function (dataSet) {
592
- 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())) {
577
+ if (dataSet.leafCount === 1 && dataSet.root === _constants2.EMPTY_HASH && (!_this3.dataSets[dataSet.name] || _this3.dataSets[dataSet.name].version < dataSet.version) && PossibleSentinelMessageDataSetNames.includes(dataSet.name.toLowerCase())) {
593
578
  // this is a special way for Locus to indicate that this meeting has ended
594
579
  return true;
595
580
  }
@@ -606,7 +591,7 @@ var HashTreeParser = /*#__PURE__*/function () {
606
591
  }, {
607
592
  key: "handleRootHashHeartBeatMessage",
608
593
  value: function handleRootHashHeartBeatMessage(message) {
609
- var _this5 = this;
594
+ var _this4 = this;
610
595
  var dataSets = message.dataSets;
611
596
  _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) {
612
597
  var name = _ref2.name,
@@ -621,8 +606,8 @@ var HashTreeParser = /*#__PURE__*/function () {
621
606
  };
622
607
  }))));
623
608
  dataSets.forEach(function (dataSet) {
624
- _this5.updateDataSetInfo(dataSet);
625
- _this5.runSyncAlgorithm(dataSet);
609
+ _this4.updateDataSetInfo(dataSet);
610
+ _this4.runSyncAlgorithm(dataSet);
626
611
  });
627
612
  }
628
613
 
@@ -635,18 +620,18 @@ var HashTreeParser = /*#__PURE__*/function () {
635
620
  }, {
636
621
  key: "queueInitForNewVisibleDataSets",
637
622
  value: function queueInitForNewVisibleDataSets(dataSetsRequiringInitialization) {
638
- var _this6 = this;
623
+ var _this5 = this;
639
624
  _loggerProxy.default.logger.info("HashTreeParser#queueInitForNewVisibleDataSets --> ".concat(this.debugId, " queuing initialization of new visible datasets: ").concat(dataSetsRequiringInitialization.map(function (ds) {
640
625
  return ds.name;
641
626
  }).join(', ')));
642
627
  queueMicrotask(function () {
643
- _this6.initializeNewVisibleDataSets(dataSetsRequiringInitialization).catch(function (error) {
628
+ _this5.initializeNewVisibleDataSets(dataSetsRequiringInitialization).catch(function (error) {
644
629
  if (error instanceof MeetingEndedError) {
645
- _this6.callLocusInfoUpdateCallback({
630
+ _this5.callLocusInfoUpdateCallback({
646
631
  updateType: LocusInfoUpdateType.MEETING_ENDED
647
632
  });
648
633
  } else {
649
- _loggerProxy.default.logger.warn("HashTreeParser#queueInitForNewVisibleDataSets --> ".concat(_this6.debugId, " error while initializing new visible datasets: ").concat(dataSetsRequiringInitialization.map(function (ds) {
634
+ _loggerProxy.default.logger.warn("HashTreeParser#queueInitForNewVisibleDataSets --> ".concat(_this5.debugId, " error while initializing new visible datasets: ").concat(dataSetsRequiringInitialization.map(function (ds) {
650
635
  return ds.name;
651
636
  }).join(', '), ": "), error);
652
637
  }
@@ -706,7 +691,7 @@ var HashTreeParser = /*#__PURE__*/function () {
706
691
  }, {
707
692
  key: "handleLocusUpdate",
708
693
  value: function handleLocusUpdate(update) {
709
- var _this7 = this;
694
+ var _this6 = this;
710
695
  if (this.state === 'stopped') {
711
696
  return;
712
697
  }
@@ -714,6 +699,7 @@ var HashTreeParser = /*#__PURE__*/function () {
714
699
  locus = update.locus,
715
700
  metadata = update.metadata;
716
701
  if (!dataSets) {
702
+ // this happens for example when we handle GET /loci response
717
703
  _loggerProxy.default.logger.info("HashTreeParser#handleLocusUpdate --> ".concat(this.debugId, " received hash tree update without dataSets"));
718
704
  } else {
719
705
  var _iterator5 = _createForOfIteratorHelper(dataSets),
@@ -743,9 +729,9 @@ var HashTreeParser = /*#__PURE__*/function () {
743
729
 
744
730
  // then process the data in hash trees, if it is a new version, then add it to updatedObjects
745
731
  (0, _keys.default)(leafInfo).forEach(function (dataSetName) {
746
- if (_this7.dataSets[dataSetName]) {
747
- if (_this7.dataSets[dataSetName].hashTree) {
748
- var appliedChangesList = _this7.dataSets[dataSetName].hashTree.putItems(leafInfo[dataSetName].map(function (leaf) {
732
+ if (_this6.dataSets[dataSetName]) {
733
+ if (_this6.dataSets[dataSetName].hashTree) {
734
+ var appliedChangesList = _this6.dataSets[dataSetName].hashTree.putItems(leafInfo[dataSetName].map(function (leaf) {
749
735
  return {
750
736
  id: leaf.id,
751
737
  type: leaf.type,
@@ -772,10 +758,10 @@ var HashTreeParser = /*#__PURE__*/function () {
772
758
  });
773
759
  } else {
774
760
  // no hash tree means that the data set is not visible
775
- _loggerProxy.default.logger.warn("HashTreeParser#handleLocusUpdate --> ".concat(_this7.debugId, " received leaf data for data set \"").concat(dataSetName, "\" that has no hash tree created, ignoring"));
761
+ _loggerProxy.default.logger.warn("HashTreeParser#handleLocusUpdate --> ".concat(_this6.debugId, " received leaf data for data set \"").concat(dataSetName, "\" that has no hash tree created, ignoring"));
776
762
  }
777
763
  } else {
778
- _loggerProxy.default.logger.info("HashTreeParser#handleLocusUpdate --> ".concat(_this7.debugId, " received leaf data for unknown data set \"").concat(dataSetName, "\", ignoring"));
764
+ _loggerProxy.default.logger.info("HashTreeParser#handleLocusUpdate --> ".concat(_this6.debugId, " received leaf data for unknown data set \"").concat(dataSetName, "\", ignoring"));
779
765
  }
780
766
  });
781
767
  if (updatedObjects.length === 0) {
@@ -823,7 +809,7 @@ var HashTreeParser = /*#__PURE__*/function () {
823
809
  }, {
824
810
  key: "checkForVisibleDataSetChanges",
825
811
  value: function checkForVisibleDataSetChanges(updatedObjects) {
826
- var _this8 = this;
812
+ var _this7 = this;
827
813
  var removedDataSets = [];
828
814
  var addedDataSets = [];
829
815
 
@@ -832,20 +818,20 @@ var HashTreeParser = /*#__PURE__*/function () {
832
818
  var _object$data;
833
819
  if ((0, _utils.isMetadata)(object) && (_object$data = object.data) !== null && _object$data !== void 0 && _object$data.visibleDataSets) {
834
820
  var newVisibleDataSets = object.data.visibleDataSets.filter(function (vds) {
835
- return !_this8.isExcludedDataSet(vds.name);
821
+ return !_this7.isExcludedDataSet(vds.name);
836
822
  });
837
- removedDataSets = _this8.visibleDataSets.filter(function (ds) {
823
+ removedDataSets = _this7.visibleDataSets.filter(function (ds) {
838
824
  return !newVisibleDataSets.some(function (nvs) {
839
825
  return nvs.name === ds.name;
840
826
  });
841
827
  });
842
828
  addedDataSets = newVisibleDataSets.filter(function (nvs) {
843
- return _this8.visibleDataSets.every(function (ds) {
829
+ return _this7.visibleDataSets.every(function (ds) {
844
830
  return ds.name !== nvs.name;
845
831
  });
846
832
  });
847
833
  if (removedDataSets.length > 0 || addedDataSets.length > 0) {
848
- _loggerProxy.default.logger.info("HashTreeParser#checkForVisibleDataSetChanges --> ".concat(_this8.debugId, " visible data sets change: removed: ").concat(removedDataSets.map(function (ds) {
834
+ _loggerProxy.default.logger.info("HashTreeParser#checkForVisibleDataSetChanges --> ".concat(_this7.debugId, " visible data sets change: removed: ").concat(removedDataSets.map(function (ds) {
849
835
  return ds.name;
850
836
  }).join(', '), ", added: ").concat(addedDataSets.map(function (ds) {
851
837
  return ds.name;
@@ -899,16 +885,16 @@ var HashTreeParser = /*#__PURE__*/function () {
899
885
  }, {
900
886
  key: "processVisibleDataSetChanges",
901
887
  value: function processVisibleDataSetChanges(removedDataSets, addedDataSets, updatedObjects) {
902
- var _this9 = this;
888
+ var _this8 = this;
903
889
  var dataSetsRequiringInitialization = [];
904
890
 
905
891
  // if a visible data set was removed, we need to tell our client that all objects from it are removed
906
892
  var removedObjects = [];
907
893
  removedDataSets.forEach(function (ds) {
908
- var _this9$dataSets$ds$na;
909
- if ((_this9$dataSets$ds$na = _this9.dataSets[ds.name]) !== null && _this9$dataSets$ds$na !== void 0 && _this9$dataSets$ds$na.hashTree) {
910
- for (var i = 0; i < _this9.dataSets[ds.name].hashTree.numLeaves; i += 1) {
911
- removedObjects.push.apply(removedObjects, (0, _toConsumableArray2.default)(_this9.dataSets[ds.name].hashTree.getLeafData(i).map(function (elementId) {
894
+ var _this8$dataSets$ds$na;
895
+ if ((_this8$dataSets$ds$na = _this8.dataSets[ds.name]) !== null && _this8$dataSets$ds$na !== void 0 && _this8$dataSets$ds$na.hashTree) {
896
+ for (var i = 0; i < _this8.dataSets[ds.name].hashTree.numLeaves; i += 1) {
897
+ removedObjects.push.apply(removedObjects, (0, _toConsumableArray2.default)(_this8.dataSets[ds.name].hashTree.getLeafData(i).map(function (elementId) {
912
898
  return {
913
899
  htMeta: {
914
900
  elementId: elementId,
@@ -918,7 +904,7 @@ var HashTreeParser = /*#__PURE__*/function () {
918
904
  };
919
905
  })));
920
906
  }
921
- _this9.deleteHashTree(ds.name);
907
+ _this8.deleteHashTree(ds.name);
922
908
  }
923
909
  });
924
910
  this.visibleDataSets = this.visibleDataSets.filter(function (vds) {
@@ -979,81 +965,78 @@ var HashTreeParser = /*#__PURE__*/function () {
979
965
  }, {
980
966
  key: "initializeNewVisibleDataSets",
981
967
  value: (function () {
982
- var _initializeNewVisibleDataSets = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee4(addedDataSets) {
983
- var _this0 = this;
968
+ var _initializeNewVisibleDataSets = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee5(addedDataSets) {
969
+ var _this9 = this;
984
970
  var allDataSets, _iterator7, _step7, _loop, _t2;
985
- return _regenerator.default.wrap(function (_context5) {
986
- while (1) switch (_context5.prev = _context5.next) {
971
+ return _regenerator.default.wrap(function (_context6) {
972
+ while (1) switch (_context6.prev = _context6.next) {
987
973
  case 0:
988
974
  if (!(this.state === 'stopped')) {
989
- _context5.next = 1;
975
+ _context6.next = 1;
990
976
  break;
991
977
  }
992
- return _context5.abrupt("return");
978
+ return _context6.abrupt("return");
993
979
  case 1:
994
- _context5.next = 2;
980
+ _context6.next = 2;
995
981
  return this.getAllVisibleDataSetsFromLocus();
996
982
  case 2:
997
- allDataSets = _context5.sent;
998
- _iterator7 = _createForOfIteratorHelper(addedDataSets);
999
- _context5.prev = 3;
983
+ allDataSets = _context6.sent;
984
+ _iterator7 = _createForOfIteratorHelper((0, _utils.sortByInitPriority)(addedDataSets, _constants2.DATA_SET_INIT_PRIORITY));
985
+ _context6.prev = 3;
1000
986
  _loop = /*#__PURE__*/_regenerator.default.mark(function _loop() {
1001
- var ds, dataSetInfo, updates;
1002
- return _regenerator.default.wrap(function (_context4) {
1003
- while (1) switch (_context4.prev = _context4.next) {
987
+ var ds, dataSetInfo;
988
+ return _regenerator.default.wrap(function (_context5) {
989
+ while (1) switch (_context5.prev = _context5.next) {
1004
990
  case 0:
1005
991
  ds = _step7.value;
1006
992
  dataSetInfo = allDataSets.find(function (d) {
1007
993
  return d.name === ds.name;
1008
994
  });
1009
- _loggerProxy.default.logger.info("HashTreeParser#initializeNewVisibleDataSets --> ".concat(_this0.debugId, " initializing data set \"").concat(ds.name, "\""));
995
+ _loggerProxy.default.logger.info("HashTreeParser#initializeNewVisibleDataSets --> ".concat(_this9.debugId, " initializing data set \"").concat(ds.name, "\""));
1010
996
  if (dataSetInfo) {
1011
- _context4.next = 1;
997
+ _context5.next = 1;
1012
998
  break;
1013
999
  }
1014
- _loggerProxy.default.logger.warn("HashTreeParser#initializeNewVisibleDataSets --> ".concat(_this0.debugId, " missing info about data set \"").concat(ds.name, "\" in Locus response from visibleDataSetsUrl"));
1015
- _context4.next = 3;
1000
+ _loggerProxy.default.logger.warn("HashTreeParser#initializeNewVisibleDataSets --> ".concat(_this9.debugId, " missing info about data set \"").concat(ds.name, "\" in Locus response from visibleDataSetsUrl"));
1001
+ _context5.next = 2;
1016
1002
  break;
1017
1003
  case 1:
1018
- _context4.next = 2;
1019
- return _this0.initializeNewVisibleDataSet(ds, dataSetInfo);
1004
+ _context5.next = 2;
1005
+ return _this9.initializeNewVisibleDataSet(ds, dataSetInfo);
1020
1006
  case 2:
1021
- updates = _context4.sent;
1022
- _this0.callLocusInfoUpdateCallback(updates);
1023
- case 3:
1024
1007
  case "end":
1025
- return _context4.stop();
1008
+ return _context5.stop();
1026
1009
  }
1027
1010
  }, _loop);
1028
1011
  });
1029
1012
  _iterator7.s();
1030
1013
  case 4:
1031
1014
  if ((_step7 = _iterator7.n()).done) {
1032
- _context5.next = 6;
1015
+ _context6.next = 6;
1033
1016
  break;
1034
1017
  }
1035
- return _context5.delegateYield(_loop(), "t0", 5);
1018
+ return _context6.delegateYield(_loop(), "t0", 5);
1036
1019
  case 5:
1037
- _context5.next = 4;
1020
+ _context6.next = 4;
1038
1021
  break;
1039
1022
  case 6:
1040
- _context5.next = 8;
1023
+ _context6.next = 8;
1041
1024
  break;
1042
1025
  case 7:
1043
- _context5.prev = 7;
1044
- _t2 = _context5["catch"](3);
1026
+ _context6.prev = 7;
1027
+ _t2 = _context6["catch"](3);
1045
1028
  _iterator7.e(_t2);
1046
1029
  case 8:
1047
- _context5.prev = 8;
1030
+ _context6.prev = 8;
1048
1031
  _iterator7.f();
1049
- return _context5.finish(8);
1032
+ return _context6.finish(8);
1050
1033
  case 9:
1051
1034
  case "end":
1052
- return _context5.stop();
1035
+ return _context6.stop();
1053
1036
  }
1054
- }, _callee4, this, [[3, 7, 8, 9]]);
1037
+ }, _callee5, this, [[3, 7, 8, 9]]);
1055
1038
  }));
1056
- function initializeNewVisibleDataSets(_x5) {
1039
+ function initializeNewVisibleDataSets(_x7) {
1057
1040
  return _initializeNewVisibleDataSets.apply(this, arguments);
1058
1041
  }
1059
1042
  return initializeNewVisibleDataSets;
@@ -1070,8 +1053,7 @@ var HashTreeParser = /*#__PURE__*/function () {
1070
1053
  key: "parseMessage",
1071
1054
  value: function parseMessage(message, debugText) {
1072
1055
  var _message$locusStateEl,
1073
- _this1 = this,
1074
- _message$locusStateEl2;
1056
+ _this0 = this;
1075
1057
  if (this.state === 'stopped') {
1076
1058
  return [];
1077
1059
  }
@@ -1086,7 +1068,7 @@ var HashTreeParser = /*#__PURE__*/function () {
1086
1068
  // first, update our metadata about the datasets with info from the message
1087
1069
  this.visibleDataSetsUrl = visibleDataSetsUrl;
1088
1070
  dataSets.forEach(function (dataSet) {
1089
- return _this1.updateDataSetInfo(dataSet);
1071
+ return _this0.updateDataSetInfo(dataSet);
1090
1072
  });
1091
1073
  var updatedObjects = [];
1092
1074
 
@@ -1106,9 +1088,9 @@ var HashTreeParser = /*#__PURE__*/function () {
1106
1088
  _step8;
1107
1089
  try {
1108
1090
  for (_iterator8.s(); !(_step8 = _iterator8.n()).done;) {
1109
- var _this1$dataSets$dataS;
1091
+ var _this0$dataSets$dataS;
1110
1092
  var dataSetName = _step8.value;
1111
- var hashTree = (_this1$dataSets$dataS = _this1.dataSets[dataSetName]) === null || _this1$dataSets$dataS === void 0 ? void 0 : _this1$dataSets$dataS.hashTree;
1093
+ var hashTree = (_this0$dataSets$dataS = _this0.dataSets[dataSetName]) === null || _this0$dataSets$dataS === void 0 ? void 0 : _this0$dataSets$dataS.hashTree;
1112
1094
  if (hashTree && object.data) {
1113
1095
  if (hashTree.putItem(object.htMeta.elementId)) {
1114
1096
  updatedMetadataObjects.push(object);
@@ -1130,13 +1112,13 @@ var HashTreeParser = /*#__PURE__*/function () {
1130
1112
  dataSetsRequiringInitialization = this.processVisibleDataSetChanges(removedDataSets, addedDataSets, updatedObjects);
1131
1113
  }
1132
1114
  }
1133
- if (((_message$locusStateEl2 = message.locusStateElements) === null || _message$locusStateEl2 === void 0 ? void 0 : _message$locusStateEl2.length) > 0) {
1115
+ if (message.locusStateElements && message.locusStateElements.length > 0) {
1134
1116
  // by this point we now have this.dataSets setup for data sets from this message
1135
1117
  // and hash trees created for the new visible data sets,
1136
1118
  // so we can now process all the updates from the message
1137
1119
  dataSets.forEach(function (dataSet) {
1138
- if (_this1.dataSets[dataSet.name]) {
1139
- var hashTree = _this1.dataSets[dataSet.name].hashTree;
1120
+ if (_this0.dataSets[dataSet.name]) {
1121
+ var hashTree = _this0.dataSets[dataSet.name].hashTree;
1140
1122
  if (hashTree) {
1141
1123
  var locusStateElementsForThisSet = message.locusStateElements.filter(function (object) {
1142
1124
  return object.htMeta.dataSetNames.includes(dataSet.name);
@@ -1160,10 +1142,10 @@ var HashTreeParser = /*#__PURE__*/function () {
1160
1142
  }
1161
1143
  });
1162
1144
  } else {
1163
- _loggerProxy.default.logger.info("Locus-info:index#parseMessage --> ".concat(_this1.debugId, " unexpected (not visible) dataSet ").concat(dataSet.name, " received in hash tree message"));
1145
+ _loggerProxy.default.logger.info("Locus-info:index#parseMessage --> ".concat(_this0.debugId, " unexpected (not visible) dataSet ").concat(dataSet.name, " received in hash tree message"));
1164
1146
  }
1165
1147
  }
1166
- _this1.runSyncAlgorithm(dataSet);
1148
+ _this0.runSyncAlgorithm(dataSet);
1167
1149
  });
1168
1150
  }
1169
1151
  if (dataSetsRequiringInitialization.length > 0) {
@@ -1220,25 +1202,25 @@ var HashTreeParser = /*#__PURE__*/function () {
1220
1202
  }, {
1221
1203
  key: "callLocusInfoUpdateCallback",
1222
1204
  value: function callLocusInfoUpdateCallback(updates) {
1223
- var _this10 = this;
1205
+ var _updates$updatedObjec,
1206
+ _this1 = this;
1224
1207
  if (this.state === 'stopped') {
1225
1208
  return;
1226
1209
  }
1227
- var updateType = updates.updateType,
1228
- updatedObjects = updates.updatedObjects;
1229
- if (updateType === LocusInfoUpdateType.OBJECTS_UPDATED && (updatedObjects === null || updatedObjects === void 0 ? void 0 : updatedObjects.length) > 0) {
1210
+ var updateType = updates.updateType;
1211
+ if (updateType === LocusInfoUpdateType.OBJECTS_UPDATED && ((_updates$updatedObjec = updates.updatedObjects) === null || _updates$updatedObjec === void 0 ? void 0 : _updates$updatedObjec.length) > 0) {
1230
1212
  // Filter out updates for objects that already have a higher version in their datasets,
1231
1213
  // or removals for objects that still exist in any of their datasets
1232
- var filteredUpdates = updatedObjects.filter(function (object) {
1214
+ var filteredUpdates = updates.updatedObjects.filter(function (object) {
1233
1215
  var elementId = object.htMeta.elementId;
1234
1216
  var type = elementId.type,
1235
1217
  id = elementId.id,
1236
1218
  version = elementId.version;
1237
1219
 
1238
1220
  // Check all datasets
1239
- for (var _i2 = 0, _Object$keys3 = (0, _keys.default)(_this10.dataSets); _i2 < _Object$keys3.length; _i2++) {
1221
+ for (var _i2 = 0, _Object$keys3 = (0, _keys.default)(_this1.dataSets); _i2 < _Object$keys3.length; _i2++) {
1240
1222
  var dataSetName = _Object$keys3[_i2];
1241
- var dataSet = _this10.dataSets[dataSetName];
1223
+ var dataSet = _this1.dataSets[dataSetName];
1242
1224
 
1243
1225
  // only visible datasets have hash trees set
1244
1226
  if (dataSet !== null && dataSet !== void 0 && dataSet.hashTree) {
@@ -1247,12 +1229,12 @@ var HashTreeParser = /*#__PURE__*/function () {
1247
1229
  if (object.data) {
1248
1230
  // For updates: filter out if any dataset has a higher version
1249
1231
  if (existingVersion > version) {
1250
- _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));
1232
+ _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));
1251
1233
  return false;
1252
1234
  }
1253
1235
  } else if (existingVersion >= version) {
1254
1236
  // For removals: filter out if the object still exists in any dataset
1255
- _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));
1237
+ _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));
1256
1238
  return false;
1257
1239
  }
1258
1240
  }
@@ -1261,13 +1243,14 @@ var HashTreeParser = /*#__PURE__*/function () {
1261
1243
  return true;
1262
1244
  });
1263
1245
  if (filteredUpdates.length > 0) {
1264
- this.locusInfoUpdateCallback(updateType, {
1246
+ this.locusInfoUpdateCallback({
1247
+ updateType: updateType,
1265
1248
  updatedObjects: filteredUpdates
1266
1249
  });
1267
1250
  }
1268
1251
  } else if (updateType !== LocusInfoUpdateType.OBJECTS_UPDATED) {
1269
- this.locusInfoUpdateCallback(updateType, {
1270
- updatedObjects: updatedObjects
1252
+ this.locusInfoUpdateCallback({
1253
+ updateType: updateType
1271
1254
  });
1272
1255
  }
1273
1256
  }
@@ -1291,84 +1274,116 @@ var HashTreeParser = /*#__PURE__*/function () {
1291
1274
  * Performs a sync for the given data set.
1292
1275
  *
1293
1276
  * @param {InternalDataSet} dataSet - The data set to sync
1294
- * @param {string} rootHash - Our current root hash for this data set
1295
1277
  * @param {string} reason - The reason for the sync (used for logging)
1278
+ * @param {boolean} [isInitialization] - Whether this is an initialization sync (sends empty leaves data instead of comparing hashes)
1296
1279
  * @returns {Promise<void>}
1297
1280
  */
1298
1281
  }, {
1299
1282
  key: "performSync",
1300
1283
  value: (function () {
1301
- var _performSync = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee5(dataSet, rootHash, reason) {
1302
- var mismatchedLeavesData, receivedHashes, _yield$this$getHashes, hashes, latestDataSetInfo, mismatchedLeaveIndexes, syncResponse, _t3, _t4;
1303
- return _regenerator.default.wrap(function (_context6) {
1304
- while (1) switch (_context6.prev = _context6.next) {
1284
+ var _performSync = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee6(dataSet, reason, isInitialization) {
1285
+ var hashTree, rootHash, leavesData, receivedHashes, hashesResult, mismatchedLeaveIndexes, syncResponse, _t3, _t4;
1286
+ return _regenerator.default.wrap(function (_context7) {
1287
+ while (1) switch (_context7.prev = _context7.next) {
1305
1288
  case 0:
1306
1289
  if (dataSet.hashTree) {
1307
- _context6.next = 1;
1290
+ _context7.next = 1;
1308
1291
  break;
1309
1292
  }
1310
- return _context6.abrupt("return");
1293
+ return _context7.abrupt("return");
1311
1294
  case 1:
1312
- _context6.prev = 1;
1295
+ hashTree = dataSet.hashTree;
1296
+ rootHash = hashTree.getRootHash();
1297
+ _context7.prev = 2;
1313
1298
  _loggerProxy.default.logger.info("HashTreeParser#performSync --> ".concat(this.debugId, " ").concat(reason, ", syncing data set \"").concat(dataSet.name, "\""));
1314
- mismatchedLeavesData = {};
1299
+ leavesData = {};
1300
+ if (isInitialization) {
1301
+ _context7.next = 10;
1302
+ break;
1303
+ }
1315
1304
  if (!(dataSet.leafCount !== 1)) {
1316
- _context6.next = 7;
1305
+ _context7.next = 9;
1317
1306
  break;
1318
1307
  }
1319
- _context6.prev = 2;
1320
- _context6.next = 3;
1308
+ _context7.prev = 3;
1309
+ _context7.next = 4;
1321
1310
  return this.getHashesFromLocus(dataSet.name, rootHash);
1322
- case 3:
1323
- _yield$this$getHashes = _context6.sent;
1324
- hashes = _yield$this$getHashes.hashes;
1325
- latestDataSetInfo = _yield$this$getHashes.dataSet;
1326
- receivedHashes = hashes;
1327
- dataSet.hashTree.resize(latestDataSetInfo.leafCount);
1328
- _context6.next = 6;
1329
- break;
1330
1311
  case 4:
1331
- _context6.prev = 4;
1332
- _t3 = _context6["catch"](2);
1333
- if (!(_t3.statusCode === 409)) {
1334
- _context6.next = 5;
1312
+ hashesResult = _context7.sent;
1313
+ if (hashesResult) {
1314
+ _context7.next = 5;
1315
+ break;
1316
+ }
1317
+ return _context7.abrupt("return");
1318
+ case 5:
1319
+ receivedHashes = hashesResult.hashes;
1320
+ hashTree.resize(hashesResult.dataSet.leafCount);
1321
+ _context7.next = 8;
1322
+ break;
1323
+ case 6:
1324
+ _context7.prev = 6;
1325
+ _t3 = _context7["catch"](3);
1326
+ if (!((_t3 === null || _t3 === void 0 ? void 0 : _t3.statusCode) === 409)) {
1327
+ _context7.next = 7;
1335
1328
  break;
1336
1329
  }
1337
1330
  // this is a leaf count mismatch, we should do nothing, just wait for another heartbeat message from Locus
1338
1331
  _loggerProxy.default.logger.info("HashTreeParser#getHashesFromLocus --> ".concat(this.debugId, " Got 409 when fetching hashes for data set \"").concat(dataSet.name, "\": ").concat(_t3.message));
1339
- return _context6.abrupt("return");
1340
- case 5:
1332
+ return _context7.abrupt("return");
1333
+ case 7:
1341
1334
  throw _t3;
1342
- case 6:
1335
+ case 8:
1343
1336
  // identify mismatched leaves
1344
- mismatchedLeaveIndexes = dataSet.hashTree.diffHashes(receivedHashes);
1337
+ mismatchedLeaveIndexes = hashTree.diffHashes(receivedHashes);
1345
1338
  mismatchedLeaveIndexes.forEach(function (index) {
1346
- mismatchedLeavesData[index] = dataSet.hashTree.getLeafData(index);
1339
+ leavesData[index] = hashTree.getLeafData(index);
1347
1340
  });
1348
- _context6.next = 8;
1341
+ _context7.next = 10;
1349
1342
  break;
1350
- case 7:
1351
- mismatchedLeavesData[0] = dataSet.hashTree.getLeafData(0);
1352
- case 8:
1353
- if (!((0, _keys.default)(mismatchedLeavesData).length > 0)) {
1354
- _context6.next = 10;
1343
+ case 9:
1344
+ leavesData = {
1345
+ 0: hashTree.getLeafData(0)
1346
+ };
1347
+ case 10:
1348
+ // request sync for mismatched leaves
1349
+ syncResponse = null;
1350
+ if (!isInitialization) {
1351
+ _context7.next = 12;
1355
1352
  break;
1356
1353
  }
1357
- _context6.next = 9;
1358
- return this.sendSyncRequestToLocus(dataSet, mismatchedLeavesData);
1359
- case 9:
1360
- syncResponse = _context6.sent;
1354
+ _context7.next = 11;
1355
+ return this.sendSyncRequestToLocus(dataSet, {
1356
+ isInitialization: true
1357
+ });
1358
+ case 11:
1359
+ syncResponse = _context7.sent;
1360
+ _context7.next = 14;
1361
+ break;
1362
+ case 12:
1363
+ if (!((0, _keys.default)(leavesData).length > 0)) {
1364
+ _context7.next = 14;
1365
+ break;
1366
+ }
1367
+ _context7.next = 13;
1368
+ return this.sendSyncRequestToLocus(dataSet, {
1369
+ mismatchedLeavesData: leavesData
1370
+ });
1371
+ case 13:
1372
+ syncResponse = _context7.sent;
1373
+ case 14:
1361
1374
  // sync API may return nothing (in that case data will arrive via messages)
1362
1375
  // or it may return a response in the same format as messages
1376
+ // We still need to restart the sync timer as a safety net in case the messages don't arrive.
1377
+ this.runSyncAlgorithm(dataSet);
1363
1378
  if (syncResponse) {
1379
+ // the format of sync response is the same as messages, so we can reuse the same handler
1364
1380
  this.handleMessage(syncResponse, 'via sync API');
1365
1381
  }
1366
- case 10:
1367
- _context6.next = 12;
1382
+ _context7.next = 16;
1368
1383
  break;
1369
- case 11:
1370
- _context6.prev = 11;
1371
- _t4 = _context6["catch"](1);
1384
+ case 15:
1385
+ _context7.prev = 15;
1386
+ _t4 = _context7["catch"](2);
1372
1387
  if (_t4 instanceof MeetingEndedError) {
1373
1388
  this.callLocusInfoUpdateCallback({
1374
1389
  updateType: LocusInfoUpdateType.MEETING_ENDED
@@ -1376,17 +1391,176 @@ var HashTreeParser = /*#__PURE__*/function () {
1376
1391
  } else {
1377
1392
  _loggerProxy.default.logger.warn("HashTreeParser#performSync --> ".concat(this.debugId, " error during sync for data set \"").concat(dataSet.name, "\":"), _t4);
1378
1393
  }
1379
- case 12:
1394
+ case 16:
1380
1395
  case "end":
1381
- return _context6.stop();
1396
+ return _context7.stop();
1382
1397
  }
1383
- }, _callee5, this, [[1, 11], [2, 4]]);
1398
+ }, _callee6, this, [[2, 15], [3, 6]]);
1384
1399
  }));
1385
- function performSync(_x6, _x7, _x8) {
1400
+ function performSync(_x8, _x9, _x0) {
1386
1401
  return _performSync.apply(this, arguments);
1387
1402
  }
1388
1403
  return performSync;
1389
1404
  }()
1405
+ /**
1406
+ * Enqueues a sync for the given data set. If the data set is already in the queue, the request is ignored.
1407
+ * This ensures that all syncs are executed sequentially and no more than 1 sync runs at a time.
1408
+ *
1409
+ * @param {string} dataSetName - The name of the data set to sync
1410
+ * @param {string} reason - The reason for the sync (used for logging)
1411
+ * @param {boolean} [isInitialization=false] - Whether this is an initialization sync (uses empty leaves data instead of hash comparison)
1412
+ * @returns {void}
1413
+ */
1414
+ )
1415
+ }, {
1416
+ key: "enqueueSyncForDataset",
1417
+ value: function enqueueSyncForDataset(dataSetName, reason) {
1418
+ var isInitialization = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
1419
+ if (this.state === 'stopped') return;
1420
+ var existingEntry = this.syncQueue.find(function (entry) {
1421
+ return entry.dataSetName === dataSetName;
1422
+ });
1423
+ if (existingEntry) {
1424
+ if (isInitialization) {
1425
+ existingEntry.isInitialization = true;
1426
+ }
1427
+ _loggerProxy.default.logger.info("HashTreeParser#enqueueSyncForDataset --> ".concat(this.debugId, " data set \"").concat(dataSetName, "\" already in sync queue, skipping"));
1428
+ return;
1429
+ }
1430
+ this.syncQueue.push({
1431
+ dataSetName: dataSetName,
1432
+ reason: reason,
1433
+ isInitialization: isInitialization
1434
+ });
1435
+ if (!this.isSyncInProgress) {
1436
+ this.syncQueueProcessingPromise = this.processSyncQueue();
1437
+ }
1438
+ }
1439
+
1440
+ /**
1441
+ * Processes the sync queue sequentially. Only one instance of this method runs at a time.
1442
+ *
1443
+ * @returns {Promise<void>}
1444
+ */
1445
+ }, {
1446
+ key: "processSyncQueue",
1447
+ value: (function () {
1448
+ var _processSyncQueue = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee7() {
1449
+ var _ref7, dataSetName, reason, isInitialization, dataSet;
1450
+ return _regenerator.default.wrap(function (_context8) {
1451
+ while (1) switch (_context8.prev = _context8.next) {
1452
+ case 0:
1453
+ if (!this.isSyncInProgress) {
1454
+ _context8.next = 1;
1455
+ break;
1456
+ }
1457
+ return _context8.abrupt("return");
1458
+ case 1:
1459
+ this.isSyncInProgress = true;
1460
+ _context8.prev = 2;
1461
+ case 3:
1462
+ if (!(this.syncQueue.length > 0 && this.state !== 'stopped')) {
1463
+ _context8.next = 6;
1464
+ break;
1465
+ }
1466
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1467
+ _ref7 = this.syncQueue.shift(), dataSetName = _ref7.dataSetName, reason = _ref7.reason, isInitialization = _ref7.isInitialization;
1468
+ dataSet = this.dataSets[dataSetName];
1469
+ if (dataSet !== null && dataSet !== void 0 && dataSet.hashTree) {
1470
+ _context8.next = 4;
1471
+ break;
1472
+ }
1473
+ return _context8.abrupt("continue", 3);
1474
+ case 4:
1475
+ _context8.next = 5;
1476
+ return this.performSync(dataSet, reason, isInitialization);
1477
+ case 5:
1478
+ _context8.next = 3;
1479
+ break;
1480
+ case 6:
1481
+ _context8.prev = 6;
1482
+ this.isSyncInProgress = false;
1483
+ return _context8.finish(6);
1484
+ case 7:
1485
+ case "end":
1486
+ return _context8.stop();
1487
+ }
1488
+ }, _callee7, this, [[2,, 6, 7]]);
1489
+ }));
1490
+ function processSyncQueue() {
1491
+ return _processSyncQueue.apply(this, arguments);
1492
+ }
1493
+ return processSyncQueue;
1494
+ }()
1495
+ /**
1496
+ * Syncs all data sets that have hash trees, one by one in sequence, using the priority order
1497
+ * provided by sortByInitPriority(). Does nothing if the parser is stopped or if a syncAllDatasets
1498
+ * call is already in progress.
1499
+ *
1500
+ * @returns {Promise<void>}
1501
+ */
1502
+ )
1503
+ }, {
1504
+ key: "syncAllDatasets",
1505
+ value: (function () {
1506
+ var _syncAllDatasets = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee8() {
1507
+ var dataSetsWithHashTrees, sorted, _iterator9, _step9, ds;
1508
+ return _regenerator.default.wrap(function (_context9) {
1509
+ while (1) switch (_context9.prev = _context9.next) {
1510
+ case 0:
1511
+ if (!(this.state === 'stopped')) {
1512
+ _context9.next = 1;
1513
+ break;
1514
+ }
1515
+ return _context9.abrupt("return");
1516
+ case 1:
1517
+ if (!this.isSyncAllInProgress) {
1518
+ _context9.next = 2;
1519
+ break;
1520
+ }
1521
+ return _context9.abrupt("return");
1522
+ case 2:
1523
+ this.isSyncAllInProgress = true;
1524
+ _context9.prev = 3;
1525
+ dataSetsWithHashTrees = (0, _values.default)(this.dataSets).filter(function (dataSet) {
1526
+ return dataSet === null || dataSet === void 0 ? void 0 : dataSet.hashTree;
1527
+ }).map(function (dataSet) {
1528
+ return {
1529
+ name: dataSet.name
1530
+ };
1531
+ });
1532
+ sorted = (0, _utils.sortByInitPriority)(dataSetsWithHashTrees, _constants2.DATA_SET_INIT_PRIORITY);
1533
+ _loggerProxy.default.logger.info("HashTreeParser#syncAllDatasets --> ".concat(this.debugId, " syncing datasets: ").concat(sorted.map(function (ds) {
1534
+ return ds.name;
1535
+ }).join(', ')));
1536
+ _iterator9 = _createForOfIteratorHelper(sorted);
1537
+ try {
1538
+ for (_iterator9.s(); !(_step9 = _iterator9.n()).done;) {
1539
+ ds = _step9.value;
1540
+ this.enqueueSyncForDataset(ds.name, 'syncAllDatasets');
1541
+ }
1542
+ } catch (err) {
1543
+ _iterator9.e(err);
1544
+ } finally {
1545
+ _iterator9.f();
1546
+ }
1547
+ _context9.next = 4;
1548
+ return this.syncQueueProcessingPromise;
1549
+ case 4:
1550
+ _context9.prev = 4;
1551
+ this.isSyncAllInProgress = false;
1552
+ return _context9.finish(4);
1553
+ case 5:
1554
+ case "end":
1555
+ return _context9.stop();
1556
+ }
1557
+ }, _callee8, this, [[3,, 4, 5]]);
1558
+ }));
1559
+ function syncAllDatasets() {
1560
+ return _syncAllDatasets.apply(this, arguments);
1561
+ }
1562
+ return syncAllDatasets;
1563
+ }()
1390
1564
  /**
1391
1565
  * Runs the sync algorithm for the given data set.
1392
1566
  *
@@ -1397,7 +1571,7 @@ var HashTreeParser = /*#__PURE__*/function () {
1397
1571
  }, {
1398
1572
  key: "runSyncAlgorithm",
1399
1573
  value: function runSyncAlgorithm(receivedDataSet) {
1400
- var _this11 = this;
1574
+ var _this10 = this;
1401
1575
  var dataSet = this.dataSets[receivedDataSet.name];
1402
1576
  if (!dataSet) {
1403
1577
  _loggerProxy.default.logger.warn("HashTreeParser#runSyncAlgorithm --> ".concat(this.debugId, " No data set found for ").concat(receivedDataSet.name, ", skipping sync algorithm"));
@@ -1408,47 +1582,25 @@ var HashTreeParser = /*#__PURE__*/function () {
1408
1582
  return;
1409
1583
  }
1410
1584
  dataSet.hashTree.resize(receivedDataSet.leafCount);
1411
-
1412
- // temporary log for the workshop // todo: remove
1413
- var ourCurrentRootHash = dataSet.hashTree.getRootHash();
1414
- _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));
1415
1585
  var delay = dataSet.idleMs + this.getWeightedBackoffTime(dataSet.backoff);
1416
1586
  if (delay > 0) {
1417
1587
  if (dataSet.timer) {
1418
1588
  clearTimeout(dataSet.timer);
1419
1589
  }
1420
1590
  _loggerProxy.default.logger.info("HashTreeParser#runSyncAlgorithm --> ".concat(this.debugId, " setting \"").concat(dataSet.name, "\" sync timer for ").concat(delay));
1421
- dataSet.timer = setTimeout(/*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee6() {
1422
- var rootHash;
1423
- return _regenerator.default.wrap(function (_context7) {
1424
- while (1) switch (_context7.prev = _context7.next) {
1425
- case 0:
1426
- dataSet.timer = undefined;
1427
- if (dataSet.hashTree) {
1428
- _context7.next = 1;
1429
- break;
1430
- }
1431
- _loggerProxy.default.logger.warn("HashTreeParser#runSyncAlgorithm --> ".concat(_this11.debugId, " Data set \"").concat(dataSet.name, "\" no longer has a hash tree, cannot run sync algorithm"));
1432
- return _context7.abrupt("return");
1433
- case 1:
1434
- rootHash = dataSet.hashTree.getRootHash();
1435
- if (!(dataSet.root !== rootHash)) {
1436
- _context7.next = 3;
1437
- break;
1438
- }
1439
- _context7.next = 2;
1440
- return _this11.performSync(dataSet, rootHash, "Root hash mismatch: received=".concat(dataSet.root, ", ours=").concat(rootHash));
1441
- case 2:
1442
- _context7.next = 4;
1443
- break;
1444
- case 3:
1445
- _loggerProxy.default.logger.info("HashTreeParser#runSyncAlgorithm --> ".concat(_this11.debugId, " \"").concat(dataSet.name, "\" root hash matching: ").concat(rootHash, ", version=").concat(dataSet.version));
1446
- case 4:
1447
- case "end":
1448
- return _context7.stop();
1449
- }
1450
- }, _callee6);
1451
- })), delay);
1591
+ dataSet.timer = setTimeout(function () {
1592
+ dataSet.timer = undefined;
1593
+ if (!dataSet.hashTree) {
1594
+ _loggerProxy.default.logger.warn("HashTreeParser#runSyncAlgorithm --> ".concat(_this10.debugId, " Data set \"").concat(dataSet.name, "\" no longer has a hash tree, cannot run sync algorithm"));
1595
+ return;
1596
+ }
1597
+ var rootHash = dataSet.hashTree.getRootHash();
1598
+ if (dataSet.root !== rootHash) {
1599
+ _this10.enqueueSyncForDataset(dataSet.name, "Root hash mismatch: received=".concat(dataSet.root, ", ours=").concat(rootHash));
1600
+ } else {
1601
+ _loggerProxy.default.logger.info("HashTreeParser#runSyncAlgorithm --> ".concat(_this10.debugId, " \"").concat(dataSet.name, "\" root hash matching: ").concat(rootHash, ", version=").concat(dataSet.version));
1602
+ }
1603
+ }, delay);
1452
1604
  } else {
1453
1605
  _loggerProxy.default.logger.info("HashTreeParser#runSyncAlgorithm --> ".concat(this.debugId, " No delay for \"").concat(dataSet.name, "\" data set, skipping sync timer reset/setup"));
1454
1606
  }
@@ -1466,16 +1618,16 @@ var HashTreeParser = /*#__PURE__*/function () {
1466
1618
  }, {
1467
1619
  key: "resetHeartbeatWatchdogs",
1468
1620
  value: function resetHeartbeatWatchdogs(receivedDataSets) {
1469
- var _this12 = this;
1621
+ var _this11 = this;
1470
1622
  if (!this.heartbeatIntervalMs) {
1471
1623
  return;
1472
1624
  }
1473
- var _iterator9 = _createForOfIteratorHelper(receivedDataSets),
1474
- _step9;
1625
+ var _iterator0 = _createForOfIteratorHelper(receivedDataSets),
1626
+ _step0;
1475
1627
  try {
1476
1628
  var _loop2 = function _loop2() {
1477
- var receivedDataSet = _step9.value;
1478
- var dataSet = _this12.dataSets[receivedDataSet.name];
1629
+ var receivedDataSet = _step0.value;
1630
+ var dataSet = _this11.dataSets[receivedDataSet.name];
1479
1631
  if (!(dataSet !== null && dataSet !== void 0 && dataSet.hashTree)) {
1480
1632
  // eslint-disable-next-line no-continue
1481
1633
  return 1; // continue
@@ -1484,30 +1636,22 @@ var HashTreeParser = /*#__PURE__*/function () {
1484
1636
  clearTimeout(dataSet.heartbeatWatchdogTimer);
1485
1637
  dataSet.heartbeatWatchdogTimer = undefined;
1486
1638
  }
1487
- var backoffTime = _this12.getWeightedBackoffTime(dataSet.backoff);
1488
- var delay = _this12.heartbeatIntervalMs + backoffTime;
1489
- dataSet.heartbeatWatchdogTimer = setTimeout(/*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee7() {
1490
- return _regenerator.default.wrap(function (_context8) {
1491
- while (1) switch (_context8.prev = _context8.next) {
1492
- case 0:
1493
- dataSet.heartbeatWatchdogTimer = undefined;
1494
- _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"));
1495
- _context8.next = 1;
1496
- return _this12.performSync(dataSet, dataSet.hashTree.getRootHash(), "heartbeat watchdog expired");
1497
- case 1:
1498
- case "end":
1499
- return _context8.stop();
1500
- }
1501
- }, _callee7);
1502
- })), delay);
1639
+ var backoffTime = _this11.getWeightedBackoffTime(dataSet.backoff);
1640
+ var delay = _this11.heartbeatIntervalMs + backoffTime;
1641
+ dataSet.heartbeatWatchdogTimer = setTimeout(function () {
1642
+ dataSet.heartbeatWatchdogTimer = undefined;
1643
+ _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"));
1644
+ _this11.enqueueSyncForDataset(dataSet.name, "heartbeat watchdog expired");
1645
+ _this11.resetHeartbeatWatchdogs([dataSet]);
1646
+ }, delay);
1503
1647
  };
1504
- for (_iterator9.s(); !(_step9 = _iterator9.n()).done;) {
1648
+ for (_iterator0.s(); !(_step0 = _iterator0.n()).done;) {
1505
1649
  if (_loop2()) continue;
1506
1650
  }
1507
1651
  } catch (err) {
1508
- _iterator9.e(err);
1652
+ _iterator0.e(err);
1509
1653
  } finally {
1510
- _iterator9.f();
1654
+ _iterator0.f();
1511
1655
  }
1512
1656
  }
1513
1657
 
@@ -1541,6 +1685,7 @@ var HashTreeParser = /*#__PURE__*/function () {
1541
1685
  value: function stop() {
1542
1686
  _loggerProxy.default.logger.info("HashTreeParser#stop --> ".concat(this.debugId, " Stopping HashTreeParser, clearing timers and hash trees"));
1543
1687
  this.stopAllTimers();
1688
+ this.syncQueue = [];
1544
1689
  (0, _values.default)(this.dataSets).forEach(function (dataSet) {
1545
1690
  dataSet.hashTree = undefined;
1546
1691
  });
@@ -1549,29 +1694,41 @@ var HashTreeParser = /*#__PURE__*/function () {
1549
1694
  }
1550
1695
 
1551
1696
  /**
1552
- * Resumes the HashTreeParser that was previously stopped.
1697
+ * Cleans up the HashTreeParser, stopping all timers and clearing all internal state.
1698
+ * After calling this, the parser should not be used anymore.
1699
+ * @returns {void}
1700
+ */
1701
+ }, {
1702
+ key: "cleanUp",
1703
+ value: function cleanUp() {
1704
+ this.stop();
1705
+ this.dataSets = {};
1706
+ }
1707
+
1708
+ /**
1709
+ * Resumes the HashTreeParser that was previously stopped, using a hash tree message.
1553
1710
  * @param {HashTreeMessage} message - The message to resume with, it must contain metadata with visible data sets info
1554
1711
  * @returns {void}
1555
1712
  */
1556
1713
  }, {
1557
- key: "resume",
1558
- value: function resume(message) {
1559
- var _message$locusStateEl3, _metadataObject$data;
1714
+ key: "resumeFromMessage",
1715
+ value: function resumeFromMessage(message) {
1716
+ var _message$locusStateEl2, _metadataObject$data;
1560
1717
  // check that message contains metadata with visible data sets - this is essential to be able to resume
1561
- var metadataObject = (_message$locusStateEl3 = message.locusStateElements) === null || _message$locusStateEl3 === void 0 ? void 0 : _message$locusStateEl3.find(function (el) {
1718
+ var metadataObject = (_message$locusStateEl2 = message.locusStateElements) === null || _message$locusStateEl2 === void 0 ? void 0 : _message$locusStateEl2.find(function (el) {
1562
1719
  return (0, _utils.isMetadata)(el);
1563
1720
  });
1564
1721
  if (!(metadataObject !== null && metadataObject !== void 0 && (_metadataObject$data = metadataObject.data) !== null && _metadataObject$data !== void 0 && _metadataObject$data.visibleDataSets)) {
1565
- _loggerProxy.default.logger.warn("HashTreeParser#resume --> ".concat(this.debugId, " Cannot resume HashTreeParser because the message is missing metadata with visible data sets info"));
1722
+ _loggerProxy.default.logger.warn("HashTreeParser#resumeFromMessage --> ".concat(this.debugId, " Cannot resume HashTreeParser because the message is missing metadata with visible data sets info"));
1566
1723
  return;
1567
1724
  }
1568
1725
  this.setVisibleDataSets(metadataObject.data.visibleDataSets, message.dataSets);
1569
1726
  this.dataSets = {};
1570
- var _iterator0 = _createForOfIteratorHelper(message.dataSets),
1571
- _step0;
1727
+ var _iterator1 = _createForOfIteratorHelper(message.dataSets),
1728
+ _step1;
1572
1729
  try {
1573
- for (_iterator0.s(); !(_step0 = _iterator0.n()).done;) {
1574
- var dataSet = _step0.value;
1730
+ for (_iterator1.s(); !(_step1 = _iterator1.n()).done;) {
1731
+ var dataSet = _step1.value;
1575
1732
  var name = dataSet.name,
1576
1733
  leafCount = dataSet.leafCount;
1577
1734
  this.dataSets[name] = _objectSpread(_objectSpread({}, dataSet), {}, {
@@ -1579,16 +1736,47 @@ var HashTreeParser = /*#__PURE__*/function () {
1579
1736
  });
1580
1737
  }
1581
1738
  } catch (err) {
1582
- _iterator0.e(err);
1739
+ _iterator1.e(err);
1583
1740
  } finally {
1584
- _iterator0.f();
1741
+ _iterator1.f();
1585
1742
  }
1586
- _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) {
1743
+ _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) {
1587
1744
  return ds.name;
1588
1745
  }).join(', ')));
1589
1746
  this.state = 'active';
1590
1747
  this.handleMessage(message, 'on resume');
1591
1748
  }
1749
+
1750
+ /**
1751
+ * Resumes the HashTreeParser that was previously stopped, using a Locus API response.
1752
+ * Unlike resumeFromMessage(), this does not require metadata/dataSets in the input,
1753
+ * as it fetches all necessary information from Locus via initializeFromGetLociResponse.
1754
+ * @param {LocusDTO} locus - locus object from an API response
1755
+ * @returns {Promise}
1756
+ */
1757
+ }, {
1758
+ key: "resumeFromApiResponse",
1759
+ value: (function () {
1760
+ var _resumeFromApiResponse = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee9(locus) {
1761
+ return _regenerator.default.wrap(function (_context0) {
1762
+ while (1) switch (_context0.prev = _context0.next) {
1763
+ case 0:
1764
+ this.state = 'active';
1765
+ this.dataSets = {};
1766
+ _loggerProxy.default.logger.info("HashTreeParser#resumeFromApiResponse --> ".concat(this.debugId, " Resuming HashTreeParser from API response"));
1767
+ _context0.next = 1;
1768
+ return this.initializeFromGetLociResponse(locus);
1769
+ case 1:
1770
+ case "end":
1771
+ return _context0.stop();
1772
+ }
1773
+ }, _callee9, this);
1774
+ }));
1775
+ function resumeFromApiResponse(_x1) {
1776
+ return _resumeFromApiResponse.apply(this, arguments);
1777
+ }
1778
+ return resumeFromApiResponse;
1779
+ }())
1592
1780
  }, {
1593
1781
  key: "checkForSentinelHttpResponse",
1594
1782
  value: function checkForSentinelHttpResponse(error, dataSetName) {
@@ -1605,12 +1793,12 @@ var HashTreeParser = /*#__PURE__*/function () {
1605
1793
  * Gets the current hashes from the locus for a specific data set.
1606
1794
  * @param {string} dataSetName
1607
1795
  * @param {string} currentRootHash
1608
- * @returns {string[]}
1796
+ * @returns {Object|null} An object containing the hashes and leaf count, or null if the hashes match and no sync is needed
1609
1797
  */
1610
1798
  }, {
1611
1799
  key: "getHashesFromLocus",
1612
1800
  value: function getHashesFromLocus(dataSetName, currentRootHash) {
1613
- var _this13 = this;
1801
+ var _this12 = this;
1614
1802
  _loggerProxy.default.logger.info("HashTreeParser#getHashesFromLocus --> ".concat(this.debugId, " Requesting hashes for data set \"").concat(dataSetName, "\""));
1615
1803
  var dataSet = this.dataSets[dataSetName];
1616
1804
  var url = "".concat(dataSet.url, "/hashtree");
@@ -1622,20 +1810,25 @@ var HashTreeParser = /*#__PURE__*/function () {
1622
1810
  }
1623
1811
  }).then(function (response) {
1624
1812
  var _response$body, _response$body2;
1813
+ if (!response.body || (0, _lodash.isEmpty)(response.body)) {
1814
+ // 204 with empty body means our hashes match Locus, no sync needed
1815
+ _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"));
1816
+ return null;
1817
+ }
1625
1818
  var hashes = (_response$body = response.body) === null || _response$body === void 0 ? void 0 : _response$body.hashes;
1626
1819
  var dataSetFromResponse = (_response$body2 = response.body) === null || _response$body2 === void 0 ? void 0 : _response$body2.dataSet;
1627
1820
  if (!hashes || !(0, _isArray.default)(hashes)) {
1628
- _loggerProxy.default.logger.warn("HashTreeParser#getHashesFromLocus --> ".concat(_this13.debugId, " Locus returned invalid hashes, response body="), response.body);
1821
+ _loggerProxy.default.logger.warn("HashTreeParser#getHashesFromLocus --> ".concat(_this12.debugId, " Locus returned invalid hashes, response body="), response.body);
1629
1822
  throw new Error("Locus returned invalid hashes: ".concat(hashes));
1630
1823
  }
1631
- _loggerProxy.default.logger.info("HashTreeParser#getHashesFromLocus --> ".concat(_this13.debugId, " Received hashes for data set \"").concat(dataSetName, "\": ").concat((0, _stringify.default)(hashes)));
1824
+ _loggerProxy.default.logger.info("HashTreeParser#getHashesFromLocus --> ".concat(_this12.debugId, " Received hashes for data set \"").concat(dataSetName, "\": ").concat((0, _stringify.default)(hashes)));
1632
1825
  return {
1633
1826
  hashes: hashes,
1634
1827
  dataSet: dataSetFromResponse
1635
1828
  };
1636
1829
  }).catch(function (error) {
1637
- _loggerProxy.default.logger.error("HashTreeParser#getHashesFromLocus --> ".concat(_this13.debugId, " Error ").concat(error.statusCode, " fetching hashes for data set \"").concat(dataSetName, "\":"), error);
1638
- _this13.checkForSentinelHttpResponse(error, dataSet.name);
1830
+ _loggerProxy.default.logger.error("HashTreeParser#getHashesFromLocus --> ".concat(_this12.debugId, " Error ").concat(error.statusCode, " fetching hashes for data set \"").concat(dataSetName, "\":"), error);
1831
+ _this12.checkForSentinelHttpResponse(error, dataSet.name);
1639
1832
  throw error;
1640
1833
  });
1641
1834
  }
@@ -1644,25 +1837,36 @@ var HashTreeParser = /*#__PURE__*/function () {
1644
1837
  * Sends a sync request to Locus for the specified data set.
1645
1838
  *
1646
1839
  * @param {InternalDataSet} dataSet The data set to sync.
1647
- * @param {Record<number, LeafDataItem[]>} mismatchedLeavesData The mismatched leaves data to include in the sync request.
1840
+ * @param {Object} options Either `{ isInitialization: true }` for init syncs (uses leafCount=1 with empty leaf data) or `{ mismatchedLeavesData }` for normal syncs.
1648
1841
  * @returns {Promise<HashTreeMessage|null>}
1649
1842
  */
1650
1843
  }, {
1651
1844
  key: "sendSyncRequestToLocus",
1652
- value: function sendSyncRequestToLocus(dataSet, mismatchedLeavesData) {
1653
- var _this14 = this;
1845
+ value: function sendSyncRequestToLocus(dataSet, options) {
1846
+ var _this13 = this;
1654
1847
  _loggerProxy.default.logger.info("HashTreeParser#sendSyncRequestToLocus --> ".concat(this.debugId, " Sending sync request for data set \"").concat(dataSet.name, "\""));
1848
+ var isInitialization = 'isInitialization' in options;
1655
1849
  var url = "".concat(dataSet.url, "/sync");
1656
1850
  var body = {
1657
- leafCount: dataSet.leafCount,
1851
+ leafCount: isInitialization ? 1 : dataSet.leafCount,
1658
1852
  leafDataEntries: []
1659
1853
  };
1660
- (0, _keys.default)(mismatchedLeavesData).forEach(function (index) {
1854
+ if (isInitialization) {
1855
+ // initialization sync: Locus requires leafCount=1 with a single empty leaf
1661
1856
  body.leafDataEntries.push({
1662
- leafIndex: (0, _parseInt2.default)(index, 10),
1663
- elementIds: mismatchedLeavesData[index]
1857
+ leafIndex: 0,
1858
+ elementIds: []
1664
1859
  });
1665
- });
1860
+ } else {
1861
+ var mismatchedLeavesData = options.mismatchedLeavesData;
1862
+ (0, _keys.default)(mismatchedLeavesData).forEach(function (index) {
1863
+ var leafIndex = (0, _parseInt2.default)(index, 10);
1864
+ body.leafDataEntries.push({
1865
+ leafIndex: leafIndex,
1866
+ elementIds: mismatchedLeavesData[leafIndex]
1867
+ });
1868
+ });
1869
+ }
1666
1870
  var ourCurrentRootHash = dataSet.hashTree ? dataSet.hashTree.getRootHash() : _constants2.EMPTY_HASH;
1667
1871
  return this.webexRequest({
1668
1872
  method: _constants.HTTP_VERBS.POST,
@@ -1672,15 +1876,15 @@ var HashTreeParser = /*#__PURE__*/function () {
1672
1876
  },
1673
1877
  body: body
1674
1878
  }).then(function (resp) {
1675
- _loggerProxy.default.logger.info("HashTreeParser#sendSyncRequestToLocus --> ".concat(_this14.debugId, " Sync request succeeded for \"").concat(dataSet.name, "\""));
1879
+ _loggerProxy.default.logger.info("HashTreeParser#sendSyncRequestToLocus --> ".concat(_this13.debugId, " Sync request succeeded for \"").concat(dataSet.name, "\""));
1676
1880
  if (!resp.body || (0, _lodash.isEmpty)(resp.body)) {
1677
- _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"));
1881
+ _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"));
1678
1882
  return null;
1679
1883
  }
1680
1884
  return resp.body;
1681
1885
  }).catch(function (error) {
1682
- _loggerProxy.default.logger.error("HashTreeParser#sendSyncRequestToLocus --> ".concat(_this14.debugId, " Error ").concat(error.statusCode, " sending sync request for data set \"").concat(dataSet.name, "\":"), error);
1683
- _this14.checkForSentinelHttpResponse(error, dataSet.name);
1886
+ _loggerProxy.default.logger.error("HashTreeParser#sendSyncRequestToLocus --> ".concat(_this13.debugId, " Error ").concat(error.statusCode, " sending sync request for data set \"").concat(dataSet.name, "\":"), error);
1887
+ _this13.checkForSentinelHttpResponse(error, dataSet.name);
1684
1888
  throw error;
1685
1889
  });
1686
1890
  }