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

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.
@@ -75,6 +75,16 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
75
75
  toDbTrx(stores, mode, trx) {
76
76
  if (trx) {
77
77
  const t = trx;
78
+ // Check if the transaction is still active by trying to access an object store
79
+ try {
80
+ const storeToCheck = stores[0] || this.allStores[0];
81
+ t.objectStore(storeToCheck);
82
+ }
83
+ catch (e) {
84
+ console.error(`[StorageIdb.toDbTrx] Passed transaction already finished! stores=${stores.join(',')}, mode=${mode}`);
85
+ console.error('[StorageIdb.toDbTrx] Stack trace:', new Error().stack);
86
+ throw e;
87
+ }
78
88
  return t;
79
89
  }
80
90
  else {
@@ -290,7 +300,8 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
290
300
  * @returns next funding output to add to transaction or undefined if there are none.
291
301
  */
292
302
  async allocateChangeInput(userId, basketId, targetSatoshis, exactSatoshis, excludeSending, transactionId) {
293
- const dbTrx = this.toDbTrx(['outputs', 'transactions'], 'readwrite');
303
+ // Include proven_txs in store list since findOutputs -> validateOutputScript needs it
304
+ const dbTrx = this.toDbTrx(['outputs', 'transactions', 'proven_txs'], 'readwrite');
294
305
  try {
295
306
  const txStatus = ['completed', 'unproven'];
296
307
  if (!excludeSending)
@@ -444,19 +455,30 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
444
455
  }
445
456
  async filterOutputTagMaps(args, filtered, userId) {
446
457
  var _a, _b, _c, _d;
458
+ // Pre-compute outputTagIds for this user BEFORE opening cursor.
459
+ // This avoids nested queries inside the cursor loop which cause IDB transaction timeouts.
460
+ let userOutputTagIds;
461
+ if (userId !== undefined) {
462
+ userOutputTagIds = new Set();
463
+ const userTags = await this.findOutputTags({ partial: { userId } });
464
+ for (const tag of userTags) {
465
+ userOutputTagIds.add(tag.outputTagId);
466
+ }
467
+ }
447
468
  const offset = ((_a = args.paged) === null || _a === void 0 ? void 0 : _a.offset) || 0;
448
469
  let skipped = 0;
449
470
  let count = 0;
450
471
  const dbTrx = this.toDbTrx(['output_tags_map'], 'readonly', args.trx);
472
+ const direction = args.orderDescending ? 'prev' : 'next';
451
473
  let cursor;
452
474
  if (((_b = args.partial) === null || _b === void 0 ? void 0 : _b.outputTagId) !== undefined) {
453
- cursor = await dbTrx.objectStore('output_tags_map').index('outputTagId').openCursor(args.partial.outputTagId);
475
+ cursor = await dbTrx.objectStore('output_tags_map').index('outputTagId').openCursor(args.partial.outputTagId, direction);
454
476
  }
455
477
  else if (((_c = args.partial) === null || _c === void 0 ? void 0 : _c.outputId) !== undefined) {
456
- cursor = await dbTrx.objectStore('output_tags_map').index('outputId').openCursor(args.partial.outputId);
478
+ cursor = await dbTrx.objectStore('output_tags_map').index('outputId').openCursor(args.partial.outputId, direction);
457
479
  }
458
480
  else {
459
- cursor = await dbTrx.objectStore('output_tags_map').openCursor();
481
+ cursor = await dbTrx.objectStore('output_tags_map').openCursor(null, direction);
460
482
  }
461
483
  let firstTime = true;
462
484
  while (cursor) {
@@ -482,10 +504,8 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
482
504
  if (args.partial.isDeleted !== undefined && r.isDeleted !== args.partial.isDeleted)
483
505
  continue;
484
506
  }
485
- if (userId !== undefined && r.txid) {
486
- const count = await this.countOutputTags({ partial: { userId, outputTagId: r.outputTagId }, trx: args.trx });
487
- if (count === 0)
488
- continue;
507
+ if (userOutputTagIds !== undefined && !userOutputTagIds.has(r.outputTagId)) {
508
+ continue;
489
509
  }
490
510
  if (skipped < offset) {
491
511
  skipped++;
@@ -512,28 +532,41 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
512
532
  throw new WERR_errors_1.WERR_INVALID_PARAMETER('args.partial.rawTx', `undefined. ProvenTxReqs may not be found by rawTx value.`);
513
533
  if (args.partial.inputBEEF)
514
534
  throw new WERR_errors_1.WERR_INVALID_PARAMETER('args.partial.inputBEEF', `undefined. ProvenTxReqs may not be found by inputBEEF value.`);
535
+ // Pre-compute txids for this user's transactions BEFORE opening cursor.
536
+ // This avoids nested queries inside the cursor loop which cause IDB transaction timeouts.
537
+ let userTxIds;
538
+ if (userId !== undefined) {
539
+ userTxIds = new Set();
540
+ const userTxs = await this.findTransactions({ partial: { userId }, noRawTx: true });
541
+ for (const tx of userTxs) {
542
+ if (tx.txid) {
543
+ userTxIds.add(tx.txid);
544
+ }
545
+ }
546
+ }
515
547
  const offset = ((_a = args.paged) === null || _a === void 0 ? void 0 : _a.offset) || 0;
516
548
  let skipped = 0;
517
549
  let count = 0;
518
550
  const dbTrx = this.toDbTrx(['proven_tx_reqs'], 'readonly', args.trx);
551
+ const direction = args.orderDescending ? 'prev' : 'next';
519
552
  let cursor;
520
553
  if ((_b = args.partial) === null || _b === void 0 ? void 0 : _b.provenTxReqId) {
521
- cursor = await dbTrx.objectStore('proven_tx_reqs').openCursor(args.partial.provenTxReqId);
554
+ cursor = await dbTrx.objectStore('proven_tx_reqs').openCursor(args.partial.provenTxReqId, direction);
522
555
  }
523
556
  else if (((_c = args.partial) === null || _c === void 0 ? void 0 : _c.provenTxId) !== undefined) {
524
- cursor = await dbTrx.objectStore('proven_tx_reqs').index('provenTxId').openCursor(args.partial.provenTxId);
557
+ cursor = await dbTrx.objectStore('proven_tx_reqs').index('provenTxId').openCursor(args.partial.provenTxId, direction);
525
558
  }
526
559
  else if (((_d = args.partial) === null || _d === void 0 ? void 0 : _d.txid) !== undefined) {
527
- cursor = await dbTrx.objectStore('proven_tx_reqs').index('txid').openCursor(args.partial.txid);
560
+ cursor = await dbTrx.objectStore('proven_tx_reqs').index('txid').openCursor(args.partial.txid, direction);
528
561
  }
529
562
  else if (((_e = args.partial) === null || _e === void 0 ? void 0 : _e.status) !== undefined) {
530
- cursor = await dbTrx.objectStore('proven_tx_reqs').index('status').openCursor(args.partial.status);
563
+ cursor = await dbTrx.objectStore('proven_tx_reqs').index('status').openCursor(args.partial.status, direction);
531
564
  }
532
565
  else if (((_f = args.partial) === null || _f === void 0 ? void 0 : _f.batch) !== undefined) {
533
- cursor = await dbTrx.objectStore('proven_tx_reqs').index('batch').openCursor(args.partial.batch);
566
+ cursor = await dbTrx.objectStore('proven_tx_reqs').index('batch').openCursor(args.partial.batch, direction);
534
567
  }
535
568
  else {
536
- cursor = await dbTrx.objectStore('proven_tx_reqs').openCursor();
569
+ cursor = await dbTrx.objectStore('proven_tx_reqs').openCursor(null, direction);
537
570
  }
538
571
  let firstTime = true;
539
572
  while (cursor) {
@@ -545,6 +578,10 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
545
578
  const r = cursor.value;
546
579
  if (args.since && args.since > r.updated_at)
547
580
  continue;
581
+ if (args.status && args.status.length > 0 && !args.status.includes(r.status))
582
+ continue;
583
+ if (args.txids && args.txids.length > 0 && !args.txids.includes(r.txid))
584
+ continue;
548
585
  if (args.partial) {
549
586
  if (args.partial.provenTxReqId && r.provenTxReqId !== args.partial.provenTxReqId)
550
587
  continue;
@@ -569,10 +606,8 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
569
606
  if (args.partial.notify && r.notify !== args.partial.notify)
570
607
  continue;
571
608
  }
572
- if (userId !== undefined && r.txid) {
573
- const count = await this.countTransactions({ partial: { userId, txid: r.txid }, trx: args.trx });
574
- if (count === 0)
575
- continue;
609
+ if (userTxIds !== undefined && r.txid && !userTxIds.has(r.txid)) {
610
+ continue;
576
611
  }
577
612
  if (skipped < offset) {
578
613
  skipped++;
@@ -599,19 +634,32 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
599
634
  throw new WERR_errors_1.WERR_INVALID_PARAMETER('args.partial.rawTx', `undefined. ProvenTxs may not be found by rawTx value.`);
600
635
  if (args.partial.merklePath)
601
636
  throw new WERR_errors_1.WERR_INVALID_PARAMETER('args.partial.merklePath', `undefined. ProvenTxs may not be found by merklePath value.`);
637
+ // Pre-compute provenTxIds for this user's transactions BEFORE opening cursor.
638
+ // This avoids nested queries inside the cursor loop which cause IDB transaction timeouts.
639
+ let userProvenTxIds;
640
+ if (userId !== undefined) {
641
+ userProvenTxIds = new Set();
642
+ const userTxs = await this.findTransactions({ partial: { userId }, noRawTx: true });
643
+ for (const tx of userTxs) {
644
+ if (tx.provenTxId !== undefined) {
645
+ userProvenTxIds.add(tx.provenTxId);
646
+ }
647
+ }
648
+ }
602
649
  const offset = ((_a = args.paged) === null || _a === void 0 ? void 0 : _a.offset) || 0;
603
650
  let skipped = 0;
604
651
  let count = 0;
605
652
  const dbTrx = this.toDbTrx(['proven_txs'], 'readonly', args.trx);
653
+ const direction = args.orderDescending ? 'prev' : 'next';
606
654
  let cursor;
607
655
  if ((_b = args.partial) === null || _b === void 0 ? void 0 : _b.provenTxId) {
608
- cursor = await dbTrx.objectStore('proven_txs').openCursor(args.partial.provenTxId);
656
+ cursor = await dbTrx.objectStore('proven_txs').openCursor(args.partial.provenTxId, direction);
609
657
  }
610
658
  else if (((_c = args.partial) === null || _c === void 0 ? void 0 : _c.txid) !== undefined) {
611
- cursor = await dbTrx.objectStore('proven_txs').index('txid').openCursor(args.partial.txid);
659
+ cursor = await dbTrx.objectStore('proven_txs').index('txid').openCursor(args.partial.txid, direction);
612
660
  }
613
661
  else {
614
- cursor = await dbTrx.objectStore('proven_txs').openCursor();
662
+ cursor = await dbTrx.objectStore('proven_txs').openCursor(null, direction);
615
663
  }
616
664
  let firstTime = true;
617
665
  while (cursor) {
@@ -641,10 +689,8 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
641
689
  if (args.partial.merkleRoot && r.merkleRoot !== args.partial.merkleRoot)
642
690
  continue;
643
691
  }
644
- if (userId !== undefined) {
645
- const count = await this.countTransactions({ partial: { userId, provenTxId: r.provenTxId }, trx: args.trx });
646
- if (count === 0)
647
- continue;
692
+ if (userProvenTxIds !== undefined && !userProvenTxIds.has(r.provenTxId)) {
693
+ continue;
648
694
  }
649
695
  if (skipped < offset) {
650
696
  skipped++;
@@ -667,19 +713,30 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
667
713
  }
668
714
  async filterTxLabelMaps(args, filtered, userId) {
669
715
  var _a, _b, _c, _d;
716
+ // Pre-compute txLabelIds for this user BEFORE opening cursor.
717
+ // This avoids nested queries inside the cursor loop which cause IDB transaction timeouts.
718
+ let userTxLabelIds;
719
+ if (userId !== undefined) {
720
+ userTxLabelIds = new Set();
721
+ const userLabels = await this.findTxLabels({ partial: { userId } });
722
+ for (const label of userLabels) {
723
+ userTxLabelIds.add(label.txLabelId);
724
+ }
725
+ }
670
726
  const offset = ((_a = args.paged) === null || _a === void 0 ? void 0 : _a.offset) || 0;
671
727
  let skipped = 0;
672
728
  let count = 0;
673
729
  const dbTrx = this.toDbTrx(['tx_labels_map'], 'readonly', args.trx);
730
+ const direction = args.orderDescending ? 'prev' : 'next';
674
731
  let cursor;
675
732
  if (((_b = args.partial) === null || _b === void 0 ? void 0 : _b.transactionId) !== undefined) {
676
- cursor = await dbTrx.objectStore('tx_labels_map').index('transactionId').openCursor(args.partial.transactionId);
733
+ cursor = await dbTrx.objectStore('tx_labels_map').index('transactionId').openCursor(args.partial.transactionId, direction);
677
734
  }
678
735
  else if (((_c = args.partial) === null || _c === void 0 ? void 0 : _c.txLabelId) !== undefined) {
679
- cursor = await dbTrx.objectStore('tx_labels_map').index('txLabelId').openCursor(args.partial.txLabelId);
736
+ cursor = await dbTrx.objectStore('tx_labels_map').index('txLabelId').openCursor(args.partial.txLabelId, direction);
680
737
  }
681
738
  else {
682
- cursor = await dbTrx.objectStore('tx_labels_map').openCursor();
739
+ cursor = await dbTrx.objectStore('tx_labels_map').openCursor(null, direction);
683
740
  }
684
741
  let firstTime = true;
685
742
  while (cursor) {
@@ -691,6 +748,8 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
691
748
  const r = cursor.value;
692
749
  if (args.since && args.since > r.updated_at)
693
750
  continue;
751
+ if (args.labelIds && !args.labelIds.includes(r.txLabelId))
752
+ continue;
694
753
  if (args.partial) {
695
754
  if (args.partial.txLabelId && r.txLabelId !== args.partial.txLabelId)
696
755
  continue;
@@ -703,10 +762,8 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
703
762
  if (args.partial.isDeleted !== undefined && r.isDeleted !== args.partial.isDeleted)
704
763
  continue;
705
764
  }
706
- if (userId !== undefined) {
707
- const count = await this.countTxLabels({ partial: { userId, txLabelId: r.txLabelId }, trx: args.trx });
708
- if (count === 0)
709
- continue;
765
+ if (userTxLabelIds !== undefined && !userTxLabelIds.has(r.txLabelId)) {
766
+ continue;
710
767
  }
711
768
  if (skipped < offset) {
712
769
  skipped++;
@@ -999,7 +1056,14 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
999
1056
  }
1000
1057
  const u = this.validatePartialForUpdate(update);
1001
1058
  const dbTrx = this.toDbTrx([storeName], 'readwrite', trx);
1002
- const store = dbTrx.objectStore(storeName);
1059
+ let store;
1060
+ try {
1061
+ store = dbTrx.objectStore(storeName);
1062
+ }
1063
+ catch (e) {
1064
+ console.error(`[StorageIdb.updateIdb] objectStore('${storeName}') failed for id=${JSON.stringify(id)}, trx=${!!trx}:`, e);
1065
+ throw e;
1066
+ }
1003
1067
  const ids = Array.isArray(id) ? id : [id];
1004
1068
  try {
1005
1069
  for (const i of ids) {
@@ -1124,7 +1188,14 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
1124
1188
  return r;
1125
1189
  }
1126
1190
  catch (err) {
1127
- tx.abort();
1191
+ // Log more detail about transaction errors to help debug IDB issues
1192
+ console.error('[StorageIdb] Transaction error:', err instanceof Error ? err.message : err);
1193
+ try {
1194
+ tx.abort();
1195
+ }
1196
+ catch (abortErr) {
1197
+ console.error('[StorageIdb] Error aborting transaction:', abortErr);
1198
+ }
1128
1199
  await tx.done;
1129
1200
  throw err;
1130
1201
  }
@@ -1135,18 +1206,19 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
1135
1206
  let skipped = 0;
1136
1207
  let count = 0;
1137
1208
  const dbTrx = this.toDbTrx(['certificate_fields'], 'readonly', args.trx);
1209
+ const direction = args.orderDescending ? 'prev' : 'next';
1138
1210
  let cursor;
1139
1211
  if (((_b = args.partial) === null || _b === void 0 ? void 0 : _b.certificateId) !== undefined) {
1140
1212
  cursor = await dbTrx
1141
1213
  .objectStore('certificate_fields')
1142
1214
  .index('certificateId')
1143
- .openCursor(args.partial.certificateId);
1215
+ .openCursor(args.partial.certificateId, direction);
1144
1216
  }
1145
1217
  else if (((_c = args.partial) === null || _c === void 0 ? void 0 : _c.userId) !== undefined) {
1146
- cursor = await dbTrx.objectStore('certificate_fields').index('userId').openCursor(args.partial.userId);
1218
+ cursor = await dbTrx.objectStore('certificate_fields').index('userId').openCursor(args.partial.userId, direction);
1147
1219
  }
1148
1220
  else {
1149
- cursor = await dbTrx.objectStore('certificate_fields').openCursor();
1221
+ cursor = await dbTrx.objectStore('certificate_fields').openCursor(null, direction);
1150
1222
  }
1151
1223
  let firstTime = true;
1152
1224
  while (cursor) {
@@ -1199,23 +1271,24 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
1199
1271
  let skipped = 0;
1200
1272
  let count = 0;
1201
1273
  const dbTrx = this.toDbTrx(['certificates'], 'readonly', args.trx);
1274
+ const direction = args.orderDescending ? 'prev' : 'next';
1202
1275
  let cursor;
1203
1276
  if ((_b = args.partial) === null || _b === void 0 ? void 0 : _b.certificateId) {
1204
- cursor = await dbTrx.objectStore('certificates').openCursor(args.partial.certificateId);
1277
+ cursor = await dbTrx.objectStore('certificates').openCursor(args.partial.certificateId, direction);
1205
1278
  }
1206
1279
  else if (((_c = args.partial) === null || _c === void 0 ? void 0 : _c.userId) !== undefined) {
1207
1280
  if (((_d = args.partial) === null || _d === void 0 ? void 0 : _d.type) && ((_e = args.partial) === null || _e === void 0 ? void 0 : _e.certifier) && ((_f = args.partial) === null || _f === void 0 ? void 0 : _f.serialNumber)) {
1208
1281
  cursor = await dbTrx
1209
1282
  .objectStore('certificates')
1210
1283
  .index('userId_type_certifier_serialNumber')
1211
- .openCursor([args.partial.userId, args.partial.type, args.partial.certifier, args.partial.serialNumber]);
1284
+ .openCursor([args.partial.userId, args.partial.type, args.partial.certifier, args.partial.serialNumber], direction);
1212
1285
  }
1213
1286
  else {
1214
- cursor = await dbTrx.objectStore('certificates').index('userId').openCursor(args.partial.userId);
1287
+ cursor = await dbTrx.objectStore('certificates').index('userId').openCursor(args.partial.userId, direction);
1215
1288
  }
1216
1289
  }
1217
1290
  else {
1218
- cursor = await dbTrx.objectStore('certificates').openCursor();
1291
+ cursor = await dbTrx.objectStore('certificates').openCursor(null, direction);
1219
1292
  }
1220
1293
  let firstTime = true;
1221
1294
  while (cursor) {
@@ -1290,18 +1363,19 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
1290
1363
  let skipped = 0;
1291
1364
  let count = 0;
1292
1365
  const dbTrx = this.toDbTrx(['commissions'], 'readonly', args.trx);
1366
+ const direction = args.orderDescending ? 'prev' : 'next';
1293
1367
  let cursor;
1294
1368
  if ((_b = args.partial) === null || _b === void 0 ? void 0 : _b.commissionId) {
1295
- cursor = await dbTrx.objectStore('commissions').openCursor(args.partial.commissionId);
1369
+ cursor = await dbTrx.objectStore('commissions').openCursor(args.partial.commissionId, direction);
1296
1370
  }
1297
1371
  else if (((_c = args.partial) === null || _c === void 0 ? void 0 : _c.userId) !== undefined) {
1298
- cursor = await dbTrx.objectStore('commissions').index('userId').openCursor(args.partial.userId);
1372
+ cursor = await dbTrx.objectStore('commissions').index('userId').openCursor(args.partial.userId, direction);
1299
1373
  }
1300
1374
  else if (((_d = args.partial) === null || _d === void 0 ? void 0 : _d.transactionId) !== undefined) {
1301
- cursor = await dbTrx.objectStore('commissions').index('transactionId').openCursor(args.partial.transactionId);
1375
+ cursor = await dbTrx.objectStore('commissions').index('transactionId').openCursor(args.partial.transactionId, direction);
1302
1376
  }
1303
1377
  else {
1304
- cursor = await dbTrx.objectStore('commissions').openCursor();
1378
+ cursor = await dbTrx.objectStore('commissions').openCursor(null, direction);
1305
1379
  }
1306
1380
  let firstTime = true;
1307
1381
  while (cursor) {
@@ -1356,12 +1430,13 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
1356
1430
  let skipped = 0;
1357
1431
  let count = 0;
1358
1432
  const dbTrx = this.toDbTrx(['monitor_events'], 'readonly', args.trx);
1433
+ const direction = args.orderDescending ? 'prev' : 'next';
1359
1434
  let cursor;
1360
1435
  if ((_b = args.partial) === null || _b === void 0 ? void 0 : _b.id) {
1361
- cursor = await dbTrx.objectStore('monitor_events').openCursor(args.partial.id);
1436
+ cursor = await dbTrx.objectStore('monitor_events').openCursor(args.partial.id, direction);
1362
1437
  }
1363
1438
  else {
1364
- cursor = await dbTrx.objectStore('monitor_events').openCursor();
1439
+ cursor = await dbTrx.objectStore('monitor_events').openCursor(null, direction);
1365
1440
  }
1366
1441
  let firstTime = true;
1367
1442
  while (cursor) {
@@ -1410,23 +1485,24 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
1410
1485
  let skipped = 0;
1411
1486
  let count = 0;
1412
1487
  const dbTrx = this.toDbTrx(['output_baskets'], 'readonly', args.trx);
1488
+ const direction = args.orderDescending ? 'prev' : 'next';
1413
1489
  let cursor;
1414
1490
  if ((_b = args.partial) === null || _b === void 0 ? void 0 : _b.basketId) {
1415
- cursor = await dbTrx.objectStore('output_baskets').openCursor(args.partial.basketId);
1491
+ cursor = await dbTrx.objectStore('output_baskets').openCursor(args.partial.basketId, direction);
1416
1492
  }
1417
1493
  else if (((_c = args.partial) === null || _c === void 0 ? void 0 : _c.userId) !== undefined) {
1418
1494
  if (((_d = args.partial) === null || _d === void 0 ? void 0 : _d.name) !== undefined) {
1419
1495
  cursor = await dbTrx
1420
1496
  .objectStore('output_baskets')
1421
1497
  .index('name_userId')
1422
- .openCursor([args.partial.name, args.partial.userId]);
1498
+ .openCursor([args.partial.name, args.partial.userId], direction);
1423
1499
  }
1424
1500
  else {
1425
- cursor = await dbTrx.objectStore('output_baskets').index('userId').openCursor(args.partial.userId);
1501
+ cursor = await dbTrx.objectStore('output_baskets').index('userId').openCursor(args.partial.userId, direction);
1426
1502
  }
1427
1503
  }
1428
1504
  else {
1429
- cursor = await dbTrx.objectStore('output_baskets').openCursor();
1505
+ cursor = await dbTrx.objectStore('output_baskets').openCursor(null, direction);
1430
1506
  }
1431
1507
  let firstTime = true;
1432
1508
  while (cursor) {
@@ -1453,7 +1529,7 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
1453
1529
  r.numberOfDesiredUTXOs !== args.partial.numberOfDesiredUTXOs)
1454
1530
  continue;
1455
1531
  if (args.partial.minimumDesiredUTXOValue !== undefined &&
1456
- r.numberOfDesiredSatoshis !== args.partial.minimumDesiredUTXOValue)
1532
+ r.minimumDesiredUTXOValue !== args.partial.minimumDesiredUTXOValue)
1457
1533
  continue;
1458
1534
  if (args.partial.isDeleted !== undefined && r.isDeleted !== args.partial.isDeleted)
1459
1535
  continue;
@@ -1494,32 +1570,33 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
1494
1570
  stores.push('transactions');
1495
1571
  }
1496
1572
  const dbTrx = this.toDbTrx(stores, 'readonly', args.trx);
1573
+ const direction = args.orderDescending ? 'prev' : 'next';
1497
1574
  let cursor;
1498
1575
  if ((_b = args.partial) === null || _b === void 0 ? void 0 : _b.outputId) {
1499
- cursor = await dbTrx.objectStore('outputs').openCursor(args.partial.outputId);
1576
+ cursor = await dbTrx.objectStore('outputs').openCursor(args.partial.outputId, direction);
1500
1577
  }
1501
1578
  else if (((_c = args.partial) === null || _c === void 0 ? void 0 : _c.userId) !== undefined) {
1502
1579
  if (((_d = args.partial) === null || _d === void 0 ? void 0 : _d.transactionId) && ((_e = args.partial) === null || _e === void 0 ? void 0 : _e.vout) !== undefined) {
1503
1580
  cursor = await dbTrx
1504
1581
  .objectStore('outputs')
1505
1582
  .index('transactionId_vout_userId')
1506
- .openCursor([args.partial.transactionId, args.partial.vout, args.partial.userId]);
1583
+ .openCursor([args.partial.transactionId, args.partial.vout, args.partial.userId], direction);
1507
1584
  }
1508
1585
  else {
1509
- cursor = await dbTrx.objectStore('outputs').index('userId').openCursor(args.partial.userId);
1586
+ cursor = await dbTrx.objectStore('outputs').index('userId').openCursor(args.partial.userId, direction);
1510
1587
  }
1511
1588
  }
1512
1589
  else if (((_f = args.partial) === null || _f === void 0 ? void 0 : _f.transactionId) !== undefined) {
1513
- cursor = await dbTrx.objectStore('outputs').index('transactionId').openCursor(args.partial.transactionId);
1590
+ cursor = await dbTrx.objectStore('outputs').index('transactionId').openCursor(args.partial.transactionId, direction);
1514
1591
  }
1515
1592
  else if (((_g = args.partial) === null || _g === void 0 ? void 0 : _g.basketId) !== undefined) {
1516
- cursor = await dbTrx.objectStore('outputs').index('basketId').openCursor(args.partial.basketId);
1593
+ cursor = await dbTrx.objectStore('outputs').index('basketId').openCursor(args.partial.basketId, direction);
1517
1594
  }
1518
1595
  else if (((_h = args.partial) === null || _h === void 0 ? void 0 : _h.spentBy) !== undefined) {
1519
- cursor = await dbTrx.objectStore('outputs').index('spentBy').openCursor(args.partial.spentBy);
1596
+ cursor = await dbTrx.objectStore('outputs').index('spentBy').openCursor(args.partial.spentBy, direction);
1520
1597
  }
1521
1598
  else {
1522
- cursor = await dbTrx.objectStore('outputs').openCursor();
1599
+ cursor = await dbTrx.objectStore('outputs').openCursor(null, direction);
1523
1600
  }
1524
1601
  let firstTime = true;
1525
1602
  while (cursor) {
@@ -1628,7 +1705,9 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
1628
1705
  }, tagIds, isQueryModeAll);
1629
1706
  for (const o of results) {
1630
1707
  if (!args.noScript) {
1631
- await this.validateOutputScript(o);
1708
+ // Pass the transaction to avoid creating separate IDB operations
1709
+ // that would cause a passed transaction to auto-commit
1710
+ await this.validateOutputScript(o, args.trx);
1632
1711
  }
1633
1712
  else {
1634
1713
  o.lockingScript = undefined;
@@ -1642,23 +1721,24 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
1642
1721
  let skipped = 0;
1643
1722
  let count = 0;
1644
1723
  const dbTrx = this.toDbTrx(['output_tags'], 'readonly', args.trx);
1724
+ const direction = args.orderDescending ? 'prev' : 'next';
1645
1725
  let cursor;
1646
1726
  if ((_b = args.partial) === null || _b === void 0 ? void 0 : _b.outputTagId) {
1647
- cursor = await dbTrx.objectStore('output_tags').openCursor(args.partial.outputTagId);
1727
+ cursor = await dbTrx.objectStore('output_tags').openCursor(args.partial.outputTagId, direction);
1648
1728
  }
1649
1729
  else if (((_c = args.partial) === null || _c === void 0 ? void 0 : _c.userId) !== undefined) {
1650
1730
  if (((_d = args.partial) === null || _d === void 0 ? void 0 : _d.tag) !== undefined) {
1651
1731
  cursor = await dbTrx
1652
1732
  .objectStore('output_tags')
1653
1733
  .index('tag_userId')
1654
- .openCursor([args.partial.tag, args.partial.userId]);
1734
+ .openCursor([args.partial.tag, args.partial.userId], direction);
1655
1735
  }
1656
1736
  else {
1657
- cursor = await dbTrx.objectStore('output_tags').index('userId').openCursor(args.partial.userId);
1737
+ cursor = await dbTrx.objectStore('output_tags').index('userId').openCursor(args.partial.userId, direction);
1658
1738
  }
1659
1739
  }
1660
1740
  else {
1661
- cursor = await dbTrx.objectStore('output_tags').openCursor();
1741
+ cursor = await dbTrx.objectStore('output_tags').openCursor(null, direction);
1662
1742
  }
1663
1743
  let firstTime = true;
1664
1744
  while (cursor) {
@@ -1711,21 +1791,22 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
1711
1791
  let skipped = 0;
1712
1792
  let count = 0;
1713
1793
  const dbTrx = this.toDbTrx(['sync_states'], 'readonly', args.trx);
1794
+ const direction = args.orderDescending ? 'prev' : 'next';
1714
1795
  let cursor;
1715
1796
  if ((_b = args.partial) === null || _b === void 0 ? void 0 : _b.syncStateId) {
1716
- cursor = await dbTrx.objectStore('sync_states').openCursor(args.partial.syncStateId);
1797
+ cursor = await dbTrx.objectStore('sync_states').openCursor(args.partial.syncStateId, direction);
1717
1798
  }
1718
1799
  else if (((_c = args.partial) === null || _c === void 0 ? void 0 : _c.userId) !== undefined) {
1719
- cursor = await dbTrx.objectStore('sync_states').index('userId').openCursor(args.partial.userId);
1800
+ cursor = await dbTrx.objectStore('sync_states').index('userId').openCursor(args.partial.userId, direction);
1720
1801
  }
1721
1802
  else if (((_d = args.partial) === null || _d === void 0 ? void 0 : _d.refNum) !== undefined) {
1722
- cursor = await dbTrx.objectStore('sync_states').index('refNum').openCursor(args.partial.refNum);
1803
+ cursor = await dbTrx.objectStore('sync_states').index('refNum').openCursor(args.partial.refNum, direction);
1723
1804
  }
1724
1805
  else if (((_e = args.partial) === null || _e === void 0 ? void 0 : _e.status) !== undefined) {
1725
- cursor = await dbTrx.objectStore('sync_states').index('status').openCursor(args.partial.status);
1806
+ cursor = await dbTrx.objectStore('sync_states').index('status').openCursor(args.partial.status, direction);
1726
1807
  }
1727
1808
  else {
1728
- cursor = await dbTrx.objectStore('sync_states').openCursor();
1809
+ cursor = await dbTrx.objectStore('sync_states').openCursor(null, direction);
1729
1810
  }
1730
1811
  let firstTime = true;
1731
1812
  while (cursor) {
@@ -1760,7 +1841,7 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
1760
1841
  continue;
1761
1842
  if (args.partial.satoshis !== undefined && r.satoshis !== args.partial.satoshis)
1762
1843
  continue;
1763
- if (args.partial.errorLocal && r.errorLocale !== args.partial.errorLocal)
1844
+ if (args.partial.errorLocal && r.errorLocal !== args.partial.errorLocal)
1764
1845
  continue;
1765
1846
  if (args.partial.errorOther && r.errorOther !== args.partial.errorOther)
1766
1847
  continue;
@@ -1798,32 +1879,33 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
1798
1879
  stores.push('tx_labels_map');
1799
1880
  }
1800
1881
  const dbTrx = this.toDbTrx(stores, 'readonly', args.trx);
1882
+ const direction = args.orderDescending ? 'prev' : 'next';
1801
1883
  let cursor;
1802
1884
  if ((_b = args.partial) === null || _b === void 0 ? void 0 : _b.transactionId) {
1803
- cursor = await dbTrx.objectStore('transactions').openCursor(args.partial.transactionId);
1885
+ cursor = await dbTrx.objectStore('transactions').openCursor(args.partial.transactionId, direction);
1804
1886
  }
1805
1887
  else if (((_c = args.partial) === null || _c === void 0 ? void 0 : _c.userId) !== undefined) {
1806
1888
  if (((_d = args.partial) === null || _d === void 0 ? void 0 : _d.status) !== undefined) {
1807
1889
  cursor = await dbTrx
1808
1890
  .objectStore('transactions')
1809
1891
  .index('status_userId')
1810
- .openCursor([args.partial.status, args.partial.userId]);
1892
+ .openCursor([args.partial.status, args.partial.userId], direction);
1811
1893
  }
1812
1894
  else {
1813
- cursor = await dbTrx.objectStore('transactions').index('userId').openCursor(args.partial.userId);
1895
+ cursor = await dbTrx.objectStore('transactions').index('userId').openCursor(args.partial.userId, direction);
1814
1896
  }
1815
1897
  }
1816
1898
  else if (((_e = args.partial) === null || _e === void 0 ? void 0 : _e.status) !== undefined) {
1817
- cursor = await dbTrx.objectStore('transactions').index('status').openCursor(args.partial.status);
1899
+ cursor = await dbTrx.objectStore('transactions').index('status').openCursor(args.partial.status, direction);
1818
1900
  }
1819
1901
  else if (((_f = args.partial) === null || _f === void 0 ? void 0 : _f.provenTxId) !== undefined) {
1820
- cursor = await dbTrx.objectStore('transactions').index('provenTxId').openCursor(args.partial.provenTxId);
1902
+ cursor = await dbTrx.objectStore('transactions').index('provenTxId').openCursor(args.partial.provenTxId, direction);
1821
1903
  }
1822
1904
  else if (((_g = args.partial) === null || _g === void 0 ? void 0 : _g.reference) !== undefined) {
1823
- cursor = await dbTrx.objectStore('transactions').index('reference').openCursor(args.partial.reference);
1905
+ cursor = await dbTrx.objectStore('transactions').index('reference').openCursor(args.partial.reference, direction);
1824
1906
  }
1825
1907
  else {
1826
- cursor = await dbTrx.objectStore('transactions').openCursor();
1908
+ cursor = await dbTrx.objectStore('transactions').openCursor(null, direction);
1827
1909
  }
1828
1910
  let firstTime = true;
1829
1911
  while (cursor) {
@@ -1917,23 +1999,24 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
1917
1999
  let skipped = 0;
1918
2000
  let count = 0;
1919
2001
  const dbTrx = this.toDbTrx(['tx_labels'], 'readonly', args.trx);
2002
+ const direction = args.orderDescending ? 'prev' : 'next';
1920
2003
  let cursor;
1921
2004
  if ((_b = args.partial) === null || _b === void 0 ? void 0 : _b.txLabelId) {
1922
- cursor = await dbTrx.objectStore('tx_labels').openCursor(args.partial.txLabelId);
2005
+ cursor = await dbTrx.objectStore('tx_labels').openCursor(args.partial.txLabelId, direction);
1923
2006
  }
1924
2007
  else if (((_c = args.partial) === null || _c === void 0 ? void 0 : _c.userId) !== undefined) {
1925
2008
  if (((_d = args.partial) === null || _d === void 0 ? void 0 : _d.label) !== undefined) {
1926
2009
  cursor = await dbTrx
1927
2010
  .objectStore('tx_labels')
1928
2011
  .index('label_userId')
1929
- .openCursor([args.partial.label, args.partial.userId]);
2012
+ .openCursor([args.partial.label, args.partial.userId], direction);
1930
2013
  }
1931
2014
  else {
1932
- cursor = await dbTrx.objectStore('tx_labels').index('userId').openCursor(args.partial.userId);
2015
+ cursor = await dbTrx.objectStore('tx_labels').index('userId').openCursor(args.partial.userId, direction);
1933
2016
  }
1934
2017
  }
1935
2018
  else {
1936
- cursor = await dbTrx.objectStore('tx_labels').openCursor();
2019
+ cursor = await dbTrx.objectStore('tx_labels').openCursor(null, direction);
1937
2020
  }
1938
2021
  let firstTime = true;
1939
2022
  while (cursor) {
@@ -1984,7 +2067,8 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
1984
2067
  let skipped = 0;
1985
2068
  let count = 0;
1986
2069
  const dbTrx = this.toDbTrx(['users'], 'readonly', args.trx);
1987
- let cursor = await dbTrx.objectStore('users').openCursor();
2070
+ const direction = args.orderDescending ? 'prev' : 'next';
2071
+ let cursor = await dbTrx.objectStore('users').openCursor(null, direction);
1988
2072
  let firstTime = true;
1989
2073
  while (cursor) {
1990
2074
  if (!firstTime)