turbine-orm 0.5.0 → 0.7.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 +194 -26
- package/dist/cjs/cli/config.js +5 -15
- package/dist/cjs/cli/index.js +240 -41
- package/dist/cjs/cli/migrate.js +71 -46
- package/dist/cjs/cli/ui.js +5 -9
- package/dist/cjs/client.js +109 -46
- package/dist/cjs/errors.js +293 -0
- package/dist/cjs/generate.js +33 -13
- package/dist/cjs/index.js +39 -20
- package/dist/cjs/introspect.js +3 -5
- package/dist/cjs/pipeline.js +9 -2
- package/dist/cjs/query.js +442 -109
- package/dist/cjs/schema-builder.js +93 -24
- package/dist/cjs/schema-sql.js +157 -19
- package/dist/cjs/schema.js +5 -2
- package/dist/cjs/serverless.js +87 -176
- package/dist/cli/config.js +6 -16
- package/dist/cli/index.js +245 -46
- package/dist/cli/migrate.d.ts +6 -1
- package/dist/cli/migrate.js +72 -47
- package/dist/cli/ui.js +5 -9
- package/dist/client.d.ts +77 -4
- package/dist/client.js +109 -46
- package/dist/errors.d.ts +138 -0
- package/dist/errors.js +278 -0
- package/dist/generate.d.ts +1 -1
- package/dist/generate.js +36 -16
- package/dist/index.d.ts +11 -9
- package/dist/index.js +16 -12
- package/dist/introspect.d.ts +1 -1
- package/dist/introspect.js +4 -6
- package/dist/pipeline.d.ts +1 -1
- package/dist/pipeline.js +9 -2
- package/dist/query.d.ts +257 -36
- package/dist/query.js +443 -110
- package/dist/schema-builder.d.ts +2 -2
- package/dist/schema-builder.js +93 -25
- package/dist/schema-sql.d.ts +7 -3
- package/dist/schema-sql.js +157 -19
- package/dist/schema.d.ts +1 -1
- package/dist/schema.js +5 -2
- package/dist/serverless.d.ts +91 -139
- package/dist/serverless.js +86 -173
- package/package.json +33 -16
- package/dist/types.d.ts +0 -93
- package/dist/types.js +0 -126
package/dist/cli/index.js
CHANGED
|
@@ -19,15 +19,15 @@
|
|
|
19
19
|
* npx turbine init --url postgres://...
|
|
20
20
|
* npx turbine migrate create add_users_table
|
|
21
21
|
*/
|
|
22
|
-
import { existsSync, mkdirSync,
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
22
|
+
import { appendFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
23
|
+
import { relative, resolve } from 'node:path';
|
|
24
|
+
import { pathToFileURL } from 'node:url';
|
|
25
25
|
import { generate } from '../generate.js';
|
|
26
|
+
import { introspect } from '../introspect.js';
|
|
26
27
|
import { schemaDiff, schemaPush } from '../schema-sql.js';
|
|
27
|
-
import {
|
|
28
|
-
import { createMigration,
|
|
29
|
-
import {
|
|
30
|
-
import { pathToFileURL } from 'node:url';
|
|
28
|
+
import { configTemplate, findConfigFile, loadConfig, resolveConfig } from './config.js';
|
|
29
|
+
import { createMigration, listMigrationFiles, migrateDown, migrateStatus, migrateUp } from './migrate.js';
|
|
30
|
+
import { banner, blue, bold, box, cyan, dim, divider, elapsed, error, table as formatTable, gray, green, header, info, label, magenta, newline, red, redactUrl, Spinner, success, symbols, warn, yellow, } from './ui.js';
|
|
31
31
|
function parseArgs() {
|
|
32
32
|
const args = process.argv.slice(2);
|
|
33
33
|
const result = {
|
|
@@ -75,6 +75,9 @@ function parseArgs() {
|
|
|
75
75
|
case '--dry-run':
|
|
76
76
|
result.dryRun = true;
|
|
77
77
|
break;
|
|
78
|
+
case '--auto':
|
|
79
|
+
result.auto = true;
|
|
80
|
+
break;
|
|
78
81
|
case '--force':
|
|
79
82
|
case '-f':
|
|
80
83
|
result.force = true;
|
|
@@ -83,6 +86,10 @@ function parseArgs() {
|
|
|
83
86
|
case '-v':
|
|
84
87
|
result.verbose = true;
|
|
85
88
|
break;
|
|
89
|
+
case '--help':
|
|
90
|
+
case '-h':
|
|
91
|
+
result.help = true;
|
|
92
|
+
break;
|
|
86
93
|
default:
|
|
87
94
|
if (!arg.startsWith('-')) {
|
|
88
95
|
result.positional.push(arg);
|
|
@@ -140,7 +147,7 @@ async function cmdInit(args, config) {
|
|
|
140
147
|
banner();
|
|
141
148
|
header('Initializing Turbine project');
|
|
142
149
|
// Detect environment
|
|
143
|
-
const envUrl = process.env
|
|
150
|
+
const envUrl = process.env.DATABASE_URL;
|
|
144
151
|
const hasEnvFile = existsSync('.env');
|
|
145
152
|
const hasEnvLocal = existsSync('.env.local');
|
|
146
153
|
if (envUrl) {
|
|
@@ -179,7 +186,7 @@ async function cmdInit(args, config) {
|
|
|
179
186
|
mkdirSync(migrDir, { recursive: true });
|
|
180
187
|
// Create .gitkeep
|
|
181
188
|
writeFileSync(`${migrDir}/.gitkeep`, '', 'utf-8');
|
|
182
|
-
success(`Created ${cyan(migrDir
|
|
189
|
+
success(`Created ${cyan(`${migrDir}/`)}`);
|
|
183
190
|
}
|
|
184
191
|
else {
|
|
185
192
|
info(`Migrations dir already exists: ${dim(migrDir)}`);
|
|
@@ -187,7 +194,7 @@ async function cmdInit(args, config) {
|
|
|
187
194
|
// Create output directory
|
|
188
195
|
if (!existsSync(config.out)) {
|
|
189
196
|
mkdirSync(config.out, { recursive: true });
|
|
190
|
-
success(`Created ${cyan(config.out
|
|
197
|
+
success(`Created ${cyan(`${config.out}/`)}`);
|
|
191
198
|
}
|
|
192
199
|
// Create seed file template
|
|
193
200
|
const seedDir = config.seedFile.substring(0, config.seedFile.lastIndexOf('/'));
|
|
@@ -231,7 +238,7 @@ async function cmdInit(args, config) {
|
|
|
231
238
|
* Define your database schema in TypeScript.
|
|
232
239
|
* Use \`npx turbine push\` to sync it to your database.
|
|
233
240
|
*
|
|
234
|
-
* @see https://
|
|
241
|
+
* @see https://turbineorm.dev
|
|
235
242
|
*/
|
|
236
243
|
|
|
237
244
|
import { defineSchema } from 'turbine-orm';
|
|
@@ -282,15 +289,15 @@ export default defineSchema({
|
|
|
282
289
|
spinner.succeed(`Found ${bold(String(tableCount))} tables`);
|
|
283
290
|
const genSpinner = new Spinner('Generating TypeScript client').start();
|
|
284
291
|
const result = generate({ schema, outDir: config.out, connectionString: url });
|
|
285
|
-
genSpinner.succeed(`Generated ${bold(String(result.files.length))} files to ${cyan(config.out
|
|
292
|
+
genSpinner.succeed(`Generated ${bold(String(result.files.length))} files to ${cyan(`${config.out}/`)}`);
|
|
286
293
|
}
|
|
287
294
|
catch (err) {
|
|
288
295
|
spinner.fail('Could not connect to database');
|
|
289
296
|
if (err instanceof Error) {
|
|
290
|
-
console.log(` ${dim(err.message)}`);
|
|
297
|
+
console.log(` ${dim(redactUrl(err.message))}`);
|
|
291
298
|
}
|
|
292
299
|
newline();
|
|
293
|
-
info(
|
|
300
|
+
info(`You can run generation later with: ${cyan('npx turbine generate')}`);
|
|
294
301
|
}
|
|
295
302
|
}
|
|
296
303
|
// Next steps
|
|
@@ -366,7 +373,7 @@ async function cmdGenerate(args, config) {
|
|
|
366
373
|
genSpinner.succeed(`Generated ${bold(String(result.files.length))} files in ${elapsed(startTime)}`);
|
|
367
374
|
// List files
|
|
368
375
|
for (const file of result.files) {
|
|
369
|
-
console.log(` ${dim(symbols.teeEnd)} ${cyan(result.outDir
|
|
376
|
+
console.log(` ${dim(symbols.teeEnd)} ${cyan(`${result.outDir}/${file}`)}`);
|
|
370
377
|
}
|
|
371
378
|
// Usage hint
|
|
372
379
|
newline();
|
|
@@ -414,9 +421,11 @@ async function cmdPush(args, config) {
|
|
|
414
421
|
for (const a of diff.alter) {
|
|
415
422
|
console.log(` ${yellow(symbols.arrowRight)} ${a.table}`);
|
|
416
423
|
for (const col of a.columns) {
|
|
417
|
-
const actionLabel = col.action === 'add'
|
|
418
|
-
|
|
419
|
-
|
|
424
|
+
const actionLabel = col.action === 'add'
|
|
425
|
+
? green('+ add')
|
|
426
|
+
: col.action === 'drop'
|
|
427
|
+
? red('- drop')
|
|
428
|
+
: yellow(`~ ${col.action.replace('_', ' ')}`);
|
|
420
429
|
console.log(` ${actionLabel} ${col.column}`);
|
|
421
430
|
}
|
|
422
431
|
}
|
|
@@ -470,17 +479,20 @@ async function cmdMigrate(args, config) {
|
|
|
470
479
|
console.log(` ${bold('turbine migrate')} ${dim('— SQL-first migration system')}`);
|
|
471
480
|
newline();
|
|
472
481
|
console.log(` ${bold('Commands:')}`);
|
|
473
|
-
console.log(` ${cyan('create <name>')}
|
|
474
|
-
console.log(` ${cyan('
|
|
475
|
-
console.log(` ${cyan('
|
|
476
|
-
console.log(` ${cyan('
|
|
482
|
+
console.log(` ${cyan('create <name>')} Create a new migration file`);
|
|
483
|
+
console.log(` ${cyan('create <name> --auto')} Auto-generate from schema diff`);
|
|
484
|
+
console.log(` ${cyan('up')} Apply pending migrations`);
|
|
485
|
+
console.log(` ${cyan('down')} Rollback last migration`);
|
|
486
|
+
console.log(` ${cyan('status')} Show migration status`);
|
|
477
487
|
newline();
|
|
478
488
|
console.log(` ${bold('Options:')}`);
|
|
489
|
+
console.log(` ${cyan('--auto')} Auto-generate UP/DOWN SQL from schema diff`);
|
|
479
490
|
console.log(` ${cyan('--step, -n')} Number of migrations to apply/rollback`);
|
|
480
491
|
console.log(` ${cyan('--dry-run')} Show SQL without executing`);
|
|
481
492
|
newline();
|
|
482
493
|
console.log(` ${bold('Examples:')}`);
|
|
483
494
|
console.log(` ${dim('npx turbine migrate create add_users_table')}`);
|
|
495
|
+
console.log(` ${dim('npx turbine migrate create add_email_index --auto')}`);
|
|
484
496
|
console.log(` ${dim('npx turbine migrate up')}`);
|
|
485
497
|
console.log(` ${dim('npx turbine migrate down --step 2')}`);
|
|
486
498
|
newline();
|
|
@@ -514,9 +526,61 @@ async function cmdMigrateCreate(args, config) {
|
|
|
514
526
|
newline();
|
|
515
527
|
console.log(` ${dim('Usage:')} ${cyan('npx turbine migrate create <name>')}`);
|
|
516
528
|
console.log(` ${dim('Example:')} ${cyan('npx turbine migrate create add_users_table')}`);
|
|
529
|
+
console.log(` ${dim('Auto:')} ${cyan('npx turbine migrate create my_change --auto')}`);
|
|
517
530
|
newline();
|
|
518
531
|
process.exit(1);
|
|
519
532
|
}
|
|
533
|
+
if (args.auto) {
|
|
534
|
+
// Auto-generate migration from schema diff
|
|
535
|
+
const url = requireUrl(config);
|
|
536
|
+
label('Database', redactUrl(url));
|
|
537
|
+
label('Schema file', config.schemaFile);
|
|
538
|
+
newline();
|
|
539
|
+
const schemaDef = await loadSchemaFile(config.schemaFile);
|
|
540
|
+
const diffSpinner = new Spinner('Computing schema diff').start();
|
|
541
|
+
const diff = await schemaDiff(schemaDef, url);
|
|
542
|
+
if (diff.statements.length === 0) {
|
|
543
|
+
diffSpinner.succeed('Database is already in sync — nothing to migrate');
|
|
544
|
+
newline();
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
diffSpinner.succeed(`Found ${bold(String(diff.statements.length))} change(s)`);
|
|
548
|
+
newline();
|
|
549
|
+
const upSQL = diff.statements.join('\n');
|
|
550
|
+
const downSQL = diff.reverseStatements.join('\n');
|
|
551
|
+
const file = createMigration(config.migrationsDir, name, { up: upSQL, down: downSQL });
|
|
552
|
+
const relPath = relative(process.cwd(), file.path);
|
|
553
|
+
success(`Created auto-migration: ${bold(file.filename)}`);
|
|
554
|
+
newline();
|
|
555
|
+
console.log(` ${dim('File:')} ${cyan(relPath)}`);
|
|
556
|
+
newline();
|
|
557
|
+
// Show summary of changes
|
|
558
|
+
if (diff.create.length > 0) {
|
|
559
|
+
console.log(` ${green('+ Create')} ${diff.create.length} table(s): ${diff.create.map((t) => t.name).join(', ')}`);
|
|
560
|
+
}
|
|
561
|
+
if (diff.alter.length > 0) {
|
|
562
|
+
console.log(` ${yellow('~ Alter')} ${diff.alter.length} table(s):`);
|
|
563
|
+
for (const a of diff.alter) {
|
|
564
|
+
for (const col of a.columns) {
|
|
565
|
+
const actionLabel = col.action === 'add'
|
|
566
|
+
? green('+ add')
|
|
567
|
+
: col.action === 'drop'
|
|
568
|
+
? red('- drop')
|
|
569
|
+
: col.action === 'add_unique'
|
|
570
|
+
? green('+ unique')
|
|
571
|
+
: col.action === 'drop_unique'
|
|
572
|
+
? red('- unique')
|
|
573
|
+
: yellow(`~ ${col.action.replace(/_/g, ' ')}`);
|
|
574
|
+
console.log(` ${actionLabel} ${a.table}.${col.column}`);
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
newline();
|
|
579
|
+
console.log(` ${dim('Review the migration, then run:')}`);
|
|
580
|
+
console.log(` ${cyan('npx turbine migrate up')}`);
|
|
581
|
+
newline();
|
|
582
|
+
return;
|
|
583
|
+
}
|
|
520
584
|
const file = createMigration(config.migrationsDir, name);
|
|
521
585
|
const relPath = relative(process.cwd(), file.path);
|
|
522
586
|
success(`Created migration: ${bold(file.filename)}`);
|
|
@@ -598,7 +662,7 @@ async function cmdMigrateDown(args, config) {
|
|
|
598
662
|
}
|
|
599
663
|
newline();
|
|
600
664
|
}
|
|
601
|
-
async function cmdMigrateStatus(
|
|
665
|
+
async function cmdMigrateStatus(_args, config) {
|
|
602
666
|
banner();
|
|
603
667
|
const url = requireUrl(config);
|
|
604
668
|
label('Database', redactUrl(url));
|
|
@@ -628,19 +692,22 @@ async function cmdMigrateStatus(args, config) {
|
|
|
628
692
|
const rows = statuses.map((s) => {
|
|
629
693
|
let status;
|
|
630
694
|
if (s.applied && s.checksumValid === false) {
|
|
631
|
-
status = red(symbols.warning
|
|
695
|
+
status = red(`${symbols.warning} Drifted`);
|
|
632
696
|
}
|
|
633
697
|
else if (s.applied) {
|
|
634
|
-
status = green(symbols.check
|
|
698
|
+
status = green(`${symbols.check} Applied`);
|
|
635
699
|
}
|
|
636
700
|
else {
|
|
637
|
-
status = yellow(symbols.dot
|
|
701
|
+
status = yellow(`${symbols.dot} Pending`);
|
|
638
702
|
}
|
|
639
703
|
return [
|
|
640
704
|
status,
|
|
641
705
|
s.file.filename,
|
|
642
706
|
s.appliedAt
|
|
643
|
-
? dim(s.appliedAt
|
|
707
|
+
? dim(s.appliedAt
|
|
708
|
+
.toISOString()
|
|
709
|
+
.replace('T', ' ')
|
|
710
|
+
.replace(/\.\d+Z$/, ' UTC'))
|
|
644
711
|
: dim('—'),
|
|
645
712
|
];
|
|
646
713
|
});
|
|
@@ -654,7 +721,7 @@ async function cmdMigrateStatus(args, config) {
|
|
|
654
721
|
// ---------------------------------------------------------------------------
|
|
655
722
|
// Command: seed
|
|
656
723
|
// ---------------------------------------------------------------------------
|
|
657
|
-
async function cmdSeed(
|
|
724
|
+
async function cmdSeed(_args, config) {
|
|
658
725
|
banner();
|
|
659
726
|
const seedFile = resolve(config.seedFile);
|
|
660
727
|
label('Seed file', config.seedFile);
|
|
@@ -670,22 +737,20 @@ async function cmdSeed(args, config) {
|
|
|
670
737
|
const spinner = new Spinner('Running seed file').start();
|
|
671
738
|
try {
|
|
672
739
|
// Use child_process to run the seed file via tsx or node
|
|
673
|
-
const {
|
|
740
|
+
const { execFileSync } = await import('node:child_process');
|
|
674
741
|
// Try tsx first (most compatible with .ts files), fall back to node --experimental-strip-types
|
|
675
742
|
const runners = [
|
|
676
|
-
{ cmd: 'npx tsx', name: 'tsx' },
|
|
677
|
-
{ cmd: 'node --experimental-strip-types', name: 'node' },
|
|
743
|
+
{ cmd: 'npx', args: ['tsx', seedFile], name: 'tsx' },
|
|
744
|
+
{ cmd: 'node', args: ['--experimental-strip-types', seedFile], name: 'node' },
|
|
678
745
|
];
|
|
679
|
-
// Shell-escape the seed file path to prevent injection
|
|
680
|
-
const escapedSeedFile = seedFile.replace(/'/g, "'\\''");
|
|
681
746
|
let ran = false;
|
|
682
747
|
for (const runner of runners) {
|
|
683
748
|
try {
|
|
684
|
-
|
|
749
|
+
execFileSync(runner.cmd, runner.args, {
|
|
685
750
|
stdio: 'inherit',
|
|
686
751
|
env: {
|
|
687
752
|
...process.env,
|
|
688
|
-
DATABASE_URL: config.url || process.env
|
|
753
|
+
DATABASE_URL: config.url || process.env.DATABASE_URL,
|
|
689
754
|
},
|
|
690
755
|
});
|
|
691
756
|
ran = true;
|
|
@@ -707,7 +772,7 @@ async function cmdSeed(args, config) {
|
|
|
707
772
|
catch (err) {
|
|
708
773
|
spinner.fail('Seed failed');
|
|
709
774
|
if (err instanceof Error) {
|
|
710
|
-
console.log(` ${dim(err.message)}`);
|
|
775
|
+
console.log(` ${dim(redactUrl(err.message))}`);
|
|
711
776
|
}
|
|
712
777
|
newline();
|
|
713
778
|
process.exit(1);
|
|
@@ -717,7 +782,7 @@ async function cmdSeed(args, config) {
|
|
|
717
782
|
// ---------------------------------------------------------------------------
|
|
718
783
|
// Command: status
|
|
719
784
|
// ---------------------------------------------------------------------------
|
|
720
|
-
async function cmdStatus(
|
|
785
|
+
async function cmdStatus(_args, config) {
|
|
721
786
|
banner();
|
|
722
787
|
const url = requireUrl(config);
|
|
723
788
|
label('Database', redactUrl(url));
|
|
@@ -735,7 +800,7 @@ async function cmdStatus(args, config) {
|
|
|
735
800
|
newline();
|
|
736
801
|
for (const tbl of Object.values(schema.tables)) {
|
|
737
802
|
const relCount = Object.keys(tbl.relations).length;
|
|
738
|
-
const
|
|
803
|
+
const _pk = tbl.primaryKey.join(', ') || dim('(none)');
|
|
739
804
|
console.log(` ${bold(cyan(tbl.name))}`);
|
|
740
805
|
for (let i = 0; i < tbl.columns.length; i++) {
|
|
741
806
|
const col = tbl.columns[i];
|
|
@@ -744,7 +809,7 @@ async function cmdStatus(args, config) {
|
|
|
744
809
|
const nullable = col.nullable ? dim('?') : '';
|
|
745
810
|
const def = col.hasDefault ? dim(' (default)') : '';
|
|
746
811
|
const pkLabel = tbl.primaryKey.includes(col.name) ? ` ${magenta('PK')}` : '';
|
|
747
|
-
console.log(` ${dim(prefix)} ${col.field}${nullable}: ${green(col.tsType)}${pkLabel}${def} ${gray(symbols.arrow
|
|
812
|
+
console.log(` ${dim(prefix)} ${col.field}${nullable}: ${green(col.tsType)}${pkLabel}${def} ${gray(`${symbols.arrow} ${col.pgType}`)}`);
|
|
748
813
|
}
|
|
749
814
|
const rels = Object.entries(tbl.relations);
|
|
750
815
|
if (rels.length > 0) {
|
|
@@ -777,11 +842,138 @@ async function cmdStudio(_args, _config) {
|
|
|
777
842
|
'A local web UI for browsing your database,',
|
|
778
843
|
'exploring relations, and managing data.',
|
|
779
844
|
'',
|
|
780
|
-
`Follow ${cyan('@
|
|
845
|
+
`Follow ${cyan('@turbineorm')} for updates.`,
|
|
781
846
|
].join('\n'), { title: bold(cyan('Studio')), padding: 2 }));
|
|
782
847
|
newline();
|
|
783
848
|
}
|
|
784
849
|
// ---------------------------------------------------------------------------
|
|
850
|
+
// Subcommand help
|
|
851
|
+
// ---------------------------------------------------------------------------
|
|
852
|
+
function showSubcommandHelp(command) {
|
|
853
|
+
const helpMap = {
|
|
854
|
+
init: showInitHelp,
|
|
855
|
+
generate: showGenerateHelp,
|
|
856
|
+
pull: showGenerateHelp,
|
|
857
|
+
push: showPushHelp,
|
|
858
|
+
migrate: showMigrateHelp,
|
|
859
|
+
migration: showMigrateHelp,
|
|
860
|
+
seed: showSeedHelp,
|
|
861
|
+
status: showStatusHelp,
|
|
862
|
+
};
|
|
863
|
+
const fn = helpMap[command];
|
|
864
|
+
if (fn) {
|
|
865
|
+
fn();
|
|
866
|
+
return true;
|
|
867
|
+
}
|
|
868
|
+
return false;
|
|
869
|
+
}
|
|
870
|
+
function showInitHelp() {
|
|
871
|
+
banner();
|
|
872
|
+
console.log(` ${bold('turbine init')} — Initialize a Turbine project`);
|
|
873
|
+
newline();
|
|
874
|
+
console.log(` ${bold('Usage:')}`);
|
|
875
|
+
console.log(` npx turbine init ${dim('[options]')}`);
|
|
876
|
+
newline();
|
|
877
|
+
console.log(` Creates ${cyan('turbine.config.ts')}, migrations directory, seed file template,`);
|
|
878
|
+
console.log(` and schema file template.`);
|
|
879
|
+
newline();
|
|
880
|
+
console.log(` ${bold('Options:')}`);
|
|
881
|
+
console.log(` ${cyan('--url, -u')} ${dim('<url>')} Postgres connection string to embed in config`);
|
|
882
|
+
console.log(` ${cyan('--force, -f')} Overwrite existing config file`);
|
|
883
|
+
newline();
|
|
884
|
+
}
|
|
885
|
+
function showGenerateHelp() {
|
|
886
|
+
banner();
|
|
887
|
+
console.log(` ${bold('turbine generate')} — Introspect database and generate TypeScript types`);
|
|
888
|
+
newline();
|
|
889
|
+
console.log(` ${bold('Usage:')}`);
|
|
890
|
+
console.log(` npx turbine generate ${dim('[options]')}`);
|
|
891
|
+
newline();
|
|
892
|
+
console.log(` Connects to your database, reads the schema, and generates:`);
|
|
893
|
+
console.log(` ${dim('•')} ${cyan('types.ts')} — Entity interfaces, Create/Update input types`);
|
|
894
|
+
console.log(` ${dim('•')} ${cyan('metadata.ts')} — Runtime schema metadata`);
|
|
895
|
+
console.log(` ${dim('•')} ${cyan('index.ts')} — Configured client with typed table accessors`);
|
|
896
|
+
newline();
|
|
897
|
+
console.log(` ${bold('Options:')}`);
|
|
898
|
+
console.log(` ${cyan('--url, -u')} ${dim('<url>')} Postgres connection string`);
|
|
899
|
+
console.log(` ${cyan('--out, -o')} ${dim('<dir>')} Output directory ${dim('(default: ./generated/turbine)')}`);
|
|
900
|
+
console.log(` ${cyan('--schema, -s')} ${dim('<name>')} Postgres schema ${dim('(default: public)')}`);
|
|
901
|
+
console.log(` ${cyan('--include')} ${dim('<tables>')} Comma-separated tables to include`);
|
|
902
|
+
console.log(` ${cyan('--exclude')} ${dim('<tables>')} Comma-separated tables to exclude`);
|
|
903
|
+
newline();
|
|
904
|
+
}
|
|
905
|
+
function showPushHelp() {
|
|
906
|
+
banner();
|
|
907
|
+
console.log(` ${bold('turbine push')} — Apply schema-builder definitions to database`);
|
|
908
|
+
newline();
|
|
909
|
+
console.log(` ${bold('Usage:')}`);
|
|
910
|
+
console.log(` npx turbine push ${dim('[options]')}`);
|
|
911
|
+
newline();
|
|
912
|
+
console.log(` Reads your ${cyan('turbine/schema.ts')} file, diffs against the live database,`);
|
|
913
|
+
console.log(` and applies CREATE/ALTER statements.`);
|
|
914
|
+
newline();
|
|
915
|
+
console.log(` ${bold('Options:')}`);
|
|
916
|
+
console.log(` ${cyan('--url, -u')} ${dim('<url>')} Postgres connection string`);
|
|
917
|
+
console.log(` ${cyan('--dry-run')} Show SQL without executing`);
|
|
918
|
+
console.log(` ${cyan('--verbose, -v')} Show detailed output`);
|
|
919
|
+
newline();
|
|
920
|
+
}
|
|
921
|
+
function showMigrateHelp() {
|
|
922
|
+
banner();
|
|
923
|
+
console.log(` ${bold('turbine migrate')} — SQL migration management`);
|
|
924
|
+
newline();
|
|
925
|
+
console.log(` ${bold('Usage:')}`);
|
|
926
|
+
console.log(` npx turbine migrate ${cyan('<subcommand>')} ${dim('[options]')}`);
|
|
927
|
+
newline();
|
|
928
|
+
console.log(` ${bold('Subcommands:')}`);
|
|
929
|
+
console.log(` ${cyan('create')} ${dim('<name>')} Create a new migration file`);
|
|
930
|
+
console.log(` ${cyan('up')} Apply pending migrations`);
|
|
931
|
+
console.log(` ${cyan('down')} Rollback last migration`);
|
|
932
|
+
console.log(` ${cyan('status')} Show applied/pending migrations`);
|
|
933
|
+
newline();
|
|
934
|
+
console.log(` ${bold('Options:')}`);
|
|
935
|
+
console.log(` ${cyan('--url, -u')} ${dim('<url>')} Postgres connection string`);
|
|
936
|
+
console.log(` ${cyan('--step, -n')} ${dim('<N>')} Number of migrations to apply/rollback`);
|
|
937
|
+
console.log(` ${cyan('--dry-run')} Show SQL without executing`);
|
|
938
|
+
console.log(` ${cyan('--verbose, -v')} Show detailed output`);
|
|
939
|
+
newline();
|
|
940
|
+
console.log(` ${bold('Examples:')}`);
|
|
941
|
+
console.log(` ${dim('$')} npx turbine migrate create add_users_table`);
|
|
942
|
+
console.log(` ${dim('$')} npx turbine migrate up`);
|
|
943
|
+
console.log(` ${dim('$')} npx turbine migrate down --step 2`);
|
|
944
|
+
console.log(` ${dim('$')} npx turbine migrate status`);
|
|
945
|
+
newline();
|
|
946
|
+
}
|
|
947
|
+
function showSeedHelp() {
|
|
948
|
+
banner();
|
|
949
|
+
console.log(` ${bold('turbine seed')} — Run seed file`);
|
|
950
|
+
newline();
|
|
951
|
+
console.log(` ${bold('Usage:')}`);
|
|
952
|
+
console.log(` npx turbine seed ${dim('[options]')}`);
|
|
953
|
+
newline();
|
|
954
|
+
console.log(` Runs the seed file specified in ${cyan('turbine.config.ts')}`);
|
|
955
|
+
console.log(` ${dim('(default: ./turbine/seed.ts)')}`);
|
|
956
|
+
newline();
|
|
957
|
+
console.log(` ${bold('Options:')}`);
|
|
958
|
+
console.log(` ${cyan('--url, -u')} ${dim('<url>')} Postgres connection string`);
|
|
959
|
+
newline();
|
|
960
|
+
}
|
|
961
|
+
function showStatusHelp() {
|
|
962
|
+
banner();
|
|
963
|
+
console.log(` ${bold('turbine status')} — Show database schema summary`);
|
|
964
|
+
newline();
|
|
965
|
+
console.log(` ${bold('Usage:')}`);
|
|
966
|
+
console.log(` npx turbine status ${dim('[options]')}`);
|
|
967
|
+
newline();
|
|
968
|
+
console.log(` Introspects your database and displays tables, columns,`);
|
|
969
|
+
console.log(` types, relations, and indexes.`);
|
|
970
|
+
newline();
|
|
971
|
+
console.log(` ${bold('Options:')}`);
|
|
972
|
+
console.log(` ${cyan('--url, -u')} ${dim('<url>')} Postgres connection string`);
|
|
973
|
+
console.log(` ${cyan('--schema, -s')} ${dim('<name>')} Postgres schema ${dim('(default: public)')}`);
|
|
974
|
+
newline();
|
|
975
|
+
}
|
|
976
|
+
// ---------------------------------------------------------------------------
|
|
785
977
|
// Help
|
|
786
978
|
// ---------------------------------------------------------------------------
|
|
787
979
|
function showHelp() {
|
|
@@ -840,6 +1032,13 @@ async function main() {
|
|
|
840
1032
|
showHelp();
|
|
841
1033
|
return;
|
|
842
1034
|
}
|
|
1035
|
+
// Subcommand help: e.g. `turbine migrate --help`
|
|
1036
|
+
if (args.help) {
|
|
1037
|
+
if (showSubcommandHelp(args.command))
|
|
1038
|
+
return;
|
|
1039
|
+
showHelp();
|
|
1040
|
+
return;
|
|
1041
|
+
}
|
|
843
1042
|
if (args.command === 'version' || args.command === '--version' || args.command === '-V') {
|
|
844
1043
|
showVersion();
|
|
845
1044
|
return;
|
|
@@ -905,7 +1104,7 @@ async function main() {
|
|
|
905
1104
|
if (err.message.includes('ECONNREFUSED') || err.message.includes('connection')) {
|
|
906
1105
|
newline();
|
|
907
1106
|
error(`Could not connect to database`);
|
|
908
|
-
console.log(` ${dim(err.message)}`);
|
|
1107
|
+
console.log(` ${dim(redactUrl(err.message))}`);
|
|
909
1108
|
newline();
|
|
910
1109
|
console.log(` ${dim('Check that:')}`);
|
|
911
1110
|
console.log(` ${dim('1.')} Your database is running`);
|
|
@@ -915,25 +1114,25 @@ async function main() {
|
|
|
915
1114
|
else if (err.message.includes('authentication')) {
|
|
916
1115
|
newline();
|
|
917
1116
|
error(`Authentication failed`);
|
|
918
|
-
console.log(` ${dim(err.message)}`);
|
|
1117
|
+
console.log(` ${dim(redactUrl(err.message))}`);
|
|
919
1118
|
}
|
|
920
1119
|
else if (err.message.includes('does not exist')) {
|
|
921
1120
|
newline();
|
|
922
1121
|
error(`Database or schema not found`);
|
|
923
|
-
console.log(` ${dim(err.message)}`);
|
|
1122
|
+
console.log(` ${dim(redactUrl(err.message))}`);
|
|
924
1123
|
}
|
|
925
1124
|
else {
|
|
926
1125
|
newline();
|
|
927
|
-
error(err.message);
|
|
1126
|
+
error(redactUrl(err.message));
|
|
928
1127
|
if (args.verbose && err.stack) {
|
|
929
1128
|
newline();
|
|
930
|
-
console.log(dim(err.stack));
|
|
1129
|
+
console.log(dim(redactUrl(err.stack)));
|
|
931
1130
|
}
|
|
932
1131
|
}
|
|
933
1132
|
}
|
|
934
1133
|
else {
|
|
935
1134
|
newline();
|
|
936
|
-
error(`Unexpected error: ${String(err)}`);
|
|
1135
|
+
error(`Unexpected error: ${redactUrl(String(err))}`);
|
|
937
1136
|
}
|
|
938
1137
|
newline();
|
|
939
1138
|
process.exit(1);
|
package/dist/cli/migrate.d.ts
CHANGED
|
@@ -73,8 +73,12 @@ export declare function parseMigrationSQL(filePath: string): {
|
|
|
73
73
|
};
|
|
74
74
|
/**
|
|
75
75
|
* Create a new migration file.
|
|
76
|
+
* If `autoContent` is provided, the UP/DOWN sections are pre-populated with the given SQL.
|
|
76
77
|
*/
|
|
77
|
-
export declare function createMigration(migrationsDir: string, name: string
|
|
78
|
+
export declare function createMigration(migrationsDir: string, name: string, autoContent?: {
|
|
79
|
+
up: string;
|
|
80
|
+
down: string;
|
|
81
|
+
}): MigrationFile;
|
|
78
82
|
/**
|
|
79
83
|
* Apply all pending migrations (UP).
|
|
80
84
|
*
|
|
@@ -86,6 +90,7 @@ export declare function createMigration(migrationsDir: string, name: string): Mi
|
|
|
86
90
|
*/
|
|
87
91
|
export declare function migrateUp(connectionString: string, migrationsDir: string, options?: {
|
|
88
92
|
step?: number;
|
|
93
|
+
force?: boolean;
|
|
89
94
|
}): Promise<{
|
|
90
95
|
applied: MigrationFile[];
|
|
91
96
|
errors: Array<{
|