@breeztech/breez-sdk-spark 0.7.13 → 0.7.15-dev1
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/breez-sdk-spark.tgz +0 -0
- package/bundler/breez_sdk_spark_wasm.d.ts +658 -515
- package/bundler/breez_sdk_spark_wasm_bg.js +266 -32
- package/bundler/breez_sdk_spark_wasm_bg.wasm +0 -0
- package/bundler/breez_sdk_spark_wasm_bg.wasm.d.ts +9 -3
- package/bundler/storage/index.js +289 -32
- package/deno/breez_sdk_spark_wasm.d.ts +658 -515
- package/deno/breez_sdk_spark_wasm.js +241 -31
- package/deno/breez_sdk_spark_wasm_bg.wasm +0 -0
- package/deno/breez_sdk_spark_wasm_bg.wasm.d.ts +9 -3
- package/nodejs/breez_sdk_spark_wasm.d.ts +658 -515
- package/nodejs/breez_sdk_spark_wasm.js +267 -32
- package/nodejs/breez_sdk_spark_wasm_bg.wasm +0 -0
- package/nodejs/breez_sdk_spark_wasm_bg.wasm.d.ts +9 -3
- package/nodejs/storage/index.cjs +105 -112
- package/package.json +1 -1
- package/web/breez_sdk_spark_wasm.d.ts +667 -518
- package/web/breez_sdk_spark_wasm.js +241 -31
- package/web/breez_sdk_spark_wasm_bg.wasm +0 -0
- package/web/breez_sdk_spark_wasm_bg.wasm.d.ts +9 -3
- package/web/storage/index.js +289 -32
package/web/storage/index.js
CHANGED
|
@@ -236,6 +236,17 @@ class MigrationManager {
|
|
|
236
236
|
settings.delete("sync_initial_complete");
|
|
237
237
|
}
|
|
238
238
|
}
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
name: "Create parentPaymentId index for related payments lookup",
|
|
242
|
+
upgrade: (db, transaction) => {
|
|
243
|
+
if (db.objectStoreNames.contains("payment_metadata")) {
|
|
244
|
+
const metadataStore = transaction.objectStore("payment_metadata");
|
|
245
|
+
if (!metadataStore.indexNames.contains("parentPaymentId")) {
|
|
246
|
+
metadataStore.createIndex("parentPaymentId", "parentPaymentId", { unique: false });
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
239
250
|
}
|
|
240
251
|
];
|
|
241
252
|
}
|
|
@@ -260,7 +271,7 @@ class IndexedDBStorage {
|
|
|
260
271
|
this.db = null;
|
|
261
272
|
this.migrationManager = null;
|
|
262
273
|
this.logger = logger;
|
|
263
|
-
this.dbVersion =
|
|
274
|
+
this.dbVersion = 8; // Current schema version
|
|
264
275
|
}
|
|
265
276
|
|
|
266
277
|
/**
|
|
@@ -418,6 +429,59 @@ class IndexedDBStorage {
|
|
|
418
429
|
|
|
419
430
|
// ===== Payment Operations =====
|
|
420
431
|
|
|
432
|
+
/**
|
|
433
|
+
* Gets the set of payment IDs that are related payments (have a parentPaymentId).
|
|
434
|
+
* Uses the parentPaymentId index for efficient lookup.
|
|
435
|
+
* @param {IDBObjectStore} metadataStore - The payment_metadata object store
|
|
436
|
+
* @returns {Promise<Set<string>>} Set of payment IDs that are related payments
|
|
437
|
+
*/
|
|
438
|
+
_getRelatedPaymentIds(metadataStore) {
|
|
439
|
+
return new Promise((resolve) => {
|
|
440
|
+
const relatedPaymentIds = new Set();
|
|
441
|
+
|
|
442
|
+
// Check if the parentPaymentId index exists (added in migration)
|
|
443
|
+
if (!metadataStore.indexNames.contains("parentPaymentId")) {
|
|
444
|
+
// Index doesn't exist yet, fall back to scanning all metadata
|
|
445
|
+
const cursorRequest = metadataStore.openCursor();
|
|
446
|
+
cursorRequest.onsuccess = (event) => {
|
|
447
|
+
const cursor = event.target.result;
|
|
448
|
+
if (cursor) {
|
|
449
|
+
if (cursor.value.parentPaymentId) {
|
|
450
|
+
relatedPaymentIds.add(cursor.value.paymentId);
|
|
451
|
+
}
|
|
452
|
+
cursor.continue();
|
|
453
|
+
} else {
|
|
454
|
+
resolve(relatedPaymentIds);
|
|
455
|
+
}
|
|
456
|
+
};
|
|
457
|
+
cursorRequest.onerror = () => resolve(new Set());
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// Use the parentPaymentId index to find all metadata entries with a parent
|
|
462
|
+
const index = metadataStore.index("parentPaymentId");
|
|
463
|
+
const cursorRequest = index.openCursor();
|
|
464
|
+
|
|
465
|
+
cursorRequest.onsuccess = (event) => {
|
|
466
|
+
const cursor = event.target.result;
|
|
467
|
+
if (cursor) {
|
|
468
|
+
// Only add if parentPaymentId is truthy (not null/undefined)
|
|
469
|
+
if (cursor.value.parentPaymentId) {
|
|
470
|
+
relatedPaymentIds.add(cursor.value.paymentId);
|
|
471
|
+
}
|
|
472
|
+
cursor.continue();
|
|
473
|
+
} else {
|
|
474
|
+
resolve(relatedPaymentIds);
|
|
475
|
+
}
|
|
476
|
+
};
|
|
477
|
+
|
|
478
|
+
cursorRequest.onerror = () => {
|
|
479
|
+
// If index lookup fails, return empty set and fall back to per-payment lookup
|
|
480
|
+
resolve(new Set());
|
|
481
|
+
};
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
|
|
421
485
|
async listPayments(request) {
|
|
422
486
|
if (!this.db) {
|
|
423
487
|
throw new StorageError("Database not initialized");
|
|
@@ -427,15 +491,18 @@ class IndexedDBStorage {
|
|
|
427
491
|
const actualOffset = request.offset !== null ? request.offset : 0;
|
|
428
492
|
const actualLimit = request.limit !== null ? request.limit : 4294967295; // u32::MAX
|
|
429
493
|
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
const lnurlReceiveMetadataStore = transaction.objectStore("lnurl_receive_metadata");
|
|
494
|
+
const transaction = this.db.transaction(
|
|
495
|
+
["payments", "payment_metadata", "lnurl_receive_metadata"],
|
|
496
|
+
"readonly"
|
|
497
|
+
);
|
|
498
|
+
const paymentStore = transaction.objectStore("payments");
|
|
499
|
+
const metadataStore = transaction.objectStore("payment_metadata");
|
|
500
|
+
const lnurlReceiveMetadataStore = transaction.objectStore("lnurl_receive_metadata");
|
|
438
501
|
|
|
502
|
+
// Build set of related payment IDs upfront for O(1) filtering
|
|
503
|
+
const relatedPaymentIds = await this._getRelatedPaymentIds(metadataStore);
|
|
504
|
+
|
|
505
|
+
return new Promise((resolve, reject) => {
|
|
439
506
|
const payments = [];
|
|
440
507
|
let count = 0;
|
|
441
508
|
let skipped = 0;
|
|
@@ -458,16 +525,23 @@ class IndexedDBStorage {
|
|
|
458
525
|
|
|
459
526
|
const payment = cursor.value;
|
|
460
527
|
|
|
528
|
+
// Skip related payments (those with a parentPaymentId)
|
|
529
|
+
if (relatedPaymentIds.has(payment.id)) {
|
|
530
|
+
cursor.continue();
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
|
|
461
534
|
if (skipped < actualOffset) {
|
|
462
535
|
skipped++;
|
|
463
536
|
cursor.continue();
|
|
464
537
|
return;
|
|
465
538
|
}
|
|
466
539
|
|
|
467
|
-
// Get metadata for this payment
|
|
540
|
+
// Get metadata for this payment (now only for non-related payments)
|
|
468
541
|
const metadataRequest = metadataStore.get(payment.id);
|
|
469
542
|
metadataRequest.onsuccess = () => {
|
|
470
543
|
const metadata = metadataRequest.result;
|
|
544
|
+
|
|
471
545
|
const paymentWithMetadata = this._mergePaymentMetadata(
|
|
472
546
|
payment,
|
|
473
547
|
metadata
|
|
@@ -478,7 +552,7 @@ class IndexedDBStorage {
|
|
|
478
552
|
cursor.continue();
|
|
479
553
|
return;
|
|
480
554
|
}
|
|
481
|
-
|
|
555
|
+
|
|
482
556
|
// Fetch lnurl receive metadata if it's a lightning payment
|
|
483
557
|
this._fetchLnurlReceiveMetadata(paymentWithMetadata, lnurlReceiveMetadataStore)
|
|
484
558
|
.then((mergedPayment) => {
|
|
@@ -667,7 +741,173 @@ class IndexedDBStorage {
|
|
|
667
741
|
});
|
|
668
742
|
}
|
|
669
743
|
|
|
670
|
-
|
|
744
|
+
/**
|
|
745
|
+
* Checks if any related payments exist (payments with a parentPaymentId).
|
|
746
|
+
* Uses the parentPaymentId index for efficient lookup.
|
|
747
|
+
* @param {IDBObjectStore} metadataStore - The payment_metadata object store
|
|
748
|
+
* @returns {Promise<boolean>} True if any related payments exist
|
|
749
|
+
*/
|
|
750
|
+
_hasRelatedPayments(metadataStore) {
|
|
751
|
+
return new Promise((resolve) => {
|
|
752
|
+
// Check if the parentPaymentId index exists (added in migration)
|
|
753
|
+
if (!metadataStore.indexNames.contains("parentPaymentId")) {
|
|
754
|
+
// Index doesn't exist yet, fall back to scanning all metadata
|
|
755
|
+
const cursorRequest = metadataStore.openCursor();
|
|
756
|
+
cursorRequest.onsuccess = (event) => {
|
|
757
|
+
const cursor = event.target.result;
|
|
758
|
+
if (cursor) {
|
|
759
|
+
if (cursor.value.parentPaymentId) {
|
|
760
|
+
resolve(true);
|
|
761
|
+
return;
|
|
762
|
+
}
|
|
763
|
+
cursor.continue();
|
|
764
|
+
} else {
|
|
765
|
+
resolve(false);
|
|
766
|
+
}
|
|
767
|
+
};
|
|
768
|
+
cursorRequest.onerror = () => resolve(true); // Assume there might be related payments on error
|
|
769
|
+
return;
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
const index = metadataStore.index("parentPaymentId");
|
|
773
|
+
const cursorRequest = index.openCursor();
|
|
774
|
+
|
|
775
|
+
cursorRequest.onsuccess = (event) => {
|
|
776
|
+
const cursor = event.target.result;
|
|
777
|
+
if (cursor && cursor.value.parentPaymentId) {
|
|
778
|
+
// Found at least one related payment
|
|
779
|
+
resolve(true);
|
|
780
|
+
} else if (cursor) {
|
|
781
|
+
// Entry with null parentPaymentId, continue searching
|
|
782
|
+
cursor.continue();
|
|
783
|
+
} else {
|
|
784
|
+
// No more entries
|
|
785
|
+
resolve(false);
|
|
786
|
+
}
|
|
787
|
+
};
|
|
788
|
+
|
|
789
|
+
cursorRequest.onerror = () => {
|
|
790
|
+
// If index lookup fails, assume there might be related payments
|
|
791
|
+
resolve(true);
|
|
792
|
+
};
|
|
793
|
+
});
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
/**
|
|
797
|
+
* Gets payments that have any of the specified parent payment IDs.
|
|
798
|
+
* @param {string[]} parentPaymentIds - Array of parent payment IDs
|
|
799
|
+
* @returns {Promise<Object>} Map of parentPaymentId -> array of RelatedPayment objects
|
|
800
|
+
*/
|
|
801
|
+
async getPaymentsByParentIds(parentPaymentIds) {
|
|
802
|
+
if (!this.db) {
|
|
803
|
+
throw new StorageError("Database not initialized");
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
if (!parentPaymentIds || parentPaymentIds.length === 0) {
|
|
807
|
+
return {};
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
const transaction = this.db.transaction(
|
|
811
|
+
["payments", "payment_metadata", "lnurl_receive_metadata"],
|
|
812
|
+
"readonly"
|
|
813
|
+
);
|
|
814
|
+
const metadataStore = transaction.objectStore("payment_metadata");
|
|
815
|
+
|
|
816
|
+
// Early exit if no related payments exist
|
|
817
|
+
const hasRelated = await this._hasRelatedPayments(metadataStore);
|
|
818
|
+
if (!hasRelated) {
|
|
819
|
+
return {};
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
const parentIdSet = new Set(parentPaymentIds);
|
|
823
|
+
const paymentStore = transaction.objectStore("payments");
|
|
824
|
+
const lnurlReceiveMetadataStore = transaction.objectStore("lnurl_receive_metadata");
|
|
825
|
+
|
|
826
|
+
return new Promise((resolve, reject) => {
|
|
827
|
+
const result = {};
|
|
828
|
+
const fetchedMetadata = [];
|
|
829
|
+
|
|
830
|
+
// Query all metadata records and filter by parentPaymentId
|
|
831
|
+
const cursorRequest = metadataStore.openCursor();
|
|
832
|
+
|
|
833
|
+
cursorRequest.onsuccess = (event) => {
|
|
834
|
+
const cursor = event.target.result;
|
|
835
|
+
if (!cursor) {
|
|
836
|
+
// All metadata processed, now fetch payment details
|
|
837
|
+
if (fetchedMetadata.length === 0) {
|
|
838
|
+
resolve(result);
|
|
839
|
+
return;
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
let processed = 0;
|
|
843
|
+
for (const metadata of fetchedMetadata) {
|
|
844
|
+
const parentId = metadata.parentPaymentId;
|
|
845
|
+
const paymentRequest = paymentStore.get(metadata.paymentId);
|
|
846
|
+
paymentRequest.onsuccess = () => {
|
|
847
|
+
const payment = paymentRequest.result;
|
|
848
|
+
if (payment) {
|
|
849
|
+
const paymentWithMetadata = this._mergePaymentMetadata(payment, metadata);
|
|
850
|
+
|
|
851
|
+
if (!result[parentId]) {
|
|
852
|
+
result[parentId] = [];
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
// Fetch lnurl receive metadata if applicable
|
|
856
|
+
this._fetchLnurlReceiveMetadata(paymentWithMetadata, lnurlReceiveMetadataStore)
|
|
857
|
+
.then((mergedPayment) => {
|
|
858
|
+
result[parentId].push(mergedPayment);
|
|
859
|
+
})
|
|
860
|
+
.catch(() => {
|
|
861
|
+
result[parentId].push(paymentWithMetadata);
|
|
862
|
+
})
|
|
863
|
+
.finally(() => {
|
|
864
|
+
processed++;
|
|
865
|
+
if (processed === fetchedMetadata.length) {
|
|
866
|
+
// Sort each parent's children by timestamp
|
|
867
|
+
for (const parentId of Object.keys(result)) {
|
|
868
|
+
result[parentId].sort((a, b) => a.timestamp - b.timestamp);
|
|
869
|
+
}
|
|
870
|
+
resolve(result);
|
|
871
|
+
}
|
|
872
|
+
});
|
|
873
|
+
} else {
|
|
874
|
+
processed++;
|
|
875
|
+
if (processed === fetchedMetadata.length) {
|
|
876
|
+
resolve(result);
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
};
|
|
880
|
+
paymentRequest.onerror = () => {
|
|
881
|
+
processed++;
|
|
882
|
+
if (processed === fetchedMetadata.length) {
|
|
883
|
+
resolve(result);
|
|
884
|
+
}
|
|
885
|
+
};
|
|
886
|
+
}
|
|
887
|
+
return;
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
const metadata = cursor.value;
|
|
891
|
+
if (metadata.parentPaymentId && parentIdSet.has(metadata.parentPaymentId)) {
|
|
892
|
+
fetchedMetadata.push(metadata);
|
|
893
|
+
}
|
|
894
|
+
cursor.continue();
|
|
895
|
+
};
|
|
896
|
+
|
|
897
|
+
cursorRequest.onerror = () => {
|
|
898
|
+
reject(
|
|
899
|
+
new StorageError(
|
|
900
|
+
`Failed to get payments by parent ids: ${
|
|
901
|
+
cursorRequest.error?.message || "Unknown error"
|
|
902
|
+
}`,
|
|
903
|
+
cursorRequest.error
|
|
904
|
+
)
|
|
905
|
+
);
|
|
906
|
+
};
|
|
907
|
+
});
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
async insertPaymentMetadata(paymentId, metadata) {
|
|
671
911
|
if (!this.db) {
|
|
672
912
|
throw new StorageError("Database not initialized");
|
|
673
913
|
}
|
|
@@ -676,30 +916,47 @@ class IndexedDBStorage {
|
|
|
676
916
|
const transaction = this.db.transaction("payment_metadata", "readwrite");
|
|
677
917
|
const store = transaction.objectStore("payment_metadata");
|
|
678
918
|
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
: null,
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
:
|
|
692
|
-
|
|
919
|
+
// First get existing record to merge with
|
|
920
|
+
const getRequest = store.get(paymentId);
|
|
921
|
+
getRequest.onsuccess = () => {
|
|
922
|
+
const existing = getRequest.result || {};
|
|
923
|
+
|
|
924
|
+
// Use COALESCE-like behavior: new value if non-null, otherwise keep existing
|
|
925
|
+
const metadataToStore = {
|
|
926
|
+
paymentId,
|
|
927
|
+
parentPaymentId: metadata.parentPaymentId ?? existing.parentPaymentId ?? null,
|
|
928
|
+
lnurlPayInfo: metadata.lnurlPayInfo
|
|
929
|
+
? JSON.stringify(metadata.lnurlPayInfo)
|
|
930
|
+
: existing.lnurlPayInfo ?? null,
|
|
931
|
+
lnurlWithdrawInfo: metadata.lnurlWithdrawInfo
|
|
932
|
+
? JSON.stringify(metadata.lnurlWithdrawInfo)
|
|
933
|
+
: existing.lnurlWithdrawInfo ?? null,
|
|
934
|
+
lnurlDescription: metadata.lnurlDescription ?? existing.lnurlDescription ?? null,
|
|
935
|
+
conversionInfo: metadata.conversionInfo
|
|
936
|
+
? JSON.stringify(metadata.conversionInfo)
|
|
937
|
+
: existing.conversionInfo ?? null,
|
|
938
|
+
};
|
|
693
939
|
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
940
|
+
const putRequest = store.put(metadataToStore);
|
|
941
|
+
putRequest.onsuccess = () => resolve();
|
|
942
|
+
putRequest.onerror = () => {
|
|
943
|
+
reject(
|
|
944
|
+
new StorageError(
|
|
945
|
+
`Failed to set payment metadata for '${paymentId}': ${
|
|
946
|
+
putRequest.error?.message || "Unknown error"
|
|
947
|
+
}`,
|
|
948
|
+
putRequest.error
|
|
949
|
+
)
|
|
950
|
+
);
|
|
951
|
+
};
|
|
952
|
+
};
|
|
953
|
+
getRequest.onerror = () => {
|
|
697
954
|
reject(
|
|
698
955
|
new StorageError(
|
|
699
|
-
`Failed to
|
|
700
|
-
|
|
956
|
+
`Failed to get existing payment metadata for '${paymentId}': ${
|
|
957
|
+
getRequest.error?.message || "Unknown error"
|
|
701
958
|
}`,
|
|
702
|
-
|
|
959
|
+
getRequest.error
|
|
703
960
|
)
|
|
704
961
|
);
|
|
705
962
|
};
|