turbine-orm 0.77.1 → 0.78.0
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/README.md +13 -9
- package/dist/cjs/cli/config.d.ts +7 -1
- package/dist/cjs/cli/config.js +11 -2
- package/dist/cjs/cli/destructive.d.ts +1 -1
- package/dist/cjs/cli/destructive.js +307 -9
- package/dist/cjs/cli/index.js +252 -42
- package/dist/cjs/cli/mcp.d.ts +23 -0
- package/dist/cjs/cli/mcp.js +190 -152
- package/dist/cjs/cli/migrate.d.ts +243 -3
- package/dist/cjs/cli/migrate.js +432 -43
- package/dist/cjs/cli/sql-statements.js +27 -0
- package/dist/cjs/cli/studio.d.ts +0 -1
- package/dist/cjs/cli/studio.js +9 -7
- package/dist/cjs/client.d.ts +8 -1
- package/dist/cjs/client.js +7 -0
- package/dist/cjs/connection-url.d.ts +82 -0
- package/dist/cjs/connection-url.js +187 -1
- package/dist/cjs/errors.d.ts +112 -12
- package/dist/cjs/errors.js +558 -114
- package/dist/cjs/generate.js +47 -15
- package/dist/cjs/index.d.ts +1 -1
- package/dist/cjs/introspect.d.ts +33 -0
- package/dist/cjs/introspect.js +54 -1
- package/dist/cjs/mssql.js +21 -1
- package/dist/cjs/nested-write.js +85 -14
- package/dist/cjs/pipeline-submittable.d.ts +2 -0
- package/dist/cjs/pipeline-submittable.js +88 -3
- package/dist/cjs/pipeline.js +13 -1
- package/dist/cjs/powdb-introspect.d.ts +5 -1
- package/dist/cjs/powdb-introspect.js +5 -1
- package/dist/cjs/powql.d.ts +14 -0
- package/dist/cjs/powql.js +44 -4
- package/dist/cjs/prisma-compat.js +95 -8
- package/dist/cjs/query/aggregates.js +56 -6
- package/dist/cjs/query/builder.d.ts +76 -13
- package/dist/cjs/query/builder.js +188 -58
- package/dist/cjs/query/compound-unique.d.ts +76 -0
- package/dist/cjs/query/compound-unique.js +129 -0
- package/dist/cjs/query/index.d.ts +1 -1
- package/dist/cjs/query/types.d.ts +65 -11
- package/dist/cjs/query/where.d.ts +85 -19
- package/dist/cjs/query/where.js +262 -47
- package/dist/cjs/query/writes.d.ts +11 -2
- package/dist/cjs/query/writes.js +116 -21
- package/dist/cjs/seed.d.ts +16 -0
- package/dist/cjs/seed.js +16 -0
- package/dist/cli/config.d.ts +7 -1
- package/dist/cli/config.js +11 -2
- package/dist/cli/destructive.d.ts +1 -1
- package/dist/cli/destructive.js +307 -9
- package/dist/cli/index.js +254 -44
- package/dist/cli/mcp.d.ts +23 -0
- package/dist/cli/mcp.js +187 -150
- package/dist/cli/migrate.d.ts +243 -3
- package/dist/cli/migrate.js +423 -45
- package/dist/cli/sql-statements.js +27 -0
- package/dist/cli/studio.d.ts +0 -1
- package/dist/cli/studio.js +10 -7
- package/dist/client.d.ts +8 -1
- package/dist/client.js +7 -0
- package/dist/connection-url.d.ts +82 -0
- package/dist/connection-url.js +183 -0
- package/dist/errors.d.ts +112 -12
- package/dist/errors.js +558 -114
- package/dist/generate.js +47 -15
- package/dist/index.d.ts +1 -1
- package/dist/introspect.d.ts +33 -0
- package/dist/introspect.js +53 -1
- package/dist/mssql.js +21 -1
- package/dist/nested-write.js +85 -14
- package/dist/pipeline-submittable.d.ts +2 -0
- package/dist/pipeline-submittable.js +87 -3
- package/dist/pipeline.js +14 -2
- package/dist/powdb-introspect.d.ts +5 -1
- package/dist/powdb-introspect.js +5 -1
- package/dist/powql.d.ts +14 -0
- package/dist/powql.js +45 -5
- package/dist/prisma-compat.js +96 -9
- package/dist/query/aggregates.js +56 -6
- package/dist/query/builder.d.ts +76 -13
- package/dist/query/builder.js +188 -58
- package/dist/query/compound-unique.d.ts +76 -0
- package/dist/query/compound-unique.js +126 -1
- package/dist/query/index.d.ts +1 -1
- package/dist/query/types.d.ts +65 -11
- package/dist/query/where.d.ts +85 -19
- package/dist/query/where.js +260 -47
- package/dist/query/writes.d.ts +11 -2
- package/dist/query/writes.js +117 -22
- package/dist/seed.d.ts +16 -0
- package/dist/seed.js +16 -0
- package/package.json +3 -3
- package/skills/turbine-orm/SKILL.md +37 -10
package/dist/cjs/cli/index.js
CHANGED
|
@@ -93,6 +93,7 @@ const node_os_1 = require("node:os");
|
|
|
93
93
|
const node_path_1 = require("node:path");
|
|
94
94
|
const node_url_1 = require("node:url");
|
|
95
95
|
const connection_url_js_1 = require("../connection-url.js");
|
|
96
|
+
const errors_js_1 = require("../errors.js");
|
|
96
97
|
const generate_js_1 = require("../generate.js");
|
|
97
98
|
const index_advisor_js_1 = require("../index-advisor.js");
|
|
98
99
|
const index_stats_js_1 = require("../index-stats.js");
|
|
@@ -362,6 +363,8 @@ function parseArgs(argv = process.argv.slice(2)) {
|
|
|
362
363
|
// command instead, which is the actual problem.
|
|
363
364
|
const accepted = acceptedFlagTokens(result.command);
|
|
364
365
|
const unknownFlags = [];
|
|
366
|
+
/** The raw `--step` operand, judged after the loop. Absent means unset. */
|
|
367
|
+
let stepRaw;
|
|
365
368
|
for (; i < args.length; i++) {
|
|
366
369
|
const arg = args[i];
|
|
367
370
|
const next = args[i + 1];
|
|
@@ -400,10 +403,17 @@ function parseArgs(argv = process.argv.slice(2)) {
|
|
|
400
403
|
i++;
|
|
401
404
|
break;
|
|
402
405
|
case '--step':
|
|
403
|
-
case '-n':
|
|
404
|
-
|
|
406
|
+
case '-n': {
|
|
407
|
+
// The VALUE is captured here and JUDGED after the loop, see
|
|
408
|
+
// `stepRaw` below. `--step -1` reached `applied.reverse().slice(0, -1)`,
|
|
409
|
+
// which is "every migration except the oldest" and began tearing down
|
|
410
|
+
// the history newest-first; `--step 0` and `--step abc` (NaN) were
|
|
411
|
+
// silent no-ops that look like a successful rollback. A count of things
|
|
412
|
+
// to do is a positive integer or it is a mistake.
|
|
413
|
+
stepRaw = next ?? '';
|
|
405
414
|
i++;
|
|
406
415
|
break;
|
|
416
|
+
}
|
|
407
417
|
case '--dry-run':
|
|
408
418
|
result.dryRun = true;
|
|
409
419
|
break;
|
|
@@ -566,6 +576,21 @@ function parseArgs(argv = process.argv.slice(2)) {
|
|
|
566
576
|
// are two lines of one error rather than two runs of the CLI.
|
|
567
577
|
if (unknownFlags.length > 0)
|
|
568
578
|
failUnknownFlags(result.command, unknownFlags);
|
|
579
|
+
// Flag VALUES are judged last, and only for a command that exists. Both halves
|
|
580
|
+
// are the fix for one report: `turbine genrate --step 0` used to complain
|
|
581
|
+
// about `--step` and never mention the misspelled command, because the check
|
|
582
|
+
// ran mid-loop and threw past `main()`'s dispatch. And it threw a bare
|
|
583
|
+
// `ValidationError` rather than calling `failArg`, so it printed one unstyled
|
|
584
|
+
// sentence with no banner and no hint, which reads like an internal crash.
|
|
585
|
+
// `accepted === undefined` means the command is unknown; main() reports that,
|
|
586
|
+
// which is the actual problem.
|
|
587
|
+
if (stepRaw !== undefined && accepted !== undefined) {
|
|
588
|
+
const parsed = /^\d+$/.test(stepRaw.trim()) ? Number.parseInt(stepRaw, 10) : Number.NaN;
|
|
589
|
+
if (!Number.isSafeInteger(parsed) || parsed < 1) {
|
|
590
|
+
failArg(`${(0, ui_js_1.cyan)('--step, -n')} expects a positive whole number of migrations, got ${(0, ui_js_1.bold)(stepRaw === '' ? '(nothing)' : `"${stepRaw}"`)}.`, 'It is a COUNT of migrations to apply or roll back, so the smallest useful value is 1.', 'Example: npx turbine migrate down --step 2');
|
|
591
|
+
}
|
|
592
|
+
result.step = parsed;
|
|
593
|
+
}
|
|
569
594
|
return result;
|
|
570
595
|
}
|
|
571
596
|
// ---------------------------------------------------------------------------
|
|
@@ -1348,6 +1373,7 @@ async function runInitGenerate(config, url) {
|
|
|
1348
1373
|
const spinner = new ui_js_1.Spinner('Introspecting database').start();
|
|
1349
1374
|
try {
|
|
1350
1375
|
const schema = await (0, introspect_js_1.introspect)({
|
|
1376
|
+
keepColumnNames: config.keepColumnNames,
|
|
1351
1377
|
connectionString: url,
|
|
1352
1378
|
schema: config.schema,
|
|
1353
1379
|
include: config.include.length ? config.include : undefined,
|
|
@@ -1612,7 +1638,7 @@ async function cmdInit(args, config) {
|
|
|
1612
1638
|
}
|
|
1613
1639
|
switch (step.id) {
|
|
1614
1640
|
case 'config': {
|
|
1615
|
-
(0, node_fs_1.writeFileSync)('turbine.config.ts', (0, config_js_1.configTemplate)(args.url ?? undefined), 'utf-8');
|
|
1641
|
+
(0, node_fs_1.writeFileSync)('turbine.config.ts', (0, config_js_1.configTemplate)(args.url ?? undefined, config.schema), 'utf-8');
|
|
1616
1642
|
(0, ui_js_1.success)(state.configExists ? `Overwrote ${(0, ui_js_1.cyan)('turbine.config.ts')}` : `Created ${(0, ui_js_1.cyan)('turbine.config.ts')}`);
|
|
1617
1643
|
tsFilesWritten.push('turbine.config.ts');
|
|
1618
1644
|
// A `--url` with a password is never inlined by configTemplate, so the
|
|
@@ -1775,6 +1801,7 @@ async function cmdGenerate(args, config) {
|
|
|
1775
1801
|
const spinner = new ui_js_1.Spinner('Introspecting database schema').start();
|
|
1776
1802
|
const skippedInternalTables = [];
|
|
1777
1803
|
const schema = await (0, introspect_js_1.introspect)({
|
|
1804
|
+
keepColumnNames: config.keepColumnNames,
|
|
1778
1805
|
connectionString: url,
|
|
1779
1806
|
schema: config.schema,
|
|
1780
1807
|
include: config.include.length ? config.include : undefined,
|
|
@@ -1975,6 +2002,7 @@ async function cmdMigrateFromPrisma(args, config) {
|
|
|
1975
2002
|
(0, ui_js_1.label)('Database', (0, ui_js_1.redactUrl)(url));
|
|
1976
2003
|
const spinner = new ui_js_1.Spinner('Introspecting database schema').start();
|
|
1977
2004
|
schemaMeta = await (0, introspect_js_1.introspect)({
|
|
2005
|
+
keepColumnNames: config.keepColumnNames,
|
|
1978
2006
|
connectionString: url,
|
|
1979
2007
|
// The Postgres NAMESPACE is fixed to `public` here (`--schema` names the
|
|
1980
2008
|
// Prisma file, not the namespace).
|
|
@@ -2224,7 +2252,7 @@ async function cmdMigrate(args, config) {
|
|
|
2224
2252
|
console.log(` ${(0, ui_js_1.cyan)('--step, -n')} Number of migrations to apply/rollback`);
|
|
2225
2253
|
console.log(` ${(0, ui_js_1.cyan)('--dry-run')} Show SQL without executing`);
|
|
2226
2254
|
console.log(` ${(0, ui_js_1.cyan)('--allow-destructive')} Run data-destroying statements without prompting`);
|
|
2227
|
-
console.log(` ${(0, ui_js_1.cyan)('--allow-drift')}
|
|
2255
|
+
console.log(` ${(0, ui_js_1.cyan)('--allow-drift')} Re-baseline drifted checksums on ${(0, ui_js_1.cyan)('up')} / ${(0, ui_js_1.cyan)('deploy')} / ${(0, ui_js_1.cyan)('down')} ${(0, ui_js_1.dim)('(advanced)')}`);
|
|
2228
2256
|
(0, ui_js_1.newline)();
|
|
2229
2257
|
console.log(` ${(0, ui_js_1.bold)('Recipes')} ${(0, ui_js_1.dim)('(--recipe):')}`);
|
|
2230
2258
|
for (const [key, recipe] of Object.entries(migrate_js_1.MIGRATION_RECIPES)) {
|
|
@@ -2515,6 +2543,7 @@ async function cmdMigrateUp(args, config) {
|
|
|
2515
2543
|
(0, ui_js_1.label)('Database', (0, ui_js_1.redactUrl)(url));
|
|
2516
2544
|
(0, ui_js_1.label)('Migrations', config.migrationsDir);
|
|
2517
2545
|
(0, ui_js_1.newline)();
|
|
2546
|
+
warnIgnoredMigrationFiles(config.migrationsDir);
|
|
2518
2547
|
const allFiles = (0, migrate_js_1.listMigrationFiles)(config.migrationsDir);
|
|
2519
2548
|
if (allFiles.length === 0) {
|
|
2520
2549
|
(0, ui_js_1.warn)('No migration files found.');
|
|
@@ -2523,19 +2552,29 @@ async function cmdMigrateUp(args, config) {
|
|
|
2523
2552
|
return;
|
|
2524
2553
|
}
|
|
2525
2554
|
if (args.dryRun) {
|
|
2526
|
-
const status = await (0, migrate_js_1.migrateStatus)(url, config.migrationsDir);
|
|
2555
|
+
const status = await (0, migrate_js_1.migrateStatus)(url, config.migrationsDir, { schema: config.schema });
|
|
2527
2556
|
let pending = status.filter((st) => !st.applied).map((st) => st.file);
|
|
2528
2557
|
if (args.step != null && args.step > 0)
|
|
2529
2558
|
pending = pending.slice(0, args.step);
|
|
2559
|
+
// The same pre-flight the real run does, by calling the same two functions
|
|
2560
|
+
// rather than re-deriving their rules. They live inside `migrateUp`, which a
|
|
2561
|
+
// dry run never reaches, so `--dry-run` used to green-light a batch the real
|
|
2562
|
+
// command refuses outright: an empty-UP migration previewed as "would apply
|
|
2563
|
+
// 1 migration" and exit 0, and the deploy that followed on merge failed.
|
|
2564
|
+
// A preview that is quieter than the run it previews is not a preview.
|
|
2565
|
+
(0, migrate_js_1.assertNoEmbeddedTransactions)(pending, 'up');
|
|
2566
|
+
(0, migrate_js_1.assertUpHasStatements)(pending);
|
|
2530
2567
|
printMigrationDryRun(pending, 'up');
|
|
2531
2568
|
return;
|
|
2532
2569
|
}
|
|
2533
2570
|
// Big, loud warning when bypassing drift detection, this is a deliberately
|
|
2534
2571
|
// dangerous operation and the user should see it on every invocation.
|
|
2535
2572
|
if (args.allowDrift) {
|
|
2536
|
-
(0, ui_js_1.warn)('--allow-drift is set
|
|
2537
|
-
console.log(` ${(0, ui_js_1.dim)('
|
|
2538
|
-
console.log(` ${(0, ui_js_1.dim)(
|
|
2573
|
+
(0, ui_js_1.warn)('--allow-drift is set: a drifted migration will be RE-BASELINED, not skipped.');
|
|
2574
|
+
console.log(` ${(0, ui_js_1.dim)('Checksum validation still runs, and its result is written back to the history')}`);
|
|
2575
|
+
console.log(` ${(0, ui_js_1.dim)("table: each modified file's stored checksum becomes its current content.")}`);
|
|
2576
|
+
console.log(` ${(0, ui_js_1.dim)('The SQL that already ran is NOT re-run. Proceed only if you are')}`);
|
|
2577
|
+
console.log(` ${(0, ui_js_1.dim)('intentionally rewriting migration history.')}`);
|
|
2539
2578
|
(0, ui_js_1.newline)();
|
|
2540
2579
|
}
|
|
2541
2580
|
if (args.allowDestructive) {
|
|
@@ -2561,6 +2600,7 @@ async function cmdMigrateUp(args, config) {
|
|
|
2561
2600
|
allowDrift: args.allowDrift,
|
|
2562
2601
|
allowDestructive: args.allowDestructive,
|
|
2563
2602
|
onNoTransaction,
|
|
2603
|
+
schema: config.schema,
|
|
2564
2604
|
});
|
|
2565
2605
|
}
|
|
2566
2606
|
catch (err) {
|
|
@@ -2578,19 +2618,26 @@ async function cmdMigrateUp(args, config) {
|
|
|
2578
2618
|
allowDrift: args.allowDrift,
|
|
2579
2619
|
allowDestructive: true,
|
|
2580
2620
|
onNoTransaction,
|
|
2621
|
+
schema: config.schema,
|
|
2581
2622
|
});
|
|
2582
2623
|
}
|
|
2583
|
-
if (result.applied.length === 0 && result.errors.length === 0) {
|
|
2584
|
-
spinner.succeed('All migrations are up to date');
|
|
2585
|
-
(0, ui_js_1.newline)();
|
|
2586
|
-
return;
|
|
2587
|
-
}
|
|
2588
2624
|
if (result.applied.length > 0) {
|
|
2589
2625
|
spinner.succeed(`Applied ${(0, ui_js_1.bold)(String(result.applied.length))} migration(s)`);
|
|
2590
2626
|
for (const file of result.applied) {
|
|
2591
2627
|
console.log(` ${(0, ui_js_1.green)(ui_js_1.symbols.check)} ${file.filename}`);
|
|
2592
2628
|
}
|
|
2593
2629
|
}
|
|
2630
|
+
else if (result.errors.length === 0) {
|
|
2631
|
+
spinner.succeed('All migrations are up to date');
|
|
2632
|
+
}
|
|
2633
|
+
// Reported on EVERY path, including the one where nothing was applied. That is
|
|
2634
|
+
// `--allow-drift`'s most common case by far (you edited an already-applied
|
|
2635
|
+
// file, so nothing is pending), and it used to sit below an early return: the
|
|
2636
|
+
// stored checksum was rewritten and the terminal said everything was up to
|
|
2637
|
+
// date. A write to migration history announced as a no-op is worse than no
|
|
2638
|
+
// report at all, because the reader now believes the file and the database
|
|
2639
|
+
// still disagree.
|
|
2640
|
+
reportRebaselined(result.rebaselined);
|
|
2594
2641
|
warnOutOfOrder(result.outOfOrder);
|
|
2595
2642
|
if (result.errors.length > 0) {
|
|
2596
2643
|
spinner.fail('Migration failed');
|
|
@@ -2603,6 +2650,40 @@ async function cmdMigrateUp(args, config) {
|
|
|
2603
2650
|
}
|
|
2604
2651
|
(0, ui_js_1.newline)();
|
|
2605
2652
|
}
|
|
2653
|
+
/**
|
|
2654
|
+
* Report each applied migration whose stored checksum was re-baselined to the
|
|
2655
|
+
* file's current content, naming both hashes so the change is auditable from
|
|
2656
|
+
* the terminal output alone.
|
|
2657
|
+
*/
|
|
2658
|
+
function reportRebaselined(rebaselined) {
|
|
2659
|
+
if (rebaselined.length === 0)
|
|
2660
|
+
return;
|
|
2661
|
+
(0, ui_js_1.newline)();
|
|
2662
|
+
(0, ui_js_1.warn)(`Re-baselined ${(0, ui_js_1.bold)(String(rebaselined.length))} migration checksum(s) to the current file content:`);
|
|
2663
|
+
for (const r of rebaselined) {
|
|
2664
|
+
console.log(` ${(0, ui_js_1.yellow)(ui_js_1.symbols.arrowRight)} ${r.name}.sql ${(0, ui_js_1.dim)(`${r.from.slice(0, 12)} -> ${r.to.slice(0, 12)}`)}`);
|
|
2665
|
+
}
|
|
2666
|
+
console.log(` ${(0, ui_js_1.dim)('The history table now records what is on disk, so future runs need no --allow-drift.')}`);
|
|
2667
|
+
(0, ui_js_1.newline)();
|
|
2668
|
+
}
|
|
2669
|
+
/**
|
|
2670
|
+
* Warn about `.sql` files in the migrations directory that the runner will
|
|
2671
|
+
* never see, because their name is not `YYYYMMDDHHMMSS_<name>.sql`.
|
|
2672
|
+
*
|
|
2673
|
+
* They were skipped in silence by `status`, `up` and `deploy` alike, so a
|
|
2674
|
+
* hand-named migration simply never ran and nothing said so.
|
|
2675
|
+
*/
|
|
2676
|
+
function warnIgnoredMigrationFiles(migrationsDir) {
|
|
2677
|
+
const ignored = (0, migrate_js_1.listIgnoredSqlFiles)(migrationsDir);
|
|
2678
|
+
if (ignored.length === 0)
|
|
2679
|
+
return;
|
|
2680
|
+
(0, ui_js_1.warn)(`${(0, ui_js_1.bold)(String(ignored.length))} .sql file(s) in ${migrationsDir} are NOT migrations and will never run:`);
|
|
2681
|
+
for (const name of ignored)
|
|
2682
|
+
console.log(` ${(0, ui_js_1.yellow)(ui_js_1.symbols.dot)} ${name}`);
|
|
2683
|
+
console.log(` ${(0, ui_js_1.dim)('A migration filename must be')} ${(0, ui_js_1.cyan)('YYYYMMDDHHMMSS_name.sql')}${(0, ui_js_1.dim)('.')}`);
|
|
2684
|
+
console.log(` ${(0, ui_js_1.dim)('Rename the file(s), or create them with')} ${(0, ui_js_1.cyan)('npx turbine migrate create <name>')}${(0, ui_js_1.dim)('.')}`);
|
|
2685
|
+
(0, ui_js_1.newline)();
|
|
2686
|
+
}
|
|
2606
2687
|
/** Print a one-line warning for each migration applied out of timestamp order. */
|
|
2607
2688
|
function warnOutOfOrder(outOfOrder) {
|
|
2608
2689
|
if (outOfOrder.length === 0)
|
|
@@ -2640,8 +2721,9 @@ async function cmdMigrateDeploy(args, config) {
|
|
|
2640
2721
|
(0, ui_js_1.label)('Database', (0, ui_js_1.redactUrl)(url));
|
|
2641
2722
|
(0, ui_js_1.label)('Migrations', config.migrationsDir);
|
|
2642
2723
|
(0, ui_js_1.newline)();
|
|
2724
|
+
warnIgnoredMigrationFiles(config.migrationsDir);
|
|
2643
2725
|
const spinner = new ui_js_1.Spinner('Checking pending migrations').start();
|
|
2644
|
-
const plan = await (0, migrate_js_1.inspectMigrationDeploy)(url, config.migrationsDir);
|
|
2726
|
+
const plan = await (0, migrate_js_1.inspectMigrationDeploy)(url, config.migrationsDir, { schema: config.schema });
|
|
2645
2727
|
spinner.stop();
|
|
2646
2728
|
// Drift handling: honor --allow-drift exactly like `up`. Without it, block;
|
|
2647
2729
|
// with it, warn loudly and proceed.
|
|
@@ -2655,8 +2737,10 @@ async function cmdMigrateDeploy(args, config) {
|
|
|
2655
2737
|
(0, ui_js_1.errorLine)();
|
|
2656
2738
|
process.exit(1);
|
|
2657
2739
|
}
|
|
2658
|
-
(0, ui_js_1.warn)('--allow-drift is set:
|
|
2659
|
-
console.log(` ${(0, ui_js_1.dim)('
|
|
2740
|
+
(0, ui_js_1.warn)('--allow-drift is set: a drifted migration will be RE-BASELINED, not skipped.');
|
|
2741
|
+
console.log(` ${(0, ui_js_1.dim)('Checksum validation still runs, and its result is written back to the history')}`);
|
|
2742
|
+
console.log(` ${(0, ui_js_1.dim)("table: each modified file's stored checksum becomes its current content.")}`);
|
|
2743
|
+
console.log(` ${(0, ui_js_1.dim)('The SQL that already ran is NOT re-run.')}`);
|
|
2660
2744
|
(0, ui_js_1.newline)();
|
|
2661
2745
|
}
|
|
2662
2746
|
if (args.dryRun) {
|
|
@@ -2665,10 +2749,21 @@ async function cmdMigrateDeploy(args, config) {
|
|
|
2665
2749
|
(0, ui_js_1.newline)();
|
|
2666
2750
|
return;
|
|
2667
2751
|
}
|
|
2752
|
+
// The same pre-flight the real run does, by calling the same two functions
|
|
2753
|
+
// rather than re-deriving their rules. They live inside `migrateUp`, which a
|
|
2754
|
+
// dry run never reaches, so `--dry-run` used to green-light a batch the real
|
|
2755
|
+
// command refuses outright: an empty-UP migration previewed as "would apply
|
|
2756
|
+
// 1 migration" and exit 0, and the deploy that followed on merge failed.
|
|
2757
|
+
// A preview that is quieter than the run it previews is not a preview.
|
|
2758
|
+
(0, migrate_js_1.assertNoEmbeddedTransactions)(plan.pending, 'up');
|
|
2759
|
+
(0, migrate_js_1.assertUpHasStatements)(plan.pending);
|
|
2668
2760
|
(0, ui_js_1.info)(`${(0, ui_js_1.bold)(String(plan.pending.length))} pending migration(s)`);
|
|
2669
2761
|
for (const file of plan.pending) {
|
|
2670
2762
|
console.log(` ${(0, ui_js_1.yellow)(ui_js_1.symbols.dot)} ${file.filename}`);
|
|
2671
2763
|
}
|
|
2764
|
+
// The real deploy warns about out-of-order applies; a preview that is
|
|
2765
|
+
// quieter than the run it previews is not a preview.
|
|
2766
|
+
warnOutOfOrder(plan.outOfOrder);
|
|
2672
2767
|
// Surface destructive statements even in a dry run so CI can see them.
|
|
2673
2768
|
const destructive = (0, migrate_js_1.collectUpDestructive)(plan.pending);
|
|
2674
2769
|
if (destructive.length > 0) {
|
|
@@ -2683,18 +2778,24 @@ async function cmdMigrateDeploy(args, config) {
|
|
|
2683
2778
|
if (destructive.length > 0)
|
|
2684
2779
|
printDestructiveNotice(destructive);
|
|
2685
2780
|
const runSpinner = new ui_js_1.Spinner('Deploying migrations').start();
|
|
2686
|
-
const result = await (0, migrate_js_1.migrateDeploy)(url, config.migrationsDir, { allowDrift: args.allowDrift });
|
|
2687
|
-
if (result.applied.length === 0 && result.errors.length === 0) {
|
|
2688
|
-
runSpinner.succeed('0 applied, all migrations are up to date');
|
|
2689
|
-
(0, ui_js_1.newline)();
|
|
2690
|
-
return;
|
|
2691
|
-
}
|
|
2781
|
+
const result = await (0, migrate_js_1.migrateDeploy)(url, config.migrationsDir, { allowDrift: args.allowDrift, schema: config.schema });
|
|
2692
2782
|
if (result.applied.length > 0) {
|
|
2693
2783
|
runSpinner.succeed(`${(0, ui_js_1.bold)(String(result.applied.length))} applied`);
|
|
2694
2784
|
for (const file of result.applied) {
|
|
2695
2785
|
console.log(` ${(0, ui_js_1.green)(ui_js_1.symbols.check)} ${file.filename}`);
|
|
2696
2786
|
}
|
|
2697
2787
|
}
|
|
2788
|
+
else if (result.errors.length === 0) {
|
|
2789
|
+
runSpinner.succeed('0 applied, all migrations are up to date');
|
|
2790
|
+
}
|
|
2791
|
+
// Reported on EVERY path, including the one where nothing was applied. That is
|
|
2792
|
+
// `--allow-drift`'s most common case by far (you edited an already-applied
|
|
2793
|
+
// file, so nothing is pending), and it used to sit below an early return: the
|
|
2794
|
+
// stored checksum was rewritten and the terminal said everything was up to
|
|
2795
|
+
// date. A write to migration history announced as a no-op is worse than no
|
|
2796
|
+
// report at all, because the reader now believes the file and the database
|
|
2797
|
+
// still disagree.
|
|
2798
|
+
reportRebaselined(result.rebaselined);
|
|
2698
2799
|
warnOutOfOrder(result.outOfOrder);
|
|
2699
2800
|
if (result.errors.length > 0) {
|
|
2700
2801
|
runSpinner.fail('Deploy failed');
|
|
@@ -2752,11 +2853,24 @@ async function cmdMigrateDown(args, config) {
|
|
|
2752
2853
|
(0, ui_js_1.label)('Migrations', config.migrationsDir);
|
|
2753
2854
|
(0, ui_js_1.newline)();
|
|
2754
2855
|
if (args.dryRun) {
|
|
2755
|
-
|
|
2756
|
-
//
|
|
2757
|
-
// missing
|
|
2758
|
-
|
|
2759
|
-
|
|
2856
|
+
// Computed by the RUNNER'S rule (planMigrationRollback), not re-derived
|
|
2857
|
+
// from `status`. The old derivation sorted by filename and dropped applied
|
|
2858
|
+
// migrations whose file is missing, so with a deleted file it named the
|
|
2859
|
+
// migration AFTER the gap while the real run stopped at the gap.
|
|
2860
|
+
const plan = await (0, migrate_js_1.planMigrationRollback)(url, config.migrationsDir, {
|
|
2861
|
+
step: args.step ?? 1,
|
|
2862
|
+
schema: config.schema,
|
|
2863
|
+
// The real rollback refuses on drift unless this is set, so the preview
|
|
2864
|
+
// has to be told too: without it a deleted migration file previewed as
|
|
2865
|
+
// "would roll back 1 migration" while `migrate down` refused with E006.
|
|
2866
|
+
allowDrift: args.allowDrift,
|
|
2867
|
+
});
|
|
2868
|
+
printMigrationDryRun(plan.toRollback, 'down');
|
|
2869
|
+
if (plan.stoppedAt) {
|
|
2870
|
+
(0, ui_js_1.warn)(`Rollback would STOP at ${(0, ui_js_1.bold)(`${plan.stoppedAt.name}.sql`)}: ${plan.stoppedAt.error}`);
|
|
2871
|
+
console.log(` ${(0, ui_js_1.dim)('A rollback batch is strictly LIFO with no gaps, so nothing after it would run.')}`);
|
|
2872
|
+
(0, ui_js_1.newline)();
|
|
2873
|
+
}
|
|
2760
2874
|
return;
|
|
2761
2875
|
}
|
|
2762
2876
|
const spinner = new ui_js_1.Spinner('Rolling back migration(s)').start();
|
|
@@ -2765,6 +2879,8 @@ async function cmdMigrateDown(args, config) {
|
|
|
2765
2879
|
result = await (0, migrate_js_1.migrateDown)(url, config.migrationsDir, {
|
|
2766
2880
|
step: args.step ?? 1,
|
|
2767
2881
|
allowDestructive: args.allowDestructive,
|
|
2882
|
+
allowDrift: args.allowDrift,
|
|
2883
|
+
schema: config.schema,
|
|
2768
2884
|
});
|
|
2769
2885
|
}
|
|
2770
2886
|
catch (err) {
|
|
@@ -2780,19 +2896,27 @@ async function cmdMigrateDown(args, config) {
|
|
|
2780
2896
|
result = await (0, migrate_js_1.migrateDown)(url, config.migrationsDir, {
|
|
2781
2897
|
step: args.step ?? 1,
|
|
2782
2898
|
allowDestructive: true,
|
|
2899
|
+
allowDrift: args.allowDrift,
|
|
2900
|
+
schema: config.schema,
|
|
2783
2901
|
});
|
|
2784
2902
|
}
|
|
2785
|
-
if (result.rolledBack.length === 0 && result.errors.length === 0) {
|
|
2786
|
-
spinner.succeed('No migrations to roll back');
|
|
2787
|
-
(0, ui_js_1.newline)();
|
|
2788
|
-
return;
|
|
2789
|
-
}
|
|
2790
2903
|
if (result.rolledBack.length > 0) {
|
|
2791
2904
|
spinner.succeed(`Rolled back ${(0, ui_js_1.bold)(String(result.rolledBack.length))} migration(s)`);
|
|
2792
2905
|
for (const file of result.rolledBack) {
|
|
2793
2906
|
console.log(` ${(0, ui_js_1.yellow)(ui_js_1.symbols.arrowRight)} ${file.filename}`);
|
|
2794
2907
|
}
|
|
2795
2908
|
}
|
|
2909
|
+
else if (result.errors.length === 0) {
|
|
2910
|
+
spinner.succeed('No migrations to roll back');
|
|
2911
|
+
}
|
|
2912
|
+
// Reported on EVERY path, including the one where nothing was applied. That is
|
|
2913
|
+
// `--allow-drift`'s most common case by far (you edited an already-applied
|
|
2914
|
+
// file, so nothing is pending), and it used to sit below an early return: the
|
|
2915
|
+
// stored checksum was rewritten and the terminal said everything was up to
|
|
2916
|
+
// date. A write to migration history announced as a no-op is worse than no
|
|
2917
|
+
// report at all, because the reader now believes the file and the database
|
|
2918
|
+
// still disagree.
|
|
2919
|
+
reportRebaselined(result.rebaselined);
|
|
2796
2920
|
if (result.errors.length > 0) {
|
|
2797
2921
|
spinner.fail('Rollback failed');
|
|
2798
2922
|
for (const { file, error: msg } of result.errors) {
|
|
@@ -2817,7 +2941,8 @@ async function cmdMigrateStatus(_args, config) {
|
|
|
2817
2941
|
(0, ui_js_1.newline)();
|
|
2818
2942
|
return;
|
|
2819
2943
|
}
|
|
2820
|
-
|
|
2944
|
+
warnIgnoredMigrationFiles(config.migrationsDir);
|
|
2945
|
+
const statuses = await (0, migrate_js_1.migrateStatus)(url, config.migrationsDir, { schema: config.schema });
|
|
2821
2946
|
const appliedCount = statuses.filter((s) => s.applied).length;
|
|
2822
2947
|
const pendingCount = statuses.filter((s) => !s.applied).length;
|
|
2823
2948
|
(0, ui_js_1.info)(`${(0, ui_js_1.bold)(String(appliedCount))} applied, ${pendingCount > 0 ? (0, ui_js_1.yellow)((0, ui_js_1.bold)(String(pendingCount))) : (0, ui_js_1.bold)(String(pendingCount))} pending`);
|
|
@@ -2885,10 +3010,61 @@ function getSeedExecutionPlan(seedFile) {
|
|
|
2885
3010
|
}
|
|
2886
3011
|
throw new Error(`Unsupported seed file extension: ${ext || '(none)'}. Use seed.ts, seed.js, or seed.sql.`);
|
|
2887
3012
|
}
|
|
3013
|
+
/**
|
|
3014
|
+
* The seed's pinned connection string, refusing a configured schema that does
|
|
3015
|
+
* not exist before any seed kind runs.
|
|
3016
|
+
*
|
|
3017
|
+
* `migrate` gained that guard and `seed` did not, because the seed reached
|
|
3018
|
+
* `connectionStringForSchema` directly instead of going through the migration
|
|
3019
|
+
* runner's connect helper. So seeding a project whose schema was missing (a
|
|
3020
|
+
* typo, or a fresh database nobody had created it in) failed with a bare
|
|
3021
|
+
* `relation "..." does not exist`: no error code, no schema name, no mention of
|
|
3022
|
+
* search_path or turbine.config.ts. That is verbatim the failure the guard
|
|
3023
|
+
* exists to prevent.
|
|
3024
|
+
*
|
|
3025
|
+
* Checked ONCE here rather than in each of the three seed kinds, because that
|
|
3026
|
+
* is the only place all three still share: the `.sql` kind opens its own client
|
|
3027
|
+
* below, while the `.ts` and `.js` kinds connect somewhere this process cannot
|
|
3028
|
+
* reach (a `tsx` CHILD PROCESS, and the user's own `defineSeed` callback), both
|
|
3029
|
+
* of them through the `DATABASE_URL` this function returns.
|
|
3030
|
+
*
|
|
3031
|
+
* The probe also reads the connection's own `search_path`, so the pin EXTENDS
|
|
3032
|
+
* it rather than replacing it, exactly as `migrate` does. Without that, seeding
|
|
3033
|
+
* into a pinned schema stops resolving extension types and functions that the
|
|
3034
|
+
* unpinned connection could see.
|
|
3035
|
+
*/
|
|
3036
|
+
async function seedConnectionString(config) {
|
|
3037
|
+
const schema = config.schema;
|
|
3038
|
+
if (schema === undefined || schema === '')
|
|
3039
|
+
return config.url;
|
|
3040
|
+
// Before the connection: an unpinnable NAME is not a missing schema.
|
|
3041
|
+
(0, migrate_js_1.assertPinnableSchema)(schema);
|
|
3042
|
+
const { default: pg } = await Promise.resolve().then(() => __importStar(require('pg')));
|
|
3043
|
+
const probe = new pg.Client({ connectionString: config.url });
|
|
3044
|
+
await probe.connect();
|
|
3045
|
+
let inherited;
|
|
3046
|
+
try {
|
|
3047
|
+
await (0, migrate_js_1.assertSchemaExists)(probe, schema);
|
|
3048
|
+
const shown = await probe.query('SHOW search_path');
|
|
3049
|
+
inherited = (0, connection_url_js_1.parseSearchPathValue)(shown.rows[0]?.search_path ?? '');
|
|
3050
|
+
}
|
|
3051
|
+
finally {
|
|
3052
|
+
await probe.end();
|
|
3053
|
+
}
|
|
3054
|
+
return (0, migrate_js_1.connectionStringForSchema)(config.url, schema, inherited);
|
|
3055
|
+
}
|
|
2888
3056
|
async function runSeedPlan(plan, config) {
|
|
2889
3057
|
const oldDatabaseUrl = process.env.DATABASE_URL;
|
|
2890
|
-
|
|
2891
|
-
|
|
3058
|
+
// The seed connects through DATABASE_URL, whichever way it runs: `defineSeed`
|
|
3059
|
+
// reads it in the tsx child and in the in-process `import()`, and the `.sql`
|
|
3060
|
+
// path opens a client on it below. So the configured schema is applied HERE,
|
|
3061
|
+
// once, as the `options=-c search_path` connection parameter (never a `SET`),
|
|
3062
|
+
// and every seed kind inherits it. Without this the seed ran against the
|
|
3063
|
+
// role's default path while `push` and the generated client used
|
|
3064
|
+
// `config.schema`, so the two halves of one project wrote to two namespaces.
|
|
3065
|
+
const seedUrl = config.url ? await seedConnectionString(config) : undefined;
|
|
3066
|
+
if (seedUrl)
|
|
3067
|
+
process.env.DATABASE_URL = seedUrl;
|
|
2892
3068
|
try {
|
|
2893
3069
|
if (plan.kind === 'tsx') {
|
|
2894
3070
|
if (!(0, loader_js_1.canResolveTsx)()) {
|
|
@@ -2907,7 +3083,7 @@ async function runSeedPlan(plan, config) {
|
|
|
2907
3083
|
stdio: 'inherit',
|
|
2908
3084
|
env: {
|
|
2909
3085
|
...process.env,
|
|
2910
|
-
DATABASE_URL:
|
|
3086
|
+
DATABASE_URL: seedUrl || process.env.DATABASE_URL,
|
|
2911
3087
|
TURBINE_SEED_SENTINEL: sentinel,
|
|
2912
3088
|
},
|
|
2913
3089
|
});
|
|
@@ -2930,7 +3106,10 @@ async function runSeedPlan(plan, config) {
|
|
|
2930
3106
|
await mod.default();
|
|
2931
3107
|
return;
|
|
2932
3108
|
}
|
|
2933
|
-
|
|
3109
|
+
// `seedUrl` is already pinned and already schema-checked; `requireUrl` is
|
|
3110
|
+
// only reached when no url was configured at all, in which case there is no
|
|
3111
|
+
// schema to pin either.
|
|
3112
|
+
const url = seedUrl ?? requireUrl(config);
|
|
2934
3113
|
const { default: pg } = await Promise.resolve().then(() => __importStar(require('pg')));
|
|
2935
3114
|
const client = new pg.Client({ connectionString: url });
|
|
2936
3115
|
await client.connect();
|
|
@@ -2990,6 +3169,7 @@ async function cmdStatus(_args, config) {
|
|
|
2990
3169
|
(0, ui_js_1.newline)();
|
|
2991
3170
|
const spinner = new ui_js_1.Spinner('Introspecting database').start();
|
|
2992
3171
|
const schema = await (0, introspect_js_1.introspect)({
|
|
3172
|
+
keepColumnNames: config.keepColumnNames,
|
|
2993
3173
|
connectionString: url,
|
|
2994
3174
|
schema: config.schema,
|
|
2995
3175
|
include: config.include.length ? config.include : undefined,
|
|
@@ -3135,6 +3315,7 @@ async function cmdDoctor(args, config) {
|
|
|
3135
3315
|
}
|
|
3136
3316
|
const spinner = jsonMode ? null : new ui_js_1.Spinner('Introspecting database').start();
|
|
3137
3317
|
const schema = await (0, introspect_js_1.introspect)({
|
|
3318
|
+
keepColumnNames: config.keepColumnNames,
|
|
3138
3319
|
connectionString: url,
|
|
3139
3320
|
schema: config.schema,
|
|
3140
3321
|
include: config.include.length ? config.include : undefined,
|
|
@@ -4141,6 +4322,7 @@ function showInitHelp() {
|
|
|
4141
4322
|
console.log(` ${(0, ui_js_1.cyan)('--skip-seed')} Don't create the seed file or run the seed`);
|
|
4142
4323
|
console.log(` ${(0, ui_js_1.cyan)('--skip-push')} Don't push the schema to the database`);
|
|
4143
4324
|
console.log(` ${(0, ui_js_1.cyan)('--skip-generate')} Don't generate the typed client`);
|
|
4325
|
+
console.log(` ${(0, ui_js_1.cyan)('--schema, -s')} ${(0, ui_js_1.dim)('<name>')} Postgres schema to write into config ${(0, ui_js_1.dim)('(default: public)')}`);
|
|
4144
4326
|
(0, ui_js_1.newline)();
|
|
4145
4327
|
}
|
|
4146
4328
|
function showGenerateHelp() {
|
|
@@ -4248,8 +4430,9 @@ function showMigrateHelp() {
|
|
|
4248
4430
|
console.log(` ${(0, ui_js_1.cyan)('--recipe')} ${(0, ui_js_1.dim)('<name>')} Scaffold a sanctioned migration pattern ${(0, ui_js_1.dim)('(create only, e.g. backfill)')}`);
|
|
4249
4431
|
console.log(` ${(0, ui_js_1.cyan)('--step, -n')} ${(0, ui_js_1.dim)('<N>')} Number of migrations to apply/rollback`);
|
|
4250
4432
|
console.log(` ${(0, ui_js_1.cyan)('--dry-run')} Show SQL without executing`);
|
|
4251
|
-
console.log(` ${(0, ui_js_1.cyan)('--allow-drift')}
|
|
4433
|
+
console.log(` ${(0, ui_js_1.cyan)('--allow-drift')} Re-baseline drifted checksums, REWRITING history ${(0, ui_js_1.dim)('(up / deploy / down)')}`);
|
|
4252
4434
|
console.log(` ${(0, ui_js_1.cyan)('--allow-destructive')} Run data-destroying migration statements without the interactive confirm`);
|
|
4435
|
+
console.log(` ${(0, ui_js_1.cyan)('--schema, -s')} ${(0, ui_js_1.dim)('<name>')} Postgres schema the migrations run in ${(0, ui_js_1.dim)('(default: public)')}`);
|
|
4253
4436
|
console.log(` ${(0, ui_js_1.cyan)('--verbose, -v')} Show detailed output`);
|
|
4254
4437
|
(0, ui_js_1.newline)();
|
|
4255
4438
|
console.log(` ${(0, ui_js_1.bold)('Examples:')}`);
|
|
@@ -4276,7 +4459,8 @@ function showSeedHelp() {
|
|
|
4276
4459
|
console.log(` ${(0, ui_js_1.dim)('TypeScript seeds run with')} ${(0, ui_js_1.cyan)('npx tsx')} ${(0, ui_js_1.dim)('and can export')} ${(0, ui_js_1.cyan)('defineSeed(fn)')}${(0, ui_js_1.dim)('.')}`);
|
|
4277
4460
|
(0, ui_js_1.newline)();
|
|
4278
4461
|
console.log(` ${(0, ui_js_1.bold)('Options:')}`);
|
|
4279
|
-
console.log(` ${(0, ui_js_1.cyan)('--url, -u')} ${(0, ui_js_1.dim)('<url>')}
|
|
4462
|
+
console.log(` ${(0, ui_js_1.cyan)('--url, -u')} ${(0, ui_js_1.dim)('<url>')} Postgres connection string`);
|
|
4463
|
+
console.log(` ${(0, ui_js_1.cyan)('--schema, -s')} ${(0, ui_js_1.dim)('<name>')} Postgres schema the seed writes into ${(0, ui_js_1.dim)('(default: public)')}`);
|
|
4280
4464
|
(0, ui_js_1.newline)();
|
|
4281
4465
|
}
|
|
4282
4466
|
function showStatusHelp() {
|
|
@@ -4484,7 +4668,7 @@ function showHelp() {
|
|
|
4484
4668
|
console.log(` ${(0, ui_js_1.cyan)('--recipe')} ${(0, ui_js_1.dim)('<name>')} Scaffold a named migration recipe, e.g. backfill ${(0, ui_js_1.dim)('(create)')}`);
|
|
4485
4669
|
console.log(` ${(0, ui_js_1.cyan)('--step, -n')} ${(0, ui_js_1.dim)('<N>')} Number of migrations to apply/rollback`);
|
|
4486
4670
|
console.log(` ${(0, ui_js_1.cyan)('--allow-destructive')} Run data-destroying statements without prompting ${(0, ui_js_1.dim)('(up/down/push)')}`);
|
|
4487
|
-
console.log(` ${(0, ui_js_1.cyan)('--allow-drift')}
|
|
4671
|
+
console.log(` ${(0, ui_js_1.cyan)('--allow-drift')} Re-baseline drifted checksums on ${(0, ui_js_1.cyan)('migrate up')} / ${(0, ui_js_1.cyan)('deploy')} / ${(0, ui_js_1.cyan)('down')} ${(0, ui_js_1.dim)('(advanced)')}`);
|
|
4488
4672
|
(0, ui_js_1.newline)();
|
|
4489
4673
|
console.log(` ${(0, ui_js_1.bold)('Init options:')}`);
|
|
4490
4674
|
console.log(` ${(0, ui_js_1.cyan)('--yes, -y')} Accept every step's default (non-interactive)`);
|
|
@@ -4656,7 +4840,20 @@ function showVersion() {
|
|
|
4656
4840
|
// Main
|
|
4657
4841
|
// ---------------------------------------------------------------------------
|
|
4658
4842
|
async function main() {
|
|
4659
|
-
|
|
4843
|
+
let args;
|
|
4844
|
+
try {
|
|
4845
|
+
args = parseArgs();
|
|
4846
|
+
}
|
|
4847
|
+
catch (err) {
|
|
4848
|
+
// A last-resort net. Every flag refusal parseArgs makes goes through
|
|
4849
|
+
// `failArg` (banner, red line, hints) and exits there, so nothing is
|
|
4850
|
+
// expected here; anything that does arrive is a bug in the parser rather
|
|
4851
|
+
// than in the caller's command line, and printing it beats swallowing it.
|
|
4852
|
+
(0, ui_js_1.errorLine)();
|
|
4853
|
+
(0, ui_js_1.error)(err instanceof Error ? err.message : String(err));
|
|
4854
|
+
(0, ui_js_1.errorLine)();
|
|
4855
|
+
process.exit(1);
|
|
4856
|
+
}
|
|
4660
4857
|
// Quick exits that don't need config
|
|
4661
4858
|
if (args.command === 'help' || args.command === '--help' || args.command === '-h') {
|
|
4662
4859
|
showHelp();
|
|
@@ -4786,7 +4983,20 @@ async function main() {
|
|
|
4786
4983
|
}
|
|
4787
4984
|
catch (err) {
|
|
4788
4985
|
if (err instanceof Error) {
|
|
4789
|
-
|
|
4986
|
+
// TYPE first, message second. These branches sniff for substrings, and
|
|
4987
|
+
// `ValidationError` beat them by accident: a refused `--schema` value says
|
|
4988
|
+
// "used as a connection parameter", which contains "connection", so a bad
|
|
4989
|
+
// flag value was reported under "Could not connect to database" with three
|
|
4990
|
+
// firewall hints. A typed error already knows what it is.
|
|
4991
|
+
if (err instanceof errors_js_1.ValidationError) {
|
|
4992
|
+
(0, ui_js_1.errorLine)();
|
|
4993
|
+
(0, ui_js_1.error)((0, ui_js_1.redactUrl)(err.message));
|
|
4994
|
+
if (args.verbose && err.stack) {
|
|
4995
|
+
(0, ui_js_1.errorLine)();
|
|
4996
|
+
console.error((0, ui_js_1.dim)((0, ui_js_1.redactUrl)(err.stack)));
|
|
4997
|
+
}
|
|
4998
|
+
}
|
|
4999
|
+
else if (err.message.includes('ECONNREFUSED') || err.message.includes('connection')) {
|
|
4790
5000
|
(0, ui_js_1.errorLine)();
|
|
4791
5001
|
(0, ui_js_1.error)(`Could not connect to database`);
|
|
4792
5002
|
(0, ui_js_1.errorLine)(`${(0, ui_js_1.dim)((0, ui_js_1.redactUrl)(err.message))}`);
|
package/dist/cjs/cli/mcp.d.ts
CHANGED
|
@@ -26,9 +26,32 @@ export interface McpTransport {
|
|
|
26
26
|
*/
|
|
27
27
|
pool?: PgCompatPool;
|
|
28
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* Why a session ended: an explicit {@link McpServerHandle.dispose} (a signal,
|
|
31
|
+
* stdin closing), or the server closing it ITSELF because the peer's framing
|
|
32
|
+
* was lost. `runMcpServer` exits non-zero on the latter so a supervisor
|
|
33
|
+
* restarts the server rather than assuming a clean end.
|
|
34
|
+
*/
|
|
35
|
+
export type McpCloseReason = 'disposed' | 'framing-lost';
|
|
29
36
|
export interface McpServerHandle {
|
|
30
37
|
dispose(): Promise<void>;
|
|
38
|
+
/**
|
|
39
|
+
* Resolves once the session has ended and the pool is closed, by either
|
|
40
|
+
* path. After the unframed-buffer refusal the server used to detach its
|
|
41
|
+
* reader and then neither exit nor answer: a live process that would never
|
|
42
|
+
* speak again, which a supervisor cannot tell from a healthy idle one.
|
|
43
|
+
*/
|
|
44
|
+
closed: Promise<McpCloseReason>;
|
|
31
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* The most bytes the stdio reader will hold WITHOUT seeing a newline.
|
|
48
|
+
*
|
|
49
|
+
* 8 MiB, which is far above any real request: the largest thing a client sends
|
|
50
|
+
* here is a `compile_query` args object, and the tool schemas cap what can
|
|
51
|
+
* meaningfully be in one. It is a liveness bound, not a policy: see the check
|
|
52
|
+
* itself for why an over-long line ends the session instead of being truncated.
|
|
53
|
+
*/
|
|
54
|
+
export declare const MAX_STDIO_BUFFER_BYTES: number;
|
|
32
55
|
export declare function startMcpServer(options: McpServerOptions, transport?: McpTransport): McpServerHandle;
|
|
33
56
|
/**
|
|
34
57
|
* Every SHORTEST relation chain from `from` to `to`, in deterministic order.
|