@hasna/cloud 0.1.10 → 0.1.12

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/dist/cli/index.js CHANGED
@@ -11452,47 +11452,100 @@ async function syncTransfer(source, target, options, _direction) {
11452
11452
  primaryKey: pkOption
11453
11453
  } = options;
11454
11454
  const results = [];
11455
- if (!isAsyncAdapter(target)) {
11455
+ const sqliteTarget = !isAsyncAdapter(target) ? target : null;
11456
+ if (sqliteTarget) {
11456
11457
  try {
11457
- target.run("PRAGMA foreign_keys = OFF");
11458
+ sqliteTarget.exec("PRAGMA foreign_keys = OFF");
11458
11459
  } catch {}
11459
11460
  }
11460
- for (let i = 0;i < tables.length; i++) {
11461
- const table = tables[i];
11462
- const result = {
11463
- table,
11464
- rowsRead: 0,
11465
- rowsWritten: 0,
11466
- rowsSkipped: 0,
11467
- errors: []
11468
- };
11469
- try {
11470
- onProgress?.({
11461
+ try {
11462
+ for (let i = 0;i < tables.length; i++) {
11463
+ const table = tables[i];
11464
+ const result = {
11471
11465
  table,
11472
- phase: "reading",
11473
11466
  rowsRead: 0,
11474
11467
  rowsWritten: 0,
11475
- totalTables: tables.length,
11476
- currentTableIndex: i
11477
- });
11478
- const rows = await readAll(source, `SELECT * FROM "${table}"`);
11479
- result.rowsRead = rows.length;
11480
- if (rows.length === 0) {
11468
+ rowsSkipped: 0,
11469
+ errors: []
11470
+ };
11471
+ try {
11481
11472
  onProgress?.({
11482
11473
  table,
11483
- phase: "done",
11474
+ phase: "reading",
11484
11475
  rowsRead: 0,
11485
11476
  rowsWritten: 0,
11486
11477
  totalTables: tables.length,
11487
11478
  currentTableIndex: i
11488
11479
  });
11489
- results.push(result);
11490
- continue;
11491
- }
11492
- const pkColumns = await resolvePrimaryKeys(source, target, table, pkOption);
11493
- const columns = Object.keys(rows[0]);
11494
- if (pkColumns.length === 0) {
11495
- result.errors.push(`Table "${table}" has no primary key — inserting without conflict handling`);
11480
+ const rows = await readAll(source, `SELECT * FROM "${table}"`);
11481
+ result.rowsRead = rows.length;
11482
+ if (rows.length === 0) {
11483
+ onProgress?.({
11484
+ table,
11485
+ phase: "done",
11486
+ rowsRead: 0,
11487
+ rowsWritten: 0,
11488
+ totalTables: tables.length,
11489
+ currentTableIndex: i
11490
+ });
11491
+ results.push(result);
11492
+ continue;
11493
+ }
11494
+ const pkColumns = await resolvePrimaryKeys(source, target, table, pkOption);
11495
+ const sourceColumns = Object.keys(rows[0]);
11496
+ let targetColumns = null;
11497
+ if (!isAsyncAdapter(target)) {
11498
+ try {
11499
+ const colInfo = target.all(`PRAGMA table_info("${table}")`);
11500
+ targetColumns = new Set(colInfo.map((c) => c.name));
11501
+ } catch {}
11502
+ } else {
11503
+ try {
11504
+ const colInfo = await target.all(`SELECT column_name FROM information_schema.columns WHERE table_schema = 'public' AND table_name = '${table}'`);
11505
+ targetColumns = new Set(colInfo.map((c) => c.column_name));
11506
+ } catch {}
11507
+ }
11508
+ const columns = targetColumns ? sourceColumns.filter((c) => targetColumns.has(c)) : sourceColumns;
11509
+ if (pkColumns.length === 0) {
11510
+ result.errors.push(`Table "${table}" has no primary key — inserting without conflict handling`);
11511
+ onProgress?.({
11512
+ table,
11513
+ phase: "writing",
11514
+ rowsRead: result.rowsRead,
11515
+ rowsWritten: 0,
11516
+ totalTables: tables.length,
11517
+ currentTableIndex: i
11518
+ });
11519
+ for (let offset = 0;offset < rows.length; offset += batchSize) {
11520
+ const batch = rows.slice(offset, offset + batchSize);
11521
+ try {
11522
+ if (isAsyncAdapter(target)) {
11523
+ await batchInsertPg(target, table, columns, batch);
11524
+ } else {
11525
+ batchInsertSqlite(target, table, columns, batch);
11526
+ }
11527
+ result.rowsWritten += batch.length;
11528
+ } catch (err) {
11529
+ result.errors.push(`Batch at offset ${offset}: ${err?.message ?? String(err)}`);
11530
+ }
11531
+ }
11532
+ onProgress?.({
11533
+ table,
11534
+ phase: "done",
11535
+ rowsRead: result.rowsRead,
11536
+ rowsWritten: result.rowsWritten,
11537
+ totalTables: tables.length,
11538
+ currentTableIndex: i
11539
+ });
11540
+ results.push(result);
11541
+ continue;
11542
+ }
11543
+ const missingPks = pkColumns.filter((pk) => !columns.includes(pk));
11544
+ if (missingPks.length > 0) {
11545
+ result.errors.push(`Table "${table}" missing PK columns in data: ${missingPks.join(", ")} — skipping`);
11546
+ results.push(result);
11547
+ continue;
11548
+ }
11496
11549
  onProgress?.({
11497
11550
  table,
11498
11551
  phase: "writing",
@@ -11501,18 +11554,27 @@ async function syncTransfer(source, target, options, _direction) {
11501
11554
  totalTables: tables.length,
11502
11555
  currentTableIndex: i
11503
11556
  });
11557
+ const updateCols = columns.filter((c) => !pkColumns.includes(c));
11504
11558
  for (let offset = 0;offset < rows.length; offset += batchSize) {
11505
11559
  const batch = rows.slice(offset, offset + batchSize);
11506
11560
  try {
11507
11561
  if (isAsyncAdapter(target)) {
11508
- await batchInsertPg(target, table, columns, batch);
11562
+ await batchUpsertPg(target, table, columns, updateCols, pkColumns, batch);
11509
11563
  } else {
11510
- batchInsertSqlite(target, table, columns, batch);
11564
+ batchUpsertSqlite(target, table, columns, updateCols, pkColumns, batch);
11511
11565
  }
11512
11566
  result.rowsWritten += batch.length;
11513
11567
  } catch (err) {
11514
11568
  result.errors.push(`Batch at offset ${offset}: ${err?.message ?? String(err)}`);
11515
11569
  }
11570
+ onProgress?.({
11571
+ table,
11572
+ phase: "writing",
11573
+ rowsRead: result.rowsRead,
11574
+ rowsWritten: result.rowsWritten,
11575
+ totalTables: tables.length,
11576
+ currentTableIndex: i
11577
+ });
11516
11578
  }
11517
11579
  onProgress?.({
11518
11580
  table,
@@ -11522,62 +11584,27 @@ async function syncTransfer(source, target, options, _direction) {
11522
11584
  totalTables: tables.length,
11523
11585
  currentTableIndex: i
11524
11586
  });
11525
- results.push(result);
11526
- continue;
11527
- }
11528
- const missingPks = pkColumns.filter((pk) => !columns.includes(pk));
11529
- if (missingPks.length > 0) {
11530
- result.errors.push(`Table "${table}" missing PK columns in data: ${missingPks.join(", ")} — skipping`);
11531
- results.push(result);
11532
- continue;
11587
+ } catch (err) {
11588
+ result.errors.push(`Table "${table}": ${err?.message ?? String(err)}`);
11533
11589
  }
11534
- onProgress?.({
11535
- table,
11536
- phase: "writing",
11537
- rowsRead: result.rowsRead,
11538
- rowsWritten: 0,
11539
- totalTables: tables.length,
11540
- currentTableIndex: i
11541
- });
11542
- const updateCols = columns.filter((c) => !pkColumns.includes(c));
11543
- for (let offset = 0;offset < rows.length; offset += batchSize) {
11544
- const batch = rows.slice(offset, offset + batchSize);
11545
- try {
11546
- if (isAsyncAdapter(target)) {
11547
- await batchUpsertPg(target, table, columns, updateCols, pkColumns, batch);
11548
- } else {
11549
- batchUpsertSqlite(target, table, columns, updateCols, pkColumns, batch);
11590
+ results.push(result);
11591
+ }
11592
+ } finally {
11593
+ if (sqliteTarget) {
11594
+ try {
11595
+ sqliteTarget.exec("PRAGMA foreign_keys = ON");
11596
+ } catch {}
11597
+ try {
11598
+ const violations = sqliteTarget.all("PRAGMA foreign_key_check");
11599
+ if (violations.length > 0) {
11600
+ const tables2 = [...new Set(violations.map((v) => v.table))];
11601
+ const msg = `FK integrity check: ${violations.length} violation(s) in table(s): ${tables2.join(", ")}`;
11602
+ if (results.length > 0) {
11603
+ results[results.length - 1].errors.push(msg);
11550
11604
  }
11551
- result.rowsWritten += batch.length;
11552
- } catch (err) {
11553
- result.errors.push(`Batch at offset ${offset}: ${err?.message ?? String(err)}`);
11554
11605
  }
11555
- onProgress?.({
11556
- table,
11557
- phase: "writing",
11558
- rowsRead: result.rowsRead,
11559
- rowsWritten: result.rowsWritten,
11560
- totalTables: tables.length,
11561
- currentTableIndex: i
11562
- });
11563
- }
11564
- onProgress?.({
11565
- table,
11566
- phase: "done",
11567
- rowsRead: result.rowsRead,
11568
- rowsWritten: result.rowsWritten,
11569
- totalTables: tables.length,
11570
- currentTableIndex: i
11571
- });
11572
- } catch (err) {
11573
- result.errors.push(`Table "${table}": ${err?.message ?? String(err)}`);
11606
+ } catch {}
11574
11607
  }
11575
- results.push(result);
11576
- }
11577
- if (!isAsyncAdapter(target)) {
11578
- try {
11579
- target.run("PRAGMA foreign_keys = ON");
11580
- } catch {}
11581
11608
  }
11582
11609
  return results;
11583
11610
  }
package/dist/index.js CHANGED
@@ -9444,47 +9444,100 @@ async function syncTransfer(source, target, options, _direction) {
9444
9444
  primaryKey: pkOption
9445
9445
  } = options;
9446
9446
  const results = [];
9447
- if (!isAsyncAdapter(target)) {
9447
+ const sqliteTarget = !isAsyncAdapter(target) ? target : null;
9448
+ if (sqliteTarget) {
9448
9449
  try {
9449
- target.run("PRAGMA foreign_keys = OFF");
9450
+ sqliteTarget.exec("PRAGMA foreign_keys = OFF");
9450
9451
  } catch {}
9451
9452
  }
9452
- for (let i = 0;i < tables.length; i++) {
9453
- const table = tables[i];
9454
- const result = {
9455
- table,
9456
- rowsRead: 0,
9457
- rowsWritten: 0,
9458
- rowsSkipped: 0,
9459
- errors: []
9460
- };
9461
- try {
9462
- onProgress?.({
9453
+ try {
9454
+ for (let i = 0;i < tables.length; i++) {
9455
+ const table = tables[i];
9456
+ const result = {
9463
9457
  table,
9464
- phase: "reading",
9465
9458
  rowsRead: 0,
9466
9459
  rowsWritten: 0,
9467
- totalTables: tables.length,
9468
- currentTableIndex: i
9469
- });
9470
- const rows = await readAll(source, `SELECT * FROM "${table}"`);
9471
- result.rowsRead = rows.length;
9472
- if (rows.length === 0) {
9460
+ rowsSkipped: 0,
9461
+ errors: []
9462
+ };
9463
+ try {
9473
9464
  onProgress?.({
9474
9465
  table,
9475
- phase: "done",
9466
+ phase: "reading",
9476
9467
  rowsRead: 0,
9477
9468
  rowsWritten: 0,
9478
9469
  totalTables: tables.length,
9479
9470
  currentTableIndex: i
9480
9471
  });
9481
- results.push(result);
9482
- continue;
9483
- }
9484
- const pkColumns = await resolvePrimaryKeys(source, target, table, pkOption);
9485
- const columns = Object.keys(rows[0]);
9486
- if (pkColumns.length === 0) {
9487
- result.errors.push(`Table "${table}" has no primary key — inserting without conflict handling`);
9472
+ const rows = await readAll(source, `SELECT * FROM "${table}"`);
9473
+ result.rowsRead = rows.length;
9474
+ if (rows.length === 0) {
9475
+ onProgress?.({
9476
+ table,
9477
+ phase: "done",
9478
+ rowsRead: 0,
9479
+ rowsWritten: 0,
9480
+ totalTables: tables.length,
9481
+ currentTableIndex: i
9482
+ });
9483
+ results.push(result);
9484
+ continue;
9485
+ }
9486
+ const pkColumns = await resolvePrimaryKeys(source, target, table, pkOption);
9487
+ const sourceColumns = Object.keys(rows[0]);
9488
+ let targetColumns = null;
9489
+ if (!isAsyncAdapter(target)) {
9490
+ try {
9491
+ const colInfo = target.all(`PRAGMA table_info("${table}")`);
9492
+ targetColumns = new Set(colInfo.map((c) => c.name));
9493
+ } catch {}
9494
+ } else {
9495
+ try {
9496
+ const colInfo = await target.all(`SELECT column_name FROM information_schema.columns WHERE table_schema = 'public' AND table_name = '${table}'`);
9497
+ targetColumns = new Set(colInfo.map((c) => c.column_name));
9498
+ } catch {}
9499
+ }
9500
+ const columns = targetColumns ? sourceColumns.filter((c) => targetColumns.has(c)) : sourceColumns;
9501
+ if (pkColumns.length === 0) {
9502
+ result.errors.push(`Table "${table}" has no primary key — inserting without conflict handling`);
9503
+ onProgress?.({
9504
+ table,
9505
+ phase: "writing",
9506
+ rowsRead: result.rowsRead,
9507
+ rowsWritten: 0,
9508
+ totalTables: tables.length,
9509
+ currentTableIndex: i
9510
+ });
9511
+ for (let offset = 0;offset < rows.length; offset += batchSize) {
9512
+ const batch = rows.slice(offset, offset + batchSize);
9513
+ try {
9514
+ if (isAsyncAdapter(target)) {
9515
+ await batchInsertPg(target, table, columns, batch);
9516
+ } else {
9517
+ batchInsertSqlite(target, table, columns, batch);
9518
+ }
9519
+ result.rowsWritten += batch.length;
9520
+ } catch (err) {
9521
+ result.errors.push(`Batch at offset ${offset}: ${err?.message ?? String(err)}`);
9522
+ }
9523
+ }
9524
+ onProgress?.({
9525
+ table,
9526
+ phase: "done",
9527
+ rowsRead: result.rowsRead,
9528
+ rowsWritten: result.rowsWritten,
9529
+ totalTables: tables.length,
9530
+ currentTableIndex: i
9531
+ });
9532
+ results.push(result);
9533
+ continue;
9534
+ }
9535
+ const missingPks = pkColumns.filter((pk) => !columns.includes(pk));
9536
+ if (missingPks.length > 0) {
9537
+ result.errors.push(`Table "${table}" missing PK columns in data: ${missingPks.join(", ")} — skipping`);
9538
+ results.push(result);
9539
+ continue;
9540
+ }
9488
9541
  onProgress?.({
9489
9542
  table,
9490
9543
  phase: "writing",
@@ -9493,18 +9546,27 @@ async function syncTransfer(source, target, options, _direction) {
9493
9546
  totalTables: tables.length,
9494
9547
  currentTableIndex: i
9495
9548
  });
9549
+ const updateCols = columns.filter((c) => !pkColumns.includes(c));
9496
9550
  for (let offset = 0;offset < rows.length; offset += batchSize) {
9497
9551
  const batch = rows.slice(offset, offset + batchSize);
9498
9552
  try {
9499
9553
  if (isAsyncAdapter(target)) {
9500
- await batchInsertPg(target, table, columns, batch);
9554
+ await batchUpsertPg(target, table, columns, updateCols, pkColumns, batch);
9501
9555
  } else {
9502
- batchInsertSqlite(target, table, columns, batch);
9556
+ batchUpsertSqlite(target, table, columns, updateCols, pkColumns, batch);
9503
9557
  }
9504
9558
  result.rowsWritten += batch.length;
9505
9559
  } catch (err) {
9506
9560
  result.errors.push(`Batch at offset ${offset}: ${err?.message ?? String(err)}`);
9507
9561
  }
9562
+ onProgress?.({
9563
+ table,
9564
+ phase: "writing",
9565
+ rowsRead: result.rowsRead,
9566
+ rowsWritten: result.rowsWritten,
9567
+ totalTables: tables.length,
9568
+ currentTableIndex: i
9569
+ });
9508
9570
  }
9509
9571
  onProgress?.({
9510
9572
  table,
@@ -9514,62 +9576,27 @@ async function syncTransfer(source, target, options, _direction) {
9514
9576
  totalTables: tables.length,
9515
9577
  currentTableIndex: i
9516
9578
  });
9517
- results.push(result);
9518
- continue;
9519
- }
9520
- const missingPks = pkColumns.filter((pk) => !columns.includes(pk));
9521
- if (missingPks.length > 0) {
9522
- result.errors.push(`Table "${table}" missing PK columns in data: ${missingPks.join(", ")} — skipping`);
9523
- results.push(result);
9524
- continue;
9579
+ } catch (err) {
9580
+ result.errors.push(`Table "${table}": ${err?.message ?? String(err)}`);
9525
9581
  }
9526
- onProgress?.({
9527
- table,
9528
- phase: "writing",
9529
- rowsRead: result.rowsRead,
9530
- rowsWritten: 0,
9531
- totalTables: tables.length,
9532
- currentTableIndex: i
9533
- });
9534
- const updateCols = columns.filter((c) => !pkColumns.includes(c));
9535
- for (let offset = 0;offset < rows.length; offset += batchSize) {
9536
- const batch = rows.slice(offset, offset + batchSize);
9537
- try {
9538
- if (isAsyncAdapter(target)) {
9539
- await batchUpsertPg(target, table, columns, updateCols, pkColumns, batch);
9540
- } else {
9541
- batchUpsertSqlite(target, table, columns, updateCols, pkColumns, batch);
9582
+ results.push(result);
9583
+ }
9584
+ } finally {
9585
+ if (sqliteTarget) {
9586
+ try {
9587
+ sqliteTarget.exec("PRAGMA foreign_keys = ON");
9588
+ } catch {}
9589
+ try {
9590
+ const violations = sqliteTarget.all("PRAGMA foreign_key_check");
9591
+ if (violations.length > 0) {
9592
+ const tables2 = [...new Set(violations.map((v) => v.table))];
9593
+ const msg = `FK integrity check: ${violations.length} violation(s) in table(s): ${tables2.join(", ")}`;
9594
+ if (results.length > 0) {
9595
+ results[results.length - 1].errors.push(msg);
9542
9596
  }
9543
- result.rowsWritten += batch.length;
9544
- } catch (err) {
9545
- result.errors.push(`Batch at offset ${offset}: ${err?.message ?? String(err)}`);
9546
9597
  }
9547
- onProgress?.({
9548
- table,
9549
- phase: "writing",
9550
- rowsRead: result.rowsRead,
9551
- rowsWritten: result.rowsWritten,
9552
- totalTables: tables.length,
9553
- currentTableIndex: i
9554
- });
9555
- }
9556
- onProgress?.({
9557
- table,
9558
- phase: "done",
9559
- rowsRead: result.rowsRead,
9560
- rowsWritten: result.rowsWritten,
9561
- totalTables: tables.length,
9562
- currentTableIndex: i
9563
- });
9564
- } catch (err) {
9565
- result.errors.push(`Table "${table}": ${err?.message ?? String(err)}`);
9598
+ } catch {}
9566
9599
  }
9567
- results.push(result);
9568
- }
9569
- if (!isAsyncAdapter(target)) {
9570
- try {
9571
- target.run("PRAGMA foreign_keys = ON");
9572
- } catch {}
9573
9600
  }
9574
9601
  return results;
9575
9602
  }
package/dist/mcp/index.js CHANGED
@@ -24791,47 +24791,100 @@ async function syncTransfer(source, target, options, _direction) {
24791
24791
  primaryKey: pkOption
24792
24792
  } = options;
24793
24793
  const results = [];
24794
- if (!isAsyncAdapter(target)) {
24794
+ const sqliteTarget = !isAsyncAdapter(target) ? target : null;
24795
+ if (sqliteTarget) {
24795
24796
  try {
24796
- target.run("PRAGMA foreign_keys = OFF");
24797
+ sqliteTarget.exec("PRAGMA foreign_keys = OFF");
24797
24798
  } catch {}
24798
24799
  }
24799
- for (let i = 0;i < tables.length; i++) {
24800
- const table = tables[i];
24801
- const result = {
24802
- table,
24803
- rowsRead: 0,
24804
- rowsWritten: 0,
24805
- rowsSkipped: 0,
24806
- errors: []
24807
- };
24808
- try {
24809
- onProgress?.({
24800
+ try {
24801
+ for (let i = 0;i < tables.length; i++) {
24802
+ const table = tables[i];
24803
+ const result = {
24810
24804
  table,
24811
- phase: "reading",
24812
24805
  rowsRead: 0,
24813
24806
  rowsWritten: 0,
24814
- totalTables: tables.length,
24815
- currentTableIndex: i
24816
- });
24817
- const rows = await readAll(source, `SELECT * FROM "${table}"`);
24818
- result.rowsRead = rows.length;
24819
- if (rows.length === 0) {
24807
+ rowsSkipped: 0,
24808
+ errors: []
24809
+ };
24810
+ try {
24820
24811
  onProgress?.({
24821
24812
  table,
24822
- phase: "done",
24813
+ phase: "reading",
24823
24814
  rowsRead: 0,
24824
24815
  rowsWritten: 0,
24825
24816
  totalTables: tables.length,
24826
24817
  currentTableIndex: i
24827
24818
  });
24828
- results.push(result);
24829
- continue;
24830
- }
24831
- const pkColumns = await resolvePrimaryKeys(source, target, table, pkOption);
24832
- const columns = Object.keys(rows[0]);
24833
- if (pkColumns.length === 0) {
24834
- result.errors.push(`Table "${table}" has no primary key — inserting without conflict handling`);
24819
+ const rows = await readAll(source, `SELECT * FROM "${table}"`);
24820
+ result.rowsRead = rows.length;
24821
+ if (rows.length === 0) {
24822
+ onProgress?.({
24823
+ table,
24824
+ phase: "done",
24825
+ rowsRead: 0,
24826
+ rowsWritten: 0,
24827
+ totalTables: tables.length,
24828
+ currentTableIndex: i
24829
+ });
24830
+ results.push(result);
24831
+ continue;
24832
+ }
24833
+ const pkColumns = await resolvePrimaryKeys(source, target, table, pkOption);
24834
+ const sourceColumns = Object.keys(rows[0]);
24835
+ let targetColumns = null;
24836
+ if (!isAsyncAdapter(target)) {
24837
+ try {
24838
+ const colInfo = target.all(`PRAGMA table_info("${table}")`);
24839
+ targetColumns = new Set(colInfo.map((c) => c.name));
24840
+ } catch {}
24841
+ } else {
24842
+ try {
24843
+ const colInfo = await target.all(`SELECT column_name FROM information_schema.columns WHERE table_schema = 'public' AND table_name = '${table}'`);
24844
+ targetColumns = new Set(colInfo.map((c) => c.column_name));
24845
+ } catch {}
24846
+ }
24847
+ const columns = targetColumns ? sourceColumns.filter((c) => targetColumns.has(c)) : sourceColumns;
24848
+ if (pkColumns.length === 0) {
24849
+ result.errors.push(`Table "${table}" has no primary key — inserting without conflict handling`);
24850
+ onProgress?.({
24851
+ table,
24852
+ phase: "writing",
24853
+ rowsRead: result.rowsRead,
24854
+ rowsWritten: 0,
24855
+ totalTables: tables.length,
24856
+ currentTableIndex: i
24857
+ });
24858
+ for (let offset = 0;offset < rows.length; offset += batchSize) {
24859
+ const batch = rows.slice(offset, offset + batchSize);
24860
+ try {
24861
+ if (isAsyncAdapter(target)) {
24862
+ await batchInsertPg(target, table, columns, batch);
24863
+ } else {
24864
+ batchInsertSqlite(target, table, columns, batch);
24865
+ }
24866
+ result.rowsWritten += batch.length;
24867
+ } catch (err) {
24868
+ result.errors.push(`Batch at offset ${offset}: ${err?.message ?? String(err)}`);
24869
+ }
24870
+ }
24871
+ onProgress?.({
24872
+ table,
24873
+ phase: "done",
24874
+ rowsRead: result.rowsRead,
24875
+ rowsWritten: result.rowsWritten,
24876
+ totalTables: tables.length,
24877
+ currentTableIndex: i
24878
+ });
24879
+ results.push(result);
24880
+ continue;
24881
+ }
24882
+ const missingPks = pkColumns.filter((pk) => !columns.includes(pk));
24883
+ if (missingPks.length > 0) {
24884
+ result.errors.push(`Table "${table}" missing PK columns in data: ${missingPks.join(", ")} — skipping`);
24885
+ results.push(result);
24886
+ continue;
24887
+ }
24835
24888
  onProgress?.({
24836
24889
  table,
24837
24890
  phase: "writing",
@@ -24840,18 +24893,27 @@ async function syncTransfer(source, target, options, _direction) {
24840
24893
  totalTables: tables.length,
24841
24894
  currentTableIndex: i
24842
24895
  });
24896
+ const updateCols = columns.filter((c) => !pkColumns.includes(c));
24843
24897
  for (let offset = 0;offset < rows.length; offset += batchSize) {
24844
24898
  const batch = rows.slice(offset, offset + batchSize);
24845
24899
  try {
24846
24900
  if (isAsyncAdapter(target)) {
24847
- await batchInsertPg(target, table, columns, batch);
24901
+ await batchUpsertPg(target, table, columns, updateCols, pkColumns, batch);
24848
24902
  } else {
24849
- batchInsertSqlite(target, table, columns, batch);
24903
+ batchUpsertSqlite(target, table, columns, updateCols, pkColumns, batch);
24850
24904
  }
24851
24905
  result.rowsWritten += batch.length;
24852
24906
  } catch (err) {
24853
24907
  result.errors.push(`Batch at offset ${offset}: ${err?.message ?? String(err)}`);
24854
24908
  }
24909
+ onProgress?.({
24910
+ table,
24911
+ phase: "writing",
24912
+ rowsRead: result.rowsRead,
24913
+ rowsWritten: result.rowsWritten,
24914
+ totalTables: tables.length,
24915
+ currentTableIndex: i
24916
+ });
24855
24917
  }
24856
24918
  onProgress?.({
24857
24919
  table,
@@ -24861,62 +24923,27 @@ async function syncTransfer(source, target, options, _direction) {
24861
24923
  totalTables: tables.length,
24862
24924
  currentTableIndex: i
24863
24925
  });
24864
- results.push(result);
24865
- continue;
24866
- }
24867
- const missingPks = pkColumns.filter((pk) => !columns.includes(pk));
24868
- if (missingPks.length > 0) {
24869
- result.errors.push(`Table "${table}" missing PK columns in data: ${missingPks.join(", ")} — skipping`);
24870
- results.push(result);
24871
- continue;
24926
+ } catch (err) {
24927
+ result.errors.push(`Table "${table}": ${err?.message ?? String(err)}`);
24872
24928
  }
24873
- onProgress?.({
24874
- table,
24875
- phase: "writing",
24876
- rowsRead: result.rowsRead,
24877
- rowsWritten: 0,
24878
- totalTables: tables.length,
24879
- currentTableIndex: i
24880
- });
24881
- const updateCols = columns.filter((c) => !pkColumns.includes(c));
24882
- for (let offset = 0;offset < rows.length; offset += batchSize) {
24883
- const batch = rows.slice(offset, offset + batchSize);
24884
- try {
24885
- if (isAsyncAdapter(target)) {
24886
- await batchUpsertPg(target, table, columns, updateCols, pkColumns, batch);
24887
- } else {
24888
- batchUpsertSqlite(target, table, columns, updateCols, pkColumns, batch);
24929
+ results.push(result);
24930
+ }
24931
+ } finally {
24932
+ if (sqliteTarget) {
24933
+ try {
24934
+ sqliteTarget.exec("PRAGMA foreign_keys = ON");
24935
+ } catch {}
24936
+ try {
24937
+ const violations = sqliteTarget.all("PRAGMA foreign_key_check");
24938
+ if (violations.length > 0) {
24939
+ const tables2 = [...new Set(violations.map((v) => v.table))];
24940
+ const msg = `FK integrity check: ${violations.length} violation(s) in table(s): ${tables2.join(", ")}`;
24941
+ if (results.length > 0) {
24942
+ results[results.length - 1].errors.push(msg);
24889
24943
  }
24890
- result.rowsWritten += batch.length;
24891
- } catch (err) {
24892
- result.errors.push(`Batch at offset ${offset}: ${err?.message ?? String(err)}`);
24893
24944
  }
24894
- onProgress?.({
24895
- table,
24896
- phase: "writing",
24897
- rowsRead: result.rowsRead,
24898
- rowsWritten: result.rowsWritten,
24899
- totalTables: tables.length,
24900
- currentTableIndex: i
24901
- });
24902
- }
24903
- onProgress?.({
24904
- table,
24905
- phase: "done",
24906
- rowsRead: result.rowsRead,
24907
- rowsWritten: result.rowsWritten,
24908
- totalTables: tables.length,
24909
- currentTableIndex: i
24910
- });
24911
- } catch (err) {
24912
- result.errors.push(`Table "${table}": ${err?.message ?? String(err)}`);
24945
+ } catch {}
24913
24946
  }
24914
- results.push(result);
24915
- }
24916
- if (!isAsyncAdapter(target)) {
24917
- try {
24918
- target.run("PRAGMA foreign_keys = ON");
24919
- } catch {}
24920
24947
  }
24921
24948
  return results;
24922
24949
  }
@@ -1 +1 @@
1
- {"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../src/sync.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAMnD,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,SAAS,GAAG,SAAS,GAAG,MAAM,CAAC;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,MAAM,oBAAoB,GAAG,CAAC,QAAQ,EAAE,YAAY,KAAK,IAAI,CAAC;AAEpE,MAAM,WAAW,WAAW;IAC1B,sBAAsB;IACtB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,kCAAkC;IAClC,UAAU,CAAC,EAAE,oBAAoB,CAAC;IAClC,qDAAqD;IACrD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uEAAuE;IACvE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAMD;;;GAGG;AACH,wBAAsB,QAAQ,CAC5B,KAAK,EAAE,SAAS,EAChB,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,UAAU,EAAE,CAAC,CAGvB;AAMD;;;GAGG;AACH,wBAAsB,QAAQ,CAC5B,MAAM,EAAE,cAAc,EACtB,KAAK,EAAE,SAAS,EAChB,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,UAAU,EAAE,CAAC,CAGvB;AAkkBD;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,SAAS,GAAG,MAAM,EAAE,CAKxD;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,EAAE,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAKxE"}
1
+ {"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../src/sync.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAMnD,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,SAAS,GAAG,SAAS,GAAG,MAAM,CAAC;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,MAAM,oBAAoB,GAAG,CAAC,QAAQ,EAAE,YAAY,KAAK,IAAI,CAAC;AAEpE,MAAM,WAAW,WAAW;IAC1B,sBAAsB;IACtB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,kCAAkC;IAClC,UAAU,CAAC,EAAE,oBAAoB,CAAC;IAClC,qDAAqD;IACrD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uEAAuE;IACvE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAMD;;;GAGG;AACH,wBAAsB,QAAQ,CAC5B,KAAK,EAAE,SAAS,EAChB,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,UAAU,EAAE,CAAC,CAGvB;AAMD;;;GAGG;AACH,wBAAsB,QAAQ,CAC5B,MAAM,EAAE,cAAc,EACtB,KAAK,EAAE,SAAS,EAChB,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,UAAU,EAAE,CAAC,CAGvB;AAymBD;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,SAAS,GAAG,MAAM,EAAE,CAKxD;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,EAAE,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAKxE"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/cloud",
3
- "version": "0.1.10",
3
+ "version": "0.1.12",
4
4
  "description": "Shared cloud infrastructure — database adapter (SQLite + PostgreSQL), sync engine, feedback system, unified dotfile config",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",