turbine-orm 0.75.0 → 0.76.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 +48 -7
- package/dist/cjs/cli/compile-query.d.ts +22 -2
- package/dist/cjs/cli/compile-query.js +49 -5
- package/dist/cjs/cli/config.d.ts +2 -0
- package/dist/cjs/cli/config.js +1 -1
- package/dist/cjs/cli/destructive.js +78 -43
- package/dist/cjs/cli/index.d.ts +95 -1
- package/dist/cjs/cli/index.js +609 -145
- package/dist/cjs/cli/mcp.js +30 -1
- package/dist/cjs/cli/pii-predicate-guard.d.ts +25 -0
- package/dist/cjs/cli/pii-predicate-guard.js +72 -12
- package/dist/cjs/cli/rate-limit.js +38 -1
- package/dist/cjs/cli/studio.js +26 -5
- package/dist/cjs/cli/ui.d.ts +33 -0
- package/dist/cjs/cli/ui.js +53 -7
- package/dist/cjs/client.d.ts +13 -1
- package/dist/cjs/client.js +1 -1
- package/dist/cjs/errors.d.ts +12 -1
- package/dist/cjs/errors.js +11 -2
- package/dist/cjs/generate.d.ts +26 -0
- package/dist/cjs/generate.js +174 -27
- package/dist/cjs/index.d.ts +1 -1
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/introspect.d.ts +17 -0
- package/dist/cjs/introspect.js +100 -1
- package/dist/cjs/mssql.d.ts +18 -0
- package/dist/cjs/mssql.js +20 -1
- package/dist/cjs/pipeline.js +44 -6
- package/dist/cjs/powql.js +51 -17
- package/dist/cjs/query/batched-loader.js +3 -3
- package/dist/cjs/query/builder.js +1 -1
- package/dist/cjs/query/relations.d.ts +5 -0
- package/dist/cjs/query/relations.js +141 -69
- package/dist/cjs/query/utils.d.ts +13 -0
- package/dist/cjs/query/utils.js +16 -0
- package/dist/cjs/serverless.d.ts +1 -1
- package/dist/cjs/serverless.js +1 -1
- package/dist/cjs/sqlite.d.ts +33 -1
- package/dist/cjs/sqlite.js +84 -3
- package/dist/cli/compile-query.d.ts +22 -2
- package/dist/cli/compile-query.js +50 -6
- package/dist/cli/config.d.ts +2 -0
- package/dist/cli/config.js +1 -1
- package/dist/cli/destructive.js +78 -43
- package/dist/cli/index.d.ts +95 -1
- package/dist/cli/index.js +604 -147
- package/dist/cli/mcp.js +30 -1
- package/dist/cli/pii-predicate-guard.d.ts +25 -0
- package/dist/cli/pii-predicate-guard.js +73 -13
- package/dist/cli/rate-limit.js +38 -1
- package/dist/cli/studio.js +27 -6
- package/dist/cli/ui.d.ts +33 -0
- package/dist/cli/ui.js +51 -7
- package/dist/client.d.ts +13 -1
- package/dist/client.js +1 -1
- package/dist/errors.d.ts +12 -1
- package/dist/errors.js +11 -2
- package/dist/generate.d.ts +26 -0
- package/dist/generate.js +172 -27
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/introspect.d.ts +17 -0
- package/dist/introspect.js +98 -1
- package/dist/mssql.d.ts +18 -0
- package/dist/mssql.js +20 -1
- package/dist/pipeline.js +44 -6
- package/dist/powql.js +53 -19
- package/dist/query/batched-loader.js +4 -4
- package/dist/query/builder.js +2 -2
- package/dist/query/relations.d.ts +5 -0
- package/dist/query/relations.js +141 -70
- package/dist/query/utils.d.ts +13 -0
- package/dist/query/utils.js +15 -0
- package/dist/serverless.d.ts +1 -1
- package/dist/serverless.js +1 -1
- package/dist/sqlite.d.ts +33 -1
- package/dist/sqlite.js +85 -4
- package/package.json +2 -2
package/dist/cjs/cli/index.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* turbine init , Initialize a Turbine project
|
|
8
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
|
-
* turbine push - Apply schema-builder definitions to database (destructive ops gated)
|
|
10
|
+
* turbine push - Apply schema-builder definitions to database (--schema-file, destructive ops gated)
|
|
11
11
|
* turbine migrate create <name> - Create a new SQL migration file (--auto | --from-diff | --recipe <name>)
|
|
12
12
|
* turbine migrate up , Apply pending migrations
|
|
13
13
|
* turbine migrate deploy , Apply pending migrations without prompts
|
|
@@ -60,6 +60,10 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
60
60
|
};
|
|
61
61
|
})();
|
|
62
62
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
63
|
+
exports.canonicalCommand = canonicalCommand;
|
|
64
|
+
exports.knownCommands = knownCommands;
|
|
65
|
+
exports.flagsForCommand = flagsForCommand;
|
|
66
|
+
exports.allFlagTokens = allFlagTokens;
|
|
63
67
|
exports.parseArgs = parseArgs;
|
|
64
68
|
exports.usesProjectConfig = usesProjectConfig;
|
|
65
69
|
exports.bootstrapCliConfig = bootstrapCliConfig;
|
|
@@ -70,10 +74,13 @@ exports.tsxInstallCommand = tsxInstallCommand;
|
|
|
70
74
|
exports.tsxRequiredNotice = tsxRequiredNotice;
|
|
71
75
|
exports.detectConsumerModuleType = detectConsumerModuleType;
|
|
72
76
|
exports.planInitSteps = planInitSteps;
|
|
77
|
+
exports.initSchemaTemplate = initSchemaTemplate;
|
|
73
78
|
exports.gitignoreIgnoresEnv = gitignoreIgnoresEnv;
|
|
74
79
|
exports.planEnvScaffold = planEnvScaffold;
|
|
75
80
|
exports.scaffoldEnvForUrl = scaffoldEnvForUrl;
|
|
81
|
+
exports.generatedClientImport = generatedClientImport;
|
|
76
82
|
exports.initEnvNotice = initEnvNotice;
|
|
83
|
+
exports.refuseSchemaFilePath = refuseSchemaFilePath;
|
|
77
84
|
exports.resolveMigrateFromPrismaUrl = resolveMigrateFromPrismaUrl;
|
|
78
85
|
exports.readPrismaSchemaSource = readPrismaSchemaSource;
|
|
79
86
|
exports.buildMigrateDeployOptions = buildMigrateDeployOptions;
|
|
@@ -93,6 +100,7 @@ const introspect_js_1 = require("../introspect.js");
|
|
|
93
100
|
const plan_divergence_js_1 = require("../plan-divergence.js");
|
|
94
101
|
const plan_flip_probe_js_1 = require("../plan-flip-probe.js");
|
|
95
102
|
const prisma_schema_fingerprint_js_1 = require("../prisma-schema-fingerprint.js");
|
|
103
|
+
const utils_js_1 = require("../query/utils.js");
|
|
96
104
|
const schema_js_1 = require("../schema.js");
|
|
97
105
|
const schema_sql_js_1 = require("../schema-sql.js");
|
|
98
106
|
const config_js_1 = require("./config.js");
|
|
@@ -114,11 +122,227 @@ const ui_js_1 = require("./ui.js");
|
|
|
114
122
|
* rather than "you forgot the recipe name".
|
|
115
123
|
*/
|
|
116
124
|
function failArg(message, ...hints) {
|
|
117
|
-
(0, ui_js_1.
|
|
125
|
+
(0, ui_js_1.errorBanner)();
|
|
118
126
|
(0, ui_js_1.error)(message);
|
|
119
127
|
for (const hint of hints)
|
|
120
|
-
|
|
121
|
-
(0, ui_js_1.
|
|
128
|
+
(0, ui_js_1.errorLine)((0, ui_js_1.dim)(hint));
|
|
129
|
+
(0, ui_js_1.errorLine)();
|
|
130
|
+
process.exit(1);
|
|
131
|
+
}
|
|
132
|
+
// ---------------------------------------------------------------------------
|
|
133
|
+
// The flag surface, as data
|
|
134
|
+
// ---------------------------------------------------------------------------
|
|
135
|
+
/**
|
|
136
|
+
* Long flags accepted by EVERY command.
|
|
137
|
+
*
|
|
138
|
+
* Deliberately generous: these are connection/output overrides and diagnostics,
|
|
139
|
+
* so accepting one where it happens to be inert costs nothing. Nothing that
|
|
140
|
+
* changes what a command DOES to your data belongs here, which is why
|
|
141
|
+
* `--dry-run`, `--allow-destructive`, `--fix` and `--yes` are all per-command
|
|
142
|
+
* below.
|
|
143
|
+
*/
|
|
144
|
+
const GLOBAL_FLAGS = [
|
|
145
|
+
'--url',
|
|
146
|
+
'--out',
|
|
147
|
+
'--schema',
|
|
148
|
+
'--include',
|
|
149
|
+
'--exclude',
|
|
150
|
+
'--verbose',
|
|
151
|
+
'--force',
|
|
152
|
+
'--help',
|
|
153
|
+
];
|
|
154
|
+
/**
|
|
155
|
+
* The flags each command accepts, BEYOND {@link GLOBAL_FLAGS}. Long spellings
|
|
156
|
+
* only; short forms come from {@link FLAG_ALIAS}.
|
|
157
|
+
*
|
|
158
|
+
* This table is the CLI's flag surface as data, for the same reason
|
|
159
|
+
* `query/option-surface.ts` is the query surface as data: an unknown flag used
|
|
160
|
+
* to be DISCARDED by the parser's `default:` branch, so `push --dry-runn`
|
|
161
|
+
* executed for real, `migrate create x --autoo` wrote an empty template and
|
|
162
|
+
* reported success, and `doctor --fixx` wrote nothing and exited 0. A typo in a
|
|
163
|
+
* safety flag silently disarmed the safety. Every entry here is cross-checked
|
|
164
|
+
* against the parser's own `case` labels by
|
|
165
|
+
* `src/test/cli-unknown-flags.test.ts`, so adding a flag to one and not the
|
|
166
|
+
* other fails the build rather than stranding it.
|
|
167
|
+
*/
|
|
168
|
+
const COMMAND_FLAGS = {
|
|
169
|
+
init: ['--yes', '--schema-file', '--skip-schema', '--with-schema', '--skip-seed', '--skip-push', '--skip-generate'],
|
|
170
|
+
generate: [
|
|
171
|
+
'--zod',
|
|
172
|
+
'--include-views',
|
|
173
|
+
'--no-timestamp',
|
|
174
|
+
'--import-ext',
|
|
175
|
+
'--keep-column-names',
|
|
176
|
+
'--legacy-to-many-uniques',
|
|
177
|
+
'--allow-empty',
|
|
178
|
+
],
|
|
179
|
+
'migrate-from-prisma': ['--no-db', '--if-db', '--allow-partial', '--no-timestamp'],
|
|
180
|
+
push: ['--schema-file', '--dry-run', '--allow-destructive'],
|
|
181
|
+
migrate: [
|
|
182
|
+
'--schema-file',
|
|
183
|
+
'--auto',
|
|
184
|
+
'--from-diff',
|
|
185
|
+
'--recipe',
|
|
186
|
+
'--step',
|
|
187
|
+
'--dry-run',
|
|
188
|
+
'--allow-drift',
|
|
189
|
+
'--allow-destructive',
|
|
190
|
+
],
|
|
191
|
+
seed: [],
|
|
192
|
+
status: [],
|
|
193
|
+
doctor: [
|
|
194
|
+
'--fix',
|
|
195
|
+
'--json',
|
|
196
|
+
'--no-concurrently',
|
|
197
|
+
'--unused',
|
|
198
|
+
'--audit',
|
|
199
|
+
'--min-scans',
|
|
200
|
+
'--metrics-url',
|
|
201
|
+
'--no-plan-divergence',
|
|
202
|
+
'--allow-pooler',
|
|
203
|
+
],
|
|
204
|
+
studio: ['--port', '--host', '--no-open', '--allow-remote', '--write', '--show-pii', '--demo'],
|
|
205
|
+
mcp: [],
|
|
206
|
+
observe: ['--port', '--host', '--no-open', '--allow-remote', '--metrics-url'],
|
|
207
|
+
skill: ['--print', '--agents', '--dir'],
|
|
208
|
+
help: [],
|
|
209
|
+
version: [],
|
|
210
|
+
};
|
|
211
|
+
/** Command spellings that dispatch to another command's handler. */
|
|
212
|
+
const COMMAND_ALIASES = {
|
|
213
|
+
gen: 'generate',
|
|
214
|
+
g: 'generate',
|
|
215
|
+
pull: 'generate',
|
|
216
|
+
migration: 'migrate',
|
|
217
|
+
m: 'migrate',
|
|
218
|
+
s: 'seed',
|
|
219
|
+
info: 'status',
|
|
220
|
+
};
|
|
221
|
+
/**
|
|
222
|
+
* Long flag to its alternative spelling, for MATCHING and for display.
|
|
223
|
+
*
|
|
224
|
+
* Kept out of {@link COMMAND_FLAGS} so a command lists each flag once: the
|
|
225
|
+
* "valid flags" block a rejection prints reads as one flag per idea, with its
|
|
226
|
+
* short form attached, rather than as a list twice as long.
|
|
227
|
+
*/
|
|
228
|
+
const FLAG_ALIAS = {
|
|
229
|
+
'--url': '-u',
|
|
230
|
+
'--out': '-o',
|
|
231
|
+
'--schema': '-s',
|
|
232
|
+
'--step': '-n',
|
|
233
|
+
'--yes': '-y',
|
|
234
|
+
'--force': '-f',
|
|
235
|
+
'--verbose': '-v',
|
|
236
|
+
'--help': '-h',
|
|
237
|
+
'--import-ext': '--import-extension',
|
|
238
|
+
};
|
|
239
|
+
/**
|
|
240
|
+
* The canonical name of `command`, or undefined when nothing dispatches it.
|
|
241
|
+
*
|
|
242
|
+
* @internal exported for tests.
|
|
243
|
+
*/
|
|
244
|
+
function canonicalCommand(command) {
|
|
245
|
+
const resolved = COMMAND_ALIASES[command] ?? command;
|
|
246
|
+
return resolved in COMMAND_FLAGS ? resolved : undefined;
|
|
247
|
+
}
|
|
248
|
+
/** Every command name a user could reasonably have meant, canonical spellings only. */
|
|
249
|
+
function knownCommands() {
|
|
250
|
+
return Object.keys(COMMAND_FLAGS);
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* The long flags `command` accepts (its own, then the global ones), or
|
|
254
|
+
* undefined when the command itself is unrecognized.
|
|
255
|
+
*
|
|
256
|
+
* An unknown command returns undefined rather than an empty list on purpose:
|
|
257
|
+
* `turbine genrate --url ...` should be told the COMMAND is misspelled, not
|
|
258
|
+
* handed a flag error for a flag that is perfectly valid on the command it
|
|
259
|
+
* meant.
|
|
260
|
+
*
|
|
261
|
+
* @internal exported for tests.
|
|
262
|
+
*/
|
|
263
|
+
function flagsForCommand(command) {
|
|
264
|
+
const canonical = canonicalCommand(command);
|
|
265
|
+
if (canonical === undefined)
|
|
266
|
+
return undefined;
|
|
267
|
+
return { own: COMMAND_FLAGS[canonical] ?? [], global: GLOBAL_FLAGS };
|
|
268
|
+
}
|
|
269
|
+
/** Every token (long + alias) `command` accepts, or undefined for an unknown command. */
|
|
270
|
+
function acceptedFlagTokens(command) {
|
|
271
|
+
const flags = flagsForCommand(command);
|
|
272
|
+
if (!flags)
|
|
273
|
+
return undefined;
|
|
274
|
+
const tokens = new Set();
|
|
275
|
+
for (const flag of [...flags.own, ...flags.global]) {
|
|
276
|
+
tokens.add(flag);
|
|
277
|
+
const alias = FLAG_ALIAS[flag];
|
|
278
|
+
if (alias)
|
|
279
|
+
tokens.add(alias);
|
|
280
|
+
}
|
|
281
|
+
return tokens;
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Every flag token any command accepts, long spellings and aliases alike.
|
|
285
|
+
*
|
|
286
|
+
* Exists for `src/test/cli-arg-safety.test.ts`, which cross-checks it against
|
|
287
|
+
* the `case` labels in {@link parseArgs} in both directions. A flag in the
|
|
288
|
+
* parser but in no command's list is unreachable (the validator rejects it
|
|
289
|
+
* before the case runs); a flag in a list with no case is accepted and then
|
|
290
|
+
* silently ignored, which is the bug this whole surface exists to end.
|
|
291
|
+
*
|
|
292
|
+
* @internal exported for tests.
|
|
293
|
+
*/
|
|
294
|
+
function allFlagTokens() {
|
|
295
|
+
const tokens = new Set();
|
|
296
|
+
for (const command of knownCommands()) {
|
|
297
|
+
for (const token of acceptedFlagTokens(command) ?? [])
|
|
298
|
+
tokens.add(token);
|
|
299
|
+
}
|
|
300
|
+
return tokens;
|
|
301
|
+
}
|
|
302
|
+
/** `--url` rendered for the help block: `--url, -u`. */
|
|
303
|
+
function displayFlag(flag) {
|
|
304
|
+
const alias = FLAG_ALIAS[flag];
|
|
305
|
+
return alias ? `${flag}, ${alias}` : flag;
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Reject the unknown flags on a recognized command, naming the closest real one.
|
|
309
|
+
*
|
|
310
|
+
* `closestName` rather than `suggestKey`: the second pass `suggestKey` adds is a
|
|
311
|
+
* camelCase-word-subsequence match, which does nothing for kebab-case flags.
|
|
312
|
+
* Long spellings ONLY are offered as candidates, because `nameCloseness` scores
|
|
313
|
+
* substring containment above edit distance and every short flag is a substring
|
|
314
|
+
* of something: `--no-opn` contains `-o`, which would otherwise outrank the
|
|
315
|
+
* `--no-open` the user obviously meant.
|
|
316
|
+
*/
|
|
317
|
+
function failUnknownFlags(command, unknown) {
|
|
318
|
+
const flags = flagsForCommand(command);
|
|
319
|
+
const canonical = canonicalCommand(command) ?? command;
|
|
320
|
+
(0, ui_js_1.errorBanner)();
|
|
321
|
+
const plural = unknown.length > 1 ? 's' : '';
|
|
322
|
+
(0, ui_js_1.error)(`Unknown flag${plural} for ${(0, ui_js_1.cyan)(`turbine ${canonical}`)}: ${unknown.map((f) => (0, ui_js_1.bold)(f)).join(' ')}`);
|
|
323
|
+
(0, ui_js_1.errorLine)();
|
|
324
|
+
if (flags) {
|
|
325
|
+
for (const flag of unknown) {
|
|
326
|
+
const suggestion = (0, utils_js_1.closestName)(flag, [...flags.own, ...flags.global]);
|
|
327
|
+
if (!suggestion)
|
|
328
|
+
continue;
|
|
329
|
+
// With one bad flag the headline already names it; with several, each
|
|
330
|
+
// suggestion has to say which flag it is about.
|
|
331
|
+
const subject = unknown.length > 1 ? `${(0, ui_js_1.bold)(flag)} ${ui_js_1.symbols.arrow} ` : '';
|
|
332
|
+
(0, ui_js_1.errorLine)(`${subject}${(0, ui_js_1.dim)('Did you mean')} ${(0, ui_js_1.cyan)(displayFlag(suggestion))}${(0, ui_js_1.dim)('?')}`);
|
|
333
|
+
}
|
|
334
|
+
if (flags.own.length > 0) {
|
|
335
|
+
(0, ui_js_1.errorLine)();
|
|
336
|
+
(0, ui_js_1.errorLine)((0, ui_js_1.dim)(`Flags for ${canonical}:`));
|
|
337
|
+
(0, ui_js_1.errorLine)(` ${flags.own.map(displayFlag).join(' ')}`);
|
|
338
|
+
}
|
|
339
|
+
(0, ui_js_1.errorLine)();
|
|
340
|
+
(0, ui_js_1.errorLine)((0, ui_js_1.dim)('Flags for every command:'));
|
|
341
|
+
(0, ui_js_1.errorLine)(` ${flags.global.map(displayFlag).join(' ')}`);
|
|
342
|
+
}
|
|
343
|
+
(0, ui_js_1.errorLine)();
|
|
344
|
+
(0, ui_js_1.errorLine)(`${(0, ui_js_1.dim)('Run')} ${(0, ui_js_1.cyan)(`npx turbine ${canonical} --help`)} ${(0, ui_js_1.dim)('for the full list.')}`);
|
|
345
|
+
(0, ui_js_1.errorLine)();
|
|
122
346
|
process.exit(1);
|
|
123
347
|
}
|
|
124
348
|
function parseArgs(argv = process.argv.slice(2)) {
|
|
@@ -133,9 +357,20 @@ function parseArgs(argv = process.argv.slice(2)) {
|
|
|
133
357
|
result.subcommand = args[i];
|
|
134
358
|
i++;
|
|
135
359
|
}
|
|
360
|
+
// The flags this command accepts. `undefined` means the COMMAND is unknown,
|
|
361
|
+
// in which case nothing here validates flags: main() reports the misspelled
|
|
362
|
+
// command instead, which is the actual problem.
|
|
363
|
+
const accepted = acceptedFlagTokens(result.command);
|
|
364
|
+
const unknownFlags = [];
|
|
136
365
|
for (; i < args.length; i++) {
|
|
137
366
|
const arg = args[i];
|
|
138
367
|
const next = args[i + 1];
|
|
368
|
+
// Checked BEFORE the switch, and on the same cursor, so a flag's VALUE
|
|
369
|
+
// (`--step -1`, `--host -x`) is consumed by its own case and never reaches
|
|
370
|
+
// this test. A bare `-`-prefixed token that is not a flag reaches it and is
|
|
371
|
+
// rejected: no command takes a negative-number positional.
|
|
372
|
+
if (accepted && arg.startsWith('-') && !accepted.has(arg))
|
|
373
|
+
unknownFlags.push(arg);
|
|
139
374
|
switch (arg) {
|
|
140
375
|
case '--url':
|
|
141
376
|
case '-u':
|
|
@@ -152,6 +387,10 @@ function parseArgs(argv = process.argv.slice(2)) {
|
|
|
152
387
|
result.schema = next;
|
|
153
388
|
i++;
|
|
154
389
|
break;
|
|
390
|
+
case '--schema-file':
|
|
391
|
+
result.schemaFile = next;
|
|
392
|
+
i++;
|
|
393
|
+
break;
|
|
155
394
|
case '--include':
|
|
156
395
|
result.include = next?.split(',');
|
|
157
396
|
i++;
|
|
@@ -323,6 +562,10 @@ function parseArgs(argv = process.argv.slice(2)) {
|
|
|
323
562
|
break;
|
|
324
563
|
}
|
|
325
564
|
}
|
|
565
|
+
// Reported once, after the whole line is parsed, so two typos on one command
|
|
566
|
+
// are two lines of one error rather than two runs of the CLI.
|
|
567
|
+
if (unknownFlags.length > 0)
|
|
568
|
+
failUnknownFlags(result.command, unknownFlags);
|
|
326
569
|
return result;
|
|
327
570
|
}
|
|
328
571
|
// ---------------------------------------------------------------------------
|
|
@@ -333,35 +576,35 @@ function parseArgs(argv = process.argv.slice(2)) {
|
|
|
333
576
|
* Called when we know we need to load a `.ts` file but the loader isn't available.
|
|
334
577
|
*/
|
|
335
578
|
function failMissingTsLoader(filePath, reason) {
|
|
336
|
-
(0, ui_js_1.
|
|
579
|
+
(0, ui_js_1.errorLine)();
|
|
337
580
|
(0, ui_js_1.error)(`Cannot load TypeScript file: ${filePath}`);
|
|
338
|
-
(0, ui_js_1.
|
|
581
|
+
(0, ui_js_1.errorLine)();
|
|
339
582
|
if (reason === 'unsupported') {
|
|
340
|
-
|
|
341
|
-
|
|
583
|
+
(0, ui_js_1.errorLine)(`${(0, ui_js_1.dim)('Your Node.js version does not support')} ${(0, ui_js_1.cyan)('module.register()')}.`);
|
|
584
|
+
(0, ui_js_1.errorLine)(`${(0, ui_js_1.dim)('Upgrade to Node.js')} ${(0, ui_js_1.cyan)('20.6+')} ${(0, ui_js_1.dim)('or use a')} ${(0, ui_js_1.cyan)('.js')} ${(0, ui_js_1.dim)('/')} ${(0, ui_js_1.cyan)('.mjs')} ${(0, ui_js_1.dim)('config file.')}`);
|
|
342
585
|
}
|
|
343
586
|
else if (reason === 'failed') {
|
|
344
587
|
// tsx IS installed but registering its loader threw. Report the real
|
|
345
588
|
// cause, telling the user to install tsx here would be a misdiagnosis.
|
|
346
|
-
|
|
347
|
-
(0, ui_js_1.
|
|
348
|
-
|
|
349
|
-
(0, ui_js_1.
|
|
350
|
-
|
|
589
|
+
(0, ui_js_1.errorLine)(`${(0, ui_js_1.dim)('tsx is installed, but registering its TypeScript loader failed:')}`);
|
|
590
|
+
(0, ui_js_1.errorLine)();
|
|
591
|
+
(0, ui_js_1.errorLine)(` ${(0, loader_js_1.getTsLoaderError)() ?? '(unknown error)'}`);
|
|
592
|
+
(0, ui_js_1.errorLine)();
|
|
593
|
+
(0, ui_js_1.errorLine)(`${(0, ui_js_1.dim)('Try upgrading tsx:')} ${(0, ui_js_1.cyan)('npm install --save-dev tsx@latest')}${(0, ui_js_1.dim)(', or rename your file to')} ${(0, ui_js_1.cyan)('.mjs')}.`);
|
|
351
594
|
}
|
|
352
595
|
else {
|
|
353
|
-
|
|
354
|
-
(0, ui_js_1.
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
(0, ui_js_1.
|
|
362
|
-
|
|
363
|
-
}
|
|
364
|
-
(0, ui_js_1.
|
|
596
|
+
(0, ui_js_1.errorLine)(`${(0, ui_js_1.dim)('Loading .ts config / schema files requires')} ${(0, ui_js_1.cyan)('tsx')} ${(0, ui_js_1.dim)('to be installed.')}`);
|
|
597
|
+
(0, ui_js_1.errorLine)();
|
|
598
|
+
(0, ui_js_1.errorLine)(`${(0, ui_js_1.dim)('Install it as a dev dependency:')}`);
|
|
599
|
+
(0, ui_js_1.errorLine)(` ${(0, ui_js_1.cyan)('npm install --save-dev tsx')}`);
|
|
600
|
+
(0, ui_js_1.errorLine)(` ${(0, ui_js_1.dim)('or')}`);
|
|
601
|
+
(0, ui_js_1.errorLine)(` ${(0, ui_js_1.cyan)('pnpm add -D tsx')}`);
|
|
602
|
+
(0, ui_js_1.errorLine)(` ${(0, ui_js_1.dim)('or')}`);
|
|
603
|
+
(0, ui_js_1.errorLine)(` ${(0, ui_js_1.cyan)('yarn add -D tsx')}`);
|
|
604
|
+
(0, ui_js_1.errorLine)();
|
|
605
|
+
(0, ui_js_1.errorLine)(`${(0, ui_js_1.dim)('Alternatively, rename your file to')} ${(0, ui_js_1.cyan)('.js')} ${(0, ui_js_1.dim)('or')} ${(0, ui_js_1.cyan)('.mjs')}.`);
|
|
606
|
+
}
|
|
607
|
+
(0, ui_js_1.errorLine)();
|
|
365
608
|
process.exit(1);
|
|
366
609
|
}
|
|
367
610
|
// ---------------------------------------------------------------------------
|
|
@@ -410,20 +653,20 @@ async function bootstrapCliConfig(args, overrides) {
|
|
|
410
653
|
function requireUrl(config, options = {}) {
|
|
411
654
|
if (!config.url) {
|
|
412
655
|
(0, ui_js_1.error)('No database URL provided.');
|
|
413
|
-
(0, ui_js_1.
|
|
414
|
-
|
|
415
|
-
|
|
656
|
+
(0, ui_js_1.errorLine)();
|
|
657
|
+
(0, ui_js_1.errorLine)(`${(0, ui_js_1.dim)('Set it in one of these ways:')}`);
|
|
658
|
+
(0, ui_js_1.errorLine)(` ${(0, ui_js_1.dim)('1.')} Add ${(0, ui_js_1.cyan)('url')} to ${(0, ui_js_1.cyan)('turbine.config.ts')}`);
|
|
416
659
|
// .env auto-load needs Node 20.12+ (process.loadEnvFile); be honest below it.
|
|
417
660
|
const envFileNote = typeof process.loadEnvFile === 'function' ? '(auto-loaded)' : '(needs Node 20.12+ to auto-load)';
|
|
418
|
-
|
|
419
|
-
|
|
661
|
+
(0, ui_js_1.errorLine)(` ${(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)}`);
|
|
662
|
+
(0, ui_js_1.errorLine)(` ${(0, ui_js_1.dim)('3.')} Pass ${(0, ui_js_1.cyan)('--url')} flag`);
|
|
420
663
|
const vars = options.datasourceVars ?? [];
|
|
421
664
|
if (vars.length > 0) {
|
|
422
665
|
const list = vars.map((v) => (0, ui_js_1.cyan)(v)).join(', ');
|
|
423
666
|
const plural = vars.length > 1 ? 'these variables' : 'this variable';
|
|
424
|
-
|
|
667
|
+
(0, ui_js_1.errorLine)(` ${(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)`)}`);
|
|
425
668
|
}
|
|
426
|
-
(0, ui_js_1.
|
|
669
|
+
(0, ui_js_1.errorLine)();
|
|
427
670
|
process.exit(1);
|
|
428
671
|
}
|
|
429
672
|
return config.url;
|
|
@@ -432,7 +675,7 @@ async function loadSchemaFile(schemaFile) {
|
|
|
432
675
|
const absPath = (0, node_path_1.resolve)(schemaFile);
|
|
433
676
|
if (!(0, node_fs_1.existsSync)(absPath)) {
|
|
434
677
|
(0, ui_js_1.error)(`Schema file not found: ${schemaFile}`);
|
|
435
|
-
|
|
678
|
+
(0, ui_js_1.errorLine)(`${(0, ui_js_1.dim)('Create one with:')} ${(0, ui_js_1.cyan)('turbine init')}`);
|
|
436
679
|
process.exit(1);
|
|
437
680
|
}
|
|
438
681
|
// If this is a TypeScript file, ensure the tsx ESM loader is registered
|
|
@@ -461,11 +704,11 @@ async function loadSchemaFile(schemaFile) {
|
|
|
461
704
|
catch (err) {
|
|
462
705
|
(0, ui_js_1.error)(`Failed to load schema file: ${schemaFile}`);
|
|
463
706
|
if (err instanceof Error) {
|
|
464
|
-
|
|
707
|
+
(0, ui_js_1.errorLine)(`${(0, ui_js_1.dim)(err.message)}`);
|
|
465
708
|
// If the error is the classic ERR_UNKNOWN_FILE_EXTENSION, give a hint.
|
|
466
709
|
if (err.message.includes('ERR_UNKNOWN_FILE_EXTENSION') || err.message.includes('Unknown file extension')) {
|
|
467
|
-
(0, ui_js_1.
|
|
468
|
-
|
|
710
|
+
(0, ui_js_1.errorLine)();
|
|
711
|
+
(0, ui_js_1.errorLine)(`${(0, ui_js_1.dim)('Hint: install')} ${(0, ui_js_1.cyan)('tsx')} ${(0, ui_js_1.dim)('to load .ts files:')} ${(0, ui_js_1.cyan)('npm install --save-dev tsx')}`);
|
|
469
712
|
}
|
|
470
713
|
printCjsHintIfApplicable(err);
|
|
471
714
|
}
|
|
@@ -484,9 +727,9 @@ function printCjsHintIfApplicable(err) {
|
|
|
484
727
|
if (msg.includes('ERR_REQUIRE_ESM') ||
|
|
485
728
|
msg.includes('require() of ES Module') ||
|
|
486
729
|
msg.includes('Cannot require() ES Module')) {
|
|
487
|
-
(0, ui_js_1.
|
|
488
|
-
|
|
489
|
-
|
|
730
|
+
(0, ui_js_1.errorLine)();
|
|
731
|
+
(0, ui_js_1.errorLine)(`${(0, ui_js_1.dim)('Hint: add')} ${(0, ui_js_1.cyan)('"type": "module"')} ${(0, ui_js_1.dim)('to your')} ${(0, ui_js_1.cyan)('package.json')}${(0, ui_js_1.dim)('.')}`);
|
|
732
|
+
(0, ui_js_1.errorLine)(`${(0, ui_js_1.dim)('Turbine is an ESM package; without it, Node/tsx tries to')} ${(0, ui_js_1.cyan)('require()')} ${(0, ui_js_1.dim)('it and fails.')}`);
|
|
490
733
|
}
|
|
491
734
|
}
|
|
492
735
|
/**
|
|
@@ -788,7 +1031,54 @@ export default defineSeed(async (db) => {
|
|
|
788
1031
|
console.log('Done!');
|
|
789
1032
|
});
|
|
790
1033
|
`;
|
|
791
|
-
|
|
1034
|
+
/**
|
|
1035
|
+
* The starter schema, with two real tables and a foreign key between them.
|
|
1036
|
+
*
|
|
1037
|
+
* Scaffolded ONLY when the target database has no tables of its own (see
|
|
1038
|
+
* {@link initSchemaTemplate}). An all-commented-out schema next to an empty
|
|
1039
|
+
* database is a dead end: `push` reports "Database already in sync",
|
|
1040
|
+
* `generate` refuses to emit an empty client, and the next-steps text ends up
|
|
1041
|
+
* describing a `db.users` that does not exist. Two tables rather than one
|
|
1042
|
+
* because the relation is what the very next query in the quickstart reads.
|
|
1043
|
+
*/
|
|
1044
|
+
const INIT_SCHEMA_EXAMPLE = `/**
|
|
1045
|
+
* Turbine schema definition
|
|
1046
|
+
*
|
|
1047
|
+
* Define your database schema in TypeScript.
|
|
1048
|
+
* Use \`npx turbine push\` to sync it to your database, then
|
|
1049
|
+
* \`npx turbine generate\` to emit the typed client.
|
|
1050
|
+
*
|
|
1051
|
+
* This starter is a working example. Rename it, edit it, or replace it
|
|
1052
|
+
* entirely: nothing here is special to Turbine.
|
|
1053
|
+
*
|
|
1054
|
+
* @see https://turbineorm.dev
|
|
1055
|
+
*/
|
|
1056
|
+
|
|
1057
|
+
import { defineSchema } from 'turbine-orm';
|
|
1058
|
+
|
|
1059
|
+
export default defineSchema({
|
|
1060
|
+
users: {
|
|
1061
|
+
id: { type: 'serial', primaryKey: true },
|
|
1062
|
+
email: { type: 'text', notNull: true, unique: true },
|
|
1063
|
+
name: { type: 'text', notNull: true },
|
|
1064
|
+
created_at: { type: 'timestamptz', notNull: true, default: 'NOW()' },
|
|
1065
|
+
},
|
|
1066
|
+
posts: {
|
|
1067
|
+
id: { type: 'serial', primaryKey: true },
|
|
1068
|
+
user_id: { type: 'integer', notNull: true, references: 'users.id' },
|
|
1069
|
+
title: { type: 'text', notNull: true },
|
|
1070
|
+
published: { type: 'boolean', notNull: true, default: 'false' },
|
|
1071
|
+
created_at: { type: 'timestamptz', notNull: true, default: 'NOW()' },
|
|
1072
|
+
},
|
|
1073
|
+
});
|
|
1074
|
+
`;
|
|
1075
|
+
/**
|
|
1076
|
+
* The starter schema with nothing defined, for the one case where a real
|
|
1077
|
+
* example could collide: `init --with-schema` beside a database that already
|
|
1078
|
+
* has tables. There, an example `users` table is a schema diff against
|
|
1079
|
+
* somebody's real data, so the file stays a comment.
|
|
1080
|
+
*/
|
|
1081
|
+
const INIT_SCHEMA_PLACEHOLDER = `/**
|
|
792
1082
|
* Turbine schema definition
|
|
793
1083
|
*
|
|
794
1084
|
* Define your database schema in TypeScript.
|
|
@@ -800,15 +1090,27 @@ const INIT_SCHEMA_TEMPLATE = `/**
|
|
|
800
1090
|
import { defineSchema } from 'turbine-orm';
|
|
801
1091
|
|
|
802
1092
|
export default defineSchema({
|
|
803
|
-
//
|
|
1093
|
+
// Your database already has tables, so this file starts empty on purpose:
|
|
1094
|
+
// \`turbine push\` applies exactly what is declared here. Describe the tables
|
|
1095
|
+
// you want Turbine to own, or run \`npx turbine generate\` to work from the
|
|
1096
|
+
// database as it already is.
|
|
1097
|
+
//
|
|
804
1098
|
// users: {
|
|
805
1099
|
// id: { type: 'serial', primaryKey: true },
|
|
806
1100
|
// email: { type: 'text', notNull: true, unique: true },
|
|
807
1101
|
// name: { type: 'text', notNull: true },
|
|
808
|
-
// created_at: { type: '
|
|
1102
|
+
// created_at: { type: 'timestamptz', notNull: true, default: 'NOW()' },
|
|
809
1103
|
// },
|
|
810
1104
|
});
|
|
811
1105
|
`;
|
|
1106
|
+
/**
|
|
1107
|
+
* Which starter schema to scaffold.
|
|
1108
|
+
*
|
|
1109
|
+
* @internal exported for tests.
|
|
1110
|
+
*/
|
|
1111
|
+
function initSchemaTemplate(dbHasTables) {
|
|
1112
|
+
return dbHasTables ? INIT_SCHEMA_PLACEHOLDER : INIT_SCHEMA_EXAMPLE;
|
|
1113
|
+
}
|
|
812
1114
|
/**
|
|
813
1115
|
* Does this `.gitignore` text already ignore `.env`?
|
|
814
1116
|
*
|
|
@@ -961,12 +1263,15 @@ function ensureInitScaffoldDirs(config) {
|
|
|
961
1263
|
}
|
|
962
1264
|
}
|
|
963
1265
|
}
|
|
964
|
-
function writeInitSchemaTemplate(config) {
|
|
1266
|
+
function writeInitSchemaTemplate(config, dbHasTables) {
|
|
965
1267
|
const schemaDir = (0, node_path_1.dirname)(config.schemaFile);
|
|
966
1268
|
if (schemaDir && !(0, node_fs_1.existsSync)(schemaDir))
|
|
967
1269
|
(0, node_fs_1.mkdirSync)(schemaDir, { recursive: true });
|
|
968
|
-
(0, node_fs_1.writeFileSync)(config.schemaFile,
|
|
1270
|
+
(0, node_fs_1.writeFileSync)(config.schemaFile, initSchemaTemplate(dbHasTables), 'utf-8');
|
|
969
1271
|
(0, ui_js_1.success)(`Created ${(0, ui_js_1.cyan)(config.schemaFile)}`);
|
|
1272
|
+
if (!dbHasTables) {
|
|
1273
|
+
console.log(` ${(0, ui_js_1.dim)('It defines a')} ${(0, ui_js_1.cyan)('users')} ${(0, ui_js_1.dim)('and a')} ${(0, ui_js_1.cyan)('posts')} ${(0, ui_js_1.dim)('table to start from.')}`);
|
|
1274
|
+
}
|
|
970
1275
|
}
|
|
971
1276
|
function writeInitSeedTemplate(seedFilePath) {
|
|
972
1277
|
const seedDir = (0, node_path_1.dirname)(seedFilePath);
|
|
@@ -1016,6 +1321,28 @@ async function runInitPush(config, url) {
|
|
|
1016
1321
|
(0, ui_js_1.success)(`Applied ${(0, ui_js_1.bold)(String(result.statementsExecuted))} statement(s)`);
|
|
1017
1322
|
}
|
|
1018
1323
|
}
|
|
1324
|
+
/**
|
|
1325
|
+
* The exact import line for the generated client, extension included.
|
|
1326
|
+
*
|
|
1327
|
+
* `import { turbine } from './generated/turbine'` is a hard TypeScript error
|
|
1328
|
+
* (TS2834) under `moduleResolution: NodeNext`, which is what this package ships
|
|
1329
|
+
* and what its own tsconfig uses: a relative import needs an explicit file
|
|
1330
|
+
* extension, and NodeNext does no directory-index resolution either. The
|
|
1331
|
+
* generator has always appended the extension to its OWN sibling imports; the
|
|
1332
|
+
* line printed at the reader was the one place it never reached.
|
|
1333
|
+
*
|
|
1334
|
+
* The extension comes from {@link resolveImportExtension}, the same resolver the
|
|
1335
|
+
* generator runs, so the printed line matches the files just written rather
|
|
1336
|
+
* than a second guess about the consumer's tsconfig. Under bundler resolution
|
|
1337
|
+
* it resolves to `''` and the directory form is correct as-is.
|
|
1338
|
+
*
|
|
1339
|
+
* @internal exported for tests.
|
|
1340
|
+
*/
|
|
1341
|
+
function generatedClientImport(config) {
|
|
1342
|
+
const dir = `./${config.out.replace(/^\.\//, '').replace(/\/+$/, '')}`;
|
|
1343
|
+
const { ext } = (0, generate_js_1.resolveImportExtension)(config.out, config.importExtension);
|
|
1344
|
+
return `import { turbine } from '${ext === '' ? dir : `${dir}/index${ext}`}';`;
|
|
1345
|
+
}
|
|
1019
1346
|
/** Introspect the database and generate the typed client. */
|
|
1020
1347
|
async function runInitGenerate(config, url) {
|
|
1021
1348
|
const spinner = new ui_js_1.Spinner('Introspecting database').start();
|
|
@@ -1027,16 +1354,22 @@ async function runInitGenerate(config, url) {
|
|
|
1027
1354
|
exclude: config.exclude.length ? config.exclude : undefined,
|
|
1028
1355
|
relationNames: config.relationNames,
|
|
1029
1356
|
});
|
|
1030
|
-
|
|
1357
|
+
const tableNames = Object.keys(schema.tables);
|
|
1358
|
+
spinner.succeed(`Found ${(0, ui_js_1.bold)(String(tableNames.length))} tables`);
|
|
1031
1359
|
const genSpinner = new ui_js_1.Spinner('Generating TypeScript client').start();
|
|
1032
1360
|
const result = (0, generate_js_1.generate)({ schema, outDir: config.out, connectionString: url });
|
|
1033
1361
|
genSpinner.succeed(`Generated ${(0, ui_js_1.bold)(String(result.files.length))} files to ${(0, ui_js_1.cyan)(`${config.out}/`)}`);
|
|
1362
|
+
// The accessor name the generator emits is snakeToCamel of the table name
|
|
1363
|
+
// (generate.ts, generateIndex), so this is the spelling that autocompletes.
|
|
1364
|
+
const first = tableNames[0];
|
|
1365
|
+
return { tableCount: tableNames.length, sampleAccessor: first ? (0, schema_js_1.snakeToCamel)(first) : undefined };
|
|
1034
1366
|
}
|
|
1035
1367
|
catch (err) {
|
|
1036
1368
|
spinner.fail('Could not generate client');
|
|
1037
1369
|
if (err instanceof Error)
|
|
1038
|
-
|
|
1370
|
+
(0, ui_js_1.errorLine)((0, ui_js_1.dim)((0, ui_js_1.redactUrl)(err.message)));
|
|
1039
1371
|
(0, ui_js_1.info)(`Run generation later with: ${(0, ui_js_1.cyan)('npx turbine generate')}`);
|
|
1372
|
+
return { tableCount: null };
|
|
1040
1373
|
}
|
|
1041
1374
|
}
|
|
1042
1375
|
/** Run the seed file. */
|
|
@@ -1260,6 +1593,10 @@ async function cmdInit(args, config) {
|
|
|
1260
1593
|
(0, ui_js_1.newline)();
|
|
1261
1594
|
// TypeScript files this run actually created: they drive the tsx heads-up below.
|
|
1262
1595
|
const tsFilesWritten = [];
|
|
1596
|
+
// What the generate step produced, if it ran. The next-steps text is derived
|
|
1597
|
+
// from this rather than assumed, so it cannot promise a `db.users` the
|
|
1598
|
+
// generated client does not have.
|
|
1599
|
+
let generated = { tableCount: null };
|
|
1263
1600
|
// Execute the plan in order. `run` proceeds; `prompt` asks; `skip` reports.
|
|
1264
1601
|
for (const step of plan) {
|
|
1265
1602
|
if (step.action === 'skip') {
|
|
@@ -1287,7 +1624,7 @@ async function cmdInit(args, config) {
|
|
|
1287
1624
|
break;
|
|
1288
1625
|
}
|
|
1289
1626
|
case 'schema':
|
|
1290
|
-
writeInitSchemaTemplate(config);
|
|
1627
|
+
writeInitSchemaTemplate(config, state.dbHasTables === true);
|
|
1291
1628
|
if ((0, loader_js_1.needsTsLoader)(config.schemaFile))
|
|
1292
1629
|
tsFilesWritten.push(config.schemaFile);
|
|
1293
1630
|
break;
|
|
@@ -1300,7 +1637,7 @@ async function cmdInit(args, config) {
|
|
|
1300
1637
|
await runInitPush(config, url);
|
|
1301
1638
|
break;
|
|
1302
1639
|
case 'generate':
|
|
1303
|
-
await runInitGenerate(config, url);
|
|
1640
|
+
generated = await runInitGenerate(config, url);
|
|
1304
1641
|
break;
|
|
1305
1642
|
case 'seed-run':
|
|
1306
1643
|
await runInitSeed(config);
|
|
@@ -1339,20 +1676,84 @@ async function cmdInit(args, config) {
|
|
|
1339
1676
|
if (!tsxMissing && !(0, loader_js_1.canResolveTsx)()) {
|
|
1340
1677
|
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()))}`);
|
|
1341
1678
|
}
|
|
1679
|
+
(0, ui_js_1.newline)();
|
|
1680
|
+
console.log(` ${(0, ui_js_1.dim)('3.')} Create migrations: ${(0, ui_js_1.cyan)('npx turbine migrate create <name>')}`);
|
|
1681
|
+
console.log(` ${(0, ui_js_1.dim)('4.')} Run migrations: ${(0, ui_js_1.cyan)('npx turbine migrate up')}`);
|
|
1682
|
+
console.log(` ${(0, ui_js_1.dim)('5.')} Seed your database: ${(0, ui_js_1.cyan)('npx turbine seed')}`);
|
|
1683
|
+
}
|
|
1684
|
+
else if (generated.tableCount === null || generated.tableCount === 0) {
|
|
1685
|
+
// There is no typed client to import: either generate did not run (push /
|
|
1686
|
+
// generate declined or skipped), or it ran against a database with no
|
|
1687
|
+
// tables and emitted a client with no accessors. Printing
|
|
1688
|
+
// `await db.users.findMany()` here is the single most misleading line the
|
|
1689
|
+
// CLI produces, because the reader has no way to know it cannot work.
|
|
1690
|
+
console.log(` ${(0, ui_js_1.dim)('1.')} Describe your tables in ${(0, ui_js_1.cyan)(config.schemaFile)}`);
|
|
1691
|
+
console.log(` ${(0, ui_js_1.dim)('2.')} Create them: ${(0, ui_js_1.cyan)('npx turbine push')}`);
|
|
1692
|
+
console.log(` ${(0, ui_js_1.dim)('3.')} Emit the client: ${(0, ui_js_1.cyan)('npx turbine generate')}`);
|
|
1693
|
+
(0, ui_js_1.newline)();
|
|
1694
|
+
console.log(` ${(0, ui_js_1.dim)('Until a table exists, the generated client has no table accessors, so')}`);
|
|
1695
|
+
console.log(` ${(0, ui_js_1.dim)('there is nothing to import yet. Working from a database you already')}`);
|
|
1696
|
+
console.log(` ${(0, ui_js_1.dim)('have? Skip step 1 and run')} ${(0, ui_js_1.cyan)('npx turbine generate')} ${(0, ui_js_1.dim)('on its own.')}`);
|
|
1697
|
+
(0, ui_js_1.newline)();
|
|
1698
|
+
console.log(` ${(0, ui_js_1.dim)('Nothing to connect to yet?')} ${(0, ui_js_1.cyan)('npx turbine studio --demo')} ${(0, ui_js_1.dim)('needs no database.')}`);
|
|
1342
1699
|
}
|
|
1343
1700
|
else {
|
|
1701
|
+
const accessor = generated.sampleAccessor ?? 'users';
|
|
1344
1702
|
console.log(` ${(0, ui_js_1.dim)('1.')} Import the generated client:`);
|
|
1345
|
-
console.log(` ${(0, ui_js_1.cyan)(
|
|
1703
|
+
console.log(` ${(0, ui_js_1.cyan)(generatedClientImport(config))}`);
|
|
1346
1704
|
(0, ui_js_1.newline)();
|
|
1347
1705
|
console.log(` ${(0, ui_js_1.dim)('2.')} Create a connection and query:`);
|
|
1348
1706
|
console.log(` ${(0, ui_js_1.dim)('const db = turbine();')}`);
|
|
1349
|
-
console.log(` ${(0, ui_js_1.dim)(
|
|
1707
|
+
console.log(` ${(0, ui_js_1.dim)(`const rows = await db.${accessor}.findMany();`)}`);
|
|
1708
|
+
(0, ui_js_1.newline)();
|
|
1709
|
+
console.log(` ${(0, ui_js_1.dim)('3.')} Create migrations: ${(0, ui_js_1.cyan)('npx turbine migrate create <name>')}`);
|
|
1710
|
+
console.log(` ${(0, ui_js_1.dim)('4.')} Run migrations: ${(0, ui_js_1.cyan)('npx turbine migrate up')}`);
|
|
1711
|
+
console.log(` ${(0, ui_js_1.dim)('5.')} Seed your database: ${(0, ui_js_1.cyan)('npx turbine seed')}`);
|
|
1350
1712
|
}
|
|
1351
1713
|
(0, ui_js_1.newline)();
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1714
|
+
}
|
|
1715
|
+
// ---------------------------------------------------------------------------
|
|
1716
|
+
// `--schema` is the namespace, `--schema-file` is the file
|
|
1717
|
+
// ---------------------------------------------------------------------------
|
|
1718
|
+
/**
|
|
1719
|
+
* Refuse a `schema` that is plainly a FILE PATH, on every command that reads it
|
|
1720
|
+
* as a Postgres namespace.
|
|
1721
|
+
*
|
|
1722
|
+
* `--schema` / `-s` sets the namespace to introspect (default `public`). The
|
|
1723
|
+
* defineSchema() file is a different idea entirely, and mistaking the two is
|
|
1724
|
+
* silent on every command that used to accept it: `generate` introspects
|
|
1725
|
+
* `WHERE table_schema = './turbine/schema.ts'` and matches nothing, and `push`
|
|
1726
|
+
* reads `config.schemaFile` instead and reports
|
|
1727
|
+
* "Schema file not found: ./turbine/schema.ts", naming a path the reader never
|
|
1728
|
+
* typed while their schema file sits in the directory they ran it from.
|
|
1729
|
+
*
|
|
1730
|
+
* The check was written for `generate` and wired into `generate` alone, which is
|
|
1731
|
+
* how `push`, the command whose flag name the mistake is actually about, kept
|
|
1732
|
+
* the bad error. It is one function called from every `--schema` command now,
|
|
1733
|
+
* for the same reason `resolveColumnName` is one function: two copies of a rule
|
|
1734
|
+
* is how two commands come to disagree about whether an argument is valid.
|
|
1735
|
+
*
|
|
1736
|
+
* @internal exported for tests.
|
|
1737
|
+
*/
|
|
1738
|
+
function refuseSchemaFilePath(config, options = {}) {
|
|
1739
|
+
if (!(0, config_js_1.looksLikeSchemaFilePath)(config.schema))
|
|
1740
|
+
return;
|
|
1741
|
+
(0, ui_js_1.error)(`The ${(0, ui_js_1.cyan)('--schema')} value looks like a file path: ${(0, ui_js_1.cyan)(config.schema)}`);
|
|
1742
|
+
(0, ui_js_1.errorLine)();
|
|
1743
|
+
(0, ui_js_1.errorLine)(`${(0, ui_js_1.dim)('Did you mean')} ${(0, ui_js_1.cyan)(`--schema-file ${config.schema}`)}${(0, ui_js_1.dim)('?')}`);
|
|
1744
|
+
(0, ui_js_1.errorLine)();
|
|
1745
|
+
(0, ui_js_1.errorLine)(`${(0, ui_js_1.dim)('--schema is the Postgres schema NAME to read')} ${(0, ui_js_1.dim)('(default:')} ${(0, ui_js_1.cyan)('public')}${(0, ui_js_1.dim)(').')}`);
|
|
1746
|
+
(0, ui_js_1.errorLine)(`${(0, ui_js_1.dim)('--schema-file is the path to your')} ${(0, ui_js_1.cyan)('defineSchema()')} ${(0, ui_js_1.dim)('file')} ${(0, ui_js_1.dim)(`(default: ${config.schemaFile}).`)}`);
|
|
1747
|
+
(0, ui_js_1.errorLine)();
|
|
1748
|
+
(0, ui_js_1.errorLine)(`${(0, ui_js_1.dim)('Or set it once in')} ${(0, ui_js_1.cyan)('turbine.config.ts')}${(0, ui_js_1.dim)(':')}`);
|
|
1749
|
+
(0, ui_js_1.errorLine)(` ${(0, ui_js_1.green)('schema:')} ${(0, ui_js_1.cyan)("'public'")}${(0, ui_js_1.dim)(", // or omit, reads the 'public' schema")}`);
|
|
1750
|
+
(0, ui_js_1.errorLine)(` ${(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`)')}`);
|
|
1751
|
+
if (options.escapeHatch) {
|
|
1752
|
+
(0, ui_js_1.errorLine)();
|
|
1753
|
+
(0, ui_js_1.errorLine)(`${(0, ui_js_1.dim)('Re-run with')} ${(0, ui_js_1.cyan)(options.escapeHatch)} ${(0, ui_js_1.dim)('to use this literal schema name anyway.')}`);
|
|
1754
|
+
}
|
|
1755
|
+
(0, ui_js_1.errorLine)();
|
|
1756
|
+
process.exit(1);
|
|
1356
1757
|
}
|
|
1357
1758
|
// ---------------------------------------------------------------------------
|
|
1358
1759
|
// Command: generate (pull)
|
|
@@ -1361,24 +1762,11 @@ async function cmdGenerate(args, config) {
|
|
|
1361
1762
|
(0, ui_js_1.banner)();
|
|
1362
1763
|
const url = requireUrl(config);
|
|
1363
1764
|
const startTime = performance.now();
|
|
1364
|
-
//
|
|
1365
|
-
//
|
|
1366
|
-
//
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
(0, ui_js_1.error)(`The "schema" option looks like a file path: ${(0, ui_js_1.cyan)(config.schema)}`);
|
|
1370
|
-
(0, ui_js_1.newline)();
|
|
1371
|
-
console.log(` ${(0, ui_js_1.dim)('"schema" is the Postgres schema NAME to introspect')} ${(0, ui_js_1.dim)('(default:')} ${(0, ui_js_1.cyan)('public')}${(0, ui_js_1.dim)(').')}`);
|
|
1372
|
-
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)('.')}`);
|
|
1373
|
-
(0, ui_js_1.newline)();
|
|
1374
|
-
console.log(` ${(0, ui_js_1.dim)('Fix your')} ${(0, ui_js_1.cyan)('turbine.config.ts')}${(0, ui_js_1.dim)(':')}`);
|
|
1375
|
-
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")}`);
|
|
1376
|
-
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`)')}`);
|
|
1377
|
-
(0, ui_js_1.newline)();
|
|
1378
|
-
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.')}`);
|
|
1379
|
-
(0, ui_js_1.newline)();
|
|
1380
|
-
process.exit(1);
|
|
1381
|
-
}
|
|
1765
|
+
// `--allow-empty` is generate's documented escape hatch for introspecting a
|
|
1766
|
+
// literal schema name that happens to look like a path, so it SKIPS the
|
|
1767
|
+
// refusal rather than merely being named by it.
|
|
1768
|
+
if (args.allowEmpty !== true)
|
|
1769
|
+
refuseSchemaFilePath(config, { escapeHatch: '--allow-empty' });
|
|
1382
1770
|
(0, ui_js_1.label)('Database', (0, ui_js_1.redactUrl)(url));
|
|
1383
1771
|
(0, ui_js_1.label)('Schema', config.schema);
|
|
1384
1772
|
(0, ui_js_1.label)('Output', config.out);
|
|
@@ -1412,15 +1800,15 @@ async function cmdGenerate(args, config) {
|
|
|
1412
1800
|
if (tableNames.length === 0 && !args.allowEmpty) {
|
|
1413
1801
|
(0, ui_js_1.newline)();
|
|
1414
1802
|
(0, ui_js_1.error)(`Introspection matched 0 tables in schema ${(0, ui_js_1.cyan)(config.schema)}, refusing to generate an empty client.`);
|
|
1415
|
-
(0, ui_js_1.
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
(0, ui_js_1.
|
|
1422
|
-
|
|
1423
|
-
(0, ui_js_1.
|
|
1803
|
+
(0, ui_js_1.errorLine)();
|
|
1804
|
+
(0, ui_js_1.errorLine)(`${(0, ui_js_1.dim)('Common causes:')}`);
|
|
1805
|
+
(0, ui_js_1.errorLine)(` ${(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)(').')}`);
|
|
1806
|
+
(0, ui_js_1.errorLine)(` ${(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)('.')}`);
|
|
1807
|
+
(0, ui_js_1.errorLine)(` ${(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.')}`);
|
|
1808
|
+
(0, ui_js_1.errorLine)(` ${(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.')}`);
|
|
1809
|
+
(0, ui_js_1.errorLine)();
|
|
1810
|
+
(0, ui_js_1.errorLine)(`${(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)('.')}`);
|
|
1811
|
+
(0, ui_js_1.errorLine)();
|
|
1424
1812
|
process.exit(1);
|
|
1425
1813
|
}
|
|
1426
1814
|
// Print table summary
|
|
@@ -1458,9 +1846,11 @@ async function cmdGenerate(args, config) {
|
|
|
1458
1846
|
(0, ui_js_1.newline)();
|
|
1459
1847
|
console.log(` ${(0, ui_js_1.bold)('Usage:')}`);
|
|
1460
1848
|
(0, ui_js_1.newline)();
|
|
1461
|
-
console.log(` ${(0, ui_js_1.cyan)(
|
|
1849
|
+
console.log(` ${(0, ui_js_1.cyan)(generatedClientImport(config))}`);
|
|
1462
1850
|
console.log(` ${(0, ui_js_1.dim)('const db = turbine({ connectionString: process.env.DATABASE_URL });')}`);
|
|
1463
|
-
|
|
1851
|
+
// Name a table the client just emitted rather than a hardcoded `users`.
|
|
1852
|
+
const sampleAccessor = (0, schema_js_1.snakeToCamel)(tableNames[0] ?? 'users');
|
|
1853
|
+
console.log(` ${(0, ui_js_1.dim)(`const row = await db.${sampleAccessor}.findFirst();`)}`);
|
|
1464
1854
|
(0, ui_js_1.newline)();
|
|
1465
1855
|
}
|
|
1466
1856
|
/**
|
|
@@ -1520,10 +1910,10 @@ async function cmdMigrateFromPrisma(args, config) {
|
|
|
1520
1910
|
}
|
|
1521
1911
|
if (!(0, node_fs_1.existsSync)(prismaPath)) {
|
|
1522
1912
|
(0, ui_js_1.error)(`Prisma schema not found: ${(0, ui_js_1.cyan)(prismaPath)}`);
|
|
1523
|
-
(0, ui_js_1.
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
(0, ui_js_1.
|
|
1913
|
+
(0, ui_js_1.errorLine)();
|
|
1914
|
+
(0, ui_js_1.errorLine)(`${(0, ui_js_1.dim)('Point at it with')} ${(0, ui_js_1.cyan)('--schema <path>')} ${(0, ui_js_1.dim)('(a .prisma file, or a directory of them;')}`);
|
|
1915
|
+
(0, ui_js_1.errorLine)(`${(0, ui_js_1.dim)('default: prisma/schema.prisma, then prisma/schema/).')}`);
|
|
1916
|
+
(0, ui_js_1.errorLine)();
|
|
1527
1917
|
process.exit(1);
|
|
1528
1918
|
}
|
|
1529
1919
|
// Parse (fatal only on a construct we must understand).
|
|
@@ -1534,8 +1924,8 @@ async function cmdMigrateFromPrisma(args, config) {
|
|
|
1534
1924
|
catch (err) {
|
|
1535
1925
|
(0, ui_js_1.newline)();
|
|
1536
1926
|
(0, ui_js_1.error)(`Could not read ${(0, ui_js_1.cyan)(prismaPath)}`);
|
|
1537
|
-
|
|
1538
|
-
(0, ui_js_1.
|
|
1927
|
+
(0, ui_js_1.errorLine)(`${(0, ui_js_1.red)(err instanceof Error ? err.message : String(err))}`);
|
|
1928
|
+
(0, ui_js_1.errorLine)();
|
|
1539
1929
|
process.exit(1);
|
|
1540
1930
|
}
|
|
1541
1931
|
let ast;
|
|
@@ -1546,8 +1936,8 @@ async function cmdMigrateFromPrisma(args, config) {
|
|
|
1546
1936
|
if (err instanceof prisma_schema_js_1.PrismaParseError) {
|
|
1547
1937
|
(0, ui_js_1.newline)();
|
|
1548
1938
|
(0, ui_js_1.error)(`Could not parse ${(0, ui_js_1.cyan)(prismaPath)}`);
|
|
1549
|
-
|
|
1550
|
-
(0, ui_js_1.
|
|
1939
|
+
(0, ui_js_1.errorLine)(`${(0, ui_js_1.red)(err.message)}`);
|
|
1940
|
+
(0, ui_js_1.errorLine)();
|
|
1551
1941
|
process.exit(1);
|
|
1552
1942
|
}
|
|
1553
1943
|
throw err;
|
|
@@ -1626,7 +2016,7 @@ async function cmdMigrateFromPrisma(args, config) {
|
|
|
1626
2016
|
const rel = (0, node_path_1.relative)(process.cwd(), outDir);
|
|
1627
2017
|
if (rel.startsWith('..') || (0, node_path_1.resolve)(rel) !== outDir) {
|
|
1628
2018
|
(0, ui_js_1.error)(`Output directory must be within the project root. Got: ${config.out}`);
|
|
1629
|
-
(0, ui_js_1.
|
|
2019
|
+
(0, ui_js_1.errorLine)();
|
|
1630
2020
|
process.exit(1);
|
|
1631
2021
|
}
|
|
1632
2022
|
(0, node_fs_1.mkdirSync)(outDir, { recursive: true });
|
|
@@ -1690,6 +2080,7 @@ async function cmdMigrateFromPrisma(args, config) {
|
|
|
1690
2080
|
// ---------------------------------------------------------------------------
|
|
1691
2081
|
async function cmdPush(args, config) {
|
|
1692
2082
|
(0, ui_js_1.banner)();
|
|
2083
|
+
refuseSchemaFilePath(config);
|
|
1693
2084
|
const url = requireUrl(config);
|
|
1694
2085
|
(0, ui_js_1.label)('Database', (0, ui_js_1.redactUrl)(url));
|
|
1695
2086
|
(0, ui_js_1.label)('Schema file', config.schemaFile);
|
|
@@ -1870,34 +2261,37 @@ async function cmdMigrate(args, config) {
|
|
|
1870
2261
|
break;
|
|
1871
2262
|
default:
|
|
1872
2263
|
(0, ui_js_1.error)(`Unknown migrate subcommand: ${sub}`);
|
|
1873
|
-
|
|
2264
|
+
(0, ui_js_1.errorLine)(`${(0, ui_js_1.dim)('Run')} ${(0, ui_js_1.cyan)('npx turbine migrate help')} ${(0, ui_js_1.dim)('for usage.')}`);
|
|
1874
2265
|
process.exit(1);
|
|
1875
2266
|
}
|
|
1876
2267
|
}
|
|
1877
2268
|
async function cmdMigrateCreate(args, config) {
|
|
1878
2269
|
(0, ui_js_1.banner)();
|
|
2270
|
+
// `--auto` / `--from-diff` diff the schema FILE against the namespace, so both
|
|
2271
|
+
// halves of the confusion are live on this one command.
|
|
2272
|
+
refuseSchemaFilePath(config);
|
|
1879
2273
|
const name = args.positional[0];
|
|
1880
2274
|
if (!name) {
|
|
1881
2275
|
(0, ui_js_1.error)('Migration name is required.');
|
|
1882
|
-
(0, ui_js_1.
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
(0, ui_js_1.
|
|
2276
|
+
(0, ui_js_1.errorLine)();
|
|
2277
|
+
(0, ui_js_1.errorLine)(`${(0, ui_js_1.dim)('Usage:')} ${(0, ui_js_1.cyan)('npx turbine migrate create <name>')}`);
|
|
2278
|
+
(0, ui_js_1.errorLine)(`${(0, ui_js_1.dim)('Example:')} ${(0, ui_js_1.cyan)('npx turbine migrate create add_users_table')}`);
|
|
2279
|
+
(0, ui_js_1.errorLine)(`${(0, ui_js_1.dim)('Auto:')} ${(0, ui_js_1.cyan)('npx turbine migrate create my_change --auto')}`);
|
|
2280
|
+
(0, ui_js_1.errorLine)();
|
|
1887
2281
|
process.exit(1);
|
|
1888
2282
|
}
|
|
1889
2283
|
// The scaffold strategies each own the file body, so combining them is
|
|
1890
2284
|
// ambiguous. Refuse up front (before any of the strategy blocks run).
|
|
1891
2285
|
if (args.fromDiff && args.recipe) {
|
|
1892
2286
|
(0, ui_js_1.error)('--from-diff cannot be combined with --recipe.');
|
|
1893
|
-
|
|
1894
|
-
(0, ui_js_1.
|
|
2287
|
+
(0, ui_js_1.errorLine)(`${(0, ui_js_1.dim)('Pick one: --from-diff generates from the schema diff, --recipe scaffolds a pattern.')}`);
|
|
2288
|
+
(0, ui_js_1.errorLine)();
|
|
1895
2289
|
process.exit(1);
|
|
1896
2290
|
}
|
|
1897
2291
|
if (args.fromDiff && args.auto) {
|
|
1898
2292
|
(0, ui_js_1.error)('--from-diff cannot be combined with --auto.');
|
|
1899
|
-
|
|
1900
|
-
(0, ui_js_1.
|
|
2293
|
+
(0, ui_js_1.errorLine)(`${(0, ui_js_1.dim)('Both generate from the schema diff; --from-diff also flags destructive statements.')}`);
|
|
2294
|
+
(0, ui_js_1.errorLine)();
|
|
1901
2295
|
process.exit(1);
|
|
1902
2296
|
}
|
|
1903
2297
|
if (args.auto) {
|
|
@@ -2051,12 +2445,12 @@ async function cmdMigrateCreate(args, config) {
|
|
|
2051
2445
|
if (args.recipe) {
|
|
2052
2446
|
if (!migrate_js_1.MIGRATION_RECIPES[args.recipe]) {
|
|
2053
2447
|
(0, ui_js_1.error)(`Unknown migration recipe: ${args.recipe}`);
|
|
2054
|
-
(0, ui_js_1.
|
|
2055
|
-
|
|
2448
|
+
(0, ui_js_1.errorLine)();
|
|
2449
|
+
(0, ui_js_1.errorLine)(`${(0, ui_js_1.dim)('Available recipes:')}`);
|
|
2056
2450
|
for (const [key, recipe] of Object.entries(migrate_js_1.MIGRATION_RECIPES)) {
|
|
2057
|
-
|
|
2451
|
+
(0, ui_js_1.errorLine)(` ${(0, ui_js_1.cyan)(key)} ${(0, ui_js_1.dim)(recipe.description)}`);
|
|
2058
2452
|
}
|
|
2059
|
-
(0, ui_js_1.
|
|
2453
|
+
(0, ui_js_1.errorLine)();
|
|
2060
2454
|
process.exit(1);
|
|
2061
2455
|
}
|
|
2062
2456
|
const file = (0, migrate_js_1.createMigration)(config.migrationsDir, name, undefined, { recipe: args.recipe });
|
|
@@ -2254,11 +2648,11 @@ async function cmdMigrateDeploy(args, config) {
|
|
|
2254
2648
|
if (plan.mismatches.length > 0) {
|
|
2255
2649
|
if (!args.allowDrift) {
|
|
2256
2650
|
(0, ui_js_1.error)('Deploy blocked by migration drift');
|
|
2257
|
-
(0, ui_js_1.
|
|
2651
|
+
(0, ui_js_1.errorLine)();
|
|
2258
2652
|
for (const line of (0, migrate_js_1.formatChecksumMismatchError)(plan.mismatches).split('\n')) {
|
|
2259
|
-
|
|
2653
|
+
(0, ui_js_1.errorLine)(`${line.replace('[turbine] ', '')}`);
|
|
2260
2654
|
}
|
|
2261
|
-
(0, ui_js_1.
|
|
2655
|
+
(0, ui_js_1.errorLine)();
|
|
2262
2656
|
process.exit(1);
|
|
2263
2657
|
}
|
|
2264
2658
|
(0, ui_js_1.warn)('--allow-drift is set: checksum validation is DISABLED for this deploy.');
|
|
@@ -2589,6 +2983,7 @@ async function cmdSeed(_args, config) {
|
|
|
2589
2983
|
// ---------------------------------------------------------------------------
|
|
2590
2984
|
async function cmdStatus(_args, config) {
|
|
2591
2985
|
(0, ui_js_1.banner)();
|
|
2986
|
+
refuseSchemaFilePath(config);
|
|
2592
2987
|
const url = requireUrl(config);
|
|
2593
2988
|
(0, ui_js_1.label)('Database', (0, ui_js_1.redactUrl)(url));
|
|
2594
2989
|
(0, ui_js_1.label)('Schema', config.schema);
|
|
@@ -2728,6 +3123,7 @@ function refusePoolerConnection(url, args) {
|
|
|
2728
3123
|
}
|
|
2729
3124
|
async function cmdDoctor(args, config) {
|
|
2730
3125
|
const jsonMode = args.json === true;
|
|
3126
|
+
refuseSchemaFilePath(config);
|
|
2731
3127
|
const url = requireUrl(config);
|
|
2732
3128
|
// Before the banner, before introspect, before any pool is constructed.
|
|
2733
3129
|
refusePoolerConnection(url, args);
|
|
@@ -3475,6 +3871,9 @@ function isLoopbackHost(host) {
|
|
|
3475
3871
|
async function cmdStudio(args, config) {
|
|
3476
3872
|
(0, ui_js_1.banner)();
|
|
3477
3873
|
const demo = args.demo === true;
|
|
3874
|
+
// Demo mode reads no namespace at all, so there is nothing to mistake there.
|
|
3875
|
+
if (!demo)
|
|
3876
|
+
refuseSchemaFilePath(config);
|
|
3478
3877
|
// Demo mode is self-contained (seeded in-memory database), so it never needs
|
|
3479
3878
|
// a DATABASE_URL. The placeholder is only used for display.
|
|
3480
3879
|
const url = demo ? 'demo://in-memory' : requireUrl(config);
|
|
@@ -3491,10 +3890,10 @@ async function cmdStudio(args, config) {
|
|
|
3491
3890
|
if (!isLoopbackHost(host)) {
|
|
3492
3891
|
if (!args.allowRemote) {
|
|
3493
3892
|
(0, ui_js_1.error)(`Studio refuses to bind to ${(0, ui_js_1.yellow)(host)} without ${(0, ui_js_1.cyan)('--allow-remote')}.`);
|
|
3494
|
-
(0, ui_js_1.
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
(0, ui_js_1.
|
|
3893
|
+
(0, ui_js_1.errorLine)();
|
|
3894
|
+
(0, ui_js_1.errorLine)(`${(0, ui_js_1.dim)('Loopback only by default')} ${(0, ui_js_1.dim)('(127.0.0.1, localhost, ::1).')}`);
|
|
3895
|
+
(0, ui_js_1.errorLine)(`${(0, ui_js_1.dim)('Pass')} ${(0, ui_js_1.cyan)('--allow-remote')} ${(0, ui_js_1.dim)('to opt in to network exposure.')}`);
|
|
3896
|
+
(0, ui_js_1.errorLine)();
|
|
3498
3897
|
process.exit(1);
|
|
3499
3898
|
}
|
|
3500
3899
|
// warn() prints; it returns void. Wrapping it in console.log() printed a
|
|
@@ -3602,6 +4001,7 @@ async function cmdStudio(args, config) {
|
|
|
3602
4001
|
// Command: mcp, read-only JSON-RPC stdio server
|
|
3603
4002
|
// ---------------------------------------------------------------------------
|
|
3604
4003
|
async function cmdMcp(_args, config) {
|
|
4004
|
+
refuseSchemaFilePath(config);
|
|
3605
4005
|
const url = requireUrl(config);
|
|
3606
4006
|
await (0, mcp_js_1.runMcpServer)({
|
|
3607
4007
|
url,
|
|
@@ -3620,10 +4020,10 @@ async function cmdObserve(args) {
|
|
|
3620
4020
|
const url = process.env.TURBINE_OBSERVE_URL;
|
|
3621
4021
|
if (!url) {
|
|
3622
4022
|
(0, ui_js_1.error)('TURBINE_OBSERVE_URL environment variable is required for the observe command.');
|
|
3623
|
-
(0, ui_js_1.
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
(0, ui_js_1.
|
|
4023
|
+
(0, ui_js_1.errorLine)();
|
|
4024
|
+
(0, ui_js_1.errorLine)(`${(0, ui_js_1.dim)('Set it to the Postgres connection string where metrics are stored.')}`);
|
|
4025
|
+
(0, ui_js_1.errorLine)(`${(0, ui_js_1.dim)('Example:')} ${(0, ui_js_1.cyan)('TURBINE_OBSERVE_URL=postgres://... npx turbine observe')}`);
|
|
4026
|
+
(0, ui_js_1.errorLine)();
|
|
3627
4027
|
process.exit(1);
|
|
3628
4028
|
}
|
|
3629
4029
|
const port = args.port ?? 4984;
|
|
@@ -3638,10 +4038,10 @@ async function cmdObserve(args) {
|
|
|
3638
4038
|
if (!isLoopbackHost(host)) {
|
|
3639
4039
|
if (!args.allowRemote) {
|
|
3640
4040
|
(0, ui_js_1.error)(`Observe refuses to bind to ${(0, ui_js_1.yellow)(host)} without ${(0, ui_js_1.cyan)('--allow-remote')}.`);
|
|
3641
|
-
(0, ui_js_1.
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
(0, ui_js_1.
|
|
4041
|
+
(0, ui_js_1.errorLine)();
|
|
4042
|
+
(0, ui_js_1.errorLine)(`${(0, ui_js_1.dim)('Loopback only by default')} ${(0, ui_js_1.dim)('(127.0.0.1, localhost, ::1).')}`);
|
|
4043
|
+
(0, ui_js_1.errorLine)(`${(0, ui_js_1.dim)('Pass')} ${(0, ui_js_1.cyan)('--allow-remote')} ${(0, ui_js_1.dim)('to opt in to network exposure.')}`);
|
|
4044
|
+
(0, ui_js_1.errorLine)();
|
|
3645
4045
|
process.exit(1);
|
|
3646
4046
|
}
|
|
3647
4047
|
// warn() prints and returns void; see the same guard in cmdStudio.
|
|
@@ -3706,6 +4106,8 @@ function showSubcommandHelp(command) {
|
|
|
3706
4106
|
doctor: showDoctorHelp,
|
|
3707
4107
|
studio: showStudioHelp,
|
|
3708
4108
|
mcp: showMcpHelp,
|
|
4109
|
+
observe: showObserveHelp,
|
|
4110
|
+
skill: showSkillHelp,
|
|
3709
4111
|
};
|
|
3710
4112
|
const fn = helpMap[command];
|
|
3711
4113
|
if (fn) {
|
|
@@ -3733,6 +4135,7 @@ function showInitHelp() {
|
|
|
3733
4135
|
console.log(` ${(0, ui_js_1.cyan)('--url, -u')} ${(0, ui_js_1.dim)('<url>')} Postgres connection string to embed in config`);
|
|
3734
4136
|
console.log(` ${(0, ui_js_1.cyan)('--force, -f')} Overwrite existing config file`);
|
|
3735
4137
|
console.log(` ${(0, ui_js_1.cyan)('--yes, -y')} Accept every step's default (non-interactive)`);
|
|
4138
|
+
console.log(` ${(0, ui_js_1.cyan)('--schema-file')} ${(0, ui_js_1.dim)('<path>')} Where to scaffold the schema file ${(0, ui_js_1.dim)('(default: ./turbine/schema.ts)')}`);
|
|
3736
4139
|
console.log(` ${(0, ui_js_1.cyan)('--skip-schema')} Don't create the starter schema file`);
|
|
3737
4140
|
console.log(` ${(0, ui_js_1.cyan)('--with-schema')} Create it even if the database already has tables`);
|
|
3738
4141
|
console.log(` ${(0, ui_js_1.cyan)('--skip-seed')} Don't create the seed file or run the seed`);
|
|
@@ -3813,8 +4216,11 @@ function showPushHelp() {
|
|
|
3813
4216
|
console.log(` Reads your ${(0, ui_js_1.cyan)('turbine/schema.ts')} file, diffs against the live database,`);
|
|
3814
4217
|
console.log(` and applies CREATE/ALTER statements.`);
|
|
3815
4218
|
(0, ui_js_1.newline)();
|
|
4219
|
+
console.log(` ${(0, ui_js_1.dim)('Note:')} the file is ${(0, ui_js_1.cyan)('--schema-file')}${(0, ui_js_1.dim)('.')} ${(0, ui_js_1.cyan)('--schema')} ${(0, ui_js_1.dim)('is the Postgres namespace to write into.')}`);
|
|
4220
|
+
(0, ui_js_1.newline)();
|
|
3816
4221
|
console.log(` ${(0, ui_js_1.bold)('Options:')}`);
|
|
3817
4222
|
console.log(` ${(0, ui_js_1.cyan)('--url, -u')} ${(0, ui_js_1.dim)('<url>')} Postgres connection string`);
|
|
4223
|
+
console.log(` ${(0, ui_js_1.cyan)('--schema-file')} ${(0, ui_js_1.dim)('<path>')} Your ${(0, ui_js_1.cyan)('defineSchema()')} file ${(0, ui_js_1.dim)('(default: ./turbine/schema.ts)')}`);
|
|
3818
4224
|
console.log(` ${(0, ui_js_1.cyan)('--dry-run')} Show SQL without executing`);
|
|
3819
4225
|
console.log(` ${(0, ui_js_1.cyan)('--allow-destructive')} Skip the interactive confirmation for data-destroying statements ${(0, ui_js_1.dim)('(CI)')}`);
|
|
3820
4226
|
console.log(` ${(0, ui_js_1.cyan)('--verbose, -v')} Show detailed output`);
|
|
@@ -3836,6 +4242,7 @@ function showMigrateHelp() {
|
|
|
3836
4242
|
(0, ui_js_1.newline)();
|
|
3837
4243
|
console.log(` ${(0, ui_js_1.bold)('Options:')}`);
|
|
3838
4244
|
console.log(` ${(0, ui_js_1.cyan)('--url, -u')} ${(0, ui_js_1.dim)('<url>')} Postgres connection string`);
|
|
4245
|
+
console.log(` ${(0, ui_js_1.cyan)('--schema-file')} ${(0, ui_js_1.dim)('<path>')} Your ${(0, ui_js_1.cyan)('defineSchema()')} file, for ${(0, ui_js_1.cyan)('--auto')} / ${(0, ui_js_1.cyan)('--from-diff')}`);
|
|
3839
4246
|
console.log(` ${(0, ui_js_1.cyan)('--auto')} Auto-generate UP/DOWN SQL from schema diff ${(0, ui_js_1.dim)('(create only)')}`);
|
|
3840
4247
|
console.log(` ${(0, ui_js_1.cyan)('--from-diff')} Generate from schema diff, flagging destructive statements ${(0, ui_js_1.dim)('(create only)')}`);
|
|
3841
4248
|
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)')}`);
|
|
@@ -3988,6 +4395,51 @@ function showMcpHelp() {
|
|
|
3988
4395
|
console.log(` ${(0, ui_js_1.cyan)('--exclude')} ${(0, ui_js_1.dim)('<tables>')} Comma-separated tables to exclude`);
|
|
3989
4396
|
(0, ui_js_1.newline)();
|
|
3990
4397
|
}
|
|
4398
|
+
function showObserveHelp() {
|
|
4399
|
+
(0, ui_js_1.banner)();
|
|
4400
|
+
console.log(` ${(0, ui_js_1.bold)('turbine observe')}, Query metrics dashboard`);
|
|
4401
|
+
(0, ui_js_1.newline)();
|
|
4402
|
+
console.log(` ${(0, ui_js_1.bold)('Usage:')}`);
|
|
4403
|
+
console.log(` TURBINE_OBSERVE_URL=postgres://... npx turbine observe ${(0, ui_js_1.dim)('[options]')}`);
|
|
4404
|
+
(0, ui_js_1.newline)();
|
|
4405
|
+
console.log(` Reads the ${(0, ui_js_1.cyan)('_turbine_metrics')} table written by ${(0, ui_js_1.cyan)('db.$observe()')} and serves`);
|
|
4406
|
+
console.log(` per-minute count / avg / p50 / p95 / p99 / error aggregates. Read-only,`);
|
|
4407
|
+
console.log(` loopback-bound, behind a random per-session token, same model as Studio.`);
|
|
4408
|
+
(0, ui_js_1.newline)();
|
|
4409
|
+
console.log(` ${(0, ui_js_1.dim)('The connection string comes from')} ${(0, ui_js_1.cyan)('TURBINE_OBSERVE_URL')}${(0, ui_js_1.dim)(', not')} ${(0, ui_js_1.cyan)('DATABASE_URL')}${(0, ui_js_1.dim)(':')}`);
|
|
4410
|
+
console.log(` ${(0, ui_js_1.dim)('metrics are meant to live in a different database from the one they measure.')}`);
|
|
4411
|
+
(0, ui_js_1.newline)();
|
|
4412
|
+
console.log(` ${(0, ui_js_1.bold)('Options:')}`);
|
|
4413
|
+
console.log(` ${(0, ui_js_1.cyan)('--port')} ${(0, ui_js_1.dim)('<n>')} HTTP port ${(0, ui_js_1.dim)('(default: 4984)')}`);
|
|
4414
|
+
console.log(` ${(0, ui_js_1.cyan)('--host')} ${(0, ui_js_1.dim)('<addr>')} Bind address ${(0, ui_js_1.dim)('(default: 127.0.0.1)')}`);
|
|
4415
|
+
console.log(` ${(0, ui_js_1.cyan)('--no-open')} Don't auto-open the browser`);
|
|
4416
|
+
console.log(` ${(0, ui_js_1.cyan)('--allow-remote')} Allow a non-loopback ${(0, ui_js_1.cyan)('--host')} ${(0, ui_js_1.dim)('(refused without it)')}`);
|
|
4417
|
+
(0, ui_js_1.newline)();
|
|
4418
|
+
}
|
|
4419
|
+
function showSkillHelp() {
|
|
4420
|
+
(0, ui_js_1.banner)();
|
|
4421
|
+
console.log(` ${(0, ui_js_1.bold)('turbine skill')}, Install the agent query skill`);
|
|
4422
|
+
(0, ui_js_1.newline)();
|
|
4423
|
+
console.log(` ${(0, ui_js_1.bold)('Usage:')}`);
|
|
4424
|
+
console.log(` npx turbine skill ${(0, ui_js_1.dim)('[options]')}`);
|
|
4425
|
+
(0, ui_js_1.newline)();
|
|
4426
|
+
console.log(` Writes ${(0, ui_js_1.cyan)('.claude/skills/turbine-orm/SKILL.md')}: how to write Turbine queries,`);
|
|
4427
|
+
console.log(` for a coding agent working in this project. The file ships in the package`);
|
|
4428
|
+
console.log(` rather than being generated here, and every factual claim in it is executed`);
|
|
4429
|
+
console.log(` against a live database on each release.`);
|
|
4430
|
+
(0, ui_js_1.newline)();
|
|
4431
|
+
console.log(` ${(0, ui_js_1.bold)('Options:')}`);
|
|
4432
|
+
console.log(` ${(0, ui_js_1.cyan)('--print')} Write the skill to stdout instead of installing it`);
|
|
4433
|
+
console.log(` ${(0, ui_js_1.cyan)('--agents')} Print the AGENTS.md / CLAUDE.md instructions block`);
|
|
4434
|
+
console.log(` ${(0, ui_js_1.cyan)('--dir')} ${(0, ui_js_1.dim)('<path>')} Skills root to install into ${(0, ui_js_1.dim)('(default: .claude/skills)')}`);
|
|
4435
|
+
(0, ui_js_1.newline)();
|
|
4436
|
+
console.log(` ${(0, ui_js_1.bold)('Examples:')}`);
|
|
4437
|
+
console.log(` ${(0, ui_js_1.dim)('$')} npx turbine skill`);
|
|
4438
|
+
console.log(` ${(0, ui_js_1.dim)('$')} npx turbine skill --agents >> AGENTS.md`);
|
|
4439
|
+
(0, ui_js_1.newline)();
|
|
4440
|
+
console.log(` ${(0, ui_js_1.dim)('Also worth connecting:')} ${(0, ui_js_1.cyan)('npx turbine mcp')}${(0, ui_js_1.dim)(', the read-only MCP server.')}`);
|
|
4441
|
+
(0, ui_js_1.newline)();
|
|
4442
|
+
}
|
|
3991
4443
|
// ---------------------------------------------------------------------------
|
|
3992
4444
|
// Help
|
|
3993
4445
|
// ---------------------------------------------------------------------------
|
|
@@ -4018,7 +4470,8 @@ function showHelp() {
|
|
|
4018
4470
|
console.log(` ${(0, ui_js_1.bold)('Options:')}`);
|
|
4019
4471
|
console.log(` ${(0, ui_js_1.cyan)('--url, -u')} ${(0, ui_js_1.dim)('<url>')} Postgres connection string`);
|
|
4020
4472
|
console.log(` ${(0, ui_js_1.cyan)('--out, -o')} ${(0, ui_js_1.dim)('<dir>')} Output directory ${(0, ui_js_1.dim)('(default: ./generated/turbine)')}`);
|
|
4021
|
-
console.log(` ${(0, ui_js_1.cyan)('--schema, -s')} ${(0, ui_js_1.dim)('<name>')} Postgres schema ${(0, ui_js_1.dim)('(default: public)')}`);
|
|
4473
|
+
console.log(` ${(0, ui_js_1.cyan)('--schema, -s')} ${(0, ui_js_1.dim)('<name>')} Postgres schema NAME to read ${(0, ui_js_1.dim)('(default: public)')}`);
|
|
4474
|
+
console.log(` ${(0, ui_js_1.cyan)('--schema-file')} ${(0, ui_js_1.dim)('<path>')} Your ${(0, ui_js_1.cyan)('defineSchema()')} file ${(0, ui_js_1.dim)('(push / migrate --auto / init)')}`);
|
|
4022
4475
|
console.log(` ${(0, ui_js_1.cyan)('--include')} ${(0, ui_js_1.dim)('<tables>')} Comma-separated tables to include`);
|
|
4023
4476
|
console.log(` ${(0, ui_js_1.cyan)('--exclude')} ${(0, ui_js_1.dim)('<tables>')} Comma-separated tables to exclude`);
|
|
4024
4477
|
console.log(` ${(0, ui_js_1.cyan)('--dry-run')} Show SQL without executing`);
|
|
@@ -4136,9 +4589,9 @@ function cmdSkill(args) {
|
|
|
4136
4589
|
const source = root ? (0, node_path_1.resolve)(root, 'skills', 'turbine-orm', 'SKILL.md') : undefined;
|
|
4137
4590
|
if (!source || !(0, node_fs_1.existsSync)(source)) {
|
|
4138
4591
|
(0, ui_js_1.error)('Could not find the packaged skill inside turbine-orm.');
|
|
4139
|
-
(0, ui_js_1.
|
|
4140
|
-
|
|
4141
|
-
(0, ui_js_1.
|
|
4592
|
+
(0, ui_js_1.errorLine)();
|
|
4593
|
+
(0, ui_js_1.errorLine)(`${(0, ui_js_1.dim)('Read it online instead:')} ${(0, ui_js_1.cyan)('https://turbineorm.dev/ai-agents')}`);
|
|
4594
|
+
(0, ui_js_1.errorLine)();
|
|
4142
4595
|
process.exit(1);
|
|
4143
4596
|
}
|
|
4144
4597
|
const body = (0, node_fs_1.readFileSync)(source, 'utf8');
|
|
@@ -4236,6 +4689,7 @@ async function main() {
|
|
|
4236
4689
|
url: args.url,
|
|
4237
4690
|
out: args.out,
|
|
4238
4691
|
schema: args.schema,
|
|
4692
|
+
schemaFile: args.schemaFile,
|
|
4239
4693
|
include: args.include,
|
|
4240
4694
|
exclude: args.exclude,
|
|
4241
4695
|
importExtension: args.importExtension,
|
|
@@ -4312,50 +4766,60 @@ async function main() {
|
|
|
4312
4766
|
case 'skill':
|
|
4313
4767
|
cmdSkill(args);
|
|
4314
4768
|
break;
|
|
4315
|
-
default:
|
|
4769
|
+
default: {
|
|
4316
4770
|
(0, ui_js_1.error)(`Unknown command: ${(0, ui_js_1.bold)(args.command)}`);
|
|
4317
|
-
(0, ui_js_1.
|
|
4318
|
-
|
|
4319
|
-
|
|
4771
|
+
(0, ui_js_1.errorLine)();
|
|
4772
|
+
// Only canonical spellings are offered. `nameCloseness` scores substring
|
|
4773
|
+
// containment above edit distance, so including the one-letter aliases
|
|
4774
|
+
// would answer "genrate" with "g" (contained, score 501) instead of
|
|
4775
|
+
// "generate" (one edit away, score 99).
|
|
4776
|
+
const suggestion = (0, utils_js_1.closestName)(args.command, knownCommands());
|
|
4777
|
+
if (suggestion) {
|
|
4778
|
+
(0, ui_js_1.errorLine)(`${(0, ui_js_1.dim)('Did you mean')} ${(0, ui_js_1.cyan)(`turbine ${suggestion}`)}${(0, ui_js_1.dim)('?')}`);
|
|
4779
|
+
(0, ui_js_1.errorLine)();
|
|
4780
|
+
}
|
|
4781
|
+
(0, ui_js_1.errorLine)(`${(0, ui_js_1.dim)('Run')} ${(0, ui_js_1.cyan)('npx turbine help')} ${(0, ui_js_1.dim)('for available commands.')}`);
|
|
4782
|
+
(0, ui_js_1.errorLine)();
|
|
4320
4783
|
process.exit(1);
|
|
4784
|
+
}
|
|
4321
4785
|
}
|
|
4322
4786
|
}
|
|
4323
4787
|
catch (err) {
|
|
4324
4788
|
if (err instanceof Error) {
|
|
4325
4789
|
if (err.message.includes('ECONNREFUSED') || err.message.includes('connection')) {
|
|
4326
|
-
(0, ui_js_1.
|
|
4790
|
+
(0, ui_js_1.errorLine)();
|
|
4327
4791
|
(0, ui_js_1.error)(`Could not connect to database`);
|
|
4328
|
-
|
|
4329
|
-
(0, ui_js_1.
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
|
|
4792
|
+
(0, ui_js_1.errorLine)(`${(0, ui_js_1.dim)((0, ui_js_1.redactUrl)(err.message))}`);
|
|
4793
|
+
(0, ui_js_1.errorLine)();
|
|
4794
|
+
(0, ui_js_1.errorLine)(`${(0, ui_js_1.dim)('Check that:')}`);
|
|
4795
|
+
(0, ui_js_1.errorLine)(` ${(0, ui_js_1.dim)('1.')} Your database is running`);
|
|
4796
|
+
(0, ui_js_1.errorLine)(` ${(0, ui_js_1.dim)('2.')} The connection string is correct`);
|
|
4797
|
+
(0, ui_js_1.errorLine)(` ${(0, ui_js_1.dim)('3.')} Network/firewall allows the connection`);
|
|
4334
4798
|
}
|
|
4335
4799
|
else if (err.message.includes('authentication')) {
|
|
4336
|
-
(0, ui_js_1.
|
|
4800
|
+
(0, ui_js_1.errorLine)();
|
|
4337
4801
|
(0, ui_js_1.error)(`Authentication failed`);
|
|
4338
|
-
|
|
4802
|
+
(0, ui_js_1.errorLine)(`${(0, ui_js_1.dim)((0, ui_js_1.redactUrl)(err.message))}`);
|
|
4339
4803
|
}
|
|
4340
4804
|
else if (err.message.includes('does not exist')) {
|
|
4341
|
-
(0, ui_js_1.
|
|
4805
|
+
(0, ui_js_1.errorLine)();
|
|
4342
4806
|
(0, ui_js_1.error)(`Database or schema not found`);
|
|
4343
|
-
|
|
4807
|
+
(0, ui_js_1.errorLine)(`${(0, ui_js_1.dim)((0, ui_js_1.redactUrl)(err.message))}`);
|
|
4344
4808
|
}
|
|
4345
4809
|
else {
|
|
4346
|
-
(0, ui_js_1.
|
|
4810
|
+
(0, ui_js_1.errorLine)();
|
|
4347
4811
|
(0, ui_js_1.error)((0, ui_js_1.redactUrl)(err.message));
|
|
4348
4812
|
if (args.verbose && err.stack) {
|
|
4349
|
-
(0, ui_js_1.
|
|
4350
|
-
console.
|
|
4813
|
+
(0, ui_js_1.errorLine)();
|
|
4814
|
+
console.error((0, ui_js_1.dim)((0, ui_js_1.redactUrl)(err.stack)));
|
|
4351
4815
|
}
|
|
4352
4816
|
}
|
|
4353
4817
|
}
|
|
4354
4818
|
else {
|
|
4355
|
-
(0, ui_js_1.
|
|
4819
|
+
(0, ui_js_1.errorLine)();
|
|
4356
4820
|
(0, ui_js_1.error)(`Unexpected error: ${(0, ui_js_1.redactUrl)(String(err))}`);
|
|
4357
4821
|
}
|
|
4358
|
-
(0, ui_js_1.
|
|
4822
|
+
(0, ui_js_1.errorLine)();
|
|
4359
4823
|
process.exit(1);
|
|
4360
4824
|
}
|
|
4361
4825
|
}
|