@hasna/cloud 0.1.11 → 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,60 +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 sourceColumns = Object.keys(rows[0]);
11494
- let targetColumns = null;
11495
- if (!isAsyncAdapter(target)) {
11496
- try {
11497
- const colInfo = target.all(`PRAGMA table_info("${table}")`);
11498
- targetColumns = new Set(colInfo.map((c) => c.name));
11499
- } catch {}
11500
- } else {
11501
- try {
11502
- const colInfo = await target.all(`SELECT column_name FROM information_schema.columns WHERE table_schema = 'public' AND table_name = '${table}'`);
11503
- targetColumns = new Set(colInfo.map((c) => c.column_name));
11504
- } catch {}
11505
- }
11506
- const columns = targetColumns ? sourceColumns.filter((c) => targetColumns.has(c)) : sourceColumns;
11507
- if (pkColumns.length === 0) {
11508
- 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
+ }
11509
11549
  onProgress?.({
11510
11550
  table,
11511
11551
  phase: "writing",
@@ -11514,18 +11554,27 @@ async function syncTransfer(source, target, options, _direction) {
11514
11554
  totalTables: tables.length,
11515
11555
  currentTableIndex: i
11516
11556
  });
11557
+ const updateCols = columns.filter((c) => !pkColumns.includes(c));
11517
11558
  for (let offset = 0;offset < rows.length; offset += batchSize) {
11518
11559
  const batch = rows.slice(offset, offset + batchSize);
11519
11560
  try {
11520
11561
  if (isAsyncAdapter(target)) {
11521
- await batchInsertPg(target, table, columns, batch);
11562
+ await batchUpsertPg(target, table, columns, updateCols, pkColumns, batch);
11522
11563
  } else {
11523
- batchInsertSqlite(target, table, columns, batch);
11564
+ batchUpsertSqlite(target, table, columns, updateCols, pkColumns, batch);
11524
11565
  }
11525
11566
  result.rowsWritten += batch.length;
11526
11567
  } catch (err) {
11527
11568
  result.errors.push(`Batch at offset ${offset}: ${err?.message ?? String(err)}`);
11528
11569
  }
11570
+ onProgress?.({
11571
+ table,
11572
+ phase: "writing",
11573
+ rowsRead: result.rowsRead,
11574
+ rowsWritten: result.rowsWritten,
11575
+ totalTables: tables.length,
11576
+ currentTableIndex: i
11577
+ });
11529
11578
  }
11530
11579
  onProgress?.({
11531
11580
  table,
@@ -11535,62 +11584,27 @@ async function syncTransfer(source, target, options, _direction) {
11535
11584
  totalTables: tables.length,
11536
11585
  currentTableIndex: i
11537
11586
  });
11538
- results.push(result);
11539
- continue;
11540
- }
11541
- const missingPks = pkColumns.filter((pk) => !columns.includes(pk));
11542
- if (missingPks.length > 0) {
11543
- result.errors.push(`Table "${table}" missing PK columns in data: ${missingPks.join(", ")} — skipping`);
11544
- results.push(result);
11545
- continue;
11587
+ } catch (err) {
11588
+ result.errors.push(`Table "${table}": ${err?.message ?? String(err)}`);
11546
11589
  }
11547
- onProgress?.({
11548
- table,
11549
- phase: "writing",
11550
- rowsRead: result.rowsRead,
11551
- rowsWritten: 0,
11552
- totalTables: tables.length,
11553
- currentTableIndex: i
11554
- });
11555
- const updateCols = columns.filter((c) => !pkColumns.includes(c));
11556
- for (let offset = 0;offset < rows.length; offset += batchSize) {
11557
- const batch = rows.slice(offset, offset + batchSize);
11558
- try {
11559
- if (isAsyncAdapter(target)) {
11560
- await batchUpsertPg(target, table, columns, updateCols, pkColumns, batch);
11561
- } else {
11562
- 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);
11563
11604
  }
11564
- result.rowsWritten += batch.length;
11565
- } catch (err) {
11566
- result.errors.push(`Batch at offset ${offset}: ${err?.message ?? String(err)}`);
11567
11605
  }
11568
- onProgress?.({
11569
- table,
11570
- phase: "writing",
11571
- rowsRead: result.rowsRead,
11572
- rowsWritten: result.rowsWritten,
11573
- totalTables: tables.length,
11574
- currentTableIndex: i
11575
- });
11576
- }
11577
- onProgress?.({
11578
- table,
11579
- phase: "done",
11580
- rowsRead: result.rowsRead,
11581
- rowsWritten: result.rowsWritten,
11582
- totalTables: tables.length,
11583
- currentTableIndex: i
11584
- });
11585
- } catch (err) {
11586
- result.errors.push(`Table "${table}": ${err?.message ?? String(err)}`);
11606
+ } catch {}
11587
11607
  }
11588
- results.push(result);
11589
- }
11590
- if (!isAsyncAdapter(target)) {
11591
- try {
11592
- target.run("PRAGMA foreign_keys = ON");
11593
- } catch {}
11594
11608
  }
11595
11609
  return results;
11596
11610
  }
package/dist/index.js CHANGED
@@ -9444,60 +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 sourceColumns = Object.keys(rows[0]);
9486
- let targetColumns = null;
9487
- if (!isAsyncAdapter(target)) {
9488
- try {
9489
- const colInfo = target.all(`PRAGMA table_info("${table}")`);
9490
- targetColumns = new Set(colInfo.map((c) => c.name));
9491
- } catch {}
9492
- } else {
9493
- try {
9494
- const colInfo = await target.all(`SELECT column_name FROM information_schema.columns WHERE table_schema = 'public' AND table_name = '${table}'`);
9495
- targetColumns = new Set(colInfo.map((c) => c.column_name));
9496
- } catch {}
9497
- }
9498
- const columns = targetColumns ? sourceColumns.filter((c) => targetColumns.has(c)) : sourceColumns;
9499
- if (pkColumns.length === 0) {
9500
- 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
+ }
9501
9541
  onProgress?.({
9502
9542
  table,
9503
9543
  phase: "writing",
@@ -9506,18 +9546,27 @@ async function syncTransfer(source, target, options, _direction) {
9506
9546
  totalTables: tables.length,
9507
9547
  currentTableIndex: i
9508
9548
  });
9549
+ const updateCols = columns.filter((c) => !pkColumns.includes(c));
9509
9550
  for (let offset = 0;offset < rows.length; offset += batchSize) {
9510
9551
  const batch = rows.slice(offset, offset + batchSize);
9511
9552
  try {
9512
9553
  if (isAsyncAdapter(target)) {
9513
- await batchInsertPg(target, table, columns, batch);
9554
+ await batchUpsertPg(target, table, columns, updateCols, pkColumns, batch);
9514
9555
  } else {
9515
- batchInsertSqlite(target, table, columns, batch);
9556
+ batchUpsertSqlite(target, table, columns, updateCols, pkColumns, batch);
9516
9557
  }
9517
9558
  result.rowsWritten += batch.length;
9518
9559
  } catch (err) {
9519
9560
  result.errors.push(`Batch at offset ${offset}: ${err?.message ?? String(err)}`);
9520
9561
  }
9562
+ onProgress?.({
9563
+ table,
9564
+ phase: "writing",
9565
+ rowsRead: result.rowsRead,
9566
+ rowsWritten: result.rowsWritten,
9567
+ totalTables: tables.length,
9568
+ currentTableIndex: i
9569
+ });
9521
9570
  }
9522
9571
  onProgress?.({
9523
9572
  table,
@@ -9527,62 +9576,27 @@ async function syncTransfer(source, target, options, _direction) {
9527
9576
  totalTables: tables.length,
9528
9577
  currentTableIndex: i
9529
9578
  });
9530
- results.push(result);
9531
- continue;
9532
- }
9533
- const missingPks = pkColumns.filter((pk) => !columns.includes(pk));
9534
- if (missingPks.length > 0) {
9535
- result.errors.push(`Table "${table}" missing PK columns in data: ${missingPks.join(", ")} — skipping`);
9536
- results.push(result);
9537
- continue;
9579
+ } catch (err) {
9580
+ result.errors.push(`Table "${table}": ${err?.message ?? String(err)}`);
9538
9581
  }
9539
- onProgress?.({
9540
- table,
9541
- phase: "writing",
9542
- rowsRead: result.rowsRead,
9543
- rowsWritten: 0,
9544
- totalTables: tables.length,
9545
- currentTableIndex: i
9546
- });
9547
- const updateCols = columns.filter((c) => !pkColumns.includes(c));
9548
- for (let offset = 0;offset < rows.length; offset += batchSize) {
9549
- const batch = rows.slice(offset, offset + batchSize);
9550
- try {
9551
- if (isAsyncAdapter(target)) {
9552
- await batchUpsertPg(target, table, columns, updateCols, pkColumns, batch);
9553
- } else {
9554
- 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);
9555
9596
  }
9556
- result.rowsWritten += batch.length;
9557
- } catch (err) {
9558
- result.errors.push(`Batch at offset ${offset}: ${err?.message ?? String(err)}`);
9559
9597
  }
9560
- onProgress?.({
9561
- table,
9562
- phase: "writing",
9563
- rowsRead: result.rowsRead,
9564
- rowsWritten: result.rowsWritten,
9565
- totalTables: tables.length,
9566
- currentTableIndex: i
9567
- });
9568
- }
9569
- onProgress?.({
9570
- table,
9571
- phase: "done",
9572
- rowsRead: result.rowsRead,
9573
- rowsWritten: result.rowsWritten,
9574
- totalTables: tables.length,
9575
- currentTableIndex: i
9576
- });
9577
- } catch (err) {
9578
- result.errors.push(`Table "${table}": ${err?.message ?? String(err)}`);
9598
+ } catch {}
9579
9599
  }
9580
- results.push(result);
9581
- }
9582
- if (!isAsyncAdapter(target)) {
9583
- try {
9584
- target.run("PRAGMA foreign_keys = ON");
9585
- } catch {}
9586
9600
  }
9587
9601
  return results;
9588
9602
  }
package/dist/mcp/index.js CHANGED
@@ -24791,60 +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 sourceColumns = Object.keys(rows[0]);
24833
- let targetColumns = null;
24834
- if (!isAsyncAdapter(target)) {
24835
- try {
24836
- const colInfo = target.all(`PRAGMA table_info("${table}")`);
24837
- targetColumns = new Set(colInfo.map((c) => c.name));
24838
- } catch {}
24839
- } else {
24840
- try {
24841
- const colInfo = await target.all(`SELECT column_name FROM information_schema.columns WHERE table_schema = 'public' AND table_name = '${table}'`);
24842
- targetColumns = new Set(colInfo.map((c) => c.column_name));
24843
- } catch {}
24844
- }
24845
- const columns = targetColumns ? sourceColumns.filter((c) => targetColumns.has(c)) : sourceColumns;
24846
- if (pkColumns.length === 0) {
24847
- 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
+ }
24848
24888
  onProgress?.({
24849
24889
  table,
24850
24890
  phase: "writing",
@@ -24853,18 +24893,27 @@ async function syncTransfer(source, target, options, _direction) {
24853
24893
  totalTables: tables.length,
24854
24894
  currentTableIndex: i
24855
24895
  });
24896
+ const updateCols = columns.filter((c) => !pkColumns.includes(c));
24856
24897
  for (let offset = 0;offset < rows.length; offset += batchSize) {
24857
24898
  const batch = rows.slice(offset, offset + batchSize);
24858
24899
  try {
24859
24900
  if (isAsyncAdapter(target)) {
24860
- await batchInsertPg(target, table, columns, batch);
24901
+ await batchUpsertPg(target, table, columns, updateCols, pkColumns, batch);
24861
24902
  } else {
24862
- batchInsertSqlite(target, table, columns, batch);
24903
+ batchUpsertSqlite(target, table, columns, updateCols, pkColumns, batch);
24863
24904
  }
24864
24905
  result.rowsWritten += batch.length;
24865
24906
  } catch (err) {
24866
24907
  result.errors.push(`Batch at offset ${offset}: ${err?.message ?? String(err)}`);
24867
24908
  }
24909
+ onProgress?.({
24910
+ table,
24911
+ phase: "writing",
24912
+ rowsRead: result.rowsRead,
24913
+ rowsWritten: result.rowsWritten,
24914
+ totalTables: tables.length,
24915
+ currentTableIndex: i
24916
+ });
24868
24917
  }
24869
24918
  onProgress?.({
24870
24919
  table,
@@ -24874,62 +24923,27 @@ async function syncTransfer(source, target, options, _direction) {
24874
24923
  totalTables: tables.length,
24875
24924
  currentTableIndex: i
24876
24925
  });
24877
- results.push(result);
24878
- continue;
24879
- }
24880
- const missingPks = pkColumns.filter((pk) => !columns.includes(pk));
24881
- if (missingPks.length > 0) {
24882
- result.errors.push(`Table "${table}" missing PK columns in data: ${missingPks.join(", ")} — skipping`);
24883
- results.push(result);
24884
- continue;
24926
+ } catch (err) {
24927
+ result.errors.push(`Table "${table}": ${err?.message ?? String(err)}`);
24885
24928
  }
24886
- onProgress?.({
24887
- table,
24888
- phase: "writing",
24889
- rowsRead: result.rowsRead,
24890
- rowsWritten: 0,
24891
- totalTables: tables.length,
24892
- currentTableIndex: i
24893
- });
24894
- const updateCols = columns.filter((c) => !pkColumns.includes(c));
24895
- for (let offset = 0;offset < rows.length; offset += batchSize) {
24896
- const batch = rows.slice(offset, offset + batchSize);
24897
- try {
24898
- if (isAsyncAdapter(target)) {
24899
- await batchUpsertPg(target, table, columns, updateCols, pkColumns, batch);
24900
- } else {
24901
- 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);
24902
24943
  }
24903
- result.rowsWritten += batch.length;
24904
- } catch (err) {
24905
- result.errors.push(`Batch at offset ${offset}: ${err?.message ?? String(err)}`);
24906
24944
  }
24907
- onProgress?.({
24908
- table,
24909
- phase: "writing",
24910
- rowsRead: result.rowsRead,
24911
- rowsWritten: result.rowsWritten,
24912
- totalTables: tables.length,
24913
- currentTableIndex: i
24914
- });
24915
- }
24916
- onProgress?.({
24917
- table,
24918
- phase: "done",
24919
- rowsRead: result.rowsRead,
24920
- rowsWritten: result.rowsWritten,
24921
- totalTables: tables.length,
24922
- currentTableIndex: i
24923
- });
24924
- } catch (err) {
24925
- result.errors.push(`Table "${table}": ${err?.message ?? String(err)}`);
24945
+ } catch {}
24926
24946
  }
24927
- results.push(result);
24928
- }
24929
- if (!isAsyncAdapter(target)) {
24930
- try {
24931
- target.run("PRAGMA foreign_keys = ON");
24932
- } catch {}
24933
24947
  }
24934
24948
  return results;
24935
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;AAslBD;;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.11",
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",