@breeztech/breez-sdk-spark 0.9.1 → 0.11.0-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.
@@ -330,6 +330,17 @@ class MigrationManager {
330
330
  if (db.objectStoreNames.contains("settings")) {
331
331
  transaction.objectStore("settings").delete("sync_initial_complete");
332
332
  }
333
+ },
334
+ },
335
+ {
336
+ name: "Add preimage to lnurl_receive_metadata for LUD-21 and NIP-57",
337
+ upgrade: (db, transaction) => {
338
+ // IndexedDB doesn't need schema changes for new fields on existing stores.
339
+ // Just clear the lnurl_metadata_updated_after setting to force re-sync.
340
+ if (db.objectStoreNames.contains("settings")) {
341
+ const settings = transaction.objectStore("settings");
342
+ settings.delete("lnurl_metadata_updated_after");
343
+ }
333
344
  }
334
345
  },
335
346
  {
@@ -397,7 +408,26 @@ class MigrationManager {
397
408
  };
398
409
  }
399
410
  },
400
- }
411
+ },
412
+ {
413
+ name: "Create contacts store",
414
+ upgrade: (db) => {
415
+ if (!db.objectStoreNames.contains("contacts")) {
416
+ const contactsStore = db.createObjectStore("contacts", { keyPath: "id" });
417
+ contactsStore.createIndex("name_identifier", ["name", "paymentIdentifier"], { unique: false });
418
+ contactsStore.createIndex("name", "name", { unique: false });
419
+ }
420
+ }
421
+ },
422
+ {
423
+ name: "Clear cached lightning address for LnurlInfo schema change",
424
+ upgrade: (db, transaction) => {
425
+ if (db.objectStoreNames.contains("settings")) {
426
+ const settings = transaction.objectStore("settings");
427
+ settings.delete("lightning_address");
428
+ }
429
+ }
430
+ },
401
431
  ];
402
432
  }
403
433
  }
@@ -421,7 +451,7 @@ class IndexedDBStorage {
421
451
  this.db = null;
422
452
  this.migrationManager = null;
423
453
  this.logger = logger;
424
- this.dbVersion = 11; // Current schema version
454
+ this.dbVersion = 14; // Current schema version
425
455
  }
426
456
 
427
457
  /**
@@ -441,8 +471,7 @@ class IndexedDBStorage {
441
471
 
442
472
  request.onerror = () => {
443
473
  const error = new StorageError(
444
- `Failed to open IndexedDB: ${
445
- request.error?.message || "Unknown error"
474
+ `Failed to open IndexedDB: ${request.error?.message || "Unknown error"
446
475
  }`,
447
476
  request.error
448
477
  );
@@ -517,8 +546,7 @@ class IndexedDBStorage {
517
546
  request.onerror = () => {
518
547
  reject(
519
548
  new StorageError(
520
- `Failed to get cached item '${key}': ${
521
- request.error?.message || "Unknown error"
549
+ `Failed to get cached item '${key}': ${request.error?.message || "Unknown error"
522
550
  }`,
523
551
  request.error
524
552
  )
@@ -542,8 +570,7 @@ class IndexedDBStorage {
542
570
  request.onerror = () => {
543
571
  reject(
544
572
  new StorageError(
545
- `Failed to set cached item '${key}': ${
546
- request.error?.message || "Unknown error"
573
+ `Failed to set cached item '${key}': ${request.error?.message || "Unknown error"
547
574
  }`,
548
575
  request.error
549
576
  )
@@ -567,8 +594,7 @@ class IndexedDBStorage {
567
594
  request.onerror = () => {
568
595
  reject(
569
596
  new StorageError(
570
- `Failed to delete cached item '${key}': ${
571
- request.error?.message || "Unknown error"
597
+ `Failed to delete cached item '${key}': ${request.error?.message || "Unknown error"
572
598
  }`,
573
599
  request.error
574
600
  )
@@ -697,24 +723,28 @@ class IndexedDBStorage {
697
723
  metadata
698
724
  );
699
725
 
700
- // Apply filters
701
- if (!this._matchesFilters(paymentWithMetadata, request)) {
702
- cursor.continue();
703
- return;
704
- }
705
-
706
- // Fetch lnurl receive metadata if it's a lightning payment
726
+ // Fetch lnurl receive metadata before filtering, so Lightning
727
+ // filters can check lnurlReceiveMetadata fields
707
728
  this._fetchLnurlReceiveMetadata(
708
729
  paymentWithMetadata,
709
730
  lnurlReceiveMetadataStore
710
731
  )
711
732
  .then((mergedPayment) => {
733
+ // Apply filters after lnurl metadata is populated
734
+ if (!this._matchesFilters(mergedPayment, request)) {
735
+ cursor.continue();
736
+ return;
737
+ }
712
738
  payments.push(mergedPayment);
713
739
  count++;
714
740
  cursor.continue();
715
741
  })
716
742
  .catch(() => {
717
- // Continue without lnurl receive metadata if fetch fails
743
+ // Apply filters even if lnurl metadata fetch fails
744
+ if (!this._matchesFilters(paymentWithMetadata, request)) {
745
+ cursor.continue();
746
+ return;
747
+ }
718
748
  payments.push(paymentWithMetadata);
719
749
  count++;
720
750
  cursor.continue();
@@ -734,8 +764,7 @@ class IndexedDBStorage {
734
764
  cursorRequest.onerror = () => {
735
765
  reject(
736
766
  new StorageError(
737
- `Failed to list payments (request: ${JSON.stringify(request)}: ${
738
- cursorRequest.error?.message || "Unknown error"
767
+ `Failed to list payments (request: ${JSON.stringify(request)}: ${cursorRequest.error?.message || "Unknown error"
739
768
  }`,
740
769
  cursorRequest.error
741
770
  )
@@ -765,8 +794,7 @@ class IndexedDBStorage {
765
794
  request.onerror = () => {
766
795
  reject(
767
796
  new StorageError(
768
- `Failed to insert payment '${payment.id}': ${
769
- request.error?.message || "Unknown error"
797
+ `Failed to insert payment '${payment.id}': ${request.error?.message || "Unknown error"
770
798
  }`,
771
799
  request.error
772
800
  )
@@ -829,8 +857,7 @@ class IndexedDBStorage {
829
857
  paymentRequest.onerror = () => {
830
858
  reject(
831
859
  new StorageError(
832
- `Failed to get payment by id '${id}': ${
833
- paymentRequest.error?.message || "Unknown error"
860
+ `Failed to get payment by id '${id}': ${paymentRequest.error?.message || "Unknown error"
834
861
  }`,
835
862
  paymentRequest.error
836
863
  )
@@ -894,8 +921,7 @@ class IndexedDBStorage {
894
921
  paymentRequest.onerror = () => {
895
922
  reject(
896
923
  new StorageError(
897
- `Failed to get payment by invoice '${invoice}': ${
898
- paymentRequest.error?.message || "Unknown error"
924
+ `Failed to get payment by invoice '${invoice}': ${paymentRequest.error?.message || "Unknown error"
899
925
  }`,
900
926
  paymentRequest.error
901
927
  )
@@ -1060,8 +1086,7 @@ class IndexedDBStorage {
1060
1086
  cursorRequest.onerror = () => {
1061
1087
  reject(
1062
1088
  new StorageError(
1063
- `Failed to get payments by parent ids: ${
1064
- cursorRequest.error?.message || "Unknown error"
1089
+ `Failed to get payments by parent ids: ${cursorRequest.error?.message || "Unknown error"
1065
1090
  }`,
1066
1091
  cursorRequest.error
1067
1092
  )
@@ -1105,8 +1130,7 @@ class IndexedDBStorage {
1105
1130
  putRequest.onerror = () => {
1106
1131
  reject(
1107
1132
  new StorageError(
1108
- `Failed to set payment metadata for '${paymentId}': ${
1109
- putRequest.error?.message || "Unknown error"
1133
+ `Failed to set payment metadata for '${paymentId}': ${putRequest.error?.message || "Unknown error"
1110
1134
  }`,
1111
1135
  putRequest.error
1112
1136
  )
@@ -1116,8 +1140,7 @@ class IndexedDBStorage {
1116
1140
  getRequest.onerror = () => {
1117
1141
  reject(
1118
1142
  new StorageError(
1119
- `Failed to get existing payment metadata for '${paymentId}': ${
1120
- getRequest.error?.message || "Unknown error"
1143
+ `Failed to get existing payment metadata for '${paymentId}': ${getRequest.error?.message || "Unknown error"
1121
1144
  }`,
1122
1145
  getRequest.error
1123
1146
  )
@@ -1154,8 +1177,7 @@ class IndexedDBStorage {
1154
1177
  request.onerror = () => {
1155
1178
  reject(
1156
1179
  new StorageError(
1157
- `Failed to add deposit '${txid}:${vout}': ${
1158
- request.error?.message || "Unknown error"
1180
+ `Failed to add deposit '${txid}:${vout}': ${request.error?.message || "Unknown error"
1159
1181
  }`,
1160
1182
  request.error
1161
1183
  )
@@ -1181,8 +1203,7 @@ class IndexedDBStorage {
1181
1203
  request.onerror = () => {
1182
1204
  reject(
1183
1205
  new StorageError(
1184
- `Failed to delete deposit '${txid}:${vout}': ${
1185
- request.error?.message || "Unknown error"
1206
+ `Failed to delete deposit '${txid}:${vout}': ${request.error?.message || "Unknown error"
1186
1207
  }`,
1187
1208
  request.error
1188
1209
  )
@@ -1216,8 +1237,7 @@ class IndexedDBStorage {
1216
1237
  request.onerror = () => {
1217
1238
  reject(
1218
1239
  new StorageError(
1219
- `Failed to list deposits: ${
1220
- request.error?.message || "Unknown error"
1240
+ `Failed to list deposits: ${request.error?.message || "Unknown error"
1221
1241
  }`,
1222
1242
  request.error
1223
1243
  )
@@ -1269,8 +1289,7 @@ class IndexedDBStorage {
1269
1289
  putRequest.onerror = () => {
1270
1290
  reject(
1271
1291
  new StorageError(
1272
- `Failed to update deposit '${txid}:${vout}': ${
1273
- putRequest.error?.message || "Unknown error"
1292
+ `Failed to update deposit '${txid}:${vout}': ${putRequest.error?.message || "Unknown error"
1274
1293
  }`,
1275
1294
  putRequest.error
1276
1295
  )
@@ -1281,8 +1300,7 @@ class IndexedDBStorage {
1281
1300
  getRequest.onerror = () => {
1282
1301
  reject(
1283
1302
  new StorageError(
1284
- `Failed to get deposit '${txid}:${vout}' for update: ${
1285
- getRequest.error?.message || "Unknown error"
1303
+ `Failed to get deposit '${txid}:${vout}' for update: ${getRequest.error?.message || "Unknown error"
1286
1304
  }`,
1287
1305
  getRequest.error
1288
1306
  )
@@ -1317,6 +1335,7 @@ class IndexedDBStorage {
1317
1335
  nostrZapRequest: item.nostrZapRequest || null,
1318
1336
  nostrZapReceipt: item.nostrZapReceipt || null,
1319
1337
  senderComment: item.senderComment || null,
1338
+ preimage: item.preimage || null,
1320
1339
  });
1321
1340
 
1322
1341
  request.onsuccess = () => {
@@ -1329,8 +1348,7 @@ class IndexedDBStorage {
1329
1348
  request.onerror = () => {
1330
1349
  reject(
1331
1350
  new StorageError(
1332
- `Failed to add lnurl metadata for payment hash '${
1333
- item.paymentHash
1351
+ `Failed to add lnurl metadata for payment hash '${item.paymentHash
1334
1352
  }': ${request.error?.message || "Unknown error"}`,
1335
1353
  request.error
1336
1354
  )
@@ -1816,6 +1834,144 @@ class IndexedDBStorage {
1816
1834
  });
1817
1835
  }
1818
1836
 
1837
+ // ===== Contact Operations =====
1838
+
1839
+ async listContacts(request) {
1840
+ if (!this.db) {
1841
+ throw new StorageError("Database not initialized");
1842
+ }
1843
+
1844
+ const actualOffset = request.offset !== null && request.offset !== undefined ? request.offset : 0;
1845
+ const actualLimit = request.limit !== null && request.limit !== undefined ? request.limit : 4294967295;
1846
+
1847
+ return new Promise((resolve, reject) => {
1848
+ const transaction = this.db.transaction("contacts", "readonly");
1849
+ const store = transaction.objectStore("contacts");
1850
+ const nameIndex = store.index("name");
1851
+
1852
+ const contacts = [];
1853
+ let count = 0;
1854
+ let skipped = 0;
1855
+
1856
+ const cursorRequest = nameIndex.openCursor();
1857
+
1858
+ cursorRequest.onsuccess = (event) => {
1859
+ const cursor = event.target.result;
1860
+
1861
+ if (!cursor || count >= actualLimit) {
1862
+ resolve(contacts);
1863
+ return;
1864
+ }
1865
+
1866
+ if (skipped < actualOffset) {
1867
+ skipped++;
1868
+ cursor.continue();
1869
+ return;
1870
+ }
1871
+
1872
+ contacts.push(cursor.value);
1873
+ count++;
1874
+ cursor.continue();
1875
+ };
1876
+
1877
+ cursorRequest.onerror = () => {
1878
+ reject(
1879
+ new StorageError(
1880
+ `Failed to list contacts: ${cursorRequest.error?.message || "Unknown error"}`,
1881
+ cursorRequest.error
1882
+ )
1883
+ );
1884
+ };
1885
+ });
1886
+ }
1887
+
1888
+ async getContact(id) {
1889
+ if (!this.db) {
1890
+ throw new StorageError("Database not initialized");
1891
+ }
1892
+
1893
+ return new Promise((resolve, reject) => {
1894
+ const transaction = this.db.transaction("contacts", "readonly");
1895
+ const store = transaction.objectStore("contacts");
1896
+ const request = store.get(id);
1897
+
1898
+ request.onsuccess = () => {
1899
+ resolve(request.result || null);
1900
+ };
1901
+
1902
+ request.onerror = () => {
1903
+ reject(
1904
+ new StorageError(
1905
+ `Failed to get contact '${id}': ${request.error?.message || "Unknown error"}`,
1906
+ request.error
1907
+ )
1908
+ );
1909
+ };
1910
+ });
1911
+ }
1912
+
1913
+ async insertContact(contact) {
1914
+ if (!this.db) {
1915
+ throw new StorageError("Database not initialized");
1916
+ }
1917
+
1918
+ return new Promise((resolve, reject) => {
1919
+ const transaction = this.db.transaction("contacts", "readwrite");
1920
+ const store = transaction.objectStore("contacts");
1921
+
1922
+ // Preserve created_at from existing record on update
1923
+ const getRequest = store.get(contact.id);
1924
+ getRequest.onsuccess = () => {
1925
+ const existingById = getRequest.result;
1926
+ const toStore = existingById
1927
+ ? { ...contact, createdAt: existingById.createdAt }
1928
+ : contact;
1929
+
1930
+ const putRequest = store.put(toStore);
1931
+ putRequest.onsuccess = () => resolve();
1932
+ putRequest.onerror = () => {
1933
+ reject(
1934
+ new StorageError(
1935
+ `Inserting contact failed: ${putRequest.error?.name || "Unknown error"} - ${putRequest.error?.message || ""}`,
1936
+ putRequest.error
1937
+ )
1938
+ );
1939
+ };
1940
+ };
1941
+ getRequest.onerror = () => {
1942
+ reject(
1943
+ new StorageError(
1944
+ `Failed to check existing contact: ${getRequest.error?.message || "Unknown error"}`,
1945
+ getRequest.error
1946
+ )
1947
+ );
1948
+ };
1949
+ });
1950
+ }
1951
+
1952
+ async deleteContact(id) {
1953
+ if (!this.db) {
1954
+ throw new StorageError("Database not initialized");
1955
+ }
1956
+
1957
+ return new Promise((resolve, reject) => {
1958
+ const transaction = this.db.transaction("contacts", "readwrite");
1959
+ const store = transaction.objectStore("contacts");
1960
+ const request = store.delete(id);
1961
+
1962
+ request.onsuccess = () => resolve();
1963
+
1964
+ request.onerror = () => {
1965
+ reject(
1966
+ new StorageError(
1967
+ `Failed to delete contact '${id}': ${request.error?.message || "Unknown error"}`,
1968
+ request.error
1969
+ )
1970
+ );
1971
+ };
1972
+ });
1973
+ }
1974
+
1819
1975
  // ===== Private Helper Methods =====
1820
1976
 
1821
1977
  _matchesFilters(payment, request) {
@@ -1933,6 +2089,33 @@ class IndexedDBStorage {
1933
2089
  continue;
1934
2090
  }
1935
2091
  }
2092
+ // Filter by LNURL preimage status
2093
+ if (
2094
+ paymentDetailsFilter.type === "lightning" &&
2095
+ paymentDetailsFilter.hasLnurlPreimage != null
2096
+ ) {
2097
+ if (details.type !== "lightning") {
2098
+ continue;
2099
+ }
2100
+ if (paymentDetailsFilter.hasLnurlPreimage) {
2101
+ // Has lnurl preimage - check lnurlReceiveMetadata.preimage exists
2102
+ if (
2103
+ !details.lnurlReceiveMetadata ||
2104
+ !details.lnurlReceiveMetadata.preimage
2105
+ ) {
2106
+ continue;
2107
+ }
2108
+ } else {
2109
+ // Pending: has lnurl metadata, has lightning preimage, but lnurl preimage not yet sent
2110
+ if (
2111
+ !details.lnurlReceiveMetadata ||
2112
+ !details.htlcDetails?.preimage ||
2113
+ details.lnurlReceiveMetadata.preimage
2114
+ ) {
2115
+ continue;
2116
+ }
2117
+ }
2118
+ }
1936
2119
 
1937
2120
 
1938
2121
  paymentDetailsFilterMatches = true;
@@ -2095,15 +2278,12 @@ class IndexedDBStorage {
2095
2278
 
2096
2279
  lnurlReceiveRequest.onsuccess = () => {
2097
2280
  const lnurlReceiveMetadata = lnurlReceiveRequest.result;
2098
- if (
2099
- lnurlReceiveMetadata &&
2100
- (lnurlReceiveMetadata.nostrZapRequest ||
2101
- lnurlReceiveMetadata.senderComment)
2102
- ) {
2281
+ if (lnurlReceiveMetadata) {
2103
2282
  payment.details.lnurlReceiveMetadata = {
2104
2283
  nostrZapRequest: lnurlReceiveMetadata.nostrZapRequest || null,
2105
2284
  nostrZapReceipt: lnurlReceiveMetadata.nostrZapReceipt || null,
2106
2285
  senderComment: lnurlReceiveMetadata.senderComment || null,
2286
+ preimage: lnurlReceiveMetadata.preimage || null,
2107
2287
  };
2108
2288
  }
2109
2289
  resolve(payment);