turbine-orm 0.50.0 → 0.51.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 +66 -66
- package/dist/adapters/cockroachdb.d.ts +5 -5
- package/dist/adapters/cockroachdb.js +10 -10
- package/dist/adapters/index.d.ts +5 -5
- package/dist/adapters/index.js +7 -7
- package/dist/adapters/yugabytedb.d.ts +7 -7
- package/dist/adapters/yugabytedb.js +10 -10
- package/dist/cjs/adapters/cockroachdb.d.ts +5 -5
- package/dist/cjs/adapters/cockroachdb.js +10 -10
- package/dist/cjs/adapters/index.d.ts +5 -5
- package/dist/cjs/adapters/index.js +7 -7
- package/dist/cjs/adapters/yugabytedb.d.ts +7 -7
- package/dist/cjs/adapters/yugabytedb.js +10 -10
- package/dist/cjs/cli/config.d.ts +13 -2
- package/dist/cjs/cli/config.js +3 -2
- package/dist/cjs/cli/destructive.d.ts +1 -1
- package/dist/cjs/cli/destructive.js +1 -1
- package/dist/cjs/cli/index.d.ts +10 -10
- package/dist/cjs/cli/index.js +49 -45
- package/dist/cjs/cli/loader.d.ts +7 -7
- package/dist/cjs/cli/loader.js +9 -9
- package/dist/cjs/cli/mcp.js +4 -4
- package/dist/cjs/cli/migrate.d.ts +5 -5
- package/dist/cjs/cli/migrate.js +11 -11
- package/dist/cjs/cli/studio-ui.generated.js +1 -1
- package/dist/cjs/cli/ui.d.ts +2 -2
- package/dist/cjs/cli/ui.js +2 -2
- package/dist/cjs/client.d.ts +49 -38
- package/dist/cjs/client.js +57 -56
- package/dist/cjs/dialect.d.ts +62 -18
- package/dist/cjs/dialect.js +40 -2
- package/dist/cjs/errors.d.ts +5 -5
- package/dist/cjs/errors.js +11 -11
- package/dist/cjs/generate.d.ts +6 -6
- package/dist/cjs/generate.js +31 -29
- package/dist/cjs/index-advisor.d.ts +5 -5
- package/dist/cjs/index-advisor.js +0 -0
- package/dist/cjs/index.d.ts +1 -1
- package/dist/cjs/index.js +7 -7
- package/dist/cjs/introspect.d.ts +35 -9
- package/dist/cjs/introspect.js +83 -32
- package/dist/cjs/mssql.d.ts +11 -11
- package/dist/cjs/mssql.js +64 -29
- package/dist/cjs/mysql.d.ts +8 -8
- package/dist/cjs/mysql.js +61 -23
- package/dist/cjs/nested-write.d.ts +21 -2
- package/dist/cjs/nested-write.js +51 -14
- package/dist/cjs/optional-peer-import.cjs +7 -7
- package/dist/cjs/optional-peer-import.d.cts +7 -7
- package/dist/cjs/pipeline-submittable.d.ts +2 -2
- package/dist/cjs/pipeline-submittable.js +6 -6
- package/dist/cjs/pipeline.d.ts +1 -1
- package/dist/cjs/pipeline.js +4 -4
- package/dist/cjs/powdb-introspect.d.ts +1 -1
- package/dist/cjs/powdb-introspect.js +1 -1
- package/dist/cjs/powdb.d.ts +28 -28
- package/dist/cjs/powdb.js +66 -66
- package/dist/cjs/powql.d.ts +27 -27
- package/dist/cjs/powql.js +73 -52
- package/dist/cjs/query/aggregates.d.ts +1 -1
- package/dist/cjs/query/aggregates.js +5 -5
- package/dist/cjs/query/batched-loader.d.ts +11 -11
- package/dist/cjs/query/batched-loader.js +24 -24
- package/dist/cjs/query/builder.d.ts +39 -21
- package/dist/cjs/query/builder.js +99 -57
- package/dist/cjs/query/compound-unique.d.ts +1 -1
- package/dist/cjs/query/compound-unique.js +0 -0
- package/dist/cjs/query/deferred.d.ts +12 -6
- package/dist/cjs/query/deferred.js +1 -1
- package/dist/cjs/query/filters.d.ts +31 -11
- package/dist/cjs/query/filters.js +67 -14
- package/dist/cjs/query/index.d.ts +1 -1
- package/dist/cjs/query/index.js +1 -1
- package/dist/cjs/query/relations.d.ts +9 -9
- package/dist/cjs/query/relations.js +164 -57
- package/dist/cjs/query/types.d.ts +86 -35
- package/dist/cjs/query/types.js +1 -1
- package/dist/cjs/query/utils.d.ts +27 -10
- package/dist/cjs/query/utils.js +86 -14
- package/dist/cjs/query/where.d.ts +47 -28
- package/dist/cjs/query/where.js +130 -31
- package/dist/cjs/query/writes.d.ts +24 -5
- package/dist/cjs/query/writes.js +102 -13
- package/dist/cjs/realtime.d.ts +7 -7
- package/dist/cjs/realtime.js +9 -9
- package/dist/cjs/schema-builder.d.ts +18 -7
- package/dist/cjs/schema-builder.js +17 -10
- package/dist/cjs/schema-metadata.d.ts +3 -3
- package/dist/cjs/schema-metadata.js +9 -9
- package/dist/cjs/schema-sql.d.ts +9 -9
- package/dist/cjs/schema-sql.js +20 -20
- package/dist/cjs/schema.d.ts +19 -9
- package/dist/cjs/schema.js +6 -6
- package/dist/cjs/serverless.d.ts +15 -15
- package/dist/cjs/serverless.js +16 -16
- package/dist/cjs/sqlite.d.ts +8 -8
- package/dist/cjs/sqlite.js +53 -22
- package/dist/cjs/typed-sql.d.ts +4 -4
- package/dist/cjs/typed-sql.js +5 -5
- package/dist/cli/config.d.ts +13 -2
- package/dist/cli/config.js +3 -2
- package/dist/cli/destructive.d.ts +1 -1
- package/dist/cli/destructive.js +1 -1
- package/dist/cli/index.d.ts +10 -10
- package/dist/cli/index.js +49 -45
- package/dist/cli/loader.d.ts +7 -7
- package/dist/cli/loader.js +9 -9
- package/dist/cli/mcp.js +4 -4
- package/dist/cli/migrate.d.ts +5 -5
- package/dist/cli/migrate.js +11 -11
- package/dist/cli/studio-ui.generated.js +1 -1
- package/dist/cli/ui.d.ts +2 -2
- package/dist/cli/ui.js +2 -2
- package/dist/client.d.ts +49 -38
- package/dist/client.js +57 -56
- package/dist/dialect.d.ts +62 -18
- package/dist/dialect.js +40 -2
- package/dist/errors.d.ts +5 -5
- package/dist/errors.js +11 -11
- package/dist/generate.d.ts +6 -6
- package/dist/generate.js +31 -29
- package/dist/index-advisor.d.ts +5 -5
- package/dist/index-advisor.js +0 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +7 -7
- package/dist/introspect.d.ts +35 -9
- package/dist/introspect.js +82 -32
- package/dist/mssql.d.ts +11 -11
- package/dist/mssql.js +64 -29
- package/dist/mysql.d.ts +8 -8
- package/dist/mysql.js +61 -23
- package/dist/nested-write.d.ts +21 -2
- package/dist/nested-write.js +51 -14
- package/dist/optional-peer-import.cjs +7 -7
- package/dist/optional-peer-import.d.cts +7 -7
- package/dist/pipeline-submittable.d.ts +2 -2
- package/dist/pipeline-submittable.js +6 -6
- package/dist/pipeline.d.ts +1 -1
- package/dist/pipeline.js +4 -4
- package/dist/powdb-introspect.d.ts +1 -1
- package/dist/powdb-introspect.js +1 -1
- package/dist/powdb.d.ts +28 -28
- package/dist/powdb.js +66 -66
- package/dist/powql.d.ts +27 -27
- package/dist/powql.js +73 -52
- package/dist/query/aggregates.d.ts +1 -1
- package/dist/query/aggregates.js +5 -5
- package/dist/query/batched-loader.d.ts +11 -11
- package/dist/query/batched-loader.js +24 -24
- package/dist/query/builder.d.ts +39 -21
- package/dist/query/builder.js +100 -58
- package/dist/query/compound-unique.d.ts +1 -1
- package/dist/query/compound-unique.js +0 -0
- package/dist/query/deferred.d.ts +12 -6
- package/dist/query/deferred.js +1 -1
- package/dist/query/filters.d.ts +31 -11
- package/dist/query/filters.js +66 -13
- package/dist/query/index.d.ts +1 -1
- package/dist/query/index.js +1 -1
- package/dist/query/relations.d.ts +9 -9
- package/dist/query/relations.js +165 -58
- package/dist/query/types.d.ts +86 -35
- package/dist/query/types.js +1 -1
- package/dist/query/utils.d.ts +27 -10
- package/dist/query/utils.js +84 -14
- package/dist/query/where.d.ts +47 -28
- package/dist/query/where.js +129 -32
- package/dist/query/writes.d.ts +24 -5
- package/dist/query/writes.js +101 -13
- package/dist/realtime.d.ts +7 -7
- package/dist/realtime.js +9 -9
- package/dist/schema-builder.d.ts +18 -7
- package/dist/schema-builder.js +17 -10
- package/dist/schema-metadata.d.ts +3 -3
- package/dist/schema-metadata.js +9 -9
- package/dist/schema-sql.d.ts +9 -9
- package/dist/schema-sql.js +20 -20
- package/dist/schema.d.ts +19 -9
- package/dist/schema.js +6 -6
- package/dist/serverless.d.ts +15 -15
- package/dist/serverless.js +16 -16
- package/dist/sqlite.d.ts +8 -8
- package/dist/sqlite.js +53 -22
- package/dist/typed-sql.d.ts +4 -4
- package/dist/typed-sql.js +5 -5
- package/package.json +2 -2
package/dist/cjs/cli/index.js
CHANGED
|
@@ -4,21 +4,21 @@
|
|
|
4
4
|
* turbine-orm CLI
|
|
5
5
|
*
|
|
6
6
|
* Commands:
|
|
7
|
-
* turbine init
|
|
8
|
-
* turbine generate | pull
|
|
7
|
+
* turbine init , Initialize a Turbine project
|
|
8
|
+
* turbine generate | pull , Introspect database and generate TypeScript types
|
|
9
9
|
* turbine migrate-from-prisma - Parse a schema.prisma and emit a Prisma->Turbine name map + report
|
|
10
10
|
* turbine push - Apply schema-builder definitions to database (destructive ops gated)
|
|
11
11
|
* turbine migrate create <name> - Create a new SQL migration file (--auto | --from-diff | --recipe <name>)
|
|
12
|
-
* turbine migrate up
|
|
13
|
-
* turbine migrate deploy
|
|
14
|
-
* turbine migrate down
|
|
15
|
-
* turbine migrate status
|
|
16
|
-
* turbine seed
|
|
17
|
-
* turbine status
|
|
12
|
+
* turbine migrate up , Apply pending migrations
|
|
13
|
+
* turbine migrate deploy , Apply pending migrations without prompts
|
|
14
|
+
* turbine migrate down , Rollback last migration
|
|
15
|
+
* turbine migrate status , Show migration status
|
|
16
|
+
* turbine seed , Run seed file
|
|
17
|
+
* turbine status , Show schema summary
|
|
18
18
|
* turbine doctor - Cost-aware missing-FK-index triage (--fix, --json, --no-concurrently, --unused, --audit)
|
|
19
19
|
* turbine studio : Launch local read-only web UI (--demo for a seeded sample DB)
|
|
20
|
-
* turbine mcp
|
|
21
|
-
* turbine observe
|
|
20
|
+
* turbine mcp , Start read-only MCP server over JSON-RPC stdio
|
|
21
|
+
* turbine observe , Launch metrics dashboard (requires TURBINE_OBSERVE_URL)
|
|
22
22
|
*
|
|
23
23
|
* Usage:
|
|
24
24
|
* DATABASE_URL=postgres://... npx turbine generate
|
|
@@ -278,7 +278,7 @@ function parseArgs(argv = process.argv.slice(2)) {
|
|
|
278
278
|
return result;
|
|
279
279
|
}
|
|
280
280
|
// ---------------------------------------------------------------------------
|
|
281
|
-
// TypeScript loader
|
|
281
|
+
// TypeScript loader, user-facing error helper
|
|
282
282
|
// ---------------------------------------------------------------------------
|
|
283
283
|
/**
|
|
284
284
|
* Print a friendly error explaining how to install tsx, then exit.
|
|
@@ -294,7 +294,7 @@ function failMissingTsLoader(filePath, reason) {
|
|
|
294
294
|
}
|
|
295
295
|
else if (reason === 'failed') {
|
|
296
296
|
// tsx IS installed but registering its loader threw. Report the real
|
|
297
|
-
// cause
|
|
297
|
+
// cause, telling the user to install tsx here would be a misdiagnosis.
|
|
298
298
|
console.log(` ${(0, ui_js_1.dim)('tsx is installed, but registering its TypeScript loader failed:')}`);
|
|
299
299
|
(0, ui_js_1.newline)();
|
|
300
300
|
console.log(` ${(0, loader_js_1.getTsLoaderError)() ?? '(unknown error)'}`);
|
|
@@ -808,6 +808,7 @@ async function runInitGenerate(config, url) {
|
|
|
808
808
|
schema: config.schema,
|
|
809
809
|
include: config.include.length ? config.include : undefined,
|
|
810
810
|
exclude: config.exclude.length ? config.exclude : undefined,
|
|
811
|
+
relationNames: config.relationNames,
|
|
811
812
|
});
|
|
812
813
|
spinner.succeed(`Found ${(0, ui_js_1.bold)(String(Object.keys(schema.tables).length))} tables`);
|
|
813
814
|
const genSpinner = new ui_js_1.Spinner('Generating TypeScript client').start();
|
|
@@ -1115,7 +1116,7 @@ async function cmdGenerate(args, config) {
|
|
|
1115
1116
|
const url = requireUrl(config);
|
|
1116
1117
|
const startTime = performance.now();
|
|
1117
1118
|
// Guard: `schema` is the Postgres NAMESPACE to introspect (default `public`),
|
|
1118
|
-
// NOT the path to your schema-builder file
|
|
1119
|
+
// NOT the path to your schema-builder file, that goes in `schemaFile`. If the
|
|
1119
1120
|
// configured `schema` looks like a file path, introspection would silently
|
|
1120
1121
|
// match zero tables and emit an empty client. Fail loudly instead.
|
|
1121
1122
|
if (!args.allowEmpty && (0, config_js_1.looksLikeSchemaFilePath)(config.schema)) {
|
|
@@ -1125,7 +1126,7 @@ async function cmdGenerate(args, config) {
|
|
|
1125
1126
|
console.log(` ${(0, ui_js_1.dim)('The path to your defineSchema() file belongs in')} ${(0, ui_js_1.cyan)('schemaFile')}${(0, ui_js_1.dim)('.')}`);
|
|
1126
1127
|
(0, ui_js_1.newline)();
|
|
1127
1128
|
console.log(` ${(0, ui_js_1.dim)('Fix your')} ${(0, ui_js_1.cyan)('turbine.config.ts')}${(0, ui_js_1.dim)(':')}`);
|
|
1128
|
-
console.log(` ${(0, ui_js_1.green)('schema:')} ${(0, ui_js_1.cyan)("'public'")}${(0, ui_js_1.dim)(", // or omit
|
|
1129
|
+
console.log(` ${(0, ui_js_1.green)('schema:')} ${(0, ui_js_1.cyan)("'public'")}${(0, ui_js_1.dim)(", // or omit, introspects the 'public' schema")}`);
|
|
1129
1130
|
console.log(` ${(0, ui_js_1.green)('schemaFile:')} ${(0, ui_js_1.cyan)(`'${config.schema}'`)}${(0, ui_js_1.dim)(', // your defineSchema() file (used by `turbine push`)')}`);
|
|
1130
1131
|
(0, ui_js_1.newline)();
|
|
1131
1132
|
console.log(` ${(0, ui_js_1.dim)('Re-run with')} ${(0, ui_js_1.cyan)('--allow-empty')} ${(0, ui_js_1.dim)('to introspect this literal schema name anyway.')}`);
|
|
@@ -1144,6 +1145,7 @@ async function cmdGenerate(args, config) {
|
|
|
1144
1145
|
schema: config.schema,
|
|
1145
1146
|
include: config.include.length ? config.include : undefined,
|
|
1146
1147
|
exclude: config.exclude.length ? config.exclude : undefined,
|
|
1148
|
+
relationNames: config.relationNames,
|
|
1147
1149
|
includeViews: args.includeViews,
|
|
1148
1150
|
legacyToManyUniques: config.legacyToManyUniques,
|
|
1149
1151
|
onDefaultTableExclusion: (tables) => skippedInternalTables.push(...tables),
|
|
@@ -1163,13 +1165,13 @@ async function cmdGenerate(args, config) {
|
|
|
1163
1165
|
// instead of silently emitting an empty typed client.
|
|
1164
1166
|
if (tableNames.length === 0 && !args.allowEmpty) {
|
|
1165
1167
|
(0, ui_js_1.newline)();
|
|
1166
|
-
(0, ui_js_1.error)(`Introspection matched 0 tables in schema ${(0, ui_js_1.cyan)(config.schema)}
|
|
1168
|
+
(0, ui_js_1.error)(`Introspection matched 0 tables in schema ${(0, ui_js_1.cyan)(config.schema)}, refusing to generate an empty client.`);
|
|
1167
1169
|
(0, ui_js_1.newline)();
|
|
1168
1170
|
console.log(` ${(0, ui_js_1.dim)('Common causes:')}`);
|
|
1169
1171
|
console.log(` ${(0, ui_js_1.dim)('•')} ${(0, ui_js_1.cyan)('schema')} ${(0, ui_js_1.dim)('points at the wrong Postgres namespace')} ${(0, ui_js_1.dim)('(it is the schema NAME, default')} ${(0, ui_js_1.cyan)('public')}${(0, ui_js_1.dim)(').')}`);
|
|
1170
1172
|
console.log(` ${(0, ui_js_1.dim)('•')} ${(0, ui_js_1.dim)('You meant to set')} ${(0, ui_js_1.cyan)('schemaFile')} ${(0, ui_js_1.dim)('(your defineSchema() file), not')} ${(0, ui_js_1.cyan)('schema')}${(0, ui_js_1.dim)('.')}`);
|
|
1171
1173
|
console.log(` ${(0, ui_js_1.dim)('•')} ${(0, ui_js_1.cyan)('include')}/${(0, ui_js_1.cyan)('exclude')} ${(0, ui_js_1.dim)('filtered out every table.')}`);
|
|
1172
|
-
console.log(` ${(0, ui_js_1.dim)('•')} ${(0, ui_js_1.dim)('The database has no tables yet
|
|
1174
|
+
console.log(` ${(0, ui_js_1.dim)('•')} ${(0, ui_js_1.dim)('The database has no tables yet, run')} ${(0, ui_js_1.cyan)('turbine push')} ${(0, ui_js_1.dim)('or a migration first.')}`);
|
|
1173
1175
|
(0, ui_js_1.newline)();
|
|
1174
1176
|
console.log(` ${(0, ui_js_1.dim)('If an empty client is genuinely what you want, re-run with')} ${(0, ui_js_1.cyan)('--allow-empty')}${(0, ui_js_1.dim)('.')}`);
|
|
1175
1177
|
(0, ui_js_1.newline)();
|
|
@@ -1296,6 +1298,7 @@ async function cmdMigrateFromPrisma(args, config) {
|
|
|
1296
1298
|
includeViews: true,
|
|
1297
1299
|
// Inherit the shared bookkeeping-table exclusions (Turbine + Prisma).
|
|
1298
1300
|
exclude: [...new Set([...config.exclude, ...prisma_resolve_js_1.DEFAULT_EXCLUDED_TABLES])],
|
|
1301
|
+
relationNames: config.relationNames,
|
|
1299
1302
|
});
|
|
1300
1303
|
spinner.succeed(`Introspected ${(0, ui_js_1.bold)(String(Object.keys(schemaMeta.tables).length))} tables`);
|
|
1301
1304
|
}
|
|
@@ -1431,7 +1434,7 @@ async function cmdPush(args, config) {
|
|
|
1431
1434
|
(0, ui_js_1.newline)();
|
|
1432
1435
|
}
|
|
1433
1436
|
if (args.dryRun) {
|
|
1434
|
-
(0, ui_js_1.info)('Dry run
|
|
1437
|
+
(0, ui_js_1.info)('Dry run, no changes applied.');
|
|
1435
1438
|
(0, ui_js_1.newline)();
|
|
1436
1439
|
return;
|
|
1437
1440
|
}
|
|
@@ -1480,7 +1483,7 @@ async function cmdMigrate(args, config) {
|
|
|
1480
1483
|
const sub = args.subcommand;
|
|
1481
1484
|
if (!sub || sub === 'help') {
|
|
1482
1485
|
(0, ui_js_1.banner)();
|
|
1483
|
-
console.log(` ${(0, ui_js_1.bold)('turbine migrate')} ${(0, ui_js_1.dim)('
|
|
1486
|
+
console.log(` ${(0, ui_js_1.bold)('turbine migrate')} ${(0, ui_js_1.dim)(', SQL-first migration system')}`);
|
|
1484
1487
|
(0, ui_js_1.newline)();
|
|
1485
1488
|
console.log(` ${(0, ui_js_1.bold)('Commands:')}`);
|
|
1486
1489
|
console.log(` ${(0, ui_js_1.cyan)('create <name>')} Create a new migration file`);
|
|
@@ -1759,16 +1762,16 @@ async function cmdMigrateUp(args, config) {
|
|
|
1759
1762
|
(0, ui_js_1.newline)();
|
|
1760
1763
|
return;
|
|
1761
1764
|
}
|
|
1762
|
-
// Big, loud warning when bypassing drift detection
|
|
1765
|
+
// Big, loud warning when bypassing drift detection, this is a deliberately
|
|
1763
1766
|
// dangerous operation and the user should see it on every invocation.
|
|
1764
1767
|
if (args.allowDrift) {
|
|
1765
|
-
(0, ui_js_1.warn)('--allow-drift is set
|
|
1768
|
+
(0, ui_js_1.warn)('--allow-drift is set, checksum validation is DISABLED for this run.');
|
|
1766
1769
|
console.log(` ${(0, ui_js_1.dim)('Applied migrations may have been modified or deleted on disk.')}`);
|
|
1767
1770
|
console.log(` ${(0, ui_js_1.dim)('Proceed only if you are intentionally rewriting migration history.')}`);
|
|
1768
1771
|
(0, ui_js_1.newline)();
|
|
1769
1772
|
}
|
|
1770
1773
|
if (args.allowDestructive) {
|
|
1771
|
-
(0, ui_js_1.warn)('--allow-destructive is set
|
|
1774
|
+
(0, ui_js_1.warn)('--allow-destructive is set, data-destroying statements in migrations WILL run.');
|
|
1772
1775
|
(0, ui_js_1.newline)();
|
|
1773
1776
|
}
|
|
1774
1777
|
const spinner = new ui_js_1.Spinner('Applying migrations').start();
|
|
@@ -1797,7 +1800,7 @@ async function cmdMigrateUp(args, config) {
|
|
|
1797
1800
|
throw err;
|
|
1798
1801
|
spinner.stop();
|
|
1799
1802
|
if (!(await confirmDestructive(err.message))) {
|
|
1800
|
-
(0, ui_js_1.error)('Aborted
|
|
1803
|
+
(0, ui_js_1.error)('Aborted, no migrations were applied and no data was touched.');
|
|
1801
1804
|
(0, ui_js_1.newline)();
|
|
1802
1805
|
process.exit(1);
|
|
1803
1806
|
}
|
|
@@ -1945,7 +1948,7 @@ function isDestructiveRefusal(err) {
|
|
|
1945
1948
|
* 1. show the full itemized report (statement kinds + targets),
|
|
1946
1949
|
* 2. require typing the literal phrase `destroy my data`,
|
|
1947
1950
|
* 3. require a final explicit `yes`.
|
|
1948
|
-
* Non-interactive shells (CI, pipes) can never pass this
|
|
1951
|
+
* Non-interactive shells (CI, pipes) can never pass this, they must use the
|
|
1949
1952
|
* explicit `--allow-destructive` flag instead. Anything but exact answers aborts.
|
|
1950
1953
|
*/
|
|
1951
1954
|
async function confirmDestructive(report) {
|
|
@@ -1967,7 +1970,7 @@ async function confirmDestructive(report) {
|
|
|
1967
1970
|
const phrase = await rl.question(` Type ${(0, ui_js_1.bold)('destroy my data')} to continue, anything else to abort: `);
|
|
1968
1971
|
if (phrase.trim() !== 'destroy my data')
|
|
1969
1972
|
return false;
|
|
1970
|
-
const finalAnswer = await rl.question(` Final confirmation
|
|
1973
|
+
const finalAnswer = await rl.question(` Final confirmation, apply the destructive statements above? Type ${(0, ui_js_1.bold)('yes')}: `);
|
|
1971
1974
|
return finalAnswer.trim() === 'yes';
|
|
1972
1975
|
}
|
|
1973
1976
|
finally {
|
|
@@ -1993,7 +1996,7 @@ async function cmdMigrateDown(args, config) {
|
|
|
1993
1996
|
throw err;
|
|
1994
1997
|
spinner.stop();
|
|
1995
1998
|
if (!(await confirmDestructive(err.message))) {
|
|
1996
|
-
(0, ui_js_1.error)('Aborted
|
|
1999
|
+
(0, ui_js_1.error)('Aborted, nothing was rolled back and no data was touched.');
|
|
1997
2000
|
(0, ui_js_1.newline)();
|
|
1998
2001
|
process.exit(1);
|
|
1999
2002
|
}
|
|
@@ -2083,7 +2086,7 @@ async function cmdMigrateStatus(_args, config) {
|
|
|
2083
2086
|
.toISOString()
|
|
2084
2087
|
.replace('T', ' ')
|
|
2085
2088
|
.replace(/\.\d+Z$/, ' UTC'))
|
|
2086
|
-
: (0, ui_js_1.dim)('
|
|
2089
|
+
: (0, ui_js_1.dim)('-'),
|
|
2087
2090
|
];
|
|
2088
2091
|
});
|
|
2089
2092
|
console.log((0, ui_js_1.table)(headers, rows));
|
|
@@ -2113,7 +2116,7 @@ async function runSeedPlan(plan, config) {
|
|
|
2113
2116
|
try {
|
|
2114
2117
|
if (plan.kind === 'tsx') {
|
|
2115
2118
|
if (!(0, loader_js_1.canResolveTsx)()) {
|
|
2116
|
-
throw new Error('TypeScript seed files require tsx
|
|
2119
|
+
throw new Error('TypeScript seed files require tsx, install tsx or use seed.js/seed.sql.');
|
|
2117
2120
|
}
|
|
2118
2121
|
// The seed runs in a child process, so we cannot observe its callback
|
|
2119
2122
|
// directly. Hand it a sentinel path: `defineSeed`'s runner writes the file
|
|
@@ -2214,6 +2217,7 @@ async function cmdStatus(_args, config) {
|
|
|
2214
2217
|
schema: config.schema,
|
|
2215
2218
|
include: config.include.length ? config.include : undefined,
|
|
2216
2219
|
exclude: config.exclude.length ? config.exclude : undefined,
|
|
2220
|
+
relationNames: config.relationNames,
|
|
2217
2221
|
});
|
|
2218
2222
|
const tableNames = Object.keys(schema.tables);
|
|
2219
2223
|
spinner.succeed(`Found ${(0, ui_js_1.bold)(String(tableNames.length))} tables`);
|
|
@@ -2677,7 +2681,7 @@ function isLoopbackHost(host) {
|
|
|
2677
2681
|
return h === '127.0.0.1' || h === 'localhost' || h === '::1' || h === '[::1]';
|
|
2678
2682
|
}
|
|
2679
2683
|
// ---------------------------------------------------------------------------
|
|
2680
|
-
// Command: studio
|
|
2684
|
+
// Command: studio, local read-only web UI
|
|
2681
2685
|
// ---------------------------------------------------------------------------
|
|
2682
2686
|
async function cmdStudio(args, config) {
|
|
2683
2687
|
(0, ui_js_1.banner)();
|
|
@@ -2693,7 +2697,7 @@ async function cmdStudio(args, config) {
|
|
|
2693
2697
|
process.exit(1);
|
|
2694
2698
|
}
|
|
2695
2699
|
// Non-loopback binds require an explicit --allow-remote opt-in. Studio has
|
|
2696
|
-
// only a random session token
|
|
2700
|
+
// only a random session token, exposing it on a LAN interface is foot-gun
|
|
2697
2701
|
// territory, so we refuse rather than warn-and-proceed.
|
|
2698
2702
|
if (!isLoopbackHost(host)) {
|
|
2699
2703
|
if (!args.allowRemote) {
|
|
@@ -2704,7 +2708,7 @@ async function cmdStudio(args, config) {
|
|
|
2704
2708
|
(0, ui_js_1.newline)();
|
|
2705
2709
|
process.exit(1);
|
|
2706
2710
|
}
|
|
2707
|
-
console.log((0, ui_js_1.warn)(`Studio is binding to ${(0, ui_js_1.yellow)(host)}
|
|
2711
|
+
console.log((0, ui_js_1.warn)(`Studio is binding to ${(0, ui_js_1.yellow)(host)}, this is NOT loopback. ` +
|
|
2708
2712
|
`Anyone on your network who can reach this port + guess the session token can read your database.`));
|
|
2709
2713
|
}
|
|
2710
2714
|
const spinner = new ui_js_1.Spinner(demo ? 'Seeding demo dataset' : 'Introspecting database').start();
|
|
@@ -2804,7 +2808,7 @@ async function cmdStudio(args, config) {
|
|
|
2804
2808
|
});
|
|
2805
2809
|
}
|
|
2806
2810
|
// ---------------------------------------------------------------------------
|
|
2807
|
-
// Command: mcp
|
|
2811
|
+
// Command: mcp, read-only JSON-RPC stdio server
|
|
2808
2812
|
// ---------------------------------------------------------------------------
|
|
2809
2813
|
async function cmdMcp(_args, config) {
|
|
2810
2814
|
const url = requireUrl(config);
|
|
@@ -2849,7 +2853,7 @@ async function cmdObserve(args) {
|
|
|
2849
2853
|
(0, ui_js_1.newline)();
|
|
2850
2854
|
process.exit(1);
|
|
2851
2855
|
}
|
|
2852
|
-
console.log((0, ui_js_1.warn)(`Observe is binding to ${(0, ui_js_1.yellow)(host)}
|
|
2856
|
+
console.log((0, ui_js_1.warn)(`Observe is binding to ${(0, ui_js_1.yellow)(host)}, this is NOT loopback. ` +
|
|
2853
2857
|
`Anyone on your network who can reach this port + guess the session token can read your metrics.`));
|
|
2854
2858
|
}
|
|
2855
2859
|
const spinner = new ui_js_1.Spinner('Connecting to metrics database').start();
|
|
@@ -2864,7 +2868,7 @@ async function cmdObserve(args) {
|
|
|
2864
2868
|
}
|
|
2865
2869
|
(0, ui_js_1.newline)();
|
|
2866
2870
|
console.log((0, ui_js_1.box)([
|
|
2867
|
-
`${(0, ui_js_1.bold)('Turbine Observe')} ${(0, ui_js_1.dim)('
|
|
2871
|
+
`${(0, ui_js_1.bold)('Turbine Observe')} ${(0, ui_js_1.dim)(', query metrics dashboard')}`,
|
|
2868
2872
|
'',
|
|
2869
2873
|
` ${(0, ui_js_1.cyan)('URL:')} ${(0, ui_js_1.bold)(handle.url)}`,
|
|
2870
2874
|
'',
|
|
@@ -2911,7 +2915,7 @@ function showSubcommandHelp(command) {
|
|
|
2911
2915
|
}
|
|
2912
2916
|
function showInitHelp() {
|
|
2913
2917
|
(0, ui_js_1.banner)();
|
|
2914
|
-
console.log(` ${(0, ui_js_1.bold)('turbine init')}
|
|
2918
|
+
console.log(` ${(0, ui_js_1.bold)('turbine init')}, Initialize a Turbine project`);
|
|
2915
2919
|
(0, ui_js_1.newline)();
|
|
2916
2920
|
console.log(` ${(0, ui_js_1.bold)('Usage:')}`);
|
|
2917
2921
|
console.log(` npx turbine init ${(0, ui_js_1.dim)('[options]')}`);
|
|
@@ -2936,16 +2940,16 @@ function showInitHelp() {
|
|
|
2936
2940
|
}
|
|
2937
2941
|
function showGenerateHelp() {
|
|
2938
2942
|
(0, ui_js_1.banner)();
|
|
2939
|
-
console.log(` ${(0, ui_js_1.bold)('turbine generate')}
|
|
2943
|
+
console.log(` ${(0, ui_js_1.bold)('turbine generate')}, Introspect database and generate TypeScript types`);
|
|
2940
2944
|
(0, ui_js_1.newline)();
|
|
2941
2945
|
console.log(` ${(0, ui_js_1.bold)('Usage:')}`);
|
|
2942
2946
|
console.log(` npx turbine generate ${(0, ui_js_1.dim)('[options]')}`);
|
|
2943
2947
|
(0, ui_js_1.newline)();
|
|
2944
2948
|
console.log(` Connects to your database, reads the schema, and generates:`);
|
|
2945
|
-
console.log(` ${(0, ui_js_1.dim)('•')} ${(0, ui_js_1.cyan)('types.ts')}
|
|
2946
|
-
console.log(` ${(0, ui_js_1.dim)('•')} ${(0, ui_js_1.cyan)('metadata.ts')}
|
|
2947
|
-
console.log(` ${(0, ui_js_1.dim)('•')} ${(0, ui_js_1.cyan)('index.ts')}
|
|
2948
|
-
console.log(` ${(0, ui_js_1.dim)('•')} ${(0, ui_js_1.cyan)('zod.ts')}
|
|
2949
|
+
console.log(` ${(0, ui_js_1.dim)('•')} ${(0, ui_js_1.cyan)('types.ts')} , Entity interfaces, Create/Update input types`);
|
|
2950
|
+
console.log(` ${(0, ui_js_1.dim)('•')} ${(0, ui_js_1.cyan)('metadata.ts')}, Runtime schema metadata`);
|
|
2951
|
+
console.log(` ${(0, ui_js_1.dim)('•')} ${(0, ui_js_1.cyan)('index.ts')} , Configured client with typed table accessors`);
|
|
2952
|
+
console.log(` ${(0, ui_js_1.dim)('•')} ${(0, ui_js_1.cyan)('zod.ts')} , Zod schemas ${(0, ui_js_1.dim)('(with --zod)')}`);
|
|
2949
2953
|
(0, ui_js_1.newline)();
|
|
2950
2954
|
console.log(` ${(0, ui_js_1.bold)('Options:')}`);
|
|
2951
2955
|
console.log(` ${(0, ui_js_1.cyan)('--url, -u')} ${(0, ui_js_1.dim)('<url>')} Postgres connection string`);
|
|
@@ -2993,7 +2997,7 @@ function showMigrateFromPrismaHelp() {
|
|
|
2993
2997
|
}
|
|
2994
2998
|
function showPushHelp() {
|
|
2995
2999
|
(0, ui_js_1.banner)();
|
|
2996
|
-
console.log(` ${(0, ui_js_1.bold)('turbine push')}
|
|
3000
|
+
console.log(` ${(0, ui_js_1.bold)('turbine push')}, Apply schema-builder definitions to database`);
|
|
2997
3001
|
(0, ui_js_1.newline)();
|
|
2998
3002
|
console.log(` ${(0, ui_js_1.bold)('Usage:')}`);
|
|
2999
3003
|
console.log(` npx turbine push ${(0, ui_js_1.dim)('[options]')}`);
|
|
@@ -3010,7 +3014,7 @@ function showPushHelp() {
|
|
|
3010
3014
|
}
|
|
3011
3015
|
function showMigrateHelp() {
|
|
3012
3016
|
(0, ui_js_1.banner)();
|
|
3013
|
-
console.log(` ${(0, ui_js_1.bold)('turbine migrate')}
|
|
3017
|
+
console.log(` ${(0, ui_js_1.bold)('turbine migrate')}, SQL migration management`);
|
|
3014
3018
|
(0, ui_js_1.newline)();
|
|
3015
3019
|
console.log(` ${(0, ui_js_1.bold)('Usage:')}`);
|
|
3016
3020
|
console.log(` npx turbine migrate ${(0, ui_js_1.cyan)('<subcommand>')} ${(0, ui_js_1.dim)('[options]')}`);
|
|
@@ -3029,7 +3033,7 @@ function showMigrateHelp() {
|
|
|
3029
3033
|
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)')}`);
|
|
3030
3034
|
console.log(` ${(0, ui_js_1.cyan)('--step, -n')} ${(0, ui_js_1.dim)('<N>')} Number of migrations to apply/rollback`);
|
|
3031
3035
|
console.log(` ${(0, ui_js_1.cyan)('--dry-run')} Show SQL without executing`);
|
|
3032
|
-
console.log(` ${(0, ui_js_1.cyan)('--allow-drift')} Bypass checksum validation ${(0, ui_js_1.dim)('(migrate up only
|
|
3036
|
+
console.log(` ${(0, ui_js_1.cyan)('--allow-drift')} Bypass checksum validation ${(0, ui_js_1.dim)('(migrate up only, advanced)')}`);
|
|
3033
3037
|
console.log(` ${(0, ui_js_1.cyan)('--allow-destructive')} Run data-destroying migration statements without the interactive confirm`);
|
|
3034
3038
|
console.log(` ${(0, ui_js_1.cyan)('--verbose, -v')} Show detailed output`);
|
|
3035
3039
|
(0, ui_js_1.newline)();
|
|
@@ -3046,7 +3050,7 @@ function showMigrateHelp() {
|
|
|
3046
3050
|
}
|
|
3047
3051
|
function showSeedHelp() {
|
|
3048
3052
|
(0, ui_js_1.banner)();
|
|
3049
|
-
console.log(` ${(0, ui_js_1.bold)('turbine seed')}
|
|
3053
|
+
console.log(` ${(0, ui_js_1.bold)('turbine seed')}, Run seed file`);
|
|
3050
3054
|
(0, ui_js_1.newline)();
|
|
3051
3055
|
console.log(` ${(0, ui_js_1.bold)('Usage:')}`);
|
|
3052
3056
|
console.log(` npx turbine seed ${(0, ui_js_1.dim)('[options]')}`);
|
|
@@ -3062,7 +3066,7 @@ function showSeedHelp() {
|
|
|
3062
3066
|
}
|
|
3063
3067
|
function showStatusHelp() {
|
|
3064
3068
|
(0, ui_js_1.banner)();
|
|
3065
|
-
console.log(` ${(0, ui_js_1.bold)('turbine status')}
|
|
3069
|
+
console.log(` ${(0, ui_js_1.bold)('turbine status')}, Show database schema summary`);
|
|
3066
3070
|
(0, ui_js_1.newline)();
|
|
3067
3071
|
console.log(` ${(0, ui_js_1.bold)('Usage:')}`);
|
|
3068
3072
|
console.log(` npx turbine status ${(0, ui_js_1.dim)('[options]')}`);
|
|
@@ -3077,7 +3081,7 @@ function showStatusHelp() {
|
|
|
3077
3081
|
}
|
|
3078
3082
|
function showMcpHelp() {
|
|
3079
3083
|
(0, ui_js_1.banner)();
|
|
3080
|
-
console.log(` ${(0, ui_js_1.bold)('turbine mcp')}
|
|
3084
|
+
console.log(` ${(0, ui_js_1.bold)('turbine mcp')}, Start read-only MCP server over stdio`);
|
|
3081
3085
|
(0, ui_js_1.newline)();
|
|
3082
3086
|
console.log(` ${(0, ui_js_1.bold)('Usage:')}`);
|
|
3083
3087
|
console.log(` npx turbine mcp ${(0, ui_js_1.dim)('[options]')}`);
|
package/dist/cjs/cli/loader.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* turbine-orm CLI
|
|
2
|
+
* turbine-orm CLI, TypeScript loader registration
|
|
3
3
|
*
|
|
4
4
|
* The CLI loads user-supplied config and schema files via dynamic `import()`.
|
|
5
5
|
* Plain Node has no built-in `.ts` loader, so importing `turbine.config.ts`
|
|
@@ -12,16 +12,16 @@
|
|
|
12
12
|
* 2. Prefer tsx's supported programmatic API, `tsx/esm/api`'s `register()`.
|
|
13
13
|
* Calling Node's `module.register('tsx/esm', ...)` directly throws
|
|
14
14
|
* "tsx must be loaded with --import instead of --loader" on every Node
|
|
15
|
-
* version that has `module.register()` (>= 20.6)
|
|
15
|
+
* version that has `module.register()` (>= 20.6), tsx's hook file
|
|
16
16
|
* guards against being loaded that way. The `tsx/esm/api` entry point
|
|
17
17
|
* is the documented path and works everywhere `module.register()` does.
|
|
18
18
|
* 3. Fall back to `module.register('tsx/esm', ...)` only for very old tsx
|
|
19
19
|
* versions (< 4.0) that predate `tsx/esm/api`.
|
|
20
20
|
* 4. If tsx isn't installed, or registration genuinely fails, surface an
|
|
21
|
-
* actionable error
|
|
21
|
+
* actionable error, including the REAL underlying error message, never
|
|
22
22
|
* a misdiagnosed "tsx is not installed".
|
|
23
23
|
*
|
|
24
|
-
* `tsx` is intentionally NOT a runtime dependency
|
|
24
|
+
* `tsx` is intentionally NOT a runtime dependency, many projects already
|
|
25
25
|
* have it, and adding a heavy dev tool to a 1-dependency ORM would be silly.
|
|
26
26
|
*/
|
|
27
27
|
/**
|
|
@@ -45,7 +45,7 @@ export type TsLoaderStatus = 'registered' | 'already' | 'unsupported' | 'missing
|
|
|
45
45
|
export declare function getTsLoaderError(): string | null;
|
|
46
46
|
/**
|
|
47
47
|
* Register the tsx ESM loader so subsequent dynamic imports of `.ts` files
|
|
48
|
-
* work. Safe to call multiple times
|
|
48
|
+
* work. Safe to call multiple times, internal flag prevents double registration.
|
|
49
49
|
*
|
|
50
50
|
* Returns:
|
|
51
51
|
* - 'registered' loader was successfully registered this call
|
|
@@ -53,9 +53,9 @@ export declare function getTsLoaderError(): string | null;
|
|
|
53
53
|
* - 'unsupported' Node lacks `module.register()` (Node < 20.6) and tsx has
|
|
54
54
|
* no programmatic API to fall back to
|
|
55
55
|
* - 'missing' `tsx` is not installed in the user's project
|
|
56
|
-
* - 'failed' tsx IS installed but registration threw
|
|
56
|
+
* - 'failed' tsx IS installed but registration threw, see
|
|
57
57
|
* {@link getTsLoaderError} for the underlying message
|
|
58
58
|
*/
|
|
59
59
|
export declare function registerTsLoader(): Promise<TsLoaderStatus>;
|
|
60
|
-
/** Reset the loader state
|
|
60
|
+
/** Reset the loader state, used by unit tests only. */
|
|
61
61
|
export declare function _resetTsLoaderStateForTests(): void;
|
package/dist/cjs/cli/loader.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
* turbine-orm CLI
|
|
3
|
+
* turbine-orm CLI, TypeScript loader registration
|
|
4
4
|
*
|
|
5
5
|
* The CLI loads user-supplied config and schema files via dynamic `import()`.
|
|
6
6
|
* Plain Node has no built-in `.ts` loader, so importing `turbine.config.ts`
|
|
@@ -13,16 +13,16 @@
|
|
|
13
13
|
* 2. Prefer tsx's supported programmatic API, `tsx/esm/api`'s `register()`.
|
|
14
14
|
* Calling Node's `module.register('tsx/esm', ...)` directly throws
|
|
15
15
|
* "tsx must be loaded with --import instead of --loader" on every Node
|
|
16
|
-
* version that has `module.register()` (>= 20.6)
|
|
16
|
+
* version that has `module.register()` (>= 20.6), tsx's hook file
|
|
17
17
|
* guards against being loaded that way. The `tsx/esm/api` entry point
|
|
18
18
|
* is the documented path and works everywhere `module.register()` does.
|
|
19
19
|
* 3. Fall back to `module.register('tsx/esm', ...)` only for very old tsx
|
|
20
20
|
* versions (< 4.0) that predate `tsx/esm/api`.
|
|
21
21
|
* 4. If tsx isn't installed, or registration genuinely fails, surface an
|
|
22
|
-
* actionable error
|
|
22
|
+
* actionable error, including the REAL underlying error message, never
|
|
23
23
|
* a misdiagnosed "tsx is not installed".
|
|
24
24
|
*
|
|
25
|
-
* `tsx` is intentionally NOT a runtime dependency
|
|
25
|
+
* `tsx` is intentionally NOT a runtime dependency, many projects already
|
|
26
26
|
* have it, and adding a heavy dev tool to a 1-dependency ORM would be silly.
|
|
27
27
|
*/
|
|
28
28
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
@@ -109,7 +109,7 @@ function getTsLoaderError() {
|
|
|
109
109
|
}
|
|
110
110
|
/**
|
|
111
111
|
* Register the tsx ESM loader so subsequent dynamic imports of `.ts` files
|
|
112
|
-
* work. Safe to call multiple times
|
|
112
|
+
* work. Safe to call multiple times, internal flag prevents double registration.
|
|
113
113
|
*
|
|
114
114
|
* Returns:
|
|
115
115
|
* - 'registered' loader was successfully registered this call
|
|
@@ -117,7 +117,7 @@ function getTsLoaderError() {
|
|
|
117
117
|
* - 'unsupported' Node lacks `module.register()` (Node < 20.6) and tsx has
|
|
118
118
|
* no programmatic API to fall back to
|
|
119
119
|
* - 'missing' `tsx` is not installed in the user's project
|
|
120
|
-
* - 'failed' tsx IS installed but registration threw
|
|
120
|
+
* - 'failed' tsx IS installed but registration threw, see
|
|
121
121
|
* {@link getTsLoaderError} for the underlying message
|
|
122
122
|
*/
|
|
123
123
|
async function registerTsLoader() {
|
|
@@ -150,14 +150,14 @@ async function registerTsLoader() {
|
|
|
150
150
|
return 'failed';
|
|
151
151
|
}
|
|
152
152
|
}
|
|
153
|
-
// tsx/esm/api not resolvable
|
|
153
|
+
// tsx/esm/api not resolvable, is tsx installed at all?
|
|
154
154
|
if (!canResolveTsx()) {
|
|
155
155
|
tsLoaderState = 'missing';
|
|
156
156
|
return 'missing';
|
|
157
157
|
}
|
|
158
158
|
// Legacy fallback for tsx < 4.0 (no tsx/esm/api): Node's module.register.
|
|
159
159
|
// On tsx >= 4.19 this path throws ("tsx must be loaded with --import
|
|
160
|
-
// instead of --loader")
|
|
160
|
+
// instead of --loader"), but those versions all ship tsx/esm/api, so we
|
|
161
161
|
// only land here for genuinely old installs.
|
|
162
162
|
try {
|
|
163
163
|
const mod = await Promise.resolve().then(() => __importStar(require('node:module')));
|
|
@@ -177,7 +177,7 @@ async function registerTsLoader() {
|
|
|
177
177
|
return 'failed';
|
|
178
178
|
}
|
|
179
179
|
}
|
|
180
|
-
/** Reset the loader state
|
|
180
|
+
/** Reset the loader state, used by unit tests only. */
|
|
181
181
|
function _resetTsLoaderStateForTests() {
|
|
182
182
|
tsLoaderState = null;
|
|
183
183
|
tsLoaderError = null;
|
package/dist/cjs/cli/mcp.js
CHANGED
|
@@ -80,7 +80,7 @@ const TOOLS = [
|
|
|
80
80
|
},
|
|
81
81
|
{
|
|
82
82
|
name: 'explain_query',
|
|
83
|
-
description: 'Run EXPLAIN (FORMAT JSON) for a schema-validated findMany query. Pass table + optional where/orderBy/limit/select
|
|
83
|
+
description: 'Run EXPLAIN (FORMAT JSON) for a schema-validated findMany query. Pass table + optional where/orderBy/limit/select, free-form SQL is rejected.',
|
|
84
84
|
inputSchema: {
|
|
85
85
|
type: 'object',
|
|
86
86
|
properties: {
|
|
@@ -330,7 +330,7 @@ async function doctorReport(ctx) {
|
|
|
330
330
|
});
|
|
331
331
|
}
|
|
332
332
|
/**
|
|
333
|
-
* EXPLAIN a schema-validated findMany query. Free-form SQL is never accepted
|
|
333
|
+
* EXPLAIN a schema-validated findMany query. Free-form SQL is never accepted -
|
|
334
334
|
* table/field identifiers are checked against introspected metadata and the
|
|
335
335
|
* SELECT is compiled by QueryInterface (same stance as Studio `/api/builder`).
|
|
336
336
|
*/
|
|
@@ -372,7 +372,7 @@ async function explainQuery(ctx, args) {
|
|
|
372
372
|
}
|
|
373
373
|
/**
|
|
374
374
|
* Extract the allowed findMany subset for explain_query (no `with` / raw SQL).
|
|
375
|
-
* Returns a plain object cast at the buildFindMany call site
|
|
375
|
+
* Returns a plain object cast at the buildFindMany call site, same pattern as Studio.
|
|
376
376
|
*/
|
|
377
377
|
function parseExplainFindManyArgs(args) {
|
|
378
378
|
const findManyArgs = {};
|
|
@@ -650,7 +650,7 @@ function buildRelations(tableNames, columnsByTable, pkByTable, rows, enums = {})
|
|
|
650
650
|
for (const [tbl, cols] of columnsByTable) {
|
|
651
651
|
columnFieldsByTable.set(tbl, new Set(cols.map((c) => c.field)));
|
|
652
652
|
// Enum-typed columns also report tsType 'unknown', but the generated type
|
|
653
|
-
// layer gives them a concrete union
|
|
653
|
+
// layer gives them a concrete union, only json/jsonb qualify as shadows.
|
|
654
654
|
unknownTypedFieldsByTable.set(tbl, new Set(cols.filter((c) => (0, introspect_js_1.isUnknownTsType)(c.tsType) && !Object.hasOwn(enums, c.pgType)).map((c) => c.field)));
|
|
655
655
|
}
|
|
656
656
|
const relations = (0, introspect_js_1.buildRelationsFromForeignKeys)(foreignKeys, columnFieldsByTable, undefined, unknownTypedFieldsByTable);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* turbine-orm CLI
|
|
2
|
+
* turbine-orm CLI, Migration system
|
|
3
3
|
*
|
|
4
4
|
* SQL-first migrations with UP/DOWN sections, tracked in _turbine_migrations.
|
|
5
5
|
* Migration files are timestamp-prefixed .sql files.
|
|
@@ -22,7 +22,7 @@ export interface MigrationFile {
|
|
|
22
22
|
path: string;
|
|
23
23
|
/** Extracted name portion (e.g. "20260325120000_create_users") */
|
|
24
24
|
name: string;
|
|
25
|
-
/** Timestamp prefix (e.g. "20260325120000")
|
|
25
|
+
/** Timestamp prefix (e.g. "20260325120000"), YYYYMMDDHHMMSS */
|
|
26
26
|
timestamp: string;
|
|
27
27
|
}
|
|
28
28
|
export interface AppliedMigration {
|
|
@@ -95,7 +95,7 @@ export declare function sanitizeName(name: string): string;
|
|
|
95
95
|
*/
|
|
96
96
|
export declare function formatTimestamp(date: Date): string;
|
|
97
97
|
/**
|
|
98
|
-
* Get pending migration files
|
|
98
|
+
* Get pending migration files, those not yet applied.
|
|
99
99
|
* Returns files sorted by timestamp (ascending).
|
|
100
100
|
*/
|
|
101
101
|
export declare function getPendingMigrations(migrationsDir: string, applied: string[]): MigrationFile[];
|
|
@@ -227,7 +227,7 @@ export declare function createMigration(migrationsDir: string, name: string, aut
|
|
|
227
227
|
/**
|
|
228
228
|
* Derive a Postgres advisory lock ID (positive int4) from the database name.
|
|
229
229
|
*
|
|
230
|
-
* Uses FNV-1a 32-bit hash
|
|
230
|
+
* Uses FNV-1a 32-bit hash, a well-known, stable, non-cryptographic hash with
|
|
231
231
|
* excellent distribution over short strings (database names are typically <64
|
|
232
232
|
* chars). Chosen over alternatives because it's:
|
|
233
233
|
* - deterministic (same input → same output, across processes/machines)
|
|
@@ -297,7 +297,7 @@ export declare function inspectMigrationDeploy(connectionString: string, migrati
|
|
|
297
297
|
* Features:
|
|
298
298
|
* - Idempotent: running twice is safe (already-applied migrations are skipped)
|
|
299
299
|
* - Advisory lock: prevents concurrent migration runs
|
|
300
|
-
* - Checksum validation: detects modified migration files (BLOCKING
|
|
300
|
+
* - Checksum validation: detects modified migration files (BLOCKING, use
|
|
301
301
|
* `allowDrift: true` to bypass when intentionally rewriting history)
|
|
302
302
|
* - Each migration runs in its own transaction
|
|
303
303
|
*
|
package/dist/cjs/cli/migrate.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
* turbine-orm CLI
|
|
3
|
+
* turbine-orm CLI, Migration system
|
|
4
4
|
*
|
|
5
5
|
* SQL-first migrations with UP/DOWN sections, tracked in _turbine_migrations.
|
|
6
6
|
* Migration files are timestamp-prefixed .sql files.
|
|
@@ -138,7 +138,7 @@ function formatTimestamp(date) {
|
|
|
138
138
|
].join('');
|
|
139
139
|
}
|
|
140
140
|
/**
|
|
141
|
-
* Get pending migration files
|
|
141
|
+
* Get pending migration files, those not yet applied.
|
|
142
142
|
* Returns files sorted by timestamp (ascending).
|
|
143
143
|
*/
|
|
144
144
|
function getPendingMigrations(migrationsDir, applied) {
|
|
@@ -642,7 +642,7 @@ ${autoContent.down}
|
|
|
642
642
|
/**
|
|
643
643
|
* Derive a Postgres advisory lock ID (positive int4) from the database name.
|
|
644
644
|
*
|
|
645
|
-
* Uses FNV-1a 32-bit hash
|
|
645
|
+
* Uses FNV-1a 32-bit hash, a well-known, stable, non-cryptographic hash with
|
|
646
646
|
* excellent distribution over short strings (database names are typically <64
|
|
647
647
|
* chars). Chosen over alternatives because it's:
|
|
648
648
|
* - deterministic (same input → same output, across processes/machines)
|
|
@@ -759,7 +759,7 @@ function formatChecksumMismatchError(mismatches) {
|
|
|
759
759
|
const modified = mismatches.filter((m) => m.type === 'modified');
|
|
760
760
|
const missing = mismatches.filter((m) => m.type === 'missing');
|
|
761
761
|
const lines = [
|
|
762
|
-
'[turbine] Migration drift detected
|
|
762
|
+
'[turbine] Migration drift detected, refusing to apply pending migrations.',
|
|
763
763
|
'',
|
|
764
764
|
'Applied migrations should be immutable. The following files no longer match their applied state:',
|
|
765
765
|
'',
|
|
@@ -781,7 +781,7 @@ function formatChecksumMismatchError(mismatches) {
|
|
|
781
781
|
if (missing.length > 0) {
|
|
782
782
|
lines.push(' (deleted files cannot be rolled back: restore the file, then run `migrate down` if needed), OR');
|
|
783
783
|
}
|
|
784
|
-
lines.push(' 3. Pass `--allow-drift` to bypass this check (advanced
|
|
784
|
+
lines.push(' 3. Pass `--allow-drift` to bypass this check (advanced, make sure you know what you are doing).');
|
|
785
785
|
return lines.join('\n');
|
|
786
786
|
}
|
|
787
787
|
/**
|
|
@@ -850,7 +850,7 @@ async function inspectMigrationDeploy(connectionString, migrationsDir) {
|
|
|
850
850
|
* Features:
|
|
851
851
|
* - Idempotent: running twice is safe (already-applied migrations are skipped)
|
|
852
852
|
* - Advisory lock: prevents concurrent migration runs
|
|
853
|
-
* - Checksum validation: detects modified migration files (BLOCKING
|
|
853
|
+
* - Checksum validation: detects modified migration files (BLOCKING, use
|
|
854
854
|
* `allowDrift: true` to bypass when intentionally rewriting history)
|
|
855
855
|
* - Each migration runs in its own transaction
|
|
856
856
|
*
|
|
@@ -874,7 +874,7 @@ async function migrateUp(connectionString, migrationsDir, options) {
|
|
|
874
874
|
const adapter = options?.adapter;
|
|
875
875
|
const gotLock = await acquireLock(client, lockId, adapter);
|
|
876
876
|
if (!gotLock) {
|
|
877
|
-
throw new errors_js_1.MigrationError('[turbine] Could not acquire migration lock
|
|
877
|
+
throw new errors_js_1.MigrationError('[turbine] Could not acquire migration lock, another migration is already running');
|
|
878
878
|
}
|
|
879
879
|
try {
|
|
880
880
|
await ensureTrackingTable(client, dialect);
|
|
@@ -911,7 +911,7 @@ async function migrateUp(connectionString, migrationsDir, options) {
|
|
|
911
911
|
// Data-loss gate: refuse to run pending migrations containing destructive
|
|
912
912
|
// statements unless the caller has EXPLICITLY opted in. The CLI layers an
|
|
913
913
|
// interactive typed confirmation on top of this; programmatic callers must
|
|
914
|
-
// pass `allowDestructive: true`. Safe-by-default is the whole point
|
|
914
|
+
// pass `allowDestructive: true`. Safe-by-default is the whole point, a
|
|
915
915
|
// DROP TABLE should never run just because a file exists.
|
|
916
916
|
if (!options?.allowDestructive && destructive.length > 0) {
|
|
917
917
|
const lines = ['[turbine] Refusing to apply migrations containing DESTRUCTIVE statements:', ''];
|
|
@@ -1025,7 +1025,7 @@ async function migrateDown(connectionString, migrationsDir, options) {
|
|
|
1025
1025
|
const adapter = options?.adapter;
|
|
1026
1026
|
const gotLock = await acquireLock(client, lockId, adapter);
|
|
1027
1027
|
if (!gotLock) {
|
|
1028
|
-
throw new errors_js_1.MigrationError('[turbine] Could not acquire migration lock
|
|
1028
|
+
throw new errors_js_1.MigrationError('[turbine] Could not acquire migration lock, another migration is already running');
|
|
1029
1029
|
}
|
|
1030
1030
|
try {
|
|
1031
1031
|
await ensureTrackingTable(client, dialect);
|
|
@@ -1035,9 +1035,9 @@ async function migrateDown(connectionString, migrationsDir, options) {
|
|
|
1035
1035
|
}
|
|
1036
1036
|
const allFiles = listMigrationFiles(migrationsDir);
|
|
1037
1037
|
const fileMap = new Map(allFiles.map((f) => [f.name, f]));
|
|
1038
|
-
// Reverse order
|
|
1038
|
+
// Reverse order, rollback most recent first
|
|
1039
1039
|
const toRollback = applied.reverse().slice(0, options?.step ?? 1);
|
|
1040
|
-
// Same data-loss gate as migrateUp
|
|
1040
|
+
// Same data-loss gate as migrateUp, DOWN sections routinely contain
|
|
1041
1041
|
// DROP TABLE (the legitimate reverse of a CREATE), which still destroys
|
|
1042
1042
|
// every row written since the migration ran. Explicit opt-in required.
|
|
1043
1043
|
if (!options?.allowDestructive) {
|