turbine-orm 0.49.0 → 0.50.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 +122 -39
- package/dist/cjs/adapters/cockroachdb.d.ts +39 -0
- package/dist/cjs/adapters/index.d.ts +110 -0
- package/dist/cjs/adapters/yugabytedb.d.ts +51 -0
- package/dist/cjs/cli/config.d.ts +181 -0
- package/dist/cjs/cli/config.js +32 -6
- package/dist/cjs/cli/destructive.d.ts +38 -0
- package/dist/cjs/cli/index.d.ts +359 -0
- package/dist/cjs/cli/index.js +228 -56
- package/dist/cjs/cli/loader.d.ts +61 -0
- package/dist/cjs/cli/mcp.d.ts +42 -0
- package/dist/cjs/cli/migrate.d.ts +356 -0
- package/dist/cjs/cli/migrate.js +131 -40
- package/dist/cjs/cli/observe-ui.d.ts +1 -0
- package/dist/cjs/cli/observe-ui.js +14 -5
- package/dist/cjs/cli/observe.d.ts +25 -0
- package/dist/cjs/cli/observe.js +49 -12
- package/dist/cjs/cli/pii-tags.d.ts +53 -0
- package/dist/cjs/cli/prisma-report.d.ts +33 -0
- package/dist/cjs/cli/prisma-report.js +73 -0
- package/dist/cjs/cli/prisma-resolve.d.ts +106 -0
- package/dist/cjs/cli/prisma-resolve.js +1 -0
- package/dist/cjs/cli/prisma-schema.d.ts +176 -0
- package/dist/cjs/cli/prisma-schema.js +82 -4
- package/dist/cjs/cli/rate-limit.d.ts +32 -0
- package/dist/cjs/cli/rate-limit.js +45 -0
- package/dist/cjs/cli/studio-demo.d.ts +43 -0
- package/dist/cjs/cli/studio-ui.generated.d.ts +1 -0
- package/dist/cjs/cli/studio.d.ts +207 -0
- package/dist/cjs/cli/studio.js +136 -71
- package/dist/cjs/cli/ui.d.ts +73 -0
- package/dist/cjs/cli/ui.js +51 -9
- package/dist/cjs/client.d.ts +837 -0
- package/dist/cjs/client.js +3 -0
- package/dist/cjs/dialect.d.ts +516 -0
- package/dist/cjs/dialect.js +37 -12
- package/dist/cjs/errors.d.ts +370 -0
- package/dist/cjs/generate.d.ts +137 -0
- package/dist/cjs/generate.js +39 -6
- package/dist/cjs/index-advisor.d.ts +153 -0
- package/dist/cjs/index-stats.d.ts +384 -0
- package/dist/cjs/index.d.ts +55 -0
- package/dist/cjs/index.js +7 -2
- package/dist/cjs/introspect.d.ts +269 -0
- package/dist/cjs/mssql.d.ts +232 -0
- package/dist/cjs/mssql.js +6 -0
- package/dist/cjs/mysql.d.ts +173 -0
- package/dist/cjs/mysql.js +16 -0
- package/dist/cjs/nested-write.d.ts +96 -0
- package/dist/cjs/nested-write.js +414 -24
- package/dist/cjs/observe.d.ts +115 -0
- package/dist/cjs/optional-peer-import.d.cts +72 -0
- package/dist/cjs/pipeline-submittable.d.ts +93 -0
- package/dist/cjs/pipeline.d.ts +71 -0
- package/dist/cjs/powdb-introspect.d.ts +84 -0
- package/dist/cjs/powdb.d.ts +931 -0
- package/dist/cjs/powdb.js +106 -21
- package/dist/cjs/powql.d.ts +592 -0
- package/dist/cjs/powql.js +42 -6
- package/dist/cjs/prisma-compat.d.ts +283 -0
- package/dist/cjs/prisma-compat.js +167 -9
- package/dist/cjs/query/aggregates.d.ts +92 -0
- package/dist/cjs/query/aggregates.js +7 -3
- package/dist/cjs/query/batched-loader.d.ts +193 -0
- package/dist/cjs/query/builder.d.ts +849 -0
- package/dist/cjs/query/builder.js +571 -65
- package/dist/cjs/query/compound-unique.d.ts +51 -0
- package/dist/cjs/query/deferred.d.ts +223 -0
- package/dist/cjs/query/filters.d.ts +201 -0
- package/dist/cjs/query/index.d.ts +14 -0
- package/dist/cjs/query/index.js +6 -1
- package/dist/cjs/query/relations.d.ts +609 -0
- package/dist/cjs/query/relations.js +693 -46
- package/dist/cjs/query/types.d.ts +1300 -0
- package/dist/cjs/query/utils.d.ts +209 -0
- package/dist/cjs/query/utils.js +208 -1
- package/dist/cjs/query/warn-registry.d.ts +68 -0
- package/dist/cjs/query/warn-registry.js +9 -0
- package/dist/cjs/query/where-compile.d.ts +139 -0
- package/dist/cjs/query/where.d.ts +548 -0
- package/dist/cjs/query/where.js +58 -22
- package/dist/cjs/query/writes.d.ts +172 -0
- package/dist/cjs/query/writes.js +105 -12
- package/dist/cjs/realtime.d.ts +70 -0
- package/dist/cjs/schema-builder.d.ts +354 -0
- package/dist/cjs/schema-metadata.d.ts +83 -0
- package/dist/cjs/schema-sql.d.ts +217 -0
- package/dist/cjs/schema-sql.js +23 -5
- package/dist/cjs/schema.d.ts +356 -0
- package/dist/cjs/schema.js +125 -0
- package/dist/cjs/seed.d.ts +15 -0
- package/dist/cjs/serverless.d.ts +142 -0
- package/dist/cjs/sqlite.d.ts +143 -0
- package/dist/cjs/sqlite.js +4 -0
- package/dist/cjs/typed-sql.d.ts +102 -0
- package/dist/cli/config.d.ts +18 -4
- package/dist/cli/config.js +31 -6
- package/dist/cli/index.d.ts +123 -0
- package/dist/cli/index.js +223 -58
- package/dist/cli/migrate.d.ts +59 -10
- package/dist/cli/migrate.js +128 -41
- package/dist/cli/observe-ui.d.ts +1 -1
- package/dist/cli/observe-ui.js +14 -5
- package/dist/cli/observe.d.ts +7 -1
- package/dist/cli/observe.js +48 -12
- package/dist/cli/prisma-report.d.ts +14 -0
- package/dist/cli/prisma-report.js +72 -0
- package/dist/cli/prisma-resolve.d.ts +6 -0
- package/dist/cli/prisma-resolve.js +1 -0
- package/dist/cli/prisma-schema.d.ts +62 -2
- package/dist/cli/prisma-schema.js +81 -4
- package/dist/cli/rate-limit.d.ts +32 -0
- package/dist/cli/rate-limit.js +40 -0
- package/dist/cli/studio.d.ts +5 -5
- package/dist/cli/studio.js +135 -70
- package/dist/cli/ui.d.ts +1 -1
- package/dist/cli/ui.js +51 -9
- package/dist/client.d.ts +40 -0
- package/dist/client.js +3 -0
- package/dist/dialect.d.ts +17 -1
- package/dist/dialect.js +37 -12
- package/dist/generate.js +40 -7
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/mssql.js +6 -0
- package/dist/mysql.js +16 -0
- package/dist/nested-write.d.ts +2 -0
- package/dist/nested-write.js +415 -25
- package/dist/powdb.d.ts +4 -2
- package/dist/powdb.js +106 -21
- package/dist/powql.d.ts +5 -0
- package/dist/powql.js +42 -6
- package/dist/prisma-compat.d.ts +2 -0
- package/dist/prisma-compat.js +166 -8
- package/dist/query/aggregates.js +7 -3
- package/dist/query/builder.d.ts +292 -21
- package/dist/query/builder.js +570 -64
- package/dist/query/deferred.d.ts +39 -0
- package/dist/query/index.d.ts +1 -1
- package/dist/query/index.js +1 -1
- package/dist/query/relations.d.ts +173 -5
- package/dist/query/relations.js +688 -47
- package/dist/query/types.d.ts +123 -39
- package/dist/query/utils.d.ts +116 -0
- package/dist/query/utils.js +198 -0
- package/dist/query/warn-registry.d.ts +9 -0
- package/dist/query/warn-registry.js +9 -0
- package/dist/query/where.d.ts +38 -1
- package/dist/query/where.js +58 -23
- package/dist/query/writes.d.ts +42 -1
- package/dist/query/writes.js +104 -13
- package/dist/schema-sql.d.ts +14 -0
- package/dist/schema-sql.js +23 -5
- package/dist/schema.d.ts +38 -0
- package/dist/schema.js +123 -0
- package/dist/sqlite.js +4 -0
- package/package.json +77 -28
package/dist/cjs/cli/index.js
CHANGED
|
@@ -60,10 +60,17 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
60
60
|
})();
|
|
61
61
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
62
62
|
exports.parseArgs = parseArgs;
|
|
63
|
+
exports.usesProjectConfig = usesProjectConfig;
|
|
64
|
+
exports.bootstrapCliConfig = bootstrapCliConfig;
|
|
63
65
|
exports.loadDotEnvForCli = loadDotEnvForCli;
|
|
64
66
|
exports.dotEnvUrlConflictWarning = dotEnvUrlConflictWarning;
|
|
67
|
+
exports.detectPackageManager = detectPackageManager;
|
|
68
|
+
exports.tsxInstallCommand = tsxInstallCommand;
|
|
69
|
+
exports.tsxRequiredNotice = tsxRequiredNotice;
|
|
65
70
|
exports.detectConsumerModuleType = detectConsumerModuleType;
|
|
66
71
|
exports.planInitSteps = planInitSteps;
|
|
72
|
+
exports.initEnvNotice = initEnvNotice;
|
|
73
|
+
exports.resolveMigrateFromPrismaUrl = resolveMigrateFromPrismaUrl;
|
|
67
74
|
exports.buildMigrateDeployOptions = buildMigrateDeployOptions;
|
|
68
75
|
exports.getSeedExecutionPlan = getSeedExecutionPlan;
|
|
69
76
|
exports.isLoopbackHost = isLoopbackHost;
|
|
@@ -310,9 +317,49 @@ function failMissingTsLoader(filePath, reason) {
|
|
|
310
317
|
process.exit(1);
|
|
311
318
|
}
|
|
312
319
|
// ---------------------------------------------------------------------------
|
|
313
|
-
//
|
|
320
|
+
// Config bootstrap
|
|
314
321
|
// ---------------------------------------------------------------------------
|
|
315
|
-
|
|
322
|
+
/**
|
|
323
|
+
* Does this invocation need a `turbine.config.*` file?
|
|
324
|
+
*
|
|
325
|
+
* Everything does, with one deliberate exception: `turbine studio --demo` boots
|
|
326
|
+
* a seeded in-memory database, needs no `DATABASE_URL` and no config file, and
|
|
327
|
+
* is the very next command the quickstart suggests after `turbine init`.
|
|
328
|
+
* Resolving the config anyway means a freshly scaffolded directory (a
|
|
329
|
+
* `turbine.config.ts` with `tsx` not installed yet) dies with "Cannot load
|
|
330
|
+
* TypeScript file" before demo mode ever starts.
|
|
331
|
+
*
|
|
332
|
+
* @internal exported for tests.
|
|
333
|
+
*/
|
|
334
|
+
function usesProjectConfig(args) {
|
|
335
|
+
return !(args.command === 'studio' && args.demo === true);
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Resolve the effective CLI config: register the tsx loader when the config file
|
|
339
|
+
* is TypeScript, import it, then merge it with env vars and CLI flags. Exits with
|
|
340
|
+
* the actionable "Cannot load TypeScript file" error when a `.ts` config cannot
|
|
341
|
+
* be loaded. Config-free invocations short-circuit without touching the disk.
|
|
342
|
+
*
|
|
343
|
+
* @internal exported for tests.
|
|
344
|
+
*/
|
|
345
|
+
async function bootstrapCliConfig(args, overrides) {
|
|
346
|
+
if (!usesProjectConfig(args)) {
|
|
347
|
+
return { config: (0, config_js_1.resolveConfig)({}, overrides), fileConfig: {}, skipped: true };
|
|
348
|
+
}
|
|
349
|
+
// If the user has a TypeScript config file, register the tsx ESM loader
|
|
350
|
+
// before we attempt to import it. Otherwise Node throws
|
|
351
|
+
// ERR_UNKNOWN_FILE_EXTENSION for `.ts`.
|
|
352
|
+
const configPath = (0, config_js_1.findConfigFile)();
|
|
353
|
+
if ((0, loader_js_1.needsTsLoader)(configPath)) {
|
|
354
|
+
const status = await (0, loader_js_1.registerTsLoader)();
|
|
355
|
+
if (status === 'missing' || status === 'unsupported' || status === 'failed') {
|
|
356
|
+
failMissingTsLoader(configPath ?? 'turbine.config.ts', status);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
const { config: fileConfig, loadError } = await (0, config_js_1.loadConfigResult)();
|
|
360
|
+
return { config: (0, config_js_1.resolveConfig)(fileConfig, overrides), fileConfig, loadError, skipped: false };
|
|
361
|
+
}
|
|
362
|
+
function requireUrl(config, options = {}) {
|
|
316
363
|
if (!config.url) {
|
|
317
364
|
(0, ui_js_1.error)('No database URL provided.');
|
|
318
365
|
(0, ui_js_1.newline)();
|
|
@@ -322,6 +369,12 @@ function requireUrl(config) {
|
|
|
322
369
|
const envFileNote = typeof process.loadEnvFile === 'function' ? '(auto-loaded)' : '(needs Node 20.12+ to auto-load)';
|
|
323
370
|
console.log(` ${(0, ui_js_1.dim)('2.')} Set ${(0, ui_js_1.cyan)('DATABASE_URL')} in your environment or a ${(0, ui_js_1.cyan)('.env')} file ${(0, ui_js_1.dim)(envFileNote)}`);
|
|
324
371
|
console.log(` ${(0, ui_js_1.dim)('3.')} Pass ${(0, ui_js_1.cyan)('--url')} flag`);
|
|
372
|
+
const vars = options.datasourceVars ?? [];
|
|
373
|
+
if (vars.length > 0) {
|
|
374
|
+
const list = vars.map((v) => (0, ui_js_1.cyan)(v)).join(', ');
|
|
375
|
+
const plural = vars.length > 1 ? 'these variables' : 'this variable';
|
|
376
|
+
console.log(` ${(0, ui_js_1.dim)('4.')} Set ${list} ${(0, ui_js_1.dim)(`(${plural}, declared by your schema.prisma datasource, ${vars.length > 1 ? 'are' : 'is'} unset)`)}`);
|
|
377
|
+
}
|
|
325
378
|
(0, ui_js_1.newline)();
|
|
326
379
|
process.exit(1);
|
|
327
380
|
}
|
|
@@ -473,6 +526,57 @@ function dotEnvUrlConflictWarning(input) {
|
|
|
473
526
|
`(${(0, ui_js_1.redactUrl)(fileUrl)}). Using the .env value. Remove DATABASE_URL from .env, or unset the config url, ` +
|
|
474
527
|
`to silence this.`);
|
|
475
528
|
}
|
|
529
|
+
/**
|
|
530
|
+
* Detect the consumer's package manager from its lockfile, defaulting to npm.
|
|
531
|
+
* Used only to print an exact, copy-pasteable install command.
|
|
532
|
+
*
|
|
533
|
+
* @internal exported for tests.
|
|
534
|
+
*/
|
|
535
|
+
function detectPackageManager(cwd = process.cwd()) {
|
|
536
|
+
if ((0, node_fs_1.existsSync)((0, node_path_1.join)(cwd, 'pnpm-lock.yaml')))
|
|
537
|
+
return 'pnpm';
|
|
538
|
+
if ((0, node_fs_1.existsSync)((0, node_path_1.join)(cwd, 'yarn.lock')))
|
|
539
|
+
return 'yarn';
|
|
540
|
+
if ((0, node_fs_1.existsSync)((0, node_path_1.join)(cwd, 'bun.lockb')) || (0, node_fs_1.existsSync)((0, node_path_1.join)(cwd, 'bun.lock')))
|
|
541
|
+
return 'bun';
|
|
542
|
+
return 'npm';
|
|
543
|
+
}
|
|
544
|
+
/**
|
|
545
|
+
* The exact "add tsx as a dev dependency" command for a package manager.
|
|
546
|
+
*
|
|
547
|
+
* @internal exported for tests.
|
|
548
|
+
*/
|
|
549
|
+
function tsxInstallCommand(pm) {
|
|
550
|
+
switch (pm) {
|
|
551
|
+
case 'pnpm':
|
|
552
|
+
return 'pnpm add -D tsx';
|
|
553
|
+
case 'yarn':
|
|
554
|
+
return 'yarn add -D tsx';
|
|
555
|
+
case 'bun':
|
|
556
|
+
return 'bun add -d tsx';
|
|
557
|
+
default:
|
|
558
|
+
return 'npm install --save-dev tsx';
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
/**
|
|
562
|
+
* The heads-up `turbine init` prints when it has just scaffolded TypeScript
|
|
563
|
+
* files and `tsx` is not resolvable: without it the CLI cannot load them, and
|
|
564
|
+
* the very next command the user runs dies on "Cannot load TypeScript file".
|
|
565
|
+
* Pure (returns the lines, prints nothing) so it can be asserted in tests.
|
|
566
|
+
*
|
|
567
|
+
* @internal exported for tests.
|
|
568
|
+
*/
|
|
569
|
+
function tsxRequiredNotice(tsFiles, installCommand) {
|
|
570
|
+
return [
|
|
571
|
+
`Turbine needs ${(0, ui_js_1.cyan)('tsx')} to load the TypeScript files just created:`,
|
|
572
|
+
...tsFiles.map((f) => ` ${(0, ui_js_1.dim)(ui_js_1.symbols.dot)} ${(0, ui_js_1.cyan)(f)}`),
|
|
573
|
+
'',
|
|
574
|
+
` ${(0, ui_js_1.dim)('Install it as a dev dependency:')}`,
|
|
575
|
+
` ${(0, ui_js_1.cyan)(installCommand)}`,
|
|
576
|
+
'',
|
|
577
|
+
` ${(0, ui_js_1.dim)('Without it, the next Turbine command fails with')} ${(0, ui_js_1.dim)('"Cannot load TypeScript file".')}`,
|
|
578
|
+
];
|
|
579
|
+
}
|
|
476
580
|
/**
|
|
477
581
|
* Read the consumer's `package.json` `"type"` field. Returns `'module'` for an
|
|
478
582
|
* ESM project, `'commonjs'` for an explicit or absent (defaulted) CommonJS
|
|
@@ -798,6 +902,48 @@ function initPromptQuestion(step, config, seedFilePath) {
|
|
|
798
902
|
return `Run ${initStepLabel(step.id)}?`;
|
|
799
903
|
}
|
|
800
904
|
}
|
|
905
|
+
/**
|
|
906
|
+
* Decide which connection notice `turbine init` prints. Pure so the whole
|
|
907
|
+
* decision matrix is testable.
|
|
908
|
+
*
|
|
909
|
+
* "No DATABASE_URL found in environment" is reserved for the case where NO
|
|
910
|
+
* source supplied one: printing it while happily using `--url` (or a config
|
|
911
|
+
* `url`) reads like a failure the user then goes looking for.
|
|
912
|
+
*
|
|
913
|
+
* @internal exported for tests.
|
|
914
|
+
*/
|
|
915
|
+
function initEnvNotice(input) {
|
|
916
|
+
if (input.envUrl) {
|
|
917
|
+
return { kind: 'success', message: `Detected ${(0, ui_js_1.cyan)('DATABASE_URL')} in the environment` };
|
|
918
|
+
}
|
|
919
|
+
if (input.hasEnvFile && !input.canAutoLoadEnv) {
|
|
920
|
+
return {
|
|
921
|
+
kind: 'info',
|
|
922
|
+
message: `Found ${(0, ui_js_1.cyan)('.env')} ${(0, ui_js_1.dim)('(this Node version cannot auto-load it. Upgrade to Node 20.12+ or export')} ${(0, ui_js_1.cyan)('DATABASE_URL')}${(0, ui_js_1.dim)(')')}`,
|
|
923
|
+
};
|
|
924
|
+
}
|
|
925
|
+
if (input.hasEnvFile) {
|
|
926
|
+
// .env exists but did not provide DATABASE_URL; if it had, the auto-load
|
|
927
|
+
// in main() would have populated envUrl above.
|
|
928
|
+
return {
|
|
929
|
+
kind: 'info',
|
|
930
|
+
message: `Found ${(0, ui_js_1.cyan)('.env')} ${(0, ui_js_1.dim)('(no')} ${(0, ui_js_1.cyan)('DATABASE_URL')} ${(0, ui_js_1.dim)('set in it yet)')}`,
|
|
931
|
+
};
|
|
932
|
+
}
|
|
933
|
+
if (input.hasEnvLocal) {
|
|
934
|
+
return {
|
|
935
|
+
kind: 'info',
|
|
936
|
+
message: `Found ${(0, ui_js_1.cyan)('.env.local')} ${(0, ui_js_1.dim)('(note: Turbine only auto-loads')} ${(0, ui_js_1.cyan)('.env')}${(0, ui_js_1.dim)(')')}`,
|
|
937
|
+
};
|
|
938
|
+
}
|
|
939
|
+
if (input.flagUrl) {
|
|
940
|
+
return { kind: 'success', message: `Using the connection string passed with ${(0, ui_js_1.cyan)('--url')}` };
|
|
941
|
+
}
|
|
942
|
+
if (input.configUrl) {
|
|
943
|
+
return { kind: 'success', message: `Using the ${(0, ui_js_1.cyan)('url')} from your config file` };
|
|
944
|
+
}
|
|
945
|
+
return { kind: 'info', message: `No ${(0, ui_js_1.cyan)('DATABASE_URL')} found in environment` };
|
|
946
|
+
}
|
|
801
947
|
async function cmdInit(args, config) {
|
|
802
948
|
(0, ui_js_1.banner)();
|
|
803
949
|
(0, ui_js_1.header)('Initializing Turbine project');
|
|
@@ -810,23 +956,18 @@ async function cmdInit(args, config) {
|
|
|
810
956
|
// On Node < 20.12 (no process.loadEnvFile) main() could not auto-load .env, so
|
|
811
957
|
// we cannot claim it "has no DATABASE_URL"; we simply could not read it.
|
|
812
958
|
const canAutoLoadEnv = typeof process.loadEnvFile === 'function';
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
(0, ui_js_1.
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
(0, ui_js_1.info)(`Found ${(0, ui_js_1.cyan)('.env.local')} ${(0, ui_js_1.dim)('(note: Turbine only auto-loads')} ${(0, ui_js_1.cyan)('.env')}${(0, ui_js_1.dim)(')')}`);
|
|
826
|
-
}
|
|
827
|
-
else {
|
|
828
|
-
(0, ui_js_1.info)(`No ${(0, ui_js_1.cyan)('DATABASE_URL')} found in environment`);
|
|
829
|
-
}
|
|
959
|
+
const envNotice = initEnvNotice({
|
|
960
|
+
envUrl,
|
|
961
|
+
hasEnvFile,
|
|
962
|
+
hasEnvLocal,
|
|
963
|
+
canAutoLoadEnv,
|
|
964
|
+
flagUrl: args.url,
|
|
965
|
+
configUrl: config.url,
|
|
966
|
+
});
|
|
967
|
+
if (envNotice.kind === 'success')
|
|
968
|
+
(0, ui_js_1.success)(envNotice.message);
|
|
969
|
+
else
|
|
970
|
+
(0, ui_js_1.info)(envNotice.message);
|
|
830
971
|
(0, ui_js_1.newline)();
|
|
831
972
|
// Heads-up (not an edit) about the consumer's module system. A CommonJS
|
|
832
973
|
// project (`npm init -y` default, or no "type" field) works fine now that the
|
|
@@ -843,7 +984,9 @@ async function cmdInit(args, config) {
|
|
|
843
984
|
const hasUrl = Boolean(url);
|
|
844
985
|
const interactive = Boolean(process.stdin.isTTY);
|
|
845
986
|
const yes = args.yes === true;
|
|
846
|
-
|
|
987
|
+
// Where init scaffolds the seed file. A root-level ./seed.ts from an older
|
|
988
|
+
// init still counts, so a re-run never scaffolds a second seed file.
|
|
989
|
+
const seedFilePath = config.seedFile ?? ((0, node_fs_1.existsSync)('./seed.ts') ? './seed.ts' : config_js_1.DEFAULT_INIT_SEED_FILE);
|
|
847
990
|
const configPath = (0, config_js_1.findConfigFile)();
|
|
848
991
|
const state = {
|
|
849
992
|
configExists: Boolean(configPath),
|
|
@@ -877,6 +1020,8 @@ async function cmdInit(args, config) {
|
|
|
877
1020
|
// Supporting dirs + .gitignore, regardless of prompts (unchanged behavior).
|
|
878
1021
|
ensureInitScaffoldDirs(config);
|
|
879
1022
|
(0, ui_js_1.newline)();
|
|
1023
|
+
// TypeScript files this run actually created: they drive the tsx heads-up below.
|
|
1024
|
+
const tsFilesWritten = [];
|
|
880
1025
|
// Execute the plan in order. `run` proceeds; `prompt` asks; `skip` reports.
|
|
881
1026
|
for (const step of plan) {
|
|
882
1027
|
if (step.action === 'skip') {
|
|
@@ -892,12 +1037,17 @@ async function cmdInit(args, config) {
|
|
|
892
1037
|
case 'config':
|
|
893
1038
|
(0, node_fs_1.writeFileSync)('turbine.config.ts', (0, config_js_1.configTemplate)(args.url ?? undefined), 'utf-8');
|
|
894
1039
|
(0, ui_js_1.success)(state.configExists ? `Overwrote ${(0, ui_js_1.cyan)('turbine.config.ts')}` : `Created ${(0, ui_js_1.cyan)('turbine.config.ts')}`);
|
|
1040
|
+
tsFilesWritten.push('turbine.config.ts');
|
|
895
1041
|
break;
|
|
896
1042
|
case 'schema':
|
|
897
1043
|
writeInitSchemaTemplate(config);
|
|
1044
|
+
if ((0, loader_js_1.needsTsLoader)(config.schemaFile))
|
|
1045
|
+
tsFilesWritten.push(config.schemaFile);
|
|
898
1046
|
break;
|
|
899
1047
|
case 'seed-file':
|
|
900
1048
|
writeInitSeedTemplate(seedFilePath);
|
|
1049
|
+
if ((0, loader_js_1.needsTsLoader)(seedFilePath))
|
|
1050
|
+
tsFilesWritten.push(seedFilePath);
|
|
901
1051
|
break;
|
|
902
1052
|
case 'push':
|
|
903
1053
|
await runInitPush(config, url);
|
|
@@ -910,6 +1060,17 @@ async function cmdInit(args, config) {
|
|
|
910
1060
|
break;
|
|
911
1061
|
}
|
|
912
1062
|
}
|
|
1063
|
+
// Scaffolding .ts files and staying silent about tsx is how a fresh project
|
|
1064
|
+
// hits "Cannot load TypeScript file" on its very next command. Warn now, name
|
|
1065
|
+
// the exact install command, and never install anything ourselves.
|
|
1066
|
+
const tsxMissing = tsFilesWritten.length > 0 && !(0, loader_js_1.canResolveTsx)();
|
|
1067
|
+
if (tsxMissing) {
|
|
1068
|
+
(0, ui_js_1.newline)();
|
|
1069
|
+
const [headline, ...rest] = tsxRequiredNotice(tsFilesWritten, tsxInstallCommand(detectPackageManager()));
|
|
1070
|
+
(0, ui_js_1.warn)(headline ?? '');
|
|
1071
|
+
for (const line of rest)
|
|
1072
|
+
console.log(line);
|
|
1073
|
+
}
|
|
913
1074
|
if (degraded) {
|
|
914
1075
|
(0, ui_js_1.newline)();
|
|
915
1076
|
(0, ui_js_1.info)('Non-interactive shell: interactive prompts were skipped.');
|
|
@@ -927,8 +1088,9 @@ async function cmdInit(args, config) {
|
|
|
927
1088
|
console.log(` ${(0, ui_js_1.dim)('or create a')} ${(0, ui_js_1.cyan)('.env')} ${(0, ui_js_1.dim)('file with')} ${(0, ui_js_1.cyan)('DATABASE_URL=postgres://...')}`);
|
|
928
1089
|
}
|
|
929
1090
|
console.log(` ${(0, ui_js_1.dim)('2.')} Run ${(0, ui_js_1.cyan)('npx turbine generate')} to introspect your DB`);
|
|
930
|
-
|
|
931
|
-
|
|
1091
|
+
// Only when the fuller heads-up above did not already run (nothing scaffolded).
|
|
1092
|
+
if (!tsxMissing && !(0, loader_js_1.canResolveTsx)()) {
|
|
1093
|
+
console.log(` ${(0, ui_js_1.dim)('Note: the TypeScript config requires')} ${(0, ui_js_1.cyan)('tsx')}: ${(0, ui_js_1.cyan)(tsxInstallCommand(detectPackageManager()))}`);
|
|
932
1094
|
}
|
|
933
1095
|
}
|
|
934
1096
|
else {
|
|
@@ -1053,20 +1215,30 @@ async function cmdGenerate(args, config) {
|
|
|
1053
1215
|
console.log(` ${(0, ui_js_1.dim)('const user = await db.users.findUnique({ where: { id: 1 } });')}`);
|
|
1054
1216
|
(0, ui_js_1.newline)();
|
|
1055
1217
|
}
|
|
1056
|
-
// ---------------------------------------------------------------------------
|
|
1057
|
-
// migrate-from-prisma
|
|
1058
|
-
// ---------------------------------------------------------------------------
|
|
1059
1218
|
/**
|
|
1060
|
-
* `
|
|
1061
|
-
* schema, resolve its models/fields/relations/compound-uniques against the live
|
|
1062
|
-
* database (unless `--no-db`), and emit (a) a Markdown resolution report and
|
|
1063
|
-
* (b) a typed `prisma-map.ts` name map next to the generated client.
|
|
1219
|
+
* Pick the connection string for `migrate-from-prisma`.
|
|
1064
1220
|
*
|
|
1065
|
-
*
|
|
1066
|
-
*
|
|
1067
|
-
*
|
|
1068
|
-
*
|
|
1221
|
+
* `configUrl` is what {@link resolveConfig} already produced (`--url`, then
|
|
1222
|
+
* `DATABASE_URL`, then `turbine.config.ts`) and always wins: an explicit flag
|
|
1223
|
+
* must never be overridden by a value declared in someone else's schema file.
|
|
1224
|
+
* Only when that is empty do we fall back to the `datasource` block, which
|
|
1225
|
+
* removes the flag a project with a non-standard variable name would otherwise
|
|
1226
|
+
* pass on every run.
|
|
1069
1227
|
*/
|
|
1228
|
+
function resolveMigrateFromPrismaUrl(configUrl, ast, env) {
|
|
1229
|
+
const lookup = (0, prisma_schema_js_1.resolvePrismaDatasourceUrl)(ast, env);
|
|
1230
|
+
if (configUrl)
|
|
1231
|
+
return { url: configUrl, source: 'config', missingVariables: lookup.missingVariables };
|
|
1232
|
+
if (lookup.resolved) {
|
|
1233
|
+
return {
|
|
1234
|
+
url: lookup.resolved.url,
|
|
1235
|
+
source: 'datasource',
|
|
1236
|
+
datasource: lookup.resolved,
|
|
1237
|
+
missingVariables: lookup.missingVariables,
|
|
1238
|
+
};
|
|
1239
|
+
}
|
|
1240
|
+
return { source: 'none', missingVariables: lookup.missingVariables };
|
|
1241
|
+
}
|
|
1070
1242
|
async function cmdMigrateFromPrisma(args, config) {
|
|
1071
1243
|
(0, ui_js_1.banner)();
|
|
1072
1244
|
// `--schema` is the Prisma schema file path in this command.
|
|
@@ -1104,7 +1276,15 @@ async function cmdMigrateFromPrisma(args, config) {
|
|
|
1104
1276
|
(0, ui_js_1.info)('Parse-only mode (--no-db): names will not be resolved.');
|
|
1105
1277
|
}
|
|
1106
1278
|
else {
|
|
1107
|
-
|
|
1279
|
+
// The schema.prisma datasource is the LAST resort for the connection string,
|
|
1280
|
+
// below `--url`, `DATABASE_URL`, and `turbine.config.ts`.
|
|
1281
|
+
const resolvedUrl = resolveMigrateFromPrismaUrl(config.url, ast, process.env);
|
|
1282
|
+
if (resolvedUrl.source === 'datasource' && resolvedUrl.datasource) {
|
|
1283
|
+
const { variable, datasource: dsName, key } = resolvedUrl.datasource;
|
|
1284
|
+
const origin = variable ? `${(0, ui_js_1.cyan)(variable)} via datasource "${dsName}"` : `datasource "${dsName}" ${key}`;
|
|
1285
|
+
(0, ui_js_1.info)(`Using the connection string declared by your Prisma schema (${origin}).`);
|
|
1286
|
+
}
|
|
1287
|
+
url = resolvedUrl.url ?? requireUrl(config, { datasourceVars: resolvedUrl.missingVariables });
|
|
1108
1288
|
(0, ui_js_1.label)('Database', (0, ui_js_1.redactUrl)(url));
|
|
1109
1289
|
const spinner = new ui_js_1.Spinner('Introspecting database schema').start();
|
|
1110
1290
|
schemaMeta = await (0, introspect_js_1.introspect)({
|
|
@@ -2795,6 +2975,8 @@ function showMigrateFromPrismaHelp() {
|
|
|
2795
2975
|
console.log(` ${(0, ui_js_1.dim)('•')} ${(0, ui_js_1.cyan)('prisma-map.ts')} - typed PRISMA_MAP name map`);
|
|
2796
2976
|
(0, ui_js_1.newline)();
|
|
2797
2977
|
console.log(` ${(0, ui_js_1.dim)('Note:')} here ${(0, ui_js_1.cyan)('--schema')} names the Prisma FILE (not the Postgres namespace).`);
|
|
2978
|
+
console.log(` ${(0, ui_js_1.dim)('Note:')} with no ${(0, ui_js_1.cyan)('--url')} / ${(0, ui_js_1.cyan)('DATABASE_URL')} / config ${(0, ui_js_1.cyan)('url')}, the connection string`);
|
|
2979
|
+
console.log(` declared by your ${(0, ui_js_1.cyan)('datasource')} block is used (including its ${(0, ui_js_1.cyan)('env("...")')}).`);
|
|
2798
2980
|
(0, ui_js_1.newline)();
|
|
2799
2981
|
console.log(` ${(0, ui_js_1.bold)('Options:')}`);
|
|
2800
2982
|
console.log(` ${(0, ui_js_1.cyan)('--schema')} ${(0, ui_js_1.dim)('<file>')} Path to schema.prisma ${(0, ui_js_1.dim)('(default: prisma/schema.prisma)')}`);
|
|
@@ -3055,27 +3237,6 @@ async function main() {
|
|
|
3055
3237
|
(0, ui_js_1.warn)(`Found ${(0, ui_js_1.cyan)('.env')} but this Node version cannot auto-load it. ` +
|
|
3056
3238
|
`Upgrade to Node 20.12+ or export ${(0, ui_js_1.cyan)('DATABASE_URL')} yourself.`);
|
|
3057
3239
|
}
|
|
3058
|
-
// If the user has a TypeScript config file, register the tsx ESM loader
|
|
3059
|
-
// before we attempt to import it. Otherwise Node throws
|
|
3060
|
-
// ERR_UNKNOWN_FILE_EXTENSION for `.ts`.
|
|
3061
|
-
const configPath = (0, config_js_1.findConfigFile)();
|
|
3062
|
-
if ((0, loader_js_1.needsTsLoader)(configPath)) {
|
|
3063
|
-
const status = await (0, loader_js_1.registerTsLoader)();
|
|
3064
|
-
if (status === 'missing' || status === 'unsupported' || status === 'failed') {
|
|
3065
|
-
failMissingTsLoader(configPath ?? 'turbine.config.ts', status);
|
|
3066
|
-
}
|
|
3067
|
-
}
|
|
3068
|
-
// Load config file. A config that exists but fails to import is surfaced
|
|
3069
|
-
// loudly (with a name + the underlying error) instead of being swallowed and
|
|
3070
|
-
// later misreported as a missing database URL.
|
|
3071
|
-
const { config: fileConfig, loadError } = await (0, config_js_1.loadConfigResult)();
|
|
3072
|
-
if (loadError && args.command !== 'init') {
|
|
3073
|
-
const underlying = loadError.error instanceof Error ? loadError.error.message : String(loadError.error);
|
|
3074
|
-
(0, ui_js_1.warn)(`Could not load ${(0, ui_js_1.cyan)(loadError.filename)}: ${underlying}`);
|
|
3075
|
-
if (loadError.error instanceof Error)
|
|
3076
|
-
printCjsHintIfApplicable(loadError.error);
|
|
3077
|
-
(0, ui_js_1.newline)();
|
|
3078
|
-
}
|
|
3079
3240
|
const overrides = {
|
|
3080
3241
|
url: args.url,
|
|
3081
3242
|
out: args.out,
|
|
@@ -3086,7 +3247,18 @@ async function main() {
|
|
|
3086
3247
|
keepColumnNames: args.keepColumnNames,
|
|
3087
3248
|
legacyToManyUniques: args.legacyToManyUniques,
|
|
3088
3249
|
};
|
|
3089
|
-
|
|
3250
|
+
// Resolve the config file (skipped entirely for config-free invocations such
|
|
3251
|
+
// as `studio --demo`). A config that exists but fails to import is surfaced
|
|
3252
|
+
// loudly (with a name + the underlying error) instead of being swallowed and
|
|
3253
|
+
// later misreported as a missing database URL.
|
|
3254
|
+
const { config, fileConfig, loadError } = await bootstrapCliConfig(args, overrides);
|
|
3255
|
+
if (loadError && args.command !== 'init') {
|
|
3256
|
+
const underlying = loadError.error instanceof Error ? loadError.error.message : String(loadError.error);
|
|
3257
|
+
(0, ui_js_1.warn)(`Could not load ${(0, ui_js_1.cyan)(loadError.filename)}: ${underlying}`);
|
|
3258
|
+
if (loadError.error instanceof Error)
|
|
3259
|
+
printCjsHintIfApplicable(loadError.error);
|
|
3260
|
+
(0, ui_js_1.newline)();
|
|
3261
|
+
}
|
|
3090
3262
|
// Warn (don't change precedence) when an .env-sourced DATABASE_URL is silently
|
|
3091
3263
|
// overriding a differing, non-empty url in the config file (a wrong-database
|
|
3092
3264
|
// hazard for push/migrate/seed). Shell-exported DATABASE_URL stays silent.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* turbine-orm CLI — TypeScript loader registration
|
|
3
|
+
*
|
|
4
|
+
* The CLI loads user-supplied config and schema files via dynamic `import()`.
|
|
5
|
+
* Plain Node has no built-in `.ts` loader, so importing `turbine.config.ts`
|
|
6
|
+
* blows up with `ERR_UNKNOWN_FILE_EXTENSION` unless we register a TypeScript
|
|
7
|
+
* loader first.
|
|
8
|
+
*
|
|
9
|
+
* Strategy:
|
|
10
|
+
* 1. If the file we're about to import ends in `.ts` / `.mts` / `.cts`,
|
|
11
|
+
* probe whether `tsx` is resolvable from the user's CWD.
|
|
12
|
+
* 2. Prefer tsx's supported programmatic API, `tsx/esm/api`'s `register()`.
|
|
13
|
+
* Calling Node's `module.register('tsx/esm', ...)` directly throws
|
|
14
|
+
* "tsx must be loaded with --import instead of --loader" on every Node
|
|
15
|
+
* version that has `module.register()` (>= 20.6) — tsx's hook file
|
|
16
|
+
* guards against being loaded that way. The `tsx/esm/api` entry point
|
|
17
|
+
* is the documented path and works everywhere `module.register()` does.
|
|
18
|
+
* 3. Fall back to `module.register('tsx/esm', ...)` only for very old tsx
|
|
19
|
+
* versions (< 4.0) that predate `tsx/esm/api`.
|
|
20
|
+
* 4. If tsx isn't installed, or registration genuinely fails, surface an
|
|
21
|
+
* actionable error — including the REAL underlying error message, never
|
|
22
|
+
* a misdiagnosed "tsx is not installed".
|
|
23
|
+
*
|
|
24
|
+
* `tsx` is intentionally NOT a runtime dependency — many projects already
|
|
25
|
+
* have it, and adding a heavy dev tool to a 1-dependency ORM would be silly.
|
|
26
|
+
*/
|
|
27
|
+
/**
|
|
28
|
+
* Detect whether a config / schema file path needs the tsx ESM loader.
|
|
29
|
+
* Returns true for `.ts`, `.mts`, and `.cts` files; false for `.js`, `.mjs`,
|
|
30
|
+
* `.cjs`, `.json`, missing paths, or anything else.
|
|
31
|
+
*/
|
|
32
|
+
export declare function needsTsLoader(filePath: string | null | undefined): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Probe whether `tsx/esm` is resolvable from the user's current working
|
|
35
|
+
* directory. Returns true if `tsx` is installed in the user's project.
|
|
36
|
+
*
|
|
37
|
+
* Accepts an injected `resolver` so unit tests don't need a real filesystem.
|
|
38
|
+
*/
|
|
39
|
+
export declare function canResolveTsx(resolver?: (id: string) => string): boolean;
|
|
40
|
+
export type TsLoaderStatus = 'registered' | 'already' | 'unsupported' | 'missing' | 'failed';
|
|
41
|
+
/**
|
|
42
|
+
* The underlying error message from the last failed registration attempt,
|
|
43
|
+
* or null. Lets the CLI report the REAL cause instead of guessing.
|
|
44
|
+
*/
|
|
45
|
+
export declare function getTsLoaderError(): string | null;
|
|
46
|
+
/**
|
|
47
|
+
* Register the tsx ESM loader so subsequent dynamic imports of `.ts` files
|
|
48
|
+
* work. Safe to call multiple times — internal flag prevents double registration.
|
|
49
|
+
*
|
|
50
|
+
* Returns:
|
|
51
|
+
* - 'registered' loader was successfully registered this call
|
|
52
|
+
* - 'already' a loader was previously registered (idempotent)
|
|
53
|
+
* - 'unsupported' Node lacks `module.register()` (Node < 20.6) and tsx has
|
|
54
|
+
* no programmatic API to fall back to
|
|
55
|
+
* - 'missing' `tsx` is not installed in the user's project
|
|
56
|
+
* - 'failed' tsx IS installed but registration threw — see
|
|
57
|
+
* {@link getTsLoaderError} for the underlying message
|
|
58
|
+
*/
|
|
59
|
+
export declare function registerTsLoader(): Promise<TsLoaderStatus>;
|
|
60
|
+
/** Reset the loader state — used by unit tests only. */
|
|
61
|
+
export declare function _resetTsLoaderStateForTests(): void;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { Readable, Writable } from 'node:stream';
|
|
2
|
+
import { type ColumnMetadata, type RelationDef } from '../schema.js';
|
|
3
|
+
export interface McpServerOptions {
|
|
4
|
+
url: string;
|
|
5
|
+
schema: string;
|
|
6
|
+
migrationsDir: string;
|
|
7
|
+
include?: string[];
|
|
8
|
+
exclude?: string[];
|
|
9
|
+
/**
|
|
10
|
+
* Directory holding generated Turbine metadata (`turbine generate`'s `out`).
|
|
11
|
+
* PII tags are code-first declarations that introspection never sets, so
|
|
12
|
+
* without this the server has nothing to redact against. Read as text;
|
|
13
|
+
* nothing from it is executed. See `pii-tags.ts`.
|
|
14
|
+
*/
|
|
15
|
+
metadataDir?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface McpTransport {
|
|
18
|
+
input?: Readable;
|
|
19
|
+
output?: Writable;
|
|
20
|
+
}
|
|
21
|
+
export interface McpServerHandle {
|
|
22
|
+
dispose(): Promise<void>;
|
|
23
|
+
}
|
|
24
|
+
export declare function startMcpServer(options: McpServerOptions, transport?: McpTransport): McpServerHandle;
|
|
25
|
+
interface ForeignKeyRow {
|
|
26
|
+
source_table: string;
|
|
27
|
+
source_column: string;
|
|
28
|
+
target_table: string;
|
|
29
|
+
target_column: string;
|
|
30
|
+
constraint_name: string;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Group raw FK rows into constraint-level entries and delegate relation
|
|
34
|
+
* naming to the SHARED introspection builder (`buildRelationsFromForeignKeys`
|
|
35
|
+
* + `addAutoManyToManyRelations` in ../introspect.ts). MCP previously carried
|
|
36
|
+
* a stale copy of a retired naming scheme, so `turbine mcp` and `turbine
|
|
37
|
+
* generate` derived DIFFERENT relation names from the same database (N-3).
|
|
38
|
+
* Exported for the parity unit test.
|
|
39
|
+
*/
|
|
40
|
+
export declare function buildRelations(tableNames: string[], columnsByTable: Map<string, ColumnMetadata[]>, pkByTable: Map<string, string[]>, rows: ForeignKeyRow[], enums?: Record<string, string[]>): Map<string, Record<string, RelationDef>>;
|
|
41
|
+
export declare function runMcpServer(options: McpServerOptions): Promise<void>;
|
|
42
|
+
export {};
|