@hasna/cloud 0.1.11 → 0.1.13
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 +121 -95
- package/dist/index.js +121 -95
- package/dist/mcp/index.js +121 -95
- package/dist/sync.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -11443,6 +11443,30 @@ async function resolvePrimaryKeys(source, target, table, pkOption) {
|
|
|
11443
11443
|
}
|
|
11444
11444
|
return pks;
|
|
11445
11445
|
}
|
|
11446
|
+
async function filterColumnsForTarget(target, table, sourceColumns) {
|
|
11447
|
+
try {
|
|
11448
|
+
if (!isAsyncAdapter(target)) {
|
|
11449
|
+
const colInfo = target.all(`PRAGMA table_info("${table}")`);
|
|
11450
|
+
if (Array.isArray(colInfo) && colInfo.length > 0) {
|
|
11451
|
+
const targetCols = new Set(colInfo.map((c) => c.name));
|
|
11452
|
+
const filtered = sourceColumns.filter((c) => targetCols.has(c));
|
|
11453
|
+
if (filtered.length < sourceColumns.length) {
|
|
11454
|
+
const dropped = sourceColumns.filter((c) => !targetCols.has(c));
|
|
11455
|
+
process.stderr.write(` [sync] ${table}: dropping ${dropped.length} columns not in target: ${dropped.join(", ")}
|
|
11456
|
+
`);
|
|
11457
|
+
}
|
|
11458
|
+
return filtered;
|
|
11459
|
+
}
|
|
11460
|
+
} else {
|
|
11461
|
+
const colInfo = await target.all(`SELECT column_name FROM information_schema.columns WHERE table_schema = 'public' AND table_name = '${table}'`);
|
|
11462
|
+
if (colInfo.length > 0) {
|
|
11463
|
+
const targetCols = new Set(colInfo.map((c) => c.column_name));
|
|
11464
|
+
return sourceColumns.filter((c) => targetCols.has(c));
|
|
11465
|
+
}
|
|
11466
|
+
}
|
|
11467
|
+
} catch {}
|
|
11468
|
+
return sourceColumns;
|
|
11469
|
+
}
|
|
11446
11470
|
async function syncTransfer(source, target, options, _direction) {
|
|
11447
11471
|
const {
|
|
11448
11472
|
tables,
|
|
@@ -11452,60 +11476,88 @@ async function syncTransfer(source, target, options, _direction) {
|
|
|
11452
11476
|
primaryKey: pkOption
|
|
11453
11477
|
} = options;
|
|
11454
11478
|
const results = [];
|
|
11455
|
-
|
|
11479
|
+
const sqliteTarget = !isAsyncAdapter(target) ? target : null;
|
|
11480
|
+
if (sqliteTarget) {
|
|
11456
11481
|
try {
|
|
11457
|
-
|
|
11482
|
+
sqliteTarget.exec("PRAGMA foreign_keys = OFF");
|
|
11458
11483
|
} catch {}
|
|
11459
11484
|
}
|
|
11460
|
-
|
|
11461
|
-
|
|
11462
|
-
|
|
11463
|
-
|
|
11464
|
-
rowsRead: 0,
|
|
11465
|
-
rowsWritten: 0,
|
|
11466
|
-
rowsSkipped: 0,
|
|
11467
|
-
errors: []
|
|
11468
|
-
};
|
|
11469
|
-
try {
|
|
11470
|
-
onProgress?.({
|
|
11485
|
+
try {
|
|
11486
|
+
for (let i = 0;i < tables.length; i++) {
|
|
11487
|
+
const table = tables[i];
|
|
11488
|
+
const result = {
|
|
11471
11489
|
table,
|
|
11472
|
-
phase: "reading",
|
|
11473
11490
|
rowsRead: 0,
|
|
11474
11491
|
rowsWritten: 0,
|
|
11475
|
-
|
|
11476
|
-
|
|
11477
|
-
}
|
|
11478
|
-
|
|
11479
|
-
result.rowsRead = rows.length;
|
|
11480
|
-
if (rows.length === 0) {
|
|
11492
|
+
rowsSkipped: 0,
|
|
11493
|
+
errors: []
|
|
11494
|
+
};
|
|
11495
|
+
try {
|
|
11481
11496
|
onProgress?.({
|
|
11482
11497
|
table,
|
|
11483
|
-
phase: "
|
|
11498
|
+
phase: "reading",
|
|
11484
11499
|
rowsRead: 0,
|
|
11485
11500
|
rowsWritten: 0,
|
|
11486
11501
|
totalTables: tables.length,
|
|
11487
11502
|
currentTableIndex: i
|
|
11488
11503
|
});
|
|
11489
|
-
|
|
11490
|
-
|
|
11491
|
-
|
|
11492
|
-
|
|
11493
|
-
|
|
11494
|
-
|
|
11495
|
-
|
|
11496
|
-
|
|
11497
|
-
|
|
11498
|
-
|
|
11499
|
-
|
|
11500
|
-
|
|
11501
|
-
|
|
11502
|
-
|
|
11503
|
-
|
|
11504
|
-
|
|
11505
|
-
|
|
11506
|
-
|
|
11507
|
-
|
|
11508
|
-
|
|
11504
|
+
const rows = await readAll(source, `SELECT * FROM "${table}"`);
|
|
11505
|
+
result.rowsRead = rows.length;
|
|
11506
|
+
if (rows.length === 0) {
|
|
11507
|
+
onProgress?.({
|
|
11508
|
+
table,
|
|
11509
|
+
phase: "done",
|
|
11510
|
+
rowsRead: 0,
|
|
11511
|
+
rowsWritten: 0,
|
|
11512
|
+
totalTables: tables.length,
|
|
11513
|
+
currentTableIndex: i
|
|
11514
|
+
});
|
|
11515
|
+
results.push(result);
|
|
11516
|
+
continue;
|
|
11517
|
+
}
|
|
11518
|
+
const pkColumns = await resolvePrimaryKeys(source, target, table, pkOption);
|
|
11519
|
+
const sourceColumns = Object.keys(rows[0]);
|
|
11520
|
+
const columns = await filterColumnsForTarget(target, table, sourceColumns);
|
|
11521
|
+
if (pkColumns.length === 0) {
|
|
11522
|
+
result.errors.push(`Table "${table}" has no primary key — inserting without conflict handling`);
|
|
11523
|
+
onProgress?.({
|
|
11524
|
+
table,
|
|
11525
|
+
phase: "writing",
|
|
11526
|
+
rowsRead: result.rowsRead,
|
|
11527
|
+
rowsWritten: 0,
|
|
11528
|
+
totalTables: tables.length,
|
|
11529
|
+
currentTableIndex: i
|
|
11530
|
+
});
|
|
11531
|
+
for (let offset = 0;offset < rows.length; offset += batchSize) {
|
|
11532
|
+
const batch = rows.slice(offset, offset + batchSize);
|
|
11533
|
+
try {
|
|
11534
|
+
if (isAsyncAdapter(target)) {
|
|
11535
|
+
await batchInsertPg(target, table, columns, batch);
|
|
11536
|
+
} else {
|
|
11537
|
+
batchInsertSqlite(target, table, columns, batch);
|
|
11538
|
+
}
|
|
11539
|
+
result.rowsWritten += batch.length;
|
|
11540
|
+
} catch (err) {
|
|
11541
|
+
result.errors.push(`Batch at offset ${offset}: ${err?.message ?? String(err)}`);
|
|
11542
|
+
}
|
|
11543
|
+
}
|
|
11544
|
+
onProgress?.({
|
|
11545
|
+
table,
|
|
11546
|
+
phase: "done",
|
|
11547
|
+
rowsRead: result.rowsRead,
|
|
11548
|
+
rowsWritten: result.rowsWritten,
|
|
11549
|
+
totalTables: tables.length,
|
|
11550
|
+
currentTableIndex: i
|
|
11551
|
+
});
|
|
11552
|
+
results.push(result);
|
|
11553
|
+
continue;
|
|
11554
|
+
}
|
|
11555
|
+
const missingPks = pkColumns.filter((pk) => !columns.includes(pk));
|
|
11556
|
+
if (missingPks.length > 0) {
|
|
11557
|
+
result.errors.push(`Table "${table}" missing PK columns in data: ${missingPks.join(", ")} — skipping`);
|
|
11558
|
+
results.push(result);
|
|
11559
|
+
continue;
|
|
11560
|
+
}
|
|
11509
11561
|
onProgress?.({
|
|
11510
11562
|
table,
|
|
11511
11563
|
phase: "writing",
|
|
@@ -11514,18 +11566,27 @@ async function syncTransfer(source, target, options, _direction) {
|
|
|
11514
11566
|
totalTables: tables.length,
|
|
11515
11567
|
currentTableIndex: i
|
|
11516
11568
|
});
|
|
11569
|
+
const updateCols = columns.filter((c) => !pkColumns.includes(c));
|
|
11517
11570
|
for (let offset = 0;offset < rows.length; offset += batchSize) {
|
|
11518
11571
|
const batch = rows.slice(offset, offset + batchSize);
|
|
11519
11572
|
try {
|
|
11520
11573
|
if (isAsyncAdapter(target)) {
|
|
11521
|
-
await
|
|
11574
|
+
await batchUpsertPg(target, table, columns, updateCols, pkColumns, batch);
|
|
11522
11575
|
} else {
|
|
11523
|
-
|
|
11576
|
+
batchUpsertSqlite(target, table, columns, updateCols, pkColumns, batch);
|
|
11524
11577
|
}
|
|
11525
11578
|
result.rowsWritten += batch.length;
|
|
11526
11579
|
} catch (err) {
|
|
11527
11580
|
result.errors.push(`Batch at offset ${offset}: ${err?.message ?? String(err)}`);
|
|
11528
11581
|
}
|
|
11582
|
+
onProgress?.({
|
|
11583
|
+
table,
|
|
11584
|
+
phase: "writing",
|
|
11585
|
+
rowsRead: result.rowsRead,
|
|
11586
|
+
rowsWritten: result.rowsWritten,
|
|
11587
|
+
totalTables: tables.length,
|
|
11588
|
+
currentTableIndex: i
|
|
11589
|
+
});
|
|
11529
11590
|
}
|
|
11530
11591
|
onProgress?.({
|
|
11531
11592
|
table,
|
|
@@ -11535,62 +11596,27 @@ async function syncTransfer(source, target, options, _direction) {
|
|
|
11535
11596
|
totalTables: tables.length,
|
|
11536
11597
|
currentTableIndex: i
|
|
11537
11598
|
});
|
|
11538
|
-
|
|
11539
|
-
|
|
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;
|
|
11599
|
+
} catch (err) {
|
|
11600
|
+
result.errors.push(`Table "${table}": ${err?.message ?? String(err)}`);
|
|
11546
11601
|
}
|
|
11547
|
-
|
|
11548
|
-
|
|
11549
|
-
|
|
11550
|
-
|
|
11551
|
-
|
|
11552
|
-
|
|
11553
|
-
|
|
11554
|
-
|
|
11555
|
-
|
|
11556
|
-
|
|
11557
|
-
|
|
11558
|
-
|
|
11559
|
-
if (
|
|
11560
|
-
|
|
11561
|
-
} else {
|
|
11562
|
-
batchUpsertSqlite(target, table, columns, updateCols, pkColumns, batch);
|
|
11602
|
+
results.push(result);
|
|
11603
|
+
}
|
|
11604
|
+
} finally {
|
|
11605
|
+
if (sqliteTarget) {
|
|
11606
|
+
try {
|
|
11607
|
+
sqliteTarget.exec("PRAGMA foreign_keys = ON");
|
|
11608
|
+
} catch {}
|
|
11609
|
+
try {
|
|
11610
|
+
const violations = sqliteTarget.all("PRAGMA foreign_key_check");
|
|
11611
|
+
if (violations.length > 0) {
|
|
11612
|
+
const tables2 = [...new Set(violations.map((v) => v.table))];
|
|
11613
|
+
const msg = `FK integrity check: ${violations.length} violation(s) in table(s): ${tables2.join(", ")}`;
|
|
11614
|
+
if (results.length > 0) {
|
|
11615
|
+
results[results.length - 1].errors.push(msg);
|
|
11563
11616
|
}
|
|
11564
|
-
result.rowsWritten += batch.length;
|
|
11565
|
-
} catch (err) {
|
|
11566
|
-
result.errors.push(`Batch at offset ${offset}: ${err?.message ?? String(err)}`);
|
|
11567
11617
|
}
|
|
11568
|
-
|
|
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)}`);
|
|
11618
|
+
} catch {}
|
|
11587
11619
|
}
|
|
11588
|
-
results.push(result);
|
|
11589
|
-
}
|
|
11590
|
-
if (!isAsyncAdapter(target)) {
|
|
11591
|
-
try {
|
|
11592
|
-
target.run("PRAGMA foreign_keys = ON");
|
|
11593
|
-
} catch {}
|
|
11594
11620
|
}
|
|
11595
11621
|
return results;
|
|
11596
11622
|
}
|
package/dist/index.js
CHANGED
|
@@ -9435,6 +9435,30 @@ async function resolvePrimaryKeys(source, target, table, pkOption) {
|
|
|
9435
9435
|
}
|
|
9436
9436
|
return pks;
|
|
9437
9437
|
}
|
|
9438
|
+
async function filterColumnsForTarget(target, table, sourceColumns) {
|
|
9439
|
+
try {
|
|
9440
|
+
if (!isAsyncAdapter(target)) {
|
|
9441
|
+
const colInfo = target.all(`PRAGMA table_info("${table}")`);
|
|
9442
|
+
if (Array.isArray(colInfo) && colInfo.length > 0) {
|
|
9443
|
+
const targetCols = new Set(colInfo.map((c) => c.name));
|
|
9444
|
+
const filtered = sourceColumns.filter((c) => targetCols.has(c));
|
|
9445
|
+
if (filtered.length < sourceColumns.length) {
|
|
9446
|
+
const dropped = sourceColumns.filter((c) => !targetCols.has(c));
|
|
9447
|
+
process.stderr.write(` [sync] ${table}: dropping ${dropped.length} columns not in target: ${dropped.join(", ")}
|
|
9448
|
+
`);
|
|
9449
|
+
}
|
|
9450
|
+
return filtered;
|
|
9451
|
+
}
|
|
9452
|
+
} else {
|
|
9453
|
+
const colInfo = await target.all(`SELECT column_name FROM information_schema.columns WHERE table_schema = 'public' AND table_name = '${table}'`);
|
|
9454
|
+
if (colInfo.length > 0) {
|
|
9455
|
+
const targetCols = new Set(colInfo.map((c) => c.column_name));
|
|
9456
|
+
return sourceColumns.filter((c) => targetCols.has(c));
|
|
9457
|
+
}
|
|
9458
|
+
}
|
|
9459
|
+
} catch {}
|
|
9460
|
+
return sourceColumns;
|
|
9461
|
+
}
|
|
9438
9462
|
async function syncTransfer(source, target, options, _direction) {
|
|
9439
9463
|
const {
|
|
9440
9464
|
tables,
|
|
@@ -9444,60 +9468,88 @@ async function syncTransfer(source, target, options, _direction) {
|
|
|
9444
9468
|
primaryKey: pkOption
|
|
9445
9469
|
} = options;
|
|
9446
9470
|
const results = [];
|
|
9447
|
-
|
|
9471
|
+
const sqliteTarget = !isAsyncAdapter(target) ? target : null;
|
|
9472
|
+
if (sqliteTarget) {
|
|
9448
9473
|
try {
|
|
9449
|
-
|
|
9474
|
+
sqliteTarget.exec("PRAGMA foreign_keys = OFF");
|
|
9450
9475
|
} catch {}
|
|
9451
9476
|
}
|
|
9452
|
-
|
|
9453
|
-
|
|
9454
|
-
|
|
9455
|
-
|
|
9456
|
-
rowsRead: 0,
|
|
9457
|
-
rowsWritten: 0,
|
|
9458
|
-
rowsSkipped: 0,
|
|
9459
|
-
errors: []
|
|
9460
|
-
};
|
|
9461
|
-
try {
|
|
9462
|
-
onProgress?.({
|
|
9477
|
+
try {
|
|
9478
|
+
for (let i = 0;i < tables.length; i++) {
|
|
9479
|
+
const table = tables[i];
|
|
9480
|
+
const result = {
|
|
9463
9481
|
table,
|
|
9464
|
-
phase: "reading",
|
|
9465
9482
|
rowsRead: 0,
|
|
9466
9483
|
rowsWritten: 0,
|
|
9467
|
-
|
|
9468
|
-
|
|
9469
|
-
}
|
|
9470
|
-
|
|
9471
|
-
result.rowsRead = rows.length;
|
|
9472
|
-
if (rows.length === 0) {
|
|
9484
|
+
rowsSkipped: 0,
|
|
9485
|
+
errors: []
|
|
9486
|
+
};
|
|
9487
|
+
try {
|
|
9473
9488
|
onProgress?.({
|
|
9474
9489
|
table,
|
|
9475
|
-
phase: "
|
|
9490
|
+
phase: "reading",
|
|
9476
9491
|
rowsRead: 0,
|
|
9477
9492
|
rowsWritten: 0,
|
|
9478
9493
|
totalTables: tables.length,
|
|
9479
9494
|
currentTableIndex: i
|
|
9480
9495
|
});
|
|
9481
|
-
|
|
9482
|
-
|
|
9483
|
-
|
|
9484
|
-
|
|
9485
|
-
|
|
9486
|
-
|
|
9487
|
-
|
|
9488
|
-
|
|
9489
|
-
|
|
9490
|
-
|
|
9491
|
-
|
|
9492
|
-
|
|
9493
|
-
|
|
9494
|
-
|
|
9495
|
-
|
|
9496
|
-
|
|
9497
|
-
|
|
9498
|
-
|
|
9499
|
-
|
|
9500
|
-
|
|
9496
|
+
const rows = await readAll(source, `SELECT * FROM "${table}"`);
|
|
9497
|
+
result.rowsRead = rows.length;
|
|
9498
|
+
if (rows.length === 0) {
|
|
9499
|
+
onProgress?.({
|
|
9500
|
+
table,
|
|
9501
|
+
phase: "done",
|
|
9502
|
+
rowsRead: 0,
|
|
9503
|
+
rowsWritten: 0,
|
|
9504
|
+
totalTables: tables.length,
|
|
9505
|
+
currentTableIndex: i
|
|
9506
|
+
});
|
|
9507
|
+
results.push(result);
|
|
9508
|
+
continue;
|
|
9509
|
+
}
|
|
9510
|
+
const pkColumns = await resolvePrimaryKeys(source, target, table, pkOption);
|
|
9511
|
+
const sourceColumns = Object.keys(rows[0]);
|
|
9512
|
+
const columns = await filterColumnsForTarget(target, table, sourceColumns);
|
|
9513
|
+
if (pkColumns.length === 0) {
|
|
9514
|
+
result.errors.push(`Table "${table}" has no primary key — inserting without conflict handling`);
|
|
9515
|
+
onProgress?.({
|
|
9516
|
+
table,
|
|
9517
|
+
phase: "writing",
|
|
9518
|
+
rowsRead: result.rowsRead,
|
|
9519
|
+
rowsWritten: 0,
|
|
9520
|
+
totalTables: tables.length,
|
|
9521
|
+
currentTableIndex: i
|
|
9522
|
+
});
|
|
9523
|
+
for (let offset = 0;offset < rows.length; offset += batchSize) {
|
|
9524
|
+
const batch = rows.slice(offset, offset + batchSize);
|
|
9525
|
+
try {
|
|
9526
|
+
if (isAsyncAdapter(target)) {
|
|
9527
|
+
await batchInsertPg(target, table, columns, batch);
|
|
9528
|
+
} else {
|
|
9529
|
+
batchInsertSqlite(target, table, columns, batch);
|
|
9530
|
+
}
|
|
9531
|
+
result.rowsWritten += batch.length;
|
|
9532
|
+
} catch (err) {
|
|
9533
|
+
result.errors.push(`Batch at offset ${offset}: ${err?.message ?? String(err)}`);
|
|
9534
|
+
}
|
|
9535
|
+
}
|
|
9536
|
+
onProgress?.({
|
|
9537
|
+
table,
|
|
9538
|
+
phase: "done",
|
|
9539
|
+
rowsRead: result.rowsRead,
|
|
9540
|
+
rowsWritten: result.rowsWritten,
|
|
9541
|
+
totalTables: tables.length,
|
|
9542
|
+
currentTableIndex: i
|
|
9543
|
+
});
|
|
9544
|
+
results.push(result);
|
|
9545
|
+
continue;
|
|
9546
|
+
}
|
|
9547
|
+
const missingPks = pkColumns.filter((pk) => !columns.includes(pk));
|
|
9548
|
+
if (missingPks.length > 0) {
|
|
9549
|
+
result.errors.push(`Table "${table}" missing PK columns in data: ${missingPks.join(", ")} — skipping`);
|
|
9550
|
+
results.push(result);
|
|
9551
|
+
continue;
|
|
9552
|
+
}
|
|
9501
9553
|
onProgress?.({
|
|
9502
9554
|
table,
|
|
9503
9555
|
phase: "writing",
|
|
@@ -9506,18 +9558,27 @@ async function syncTransfer(source, target, options, _direction) {
|
|
|
9506
9558
|
totalTables: tables.length,
|
|
9507
9559
|
currentTableIndex: i
|
|
9508
9560
|
});
|
|
9561
|
+
const updateCols = columns.filter((c) => !pkColumns.includes(c));
|
|
9509
9562
|
for (let offset = 0;offset < rows.length; offset += batchSize) {
|
|
9510
9563
|
const batch = rows.slice(offset, offset + batchSize);
|
|
9511
9564
|
try {
|
|
9512
9565
|
if (isAsyncAdapter(target)) {
|
|
9513
|
-
await
|
|
9566
|
+
await batchUpsertPg(target, table, columns, updateCols, pkColumns, batch);
|
|
9514
9567
|
} else {
|
|
9515
|
-
|
|
9568
|
+
batchUpsertSqlite(target, table, columns, updateCols, pkColumns, batch);
|
|
9516
9569
|
}
|
|
9517
9570
|
result.rowsWritten += batch.length;
|
|
9518
9571
|
} catch (err) {
|
|
9519
9572
|
result.errors.push(`Batch at offset ${offset}: ${err?.message ?? String(err)}`);
|
|
9520
9573
|
}
|
|
9574
|
+
onProgress?.({
|
|
9575
|
+
table,
|
|
9576
|
+
phase: "writing",
|
|
9577
|
+
rowsRead: result.rowsRead,
|
|
9578
|
+
rowsWritten: result.rowsWritten,
|
|
9579
|
+
totalTables: tables.length,
|
|
9580
|
+
currentTableIndex: i
|
|
9581
|
+
});
|
|
9521
9582
|
}
|
|
9522
9583
|
onProgress?.({
|
|
9523
9584
|
table,
|
|
@@ -9527,62 +9588,27 @@ async function syncTransfer(source, target, options, _direction) {
|
|
|
9527
9588
|
totalTables: tables.length,
|
|
9528
9589
|
currentTableIndex: i
|
|
9529
9590
|
});
|
|
9530
|
-
|
|
9531
|
-
|
|
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;
|
|
9591
|
+
} catch (err) {
|
|
9592
|
+
result.errors.push(`Table "${table}": ${err?.message ?? String(err)}`);
|
|
9538
9593
|
}
|
|
9539
|
-
|
|
9540
|
-
|
|
9541
|
-
|
|
9542
|
-
|
|
9543
|
-
|
|
9544
|
-
|
|
9545
|
-
|
|
9546
|
-
|
|
9547
|
-
|
|
9548
|
-
|
|
9549
|
-
|
|
9550
|
-
|
|
9551
|
-
if (
|
|
9552
|
-
|
|
9553
|
-
} else {
|
|
9554
|
-
batchUpsertSqlite(target, table, columns, updateCols, pkColumns, batch);
|
|
9594
|
+
results.push(result);
|
|
9595
|
+
}
|
|
9596
|
+
} finally {
|
|
9597
|
+
if (sqliteTarget) {
|
|
9598
|
+
try {
|
|
9599
|
+
sqliteTarget.exec("PRAGMA foreign_keys = ON");
|
|
9600
|
+
} catch {}
|
|
9601
|
+
try {
|
|
9602
|
+
const violations = sqliteTarget.all("PRAGMA foreign_key_check");
|
|
9603
|
+
if (violations.length > 0) {
|
|
9604
|
+
const tables2 = [...new Set(violations.map((v) => v.table))];
|
|
9605
|
+
const msg = `FK integrity check: ${violations.length} violation(s) in table(s): ${tables2.join(", ")}`;
|
|
9606
|
+
if (results.length > 0) {
|
|
9607
|
+
results[results.length - 1].errors.push(msg);
|
|
9555
9608
|
}
|
|
9556
|
-
result.rowsWritten += batch.length;
|
|
9557
|
-
} catch (err) {
|
|
9558
|
-
result.errors.push(`Batch at offset ${offset}: ${err?.message ?? String(err)}`);
|
|
9559
9609
|
}
|
|
9560
|
-
|
|
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)}`);
|
|
9610
|
+
} catch {}
|
|
9579
9611
|
}
|
|
9580
|
-
results.push(result);
|
|
9581
|
-
}
|
|
9582
|
-
if (!isAsyncAdapter(target)) {
|
|
9583
|
-
try {
|
|
9584
|
-
target.run("PRAGMA foreign_keys = ON");
|
|
9585
|
-
} catch {}
|
|
9586
9612
|
}
|
|
9587
9613
|
return results;
|
|
9588
9614
|
}
|
package/dist/mcp/index.js
CHANGED
|
@@ -24782,6 +24782,30 @@ async function resolvePrimaryKeys(source, target, table, pkOption) {
|
|
|
24782
24782
|
}
|
|
24783
24783
|
return pks;
|
|
24784
24784
|
}
|
|
24785
|
+
async function filterColumnsForTarget(target, table, sourceColumns) {
|
|
24786
|
+
try {
|
|
24787
|
+
if (!isAsyncAdapter(target)) {
|
|
24788
|
+
const colInfo = target.all(`PRAGMA table_info("${table}")`);
|
|
24789
|
+
if (Array.isArray(colInfo) && colInfo.length > 0) {
|
|
24790
|
+
const targetCols = new Set(colInfo.map((c) => c.name));
|
|
24791
|
+
const filtered = sourceColumns.filter((c) => targetCols.has(c));
|
|
24792
|
+
if (filtered.length < sourceColumns.length) {
|
|
24793
|
+
const dropped = sourceColumns.filter((c) => !targetCols.has(c));
|
|
24794
|
+
process.stderr.write(` [sync] ${table}: dropping ${dropped.length} columns not in target: ${dropped.join(", ")}
|
|
24795
|
+
`);
|
|
24796
|
+
}
|
|
24797
|
+
return filtered;
|
|
24798
|
+
}
|
|
24799
|
+
} else {
|
|
24800
|
+
const colInfo = await target.all(`SELECT column_name FROM information_schema.columns WHERE table_schema = 'public' AND table_name = '${table}'`);
|
|
24801
|
+
if (colInfo.length > 0) {
|
|
24802
|
+
const targetCols = new Set(colInfo.map((c) => c.column_name));
|
|
24803
|
+
return sourceColumns.filter((c) => targetCols.has(c));
|
|
24804
|
+
}
|
|
24805
|
+
}
|
|
24806
|
+
} catch {}
|
|
24807
|
+
return sourceColumns;
|
|
24808
|
+
}
|
|
24785
24809
|
async function syncTransfer(source, target, options, _direction) {
|
|
24786
24810
|
const {
|
|
24787
24811
|
tables,
|
|
@@ -24791,60 +24815,88 @@ async function syncTransfer(source, target, options, _direction) {
|
|
|
24791
24815
|
primaryKey: pkOption
|
|
24792
24816
|
} = options;
|
|
24793
24817
|
const results = [];
|
|
24794
|
-
|
|
24818
|
+
const sqliteTarget = !isAsyncAdapter(target) ? target : null;
|
|
24819
|
+
if (sqliteTarget) {
|
|
24795
24820
|
try {
|
|
24796
|
-
|
|
24821
|
+
sqliteTarget.exec("PRAGMA foreign_keys = OFF");
|
|
24797
24822
|
} catch {}
|
|
24798
24823
|
}
|
|
24799
|
-
|
|
24800
|
-
|
|
24801
|
-
|
|
24802
|
-
|
|
24803
|
-
rowsRead: 0,
|
|
24804
|
-
rowsWritten: 0,
|
|
24805
|
-
rowsSkipped: 0,
|
|
24806
|
-
errors: []
|
|
24807
|
-
};
|
|
24808
|
-
try {
|
|
24809
|
-
onProgress?.({
|
|
24824
|
+
try {
|
|
24825
|
+
for (let i = 0;i < tables.length; i++) {
|
|
24826
|
+
const table = tables[i];
|
|
24827
|
+
const result = {
|
|
24810
24828
|
table,
|
|
24811
|
-
phase: "reading",
|
|
24812
24829
|
rowsRead: 0,
|
|
24813
24830
|
rowsWritten: 0,
|
|
24814
|
-
|
|
24815
|
-
|
|
24816
|
-
}
|
|
24817
|
-
|
|
24818
|
-
result.rowsRead = rows.length;
|
|
24819
|
-
if (rows.length === 0) {
|
|
24831
|
+
rowsSkipped: 0,
|
|
24832
|
+
errors: []
|
|
24833
|
+
};
|
|
24834
|
+
try {
|
|
24820
24835
|
onProgress?.({
|
|
24821
24836
|
table,
|
|
24822
|
-
phase: "
|
|
24837
|
+
phase: "reading",
|
|
24823
24838
|
rowsRead: 0,
|
|
24824
24839
|
rowsWritten: 0,
|
|
24825
24840
|
totalTables: tables.length,
|
|
24826
24841
|
currentTableIndex: i
|
|
24827
24842
|
});
|
|
24828
|
-
|
|
24829
|
-
|
|
24830
|
-
|
|
24831
|
-
|
|
24832
|
-
|
|
24833
|
-
|
|
24834
|
-
|
|
24835
|
-
|
|
24836
|
-
|
|
24837
|
-
|
|
24838
|
-
|
|
24839
|
-
|
|
24840
|
-
|
|
24841
|
-
|
|
24842
|
-
|
|
24843
|
-
|
|
24844
|
-
|
|
24845
|
-
|
|
24846
|
-
|
|
24847
|
-
|
|
24843
|
+
const rows = await readAll(source, `SELECT * FROM "${table}"`);
|
|
24844
|
+
result.rowsRead = rows.length;
|
|
24845
|
+
if (rows.length === 0) {
|
|
24846
|
+
onProgress?.({
|
|
24847
|
+
table,
|
|
24848
|
+
phase: "done",
|
|
24849
|
+
rowsRead: 0,
|
|
24850
|
+
rowsWritten: 0,
|
|
24851
|
+
totalTables: tables.length,
|
|
24852
|
+
currentTableIndex: i
|
|
24853
|
+
});
|
|
24854
|
+
results.push(result);
|
|
24855
|
+
continue;
|
|
24856
|
+
}
|
|
24857
|
+
const pkColumns = await resolvePrimaryKeys(source, target, table, pkOption);
|
|
24858
|
+
const sourceColumns = Object.keys(rows[0]);
|
|
24859
|
+
const columns = await filterColumnsForTarget(target, table, sourceColumns);
|
|
24860
|
+
if (pkColumns.length === 0) {
|
|
24861
|
+
result.errors.push(`Table "${table}" has no primary key — inserting without conflict handling`);
|
|
24862
|
+
onProgress?.({
|
|
24863
|
+
table,
|
|
24864
|
+
phase: "writing",
|
|
24865
|
+
rowsRead: result.rowsRead,
|
|
24866
|
+
rowsWritten: 0,
|
|
24867
|
+
totalTables: tables.length,
|
|
24868
|
+
currentTableIndex: i
|
|
24869
|
+
});
|
|
24870
|
+
for (let offset = 0;offset < rows.length; offset += batchSize) {
|
|
24871
|
+
const batch = rows.slice(offset, offset + batchSize);
|
|
24872
|
+
try {
|
|
24873
|
+
if (isAsyncAdapter(target)) {
|
|
24874
|
+
await batchInsertPg(target, table, columns, batch);
|
|
24875
|
+
} else {
|
|
24876
|
+
batchInsertSqlite(target, table, columns, batch);
|
|
24877
|
+
}
|
|
24878
|
+
result.rowsWritten += batch.length;
|
|
24879
|
+
} catch (err) {
|
|
24880
|
+
result.errors.push(`Batch at offset ${offset}: ${err?.message ?? String(err)}`);
|
|
24881
|
+
}
|
|
24882
|
+
}
|
|
24883
|
+
onProgress?.({
|
|
24884
|
+
table,
|
|
24885
|
+
phase: "done",
|
|
24886
|
+
rowsRead: result.rowsRead,
|
|
24887
|
+
rowsWritten: result.rowsWritten,
|
|
24888
|
+
totalTables: tables.length,
|
|
24889
|
+
currentTableIndex: i
|
|
24890
|
+
});
|
|
24891
|
+
results.push(result);
|
|
24892
|
+
continue;
|
|
24893
|
+
}
|
|
24894
|
+
const missingPks = pkColumns.filter((pk) => !columns.includes(pk));
|
|
24895
|
+
if (missingPks.length > 0) {
|
|
24896
|
+
result.errors.push(`Table "${table}" missing PK columns in data: ${missingPks.join(", ")} — skipping`);
|
|
24897
|
+
results.push(result);
|
|
24898
|
+
continue;
|
|
24899
|
+
}
|
|
24848
24900
|
onProgress?.({
|
|
24849
24901
|
table,
|
|
24850
24902
|
phase: "writing",
|
|
@@ -24853,18 +24905,27 @@ async function syncTransfer(source, target, options, _direction) {
|
|
|
24853
24905
|
totalTables: tables.length,
|
|
24854
24906
|
currentTableIndex: i
|
|
24855
24907
|
});
|
|
24908
|
+
const updateCols = columns.filter((c) => !pkColumns.includes(c));
|
|
24856
24909
|
for (let offset = 0;offset < rows.length; offset += batchSize) {
|
|
24857
24910
|
const batch = rows.slice(offset, offset + batchSize);
|
|
24858
24911
|
try {
|
|
24859
24912
|
if (isAsyncAdapter(target)) {
|
|
24860
|
-
await
|
|
24913
|
+
await batchUpsertPg(target, table, columns, updateCols, pkColumns, batch);
|
|
24861
24914
|
} else {
|
|
24862
|
-
|
|
24915
|
+
batchUpsertSqlite(target, table, columns, updateCols, pkColumns, batch);
|
|
24863
24916
|
}
|
|
24864
24917
|
result.rowsWritten += batch.length;
|
|
24865
24918
|
} catch (err) {
|
|
24866
24919
|
result.errors.push(`Batch at offset ${offset}: ${err?.message ?? String(err)}`);
|
|
24867
24920
|
}
|
|
24921
|
+
onProgress?.({
|
|
24922
|
+
table,
|
|
24923
|
+
phase: "writing",
|
|
24924
|
+
rowsRead: result.rowsRead,
|
|
24925
|
+
rowsWritten: result.rowsWritten,
|
|
24926
|
+
totalTables: tables.length,
|
|
24927
|
+
currentTableIndex: i
|
|
24928
|
+
});
|
|
24868
24929
|
}
|
|
24869
24930
|
onProgress?.({
|
|
24870
24931
|
table,
|
|
@@ -24874,62 +24935,27 @@ async function syncTransfer(source, target, options, _direction) {
|
|
|
24874
24935
|
totalTables: tables.length,
|
|
24875
24936
|
currentTableIndex: i
|
|
24876
24937
|
});
|
|
24877
|
-
|
|
24878
|
-
|
|
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;
|
|
24938
|
+
} catch (err) {
|
|
24939
|
+
result.errors.push(`Table "${table}": ${err?.message ?? String(err)}`);
|
|
24885
24940
|
}
|
|
24886
|
-
|
|
24887
|
-
|
|
24888
|
-
|
|
24889
|
-
|
|
24890
|
-
|
|
24891
|
-
|
|
24892
|
-
|
|
24893
|
-
|
|
24894
|
-
|
|
24895
|
-
|
|
24896
|
-
|
|
24897
|
-
|
|
24898
|
-
if (
|
|
24899
|
-
|
|
24900
|
-
} else {
|
|
24901
|
-
batchUpsertSqlite(target, table, columns, updateCols, pkColumns, batch);
|
|
24941
|
+
results.push(result);
|
|
24942
|
+
}
|
|
24943
|
+
} finally {
|
|
24944
|
+
if (sqliteTarget) {
|
|
24945
|
+
try {
|
|
24946
|
+
sqliteTarget.exec("PRAGMA foreign_keys = ON");
|
|
24947
|
+
} catch {}
|
|
24948
|
+
try {
|
|
24949
|
+
const violations = sqliteTarget.all("PRAGMA foreign_key_check");
|
|
24950
|
+
if (violations.length > 0) {
|
|
24951
|
+
const tables2 = [...new Set(violations.map((v) => v.table))];
|
|
24952
|
+
const msg = `FK integrity check: ${violations.length} violation(s) in table(s): ${tables2.join(", ")}`;
|
|
24953
|
+
if (results.length > 0) {
|
|
24954
|
+
results[results.length - 1].errors.push(msg);
|
|
24902
24955
|
}
|
|
24903
|
-
result.rowsWritten += batch.length;
|
|
24904
|
-
} catch (err) {
|
|
24905
|
-
result.errors.push(`Batch at offset ${offset}: ${err?.message ?? String(err)}`);
|
|
24906
24956
|
}
|
|
24907
|
-
|
|
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)}`);
|
|
24957
|
+
} catch {}
|
|
24926
24958
|
}
|
|
24927
|
-
results.push(result);
|
|
24928
|
-
}
|
|
24929
|
-
if (!isAsyncAdapter(target)) {
|
|
24930
|
-
try {
|
|
24931
|
-
target.run("PRAGMA foreign_keys = ON");
|
|
24932
|
-
} catch {}
|
|
24933
24959
|
}
|
|
24934
24960
|
return results;
|
|
24935
24961
|
}
|
package/dist/sync.d.ts.map
CHANGED
|
@@ -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;
|
|
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;AAqoBD;;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