@breeztech/breez-sdk-spark 0.15.1 → 0.17.0

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.
Files changed (51) hide show
  1. package/breez-sdk-spark.tgz +0 -0
  2. package/bundler/breez_sdk_spark_wasm.d.ts +604 -237
  3. package/bundler/breez_sdk_spark_wasm.js +1 -1
  4. package/bundler/breez_sdk_spark_wasm_bg.js +729 -434
  5. package/bundler/breez_sdk_spark_wasm_bg.wasm +0 -0
  6. package/bundler/breez_sdk_spark_wasm_bg.wasm.d.ts +63 -49
  7. package/bundler/storage/index.js +356 -34
  8. package/deno/breez_sdk_spark_wasm.d.ts +604 -237
  9. package/deno/breez_sdk_spark_wasm.js +729 -434
  10. package/deno/breez_sdk_spark_wasm_bg.wasm +0 -0
  11. package/deno/breez_sdk_spark_wasm_bg.wasm.d.ts +63 -49
  12. package/nodejs/breez_sdk_spark_wasm.d.ts +604 -237
  13. package/nodejs/breez_sdk_spark_wasm.js +741 -441
  14. package/nodejs/breez_sdk_spark_wasm_bg.wasm +0 -0
  15. package/nodejs/breez_sdk_spark_wasm_bg.wasm.d.ts +63 -49
  16. package/nodejs/index.js +10 -10
  17. package/nodejs/index.mjs +13 -8
  18. package/nodejs/mysql-session-store/errors.cjs +13 -0
  19. package/nodejs/{mysql-session-manager → mysql-session-store}/index.cjs +24 -21
  20. package/nodejs/{mysql-session-manager → mysql-session-store}/migrations.cjs +17 -11
  21. package/nodejs/mysql-session-store/package.json +9 -0
  22. package/nodejs/mysql-storage/index.cjs +358 -125
  23. package/nodejs/mysql-storage/migrations.cjs +67 -2
  24. package/nodejs/mysql-token-store/index.cjs +99 -79
  25. package/nodejs/mysql-token-store/migrations.cjs +59 -2
  26. package/nodejs/mysql-tree-store/index.cjs +15 -9
  27. package/nodejs/mysql-tree-store/migrations.cjs +16 -2
  28. package/nodejs/package.json +2 -2
  29. package/nodejs/postgres-session-store/errors.cjs +13 -0
  30. package/nodejs/{postgres-session-manager → postgres-session-store}/index.cjs +23 -23
  31. package/nodejs/{postgres-session-manager → postgres-session-store}/migrations.cjs +14 -14
  32. package/nodejs/postgres-session-store/package.json +9 -0
  33. package/nodejs/postgres-storage/index.cjs +296 -119
  34. package/nodejs/postgres-storage/migrations.cjs +51 -0
  35. package/nodejs/postgres-token-store/index.cjs +89 -64
  36. package/nodejs/postgres-token-store/migrations.cjs +44 -0
  37. package/nodejs/storage/index.cjs +285 -125
  38. package/nodejs/storage/migrations.cjs +47 -0
  39. package/package.json +6 -1
  40. package/ssr/index.js +57 -28
  41. package/web/breez_sdk_spark_wasm.d.ts +667 -286
  42. package/web/breez_sdk_spark_wasm.js +729 -434
  43. package/web/breez_sdk_spark_wasm_bg.wasm +0 -0
  44. package/web/breez_sdk_spark_wasm_bg.wasm.d.ts +63 -49
  45. package/web/passkey-prf-provider/index.d.ts +203 -0
  46. package/web/passkey-prf-provider/index.js +733 -0
  47. package/web/storage/index.js +356 -34
  48. package/nodejs/mysql-session-manager/errors.cjs +0 -13
  49. package/nodejs/mysql-session-manager/package.json +0 -9
  50. package/nodejs/postgres-session-manager/errors.cjs +0 -13
  51. package/nodejs/postgres-session-manager/package.json +0 -9
@@ -41,7 +41,8 @@ const SELECT_PAYMENT_SQL = `
41
41
  p.timestamp,
42
42
  p.method,
43
43
  p.withdraw_tx_id,
44
- p.deposit_tx_id,
44
+ pd.tx_id AS deposit_tx_id,
45
+ pd.vout AS deposit_vout,
45
46
  p.spark,
46
47
  l.invoice AS lightning_invoice,
47
48
  l.payment_hash AS lightning_payment_hash,
@@ -69,6 +70,7 @@ const SELECT_PAYMENT_SQL = `
69
70
  LEFT JOIN payment_details_lightning l ON p.id = l.payment_id
70
71
  LEFT JOIN payment_details_token t ON p.id = t.payment_id
71
72
  LEFT JOIN payment_details_spark s ON p.id = s.payment_id
73
+ LEFT JOIN payment_details_deposit pd ON p.id = pd.payment_id
72
74
  LEFT JOIN payment_metadata pm ON p.id = pm.payment_id
73
75
  LEFT JOIN lnurl_receive_metadata lrm ON l.payment_hash = lrm.payment_hash`;
74
76
 
@@ -204,6 +206,14 @@ class SqliteStorage {
204
206
  const allPaymentDetailsClauses = [];
205
207
  for (const paymentDetailsFilter of request.paymentDetailsFilter) {
206
208
  const paymentDetailsClauses = [];
209
+ // Base type check: ensure payment type matches the filter type
210
+ if (paymentDetailsFilter.type === "spark") {
211
+ paymentDetailsClauses.push("p.spark = 1");
212
+ } else if (paymentDetailsFilter.type === "token") {
213
+ paymentDetailsClauses.push("p.spark IS NULL AND t.tx_hash IS NOT NULL");
214
+ } else if (paymentDetailsFilter.type === "lightning") {
215
+ paymentDetailsClauses.push("l.htlc_status IS NOT NULL");
216
+ }
207
217
  // Filter by HTLC status (Spark or Lightning)
208
218
  const htlcAlias =
209
219
  paymentDetailsFilter.type === "spark" ? "s"
@@ -230,20 +240,35 @@ class SqliteStorage {
230
240
  }
231
241
  params.push(...paymentDetailsFilter.htlcStatus);
232
242
  }
233
- // Filter by token conversion info presence
243
+ // Filter by conversion type + status
234
244
  if (
235
- (paymentDetailsFilter.type === "spark" || paymentDetailsFilter.type === "token") &&
236
- paymentDetailsFilter.conversionRefundNeeded !== undefined
245
+ (paymentDetailsFilter.type === "spark" ||
246
+ paymentDetailsFilter.type === "token" ||
247
+ paymentDetailsFilter.type === "lightning") &&
248
+ paymentDetailsFilter.conversionFilter !== undefined
237
249
  ) {
238
- const typeCheck = paymentDetailsFilter.type === "spark" ? "p.spark = 1" : "p.spark IS NULL";
239
- const refundNeeded =
240
- paymentDetailsFilter.conversionRefundNeeded === true
241
- ? "= 'refundNeeded'"
242
- : "!= 'refundNeeded'";
243
- paymentDetailsClauses.push(
244
- `${typeCheck} AND pm.conversion_info IS NOT NULL AND
245
- json_extract(pm.conversion_info, '$.status') ${refundNeeded}`
246
- );
250
+ // Lightning carries the Boltz conversion; its `conversion_info.type`
251
+ // is the authoritative discriminator, so no payment-type column check.
252
+ const typeCheck =
253
+ paymentDetailsFilter.type === "spark"
254
+ ? "p.spark = 1"
255
+ : paymentDetailsFilter.type === "token"
256
+ ? "p.spark IS NULL"
257
+ : null;
258
+ let statusClause;
259
+ if (paymentDetailsFilter.conversionFilter === "ammRefundNeeded") {
260
+ statusClause = "json_extract(pm.conversion_info, '$.type') = 'amm' AND json_extract(pm.conversion_info, '$.status') = 'refundNeeded'";
261
+ } else if (paymentDetailsFilter.conversionFilter === "orchestraPending") {
262
+ statusClause = "json_extract(pm.conversion_info, '$.type') = 'orchestra' AND json_extract(pm.conversion_info, '$.status') NOT IN ('completed', 'failed', 'refunded')";
263
+ } else if (paymentDetailsFilter.conversionFilter === "boltzPending") {
264
+ statusClause = "json_extract(pm.conversion_info, '$.type') = 'boltz' AND json_extract(pm.conversion_info, '$.status') NOT IN ('completed', 'failed', 'refunded')";
265
+ }
266
+ if (statusClause) {
267
+ const prefix = typeCheck ? `${typeCheck} AND ` : "";
268
+ paymentDetailsClauses.push(
269
+ `${prefix}pm.conversion_info IS NOT NULL AND ${statusClause}`
270
+ );
271
+ }
247
272
  }
248
273
  // Filter by token transaction hash
249
274
  if (
@@ -313,129 +338,180 @@ class SqliteStorage {
313
338
  }
314
339
  }
315
340
 
316
- insertPayment(payment) {
317
- try {
318
- if (!payment) {
319
- return Promise.reject(
320
- new StorageError("Payment cannot be null or undefined")
321
- );
322
- }
341
+ async applyPaymentUpdate(payment) {
342
+ if (!payment) {
343
+ throw new StorageError("Payment cannot be null or undefined");
344
+ }
323
345
 
324
- const paymentInsert = this.db.prepare(
325
- `INSERT INTO payments (id, payment_type, status, amount, fees, timestamp, method, withdraw_tx_id, deposit_tx_id, spark)
326
- VALUES (@id, @paymentType, @status, @amount, @fees, @timestamp, @method, @withdrawTxId, @depositTxId, @spark)
327
- ON CONFLICT(id) DO UPDATE SET
328
- payment_type=excluded.payment_type,
329
- status=excluded.status,
330
- amount=excluded.amount,
331
- fees=excluded.fees,
332
- timestamp=excluded.timestamp,
333
- method=excluded.method,
334
- withdraw_tx_id=excluded.withdraw_tx_id,
335
- deposit_tx_id=excluded.deposit_tx_id,
336
- spark=excluded.spark`
337
- );
338
- const lightningInsert = this.db.prepare(
339
- `INSERT INTO payment_details_lightning
340
- (payment_id, invoice, payment_hash, destination_pubkey, description, preimage, htlc_status, htlc_expiry_time)
341
- VALUES (@id, @invoice, @paymentHash, @destinationPubkey, @description, @preimage, @htlcStatus, @htlcExpiryTime)
342
- ON CONFLICT(payment_id) DO UPDATE SET
343
- invoice=excluded.invoice,
344
- payment_hash=excluded.payment_hash,
345
- destination_pubkey=excluded.destination_pubkey,
346
- description=excluded.description,
347
- preimage=COALESCE(excluded.preimage, payment_details_lightning.preimage),
348
- htlc_status=COALESCE(excluded.htlc_status, payment_details_lightning.htlc_status),
349
- htlc_expiry_time=COALESCE(excluded.htlc_expiry_time, payment_details_lightning.htlc_expiry_time)`
350
- );
351
- const tokenInsert = this.db.prepare(
352
- `INSERT INTO payment_details_token
353
- (payment_id, metadata, tx_hash, tx_type, invoice_details)
354
- VALUES (@id, @metadata, @txHash, @txType, @invoiceDetails)
355
- ON CONFLICT(payment_id) DO UPDATE SET
356
- metadata=excluded.metadata,
357
- tx_hash=excluded.tx_hash,
358
- tx_type=excluded.tx_type,
359
- invoice_details=COALESCE(excluded.invoice_details, payment_details_token.invoice_details)`
360
- );
361
- const sparkInsert = this.db.prepare(
362
- `INSERT INTO payment_details_spark
363
- (payment_id, invoice_details, htlc_details)
364
- VALUES (@id, @invoiceDetails, @htlcDetails)
365
- ON CONFLICT(payment_id) DO UPDATE SET
366
- invoice_details=COALESCE(excluded.invoice_details, payment_details_spark.invoice_details),
367
- htlc_details=COALESCE(excluded.htlc_details, payment_details_spark.htlc_details)`
346
+ try {
347
+ const selectStatus = this.db.prepare(
348
+ "SELECT status FROM payments WHERE id = ?"
368
349
  );
350
+ let shouldEmit = false;
369
351
  const transaction = this.db.transaction(() => {
370
- paymentInsert.run({
371
- id: payment.id,
372
- paymentType: payment.paymentType,
373
- status: payment.status,
374
- amount: payment.amount.toString(),
375
- fees: payment.fees.toString(),
376
- timestamp: payment.timestamp,
377
- method: payment.method ? JSON.stringify(payment.method) : null,
378
- withdrawTxId:
379
- payment.details?.type === "withdraw" ? payment.details.txId : null,
380
- depositTxId:
381
- payment.details?.type === "deposit" ? payment.details.txId : null,
382
- spark: payment.details?.type === "spark" ? 1 : null,
383
- });
384
-
385
- if (
386
- payment.details?.type === "spark" &&
387
- (payment.details.invoiceDetails != null ||
388
- payment.details.htlcDetails != null)
389
- ) {
390
- sparkInsert.run({
391
- id: payment.id,
392
- invoiceDetails: payment.details.invoiceDetails
393
- ? JSON.stringify(payment.details.invoiceDetails)
394
- : null,
395
- htlcDetails: payment.details.htlcDetails
396
- ? JSON.stringify(payment.details.htlcDetails)
397
- : null,
398
- });
399
- }
400
-
401
- if (payment.details?.type === "lightning") {
402
- lightningInsert.run({
403
- id: payment.id,
404
- invoice: payment.details.invoice,
405
- paymentHash: payment.details.htlcDetails.paymentHash,
406
- destinationPubkey: payment.details.destinationPubkey,
407
- description: payment.details.description,
408
- preimage: payment.details.htlcDetails?.preimage,
409
- htlcStatus: payment.details.htlcDetails?.status ?? null,
410
- htlcExpiryTime: payment.details.htlcDetails?.expiryTime ?? 0,
411
- });
412
- }
413
-
414
- if (payment.details?.type === "token") {
415
- tokenInsert.run({
416
- id: payment.id,
417
- metadata: JSON.stringify(payment.details.metadata),
418
- txHash: payment.details.txHash,
419
- txType: payment.details.txType,
420
- invoiceDetails: payment.details.invoiceDetails
421
- ? JSON.stringify(payment.details.invoiceDetails)
422
- : null,
423
- });
352
+ const row = selectStatus.get(payment.id);
353
+ const stored = row
354
+ ? this._normalizePaymentStatus(row.status)
355
+ : null;
356
+ const next = this._normalizePaymentStatus(payment.status);
357
+
358
+ if (stored == null) {
359
+ this._runPaymentUpsert(payment);
360
+ shouldEmit = true;
361
+ } else if (stored === next) {
362
+ console.debug(
363
+ `Skipping redundant payment event: id=${payment.id} status=${next}`
364
+ );
365
+ this._runPaymentUpsert(payment);
366
+ shouldEmit = false;
367
+ } else if (this._isFinalPaymentStatus(stored)) {
368
+ console.warn(
369
+ `Skipping payment update (would replace terminal status): id=${payment.id} stored=${stored} new=${next}`
370
+ );
371
+ shouldEmit = false;
372
+ } else {
373
+ this._runPaymentUpsert(payment);
374
+ shouldEmit = true;
424
375
  }
425
376
  });
426
377
 
427
- transaction();
428
- return Promise.resolve();
378
+ transaction.immediate();
379
+ return shouldEmit;
429
380
  } catch (error) {
430
- return Promise.reject(
431
- new StorageError(
432
- `Failed to insert payment '${payment.id}': ${error.message}`,
433
- error
434
- )
381
+ throw new StorageError(
382
+ `Failed to apply payment update '${payment.id}': ${error.message}`,
383
+ error
435
384
  );
436
385
  }
437
386
  }
438
387
 
388
+ _runPaymentUpsert(payment) {
389
+ const paymentInsert = this.db.prepare(
390
+ `INSERT INTO payments (id, payment_type, status, amount, fees, timestamp, method, withdraw_tx_id, spark)
391
+ VALUES (@id, @paymentType, @status, @amount, @fees, @timestamp, @method, @withdrawTxId, @spark)
392
+ ON CONFLICT(id) DO UPDATE SET
393
+ payment_type=excluded.payment_type,
394
+ status=excluded.status,
395
+ amount=excluded.amount,
396
+ fees=excluded.fees,
397
+ timestamp=excluded.timestamp,
398
+ method=excluded.method,
399
+ withdraw_tx_id=excluded.withdraw_tx_id,
400
+ spark=excluded.spark`
401
+ );
402
+ const depositInsert = this.db.prepare(
403
+ `INSERT INTO payment_details_deposit
404
+ (payment_id, tx_id, vout)
405
+ VALUES (@id, @txId, @vout)
406
+ ON CONFLICT(payment_id) DO UPDATE SET
407
+ tx_id=excluded.tx_id,
408
+ vout=excluded.vout`
409
+ );
410
+ const lightningInsert = this.db.prepare(
411
+ `INSERT INTO payment_details_lightning
412
+ (payment_id, invoice, payment_hash, destination_pubkey, description, preimage, htlc_status, htlc_expiry_time)
413
+ VALUES (@id, @invoice, @paymentHash, @destinationPubkey, @description, @preimage, @htlcStatus, @htlcExpiryTime)
414
+ ON CONFLICT(payment_id) DO UPDATE SET
415
+ invoice=excluded.invoice,
416
+ payment_hash=excluded.payment_hash,
417
+ destination_pubkey=excluded.destination_pubkey,
418
+ description=excluded.description,
419
+ preimage=COALESCE(excluded.preimage, payment_details_lightning.preimage),
420
+ htlc_status=COALESCE(excluded.htlc_status, payment_details_lightning.htlc_status),
421
+ htlc_expiry_time=COALESCE(excluded.htlc_expiry_time, payment_details_lightning.htlc_expiry_time)`
422
+ );
423
+ const tokenInsert = this.db.prepare(
424
+ `INSERT INTO payment_details_token
425
+ (payment_id, metadata, tx_hash, tx_type, invoice_details)
426
+ VALUES (@id, @metadata, @txHash, @txType, @invoiceDetails)
427
+ ON CONFLICT(payment_id) DO UPDATE SET
428
+ metadata=excluded.metadata,
429
+ tx_hash=excluded.tx_hash,
430
+ tx_type=excluded.tx_type,
431
+ invoice_details=COALESCE(excluded.invoice_details, payment_details_token.invoice_details)`
432
+ );
433
+ const sparkInsert = this.db.prepare(
434
+ `INSERT INTO payment_details_spark
435
+ (payment_id, invoice_details, htlc_details)
436
+ VALUES (@id, @invoiceDetails, @htlcDetails)
437
+ ON CONFLICT(payment_id) DO UPDATE SET
438
+ invoice_details=COALESCE(excluded.invoice_details, payment_details_spark.invoice_details),
439
+ htlc_details=COALESCE(excluded.htlc_details, payment_details_spark.htlc_details)`
440
+ );
441
+
442
+ paymentInsert.run({
443
+ id: payment.id,
444
+ paymentType: payment.paymentType,
445
+ status: payment.status,
446
+ amount: payment.amount.toString(),
447
+ fees: payment.fees.toString(),
448
+ timestamp: payment.timestamp,
449
+ method: payment.method ? JSON.stringify(payment.method) : null,
450
+ withdrawTxId:
451
+ payment.details?.type === "withdraw" ? payment.details.txId : null,
452
+ spark: payment.details?.type === "spark" ? 1 : null,
453
+ });
454
+
455
+ if (payment.details?.type === "deposit") {
456
+ depositInsert.run({
457
+ id: payment.id,
458
+ txId: payment.details.txId,
459
+ vout: payment.details.vout,
460
+ });
461
+ }
462
+
463
+ if (
464
+ payment.details?.type === "spark" &&
465
+ (payment.details.invoiceDetails != null ||
466
+ payment.details.htlcDetails != null)
467
+ ) {
468
+ sparkInsert.run({
469
+ id: payment.id,
470
+ invoiceDetails: payment.details.invoiceDetails
471
+ ? JSON.stringify(payment.details.invoiceDetails)
472
+ : null,
473
+ htlcDetails: payment.details.htlcDetails
474
+ ? JSON.stringify(payment.details.htlcDetails)
475
+ : null,
476
+ });
477
+ }
478
+
479
+ if (payment.details?.type === "lightning") {
480
+ lightningInsert.run({
481
+ id: payment.id,
482
+ invoice: payment.details.invoice,
483
+ paymentHash: payment.details.htlcDetails.paymentHash,
484
+ destinationPubkey: payment.details.destinationPubkey,
485
+ description: payment.details.description,
486
+ preimage: payment.details.htlcDetails?.preimage,
487
+ htlcStatus: payment.details.htlcDetails?.status ?? null,
488
+ htlcExpiryTime: payment.details.htlcDetails?.expiryTime ?? 0,
489
+ });
490
+ }
491
+
492
+ if (payment.details?.type === "token") {
493
+ tokenInsert.run({
494
+ id: payment.id,
495
+ metadata: JSON.stringify(payment.details.metadata),
496
+ txHash: payment.details.txHash,
497
+ txType: payment.details.txType,
498
+ invoiceDetails: payment.details.invoiceDetails
499
+ ? JSON.stringify(payment.details.invoiceDetails)
500
+ : null,
501
+ });
502
+ }
503
+ }
504
+
505
+ _normalizePaymentStatus(status) {
506
+ return typeof status === "string" ? status.toLowerCase() : status;
507
+ }
508
+
509
+ _isFinalPaymentStatus(status) {
510
+ const normalized = this._normalizePaymentStatus(status);
511
+ return normalized === "completed" || normalized === "failed";
512
+ }
513
+
514
+
439
515
  getPaymentById(id) {
440
516
  try {
441
517
  if (!id) {
@@ -760,6 +836,17 @@ class SqliteStorage {
760
836
  senderComment: row.lnurl_sender_comment || null,
761
837
  };
762
838
  }
839
+
840
+ if (row.conversion_info) {
841
+ try {
842
+ details.conversionInfo = JSON.parse(row.conversion_info);
843
+ } catch (e) {
844
+ throw new StorageError(
845
+ `Failed to parse conversion_info JSON for payment ${row.id}: ${e.message}`,
846
+ e
847
+ );
848
+ }
849
+ }
763
850
  } else if (row.withdraw_tx_id) {
764
851
  details = {
765
852
  type: "withdraw",
@@ -769,6 +856,7 @@ class SqliteStorage {
769
856
  details = {
770
857
  type: "deposit",
771
858
  txId: row.deposit_tx_id,
859
+ vout: row.deposit_vout,
772
860
  };
773
861
  } else if (row.spark) {
774
862
  details = {
@@ -1324,6 +1412,78 @@ class SqliteStorage {
1324
1412
  );
1325
1413
  }
1326
1414
  }
1415
+
1416
+ // ===== Cross-Chain Swap Operations =====
1417
+
1418
+ setCrossChainSwap(swap) {
1419
+ try {
1420
+ const stmt = this.db.prepare(`
1421
+ INSERT INTO cross_chain_swaps (provider, id, is_terminal, updated_at, data, secrets)
1422
+ VALUES (?, ?, ?, ?, ?, ?)
1423
+ ON CONFLICT(provider, id) DO UPDATE SET
1424
+ is_terminal = excluded.is_terminal,
1425
+ updated_at = excluded.updated_at,
1426
+ data = excluded.data,
1427
+ secrets = excluded.secrets
1428
+ `);
1429
+ stmt.run(
1430
+ swap.provider,
1431
+ swap.id,
1432
+ swap.isTerminal ? 1 : 0,
1433
+ swap.updatedAt,
1434
+ swap.data,
1435
+ swap.secrets
1436
+ );
1437
+ return Promise.resolve();
1438
+ } catch (error) {
1439
+ return Promise.reject(
1440
+ new StorageError(`Failed to set cross-chain swap: ${error.message}`, error)
1441
+ );
1442
+ }
1443
+ }
1444
+
1445
+ getCrossChainSwap(provider, id) {
1446
+ try {
1447
+ const stmt = this.db.prepare(
1448
+ `SELECT provider, id, is_terminal, updated_at, data, secrets
1449
+ FROM cross_chain_swaps WHERE provider = ? AND id = ?`
1450
+ );
1451
+ const row = stmt.get(provider, id);
1452
+ return Promise.resolve(row ? crossChainSwapFromRow(row) : null);
1453
+ } catch (error) {
1454
+ return Promise.reject(
1455
+ new StorageError(`Failed to get cross-chain swap: ${error.message}`, error)
1456
+ );
1457
+ }
1458
+ }
1459
+
1460
+ listActiveCrossChainSwaps(provider) {
1461
+ try {
1462
+ const stmt = this.db.prepare(
1463
+ `SELECT provider, id, is_terminal, updated_at, data, secrets
1464
+ FROM cross_chain_swaps WHERE provider = ? AND is_terminal = 0`
1465
+ );
1466
+ const rows = stmt.all(provider);
1467
+ return Promise.resolve(rows.map(crossChainSwapFromRow));
1468
+ } catch (error) {
1469
+ return Promise.reject(
1470
+ new StorageError(`Failed to list active cross-chain swaps: ${error.message}`, error)
1471
+ );
1472
+ }
1473
+ }
1474
+ }
1475
+
1476
+ /// Maps a `cross_chain_swaps` row to a StoredCrossChainSwap, parsing `data` and
1477
+ /// the is_terminal flag back into the camelCase shape the SDK expects.
1478
+ function crossChainSwapFromRow(row) {
1479
+ return {
1480
+ provider: row.provider,
1481
+ id: row.id,
1482
+ isTerminal: row.is_terminal !== 0,
1483
+ updatedAt: Number(row.updated_at),
1484
+ data: row.data,
1485
+ secrets: row.secrets,
1486
+ };
1327
1487
  }
1328
1488
 
1329
1489
  async function createDefaultStorage(dataDir, logger = null) {
@@ -433,6 +433,53 @@ class MigrationManager {
433
433
  `CREATE INDEX idx_payment_metadata_payment_id ON payment_metadata(payment_id)`,
434
434
  ],
435
435
  },
436
+ {
437
+ // Move deposit details into their own table so vout can be NOT NULL and
438
+ // the schema matches payment_details_lightning / _token / _spark. We can't
439
+ // safely backfill the new table from the dropped deposit_tx_id column: we
440
+ // never stored the original SSP output_index, and vout=0 is a valid output
441
+ // index, so defaulting would silently mislabel. Drop the column and leave
442
+ // the payments row in place. The read path sees an unjoined deposit row
443
+ // as `details: None` until the resync re-fetches the SSP user_request and
444
+ // the upsert inserts the new details row.
445
+ name: "Move deposit details into payment_details_deposit table",
446
+ sql: [
447
+ `CREATE TABLE payment_details_deposit (
448
+ payment_id TEXT PRIMARY KEY,
449
+ tx_id TEXT NOT NULL,
450
+ vout INTEGER NOT NULL,
451
+ FOREIGN KEY (payment_id) REFERENCES payments(id) ON DELETE CASCADE
452
+ )`,
453
+ `ALTER TABLE payments DROP COLUMN deposit_tx_id`,
454
+ `UPDATE settings
455
+ SET value = json_set(value, '$.offset', 0)
456
+ WHERE key = 'sync_offset' AND json_valid(value)`,
457
+ ],
458
+ },
459
+ {
460
+ name: "Backfill conversion_info type discriminator",
461
+ sql: `UPDATE payment_metadata SET conversion_info = json_set(conversion_info, '$.type', 'amm') WHERE conversion_info IS NOT NULL AND json_extract(conversion_info, '$.type') IS NULL`,
462
+ },
463
+ {
464
+ // Cross-chain swap rows, synced for cross-instance recovery. Shared
465
+ // across providers, discriminated by `provider`. `data` is
466
+ // provider-opaque JSON; `secrets` is provider-opaque ciphertext (empty
467
+ // when the provider has none).
468
+ name: "Create cross_chain_swaps table",
469
+ sql: [
470
+ `CREATE TABLE cross_chain_swaps (
471
+ provider TEXT NOT NULL,
472
+ id TEXT NOT NULL,
473
+ is_terminal INTEGER NOT NULL,
474
+ updated_at INTEGER NOT NULL,
475
+ data TEXT NOT NULL,
476
+ secrets TEXT NOT NULL,
477
+ PRIMARY KEY (provider, id)
478
+ )`,
479
+ `CREATE INDEX idx_cross_chain_swaps_provider_is_terminal
480
+ ON cross_chain_swaps(provider, is_terminal)`,
481
+ ],
482
+ },
436
483
  ];
437
484
  }
438
485
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@breeztech/breez-sdk-spark",
3
- "version": "0.15.1",
3
+ "version": "0.17.0",
4
4
  "description": "Breez Spark SDK",
5
5
  "repository": "https://github.com/breez/spark-sdk",
6
6
  "author": "Breez <contact@breez.technology> (https://github.com/breez)",
@@ -23,6 +23,11 @@
23
23
  "./nodejs": "./nodejs/index.js",
24
24
  "./web": "./web/index.js",
25
25
  "./ssr": "./ssr/index.js",
26
+ "./passkey-prf-provider": {
27
+ "types": "./web/passkey-prf-provider/index.d.ts",
28
+ "import": "./web/passkey-prf-provider/index.js",
29
+ "default": "./web/passkey-prf-provider/index.js"
30
+ },
26
31
  ".": {
27
32
  "node": {
28
33
  "import": "./nodejs/index.mjs",