@twin.org/synchronised-storage-service 0.0.1-next.2 → 0.0.1-next.3

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.
@@ -22,9 +22,9 @@ exports.SyncSnapshotEntry = class SyncSnapshotEntry {
22
22
  */
23
23
  id;
24
24
  /**
25
- * The schema type for the snapshot i.e. which entity is being synchronized.
25
+ * The storage key for the snapshot i.e. which entity is being synchronized.
26
26
  */
27
- schemaType;
27
+ storageKey;
28
28
  /**
29
29
  * The date the snapshot was created.
30
30
  */
@@ -44,7 +44,7 @@ exports.SyncSnapshotEntry = class SyncSnapshotEntry {
44
44
  /**
45
45
  * The changes that were made in this snapshot, if this is a local snapshot.
46
46
  */
47
- localChanges;
47
+ changes;
48
48
  };
49
49
  __decorate([
50
50
  entity.property({ type: "string", isPrimary: true }),
@@ -53,7 +53,7 @@ __decorate([
53
53
  __decorate([
54
54
  entity.property({ type: "string", isSecondary: true }),
55
55
  __metadata("design:type", String)
56
- ], exports.SyncSnapshotEntry.prototype, "schemaType", void 0);
56
+ ], exports.SyncSnapshotEntry.prototype, "storageKey", void 0);
57
57
  __decorate([
58
58
  entity.property({ type: "string" }),
59
59
  __metadata("design:type", String)
@@ -73,7 +73,7 @@ __decorate([
73
73
  __decorate([
74
74
  entity.property({ type: "array", itemType: "object", optional: true }),
75
75
  __metadata("design:type", Array)
76
- ], exports.SyncSnapshotEntry.prototype, "localChanges", void 0);
76
+ ], exports.SyncSnapshotEntry.prototype, "changes", void 0);
77
77
  exports.SyncSnapshotEntry = __decorate([
78
78
  entity.entity()
79
79
  ], exports.SyncSnapshotEntry);
@@ -231,15 +231,14 @@ class ChangeSetHelper {
231
231
  /**
232
232
  * Apply a sync changeset.
233
233
  * @param changeSetStorageId The id of the sync changeset to apply.
234
- * @returns True if the change was applied.
234
+ * @returns The changeset if it existed.
235
235
  */
236
236
  async getAndApplyChangeset(changeSetStorageId) {
237
237
  const syncChangeset = await this.getAndVerifyChangeset(changeSetStorageId);
238
238
  if (!core.Is.empty(syncChangeset)) {
239
239
  await this.applyChangeset(syncChangeset);
240
- return true;
241
240
  }
242
- return false;
241
+ return syncChangeset;
243
242
  }
244
243
  /**
245
244
  * Apply a sync changeset.
@@ -266,7 +265,7 @@ class ChangeSetHelper {
266
265
  // so we need to restore it here.
267
266
  change.entity.nodeIdentity = syncChangeset.nodeIdentity;
268
267
  await this._eventBusComponent.publish(synchronisedStorageModels.SynchronisedStorageTopics.RemoteItemSet, {
269
- schemaType: syncChangeset.schemaType,
268
+ storageKey: syncChangeset.storageKey,
270
269
  entity: change.entity
271
270
  });
272
271
  }
@@ -274,7 +273,7 @@ class ChangeSetHelper {
274
273
  case synchronisedStorageModels.SyncChangeOperation.Delete:
275
274
  if (!core.Is.empty(change.id)) {
276
275
  await this._eventBusComponent.publish(synchronisedStorageModels.SynchronisedStorageTopics.RemoteItemRemove, {
277
- schemaType: syncChangeset.schemaType,
276
+ storageKey: syncChangeset.storageKey,
278
277
  id: change.id
279
278
  });
280
279
  }
@@ -286,9 +285,10 @@ class ChangeSetHelper {
286
285
  /**
287
286
  * Store the changeset.
288
287
  * @param syncChangeSet The sync change set to store.
288
+ * @param nodeIdentity The node identity to use for the changeset.
289
289
  * @returns The id of the change set.
290
290
  */
291
- async storeChangeSet(syncChangeSet) {
291
+ async storeChangeSet(syncChangeSet, nodeIdentity) {
292
292
  await this._logging?.log({
293
293
  level: "info",
294
294
  source: this.CLASS_NAME,
@@ -302,7 +302,7 @@ class ChangeSetHelper {
302
302
  return this._blobStorageComponent.create(core.Converter.bytesToBase64(core.ObjectHelper.toBytes(syncChangeSet)), undefined, undefined, undefined, {
303
303
  disableEncryption: true,
304
304
  compress: blobStorageModels.BlobStorageCompressionType.Gzip
305
- });
305
+ }, undefined, nodeIdentity);
306
306
  }
307
307
  /**
308
308
  * Verify the proof of a sync changeset.
@@ -356,7 +356,7 @@ class ChangeSetHelper {
356
356
  delete changeSetWithoutProof.proof;
357
357
  const proof = await this._identityConnector.createProof(syncChangeset.nodeIdentity, identityModels.DocumentHelper.joinId(syncChangeset.nodeIdentity, this._decentralisedStorageMethodId), standardsW3cDid.ProofTypes.DataIntegrityProof, changeSetWithoutProof);
358
358
  await this._logging?.log({
359
- level: "error",
359
+ level: "info",
360
360
  source: this.CLASS_NAME,
361
361
  message: "createdChangeSetProof",
362
362
  data: {
@@ -406,33 +406,51 @@ class LocalSyncStateHelper {
406
406
  }
407
407
  /**
408
408
  * Add a new change to the local snapshot.
409
- * @param schemaType The schema type of the snapshot to add the change for.
409
+ * @param storageKey The storage key of the snapshot to add the change for.
410
410
  * @param operation The operation to perform.
411
411
  * @param id The id of the entity to add the change for.
412
412
  * @returns Nothing.
413
413
  */
414
- async addLocalChange(schemaType, operation, id) {
415
- const localChangeSnapshot = await this.getLocalChangeSnapshot(schemaType);
416
- localChangeSnapshot.localChanges ??= [];
414
+ async addLocalChange(storageKey, operation, id) {
415
+ await this._logging?.log({
416
+ level: "info",
417
+ source: this.CLASS_NAME,
418
+ message: "addLocalChange",
419
+ data: {
420
+ storageKey,
421
+ operation,
422
+ id
423
+ }
424
+ });
425
+ const localChangeSnapshot = await this.getLocalChangeSnapshot(storageKey);
426
+ localChangeSnapshot.changes ??= [];
417
427
  // If we already have a change for this id we are
418
428
  // about to supersede it, we remove the previous change
419
429
  // to avoid having multiple changes for the same id
420
- const previousChangeIndex = localChangeSnapshot.localChanges.findIndex(change => change.id === id);
430
+ const previousChangeIndex = localChangeSnapshot.changes.findIndex(change => change.id === id);
421
431
  if (previousChangeIndex !== -1) {
422
- localChangeSnapshot.localChanges.splice(previousChangeIndex, 1);
432
+ localChangeSnapshot.changes.splice(previousChangeIndex, 1);
423
433
  }
424
- if (localChangeSnapshot.localChanges.length > 0) {
434
+ if (localChangeSnapshot.changes.length > 0) {
425
435
  localChangeSnapshot.dateModified = new Date(Date.now()).toISOString();
426
436
  }
427
- localChangeSnapshot.localChanges.push({ operation, id });
437
+ localChangeSnapshot.changes.push({ operation, id });
428
438
  await this.setLocalChangeSnapshot(localChangeSnapshot);
429
439
  }
430
440
  /**
431
441
  * Get the current local snapshot.
432
- * @param schemaType The schema type of the snapshot to get.
442
+ * @param storageKey The storage key of the snapshot to get.
433
443
  * @returns The local snapshot entry.
434
444
  */
435
- async getLocalChangeSnapshot(schemaType) {
445
+ async getLocalChangeSnapshot(storageKey) {
446
+ await this._logging?.log({
447
+ level: "info",
448
+ source: this.CLASS_NAME,
449
+ message: "getLocalChangeSnapshot",
450
+ data: {
451
+ storageKey
452
+ }
453
+ });
436
454
  const queryResult = await this._localSyncSnapshotEntryEntityStorage.query({
437
455
  conditions: [
438
456
  {
@@ -441,18 +459,34 @@ class LocalSyncStateHelper {
441
459
  comparison: entity.ComparisonOperator.Equals
442
460
  },
443
461
  {
444
- property: "schemaType",
445
- value: schemaType,
462
+ property: "storageKey",
463
+ value: storageKey,
446
464
  comparison: entity.ComparisonOperator.Equals
447
465
  }
448
466
  ]
449
467
  });
450
468
  if (queryResult.entities.length > 0) {
469
+ await this._logging?.log({
470
+ level: "info",
471
+ source: this.CLASS_NAME,
472
+ message: "localChangeSnapshotExists",
473
+ data: {
474
+ storageKey
475
+ }
476
+ });
451
477
  return queryResult.entities[0];
452
478
  }
479
+ await this._logging?.log({
480
+ level: "info",
481
+ source: this.CLASS_NAME,
482
+ message: "localChangeSnapshotDoesNotExist",
483
+ data: {
484
+ storageKey
485
+ }
486
+ });
453
487
  return {
454
488
  id: core.Converter.bytesToHex(core.RandomHelper.generate(32)),
455
- schemaType,
489
+ storageKey,
456
490
  dateCreated: new Date(Date.now()).toISOString(),
457
491
  changeSetStorageIds: [],
458
492
  isLocalSnapshot: true
@@ -464,6 +498,14 @@ class LocalSyncStateHelper {
464
498
  * @returns Nothing.
465
499
  */
466
500
  async setLocalChangeSnapshot(localChangeSnapshot) {
501
+ await this._logging?.log({
502
+ level: "info",
503
+ source: this.CLASS_NAME,
504
+ message: "setLocalChangeSnapshot",
505
+ data: {
506
+ storageKey: localChangeSnapshot.storageKey
507
+ }
508
+ });
467
509
  await this._localSyncSnapshotEntryEntityStorage.set(localChangeSnapshot);
468
510
  }
469
511
  /**
@@ -484,11 +526,11 @@ class LocalSyncStateHelper {
484
526
  }
485
527
  /**
486
528
  * Sync local data using a remote sync state.
487
- * @param schemaType The schema type of the snapshot to sync with.
529
+ * @param storageKey The storage key of the snapshot to sync with.
488
530
  * @param remoteSyncState The sync state to sync with.
489
531
  * @returns Nothing.
490
532
  */
491
- async syncFromRemote(schemaType, remoteSyncState) {
533
+ async syncFromRemote(storageKey, remoteSyncState) {
492
534
  await this._logging?.log({
493
535
  level: "info",
494
536
  source: this.CLASS_NAME,
@@ -514,7 +556,7 @@ class LocalSyncStateHelper {
514
556
  const localSnapshot = await this._localSyncSnapshotEntryEntityStorage.get(remoteSnapshot.id);
515
557
  const remoteSnapshotWithContext = {
516
558
  ...remoteSnapshot,
517
- schemaType
559
+ storageKey
518
560
  };
519
561
  if (core.Is.empty(localSnapshot)) {
520
562
  // We don't have the snapshot locally, so we need to process it
@@ -633,12 +675,12 @@ class RemoteSyncStateHelper {
633
675
  */
634
676
  _changeSetHelper;
635
677
  /**
636
- * The storage ids of the batch responses for each schema type.
678
+ * The storage ids of the batch responses for each storage key.
637
679
  * @internal
638
680
  */
639
681
  _batchResponseStorageIds;
640
682
  /**
641
- * The full changes for each schema type.
683
+ * The full changes for each storage key.
642
684
  * @internal
643
685
  */
644
686
  _populateFullChanges;
@@ -686,52 +728,73 @@ class RemoteSyncStateHelper {
686
728
  }
687
729
  /**
688
730
  * Create and store a change set.
689
- * @param schemaType The schema type of the change set.
731
+ * @param storageKey The storage key of the change set.
690
732
  * @param changes The changes to apply.
691
733
  * @param completeCallback The callback to call when the changeset is created and stored.
692
734
  * @returns The storage id of the change set if created.
693
735
  */
694
- async createAndStoreChangeSet(schemaType, changes, completeCallback) {
695
- if (core.Is.arrayValue(changes)) {
696
- this._populateFullChanges[schemaType] = {
697
- changes,
698
- entities: {},
699
- requestIds: [],
700
- completeCallback: async () => this.finaliseFullChanges(schemaType, completeCallback)
701
- };
702
- const setChanges = changes.filter(c => c.operation === synchronisedStorageModels.SyncChangeOperation.Set);
703
- if (setChanges.length === 0) {
704
- // If we don't need to request any full details, we can just call the complete callback
705
- await this.finaliseFullChanges(schemaType, completeCallback);
706
- }
707
- else {
708
- // Otherwise we need to request the full details for each change
709
- this._populateFullChanges[schemaType].requestIds = setChanges.map(change => change.id);
710
- // Once all the requests are handled the callback will be called
711
- for (const change of setChanges) {
712
- // Create a request for each change to populate the full details
713
- this._eventBusComponent.publish(synchronisedStorageModels.SynchronisedStorageTopics.LocalItemRequest, {
714
- schemaType,
715
- id: change.id
716
- });
717
- }
736
+ async createAndStoreChangeSet(storageKey, changes, completeCallback) {
737
+ await this._logging?.log({
738
+ level: "info",
739
+ source: this.CLASS_NAME,
740
+ message: "createAndStoreChangeSet",
741
+ data: {
742
+ storageKey,
743
+ changeCount: changes.length
718
744
  }
745
+ });
746
+ this._populateFullChanges[storageKey] = {
747
+ changes,
748
+ entities: {},
749
+ requestIds: [],
750
+ completeCallback: async () => this.finaliseFullChanges(storageKey, completeCallback)
751
+ };
752
+ const setChanges = changes.filter(c => c.operation === synchronisedStorageModels.SyncChangeOperation.Set);
753
+ if (setChanges.length === 0) {
754
+ // If we don't need to request any full details, we can just call the complete callback
755
+ await this.finaliseFullChanges(storageKey, completeCallback);
719
756
  }
720
757
  else {
721
- await completeCallback();
758
+ // Otherwise we need to request the full details for each change
759
+ this._populateFullChanges[storageKey].requestIds = setChanges.map(change => change.id);
760
+ // Once all the requests are handled the callback will be called
761
+ for (const change of setChanges) {
762
+ // Create a request for each change to populate the full details
763
+ await this._logging?.log({
764
+ level: "info",
765
+ source: this.CLASS_NAME,
766
+ message: "createChangeSetRequestingItem",
767
+ data: {
768
+ storageKey,
769
+ id: change.id
770
+ }
771
+ });
772
+ this._eventBusComponent.publish(synchronisedStorageModels.SynchronisedStorageTopics.LocalItemRequest, {
773
+ storageKey,
774
+ id: change.id
775
+ });
776
+ }
722
777
  }
723
778
  }
724
779
  /**
725
780
  * Finalise the full details for the sync change set.
726
- * @param schemaType The schema type of the change set.
781
+ * @param storageKey The storage key of the change set.
727
782
  * @param completeCallback The callback to call when the changeset is populated.
728
783
  * @returns Nothing.
729
784
  */
730
- async finaliseFullChanges(schemaType, completeCallback) {
785
+ async finaliseFullChanges(storageKey, completeCallback) {
786
+ await this._logging?.log({
787
+ level: "info",
788
+ source: this.CLASS_NAME,
789
+ message: "finalisingSyncChanges",
790
+ data: {
791
+ storageKey
792
+ }
793
+ });
731
794
  if (core.Is.stringValue(this._nodeIdentity)) {
732
- const changes = this._populateFullChanges[schemaType].changes;
795
+ const changes = this._populateFullChanges[storageKey].changes;
733
796
  for (const change of changes) {
734
- change.entity = this._populateFullChanges[schemaType].entities[change.id] ?? change.entity;
797
+ change.entity = this._populateFullChanges[storageKey].entities[change.id] ?? change.entity;
735
798
  if (change.operation === synchronisedStorageModels.SyncChangeOperation.Set && core.Is.objectValue(change.entity)) {
736
799
  // Remove the node identity as the changeset has this stored at the top level
737
800
  // and we do not want to store it in the change itself to reduce redundancy
@@ -742,15 +805,29 @@ class RemoteSyncStateHelper {
742
805
  const syncChangeSet = {
743
806
  id: core.Converter.bytesToHex(core.RandomHelper.generate(32)),
744
807
  dateCreated: new Date(Date.now()).toISOString(),
745
- schemaType,
808
+ storageKey,
746
809
  changes,
747
810
  nodeIdentity: this._nodeIdentity
748
811
  };
749
- // And sign it with the node identity
750
- syncChangeSet.proof = await this._changeSetHelper.createChangeSetProof(syncChangeSet);
751
- // Store the changeset in the blob storage
752
- const changeSetStorageId = await this._changeSetHelper.storeChangeSet(syncChangeSet);
753
- await completeCallback(changeSetStorageId);
812
+ try {
813
+ // And sign it with the node identity
814
+ syncChangeSet.proof = await this._changeSetHelper.createChangeSetProof(syncChangeSet);
815
+ // Store the changeset in the blob storage
816
+ const changeSetStorageId = await this._changeSetHelper.storeChangeSet(syncChangeSet, this._nodeIdentity);
817
+ await completeCallback(changeSetStorageId);
818
+ }
819
+ catch (err) {
820
+ await this._logging?.log({
821
+ level: "error",
822
+ source: this.CLASS_NAME,
823
+ message: "finalisingSyncChangesFailed",
824
+ data: {
825
+ storageKey
826
+ },
827
+ error: core.BaseError.fromError(err)
828
+ });
829
+ await completeCallback();
830
+ }
754
831
  }
755
832
  else {
756
833
  await completeCallback();
@@ -758,15 +835,25 @@ class RemoteSyncStateHelper {
758
835
  }
759
836
  /**
760
837
  * Add a new changeset into the sync state.
838
+ * @param storageKey The storage key of the change set to add.
761
839
  * @param changeSetStorageId The id of the change set to add the the current state
762
840
  * @returns Nothing.
763
841
  */
764
- async addChangeSetToSyncState(changeSetStorageId) {
765
- // First load the current sync state if there is one
766
- const syncStatePointer = await this.getVerifiableSyncPointer();
842
+ async addChangeSetToSyncState(storageKey, changeSetStorageId) {
843
+ await this._logging?.log({
844
+ level: "info",
845
+ source: this.CLASS_NAME,
846
+ message: "addChangeSetToSyncState",
847
+ data: {
848
+ storageKey,
849
+ changeSetStorageId
850
+ }
851
+ });
852
+ // First load the sync pointer store to get the current sync pointer for the storage key
853
+ const syncPointerStore = await this.getVerifiableSyncPointerStore();
767
854
  let syncState;
768
- if (!core.Is.empty(syncStatePointer?.syncPointerId)) {
769
- syncState = await this.getRemoteSyncState(syncStatePointer.syncPointerId);
855
+ if (!core.Is.empty(syncPointerStore.syncPointers[storageKey])) {
856
+ syncState = await this.getRemoteSyncState(syncPointerStore.syncPointers[storageKey]);
770
857
  }
771
858
  // No current sync state, so we create a new one
772
859
  if (core.Is.empty(syncState)) {
@@ -791,34 +878,34 @@ class RemoteSyncStateHelper {
791
878
  // Add the changeset storage id to the current snapshot
792
879
  currentSnapshot.changeSetStorageIds.push(changeSetStorageId);
793
880
  // Store the sync state in the blob storage
794
- const syncStateId = await this.storeRemoteSyncState(syncState);
795
- // Store the verifiable sync pointer in the verifiable storage
796
- await this.storeVerifiableSyncPointer(syncStateId);
881
+ syncPointerStore.syncPointers[storageKey] = await this.storeRemoteSyncState(syncState);
882
+ // Store the verifiable sync pointer store in the verifiable storage
883
+ await this.storeVerifiableSyncPointerStore(syncPointerStore);
797
884
  }
798
885
  /**
799
886
  * Create a consolidated snapshot for the entire storage.
800
- * @param schemaType The schema type of the snapshot to create.
887
+ * @param storageKey The storage key of the snapshot to create.
801
888
  * @param batchSize The batch size to use for consolidation.
802
889
  * @returns Nothing.
803
890
  */
804
- async consolidateFromLocal(schemaType, batchSize) {
891
+ async consolidateFromLocal(storageKey, batchSize) {
805
892
  await this._logging?.log({
806
893
  level: "info",
807
894
  source: this.CLASS_NAME,
808
895
  message: "consolidationStarting"
809
896
  });
810
- await this._eventBusComponent.publish(synchronisedStorageModels.SynchronisedStorageTopics.BatchRequest, { schemaType, batchSize });
897
+ await this._eventBusComponent.publish(synchronisedStorageModels.SynchronisedStorageTopics.BatchRequest, { storageKey, batchSize });
811
898
  }
812
899
  /**
813
- * Get the sync pointer.
814
- * @returns The sync pointer.
900
+ * Get the sync pointer store.
901
+ * @returns The sync pointer store.
815
902
  */
816
- async getVerifiableSyncPointer() {
903
+ async getVerifiableSyncPointerStore() {
817
904
  try {
818
905
  await this._logging?.log({
819
906
  level: "info",
820
907
  source: this.CLASS_NAME,
821
- message: "verifiableSyncPointerRetrieving",
908
+ message: "verifiableSyncPointerStoreRetrieving",
822
909
  data: {
823
910
  key: this._synchronisedStorageKey
824
911
  }
@@ -829,10 +916,9 @@ class RemoteSyncStateHelper {
829
916
  await this._logging?.log({
830
917
  level: "info",
831
918
  source: this.CLASS_NAME,
832
- message: "verifiableSyncPointerRetrieved",
919
+ message: "verifiableSyncPointerStoreRetrieved",
833
920
  data: {
834
- key: this._synchronisedStorageKey,
835
- syncPointerId: syncPointer.syncPointerId
921
+ key: this._synchronisedStorageKey
836
922
  }
837
923
  });
838
924
  return syncPointer;
@@ -846,34 +932,34 @@ class RemoteSyncStateHelper {
846
932
  await this._logging?.log({
847
933
  level: "info",
848
934
  source: this.CLASS_NAME,
849
- message: "verifiableSyncPointerNotFound",
935
+ message: "verifiableSyncPointerStoreNotFound",
850
936
  data: {
851
937
  key: this._synchronisedStorageKey
852
938
  }
853
939
  });
940
+ // If no sync pointer store exists, we return an empty one
941
+ return {
942
+ syncPointers: {}
943
+ };
854
944
  }
855
945
  /**
856
946
  * Store the verifiable sync pointer in the verifiable storage.
857
- * @param syncStateId The id of the sync state to store.
947
+ * @param syncPointerStore The sync pointer store to store.
858
948
  * @returns Nothing.
859
949
  */
860
- async storeVerifiableSyncPointer(syncStateId) {
861
- // Create a new verifiable sync pointer object pointing to the sync state
862
- const verifiableSyncPointer = {
863
- syncPointerId: syncStateId
864
- };
865
- await this._logging?.log({
866
- level: "info",
867
- source: this.CLASS_NAME,
868
- message: "verifiableSyncPointerStoring",
869
- data: {
870
- key: this._synchronisedStorageKey,
871
- syncPointerId: verifiableSyncPointer.syncPointerId
872
- }
873
- });
874
- // Store the verifiable sync pointer in the verifiable storage
875
- await this._verifiableSyncPointerStorageConnector.create(this._synchronisedStorageKey, core.ObjectHelper.toBytes(verifiableSyncPointer));
876
- return verifiableSyncPointer;
950
+ async storeVerifiableSyncPointerStore(syncPointerStore) {
951
+ if (this._nodeIdentity) {
952
+ await this._logging?.log({
953
+ level: "info",
954
+ source: this.CLASS_NAME,
955
+ message: "verifiableSyncPointerStoreStoring",
956
+ data: {
957
+ key: this._synchronisedStorageKey
958
+ }
959
+ });
960
+ // Store the verifiable sync pointer in the verifiable storage
961
+ await this._verifiableSyncPointerStorageConnector.update(this._nodeIdentity, this._synchronisedStorageKey, core.ObjectHelper.toBytes(syncPointerStore));
962
+ }
877
963
  }
878
964
  /**
879
965
  * Store the remote sync state.
@@ -891,7 +977,7 @@ class RemoteSyncStateHelper {
891
977
  });
892
978
  // We don't want to encrypt the sync state as no other nodes would be able to read it
893
979
  // the blob storage also needs to be publicly accessible so that other nodes can retrieve it
894
- return this._blobStorageComponent.create(core.Converter.bytesToBase64(core.ObjectHelper.toBytes(syncState)), undefined, undefined, undefined, { disableEncryption: true, compress: blobStorageModels.BlobStorageCompressionType.Gzip });
980
+ return this._blobStorageComponent.create(core.Converter.bytesToBase64(core.ObjectHelper.toBytes(syncState)), undefined, undefined, undefined, { disableEncryption: true, compress: blobStorageModels.BlobStorageCompressionType.Gzip }, undefined, this._nodeIdentity);
895
981
  }
896
982
  /**
897
983
  * Get the remote sync state.
@@ -910,7 +996,7 @@ class RemoteSyncStateHelper {
910
996
  });
911
997
  const blobEntry = await this._blobStorageComponent.get(syncPointerId, {
912
998
  includeContent: true
913
- });
999
+ }, undefined, this._nodeIdentity);
914
1000
  if (core.Is.stringBase64(blobEntry.blob)) {
915
1001
  const syncState = core.ObjectHelper.fromBytes(core.Converter.base64ToBytes(blobEntry.blob));
916
1002
  await this._logging?.log({
@@ -953,28 +1039,31 @@ class RemoteSyncStateHelper {
953
1039
  operation: synchronisedStorageModels.SyncChangeOperation.Set,
954
1040
  id: change[response.primaryKey]
955
1041
  })),
956
- schemaType: response.schemaType,
1042
+ storageKey: response.storageKey,
957
1043
  nodeIdentity: this._nodeIdentity
958
1044
  };
959
1045
  // And sign it with the node identity
960
1046
  syncChangeSet.proof = await this._changeSetHelper.createChangeSetProof(syncChangeSet);
961
1047
  // Store the changeset in the blob storage
962
- const changeSetStorageId = await this._changeSetHelper.storeChangeSet(syncChangeSet);
1048
+ const changeSetStorageId = await this._changeSetHelper.storeChangeSet(syncChangeSet, this._nodeIdentity);
963
1049
  // Add the changeset storage id to the snapshot ids
964
- this._batchResponseStorageIds[response.schemaType] ??= [];
965
- this._batchResponseStorageIds[response.schemaType].push(changeSetStorageId);
1050
+ this._batchResponseStorageIds[response.storageKey] ??= [];
1051
+ this._batchResponseStorageIds[response.storageKey].push(changeSetStorageId);
966
1052
  if (response.lastEntry) {
967
1053
  const syncState = { snapshots: [] };
968
1054
  const batchSnapshot = {
969
1055
  id: core.Converter.bytesToHex(core.RandomHelper.generate(32)),
970
1056
  dateCreated: new Date(Date.now()).toISOString(),
971
- changeSetStorageIds: this._batchResponseStorageIds[response.schemaType]
1057
+ changeSetStorageIds: this._batchResponseStorageIds[response.storageKey]
972
1058
  };
973
1059
  syncState.snapshots.push(batchSnapshot);
974
1060
  // Store the sync state in the blob storage
975
1061
  const syncStateId = await this.storeRemoteSyncState(syncState);
1062
+ // Get the current sync pointer store
1063
+ const syncPointerStore = await this.getVerifiableSyncPointerStore();
1064
+ syncPointerStore.syncPointers[response.storageKey] = syncStateId;
976
1065
  // Store the verifiable sync pointer in the verifiable storage
977
- await this.storeVerifiableSyncPointer(syncStateId);
1066
+ await this.storeVerifiableSyncPointerStore(syncPointerStore);
978
1067
  await this._logging?.log({
979
1068
  level: "info",
980
1069
  source: this.CLASS_NAME,
@@ -988,13 +1077,22 @@ class RemoteSyncStateHelper {
988
1077
  * @param response The item response to handle.
989
1078
  */
990
1079
  async handleLocalItemResponse(response) {
991
- if (!core.Is.empty(this._populateFullChanges[response.schemaType])) {
992
- const idx = this._populateFullChanges[response.schemaType].requestIds.indexOf(response.id);
1080
+ await this._logging?.log({
1081
+ level: "info",
1082
+ source: this.CLASS_NAME,
1083
+ message: "createChangeSetRespondingItem",
1084
+ data: {
1085
+ storageKey: response.storageKey,
1086
+ id: response.id
1087
+ }
1088
+ });
1089
+ if (!core.Is.empty(this._populateFullChanges[response.storageKey])) {
1090
+ const idx = this._populateFullChanges[response.storageKey].requestIds.indexOf(response.id);
993
1091
  if (idx !== -1) {
994
- this._populateFullChanges[response.schemaType].requestIds.splice(idx, 1);
995
- this._populateFullChanges[response.schemaType].entities[response.id] = response.entity;
996
- if (this._populateFullChanges[response.schemaType].requestIds.length === 0) {
997
- await this._populateFullChanges[response.schemaType].completeCallback();
1092
+ this._populateFullChanges[response.storageKey].requestIds.splice(idx, 1);
1093
+ this._populateFullChanges[response.storageKey].entities[response.id] = response.entity;
1094
+ if (this._populateFullChanges[response.storageKey].requestIds.length === 0) {
1095
+ await this._populateFullChanges[response.storageKey].completeCallback();
998
1096
  }
999
1097
  }
1000
1098
  }
@@ -1006,20 +1104,20 @@ class RemoteSyncStateHelper {
1006
1104
  */
1007
1105
  class SynchronisedStorageService {
1008
1106
  /**
1009
- * The default interval to check for entity updates, defaults to 5 mins.
1107
+ * The default interval to check for entity updates.
1010
1108
  * @internal
1011
1109
  */
1012
- static _DEFAULT_ENTITY_UPDATE_INTERVAL_MS = 300000;
1110
+ static _DEFAULT_ENTITY_UPDATE_INTERVAL_MINUTES = 5;
1013
1111
  /**
1014
- * The default interval to perform consolidation, defaults to 60 mins.
1112
+ * The default interval to perform consolidation.
1015
1113
  * @internal
1016
1114
  */
1017
- static _DEFAULT_CONSOLIDATION_INTERVAL_MS = 3600000;
1115
+ static _DEFAULT_CONSOLIDATION_INTERVAL_MINUTES = 60;
1018
1116
  /**
1019
1117
  * The default size of a consolidation batch.
1020
1118
  * @internal
1021
1119
  */
1022
- static _DEFAULT_CONSOLIDATION_BATCH_SIZE = 1000;
1120
+ static _DEFAULT_CONSOLIDATION_BATCH_SIZE = 100;
1023
1121
  /**
1024
1122
  * Runtime name for the class.
1025
1123
  */
@@ -1054,6 +1152,11 @@ class SynchronisedStorageService {
1054
1152
  * @internal
1055
1153
  */
1056
1154
  _identityConnector;
1155
+ /**
1156
+ * The task scheduler component.
1157
+ * @internal
1158
+ */
1159
+ _taskSchedulerComponent;
1057
1160
  /**
1058
1161
  * The synchronised storage service to use when this is not a trusted node.
1059
1162
  * @internal
@@ -1080,15 +1183,15 @@ class SynchronisedStorageService {
1080
1183
  */
1081
1184
  _config;
1082
1185
  /**
1083
- * The timer ids for checking for entity updates.
1186
+ * The flag to determine if the service has been started.
1084
1187
  * @internal
1085
1188
  */
1086
- _entityUpdateTimers;
1189
+ _serviceStarted;
1087
1190
  /**
1088
- * The timer ids for consolidation.
1191
+ * The active storage keys for the synchronised storage service.
1089
1192
  * @internal
1090
1193
  */
1091
- _consolidationTimers;
1194
+ _activeStorageKeys;
1092
1195
  /**
1093
1196
  * The identity of the node this connector is running on.
1094
1197
  * @internal
@@ -1107,14 +1210,15 @@ class SynchronisedStorageService {
1107
1210
  this._verifiableSyncPointerStorageConnector = verifiableStorageModels.VerifiableStorageConnectorFactory.get(options.verifiableStorageConnectorType ?? "verifiable-storage");
1108
1211
  this._blobStorageComponent = core.ComponentFactory.get(options.blobStorageComponentType ?? "blob-storage");
1109
1212
  this._identityConnector = identityModels.IdentityConnectorFactory.get(options.identityConnectorType ?? "identity");
1213
+ this._taskSchedulerComponent = core.ComponentFactory.get(options.taskSchedulerComponentType ?? "task-scheduler");
1110
1214
  this._config = {
1111
1215
  synchronisedStorageKey: options.config.synchronisedStorageKey,
1112
1216
  synchronisedStorageMethodId: options.config.synchronisedStorageMethodId ?? "synchronised-storage-assertion",
1113
- entityUpdateIntervalMs: options.config.entityUpdateIntervalMs ??
1114
- SynchronisedStorageService._DEFAULT_ENTITY_UPDATE_INTERVAL_MS,
1217
+ entityUpdateIntervalMinutes: options.config.entityUpdateIntervalMinutes ??
1218
+ SynchronisedStorageService._DEFAULT_ENTITY_UPDATE_INTERVAL_MINUTES,
1115
1219
  isTrustedNode: options.config.isTrustedNode ?? false,
1116
- consolidationIntervalMs: options.config.consolidationIntervalMs ??
1117
- SynchronisedStorageService._DEFAULT_CONSOLIDATION_INTERVAL_MS,
1220
+ consolidationIntervalMinutes: options.config.consolidationIntervalMinutes ??
1221
+ SynchronisedStorageService._DEFAULT_CONSOLIDATION_INTERVAL_MINUTES,
1118
1222
  consolidationBatchSize: options.config.consolidationBatchSize ??
1119
1223
  SynchronisedStorageService._DEFAULT_CONSOLIDATION_BATCH_SIZE
1120
1224
  };
@@ -1128,10 +1232,10 @@ class SynchronisedStorageService {
1128
1232
  this._changeSetHelper = new ChangeSetHelper(this._logging, this._eventBusComponent, this._blobStorageComponent, this._identityConnector, this._config.synchronisedStorageMethodId);
1129
1233
  this._localSyncStateHelper = new LocalSyncStateHelper(this._logging, this._localSyncSnapshotEntryEntityStorage, this._changeSetHelper);
1130
1234
  this._remoteSyncStateHelper = new RemoteSyncStateHelper(this._logging, this._eventBusComponent, this._blobStorageComponent, this._verifiableSyncPointerStorageConnector, this._changeSetHelper, this._config.synchronisedStorageKey);
1131
- this._consolidationTimers = {};
1132
- this._entityUpdateTimers = {};
1133
- this._eventBusComponent.subscribe(synchronisedStorageModels.SynchronisedStorageTopics.RegisterSchemaType, async (event) => this.registerType(event.data));
1134
- this._eventBusComponent.subscribe(synchronisedStorageModels.SynchronisedStorageTopics.LocalItemChange, async (event) => this._localSyncStateHelper.addLocalChange(event.data.schemaType, event.data.operation, event.data.id));
1235
+ this._serviceStarted = false;
1236
+ this._activeStorageKeys = {};
1237
+ this._eventBusComponent.subscribe(synchronisedStorageModels.SynchronisedStorageTopics.RegisterStorageKey, async (event) => this.registerType(event.data));
1238
+ this._eventBusComponent.subscribe(synchronisedStorageModels.SynchronisedStorageTopics.LocalItemChange, async (event) => this._localSyncStateHelper.addLocalChange(event.data.storageKey, event.data.operation, event.data.id));
1135
1239
  }
1136
1240
  /**
1137
1241
  * The component needs to be started when the node is initialized.
@@ -1143,6 +1247,11 @@ class SynchronisedStorageService {
1143
1247
  async start(nodeIdentity, nodeLoggingConnectorType, componentState) {
1144
1248
  this._nodeIdentity = nodeIdentity;
1145
1249
  this._remoteSyncStateHelper.setNodeIdentity(nodeIdentity);
1250
+ this._serviceStarted = true;
1251
+ // If there are already storage keys registered, we need to activate them
1252
+ for (const storageKey in this._activeStorageKeys) {
1253
+ await this.activateStorageKey(storageKey);
1254
+ }
1146
1255
  }
1147
1256
  /**
1148
1257
  * The component needs to be stopped when the node is closed.
@@ -1152,13 +1261,10 @@ class SynchronisedStorageService {
1152
1261
  * @returns Nothing.
1153
1262
  */
1154
1263
  async stop(nodeIdentity, nodeLoggingConnectorType, componentState) {
1155
- for (const schemaType in this._entityUpdateTimers) {
1156
- clearTimeout(this._entityUpdateTimers[schemaType]);
1157
- delete this._entityUpdateTimers[schemaType];
1158
- }
1159
- for (const schemaType in this._consolidationTimers) {
1160
- clearTimeout(this._consolidationTimers[schemaType]);
1161
- delete this._consolidationTimers[schemaType];
1264
+ for (const storageKey in this._activeStorageKeys) {
1265
+ this._activeStorageKeys[storageKey] = false;
1266
+ this._taskSchedulerComponent.removeTask(`synchronised-storage-update-${storageKey}`);
1267
+ this._taskSchedulerComponent.removeTask(`synchronised-storage-consolidation-${storageKey}`);
1162
1268
  }
1163
1269
  }
1164
1270
  /**
@@ -1179,21 +1285,29 @@ class SynchronisedStorageService {
1179
1285
  // This will be performed using rights-management
1180
1286
  const changeSet = await this._changeSetHelper.getAndApplyChangeset(changeSetStorageId);
1181
1287
  if (!core.Is.empty(changeSet)) {
1182
- await this._remoteSyncStateHelper.addChangeSetToSyncState(changeSetStorageId);
1288
+ await this._remoteSyncStateHelper.addChangeSetToSyncState(changeSet.storageKey, changeSetStorageId);
1183
1289
  }
1184
1290
  }
1185
1291
  /**
1186
1292
  * Start the sync with further updates after an interval.
1187
- * @param schemaType The schema type to sync.
1293
+ * @param storageKey The storage key to sync.
1188
1294
  * @returns Nothing.
1189
1295
  * @internal
1190
1296
  */
1191
- async startEntitySync(schemaType) {
1297
+ async startEntitySync(storageKey) {
1192
1298
  try {
1299
+ await this._logging?.log({
1300
+ level: "info",
1301
+ source: this.CLASS_NAME,
1302
+ message: "startEntitySync",
1303
+ data: {
1304
+ storageKey
1305
+ }
1306
+ });
1193
1307
  // First we check for remote changes
1194
- await this.updateFromRemoteSyncState(schemaType);
1308
+ await this.updateFromRemoteSyncState(storageKey);
1195
1309
  // Now send any updates we have to the remote storage
1196
- await this.updateFromLocalSyncState(schemaType);
1310
+ await this.updateFromLocalSyncState(storageKey);
1197
1311
  }
1198
1312
  catch (error) {
1199
1313
  await this._logging?.log({
@@ -1203,27 +1317,31 @@ class SynchronisedStorageService {
1203
1317
  error: core.BaseError.fromError(error)
1204
1318
  });
1205
1319
  }
1206
- finally {
1207
- // Set a timer to check for updates again
1208
- this._entityUpdateTimers[schemaType] = setTimeout(async () => this.startEntitySync(schemaType), this._config.entityUpdateIntervalMs);
1209
- }
1210
1320
  }
1211
1321
  /**
1212
1322
  * Check for updates in the remote storage.
1213
- * @param schemaType The schema type to check for updates.
1323
+ * @param storageKey The storage key to check for updates.
1214
1324
  * @returns Nothing.
1215
1325
  * @internal
1216
1326
  */
1217
- async updateFromRemoteSyncState(schemaType) {
1218
- // Get the verifiable sync pointer from the verifiable storage
1219
- const verifiableSyncPointer = await this._remoteSyncStateHelper.getVerifiableSyncPointer();
1220
- if (!core.Is.empty(verifiableSyncPointer)) {
1327
+ async updateFromRemoteSyncState(storageKey) {
1328
+ await this._logging?.log({
1329
+ level: "info",
1330
+ source: this.CLASS_NAME,
1331
+ message: "updateFromRemoteSyncState",
1332
+ data: {
1333
+ storageKey
1334
+ }
1335
+ });
1336
+ // Get the verifiable sync pointer store from the verifiable storage
1337
+ const verifiableSyncPointerStore = await this._remoteSyncStateHelper.getVerifiableSyncPointerStore();
1338
+ if (!core.Is.empty(verifiableSyncPointerStore.syncPointers[storageKey])) {
1221
1339
  // Load the sync state from the remote blob storage using the sync pointer
1222
1340
  // to load the sync state
1223
- const remoteSyncState = await this._remoteSyncStateHelper.getRemoteSyncState(verifiableSyncPointer.syncPointerId);
1341
+ const remoteSyncState = await this._remoteSyncStateHelper.getRemoteSyncState(verifiableSyncPointerStore.syncPointers[storageKey]);
1224
1342
  // If we got the sync state we can try and sync from it
1225
1343
  if (!core.Is.undefined(remoteSyncState)) {
1226
- await this._localSyncStateHelper.syncFromRemote(schemaType, remoteSyncState);
1344
+ await this._localSyncStateHelper.syncFromRemote(storageKey, remoteSyncState);
1227
1345
  }
1228
1346
  }
1229
1347
  }
@@ -1232,43 +1350,78 @@ class SynchronisedStorageService {
1232
1350
  * @returns Nothing.
1233
1351
  * @internal
1234
1352
  */
1235
- async updateFromLocalSyncState(schemaType) {
1236
- if (core.Is.stringValue(this._nodeIdentity)) {
1237
- // Ge the current local change snapshot
1238
- const localChangeSnapshot = await this._localSyncStateHelper.getLocalChangeSnapshot(schemaType);
1239
- if (!core.Is.empty(localChangeSnapshot)) {
1240
- await this._remoteSyncStateHelper.createAndStoreChangeSet(schemaType, localChangeSnapshot.localChanges, async (changeSetStorageId) => {
1241
- if (core.Is.stringValue(changeSetStorageId)) {
1242
- // Send the local changes to the remote storage if we are a trusted node
1243
- if (this._config.isTrustedNode) {
1244
- await this._remoteSyncStateHelper.addChangeSetToSyncState(changeSetStorageId);
1245
- }
1246
- else if (!core.Is.empty(this._trustedSynchronisedStorageComponent)) {
1247
- // If we are not a trusted node, we need to send the changes to the trusted node
1248
- await this._trustedSynchronisedStorageComponent.syncChangeSet(changeSetStorageId);
1353
+ async updateFromLocalSyncState(storageKey) {
1354
+ await this._logging?.log({
1355
+ level: "info",
1356
+ source: this.CLASS_NAME,
1357
+ message: "updateFromLocalSyncState",
1358
+ data: {
1359
+ storageKey
1360
+ }
1361
+ });
1362
+ const localChangeSnapshot = await this._localSyncStateHelper.getLocalChangeSnapshot(storageKey);
1363
+ if (core.Is.arrayValue(localChangeSnapshot.changes)) {
1364
+ await this._remoteSyncStateHelper.createAndStoreChangeSet(storageKey, localChangeSnapshot.changes, async (changeSetStorageId) => {
1365
+ if (core.Is.stringValue(changeSetStorageId)) {
1366
+ await this._logging?.log({
1367
+ level: "info",
1368
+ source: this.CLASS_NAME,
1369
+ message: "createdStorageChangeSet",
1370
+ data: {
1371
+ storageKey,
1372
+ changeSetStorageId
1249
1373
  }
1374
+ });
1375
+ // Send the local changes to the remote storage if we are a trusted node
1376
+ if (this._config.isTrustedNode) {
1377
+ await this._remoteSyncStateHelper.addChangeSetToSyncState(storageKey, changeSetStorageId);
1250
1378
  await this._localSyncStateHelper.removeLocalChangeSnapshot(localChangeSnapshot);
1251
1379
  }
1252
- });
1253
- }
1380
+ else if (!core.Is.empty(this._trustedSynchronisedStorageComponent)) {
1381
+ // If we are not a trusted node, we need to send the changes to the trusted node
1382
+ await this._trustedSynchronisedStorageComponent.syncChangeSet(changeSetStorageId);
1383
+ await this._localSyncStateHelper.removeLocalChangeSnapshot(localChangeSnapshot);
1384
+ }
1385
+ }
1386
+ else {
1387
+ await this._logging?.log({
1388
+ level: "info",
1389
+ source: this.CLASS_NAME,
1390
+ message: "createdStorageChangeSetNone",
1391
+ data: {
1392
+ storageKey
1393
+ }
1394
+ });
1395
+ }
1396
+ });
1397
+ }
1398
+ else {
1399
+ await this._logging?.log({
1400
+ level: "info",
1401
+ source: this.CLASS_NAME,
1402
+ message: "updateFromLocalSyncStateNoChanges",
1403
+ data: {
1404
+ storageKey
1405
+ }
1406
+ });
1254
1407
  }
1255
1408
  }
1256
1409
  /**
1257
1410
  * Start the consolidation sync.
1258
- * @param schemaType The schema type to consolidate.
1411
+ * @param storageKey The storage key to consolidate.
1259
1412
  * @returns Nothing.
1260
1413
  * @internal
1261
1414
  */
1262
- async startConsolidationSync(schemaType) {
1415
+ async startConsolidationSync(storageKey) {
1263
1416
  let localChangeSnapshot;
1264
1417
  try {
1265
1418
  // If we are performing a consolidation, we can remove the local changes
1266
- await this._localSyncStateHelper.getLocalChangeSnapshot(schemaType);
1419
+ await this._localSyncStateHelper.getLocalChangeSnapshot(storageKey);
1267
1420
  if (!core.Is.empty(localChangeSnapshot)) {
1268
1421
  await this._localSyncStateHelper.removeLocalChangeSnapshot(localChangeSnapshot);
1269
1422
  }
1270
1423
  if (core.Is.stringValue(this._nodeIdentity)) {
1271
- await this._remoteSyncStateHelper.consolidateFromLocal(schemaType, this._config.consolidationBatchSize ??
1424
+ await this._remoteSyncStateHelper.consolidateFromLocal(storageKey, this._config.consolidationBatchSize ??
1272
1425
  SynchronisedStorageService._DEFAULT_CONSOLIDATION_BATCH_SIZE);
1273
1426
  // The consolidation was successful, so we can remove the local change snapshot permanently
1274
1427
  localChangeSnapshot = undefined;
@@ -1286,10 +1439,6 @@ class SynchronisedStorageService {
1286
1439
  error: core.BaseError.fromError(error)
1287
1440
  });
1288
1441
  }
1289
- finally {
1290
- // Set a timer to perform the consolidation again
1291
- this._consolidationTimers[schemaType] = setTimeout(async () => this.startConsolidationSync(schemaType), this._config.consolidationIntervalMs);
1292
- }
1293
1442
  }
1294
1443
  /**
1295
1444
  * Register a new sync type.
@@ -1302,14 +1451,48 @@ class SynchronisedStorageService {
1302
1451
  source: this.CLASS_NAME,
1303
1452
  message: "registerType",
1304
1453
  data: {
1305
- schemaType: syncRegisterType.schemaType
1454
+ storageKey: syncRegisterType.storageKey
1306
1455
  }
1307
1456
  });
1308
- if (this._config.entityUpdateIntervalMs > 0) {
1309
- await this.startEntitySync(syncRegisterType.schemaType);
1457
+ if (core.Is.empty(this._activeStorageKeys[syncRegisterType.storageKey])) {
1458
+ this._activeStorageKeys[syncRegisterType.storageKey] = false;
1459
+ if (this._serviceStarted) {
1460
+ await this.activateStorageKey(syncRegisterType.storageKey);
1461
+ }
1310
1462
  }
1311
- if (this._config.isTrustedNode && this._config.consolidationIntervalMs > 0) {
1312
- await this.startConsolidationSync(syncRegisterType.schemaType);
1463
+ }
1464
+ /**
1465
+ * Activate a storage key.
1466
+ * @param storageKey The storage key to activate.
1467
+ * @internal
1468
+ */
1469
+ async activateStorageKey(storageKey) {
1470
+ if (!core.Is.empty(this._activeStorageKeys[storageKey]) && !this._activeStorageKeys[storageKey]) {
1471
+ await this._logging?.log({
1472
+ level: "info",
1473
+ source: this.CLASS_NAME,
1474
+ message: "activateType",
1475
+ data: {
1476
+ storageKey
1477
+ }
1478
+ });
1479
+ this._activeStorageKeys[storageKey] = true;
1480
+ if (this._config.entityUpdateIntervalMinutes > 0) {
1481
+ await this._taskSchedulerComponent.addTask(`synchronised-storage-update-${storageKey}`, [
1482
+ {
1483
+ nextTriggerTime: Date.now(),
1484
+ intervalMinutes: this._config.entityUpdateIntervalMinutes
1485
+ }
1486
+ ], async () => this.startEntitySync(storageKey));
1487
+ }
1488
+ if (this._config.isTrustedNode && this._config.consolidationIntervalMinutes > 0) {
1489
+ await this._taskSchedulerComponent.addTask(`synchronised-storage-consolidation-${storageKey}`, [
1490
+ {
1491
+ nextTriggerTime: Date.now(),
1492
+ intervalMinutes: this._config.consolidationIntervalMinutes
1493
+ }
1494
+ ], async () => this.startConsolidationSync(storageKey));
1495
+ }
1313
1496
  }
1314
1497
  }
1315
1498
  }