@breeztech/breez-sdk-spark 0.7.21 → 0.8.2
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 +581 -533
- package/bundler/breez_sdk_spark_wasm_bg.js +74 -56
- package/bundler/breez_sdk_spark_wasm_bg.wasm +0 -0
- package/bundler/breez_sdk_spark_wasm_bg.wasm.d.ts +4 -2
- package/bundler/storage/index.js +521 -215
- package/deno/breez_sdk_spark_wasm.d.ts +581 -533
- package/deno/breez_sdk_spark_wasm.js +72 -54
- package/deno/breez_sdk_spark_wasm_bg.wasm +0 -0
- package/deno/breez_sdk_spark_wasm_bg.wasm.d.ts +4 -2
- package/nodejs/breez_sdk_spark_wasm.d.ts +581 -533
- package/nodejs/breez_sdk_spark_wasm.js +74 -56
- package/nodejs/breez_sdk_spark_wasm_bg.wasm +0 -0
- package/nodejs/breez_sdk_spark_wasm_bg.wasm.d.ts +4 -2
- package/nodejs/storage/index.cjs +160 -182
- package/nodejs/storage/migrations.cjs +27 -3
- package/package.json +1 -1
- package/web/breez_sdk_spark_wasm.d.ts +585 -535
- package/web/breez_sdk_spark_wasm.js +72 -54
- package/web/breez_sdk_spark_wasm_bg.wasm +0 -0
- package/web/breez_sdk_spark_wasm_bg.wasm.d.ts +4 -2
- package/web/storage/index.js +521 -215
package/nodejs/storage/index.cjs
CHANGED
|
@@ -27,6 +27,47 @@ try {
|
|
|
27
27
|
const { StorageError } = require("./errors.cjs");
|
|
28
28
|
const { MigrationManager } = require("./migrations.cjs");
|
|
29
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Base query for payment lookups.
|
|
32
|
+
* All columns are accessed by name in _rowToPayment.
|
|
33
|
+
* parent_payment_id is only used by getPaymentsByParentIds.
|
|
34
|
+
*/
|
|
35
|
+
const SELECT_PAYMENT_SQL = `
|
|
36
|
+
SELECT p.id,
|
|
37
|
+
p.payment_type,
|
|
38
|
+
p.status,
|
|
39
|
+
p.amount,
|
|
40
|
+
p.fees,
|
|
41
|
+
p.timestamp,
|
|
42
|
+
p.method,
|
|
43
|
+
p.withdraw_tx_id,
|
|
44
|
+
p.deposit_tx_id,
|
|
45
|
+
p.spark,
|
|
46
|
+
l.invoice AS lightning_invoice,
|
|
47
|
+
l.payment_hash AS lightning_payment_hash,
|
|
48
|
+
l.destination_pubkey AS lightning_destination_pubkey,
|
|
49
|
+
COALESCE(l.description, pm.lnurl_description) AS lightning_description,
|
|
50
|
+
l.preimage AS lightning_preimage,
|
|
51
|
+
pm.lnurl_pay_info,
|
|
52
|
+
pm.lnurl_withdraw_info,
|
|
53
|
+
pm.conversion_info,
|
|
54
|
+
t.metadata AS token_metadata,
|
|
55
|
+
t.tx_hash AS token_tx_hash,
|
|
56
|
+
t.tx_type AS token_tx_type,
|
|
57
|
+
t.invoice_details AS token_invoice_details,
|
|
58
|
+
s.invoice_details AS spark_invoice_details,
|
|
59
|
+
s.htlc_details AS spark_htlc_details,
|
|
60
|
+
lrm.nostr_zap_request AS lnurl_nostr_zap_request,
|
|
61
|
+
lrm.nostr_zap_receipt AS lnurl_nostr_zap_receipt,
|
|
62
|
+
lrm.sender_comment AS lnurl_sender_comment,
|
|
63
|
+
pm.parent_payment_id
|
|
64
|
+
FROM payments p
|
|
65
|
+
LEFT JOIN payment_details_lightning l ON p.id = l.payment_id
|
|
66
|
+
LEFT JOIN payment_details_token t ON p.id = t.payment_id
|
|
67
|
+
LEFT JOIN payment_details_spark s ON p.id = s.payment_id
|
|
68
|
+
LEFT JOIN payment_metadata pm ON p.id = pm.payment_id
|
|
69
|
+
LEFT JOIN lnurl_receive_metadata lrm ON l.payment_hash = lrm.payment_hash`;
|
|
70
|
+
|
|
30
71
|
class SqliteStorage {
|
|
31
72
|
constructor(dbPath, logger = null) {
|
|
32
73
|
this.dbPath = dbPath;
|
|
@@ -196,6 +237,14 @@ class SqliteStorage {
|
|
|
196
237
|
paymentDetailsClauses.push("t.tx_hash = ?");
|
|
197
238
|
params.push(paymentDetailsFilter.txHash);
|
|
198
239
|
}
|
|
240
|
+
// Filter by token transaction type
|
|
241
|
+
if (
|
|
242
|
+
paymentDetailsFilter.type === "token" &&
|
|
243
|
+
paymentDetailsFilter.txType !== undefined
|
|
244
|
+
) {
|
|
245
|
+
paymentDetailsClauses.push("t.tx_type = ?");
|
|
246
|
+
params.push(paymentDetailsFilter.txType);
|
|
247
|
+
}
|
|
199
248
|
|
|
200
249
|
if (paymentDetailsClauses.length > 0) {
|
|
201
250
|
allPaymentDetailsClauses.push(`(${paymentDetailsClauses.join(" AND ")})`);
|
|
@@ -221,50 +270,16 @@ class SqliteStorage {
|
|
|
221
270
|
}
|
|
222
271
|
}
|
|
223
272
|
|
|
273
|
+
// Exclude child payments (those with a parent_payment_id)
|
|
274
|
+
whereClauses.push("pm.parent_payment_id IS NULL");
|
|
275
|
+
|
|
224
276
|
// Build the WHERE clause
|
|
225
277
|
const whereSql =
|
|
226
278
|
whereClauses.length > 0 ? `WHERE ${whereClauses.join(" AND ")}` : "";
|
|
227
279
|
|
|
228
280
|
// Determine sort order
|
|
229
281
|
const orderDirection = request.sortAscending ? "ASC" : "DESC";
|
|
230
|
-
|
|
231
|
-
const query = `
|
|
232
|
-
SELECT p.id
|
|
233
|
-
, p.payment_type
|
|
234
|
-
, p.status
|
|
235
|
-
, p.amount
|
|
236
|
-
, p.fees
|
|
237
|
-
, p.timestamp
|
|
238
|
-
, p.method
|
|
239
|
-
, p.withdraw_tx_id
|
|
240
|
-
, p.deposit_tx_id
|
|
241
|
-
, p.spark
|
|
242
|
-
, l.invoice AS lightning_invoice
|
|
243
|
-
, l.payment_hash AS lightning_payment_hash
|
|
244
|
-
, l.destination_pubkey AS lightning_destination_pubkey
|
|
245
|
-
, COALESCE(l.description, pm.lnurl_description) AS lightning_description
|
|
246
|
-
, l.preimage AS lightning_preimage
|
|
247
|
-
, pm.lnurl_pay_info
|
|
248
|
-
, pm.lnurl_withdraw_info
|
|
249
|
-
, pm.conversion_info
|
|
250
|
-
, t.metadata AS token_metadata
|
|
251
|
-
, t.tx_hash AS token_tx_hash
|
|
252
|
-
, t.invoice_details AS token_invoice_details
|
|
253
|
-
, s.invoice_details AS spark_invoice_details
|
|
254
|
-
, s.htlc_details AS spark_htlc_details
|
|
255
|
-
, lrm.nostr_zap_request AS lnurl_nostr_zap_request
|
|
256
|
-
, lrm.nostr_zap_receipt AS lnurl_nostr_zap_receipt
|
|
257
|
-
, lrm.sender_comment AS lnurl_sender_comment
|
|
258
|
-
FROM payments p
|
|
259
|
-
LEFT JOIN payment_details_lightning l ON p.id = l.payment_id
|
|
260
|
-
LEFT JOIN payment_details_token t ON p.id = t.payment_id
|
|
261
|
-
LEFT JOIN payment_details_spark s ON p.id = s.payment_id
|
|
262
|
-
LEFT JOIN payment_metadata pm ON p.id = pm.payment_id
|
|
263
|
-
LEFT JOIN lnurl_receive_metadata lrm ON l.payment_hash = lrm.payment_hash
|
|
264
|
-
${whereSql}
|
|
265
|
-
ORDER BY p.timestamp ${orderDirection}
|
|
266
|
-
LIMIT ? OFFSET ?
|
|
267
|
-
`;
|
|
282
|
+
const query = `${SELECT_PAYMENT_SQL} ${whereSql} ORDER BY p.timestamp ${orderDirection} LIMIT ? OFFSET ?`;
|
|
268
283
|
|
|
269
284
|
params.push(actualLimit, actualOffset);
|
|
270
285
|
const stmt = this.db.prepare(query);
|
|
@@ -318,11 +333,12 @@ class SqliteStorage {
|
|
|
318
333
|
);
|
|
319
334
|
const tokenInsert = this.db.prepare(
|
|
320
335
|
`INSERT INTO payment_details_token
|
|
321
|
-
(payment_id, metadata, tx_hash, invoice_details)
|
|
322
|
-
VALUES (@id, @metadata, @txHash, @invoiceDetails)
|
|
336
|
+
(payment_id, metadata, tx_hash, tx_type, invoice_details)
|
|
337
|
+
VALUES (@id, @metadata, @txHash, @txType, @invoiceDetails)
|
|
323
338
|
ON CONFLICT(payment_id) DO UPDATE SET
|
|
324
339
|
metadata=excluded.metadata,
|
|
325
340
|
tx_hash=excluded.tx_hash,
|
|
341
|
+
tx_type=excluded.tx_type,
|
|
326
342
|
invoice_details=COALESCE(excluded.invoice_details, payment_details_token.invoice_details)`
|
|
327
343
|
);
|
|
328
344
|
const sparkInsert = this.db.prepare(
|
|
@@ -381,6 +397,7 @@ class SqliteStorage {
|
|
|
381
397
|
id: payment.id,
|
|
382
398
|
metadata: JSON.stringify(payment.details.metadata),
|
|
383
399
|
txHash: payment.details.txHash,
|
|
400
|
+
txType: payment.details.txType,
|
|
384
401
|
invoiceDetails: payment.details.invoiceDetails
|
|
385
402
|
? JSON.stringify(payment.details.invoiceDetails)
|
|
386
403
|
: null,
|
|
@@ -408,43 +425,9 @@ class SqliteStorage {
|
|
|
408
425
|
);
|
|
409
426
|
}
|
|
410
427
|
|
|
411
|
-
const stmt = this.db.prepare(
|
|
412
|
-
SELECT p.id
|
|
413
|
-
, p.payment_type
|
|
414
|
-
, p.status
|
|
415
|
-
, p.amount
|
|
416
|
-
, p.fees
|
|
417
|
-
, p.timestamp
|
|
418
|
-
, p.method
|
|
419
|
-
, p.withdraw_tx_id
|
|
420
|
-
, p.deposit_tx_id
|
|
421
|
-
, p.spark
|
|
422
|
-
, l.invoice AS lightning_invoice
|
|
423
|
-
, l.payment_hash AS lightning_payment_hash
|
|
424
|
-
, l.destination_pubkey AS lightning_destination_pubkey
|
|
425
|
-
, COALESCE(l.description, pm.lnurl_description) AS lightning_description
|
|
426
|
-
, l.preimage AS lightning_preimage
|
|
427
|
-
, pm.lnurl_pay_info
|
|
428
|
-
, pm.lnurl_withdraw_info
|
|
429
|
-
, pm.conversion_info
|
|
430
|
-
, t.metadata AS token_metadata
|
|
431
|
-
, t.tx_hash AS token_tx_hash
|
|
432
|
-
, t.invoice_details AS token_invoice_details
|
|
433
|
-
, s.invoice_details AS spark_invoice_details
|
|
434
|
-
, s.htlc_details AS spark_htlc_details
|
|
435
|
-
, lrm.nostr_zap_request AS lnurl_nostr_zap_request
|
|
436
|
-
, lrm.nostr_zap_receipt AS lnurl_nostr_zap_receipt
|
|
437
|
-
, lrm.sender_comment AS lnurl_sender_comment
|
|
438
|
-
FROM payments p
|
|
439
|
-
LEFT JOIN payment_details_lightning l ON p.id = l.payment_id
|
|
440
|
-
LEFT JOIN payment_details_token t ON p.id = t.payment_id
|
|
441
|
-
LEFT JOIN payment_details_spark s ON p.id = s.payment_id
|
|
442
|
-
LEFT JOIN payment_metadata pm ON p.id = pm.payment_id
|
|
443
|
-
LEFT JOIN lnurl_receive_metadata lrm ON l.payment_hash = lrm.payment_hash
|
|
444
|
-
WHERE p.id = ?
|
|
445
|
-
`);
|
|
446
|
-
|
|
428
|
+
const stmt = this.db.prepare(`${SELECT_PAYMENT_SQL} WHERE p.id = ?`);
|
|
447
429
|
const row = stmt.get(id);
|
|
430
|
+
|
|
448
431
|
if (!row) {
|
|
449
432
|
return Promise.reject(
|
|
450
433
|
new StorageError(`Payment with id '${id}' not found`)
|
|
@@ -472,43 +455,9 @@ class SqliteStorage {
|
|
|
472
455
|
);
|
|
473
456
|
}
|
|
474
457
|
|
|
475
|
-
const stmt = this.db.prepare(
|
|
476
|
-
SELECT p.id
|
|
477
|
-
, p.payment_type
|
|
478
|
-
, p.status
|
|
479
|
-
, p.amount
|
|
480
|
-
, p.fees
|
|
481
|
-
, p.timestamp
|
|
482
|
-
, p.method
|
|
483
|
-
, p.withdraw_tx_id
|
|
484
|
-
, p.deposit_tx_id
|
|
485
|
-
, p.spark
|
|
486
|
-
, l.invoice AS lightning_invoice
|
|
487
|
-
, l.payment_hash AS lightning_payment_hash
|
|
488
|
-
, l.destination_pubkey AS lightning_destination_pubkey
|
|
489
|
-
, COALESCE(l.description, pm.lnurl_description) AS lightning_description
|
|
490
|
-
, l.preimage AS lightning_preimage
|
|
491
|
-
, pm.lnurl_pay_info
|
|
492
|
-
, pm.lnurl_withdraw_info
|
|
493
|
-
, pm.conversion_info
|
|
494
|
-
, t.metadata AS token_metadata
|
|
495
|
-
, t.tx_hash AS token_tx_hash
|
|
496
|
-
, t.invoice_details AS token_invoice_details
|
|
497
|
-
, s.invoice_details AS spark_invoice_details
|
|
498
|
-
, s.htlc_details AS spark_htlc_details
|
|
499
|
-
, lrm.nostr_zap_request AS lnurl_nostr_zap_request
|
|
500
|
-
, lrm.nostr_zap_receipt AS lnurl_nostr_zap_receipt
|
|
501
|
-
, lrm.sender_comment AS lnurl_sender_comment
|
|
502
|
-
FROM payments p
|
|
503
|
-
LEFT JOIN payment_details_lightning l ON p.id = l.payment_id
|
|
504
|
-
LEFT JOIN payment_details_token t ON p.id = t.payment_id
|
|
505
|
-
LEFT JOIN payment_details_spark s ON p.id = s.payment_id
|
|
506
|
-
LEFT JOIN payment_metadata pm ON p.id = pm.payment_id
|
|
507
|
-
LEFT JOIN lnurl_receive_metadata lrm ON l.payment_hash = lrm.payment_hash
|
|
508
|
-
WHERE l.invoice = ?
|
|
509
|
-
`);
|
|
510
|
-
|
|
458
|
+
const stmt = this.db.prepare(`${SELECT_PAYMENT_SQL} WHERE l.invoice = ?`);
|
|
511
459
|
const row = stmt.get(invoice);
|
|
460
|
+
|
|
512
461
|
if (!row) {
|
|
513
462
|
return Promise.resolve(null);
|
|
514
463
|
}
|
|
@@ -526,11 +475,66 @@ class SqliteStorage {
|
|
|
526
475
|
}
|
|
527
476
|
}
|
|
528
477
|
|
|
529
|
-
|
|
478
|
+
/**
|
|
479
|
+
* Gets payments that have any of the specified parent payment IDs.
|
|
480
|
+
* @param {string[]} parentPaymentIds - Array of parent payment IDs
|
|
481
|
+
* @returns {Promise<Object>} Map of parentPaymentId -> array of RelatedPayment objects
|
|
482
|
+
*/
|
|
483
|
+
getPaymentsByParentIds(parentPaymentIds) {
|
|
484
|
+
try {
|
|
485
|
+
if (!parentPaymentIds || parentPaymentIds.length === 0) {
|
|
486
|
+
return Promise.resolve({});
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
// Early exit if no related payments exist
|
|
490
|
+
const hasRelated = this.db
|
|
491
|
+
.prepare(
|
|
492
|
+
"SELECT EXISTS(SELECT 1 FROM payment_metadata WHERE parent_payment_id IS NOT NULL LIMIT 1)"
|
|
493
|
+
)
|
|
494
|
+
.pluck()
|
|
495
|
+
.get();
|
|
496
|
+
if (!hasRelated) {
|
|
497
|
+
return Promise.resolve({});
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
const placeholders = parentPaymentIds.map(() => "?").join(", ");
|
|
501
|
+
const query = `${SELECT_PAYMENT_SQL} WHERE pm.parent_payment_id IN (${placeholders}) ORDER BY p.timestamp ASC`;
|
|
502
|
+
|
|
503
|
+
const stmt = this.db.prepare(query);
|
|
504
|
+
const rows = stmt.all(...parentPaymentIds);
|
|
505
|
+
|
|
506
|
+
// Group payments by parent_payment_id
|
|
507
|
+
const result = {};
|
|
508
|
+
for (const row of rows) {
|
|
509
|
+
const parentId = row.parent_payment_id;
|
|
510
|
+
if (!result[parentId]) {
|
|
511
|
+
result[parentId] = [];
|
|
512
|
+
}
|
|
513
|
+
result[parentId].push(this._rowToPayment(row));
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
return Promise.resolve(result);
|
|
517
|
+
} catch (error) {
|
|
518
|
+
return Promise.reject(
|
|
519
|
+
new StorageError(
|
|
520
|
+
`Failed to get payments by parent ids: ${error.message}`,
|
|
521
|
+
error
|
|
522
|
+
)
|
|
523
|
+
);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
insertPaymentMetadata(paymentId, metadata) {
|
|
530
528
|
try {
|
|
531
529
|
const stmt = this.db.prepare(`
|
|
532
|
-
INSERT
|
|
530
|
+
INSERT INTO payment_metadata (payment_id, parent_payment_id, lnurl_pay_info, lnurl_withdraw_info, lnurl_description, conversion_info)
|
|
533
531
|
VALUES (?, ?, ?, ?, ?, ?)
|
|
532
|
+
ON CONFLICT(payment_id) DO UPDATE SET
|
|
533
|
+
parent_payment_id = COALESCE(excluded.parent_payment_id, parent_payment_id),
|
|
534
|
+
lnurl_pay_info = COALESCE(excluded.lnurl_pay_info, lnurl_pay_info),
|
|
535
|
+
lnurl_withdraw_info = COALESCE(excluded.lnurl_withdraw_info, lnurl_withdraw_info),
|
|
536
|
+
lnurl_description = COALESCE(excluded.lnurl_description, lnurl_description),
|
|
537
|
+
conversion_info = COALESCE(excluded.conversion_info, conversion_info)
|
|
534
538
|
`);
|
|
535
539
|
|
|
536
540
|
stmt.run(
|
|
@@ -755,6 +759,7 @@ class SqliteStorage {
|
|
|
755
759
|
type: "token",
|
|
756
760
|
metadata: JSON.parse(row.token_metadata),
|
|
757
761
|
txHash: row.token_tx_hash,
|
|
762
|
+
txType: row.token_tx_type,
|
|
758
763
|
invoiceDetails: row.token_invoice_details
|
|
759
764
|
? JSON.parse(row.token_invoice_details)
|
|
760
765
|
: null,
|
|
@@ -793,11 +798,10 @@ class SqliteStorage {
|
|
|
793
798
|
syncAddOutgoingChange(record) {
|
|
794
799
|
try {
|
|
795
800
|
const transaction = this.db.transaction(() => {
|
|
796
|
-
//
|
|
801
|
+
// This revision is a local queue id for pending rows, not a server revision.
|
|
797
802
|
const revisionQuery = this.db.prepare(`
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
RETURNING CAST(revision AS TEXT) AS revision
|
|
803
|
+
SELECT CAST(COALESCE(MAX(revision), 0) + 1 AS TEXT) AS revision
|
|
804
|
+
FROM sync_outgoing
|
|
801
805
|
`);
|
|
802
806
|
const revision = BigInt(revisionQuery.get().revision);
|
|
803
807
|
|
|
@@ -836,7 +840,7 @@ class SqliteStorage {
|
|
|
836
840
|
}
|
|
837
841
|
}
|
|
838
842
|
|
|
839
|
-
syncCompleteOutgoingSync(record) {
|
|
843
|
+
syncCompleteOutgoingSync(record, localRevision) {
|
|
840
844
|
try {
|
|
841
845
|
const transaction = this.db.transaction(() => {
|
|
842
846
|
// Delete records that have been synced
|
|
@@ -848,7 +852,7 @@ class SqliteStorage {
|
|
|
848
852
|
deleteStmt.run(
|
|
849
853
|
record.id.type,
|
|
850
854
|
record.id.dataId,
|
|
851
|
-
|
|
855
|
+
localRevision.toString()
|
|
852
856
|
);
|
|
853
857
|
|
|
854
858
|
// Update or insert the sync state
|
|
@@ -871,6 +875,12 @@ class SqliteStorage {
|
|
|
871
875
|
Math.floor(Date.now() / 1000),
|
|
872
876
|
JSON.stringify(record.data)
|
|
873
877
|
);
|
|
878
|
+
|
|
879
|
+
// Update sync_revision to track the highest known revision
|
|
880
|
+
const updateRevisionStmt = this.db.prepare(`
|
|
881
|
+
UPDATE sync_revision SET revision = MAX(revision, CAST(? AS INTEGER))
|
|
882
|
+
`);
|
|
883
|
+
updateRevisionStmt.run(record.revision.toString());
|
|
874
884
|
});
|
|
875
885
|
|
|
876
886
|
transaction();
|
|
@@ -917,7 +927,7 @@ class SqliteStorage {
|
|
|
917
927
|
},
|
|
918
928
|
schemaVersion: row.schema_version,
|
|
919
929
|
updatedFields: JSON.parse(row.updated_fields_json),
|
|
920
|
-
|
|
930
|
+
localRevision: BigInt(row.revision),
|
|
921
931
|
};
|
|
922
932
|
|
|
923
933
|
let parent = null;
|
|
@@ -953,7 +963,7 @@ class SqliteStorage {
|
|
|
953
963
|
syncGetLastRevision() {
|
|
954
964
|
try {
|
|
955
965
|
const stmt = this.db.prepare(
|
|
956
|
-
`SELECT CAST(
|
|
966
|
+
`SELECT CAST(revision AS TEXT) as revision FROM sync_revision`
|
|
957
967
|
);
|
|
958
968
|
const row = stmt.get();
|
|
959
969
|
|
|
@@ -1028,47 +1038,6 @@ class SqliteStorage {
|
|
|
1028
1038
|
}
|
|
1029
1039
|
}
|
|
1030
1040
|
|
|
1031
|
-
syncRebasePendingOutgoingRecords(revision) {
|
|
1032
|
-
try {
|
|
1033
|
-
const transaction = this.db.transaction(() => {
|
|
1034
|
-
// Get current revision
|
|
1035
|
-
const getLastRevisionStmt = this.db.prepare(`
|
|
1036
|
-
SELECT CAST(COALESCE(MAX(revision), 0) AS TEXT) as last_revision FROM sync_state
|
|
1037
|
-
`);
|
|
1038
|
-
const revisionRow = getLastRevisionStmt.get();
|
|
1039
|
-
const lastRevision = revisionRow
|
|
1040
|
-
? BigInt(revisionRow.last_revision)
|
|
1041
|
-
: BigInt(0);
|
|
1042
|
-
|
|
1043
|
-
// Calculate the difference to add to all revision numbers
|
|
1044
|
-
const diff =
|
|
1045
|
-
revision > lastRevision ? revision - lastRevision : BigInt(0);
|
|
1046
|
-
|
|
1047
|
-
if (diff === BigInt(0)) {
|
|
1048
|
-
return; // No rebasing needed
|
|
1049
|
-
}
|
|
1050
|
-
|
|
1051
|
-
// Update all pending outgoing records
|
|
1052
|
-
const updateRecordsStmt = this.db.prepare(`
|
|
1053
|
-
UPDATE sync_outgoing
|
|
1054
|
-
SET revision = revision + CAST(? AS INTEGER)
|
|
1055
|
-
`);
|
|
1056
|
-
|
|
1057
|
-
updateRecordsStmt.run(diff.toString());
|
|
1058
|
-
});
|
|
1059
|
-
|
|
1060
|
-
transaction();
|
|
1061
|
-
return Promise.resolve();
|
|
1062
|
-
} catch (error) {
|
|
1063
|
-
return Promise.reject(
|
|
1064
|
-
new StorageError(
|
|
1065
|
-
`Failed to rebase pending outgoing records: ${error.message}`,
|
|
1066
|
-
error
|
|
1067
|
-
)
|
|
1068
|
-
);
|
|
1069
|
-
}
|
|
1070
|
-
}
|
|
1071
|
-
|
|
1072
1041
|
syncGetIncomingRecords(limit) {
|
|
1073
1042
|
try {
|
|
1074
1043
|
const transaction = this.db.transaction(() => {
|
|
@@ -1174,7 +1143,7 @@ class SqliteStorage {
|
|
|
1174
1143
|
},
|
|
1175
1144
|
schemaVersion: row.schema_version,
|
|
1176
1145
|
updatedFields: JSON.parse(row.updated_fields_json),
|
|
1177
|
-
|
|
1146
|
+
localRevision: BigInt(row.revision),
|
|
1178
1147
|
};
|
|
1179
1148
|
|
|
1180
1149
|
let parent = null;
|
|
@@ -1206,26 +1175,35 @@ class SqliteStorage {
|
|
|
1206
1175
|
|
|
1207
1176
|
syncUpdateRecordFromIncoming(record) {
|
|
1208
1177
|
try {
|
|
1209
|
-
const
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1178
|
+
const transaction = this.db.transaction(() => {
|
|
1179
|
+
const stmt = this.db.prepare(`
|
|
1180
|
+
INSERT OR REPLACE INTO sync_state (
|
|
1181
|
+
record_type,
|
|
1182
|
+
data_id,
|
|
1183
|
+
revision,
|
|
1184
|
+
schema_version,
|
|
1185
|
+
commit_time,
|
|
1186
|
+
data
|
|
1187
|
+
) VALUES (?, ?, CAST(? AS INTEGER), ?, ?, ?)
|
|
1188
|
+
`);
|
|
1219
1189
|
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1190
|
+
stmt.run(
|
|
1191
|
+
record.id.type,
|
|
1192
|
+
record.id.dataId,
|
|
1193
|
+
record.revision.toString(),
|
|
1194
|
+
record.schemaVersion,
|
|
1195
|
+
Math.floor(Date.now() / 1000),
|
|
1196
|
+
JSON.stringify(record.data)
|
|
1197
|
+
);
|
|
1228
1198
|
|
|
1199
|
+
// Update sync_revision to track the highest known revision
|
|
1200
|
+
const updateRevisionStmt = this.db.prepare(`
|
|
1201
|
+
UPDATE sync_revision SET revision = MAX(revision, CAST(? AS INTEGER))
|
|
1202
|
+
`);
|
|
1203
|
+
updateRevisionStmt.run(record.revision.toString());
|
|
1204
|
+
});
|
|
1205
|
+
|
|
1206
|
+
transaction();
|
|
1229
1207
|
return Promise.resolve();
|
|
1230
1208
|
} catch (error) {
|
|
1231
1209
|
return Promise.reject(
|
|
@@ -229,6 +229,8 @@ class MigrationManager {
|
|
|
229
229
|
{
|
|
230
230
|
name: "Create sync tables",
|
|
231
231
|
sql: [
|
|
232
|
+
// sync_revision: tracks the last committed revision (from server-acknowledged
|
|
233
|
+
// or server-received records). Does NOT include pending outgoing revisions.
|
|
232
234
|
`CREATE TABLE sync_revision (
|
|
233
235
|
revision INTEGER NOT NULL DEFAULT 0
|
|
234
236
|
)`,
|
|
@@ -285,13 +287,13 @@ class MigrationManager {
|
|
|
285
287
|
nostr_zap_request TEXT,
|
|
286
288
|
nostr_zap_receipt TEXT,
|
|
287
289
|
sender_comment TEXT
|
|
288
|
-
)
|
|
290
|
+
)`,
|
|
289
291
|
},
|
|
290
292
|
{
|
|
291
293
|
// Delete all unclaimed deposits to clear old claim_error JSON format.
|
|
292
294
|
// Deposits will be recovered on next sync.
|
|
293
295
|
name: "Clear unclaimed deposits for claim_error format change",
|
|
294
|
-
sql: `DELETE FROM unclaimed_deposits
|
|
296
|
+
sql: `DELETE FROM unclaimed_deposits`,
|
|
295
297
|
},
|
|
296
298
|
{
|
|
297
299
|
// Clear all sync tables due to BreezSigner signature change.
|
|
@@ -309,7 +311,7 @@ class MigrationManager {
|
|
|
309
311
|
},
|
|
310
312
|
{
|
|
311
313
|
name: "Add token conversion info to payment_metadata",
|
|
312
|
-
sql: `ALTER TABLE payment_metadata ADD COLUMN token_conversion_info TEXT
|
|
314
|
+
sql: `ALTER TABLE payment_metadata ADD COLUMN token_conversion_info TEXT`,
|
|
313
315
|
},
|
|
314
316
|
{
|
|
315
317
|
name: "Add parent payment id to payment_metadata",
|
|
@@ -321,6 +323,28 @@ class MigrationManager {
|
|
|
321
323
|
`ALTER TABLE payment_metadata DROP COLUMN token_conversion_info`,
|
|
322
324
|
`ALTER TABLE payment_metadata ADD COLUMN conversion_info TEXT`]
|
|
323
325
|
},
|
|
326
|
+
{
|
|
327
|
+
name: "Add tx_type column to payment_details_token",
|
|
328
|
+
sql: [
|
|
329
|
+
// Add tx_type column with a default value of 'transfer'.
|
|
330
|
+
// Delete sync cache to trigger token re-sync which will update all records with correct tx_type.
|
|
331
|
+
// Note: This intentionally couples to the CachedSyncInfo schema at migration time.
|
|
332
|
+
`ALTER TABLE payment_details_token ADD COLUMN tx_type TEXT NOT NULL DEFAULT 'transfer'`,
|
|
333
|
+
`UPDATE settings
|
|
334
|
+
SET value = json_set(value, '$.last_synced_final_token_payment_id', NULL)
|
|
335
|
+
WHERE key = 'sync_offset' AND json_valid(value) AND json_type(value, '$.last_synced_final_token_payment_id') IS NOT NULL`,
|
|
336
|
+
],
|
|
337
|
+
},
|
|
338
|
+
{
|
|
339
|
+
name: "Clear sync tables to force re-sync",
|
|
340
|
+
sql: [
|
|
341
|
+
`DELETE FROM sync_outgoing`,
|
|
342
|
+
`DELETE FROM sync_incoming`,
|
|
343
|
+
`DELETE FROM sync_state`,
|
|
344
|
+
`UPDATE sync_revision SET revision = 0`,
|
|
345
|
+
`DELETE FROM settings WHERE key = 'sync_initial_complete'`
|
|
346
|
+
]
|
|
347
|
+
},
|
|
324
348
|
];
|
|
325
349
|
}
|
|
326
350
|
}
|
package/package.json
CHANGED