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/cli/index.js
CHANGED
|
@@ -29,7 +29,8 @@ import { appendFileSync, existsSync, mkdirSync, mkdtempSync, readdirSync, readFi
|
|
|
29
29
|
import { tmpdir } from 'node:os';
|
|
30
30
|
import { basename, dirname, extname, join, relative, resolve, sep } from 'node:path';
|
|
31
31
|
import { pathToFileURL } from 'node:url';
|
|
32
|
-
import { detectPooler, poolerRefusalMessage } from '../connection-url.js';
|
|
32
|
+
import { detectPooler, parseSearchPathValue, poolerRefusalMessage } from '../connection-url.js';
|
|
33
|
+
import { ValidationError } from '../errors.js';
|
|
33
34
|
import { generate, generatePrismaMap, resolveImportExtension } from '../generate.js';
|
|
34
35
|
import { buildCreateIndexSql, buildCreateIndexStatements, buildDropIndexSql, collectDoctorProbeIndexNames, collectRelationProbeColumns, findMissingRelationIndexes, } from '../index-advisor.js';
|
|
35
36
|
import { auditDoctorIndexes, collectStatsSnapshot, collectTableHeat, findInvalidIndexes, findRedundantIndexes, findUnusedIndexes, formatBytes, isSnapshotUsable, STATS_THRESHOLDS, scoreMissingIndex, } from '../index-stats.js';
|
|
@@ -44,7 +45,7 @@ import { configTemplate, connectionStringHasPassword, DEFAULT_INIT_SEED_FILE, fi
|
|
|
44
45
|
import { DESTRUCTIVE_KIND_LABEL } from './destructive.js';
|
|
45
46
|
import { canResolveTsx, getTsLoaderError, needsTsLoader, registerTsLoader } from './loader.js';
|
|
46
47
|
import { runMcpServer } from './mcp.js';
|
|
47
|
-
import { buildDiffMigrationBody, collectUpDestructive, createMigration, formatChecksumMismatchError, inspectMigrationDeploy, listMigrationFiles, MIGRATION_RECIPES, migrateDeploy, migrateDown, migrateStatus, migrateUp, parseMigrationContent, } from './migrate.js';
|
|
48
|
+
import { assertNoEmbeddedTransactions, assertPinnableSchema, assertSchemaExists, assertUpHasStatements, buildDiffMigrationBody, collectUpDestructive, connectionStringForSchema, createMigration, formatChecksumMismatchError, inspectMigrationDeploy, listIgnoredSqlFiles, listMigrationFiles, MIGRATION_RECIPES, migrateDeploy, migrateDown, migrateStatus, migrateUp, parseMigrationContent, planMigrationRollback, } from './migrate.js';
|
|
48
49
|
import { startObserve } from './observe.js';
|
|
49
50
|
import { formatPrismaReport, summaryLines } from './prisma-report.js';
|
|
50
51
|
import { DEFAULT_EXCLUDED_TABLES, resolvePrismaSchema } from './prisma-resolve.js';
|
|
@@ -299,6 +300,8 @@ export function parseArgs(argv = process.argv.slice(2)) {
|
|
|
299
300
|
// command instead, which is the actual problem.
|
|
300
301
|
const accepted = acceptedFlagTokens(result.command);
|
|
301
302
|
const unknownFlags = [];
|
|
303
|
+
/** The raw `--step` operand, judged after the loop. Absent means unset. */
|
|
304
|
+
let stepRaw;
|
|
302
305
|
for (; i < args.length; i++) {
|
|
303
306
|
const arg = args[i];
|
|
304
307
|
const next = args[i + 1];
|
|
@@ -337,10 +340,17 @@ export function parseArgs(argv = process.argv.slice(2)) {
|
|
|
337
340
|
i++;
|
|
338
341
|
break;
|
|
339
342
|
case '--step':
|
|
340
|
-
case '-n':
|
|
341
|
-
|
|
343
|
+
case '-n': {
|
|
344
|
+
// The VALUE is captured here and JUDGED after the loop, see
|
|
345
|
+
// `stepRaw` below. `--step -1` reached `applied.reverse().slice(0, -1)`,
|
|
346
|
+
// which is "every migration except the oldest" and began tearing down
|
|
347
|
+
// the history newest-first; `--step 0` and `--step abc` (NaN) were
|
|
348
|
+
// silent no-ops that look like a successful rollback. A count of things
|
|
349
|
+
// to do is a positive integer or it is a mistake.
|
|
350
|
+
stepRaw = next ?? '';
|
|
342
351
|
i++;
|
|
343
352
|
break;
|
|
353
|
+
}
|
|
344
354
|
case '--dry-run':
|
|
345
355
|
result.dryRun = true;
|
|
346
356
|
break;
|
|
@@ -503,6 +513,21 @@ export function parseArgs(argv = process.argv.slice(2)) {
|
|
|
503
513
|
// are two lines of one error rather than two runs of the CLI.
|
|
504
514
|
if (unknownFlags.length > 0)
|
|
505
515
|
failUnknownFlags(result.command, unknownFlags);
|
|
516
|
+
// Flag VALUES are judged last, and only for a command that exists. Both halves
|
|
517
|
+
// are the fix for one report: `turbine genrate --step 0` used to complain
|
|
518
|
+
// about `--step` and never mention the misspelled command, because the check
|
|
519
|
+
// ran mid-loop and threw past `main()`'s dispatch. And it threw a bare
|
|
520
|
+
// `ValidationError` rather than calling `failArg`, so it printed one unstyled
|
|
521
|
+
// sentence with no banner and no hint, which reads like an internal crash.
|
|
522
|
+
// `accepted === undefined` means the command is unknown; main() reports that,
|
|
523
|
+
// which is the actual problem.
|
|
524
|
+
if (stepRaw !== undefined && accepted !== undefined) {
|
|
525
|
+
const parsed = /^\d+$/.test(stepRaw.trim()) ? Number.parseInt(stepRaw, 10) : Number.NaN;
|
|
526
|
+
if (!Number.isSafeInteger(parsed) || parsed < 1) {
|
|
527
|
+
failArg(`${cyan('--step, -n')} expects a positive whole number of migrations, got ${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');
|
|
528
|
+
}
|
|
529
|
+
result.step = parsed;
|
|
530
|
+
}
|
|
506
531
|
return result;
|
|
507
532
|
}
|
|
508
533
|
// ---------------------------------------------------------------------------
|
|
@@ -1285,6 +1310,7 @@ async function runInitGenerate(config, url) {
|
|
|
1285
1310
|
const spinner = new Spinner('Introspecting database').start();
|
|
1286
1311
|
try {
|
|
1287
1312
|
const schema = await introspect({
|
|
1313
|
+
keepColumnNames: config.keepColumnNames,
|
|
1288
1314
|
connectionString: url,
|
|
1289
1315
|
schema: config.schema,
|
|
1290
1316
|
include: config.include.length ? config.include : undefined,
|
|
@@ -1549,7 +1575,7 @@ async function cmdInit(args, config) {
|
|
|
1549
1575
|
}
|
|
1550
1576
|
switch (step.id) {
|
|
1551
1577
|
case 'config': {
|
|
1552
|
-
writeFileSync('turbine.config.ts', configTemplate(args.url ?? undefined), 'utf-8');
|
|
1578
|
+
writeFileSync('turbine.config.ts', configTemplate(args.url ?? undefined, config.schema), 'utf-8');
|
|
1553
1579
|
success(state.configExists ? `Overwrote ${cyan('turbine.config.ts')}` : `Created ${cyan('turbine.config.ts')}`);
|
|
1554
1580
|
tsFilesWritten.push('turbine.config.ts');
|
|
1555
1581
|
// A `--url` with a password is never inlined by configTemplate, so the
|
|
@@ -1712,6 +1738,7 @@ async function cmdGenerate(args, config) {
|
|
|
1712
1738
|
const spinner = new Spinner('Introspecting database schema').start();
|
|
1713
1739
|
const skippedInternalTables = [];
|
|
1714
1740
|
const schema = await introspect({
|
|
1741
|
+
keepColumnNames: config.keepColumnNames,
|
|
1715
1742
|
connectionString: url,
|
|
1716
1743
|
schema: config.schema,
|
|
1717
1744
|
include: config.include.length ? config.include : undefined,
|
|
@@ -1912,6 +1939,7 @@ async function cmdMigrateFromPrisma(args, config) {
|
|
|
1912
1939
|
label('Database', redactUrl(url));
|
|
1913
1940
|
const spinner = new Spinner('Introspecting database schema').start();
|
|
1914
1941
|
schemaMeta = await introspect({
|
|
1942
|
+
keepColumnNames: config.keepColumnNames,
|
|
1915
1943
|
connectionString: url,
|
|
1916
1944
|
// The Postgres NAMESPACE is fixed to `public` here (`--schema` names the
|
|
1917
1945
|
// Prisma file, not the namespace).
|
|
@@ -2161,7 +2189,7 @@ async function cmdMigrate(args, config) {
|
|
|
2161
2189
|
console.log(` ${cyan('--step, -n')} Number of migrations to apply/rollback`);
|
|
2162
2190
|
console.log(` ${cyan('--dry-run')} Show SQL without executing`);
|
|
2163
2191
|
console.log(` ${cyan('--allow-destructive')} Run data-destroying statements without prompting`);
|
|
2164
|
-
console.log(` ${cyan('--allow-drift')}
|
|
2192
|
+
console.log(` ${cyan('--allow-drift')} Re-baseline drifted checksums on ${cyan('up')} / ${cyan('deploy')} / ${cyan('down')} ${dim('(advanced)')}`);
|
|
2165
2193
|
newline();
|
|
2166
2194
|
console.log(` ${bold('Recipes')} ${dim('(--recipe):')}`);
|
|
2167
2195
|
for (const [key, recipe] of Object.entries(MIGRATION_RECIPES)) {
|
|
@@ -2452,6 +2480,7 @@ async function cmdMigrateUp(args, config) {
|
|
|
2452
2480
|
label('Database', redactUrl(url));
|
|
2453
2481
|
label('Migrations', config.migrationsDir);
|
|
2454
2482
|
newline();
|
|
2483
|
+
warnIgnoredMigrationFiles(config.migrationsDir);
|
|
2455
2484
|
const allFiles = listMigrationFiles(config.migrationsDir);
|
|
2456
2485
|
if (allFiles.length === 0) {
|
|
2457
2486
|
warn('No migration files found.');
|
|
@@ -2460,19 +2489,29 @@ async function cmdMigrateUp(args, config) {
|
|
|
2460
2489
|
return;
|
|
2461
2490
|
}
|
|
2462
2491
|
if (args.dryRun) {
|
|
2463
|
-
const status = await migrateStatus(url, config.migrationsDir);
|
|
2492
|
+
const status = await migrateStatus(url, config.migrationsDir, { schema: config.schema });
|
|
2464
2493
|
let pending = status.filter((st) => !st.applied).map((st) => st.file);
|
|
2465
2494
|
if (args.step != null && args.step > 0)
|
|
2466
2495
|
pending = pending.slice(0, args.step);
|
|
2496
|
+
// The same pre-flight the real run does, by calling the same two functions
|
|
2497
|
+
// rather than re-deriving their rules. They live inside `migrateUp`, which a
|
|
2498
|
+
// dry run never reaches, so `--dry-run` used to green-light a batch the real
|
|
2499
|
+
// command refuses outright: an empty-UP migration previewed as "would apply
|
|
2500
|
+
// 1 migration" and exit 0, and the deploy that followed on merge failed.
|
|
2501
|
+
// A preview that is quieter than the run it previews is not a preview.
|
|
2502
|
+
assertNoEmbeddedTransactions(pending, 'up');
|
|
2503
|
+
assertUpHasStatements(pending);
|
|
2467
2504
|
printMigrationDryRun(pending, 'up');
|
|
2468
2505
|
return;
|
|
2469
2506
|
}
|
|
2470
2507
|
// Big, loud warning when bypassing drift detection, this is a deliberately
|
|
2471
2508
|
// dangerous operation and the user should see it on every invocation.
|
|
2472
2509
|
if (args.allowDrift) {
|
|
2473
|
-
warn('--allow-drift is set
|
|
2474
|
-
console.log(` ${dim('
|
|
2475
|
-
console.log(` ${dim(
|
|
2510
|
+
warn('--allow-drift is set: a drifted migration will be RE-BASELINED, not skipped.');
|
|
2511
|
+
console.log(` ${dim('Checksum validation still runs, and its result is written back to the history')}`);
|
|
2512
|
+
console.log(` ${dim("table: each modified file's stored checksum becomes its current content.")}`);
|
|
2513
|
+
console.log(` ${dim('The SQL that already ran is NOT re-run. Proceed only if you are')}`);
|
|
2514
|
+
console.log(` ${dim('intentionally rewriting migration history.')}`);
|
|
2476
2515
|
newline();
|
|
2477
2516
|
}
|
|
2478
2517
|
if (args.allowDestructive) {
|
|
@@ -2498,6 +2537,7 @@ async function cmdMigrateUp(args, config) {
|
|
|
2498
2537
|
allowDrift: args.allowDrift,
|
|
2499
2538
|
allowDestructive: args.allowDestructive,
|
|
2500
2539
|
onNoTransaction,
|
|
2540
|
+
schema: config.schema,
|
|
2501
2541
|
});
|
|
2502
2542
|
}
|
|
2503
2543
|
catch (err) {
|
|
@@ -2515,19 +2555,26 @@ async function cmdMigrateUp(args, config) {
|
|
|
2515
2555
|
allowDrift: args.allowDrift,
|
|
2516
2556
|
allowDestructive: true,
|
|
2517
2557
|
onNoTransaction,
|
|
2558
|
+
schema: config.schema,
|
|
2518
2559
|
});
|
|
2519
2560
|
}
|
|
2520
|
-
if (result.applied.length === 0 && result.errors.length === 0) {
|
|
2521
|
-
spinner.succeed('All migrations are up to date');
|
|
2522
|
-
newline();
|
|
2523
|
-
return;
|
|
2524
|
-
}
|
|
2525
2561
|
if (result.applied.length > 0) {
|
|
2526
2562
|
spinner.succeed(`Applied ${bold(String(result.applied.length))} migration(s)`);
|
|
2527
2563
|
for (const file of result.applied) {
|
|
2528
2564
|
console.log(` ${green(symbols.check)} ${file.filename}`);
|
|
2529
2565
|
}
|
|
2530
2566
|
}
|
|
2567
|
+
else if (result.errors.length === 0) {
|
|
2568
|
+
spinner.succeed('All migrations are up to date');
|
|
2569
|
+
}
|
|
2570
|
+
// Reported on EVERY path, including the one where nothing was applied. That is
|
|
2571
|
+
// `--allow-drift`'s most common case by far (you edited an already-applied
|
|
2572
|
+
// file, so nothing is pending), and it used to sit below an early return: the
|
|
2573
|
+
// stored checksum was rewritten and the terminal said everything was up to
|
|
2574
|
+
// date. A write to migration history announced as a no-op is worse than no
|
|
2575
|
+
// report at all, because the reader now believes the file and the database
|
|
2576
|
+
// still disagree.
|
|
2577
|
+
reportRebaselined(result.rebaselined);
|
|
2531
2578
|
warnOutOfOrder(result.outOfOrder);
|
|
2532
2579
|
if (result.errors.length > 0) {
|
|
2533
2580
|
spinner.fail('Migration failed');
|
|
@@ -2540,6 +2587,40 @@ async function cmdMigrateUp(args, config) {
|
|
|
2540
2587
|
}
|
|
2541
2588
|
newline();
|
|
2542
2589
|
}
|
|
2590
|
+
/**
|
|
2591
|
+
* Report each applied migration whose stored checksum was re-baselined to the
|
|
2592
|
+
* file's current content, naming both hashes so the change is auditable from
|
|
2593
|
+
* the terminal output alone.
|
|
2594
|
+
*/
|
|
2595
|
+
function reportRebaselined(rebaselined) {
|
|
2596
|
+
if (rebaselined.length === 0)
|
|
2597
|
+
return;
|
|
2598
|
+
newline();
|
|
2599
|
+
warn(`Re-baselined ${bold(String(rebaselined.length))} migration checksum(s) to the current file content:`);
|
|
2600
|
+
for (const r of rebaselined) {
|
|
2601
|
+
console.log(` ${yellow(symbols.arrowRight)} ${r.name}.sql ${dim(`${r.from.slice(0, 12)} -> ${r.to.slice(0, 12)}`)}`);
|
|
2602
|
+
}
|
|
2603
|
+
console.log(` ${dim('The history table now records what is on disk, so future runs need no --allow-drift.')}`);
|
|
2604
|
+
newline();
|
|
2605
|
+
}
|
|
2606
|
+
/**
|
|
2607
|
+
* Warn about `.sql` files in the migrations directory that the runner will
|
|
2608
|
+
* never see, because their name is not `YYYYMMDDHHMMSS_<name>.sql`.
|
|
2609
|
+
*
|
|
2610
|
+
* They were skipped in silence by `status`, `up` and `deploy` alike, so a
|
|
2611
|
+
* hand-named migration simply never ran and nothing said so.
|
|
2612
|
+
*/
|
|
2613
|
+
function warnIgnoredMigrationFiles(migrationsDir) {
|
|
2614
|
+
const ignored = listIgnoredSqlFiles(migrationsDir);
|
|
2615
|
+
if (ignored.length === 0)
|
|
2616
|
+
return;
|
|
2617
|
+
warn(`${bold(String(ignored.length))} .sql file(s) in ${migrationsDir} are NOT migrations and will never run:`);
|
|
2618
|
+
for (const name of ignored)
|
|
2619
|
+
console.log(` ${yellow(symbols.dot)} ${name}`);
|
|
2620
|
+
console.log(` ${dim('A migration filename must be')} ${cyan('YYYYMMDDHHMMSS_name.sql')}${dim('.')}`);
|
|
2621
|
+
console.log(` ${dim('Rename the file(s), or create them with')} ${cyan('npx turbine migrate create <name>')}${dim('.')}`);
|
|
2622
|
+
newline();
|
|
2623
|
+
}
|
|
2543
2624
|
/** Print a one-line warning for each migration applied out of timestamp order. */
|
|
2544
2625
|
function warnOutOfOrder(outOfOrder) {
|
|
2545
2626
|
if (outOfOrder.length === 0)
|
|
@@ -2577,8 +2658,9 @@ async function cmdMigrateDeploy(args, config) {
|
|
|
2577
2658
|
label('Database', redactUrl(url));
|
|
2578
2659
|
label('Migrations', config.migrationsDir);
|
|
2579
2660
|
newline();
|
|
2661
|
+
warnIgnoredMigrationFiles(config.migrationsDir);
|
|
2580
2662
|
const spinner = new Spinner('Checking pending migrations').start();
|
|
2581
|
-
const plan = await inspectMigrationDeploy(url, config.migrationsDir);
|
|
2663
|
+
const plan = await inspectMigrationDeploy(url, config.migrationsDir, { schema: config.schema });
|
|
2582
2664
|
spinner.stop();
|
|
2583
2665
|
// Drift handling: honor --allow-drift exactly like `up`. Without it, block;
|
|
2584
2666
|
// with it, warn loudly and proceed.
|
|
@@ -2592,8 +2674,10 @@ async function cmdMigrateDeploy(args, config) {
|
|
|
2592
2674
|
errorLine();
|
|
2593
2675
|
process.exit(1);
|
|
2594
2676
|
}
|
|
2595
|
-
warn('--allow-drift is set:
|
|
2596
|
-
console.log(` ${dim('
|
|
2677
|
+
warn('--allow-drift is set: a drifted migration will be RE-BASELINED, not skipped.');
|
|
2678
|
+
console.log(` ${dim('Checksum validation still runs, and its result is written back to the history')}`);
|
|
2679
|
+
console.log(` ${dim("table: each modified file's stored checksum becomes its current content.")}`);
|
|
2680
|
+
console.log(` ${dim('The SQL that already ran is NOT re-run.')}`);
|
|
2597
2681
|
newline();
|
|
2598
2682
|
}
|
|
2599
2683
|
if (args.dryRun) {
|
|
@@ -2602,10 +2686,21 @@ async function cmdMigrateDeploy(args, config) {
|
|
|
2602
2686
|
newline();
|
|
2603
2687
|
return;
|
|
2604
2688
|
}
|
|
2689
|
+
// The same pre-flight the real run does, by calling the same two functions
|
|
2690
|
+
// rather than re-deriving their rules. They live inside `migrateUp`, which a
|
|
2691
|
+
// dry run never reaches, so `--dry-run` used to green-light a batch the real
|
|
2692
|
+
// command refuses outright: an empty-UP migration previewed as "would apply
|
|
2693
|
+
// 1 migration" and exit 0, and the deploy that followed on merge failed.
|
|
2694
|
+
// A preview that is quieter than the run it previews is not a preview.
|
|
2695
|
+
assertNoEmbeddedTransactions(plan.pending, 'up');
|
|
2696
|
+
assertUpHasStatements(plan.pending);
|
|
2605
2697
|
info(`${bold(String(plan.pending.length))} pending migration(s)`);
|
|
2606
2698
|
for (const file of plan.pending) {
|
|
2607
2699
|
console.log(` ${yellow(symbols.dot)} ${file.filename}`);
|
|
2608
2700
|
}
|
|
2701
|
+
// The real deploy warns about out-of-order applies; a preview that is
|
|
2702
|
+
// quieter than the run it previews is not a preview.
|
|
2703
|
+
warnOutOfOrder(plan.outOfOrder);
|
|
2609
2704
|
// Surface destructive statements even in a dry run so CI can see them.
|
|
2610
2705
|
const destructive = collectUpDestructive(plan.pending);
|
|
2611
2706
|
if (destructive.length > 0) {
|
|
@@ -2620,18 +2715,24 @@ async function cmdMigrateDeploy(args, config) {
|
|
|
2620
2715
|
if (destructive.length > 0)
|
|
2621
2716
|
printDestructiveNotice(destructive);
|
|
2622
2717
|
const runSpinner = new Spinner('Deploying migrations').start();
|
|
2623
|
-
const result = await migrateDeploy(url, config.migrationsDir, { allowDrift: args.allowDrift });
|
|
2624
|
-
if (result.applied.length === 0 && result.errors.length === 0) {
|
|
2625
|
-
runSpinner.succeed('0 applied, all migrations are up to date');
|
|
2626
|
-
newline();
|
|
2627
|
-
return;
|
|
2628
|
-
}
|
|
2718
|
+
const result = await migrateDeploy(url, config.migrationsDir, { allowDrift: args.allowDrift, schema: config.schema });
|
|
2629
2719
|
if (result.applied.length > 0) {
|
|
2630
2720
|
runSpinner.succeed(`${bold(String(result.applied.length))} applied`);
|
|
2631
2721
|
for (const file of result.applied) {
|
|
2632
2722
|
console.log(` ${green(symbols.check)} ${file.filename}`);
|
|
2633
2723
|
}
|
|
2634
2724
|
}
|
|
2725
|
+
else if (result.errors.length === 0) {
|
|
2726
|
+
runSpinner.succeed('0 applied, all migrations are up to date');
|
|
2727
|
+
}
|
|
2728
|
+
// Reported on EVERY path, including the one where nothing was applied. That is
|
|
2729
|
+
// `--allow-drift`'s most common case by far (you edited an already-applied
|
|
2730
|
+
// file, so nothing is pending), and it used to sit below an early return: the
|
|
2731
|
+
// stored checksum was rewritten and the terminal said everything was up to
|
|
2732
|
+
// date. A write to migration history announced as a no-op is worse than no
|
|
2733
|
+
// report at all, because the reader now believes the file and the database
|
|
2734
|
+
// still disagree.
|
|
2735
|
+
reportRebaselined(result.rebaselined);
|
|
2635
2736
|
warnOutOfOrder(result.outOfOrder);
|
|
2636
2737
|
if (result.errors.length > 0) {
|
|
2637
2738
|
runSpinner.fail('Deploy failed');
|
|
@@ -2689,11 +2790,24 @@ async function cmdMigrateDown(args, config) {
|
|
|
2689
2790
|
label('Migrations', config.migrationsDir);
|
|
2690
2791
|
newline();
|
|
2691
2792
|
if (args.dryRun) {
|
|
2692
|
-
|
|
2693
|
-
//
|
|
2694
|
-
// missing
|
|
2695
|
-
|
|
2696
|
-
|
|
2793
|
+
// Computed by the RUNNER'S rule (planMigrationRollback), not re-derived
|
|
2794
|
+
// from `status`. The old derivation sorted by filename and dropped applied
|
|
2795
|
+
// migrations whose file is missing, so with a deleted file it named the
|
|
2796
|
+
// migration AFTER the gap while the real run stopped at the gap.
|
|
2797
|
+
const plan = await planMigrationRollback(url, config.migrationsDir, {
|
|
2798
|
+
step: args.step ?? 1,
|
|
2799
|
+
schema: config.schema,
|
|
2800
|
+
// The real rollback refuses on drift unless this is set, so the preview
|
|
2801
|
+
// has to be told too: without it a deleted migration file previewed as
|
|
2802
|
+
// "would roll back 1 migration" while `migrate down` refused with E006.
|
|
2803
|
+
allowDrift: args.allowDrift,
|
|
2804
|
+
});
|
|
2805
|
+
printMigrationDryRun(plan.toRollback, 'down');
|
|
2806
|
+
if (plan.stoppedAt) {
|
|
2807
|
+
warn(`Rollback would STOP at ${bold(`${plan.stoppedAt.name}.sql`)}: ${plan.stoppedAt.error}`);
|
|
2808
|
+
console.log(` ${dim('A rollback batch is strictly LIFO with no gaps, so nothing after it would run.')}`);
|
|
2809
|
+
newline();
|
|
2810
|
+
}
|
|
2697
2811
|
return;
|
|
2698
2812
|
}
|
|
2699
2813
|
const spinner = new Spinner('Rolling back migration(s)').start();
|
|
@@ -2702,6 +2816,8 @@ async function cmdMigrateDown(args, config) {
|
|
|
2702
2816
|
result = await migrateDown(url, config.migrationsDir, {
|
|
2703
2817
|
step: args.step ?? 1,
|
|
2704
2818
|
allowDestructive: args.allowDestructive,
|
|
2819
|
+
allowDrift: args.allowDrift,
|
|
2820
|
+
schema: config.schema,
|
|
2705
2821
|
});
|
|
2706
2822
|
}
|
|
2707
2823
|
catch (err) {
|
|
@@ -2717,19 +2833,27 @@ async function cmdMigrateDown(args, config) {
|
|
|
2717
2833
|
result = await migrateDown(url, config.migrationsDir, {
|
|
2718
2834
|
step: args.step ?? 1,
|
|
2719
2835
|
allowDestructive: true,
|
|
2836
|
+
allowDrift: args.allowDrift,
|
|
2837
|
+
schema: config.schema,
|
|
2720
2838
|
});
|
|
2721
2839
|
}
|
|
2722
|
-
if (result.rolledBack.length === 0 && result.errors.length === 0) {
|
|
2723
|
-
spinner.succeed('No migrations to roll back');
|
|
2724
|
-
newline();
|
|
2725
|
-
return;
|
|
2726
|
-
}
|
|
2727
2840
|
if (result.rolledBack.length > 0) {
|
|
2728
2841
|
spinner.succeed(`Rolled back ${bold(String(result.rolledBack.length))} migration(s)`);
|
|
2729
2842
|
for (const file of result.rolledBack) {
|
|
2730
2843
|
console.log(` ${yellow(symbols.arrowRight)} ${file.filename}`);
|
|
2731
2844
|
}
|
|
2732
2845
|
}
|
|
2846
|
+
else if (result.errors.length === 0) {
|
|
2847
|
+
spinner.succeed('No migrations to roll back');
|
|
2848
|
+
}
|
|
2849
|
+
// Reported on EVERY path, including the one where nothing was applied. That is
|
|
2850
|
+
// `--allow-drift`'s most common case by far (you edited an already-applied
|
|
2851
|
+
// file, so nothing is pending), and it used to sit below an early return: the
|
|
2852
|
+
// stored checksum was rewritten and the terminal said everything was up to
|
|
2853
|
+
// date. A write to migration history announced as a no-op is worse than no
|
|
2854
|
+
// report at all, because the reader now believes the file and the database
|
|
2855
|
+
// still disagree.
|
|
2856
|
+
reportRebaselined(result.rebaselined);
|
|
2733
2857
|
if (result.errors.length > 0) {
|
|
2734
2858
|
spinner.fail('Rollback failed');
|
|
2735
2859
|
for (const { file, error: msg } of result.errors) {
|
|
@@ -2754,7 +2878,8 @@ async function cmdMigrateStatus(_args, config) {
|
|
|
2754
2878
|
newline();
|
|
2755
2879
|
return;
|
|
2756
2880
|
}
|
|
2757
|
-
|
|
2881
|
+
warnIgnoredMigrationFiles(config.migrationsDir);
|
|
2882
|
+
const statuses = await migrateStatus(url, config.migrationsDir, { schema: config.schema });
|
|
2758
2883
|
const appliedCount = statuses.filter((s) => s.applied).length;
|
|
2759
2884
|
const pendingCount = statuses.filter((s) => !s.applied).length;
|
|
2760
2885
|
info(`${bold(String(appliedCount))} applied, ${pendingCount > 0 ? yellow(bold(String(pendingCount))) : bold(String(pendingCount))} pending`);
|
|
@@ -2822,10 +2947,61 @@ export function getSeedExecutionPlan(seedFile) {
|
|
|
2822
2947
|
}
|
|
2823
2948
|
throw new Error(`Unsupported seed file extension: ${ext || '(none)'}. Use seed.ts, seed.js, or seed.sql.`);
|
|
2824
2949
|
}
|
|
2950
|
+
/**
|
|
2951
|
+
* The seed's pinned connection string, refusing a configured schema that does
|
|
2952
|
+
* not exist before any seed kind runs.
|
|
2953
|
+
*
|
|
2954
|
+
* `migrate` gained that guard and `seed` did not, because the seed reached
|
|
2955
|
+
* `connectionStringForSchema` directly instead of going through the migration
|
|
2956
|
+
* runner's connect helper. So seeding a project whose schema was missing (a
|
|
2957
|
+
* typo, or a fresh database nobody had created it in) failed with a bare
|
|
2958
|
+
* `relation "..." does not exist`: no error code, no schema name, no mention of
|
|
2959
|
+
* search_path or turbine.config.ts. That is verbatim the failure the guard
|
|
2960
|
+
* exists to prevent.
|
|
2961
|
+
*
|
|
2962
|
+
* Checked ONCE here rather than in each of the three seed kinds, because that
|
|
2963
|
+
* is the only place all three still share: the `.sql` kind opens its own client
|
|
2964
|
+
* below, while the `.ts` and `.js` kinds connect somewhere this process cannot
|
|
2965
|
+
* reach (a `tsx` CHILD PROCESS, and the user's own `defineSeed` callback), both
|
|
2966
|
+
* of them through the `DATABASE_URL` this function returns.
|
|
2967
|
+
*
|
|
2968
|
+
* The probe also reads the connection's own `search_path`, so the pin EXTENDS
|
|
2969
|
+
* it rather than replacing it, exactly as `migrate` does. Without that, seeding
|
|
2970
|
+
* into a pinned schema stops resolving extension types and functions that the
|
|
2971
|
+
* unpinned connection could see.
|
|
2972
|
+
*/
|
|
2973
|
+
async function seedConnectionString(config) {
|
|
2974
|
+
const schema = config.schema;
|
|
2975
|
+
if (schema === undefined || schema === '')
|
|
2976
|
+
return config.url;
|
|
2977
|
+
// Before the connection: an unpinnable NAME is not a missing schema.
|
|
2978
|
+
assertPinnableSchema(schema);
|
|
2979
|
+
const { default: pg } = await import('pg');
|
|
2980
|
+
const probe = new pg.Client({ connectionString: config.url });
|
|
2981
|
+
await probe.connect();
|
|
2982
|
+
let inherited;
|
|
2983
|
+
try {
|
|
2984
|
+
await assertSchemaExists(probe, schema);
|
|
2985
|
+
const shown = await probe.query('SHOW search_path');
|
|
2986
|
+
inherited = parseSearchPathValue(shown.rows[0]?.search_path ?? '');
|
|
2987
|
+
}
|
|
2988
|
+
finally {
|
|
2989
|
+
await probe.end();
|
|
2990
|
+
}
|
|
2991
|
+
return connectionStringForSchema(config.url, schema, inherited);
|
|
2992
|
+
}
|
|
2825
2993
|
async function runSeedPlan(plan, config) {
|
|
2826
2994
|
const oldDatabaseUrl = process.env.DATABASE_URL;
|
|
2827
|
-
|
|
2828
|
-
|
|
2995
|
+
// The seed connects through DATABASE_URL, whichever way it runs: `defineSeed`
|
|
2996
|
+
// reads it in the tsx child and in the in-process `import()`, and the `.sql`
|
|
2997
|
+
// path opens a client on it below. So the configured schema is applied HERE,
|
|
2998
|
+
// once, as the `options=-c search_path` connection parameter (never a `SET`),
|
|
2999
|
+
// and every seed kind inherits it. Without this the seed ran against the
|
|
3000
|
+
// role's default path while `push` and the generated client used
|
|
3001
|
+
// `config.schema`, so the two halves of one project wrote to two namespaces.
|
|
3002
|
+
const seedUrl = config.url ? await seedConnectionString(config) : undefined;
|
|
3003
|
+
if (seedUrl)
|
|
3004
|
+
process.env.DATABASE_URL = seedUrl;
|
|
2829
3005
|
try {
|
|
2830
3006
|
if (plan.kind === 'tsx') {
|
|
2831
3007
|
if (!canResolveTsx()) {
|
|
@@ -2844,7 +3020,7 @@ async function runSeedPlan(plan, config) {
|
|
|
2844
3020
|
stdio: 'inherit',
|
|
2845
3021
|
env: {
|
|
2846
3022
|
...process.env,
|
|
2847
|
-
DATABASE_URL:
|
|
3023
|
+
DATABASE_URL: seedUrl || process.env.DATABASE_URL,
|
|
2848
3024
|
TURBINE_SEED_SENTINEL: sentinel,
|
|
2849
3025
|
},
|
|
2850
3026
|
});
|
|
@@ -2867,7 +3043,10 @@ async function runSeedPlan(plan, config) {
|
|
|
2867
3043
|
await mod.default();
|
|
2868
3044
|
return;
|
|
2869
3045
|
}
|
|
2870
|
-
|
|
3046
|
+
// `seedUrl` is already pinned and already schema-checked; `requireUrl` is
|
|
3047
|
+
// only reached when no url was configured at all, in which case there is no
|
|
3048
|
+
// schema to pin either.
|
|
3049
|
+
const url = seedUrl ?? requireUrl(config);
|
|
2871
3050
|
const { default: pg } = await import('pg');
|
|
2872
3051
|
const client = new pg.Client({ connectionString: url });
|
|
2873
3052
|
await client.connect();
|
|
@@ -2927,6 +3106,7 @@ async function cmdStatus(_args, config) {
|
|
|
2927
3106
|
newline();
|
|
2928
3107
|
const spinner = new Spinner('Introspecting database').start();
|
|
2929
3108
|
const schema = await introspect({
|
|
3109
|
+
keepColumnNames: config.keepColumnNames,
|
|
2930
3110
|
connectionString: url,
|
|
2931
3111
|
schema: config.schema,
|
|
2932
3112
|
include: config.include.length ? config.include : undefined,
|
|
@@ -3072,6 +3252,7 @@ async function cmdDoctor(args, config) {
|
|
|
3072
3252
|
}
|
|
3073
3253
|
const spinner = jsonMode ? null : new Spinner('Introspecting database').start();
|
|
3074
3254
|
const schema = await introspect({
|
|
3255
|
+
keepColumnNames: config.keepColumnNames,
|
|
3075
3256
|
connectionString: url,
|
|
3076
3257
|
schema: config.schema,
|
|
3077
3258
|
include: config.include.length ? config.include : undefined,
|
|
@@ -4078,6 +4259,7 @@ function showInitHelp() {
|
|
|
4078
4259
|
console.log(` ${cyan('--skip-seed')} Don't create the seed file or run the seed`);
|
|
4079
4260
|
console.log(` ${cyan('--skip-push')} Don't push the schema to the database`);
|
|
4080
4261
|
console.log(` ${cyan('--skip-generate')} Don't generate the typed client`);
|
|
4262
|
+
console.log(` ${cyan('--schema, -s')} ${dim('<name>')} Postgres schema to write into config ${dim('(default: public)')}`);
|
|
4081
4263
|
newline();
|
|
4082
4264
|
}
|
|
4083
4265
|
function showGenerateHelp() {
|
|
@@ -4185,8 +4367,9 @@ function showMigrateHelp() {
|
|
|
4185
4367
|
console.log(` ${cyan('--recipe')} ${dim('<name>')} Scaffold a sanctioned migration pattern ${dim('(create only, e.g. backfill)')}`);
|
|
4186
4368
|
console.log(` ${cyan('--step, -n')} ${dim('<N>')} Number of migrations to apply/rollback`);
|
|
4187
4369
|
console.log(` ${cyan('--dry-run')} Show SQL without executing`);
|
|
4188
|
-
console.log(` ${cyan('--allow-drift')}
|
|
4370
|
+
console.log(` ${cyan('--allow-drift')} Re-baseline drifted checksums, REWRITING history ${dim('(up / deploy / down)')}`);
|
|
4189
4371
|
console.log(` ${cyan('--allow-destructive')} Run data-destroying migration statements without the interactive confirm`);
|
|
4372
|
+
console.log(` ${cyan('--schema, -s')} ${dim('<name>')} Postgres schema the migrations run in ${dim('(default: public)')}`);
|
|
4190
4373
|
console.log(` ${cyan('--verbose, -v')} Show detailed output`);
|
|
4191
4374
|
newline();
|
|
4192
4375
|
console.log(` ${bold('Examples:')}`);
|
|
@@ -4213,7 +4396,8 @@ function showSeedHelp() {
|
|
|
4213
4396
|
console.log(` ${dim('TypeScript seeds run with')} ${cyan('npx tsx')} ${dim('and can export')} ${cyan('defineSeed(fn)')}${dim('.')}`);
|
|
4214
4397
|
newline();
|
|
4215
4398
|
console.log(` ${bold('Options:')}`);
|
|
4216
|
-
console.log(` ${cyan('--url, -u')} ${dim('<url>')}
|
|
4399
|
+
console.log(` ${cyan('--url, -u')} ${dim('<url>')} Postgres connection string`);
|
|
4400
|
+
console.log(` ${cyan('--schema, -s')} ${dim('<name>')} Postgres schema the seed writes into ${dim('(default: public)')}`);
|
|
4217
4401
|
newline();
|
|
4218
4402
|
}
|
|
4219
4403
|
function showStatusHelp() {
|
|
@@ -4421,7 +4605,7 @@ function showHelp() {
|
|
|
4421
4605
|
console.log(` ${cyan('--recipe')} ${dim('<name>')} Scaffold a named migration recipe, e.g. backfill ${dim('(create)')}`);
|
|
4422
4606
|
console.log(` ${cyan('--step, -n')} ${dim('<N>')} Number of migrations to apply/rollback`);
|
|
4423
4607
|
console.log(` ${cyan('--allow-destructive')} Run data-destroying statements without prompting ${dim('(up/down/push)')}`);
|
|
4424
|
-
console.log(` ${cyan('--allow-drift')}
|
|
4608
|
+
console.log(` ${cyan('--allow-drift')} Re-baseline drifted checksums on ${cyan('migrate up')} / ${cyan('deploy')} / ${cyan('down')} ${dim('(advanced)')}`);
|
|
4425
4609
|
newline();
|
|
4426
4610
|
console.log(` ${bold('Init options:')}`);
|
|
4427
4611
|
console.log(` ${cyan('--yes, -y')} Accept every step's default (non-interactive)`);
|
|
@@ -4593,7 +4777,20 @@ function showVersion() {
|
|
|
4593
4777
|
// Main
|
|
4594
4778
|
// ---------------------------------------------------------------------------
|
|
4595
4779
|
async function main() {
|
|
4596
|
-
|
|
4780
|
+
let args;
|
|
4781
|
+
try {
|
|
4782
|
+
args = parseArgs();
|
|
4783
|
+
}
|
|
4784
|
+
catch (err) {
|
|
4785
|
+
// A last-resort net. Every flag refusal parseArgs makes goes through
|
|
4786
|
+
// `failArg` (banner, red line, hints) and exits there, so nothing is
|
|
4787
|
+
// expected here; anything that does arrive is a bug in the parser rather
|
|
4788
|
+
// than in the caller's command line, and printing it beats swallowing it.
|
|
4789
|
+
errorLine();
|
|
4790
|
+
error(err instanceof Error ? err.message : String(err));
|
|
4791
|
+
errorLine();
|
|
4792
|
+
process.exit(1);
|
|
4793
|
+
}
|
|
4597
4794
|
// Quick exits that don't need config
|
|
4598
4795
|
if (args.command === 'help' || args.command === '--help' || args.command === '-h') {
|
|
4599
4796
|
showHelp();
|
|
@@ -4723,7 +4920,20 @@ async function main() {
|
|
|
4723
4920
|
}
|
|
4724
4921
|
catch (err) {
|
|
4725
4922
|
if (err instanceof Error) {
|
|
4726
|
-
|
|
4923
|
+
// TYPE first, message second. These branches sniff for substrings, and
|
|
4924
|
+
// `ValidationError` beat them by accident: a refused `--schema` value says
|
|
4925
|
+
// "used as a connection parameter", which contains "connection", so a bad
|
|
4926
|
+
// flag value was reported under "Could not connect to database" with three
|
|
4927
|
+
// firewall hints. A typed error already knows what it is.
|
|
4928
|
+
if (err instanceof ValidationError) {
|
|
4929
|
+
errorLine();
|
|
4930
|
+
error(redactUrl(err.message));
|
|
4931
|
+
if (args.verbose && err.stack) {
|
|
4932
|
+
errorLine();
|
|
4933
|
+
console.error(dim(redactUrl(err.stack)));
|
|
4934
|
+
}
|
|
4935
|
+
}
|
|
4936
|
+
else if (err.message.includes('ECONNREFUSED') || err.message.includes('connection')) {
|
|
4727
4937
|
errorLine();
|
|
4728
4938
|
error(`Could not connect to database`);
|
|
4729
4939
|
errorLine(`${dim(redactUrl(err.message))}`);
|
package/dist/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.
|