@breeztech/breez-sdk-spark 0.13.10-dev → 0.13.11-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.
Files changed (41) hide show
  1. package/breez-sdk-spark.tgz +0 -0
  2. package/bundler/breez_sdk_spark_wasm.d.ts +33 -0
  3. package/bundler/breez_sdk_spark_wasm.js +1 -1
  4. package/bundler/breez_sdk_spark_wasm_bg.js +66 -24
  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/deno/breez_sdk_spark_wasm.d.ts +33 -0
  8. package/deno/breez_sdk_spark_wasm.js +66 -24
  9. package/deno/breez_sdk_spark_wasm_bg.wasm +0 -0
  10. package/deno/breez_sdk_spark_wasm_bg.wasm.d.ts +7 -5
  11. package/nodejs/breez_sdk_spark_wasm.d.ts +33 -0
  12. package/nodejs/breez_sdk_spark_wasm.js +67 -24
  13. package/nodejs/breez_sdk_spark_wasm_bg.wasm +0 -0
  14. package/nodejs/breez_sdk_spark_wasm_bg.wasm.d.ts +7 -5
  15. package/nodejs/index.js +34 -0
  16. package/nodejs/index.mjs +1 -0
  17. package/nodejs/mysql-storage/errors.cjs +19 -0
  18. package/nodejs/mysql-storage/index.cjs +1366 -0
  19. package/nodejs/mysql-storage/migrations.cjs +387 -0
  20. package/nodejs/mysql-storage/package.json +9 -0
  21. package/nodejs/mysql-token-store/errors.cjs +9 -0
  22. package/nodejs/mysql-token-store/index.cjs +988 -0
  23. package/nodejs/mysql-token-store/migrations.cjs +255 -0
  24. package/nodejs/mysql-token-store/package.json +9 -0
  25. package/nodejs/mysql-tree-store/errors.cjs +9 -0
  26. package/nodejs/mysql-tree-store/index.cjs +939 -0
  27. package/nodejs/mysql-tree-store/migrations.cjs +221 -0
  28. package/nodejs/mysql-tree-store/package.json +9 -0
  29. package/nodejs/package.json +3 -0
  30. package/nodejs/postgres-storage/index.cjs +147 -92
  31. package/nodejs/postgres-storage/migrations.cjs +85 -4
  32. package/nodejs/postgres-token-store/index.cjs +176 -89
  33. package/nodejs/postgres-token-store/migrations.cjs +92 -3
  34. package/nodejs/postgres-tree-store/index.cjs +168 -83
  35. package/nodejs/postgres-tree-store/migrations.cjs +80 -3
  36. package/package.json +1 -1
  37. package/ssr/index.js +5 -0
  38. package/web/breez_sdk_spark_wasm.d.ts +40 -5
  39. package/web/breez_sdk_spark_wasm.js +66 -24
  40. package/web/breez_sdk_spark_wasm_bg.wasm +0 -0
  41. package/web/breez_sdk_spark_wasm_bg.wasm.d.ts +7 -5
@@ -0,0 +1,255 @@
1
+ /**
2
+ * Token store migrations for MySQL 8.0+. Mirrors `postgres-token-store/migrations.cjs`.
3
+ */
4
+
5
+ const { TokenStoreError } = require("./errors.cjs");
6
+
7
+ const TOKEN_MIGRATIONS_TABLE = "token_schema_migrations";
8
+ const MIGRATION_LOCK_NAME = "breez_mysql_token_store_migration_lock";
9
+ const MIGRATION_LOCK_TIMEOUT = 60;
10
+
11
+ /**
12
+ * Runs a single migration step. Plain strings are run as-is; tagged objects
13
+ * (`{ op: 'dropPrimaryKey', table }`) are guarded against partial-apply replay
14
+ * by checking `information_schema` first. MySQL DDL implicitly commits, so
15
+ * if the migration crashes between two DDL statements the version row never
16
+ * gets recorded — and on retry, an unguarded DROP PRIMARY KEY would fail
17
+ * (`ER_CANT_DROP_FIELD_OR_KEY`) because the PK is already gone.
18
+ */
19
+ async function runMigrationStep(conn, step) {
20
+ if (typeof step === "string") {
21
+ await conn.query(step);
22
+ return;
23
+ }
24
+ if (step.op === "dropPrimaryKey") {
25
+ const [rows] = await conn.query(
26
+ `SELECT COUNT(*) AS c FROM information_schema.table_constraints
27
+ WHERE table_schema = DATABASE()
28
+ AND table_name = ?
29
+ AND constraint_type = 'PRIMARY KEY'`,
30
+ [step.table]
31
+ );
32
+ if (rows[0].c > 0) {
33
+ await conn.query(`ALTER TABLE \`${step.table}\` DROP PRIMARY KEY`);
34
+ }
35
+ return;
36
+ }
37
+ throw new Error(`Unknown migration step op: ${JSON.stringify(step)}`);
38
+ }
39
+
40
+ class MysqlTokenStoreMigrationManager {
41
+ constructor(logger = null) {
42
+ this.logger = logger;
43
+ }
44
+
45
+ /**
46
+ * @param {import('mysql2/promise').Pool} pool
47
+ * @param {Buffer|Uint8Array} identity - 33-byte secp256k1 compressed pubkey
48
+ * identifying the tenant. Used to backfill `user_id` columns in the
49
+ * multi-tenant scoping migration. Required.
50
+ */
51
+ async migrate(pool, identity) {
52
+ if (!identity || identity.length !== 33) {
53
+ throw new TokenStoreError(
54
+ "tenant identity (33-byte secp256k1 pubkey) is required"
55
+ );
56
+ }
57
+ const conn = await pool.getConnection();
58
+ try {
59
+ const [lockRows] = await conn.query(
60
+ "SELECT GET_LOCK(?, ?) AS acquired",
61
+ [MIGRATION_LOCK_NAME, MIGRATION_LOCK_TIMEOUT]
62
+ );
63
+ if (!lockRows || lockRows[0].acquired !== 1) {
64
+ throw new TokenStoreError(
65
+ `Failed to acquire token store migration lock within ${MIGRATION_LOCK_TIMEOUT}s`
66
+ );
67
+ }
68
+
69
+ try {
70
+ await conn.query("START TRANSACTION");
71
+
72
+ await conn.query(`
73
+ CREATE TABLE IF NOT EXISTS \`${TOKEN_MIGRATIONS_TABLE}\` (
74
+ version INT PRIMARY KEY,
75
+ applied_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6)
76
+ )
77
+ `);
78
+
79
+ const [versionRows] = await conn.query(
80
+ `SELECT COALESCE(MAX(version), 0) AS version FROM \`${TOKEN_MIGRATIONS_TABLE}\``
81
+ );
82
+ const currentVersion = versionRows[0].version;
83
+
84
+ const migrations = this._getMigrations(identity);
85
+
86
+ if (currentVersion >= migrations.length) {
87
+ await conn.query("COMMIT");
88
+ return;
89
+ }
90
+
91
+ for (let i = currentVersion; i < migrations.length; i++) {
92
+ const migration = migrations[i];
93
+ const version = i + 1;
94
+ for (const step of migration.sql) {
95
+ await runMigrationStep(conn, step);
96
+ }
97
+ await conn.query(
98
+ `INSERT INTO \`${TOKEN_MIGRATIONS_TABLE}\` (version) VALUES (?)`,
99
+ [version]
100
+ );
101
+ }
102
+
103
+ await conn.query("COMMIT");
104
+ } catch (error) {
105
+ await conn.query("ROLLBACK").catch(() => {});
106
+ throw new TokenStoreError(
107
+ `Token store migration failed: ${error.message}`,
108
+ error
109
+ );
110
+ } finally {
111
+ await conn
112
+ .query("SELECT RELEASE_LOCK(?)", [MIGRATION_LOCK_NAME])
113
+ .catch(() => {});
114
+ }
115
+ } finally {
116
+ conn.release();
117
+ }
118
+ }
119
+
120
+ /**
121
+ * @param {Buffer|Uint8Array} identity - tenant identity inlined as a hex
122
+ * `UNHEX('…')` literal in the multi-tenant scoping migration. Safe because
123
+ * the bytes come from a typed secp256k1 pubkey (`[0-9a-f]{66}` after hex
124
+ * encoding) — not user-controlled input.
125
+ */
126
+ _getMigrations(identity) {
127
+ const idHex = Buffer.from(identity).toString("hex");
128
+ const idLit = `UNHEX('${idHex}')`;
129
+
130
+ return [
131
+ {
132
+ name: "Create token store tables",
133
+ sql: [
134
+ `CREATE TABLE IF NOT EXISTS token_metadata (
135
+ identifier VARCHAR(255) NOT NULL PRIMARY KEY,
136
+ issuer_public_key VARCHAR(255) NOT NULL,
137
+ name VARCHAR(255) NOT NULL,
138
+ ticker VARCHAR(64) NOT NULL,
139
+ decimals INT NOT NULL,
140
+ max_supply VARCHAR(128) NOT NULL,
141
+ is_freezable TINYINT(1) NOT NULL,
142
+ creation_entity_public_key VARCHAR(255) NULL
143
+ )`,
144
+ `CREATE INDEX idx_token_metadata_issuer_pk
145
+ ON token_metadata (issuer_public_key)`,
146
+ `CREATE TABLE IF NOT EXISTS token_reservations (
147
+ id VARCHAR(255) NOT NULL PRIMARY KEY,
148
+ purpose VARCHAR(64) NOT NULL,
149
+ created_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6)
150
+ )`,
151
+ `CREATE TABLE IF NOT EXISTS token_outputs (
152
+ id VARCHAR(255) NOT NULL PRIMARY KEY,
153
+ token_identifier VARCHAR(255) NOT NULL,
154
+ owner_public_key VARCHAR(255) NOT NULL,
155
+ revocation_commitment VARCHAR(255) NOT NULL,
156
+ withdraw_bond_sats BIGINT NOT NULL,
157
+ withdraw_relative_block_locktime BIGINT NOT NULL,
158
+ token_public_key VARCHAR(255) NULL,
159
+ token_amount VARCHAR(128) NOT NULL,
160
+ prev_tx_hash VARCHAR(255) NOT NULL,
161
+ prev_tx_vout INT NOT NULL,
162
+ reservation_id VARCHAR(255) NULL,
163
+ added_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
164
+ CONSTRAINT fk_token_outputs_metadata FOREIGN KEY (token_identifier)
165
+ REFERENCES token_metadata(identifier),
166
+ CONSTRAINT fk_token_outputs_reservation FOREIGN KEY (reservation_id)
167
+ REFERENCES token_reservations(id) ON DELETE SET NULL
168
+ )`,
169
+ `CREATE INDEX idx_token_outputs_identifier
170
+ ON token_outputs (token_identifier)`,
171
+ `CREATE INDEX idx_token_outputs_reservation
172
+ ON token_outputs (reservation_id)`,
173
+ `CREATE TABLE IF NOT EXISTS token_spent_outputs (
174
+ output_id VARCHAR(255) NOT NULL PRIMARY KEY,
175
+ spent_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6)
176
+ )`,
177
+ `CREATE TABLE IF NOT EXISTS token_swap_status (
178
+ id INT NOT NULL PRIMARY KEY DEFAULT 1,
179
+ last_completed_at DATETIME(6) NULL,
180
+ CHECK (id = 1)
181
+ )`,
182
+ `INSERT INTO token_swap_status (id) VALUES (1)
183
+ ON DUPLICATE KEY UPDATE id = id`,
184
+ ],
185
+ },
186
+ {
187
+ // Mirrors Rust migration 2 in spark-mysql/src/token_store.rs and the
188
+ // postgres equivalent. Adds user_id to every token-store table
189
+ // (including token_metadata — per-tenant to avoid 0-balance leakage
190
+ // for tokens a tenant never owned), backfills with the connecting
191
+ // tenant, and rewrites primary keys / FKs / indexes to lead with
192
+ // user_id. Composite FKs use NO ACTION because column-list SET NULL
193
+ // would null user_id (NOT NULL).
194
+ name: "Multi-tenant scoping: add user_id and rewrite primary keys / FKs",
195
+ sql: [
196
+ // Drop dependent FKs FIRST so we can rewrite the parent PKs.
197
+ `ALTER TABLE token_outputs DROP FOREIGN KEY fk_token_outputs_metadata`,
198
+ `ALTER TABLE token_outputs DROP FOREIGN KEY fk_token_outputs_reservation`,
199
+
200
+ // token_metadata: per-tenant scoping (privacy — see header).
201
+ `ALTER TABLE token_metadata ADD COLUMN user_id VARBINARY(33) NULL`,
202
+ `UPDATE token_metadata SET user_id = ${idLit} WHERE user_id IS NULL`,
203
+ `ALTER TABLE token_metadata MODIFY COLUMN user_id VARBINARY(33) NOT NULL`,
204
+ `ALTER TABLE token_metadata DROP PRIMARY KEY, ADD PRIMARY KEY (user_id, identifier)`,
205
+ `DROP INDEX idx_token_metadata_issuer_pk ON token_metadata`,
206
+ `CREATE INDEX idx_token_metadata_user_issuer_pk
207
+ ON token_metadata (user_id, issuer_public_key)`,
208
+
209
+ // token_reservations: scope by user_id.
210
+ `ALTER TABLE token_reservations ADD COLUMN user_id VARBINARY(33) NULL`,
211
+ `UPDATE token_reservations SET user_id = ${idLit} WHERE user_id IS NULL`,
212
+ `ALTER TABLE token_reservations MODIFY COLUMN user_id VARBINARY(33) NOT NULL`,
213
+ `ALTER TABLE token_reservations DROP PRIMARY KEY, ADD PRIMARY KEY (user_id, id)`,
214
+
215
+ // token_outputs: scope by user_id, rekey, re-add composite FKs.
216
+ `ALTER TABLE token_outputs ADD COLUMN user_id VARBINARY(33) NULL`,
217
+ `UPDATE token_outputs SET user_id = ${idLit} WHERE user_id IS NULL`,
218
+ `ALTER TABLE token_outputs MODIFY COLUMN user_id VARBINARY(33) NOT NULL`,
219
+ `ALTER TABLE token_outputs DROP PRIMARY KEY, ADD PRIMARY KEY (user_id, id)`,
220
+ `ALTER TABLE token_outputs
221
+ ADD CONSTRAINT fk_token_outputs_metadata_user
222
+ FOREIGN KEY (user_id, token_identifier)
223
+ REFERENCES token_metadata(user_id, identifier)`,
224
+ `ALTER TABLE token_outputs
225
+ ADD CONSTRAINT fk_token_outputs_reservation_user
226
+ FOREIGN KEY (user_id, reservation_id)
227
+ REFERENCES token_reservations(user_id, id)`,
228
+ `DROP INDEX idx_token_outputs_identifier ON token_outputs`,
229
+ `DROP INDEX idx_token_outputs_reservation ON token_outputs`,
230
+ `CREATE INDEX idx_token_outputs_user_identifier
231
+ ON token_outputs (user_id, token_identifier)`,
232
+ `CREATE INDEX idx_token_outputs_user_reservation
233
+ ON token_outputs (user_id, reservation_id)`,
234
+
235
+ // token_spent_outputs: scope by user_id.
236
+ `ALTER TABLE token_spent_outputs ADD COLUMN user_id VARBINARY(33) NULL`,
237
+ `UPDATE token_spent_outputs SET user_id = ${idLit} WHERE user_id IS NULL`,
238
+ `ALTER TABLE token_spent_outputs MODIFY COLUMN user_id VARBINARY(33) NOT NULL`,
239
+ `ALTER TABLE token_spent_outputs DROP PRIMARY KEY, ADD PRIMARY KEY (user_id, output_id)`,
240
+
241
+ // token_swap_status was a singleton (PK id=1, CHECK id=1). Drop the
242
+ // PK and the id column, then re-key by user_id.
243
+ { op: "dropPrimaryKey", table: "token_swap_status" },
244
+ `ALTER TABLE token_swap_status DROP COLUMN id`,
245
+ `ALTER TABLE token_swap_status ADD COLUMN user_id VARBINARY(33) NULL`,
246
+ `UPDATE token_swap_status SET user_id = ${idLit} WHERE user_id IS NULL`,
247
+ `ALTER TABLE token_swap_status MODIFY COLUMN user_id VARBINARY(33) NOT NULL`,
248
+ `ALTER TABLE token_swap_status ADD PRIMARY KEY (user_id)`,
249
+ ],
250
+ },
251
+ ];
252
+ }
253
+ }
254
+
255
+ module.exports = { MysqlTokenStoreMigrationManager };
@@ -0,0 +1,9 @@
1
+ {
2
+ "dependencies": {
3
+ "mysql2": "^3.11.0"
4
+ },
5
+ "description": "Node.js MySQL token store implementation for Breez SDK WASM (CommonJS)",
6
+ "main": "index.cjs",
7
+ "name": "@breez-sdk/mysql-token-store",
8
+ "version": "1.0.0"
9
+ }
@@ -0,0 +1,9 @@
1
+ class TreeStoreError extends Error {
2
+ constructor(message, cause = null) {
3
+ super(message);
4
+ this.name = 'TreeStoreError';
5
+ this.cause = cause;
6
+ }
7
+ }
8
+
9
+ module.exports = { TreeStoreError };