@bopen-io/wallet-toolbox 1.7.20-idb-fix.2 → 1.7.20-idb-fix.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bopen-io/wallet-toolbox",
3
- "version": "1.7.20-idb-fix.2",
3
+ "version": "1.7.20-idb-fix.5",
4
4
  "description": "BRC100 conforming wallet, wallet storage and wallet signer components",
5
5
  "main": "./out/src/index.js",
6
6
  "types": "./out/src/index.d.ts",
@@ -744,23 +744,27 @@ export class WalletStorageManager implements sdk.WalletStorage {
744
744
  const identityKey = auth.identityKey
745
745
 
746
746
  const writerSettings = await writer.makeAvailable()
747
+ const isRemoteWriter = writer instanceof StorageClient
747
748
 
748
749
  let inserts = 0,
749
750
  updates = 0
750
751
 
751
- log = await this.runAsSync(async sync => {
752
- const reader = sync
753
- const readerSettings = reader.getSettings()
754
-
752
+ // If writer is remote (StorageClient), we must NOT hold a local IDB transaction
753
+ // across network calls. Instead, do fresh reads for each chunk iteration.
754
+ if (isRemoteWriter && !activeSync) {
755
+ const readerSettings = this.getSettings()
755
756
  log += progLog(`syncToWriter from ${readerSettings.storageName} to ${writerSettings.storageName}\n`)
756
757
 
757
758
  let i = -1
758
759
  for (;;) {
759
760
  i++
761
+ // Network call to remote writer - OK, no local transaction held
760
762
  const ss = await EntitySyncState.fromStorage(writer, identityKey, readerSettings)
761
763
  const args = ss.makeRequestSyncChunkArgs(identityKey, writerSettings.storageIdentityKey)
762
- const chunk = await reader.getSyncChunk(args)
764
+ // Fresh local read - brief transaction that completes before network call
765
+ const chunk = await this.runAsSync(async sync => sync.getSyncChunk(args))
763
766
  log += EntitySyncState.syncChunkSummary(chunk)
767
+ // Network call to remote writer - OK, no local transaction held
764
768
  const r = await writer.processSyncChunk(args, chunk)
765
769
  inserts += r.inserts
766
770
  updates += r.updates
@@ -768,8 +772,31 @@ export class WalletStorageManager implements sdk.WalletStorage {
768
772
  if (r.done) break
769
773
  }
770
774
  log += progLog(`syncToWriter complete: ${inserts} inserts, ${updates} updates\n`)
771
- return log
772
- }, activeSync)
775
+ } else {
776
+ // Writer is local or we have an active sync context - use original approach
777
+ log = await this.runAsSync(async sync => {
778
+ const reader = sync
779
+ const readerSettings = reader.getSettings()
780
+
781
+ log += progLog(`syncToWriter from ${readerSettings.storageName} to ${writerSettings.storageName}\n`)
782
+
783
+ let i = -1
784
+ for (;;) {
785
+ i++
786
+ const ss = await EntitySyncState.fromStorage(writer, identityKey, readerSettings)
787
+ const args = ss.makeRequestSyncChunkArgs(identityKey, writerSettings.storageIdentityKey)
788
+ const chunk = await reader.getSyncChunk(args)
789
+ log += EntitySyncState.syncChunkSummary(chunk)
790
+ const r = await writer.processSyncChunk(args, chunk)
791
+ inserts += r.inserts
792
+ updates += r.updates
793
+ log += progLog(`chunk ${i} inserted ${r.inserts} updated ${r.updates} ${r.maxUpdated_at}\n`)
794
+ if (r.done) break
795
+ }
796
+ log += progLog(`syncToWriter complete: ${inserts} inserts, ${updates} updates\n`)
797
+ return log
798
+ }, activeSync)
799
+ }
773
800
 
774
801
  return { inserts, updates, log }
775
802
  }
@@ -817,38 +844,55 @@ export class WalletStorageManager implements sdk.WalletStorage {
817
844
 
818
845
  log += progLog('\n')
819
846
 
820
- log += await this.runAsSync(async sync => {
821
- let log = ''
822
-
823
- if (this._conflictingActives!.length > 0) {
824
- // Merge state from conflicting actives into `newActive`.
825
-
826
- // Handle case where new active is current active to resolve conflicts.
827
- // And where new active is one of the current conflict actives.
828
- this._conflictingActives!.push(this._active!)
829
- // Remove the new active from conflicting actives and
830
- // set new active as the conflicting active that matches the target `storageIdentityKey`
831
- this._conflictingActives = this._conflictingActives!.filter(ca => {
832
- const isNewActive = ca.settings!.storageIdentityKey === storageIdentityKey
833
- return !isNewActive
834
- })
835
-
836
- // Merge state from conflicting actives into `newActive`.
837
- for (const conflict of this._conflictingActives) {
838
- log += progLog('MERGING STATE FROM CONFLICTING ACTIVES:\n')
839
- const sfr = await this.syncToWriter(
840
- { identityKey, userId: newActive.user!.userId, isActive: false },
841
- newActive.storage,
842
- conflict.storage,
843
- undefined,
844
- progLog
845
- )
846
- log += sfr.log
847
+ // Handle conflicting actives OUTSIDE runAsSync to avoid IDB timeout.
848
+ // Network calls to remote storage can take longer than IDB transaction timeout.
849
+ if (this._conflictingActives!.length > 0) {
850
+ // Handle case where new active is current active to resolve conflicts.
851
+ // And where new active is one of the current conflict actives.
852
+ this._conflictingActives!.push(this._active!)
853
+ // Remove the new active from conflicting actives
854
+ this._conflictingActives = this._conflictingActives!.filter(ca => {
855
+ const isNewActive = ca.settings!.storageIdentityKey === storageIdentityKey
856
+ return !isNewActive
857
+ })
858
+
859
+ // Merge state from each conflicting active into newActive.
860
+ // Network calls happen here, outside any held IDB transaction.
861
+ for (const conflict of this._conflictingActives) {
862
+ log += progLog('MERGING STATE FROM CONFLICTING ACTIVES:\n')
863
+ const readerSettings = await conflict.storage.makeAvailable()
864
+ const writerSettings = await newActive.storage.makeAvailable()
865
+
866
+ let i = -1
867
+ for (;;) {
868
+ i++
869
+ // Get sync state from writer (newActive) - brief local read
870
+ const ss = await EntitySyncState.fromStorage(newActive.storage, identityKey, readerSettings)
871
+ const args = ss.makeRequestSyncChunkArgs(identityKey, writerSettings.storageIdentityKey)
872
+
873
+ // Network call to conflict (reader) - outside transaction
874
+ const chunk = await conflict.storage.getSyncChunk(args)
875
+ log += EntitySyncState.syncChunkSummary(chunk)
876
+
877
+ // Preserve activeStorage - merging from reader cannot change active
878
+ if (chunk.user) {
879
+ chunk.user.activeStorage = storageIdentityKey
880
+ }
881
+
882
+ // Brief local write per chunk
883
+ const r = await newActive.storage.processSyncChunk(args, chunk)
884
+ log += progLog(`chunk ${i} inserted ${r.inserts} updated ${r.updates} ${r.maxUpdated_at}\n`)
885
+ if (r.done) break
847
886
  }
848
- log += progLog('PROPAGATE MERGED ACTIVE STATE TO NON-ACTIVES\n')
849
- } else {
850
- log += progLog('BACKUP CURRENT ACTIVE STATE THEN SET NEW ACTIVE\n')
851
887
  }
888
+ log += progLog('PROPAGATE MERGED ACTIVE STATE TO NON-ACTIVES\n')
889
+ } else {
890
+ log += progLog('BACKUP CURRENT ACTIVE STATE THEN SET NEW ACTIVE\n')
891
+ }
892
+
893
+ // Continue with local-only operations in runAsSync
894
+ log += await this.runAsSync(async sync => {
895
+ let innerLog = ''
852
896
 
853
897
  // If there were conflicting actives,
854
898
  // Push state merged from all merged actives into newActive to all stores other than the now single active.
@@ -869,18 +913,18 @@ export class WalletStorageManager implements sdk.WalletStorage {
869
913
  const stwr = await this.syncToWriter(
870
914
  { identityKey, userId: store.user!.userId, isActive: false },
871
915
  store.storage,
872
- backupSource.storage,
916
+ sync,
873
917
  undefined,
874
918
  progLog
875
919
  )
876
- log += stwr.log
920
+ innerLog += stwr.log
877
921
  }
878
922
  }
879
923
 
880
924
  this._isAvailable = false
881
925
  await this.makeAvailable()
882
926
 
883
- return log
927
+ return innerLog
884
928
  })
885
929
 
886
930
  return log