@hasna/cloud 0.1.3 → 0.1.4
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/auto-sync.d.ts +57 -0
- package/dist/auto-sync.d.ts.map +1 -0
- package/dist/cli/index.js +170 -154
- package/dist/cli-helpers.d.ts.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +723 -63
- package/dist/mcp/index.js +167 -151
- package/dist/mcp-helpers.d.ts.map +1 -1
- package/dist/sync-conflicts.d.ts +76 -0
- package/dist/sync-conflicts.d.ts.map +1 -0
- package/dist/sync-incremental.d.ts +62 -0
- package/dist/sync-incremental.d.ts.map +1 -0
- package/dist/sync-progress.d.ts +68 -0
- package/dist/sync-progress.d.ts.map +1 -0
- package/dist/sync.d.ts +10 -9
- package/dist/sync.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { DbAdapter } from "./adapter.js";
|
|
2
|
+
export interface AutoSyncConfig {
|
|
3
|
+
auto_sync_on_start: boolean;
|
|
4
|
+
auto_sync_on_stop: boolean;
|
|
5
|
+
}
|
|
6
|
+
export interface AutoSyncContext {
|
|
7
|
+
serviceName: string;
|
|
8
|
+
local: DbAdapter;
|
|
9
|
+
remote: DbAdapter;
|
|
10
|
+
tables: string[];
|
|
11
|
+
config: AutoSyncConfig;
|
|
12
|
+
}
|
|
13
|
+
export interface AutoSyncResult {
|
|
14
|
+
event: "start" | "stop";
|
|
15
|
+
direction: "pull" | "push";
|
|
16
|
+
success: boolean;
|
|
17
|
+
tables_synced: number;
|
|
18
|
+
total_rows_synced: number;
|
|
19
|
+
errors: string[];
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Read auto-sync configuration from `~/.hasna/cloud/config.json`.
|
|
23
|
+
* Falls back to defaults if the file does not exist or is malformed.
|
|
24
|
+
*/
|
|
25
|
+
export declare function getAutoSyncConfig(): AutoSyncConfig;
|
|
26
|
+
/**
|
|
27
|
+
* Set up auto-sync hooks for a service's MCP server.
|
|
28
|
+
*
|
|
29
|
+
* - On connect: if `auto_sync_on_start` and mode is `hybrid` or `cloud`,
|
|
30
|
+
* pull from cloud to local.
|
|
31
|
+
* - On disconnect/SIGTERM: if `auto_sync_on_stop` and mode is `hybrid` or `cloud`,
|
|
32
|
+
* push from local to cloud.
|
|
33
|
+
*
|
|
34
|
+
* @param serviceName - The service identifier.
|
|
35
|
+
* @param server - The MCP server instance (any object with `onconnect`/`ondisconnect` events).
|
|
36
|
+
* @param local - The local database adapter.
|
|
37
|
+
* @param remote - The remote database adapter.
|
|
38
|
+
* @param tables - Tables to sync.
|
|
39
|
+
* @returns An object with methods to manually trigger start/stop syncs.
|
|
40
|
+
*/
|
|
41
|
+
export declare function setupAutoSync(serviceName: string, server: any, local: DbAdapter, remote: DbAdapter, tables: string[]): {
|
|
42
|
+
syncOnStart: () => AutoSyncResult | null;
|
|
43
|
+
syncOnStop: () => AutoSyncResult | null;
|
|
44
|
+
config: AutoSyncConfig;
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Enable auto-sync for a service. Simplified entry point that services
|
|
48
|
+
* can call with minimal configuration.
|
|
49
|
+
*
|
|
50
|
+
* @param serviceName - The service name (used for logging context).
|
|
51
|
+
* @param mcpServer - The MCP server instance.
|
|
52
|
+
* @param local - The local database adapter.
|
|
53
|
+
* @param remote - The remote database adapter.
|
|
54
|
+
* @param tables - Tables to sync on start/stop.
|
|
55
|
+
*/
|
|
56
|
+
export declare function enableAutoSync(serviceName: string, mcpServer: any, local: DbAdapter, remote: DbAdapter, tables: string[]): void;
|
|
57
|
+
//# sourceMappingURL=auto-sync.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auto-sync.d.ts","sourceRoot":"","sources":["../src/auto-sync.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAQ9C,MAAM,WAAW,cAAc;IAC7B,kBAAkB,EAAE,OAAO,CAAC;IAC5B,iBAAiB,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,SAAS,CAAC;IACjB,MAAM,EAAE,SAAS,CAAC;IAClB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,MAAM,EAAE,cAAc,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,OAAO,GAAG,MAAM,CAAC;IACxB,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAaD;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,cAAc,CAmBlD;AAwFD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,aAAa,CAC3B,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,GAAG,EACX,KAAK,EAAE,SAAS,EAChB,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,MAAM,EAAE,GACf;IACD,WAAW,EAAE,MAAM,cAAc,GAAG,IAAI,CAAC;IACzC,UAAU,EAAE,MAAM,cAAc,GAAG,IAAI,CAAC;IACxC,MAAM,EAAE,cAAc,CAAC;CACxB,CAiDA;AAMD;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAC5B,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,GAAG,EACd,KAAK,EAAE,SAAS,EAChB,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,MAAM,EAAE,GACf,IAAI,CAEN"}
|
package/dist/cli/index.js
CHANGED
|
@@ -2191,7 +2191,7 @@ var require_arrayParser = __commonJS((exports, module) => {
|
|
|
2191
2191
|
};
|
|
2192
2192
|
});
|
|
2193
2193
|
|
|
2194
|
-
// node_modules/postgres-date/index.js
|
|
2194
|
+
// node_modules/pg-types/node_modules/postgres-date/index.js
|
|
2195
2195
|
var require_postgres_date = __commonJS((exports, module) => {
|
|
2196
2196
|
var DATE_TIME = /(\d{1,})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})(\.\d{1,})?.*?( BC)?$/;
|
|
2197
2197
|
var DATE = /^(\d{1,})-(\d{2})-(\d{2})( BC)?$/;
|
|
@@ -2293,7 +2293,7 @@ var require_mutable = __commonJS((exports, module) => {
|
|
|
2293
2293
|
}
|
|
2294
2294
|
});
|
|
2295
2295
|
|
|
2296
|
-
// node_modules/postgres-interval/index.js
|
|
2296
|
+
// node_modules/pg-types/node_modules/postgres-interval/index.js
|
|
2297
2297
|
var require_postgres_interval = __commonJS((exports, module) => {
|
|
2298
2298
|
var extend = require_mutable();
|
|
2299
2299
|
module.exports = PostgresInterval;
|
|
@@ -2385,7 +2385,7 @@ var require_postgres_interval = __commonJS((exports, module) => {
|
|
|
2385
2385
|
}
|
|
2386
2386
|
});
|
|
2387
2387
|
|
|
2388
|
-
// node_modules/postgres-bytea/index.js
|
|
2388
|
+
// node_modules/pg-types/node_modules/postgres-bytea/index.js
|
|
2389
2389
|
var require_postgres_bytea = __commonJS((exports, module) => {
|
|
2390
2390
|
var bufferFrom = Buffer.from || Buffer;
|
|
2391
2391
|
module.exports = function parseBytea(input) {
|
|
@@ -11257,17 +11257,88 @@ function createDatabase(options) {
|
|
|
11257
11257
|
}
|
|
11258
11258
|
|
|
11259
11259
|
// src/sync.ts
|
|
11260
|
-
function syncPush(local,
|
|
11261
|
-
|
|
11260
|
+
async function syncPush(local, remote, options) {
|
|
11261
|
+
const orderedTables = await getTableOrder(remote, options.tables);
|
|
11262
|
+
return syncTransfer(local, remote, { ...options, tables: orderedTables }, "push");
|
|
11262
11263
|
}
|
|
11263
|
-
function syncPull(
|
|
11264
|
-
|
|
11264
|
+
async function syncPull(remote, local, options) {
|
|
11265
|
+
const orderedTables = await getTableOrder(remote, options.tables);
|
|
11266
|
+
return syncTransfer(remote, local, { ...options, tables: orderedTables }, "pull");
|
|
11265
11267
|
}
|
|
11266
|
-
function
|
|
11268
|
+
async function getTableOrder(remote, tables) {
|
|
11269
|
+
if (tables.length <= 1)
|
|
11270
|
+
return tables;
|
|
11271
|
+
try {
|
|
11272
|
+
const fks = await remote.all(`
|
|
11273
|
+
SELECT DISTINCT
|
|
11274
|
+
tc.table_name AS source_table,
|
|
11275
|
+
ccu.table_name AS referenced_table
|
|
11276
|
+
FROM information_schema.table_constraints tc
|
|
11277
|
+
JOIN information_schema.constraint_column_usage ccu
|
|
11278
|
+
ON tc.constraint_name = ccu.constraint_name
|
|
11279
|
+
AND tc.table_schema = ccu.table_schema
|
|
11280
|
+
WHERE tc.constraint_type = 'FOREIGN KEY'
|
|
11281
|
+
AND tc.table_schema = 'public'
|
|
11282
|
+
`);
|
|
11283
|
+
if (fks.length > 0) {
|
|
11284
|
+
return topoSort(tables, fks);
|
|
11285
|
+
}
|
|
11286
|
+
} catch {}
|
|
11287
|
+
return heuristicOrder(tables);
|
|
11288
|
+
}
|
|
11289
|
+
function topoSort(tables, fks) {
|
|
11290
|
+
const tableSet = new Set(tables);
|
|
11291
|
+
const deps = new Map;
|
|
11292
|
+
for (const t of tables) {
|
|
11293
|
+
deps.set(t, new Set);
|
|
11294
|
+
}
|
|
11295
|
+
for (const fk of fks) {
|
|
11296
|
+
if (tableSet.has(fk.source_table) && tableSet.has(fk.referenced_table)) {
|
|
11297
|
+
deps.get(fk.source_table).add(fk.referenced_table);
|
|
11298
|
+
}
|
|
11299
|
+
}
|
|
11300
|
+
const sorted = [];
|
|
11301
|
+
const visited = new Set;
|
|
11302
|
+
const visiting = new Set;
|
|
11303
|
+
function visit(table) {
|
|
11304
|
+
if (visited.has(table))
|
|
11305
|
+
return;
|
|
11306
|
+
if (visiting.has(table)) {
|
|
11307
|
+
sorted.push(table);
|
|
11308
|
+
visited.add(table);
|
|
11309
|
+
return;
|
|
11310
|
+
}
|
|
11311
|
+
visiting.add(table);
|
|
11312
|
+
const tableDeps = deps.get(table) ?? new Set;
|
|
11313
|
+
for (const dep of tableDeps) {
|
|
11314
|
+
visit(dep);
|
|
11315
|
+
}
|
|
11316
|
+
visiting.delete(table);
|
|
11317
|
+
visited.add(table);
|
|
11318
|
+
sorted.push(table);
|
|
11319
|
+
}
|
|
11320
|
+
for (const t of tables) {
|
|
11321
|
+
visit(t);
|
|
11322
|
+
}
|
|
11323
|
+
return sorted;
|
|
11324
|
+
}
|
|
11325
|
+
function heuristicOrder(tables) {
|
|
11326
|
+
const sorted = [...tables].sort((a, b) => {
|
|
11327
|
+
const aIsChild = a.includes("_") && tables.some((t) => a.startsWith(t + "_") || a.endsWith("_" + t));
|
|
11328
|
+
const bIsChild = b.includes("_") && tables.some((t) => b.startsWith(t + "_") || b.endsWith("_" + t));
|
|
11329
|
+
if (aIsChild && !bIsChild)
|
|
11330
|
+
return 1;
|
|
11331
|
+
if (!aIsChild && bIsChild)
|
|
11332
|
+
return -1;
|
|
11333
|
+
return a.localeCompare(b);
|
|
11334
|
+
});
|
|
11335
|
+
return sorted;
|
|
11336
|
+
}
|
|
11337
|
+
async function syncTransfer(source, target, options, _direction) {
|
|
11267
11338
|
const {
|
|
11268
11339
|
tables,
|
|
11269
11340
|
onProgress,
|
|
11270
|
-
batchSize =
|
|
11341
|
+
batchSize = 100,
|
|
11271
11342
|
conflictColumn = "updated_at",
|
|
11272
11343
|
primaryKey = "id"
|
|
11273
11344
|
} = options;
|
|
@@ -11290,7 +11361,7 @@ function syncTransfer(source, target, options, _direction) {
|
|
|
11290
11361
|
totalTables: tables.length,
|
|
11291
11362
|
currentTableIndex: i
|
|
11292
11363
|
});
|
|
11293
|
-
const rows = source
|
|
11364
|
+
const rows = await readAll(source, `SELECT * FROM "${table}"`);
|
|
11294
11365
|
result.rowsRead = rows.length;
|
|
11295
11366
|
if (rows.length === 0) {
|
|
11296
11367
|
onProgress?.({
|
|
@@ -11305,7 +11376,6 @@ function syncTransfer(source, target, options, _direction) {
|
|
|
11305
11376
|
continue;
|
|
11306
11377
|
}
|
|
11307
11378
|
const columns = Object.keys(rows[0]);
|
|
11308
|
-
const hasConflictCol = columns.includes(conflictColumn);
|
|
11309
11379
|
const hasPrimaryKey = columns.includes(primaryKey);
|
|
11310
11380
|
if (!hasPrimaryKey) {
|
|
11311
11381
|
result.errors.push(`Table "${table}" has no "${primaryKey}" column — skipping`);
|
|
@@ -11320,34 +11390,18 @@ function syncTransfer(source, target, options, _direction) {
|
|
|
11320
11390
|
totalTables: tables.length,
|
|
11321
11391
|
currentTableIndex: i
|
|
11322
11392
|
});
|
|
11393
|
+
const updateCols = columns.filter((c) => c !== primaryKey);
|
|
11323
11394
|
for (let offset = 0;offset < rows.length; offset += batchSize) {
|
|
11324
11395
|
const batch = rows.slice(offset, offset + batchSize);
|
|
11325
|
-
|
|
11326
|
-
|
|
11327
|
-
|
|
11328
|
-
|
|
11329
|
-
|
|
11330
|
-
const existingTime = new Date(existing[conflictColumn]).getTime();
|
|
11331
|
-
const incomingTime = new Date(row[conflictColumn]).getTime();
|
|
11332
|
-
if (existingTime >= incomingTime) {
|
|
11333
|
-
result.rowsSkipped++;
|
|
11334
|
-
continue;
|
|
11335
|
-
}
|
|
11336
|
-
}
|
|
11337
|
-
const setClauses = columns.filter((c) => c !== primaryKey).map((c) => `"${c}" = ?`).join(", ");
|
|
11338
|
-
const values = columns.filter((c) => c !== primaryKey).map((c) => row[c]);
|
|
11339
|
-
values.push(row[primaryKey]);
|
|
11340
|
-
target.run(`UPDATE "${table}" SET ${setClauses} WHERE "${primaryKey}" = ?`, ...values);
|
|
11341
|
-
} else {
|
|
11342
|
-
const placeholders = columns.map(() => "?").join(", ");
|
|
11343
|
-
const colList = columns.map((c) => `"${c}"`).join(", ");
|
|
11344
|
-
const values = columns.map((c) => row[c]);
|
|
11345
|
-
target.run(`INSERT INTO "${table}" (${colList}) VALUES (${placeholders})`, ...values);
|
|
11346
|
-
}
|
|
11347
|
-
result.rowsWritten++;
|
|
11348
|
-
} catch (err) {
|
|
11349
|
-
result.errors.push(`Row ${row[primaryKey]}: ${err?.message ?? String(err)}`);
|
|
11396
|
+
try {
|
|
11397
|
+
if (isAsyncAdapter(target)) {
|
|
11398
|
+
await batchUpsertPg(target, table, columns, updateCols, primaryKey, batch);
|
|
11399
|
+
} else {
|
|
11400
|
+
batchUpsertSqlite(target, table, columns, updateCols, primaryKey, batch);
|
|
11350
11401
|
}
|
|
11402
|
+
result.rowsWritten += batch.length;
|
|
11403
|
+
} catch (err) {
|
|
11404
|
+
result.errors.push(`Batch at offset ${offset}: ${err?.message ?? String(err)}`);
|
|
11351
11405
|
}
|
|
11352
11406
|
onProgress?.({
|
|
11353
11407
|
table,
|
|
@@ -11373,10 +11427,46 @@ function syncTransfer(source, target, options, _direction) {
|
|
|
11373
11427
|
}
|
|
11374
11428
|
return results;
|
|
11375
11429
|
}
|
|
11430
|
+
async function batchUpsertPg(target, table, columns, updateCols, primaryKey, batch) {
|
|
11431
|
+
if (batch.length === 0)
|
|
11432
|
+
return;
|
|
11433
|
+
const colList = columns.map((c) => `"${c}"`).join(", ");
|
|
11434
|
+
const valuePlaceholders = batch.map((_, rowIdx) => {
|
|
11435
|
+
const offset = rowIdx * columns.length;
|
|
11436
|
+
return `(${columns.map((_2, colIdx) => `$${offset + colIdx + 1}`).join(", ")})`;
|
|
11437
|
+
}).join(", ");
|
|
11438
|
+
const setClause = updateCols.length > 0 ? updateCols.map((c) => `"${c}" = EXCLUDED."${c}"`).join(", ") : `"${primaryKey}" = EXCLUDED."${primaryKey}"`;
|
|
11439
|
+
const sql = `INSERT INTO "${table}" (${colList}) VALUES ${valuePlaceholders}
|
|
11440
|
+
ON CONFLICT ("${primaryKey}") DO UPDATE SET ${setClause}`;
|
|
11441
|
+
const params = batch.flatMap((row) => columns.map((c) => row[c] ?? null));
|
|
11442
|
+
await target.run(sql, ...params);
|
|
11443
|
+
}
|
|
11444
|
+
function batchUpsertSqlite(target, table, columns, updateCols, primaryKey, batch) {
|
|
11445
|
+
if (batch.length === 0)
|
|
11446
|
+
return;
|
|
11447
|
+
const colList = columns.map((c) => `"${c}"`).join(", ");
|
|
11448
|
+
const valuePlaceholders = batch.map(() => `(${columns.map(() => "?").join(", ")})`).join(", ");
|
|
11449
|
+
const setClause = updateCols.length > 0 ? updateCols.map((c) => `"${c}" = EXCLUDED."${c}"`).join(", ") : `"${primaryKey}" = EXCLUDED."${primaryKey}"`;
|
|
11450
|
+
const sql = `INSERT INTO "${table}" (${colList}) VALUES ${valuePlaceholders}
|
|
11451
|
+
ON CONFLICT ("${primaryKey}") DO UPDATE SET ${setClause}`;
|
|
11452
|
+
const params = batch.flatMap((row) => columns.map((c) => row[c] ?? null));
|
|
11453
|
+
target.run(sql, ...params);
|
|
11454
|
+
}
|
|
11455
|
+
function isAsyncAdapter(adapter) {
|
|
11456
|
+
return adapter.constructor.name === "PgAdapterAsync" || typeof adapter.raw?.connect === "function";
|
|
11457
|
+
}
|
|
11458
|
+
async function readAll(adapter, sql) {
|
|
11459
|
+
const result = adapter.all(sql);
|
|
11460
|
+
return result instanceof Promise ? await result : result;
|
|
11461
|
+
}
|
|
11376
11462
|
function listSqliteTables(db) {
|
|
11377
11463
|
const rows = db.all(`SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name`);
|
|
11378
11464
|
return rows.map((r) => r.name);
|
|
11379
11465
|
}
|
|
11466
|
+
async function listPgTables(db) {
|
|
11467
|
+
const rows = await db.all(`SELECT tablename FROM pg_tables WHERE schemaname = 'public' ORDER BY tablename`);
|
|
11468
|
+
return rows.map((r) => r.tablename);
|
|
11469
|
+
}
|
|
11380
11470
|
|
|
11381
11471
|
// src/feedback.ts
|
|
11382
11472
|
import { hostname } from "os";
|
|
@@ -11578,10 +11668,8 @@ class SqliteAdapter2 {
|
|
|
11578
11668
|
return this.db;
|
|
11579
11669
|
}
|
|
11580
11670
|
}
|
|
11581
|
-
|
|
11582
|
-
class PgAdapter2 {
|
|
11671
|
+
class PgAdapterAsync {
|
|
11583
11672
|
pool;
|
|
11584
|
-
_client = null;
|
|
11585
11673
|
constructor(arg) {
|
|
11586
11674
|
if (typeof arg === "string") {
|
|
11587
11675
|
this.pool = new esm_default.Pool({ connectionString: arg });
|
|
@@ -11589,118 +11677,47 @@ class PgAdapter2 {
|
|
|
11589
11677
|
this.pool = arg;
|
|
11590
11678
|
}
|
|
11591
11679
|
}
|
|
11592
|
-
|
|
11593
|
-
let result;
|
|
11594
|
-
let error;
|
|
11595
|
-
let done = false;
|
|
11596
|
-
fn().then((r) => {
|
|
11597
|
-
result = r;
|
|
11598
|
-
done = true;
|
|
11599
|
-
}).catch((e) => {
|
|
11600
|
-
error = e;
|
|
11601
|
-
done = true;
|
|
11602
|
-
});
|
|
11603
|
-
const deadline = Date.now() + 30000;
|
|
11604
|
-
while (!done && Date.now() < deadline) {
|
|
11605
|
-
Bun.sleepSync(1);
|
|
11606
|
-
}
|
|
11607
|
-
if (error)
|
|
11608
|
-
throw error;
|
|
11609
|
-
if (!done)
|
|
11610
|
-
throw new Error("PgAdapter: query timed out (30s)");
|
|
11611
|
-
return result;
|
|
11612
|
-
}
|
|
11613
|
-
run(sql, ...params) {
|
|
11680
|
+
async run(sql, ...params) {
|
|
11614
11681
|
const pgSql = translateSql(sql, "pg");
|
|
11615
11682
|
const pgParams = translateParams(params);
|
|
11616
|
-
|
|
11617
|
-
|
|
11618
|
-
|
|
11619
|
-
|
|
11620
|
-
|
|
11621
|
-
};
|
|
11622
|
-
});
|
|
11683
|
+
const res = await this.pool.query(pgSql, pgParams);
|
|
11684
|
+
return {
|
|
11685
|
+
changes: res.rowCount ?? 0,
|
|
11686
|
+
lastInsertRowid: res.rows?.[0]?.id ?? 0
|
|
11687
|
+
};
|
|
11623
11688
|
}
|
|
11624
|
-
get(sql, ...params) {
|
|
11689
|
+
async get(sql, ...params) {
|
|
11625
11690
|
const pgSql = translateSql(sql, "pg");
|
|
11626
11691
|
const pgParams = translateParams(params);
|
|
11627
|
-
|
|
11628
|
-
|
|
11629
|
-
return res.rows[0] ?? null;
|
|
11630
|
-
});
|
|
11692
|
+
const res = await this.pool.query(pgSql, pgParams);
|
|
11693
|
+
return res.rows[0] ?? null;
|
|
11631
11694
|
}
|
|
11632
|
-
all(sql, ...params) {
|
|
11695
|
+
async all(sql, ...params) {
|
|
11633
11696
|
const pgSql = translateSql(sql, "pg");
|
|
11634
11697
|
const pgParams = translateParams(params);
|
|
11635
|
-
|
|
11636
|
-
|
|
11637
|
-
return res.rows;
|
|
11638
|
-
});
|
|
11698
|
+
const res = await this.pool.query(pgSql, pgParams);
|
|
11699
|
+
return res.rows;
|
|
11639
11700
|
}
|
|
11640
|
-
exec(sql) {
|
|
11641
|
-
const pgSql = translateSql(sql, "pg");
|
|
11642
|
-
this.runSync(async () => {
|
|
11643
|
-
await this.pool.query(pgSql);
|
|
11644
|
-
});
|
|
11645
|
-
}
|
|
11646
|
-
prepare(sql) {
|
|
11701
|
+
async exec(sql) {
|
|
11647
11702
|
const pgSql = translateSql(sql, "pg");
|
|
11648
|
-
|
|
11649
|
-
return {
|
|
11650
|
-
run(...params) {
|
|
11651
|
-
const pgParams = translateParams(params);
|
|
11652
|
-
return adapter.runSync(async () => {
|
|
11653
|
-
const res = await adapter.pool.query(pgSql, pgParams);
|
|
11654
|
-
return {
|
|
11655
|
-
changes: res.rowCount ?? 0,
|
|
11656
|
-
lastInsertRowid: res.rows?.[0]?.id ?? 0
|
|
11657
|
-
};
|
|
11658
|
-
});
|
|
11659
|
-
},
|
|
11660
|
-
get(...params) {
|
|
11661
|
-
const pgParams = translateParams(params);
|
|
11662
|
-
return adapter.runSync(async () => {
|
|
11663
|
-
const res = await adapter.pool.query(pgSql, pgParams);
|
|
11664
|
-
return res.rows[0] ?? null;
|
|
11665
|
-
});
|
|
11666
|
-
},
|
|
11667
|
-
all(...params) {
|
|
11668
|
-
const pgParams = translateParams(params);
|
|
11669
|
-
return adapter.runSync(async () => {
|
|
11670
|
-
const res = await adapter.pool.query(pgSql, pgParams);
|
|
11671
|
-
return res.rows;
|
|
11672
|
-
});
|
|
11673
|
-
},
|
|
11674
|
-
finalize() {}
|
|
11675
|
-
};
|
|
11703
|
+
await this.pool.query(pgSql);
|
|
11676
11704
|
}
|
|
11677
|
-
close() {
|
|
11678
|
-
this.
|
|
11679
|
-
await this.pool.end();
|
|
11680
|
-
});
|
|
11705
|
+
async close() {
|
|
11706
|
+
await this.pool.end();
|
|
11681
11707
|
}
|
|
11682
|
-
transaction(fn) {
|
|
11683
|
-
|
|
11684
|
-
|
|
11685
|
-
|
|
11686
|
-
|
|
11687
|
-
|
|
11688
|
-
|
|
11689
|
-
|
|
11690
|
-
|
|
11691
|
-
|
|
11692
|
-
|
|
11693
|
-
|
|
11694
|
-
|
|
11695
|
-
await client.query("COMMIT");
|
|
11696
|
-
return result;
|
|
11697
|
-
} catch (err) {
|
|
11698
|
-
await client.query("ROLLBACK");
|
|
11699
|
-
throw err;
|
|
11700
|
-
} finally {
|
|
11701
|
-
client.release();
|
|
11702
|
-
}
|
|
11703
|
-
});
|
|
11708
|
+
async transaction(fn) {
|
|
11709
|
+
const client = await this.pool.connect();
|
|
11710
|
+
try {
|
|
11711
|
+
await client.query("BEGIN");
|
|
11712
|
+
const result = await fn(client);
|
|
11713
|
+
await client.query("COMMIT");
|
|
11714
|
+
return result;
|
|
11715
|
+
} catch (err) {
|
|
11716
|
+
await client.query("ROLLBACK");
|
|
11717
|
+
throw err;
|
|
11718
|
+
} finally {
|
|
11719
|
+
client.release();
|
|
11720
|
+
}
|
|
11704
11721
|
}
|
|
11705
11722
|
get raw() {
|
|
11706
11723
|
return this.pool;
|
|
@@ -11742,19 +11759,19 @@ program2.command("status").description("Show current cloud configuration and con
|
|
|
11742
11759
|
Checking PostgreSQL connection...`);
|
|
11743
11760
|
try {
|
|
11744
11761
|
const connStr = getConnectionString("postgres");
|
|
11745
|
-
const pg2 = new
|
|
11746
|
-
const row = pg2.get("SELECT 1 as ok");
|
|
11762
|
+
const pg2 = new PgAdapterAsync(connStr);
|
|
11763
|
+
const row = await pg2.get("SELECT 1 as ok");
|
|
11747
11764
|
if (row?.ok === 1) {
|
|
11748
11765
|
console.log("PostgreSQL: connected");
|
|
11749
11766
|
}
|
|
11750
|
-
pg2.close();
|
|
11767
|
+
await pg2.close();
|
|
11751
11768
|
} catch (err) {
|
|
11752
11769
|
console.log("PostgreSQL: connection failed \u2014", err?.message);
|
|
11753
11770
|
}
|
|
11754
11771
|
}
|
|
11755
11772
|
});
|
|
11756
11773
|
var syncCmd = program2.command("sync").description("Sync data between local and cloud");
|
|
11757
|
-
syncCmd.command("push").description("Push local data to cloud").requiredOption("--service <name>", "Service name").option("--tables <tables>", "Comma-separated table names (default: all)").action((opts) => {
|
|
11774
|
+
syncCmd.command("push").description("Push local data to cloud").requiredOption("--service <name>", "Service name").option("--tables <tables>", "Comma-separated table names (default: all)").action(async (opts) => {
|
|
11758
11775
|
const config = getCloudConfig();
|
|
11759
11776
|
if (config.mode === "local") {
|
|
11760
11777
|
console.error("Error: mode is 'local'. Run `cloud setup --mode hybrid` or `--mode cloud` first.");
|
|
@@ -11775,8 +11792,8 @@ syncCmd.command("push").description("Push local data to cloud").requiredOption("
|
|
|
11775
11792
|
}
|
|
11776
11793
|
console.log(`Pushing ${tables.length} table(s) to cloud...`);
|
|
11777
11794
|
const connStr = getConnectionString(opts.service);
|
|
11778
|
-
const cloud = new
|
|
11779
|
-
const results = syncPush(local, cloud, {
|
|
11795
|
+
const cloud = new PgAdapterAsync(connStr);
|
|
11796
|
+
const results = await syncPush(local, cloud, {
|
|
11780
11797
|
tables,
|
|
11781
11798
|
onProgress: (p) => {
|
|
11782
11799
|
if (p.phase === "done") {
|
|
@@ -11785,7 +11802,7 @@ syncCmd.command("push").description("Push local data to cloud").requiredOption("
|
|
|
11785
11802
|
}
|
|
11786
11803
|
});
|
|
11787
11804
|
local.close();
|
|
11788
|
-
cloud.close();
|
|
11805
|
+
await cloud.close();
|
|
11789
11806
|
const totalWritten = results.reduce((s, r) => s + r.rowsWritten, 0);
|
|
11790
11807
|
const totalErrors = results.reduce((s, r) => s + r.errors.length, 0);
|
|
11791
11808
|
console.log(`
|
|
@@ -11798,7 +11815,7 @@ Done. ${totalWritten} rows pushed, ${totalErrors} errors.`);
|
|
|
11798
11815
|
}
|
|
11799
11816
|
}
|
|
11800
11817
|
});
|
|
11801
|
-
syncCmd.command("pull").description("Pull cloud data to local").requiredOption("--service <name>", "Service name").option("--tables <tables>", "Comma-separated table names (default: all)").action((opts) => {
|
|
11818
|
+
syncCmd.command("pull").description("Pull cloud data to local").requiredOption("--service <name>", "Service name").option("--tables <tables>", "Comma-separated table names (default: all)").action(async (opts) => {
|
|
11802
11819
|
const config = getCloudConfig();
|
|
11803
11820
|
if (config.mode === "local") {
|
|
11804
11821
|
console.error("Error: mode is 'local'. Run `cloud setup --mode hybrid` or `--mode cloud` first.");
|
|
@@ -11807,18 +11824,17 @@ syncCmd.command("pull").description("Pull cloud data to local").requiredOption("
|
|
|
11807
11824
|
const dbPath = getDbPath2(opts.service);
|
|
11808
11825
|
const local = new SqliteAdapter2(dbPath);
|
|
11809
11826
|
const connStr = getConnectionString(opts.service);
|
|
11810
|
-
const cloud = new
|
|
11827
|
+
const cloud = new PgAdapterAsync(connStr);
|
|
11811
11828
|
let tables;
|
|
11812
11829
|
if (opts.tables) {
|
|
11813
11830
|
tables = opts.tables.split(",").map((t) => t.trim());
|
|
11814
11831
|
} else {
|
|
11815
11832
|
try {
|
|
11816
|
-
|
|
11817
|
-
tables = rows.map((r) => r.tablename);
|
|
11833
|
+
tables = await listPgTables(cloud);
|
|
11818
11834
|
} catch {
|
|
11819
11835
|
console.error("Failed to list tables from cloud.");
|
|
11820
11836
|
local.close();
|
|
11821
|
-
cloud.close();
|
|
11837
|
+
await cloud.close();
|
|
11822
11838
|
process.exit(1);
|
|
11823
11839
|
return;
|
|
11824
11840
|
}
|
|
@@ -11826,11 +11842,11 @@ syncCmd.command("pull").description("Pull cloud data to local").requiredOption("
|
|
|
11826
11842
|
if (tables.length === 0) {
|
|
11827
11843
|
console.log("No tables found to sync.");
|
|
11828
11844
|
local.close();
|
|
11829
|
-
cloud.close();
|
|
11845
|
+
await cloud.close();
|
|
11830
11846
|
return;
|
|
11831
11847
|
}
|
|
11832
11848
|
console.log(`Pulling ${tables.length} table(s) from cloud...`);
|
|
11833
|
-
const results = syncPull(
|
|
11849
|
+
const results = await syncPull(cloud, local, {
|
|
11834
11850
|
tables,
|
|
11835
11851
|
onProgress: (p) => {
|
|
11836
11852
|
if (p.phase === "done") {
|
|
@@ -11839,7 +11855,7 @@ syncCmd.command("pull").description("Pull cloud data to local").requiredOption("
|
|
|
11839
11855
|
}
|
|
11840
11856
|
});
|
|
11841
11857
|
local.close();
|
|
11842
|
-
cloud.close();
|
|
11858
|
+
await cloud.close();
|
|
11843
11859
|
const totalWritten = results.reduce((s, r) => s + r.rowsWritten, 0);
|
|
11844
11860
|
const totalErrors = results.reduce((s, r) => s + r.errors.length, 0);
|
|
11845
11861
|
console.log(`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli-helpers.d.ts","sourceRoot":"","sources":["../src/cli-helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAWzC;;;;;;;;;;;;;GAaG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,OAAO,EAChB,WAAW,EAAE,MAAM,GAClB,IAAI,
|
|
1
|
+
{"version":3,"file":"cli-helpers.d.ts","sourceRoot":"","sources":["../src/cli-helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAWzC;;;;;;;;;;;;;GAaG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,OAAO,EAChB,WAAW,EAAE,MAAM,GAClB,IAAI,CAqHN"}
|
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,10 @@ export { getCloudConfig, saveCloudConfig, getConnectionString, createDatabase, g
|
|
|
4
4
|
export { syncPush, syncPull, listSqliteTables, listPgTables, type SyncOptions, type SyncResult, type SyncProgress, type SyncProgressCallback, } from "./sync.js";
|
|
5
5
|
export { saveFeedback, sendFeedback, listFeedback, ensureFeedbackTable, type Feedback, } from "./feedback.js";
|
|
6
6
|
export { migrateDotfile, getDataDir, getDbPath, hasLegacyDotfile, getHasnaDir, } from "./dotfile.js";
|
|
7
|
+
export { SyncProgressTracker, type SyncProgressInfo, type ProgressCallback, type ResumePoint, } from "./sync-progress.js";
|
|
8
|
+
export { detectConflicts, resolveConflicts, getWinningData, ensureConflictsTable, storeConflicts, listConflicts, resolveConflict, getConflict, purgeResolvedConflicts, type SyncConflict, type ConflictStrategy, type StoredConflict, } from "./sync-conflicts.js";
|
|
9
|
+
export { incrementalSyncPush, incrementalSyncPull, ensureSyncMetaTable, getSyncMetaAll, getSyncMetaForTable, resetSyncMeta, resetAllSyncMeta, type IncrementalSyncStats, type IncrementalSyncOptions, type SyncMeta, } from "./sync-incremental.js";
|
|
10
|
+
export { setupAutoSync, enableAutoSync, getAutoSyncConfig, type AutoSyncConfig, type AutoSyncContext, type AutoSyncResult, } from "./auto-sync.js";
|
|
7
11
|
export { registerCloudTools } from "./mcp-helpers.js";
|
|
8
12
|
export { registerCloudCommands } from "./cli-helpers.js";
|
|
9
13
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,OAAO,EACL,aAAa,EACb,SAAS,EACT,cAAc,EACd,KAAK,SAAS,EACd,KAAK,iBAAiB,EACtB,KAAK,SAAS,GACf,MAAM,cAAc,CAAC;AAGtB,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,eAAe,EACf,KAAK,OAAO,GACb,MAAM,cAAc,CAAC;AAGtB,OAAO,EACL,cAAc,EACd,eAAe,EACf,mBAAmB,EACnB,cAAc,EACd,YAAY,EACZ,aAAa,EACb,iBAAiB,EACjB,KAAK,WAAW,EAChB,KAAK,qBAAqB,GAC3B,MAAM,aAAa,CAAC;AAGrB,OAAO,EACL,QAAQ,EACR,QAAQ,EACR,gBAAgB,EAChB,YAAY,EACZ,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,YAAY,EACjB,KAAK,oBAAoB,GAC1B,MAAM,WAAW,CAAC;AAGnB,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,mBAAmB,EACnB,KAAK,QAAQ,GACd,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,cAAc,EACd,UAAU,EACV,SAAS,EACT,gBAAgB,EAChB,WAAW,GACZ,MAAM,cAAc,CAAC;AAGtB,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,OAAO,EACL,aAAa,EACb,SAAS,EACT,cAAc,EACd,KAAK,SAAS,EACd,KAAK,iBAAiB,EACtB,KAAK,SAAS,GACf,MAAM,cAAc,CAAC;AAGtB,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,eAAe,EACf,KAAK,OAAO,GACb,MAAM,cAAc,CAAC;AAGtB,OAAO,EACL,cAAc,EACd,eAAe,EACf,mBAAmB,EACnB,cAAc,EACd,YAAY,EACZ,aAAa,EACb,iBAAiB,EACjB,KAAK,WAAW,EAChB,KAAK,qBAAqB,GAC3B,MAAM,aAAa,CAAC;AAGrB,OAAO,EACL,QAAQ,EACR,QAAQ,EACR,gBAAgB,EAChB,YAAY,EACZ,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,YAAY,EACjB,KAAK,oBAAoB,GAC1B,MAAM,WAAW,CAAC;AAGnB,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,mBAAmB,EACnB,KAAK,QAAQ,GACd,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,cAAc,EACd,UAAU,EACV,SAAS,EACT,gBAAgB,EAChB,WAAW,GACZ,MAAM,cAAc,CAAC;AAGtB,OAAO,EACL,mBAAmB,EACnB,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,WAAW,GACjB,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,oBAAoB,EACpB,cAAc,EACd,aAAa,EACb,eAAe,EACf,WAAW,EACX,sBAAsB,EACtB,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACrB,KAAK,cAAc,GACpB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EACL,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,cAAc,EACd,mBAAmB,EACnB,aAAa,EACb,gBAAgB,EAChB,KAAK,oBAAoB,EACzB,KAAK,sBAAsB,EAC3B,KAAK,QAAQ,GACd,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EACL,aAAa,EACb,cAAc,EACd,iBAAiB,EACjB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,cAAc,GACpB,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC"}
|