@breeztech/breez-sdk-spark 0.12.2-dev1 → 0.12.2-dev2

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 (32) hide show
  1. package/README.md +4 -4
  2. package/breez-sdk-spark.tgz +0 -0
  3. package/bundler/breez_sdk_spark_wasm.d.ts +731 -601
  4. package/bundler/breez_sdk_spark_wasm_bg.js +151 -54
  5. package/bundler/breez_sdk_spark_wasm_bg.wasm +0 -0
  6. package/bundler/breez_sdk_spark_wasm_bg.wasm.d.ts +7 -5
  7. package/bundler/storage/index.js +65 -43
  8. package/deno/breez_sdk_spark_wasm.d.ts +731 -601
  9. package/deno/breez_sdk_spark_wasm.js +141 -54
  10. package/deno/breez_sdk_spark_wasm_bg.wasm +0 -0
  11. package/deno/breez_sdk_spark_wasm_bg.wasm.d.ts +7 -5
  12. package/nodejs/breez_sdk_spark_wasm.d.ts +731 -601
  13. package/nodejs/breez_sdk_spark_wasm.js +151 -54
  14. package/nodejs/breez_sdk_spark_wasm_bg.wasm +0 -0
  15. package/nodejs/breez_sdk_spark_wasm_bg.wasm.d.ts +7 -5
  16. package/nodejs/index.js +16 -2
  17. package/nodejs/package.json +1 -0
  18. package/nodejs/postgres-storage/index.cjs +42 -31
  19. package/nodejs/postgres-storage/migrations.cjs +24 -0
  20. package/nodejs/postgres-token-store/errors.cjs +13 -0
  21. package/nodejs/postgres-token-store/index.cjs +857 -0
  22. package/nodejs/postgres-token-store/migrations.cjs +163 -0
  23. package/nodejs/postgres-token-store/package.json +9 -0
  24. package/nodejs/postgres-tree-store/index.cjs +12 -2
  25. package/nodejs/storage/index.cjs +19 -28
  26. package/nodejs/storage/migrations.cjs +18 -0
  27. package/package.json +1 -1
  28. package/web/breez_sdk_spark_wasm.d.ts +738 -606
  29. package/web/breez_sdk_spark_wasm.js +141 -54
  30. package/web/breez_sdk_spark_wasm_bg.wasm +0 -0
  31. package/web/breez_sdk_spark_wasm_bg.wasm.d.ts +7 -5
  32. package/web/storage/index.js +65 -43
@@ -0,0 +1,163 @@
1
+ /**
2
+ * Database Migration Manager for Breez SDK PostgreSQL Token Store
3
+ *
4
+ * Uses a token_schema_migrations table + pg_advisory_xact_lock to safely run
5
+ * migrations from concurrent processes.
6
+ */
7
+
8
+ const { TokenStoreError } = require("./errors.cjs");
9
+
10
+ /**
11
+ * Advisory lock ID for token store migrations.
12
+ * Uses a different lock ID from the storage/tree store migrations to avoid contention.
13
+ * Derived from ASCII bytes of "TOKN" (0x544F4B4E).
14
+ */
15
+ const MIGRATION_LOCK_ID = "1414022990"; // 0x544F4B4E as decimal string
16
+
17
+ class TokenStoreMigrationManager {
18
+ constructor(logger = null) {
19
+ this.logger = logger;
20
+ }
21
+
22
+ /**
23
+ * Run all pending migrations inside a single transaction with an advisory lock.
24
+ * @param {import('pg').Pool} pool
25
+ */
26
+ async migrate(pool) {
27
+ const client = await pool.connect();
28
+ try {
29
+ await client.query("BEGIN");
30
+
31
+ // Transaction-level advisory lock — automatically released on COMMIT/ROLLBACK
32
+ await client.query(`SELECT pg_advisory_xact_lock(${MIGRATION_LOCK_ID})`);
33
+
34
+ // Create the migrations tracking table if needed
35
+ await client.query(`
36
+ CREATE TABLE IF NOT EXISTS token_schema_migrations (
37
+ version INTEGER PRIMARY KEY,
38
+ applied_at TIMESTAMPTZ DEFAULT NOW()
39
+ )
40
+ `);
41
+
42
+ // Get current version
43
+ const versionResult = await client.query(
44
+ "SELECT COALESCE(MAX(version), 0) AS version FROM token_schema_migrations"
45
+ );
46
+ const currentVersion = versionResult.rows[0].version;
47
+
48
+ const migrations = this._getMigrations();
49
+
50
+ if (currentVersion >= migrations.length) {
51
+ this._log("info", `Token store database is up to date (version ${currentVersion})`);
52
+ await client.query("COMMIT");
53
+ return;
54
+ }
55
+
56
+ this._log(
57
+ "info",
58
+ `Migrating token store database from version ${currentVersion} to ${migrations.length}`
59
+ );
60
+
61
+ for (let i = currentVersion; i < migrations.length; i++) {
62
+ const migration = migrations[i];
63
+ const version = i + 1;
64
+ this._log("debug", `Running token store migration ${version}: ${migration.name}`);
65
+
66
+ for (const sql of migration.sql) {
67
+ await client.query(sql);
68
+ }
69
+
70
+ await client.query(
71
+ "INSERT INTO token_schema_migrations (version) VALUES ($1)",
72
+ [version]
73
+ );
74
+ }
75
+
76
+ await client.query("COMMIT");
77
+ this._log("info", "Token store database migration completed successfully");
78
+ } catch (error) {
79
+ await client.query("ROLLBACK").catch(() => {});
80
+ throw new TokenStoreError(
81
+ `Token store migration failed: ${error.message}`,
82
+ error
83
+ );
84
+ } finally {
85
+ client.release();
86
+ }
87
+ }
88
+
89
+ _log(level, message) {
90
+ if (this.logger && typeof this.logger.log === "function") {
91
+ this.logger.log({ line: message, level });
92
+ } else if (level === "error") {
93
+ console.error(`[TokenStoreMigrationManager] ${message}`);
94
+ }
95
+ }
96
+
97
+ /**
98
+ * Migrations matching the Rust PostgresTokenStore schema exactly.
99
+ */
100
+ _getMigrations() {
101
+ return [
102
+ {
103
+ name: "Create token store tables with race condition protection",
104
+ sql: [
105
+ `CREATE TABLE IF NOT EXISTS token_metadata (
106
+ identifier TEXT PRIMARY KEY,
107
+ issuer_public_key TEXT NOT NULL,
108
+ name TEXT NOT NULL,
109
+ ticker TEXT NOT NULL,
110
+ decimals INTEGER NOT NULL,
111
+ max_supply TEXT NOT NULL,
112
+ is_freezable BOOLEAN NOT NULL,
113
+ creation_entity_public_key TEXT
114
+ )`,
115
+
116
+ `CREATE INDEX IF NOT EXISTS idx_token_metadata_issuer_pk
117
+ ON token_metadata (issuer_public_key)`,
118
+
119
+ `CREATE TABLE IF NOT EXISTS token_reservations (
120
+ id TEXT PRIMARY KEY,
121
+ purpose TEXT NOT NULL,
122
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
123
+ )`,
124
+
125
+ `CREATE TABLE IF NOT EXISTS token_outputs (
126
+ id TEXT PRIMARY KEY,
127
+ token_identifier TEXT NOT NULL REFERENCES token_metadata(identifier),
128
+ owner_public_key TEXT NOT NULL,
129
+ revocation_commitment TEXT NOT NULL,
130
+ withdraw_bond_sats BIGINT NOT NULL,
131
+ withdraw_relative_block_locktime BIGINT NOT NULL,
132
+ token_public_key TEXT,
133
+ token_amount TEXT NOT NULL,
134
+ prev_tx_hash TEXT NOT NULL,
135
+ prev_tx_vout INTEGER NOT NULL,
136
+ reservation_id TEXT REFERENCES token_reservations(id) ON DELETE SET NULL,
137
+ added_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
138
+ )`,
139
+
140
+ `CREATE INDEX IF NOT EXISTS idx_token_outputs_identifier
141
+ ON token_outputs (token_identifier)`,
142
+
143
+ `CREATE INDEX IF NOT EXISTS idx_token_outputs_reservation
144
+ ON token_outputs (reservation_id) WHERE reservation_id IS NOT NULL`,
145
+
146
+ `CREATE TABLE IF NOT EXISTS token_spent_outputs (
147
+ output_id TEXT PRIMARY KEY,
148
+ spent_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
149
+ )`,
150
+
151
+ `CREATE TABLE IF NOT EXISTS token_swap_status (
152
+ id INTEGER PRIMARY KEY DEFAULT 1 CHECK (id = 1),
153
+ last_completed_at TIMESTAMPTZ
154
+ )`,
155
+
156
+ `INSERT INTO token_swap_status (id) VALUES (1) ON CONFLICT DO NOTHING`,
157
+ ],
158
+ },
159
+ ];
160
+ }
161
+ }
162
+
163
+ module.exports = { TokenStoreMigrationManager };
@@ -0,0 +1,9 @@
1
+ {
2
+ "dependencies": {
3
+ "pg": "^8.18.0"
4
+ },
5
+ "description": "Node.js PostgreSQL token store implementation for Breez SDK WASM (CommonJS)",
6
+ "main": "index.cjs",
7
+ "name": "@breez-sdk/postgres-token-store",
8
+ "version": "1.0.0"
9
+ }
@@ -28,7 +28,7 @@ const { TreeStoreMigrationManager } = require("./migrations.cjs");
28
28
  * Advisory lock key for serializing tree store write operations.
29
29
  * Matches the Rust constant TREE_STORE_WRITE_LOCK_KEY = 0x7472_6565_5354_4f52
30
30
  */
31
- const TREE_STORE_WRITE_LOCK_KEY = "8391086132283252818"; // 0x7472656553544f52 as decimal string
31
+ const TREE_STORE_WRITE_LOCK_KEY = "8390880541608791890"; // 0x7472656553544f52 as decimal string
32
32
 
33
33
  /**
34
34
  * Timeout for reservations in seconds. Reservations older than this are stale.
@@ -789,10 +789,20 @@ async function createPostgresTreeStore(config, logger = null) {
789
789
  connectionTimeoutMillis: config.createTimeoutSecs * 1000,
790
790
  idleTimeoutMillis: config.recycleTimeoutSecs * 1000,
791
791
  });
792
+ return createPostgresTreeStoreWithPool(pool, logger);
793
+ }
792
794
 
795
+ /**
796
+ * Create a PostgresTreeStore instance from an existing pg.Pool.
797
+ *
798
+ * @param {pg.Pool} pool - An existing connection pool
799
+ * @param {object} [logger] - Optional logger
800
+ * @returns {Promise<PostgresTreeStore>}
801
+ */
802
+ async function createPostgresTreeStoreWithPool(pool, logger = null) {
793
803
  const store = new PostgresTreeStore(pool, logger);
794
804
  await store.initialize();
795
805
  return store;
796
806
  }
797
807
 
798
- module.exports = { PostgresTreeStore, createPostgresTreeStore, TreeStoreError };
808
+ module.exports = { PostgresTreeStore, createPostgresTreeStore, createPostgresTreeStoreWithPool, TreeStoreError };
@@ -53,6 +53,7 @@ const SELECT_PAYMENT_SQL = `
53
53
  pm.lnurl_pay_info,
54
54
  pm.lnurl_withdraw_info,
55
55
  pm.conversion_info,
56
+ pm.conversion_status,
56
57
  t.metadata AS token_metadata,
57
58
  t.tx_hash AS token_tx_hash,
58
59
  t.tx_type AS token_tx_type,
@@ -260,21 +261,6 @@ class SqliteStorage {
260
261
  paymentDetailsClauses.push("t.tx_type = ?");
261
262
  params.push(paymentDetailsFilter.txType);
262
263
  }
263
- // Filter by LNURL preimage status
264
- if (
265
- paymentDetailsFilter.type === "lightning" &&
266
- paymentDetailsFilter.hasLnurlPreimage !== undefined
267
- ) {
268
- if (paymentDetailsFilter.hasLnurlPreimage) {
269
- paymentDetailsClauses.push("lrm.preimage IS NOT NULL");
270
- } else {
271
- paymentDetailsClauses.push(
272
- "lrm.payment_hash IS NOT NULL AND l.preimage IS NOT NULL AND lrm.preimage IS NULL"
273
- );
274
- }
275
- }
276
-
277
-
278
264
  if (paymentDetailsClauses.length > 0) {
279
265
  allPaymentDetailsClauses.push(`(${paymentDetailsClauses.join(" AND ")})`);
280
266
  }
@@ -560,14 +546,15 @@ class SqliteStorage {
560
546
  insertPaymentMetadata(paymentId, metadata) {
561
547
  try {
562
548
  const stmt = this.db.prepare(`
563
- INSERT INTO payment_metadata (payment_id, parent_payment_id, lnurl_pay_info, lnurl_withdraw_info, lnurl_description, conversion_info)
564
- VALUES (?, ?, ?, ?, ?, ?)
549
+ INSERT INTO payment_metadata (payment_id, parent_payment_id, lnurl_pay_info, lnurl_withdraw_info, lnurl_description, conversion_info, conversion_status)
550
+ VALUES (?, ?, ?, ?, ?, ?, ?)
565
551
  ON CONFLICT(payment_id) DO UPDATE SET
566
552
  parent_payment_id = COALESCE(excluded.parent_payment_id, parent_payment_id),
567
553
  lnurl_pay_info = COALESCE(excluded.lnurl_pay_info, lnurl_pay_info),
568
554
  lnurl_withdraw_info = COALESCE(excluded.lnurl_withdraw_info, lnurl_withdraw_info),
569
555
  lnurl_description = COALESCE(excluded.lnurl_description, lnurl_description),
570
- conversion_info = COALESCE(excluded.conversion_info, conversion_info)
556
+ conversion_info = COALESCE(excluded.conversion_info, conversion_info),
557
+ conversion_status = COALESCE(excluded.conversion_status, conversion_status)
571
558
  `);
572
559
 
573
560
  stmt.run(
@@ -580,7 +567,8 @@ class SqliteStorage {
580
567
  metadata.lnurlDescription,
581
568
  metadata.conversionInfo
582
569
  ? JSON.stringify(metadata.conversionInfo)
583
- : null
570
+ : null,
571
+ metadata.conversionStatus ?? null
584
572
  );
585
573
  return Promise.resolve();
586
574
  } catch (error) {
@@ -595,14 +583,15 @@ class SqliteStorage {
595
583
 
596
584
  // ===== Deposit Operations =====
597
585
 
598
- addDeposit(txid, vout, amountSats) {
586
+ addDeposit(txid, vout, amountSats, isMature) {
599
587
  try {
600
588
  const stmt = this.db.prepare(`
601
- INSERT OR IGNORE INTO unclaimed_deposits (txid, vout, amount_sats)
602
- VALUES (?, ?, ?)
589
+ INSERT INTO unclaimed_deposits (txid, vout, amount_sats, is_mature)
590
+ VALUES (?, ?, ?, ?)
591
+ ON CONFLICT(txid, vout) DO UPDATE SET is_mature = excluded.is_mature, amount_sats = excluded.amount_sats
603
592
  `);
604
593
 
605
- stmt.run(txid, vout, amountSats);
594
+ stmt.run(txid, vout, amountSats, isMature ? 1 : 0);
606
595
  return Promise.resolve();
607
596
  } catch (error) {
608
597
  return Promise.reject(
@@ -635,7 +624,7 @@ class SqliteStorage {
635
624
  listDeposits() {
636
625
  try {
637
626
  const stmt = this.db.prepare(`
638
- SELECT txid, vout, amount_sats, claim_error, refund_tx, refund_tx_id
627
+ SELECT txid, vout, amount_sats, is_mature, claim_error, refund_tx, refund_tx_id
639
628
  FROM unclaimed_deposits
640
629
  `);
641
630
 
@@ -645,6 +634,7 @@ class SqliteStorage {
645
634
  txid: row.txid,
646
635
  vout: row.vout,
647
636
  amountSats: row.amount_sats,
637
+ isMature: Boolean(row.is_mature ?? 1),
648
638
  claimError: row.claim_error ? JSON.parse(row.claim_error) : null,
649
639
  refundTx: row.refund_tx,
650
640
  refundTxId: row.refund_tx_id,
@@ -695,7 +685,7 @@ class SqliteStorage {
695
685
  setLnurlMetadata(metadata) {
696
686
  try {
697
687
  const stmt = this.db.prepare(
698
- "INSERT OR REPLACE INTO lnurl_receive_metadata (payment_hash, nostr_zap_request, nostr_zap_receipt, sender_comment, preimage) VALUES (?, ?, ?, ?, ?)"
688
+ "INSERT OR REPLACE INTO lnurl_receive_metadata (payment_hash, nostr_zap_request, nostr_zap_receipt, sender_comment) VALUES (?, ?, ?, ?)"
699
689
  );
700
690
 
701
691
  const transaction = this.db.transaction(() => {
@@ -704,8 +694,7 @@ class SqliteStorage {
704
694
  item.paymentHash,
705
695
  item.nostrZapRequest || null,
706
696
  item.nostrZapReceipt || null,
707
- item.senderComment || null,
708
- item.preimage || null
697
+ item.senderComment || null
709
698
  );
710
699
  }
711
700
  });
@@ -830,7 +819,9 @@ class SqliteStorage {
830
819
  timestamp: row.timestamp,
831
820
  method,
832
821
  details,
833
- conversionDetails: null,
822
+ conversionDetails: row.conversion_status
823
+ ? { status: row.conversion_status, from: null, to: null }
824
+ : null,
834
825
  };
835
826
  }
836
827
 
@@ -392,6 +392,24 @@ class MigrationManager {
392
392
  updated_at INTEGER NOT NULL
393
393
  )`
394
394
  },
395
+ {
396
+ name: "Drop preimage column from lnurl_receive_metadata",
397
+ sql: `ALTER TABLE lnurl_receive_metadata DROP COLUMN preimage`
398
+ },
399
+ {
400
+ name: "Clear cached lightning address for CachedLightningAddress format change",
401
+ sql: `DELETE FROM settings WHERE key = 'lightning_address'`
402
+ },
403
+ {
404
+ name: "Add is_mature to unclaimed_deposits",
405
+ sql: [
406
+ `ALTER TABLE unclaimed_deposits ADD COLUMN is_mature INTEGER NOT NULL DEFAULT 1`,
407
+ ]
408
+ },
409
+ {
410
+ name: "Add conversion_status to payment_metadata",
411
+ sql: `ALTER TABLE payment_metadata ADD COLUMN conversion_status TEXT`,
412
+ },
395
413
  ];
396
414
  }
397
415
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@breeztech/breez-sdk-spark",
3
- "version": "0.12.2-dev1",
3
+ "version": "0.12.2-dev2",
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)",