turbine-orm 0.19.0 → 0.19.2
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 +83 -15
- package/dist/adapters/index.d.ts +3 -2
- package/dist/cjs/cli/index.js +43 -13
- package/dist/cjs/cli/loader.js +62 -7
- package/dist/cjs/cli/studio-ui.generated.js +1 -1
- package/dist/cjs/cli/studio.js +25 -35
- package/dist/cjs/client.js +20 -13
- package/dist/cjs/query/builder.js +342 -104
- package/dist/cjs/query/utils.js +1 -0
- package/dist/cli/index.js +45 -15
- package/dist/cli/loader.d.ts +22 -5
- package/dist/cli/loader.js +61 -7
- package/dist/cli/migrate.d.ts +2 -2
- package/dist/cli/studio-ui.generated.js +1 -1
- package/dist/cli/studio.d.ts +9 -14
- package/dist/cli/studio.js +25 -34
- package/dist/client.d.ts +12 -13
- package/dist/client.js +20 -13
- package/dist/index.d.ts +1 -1
- package/dist/query/builder.d.ts +43 -6
- package/dist/query/builder.js +342 -104
- package/dist/query/index.d.ts +1 -1
- package/dist/query/types.d.ts +62 -12
- package/dist/query/utils.js +1 -0
- package/package.json +4 -4
- package/dist/cjs/query.js +0 -2711
- package/dist/query.d.ts +0 -878
- package/dist/query.js +0 -2705
package/dist/cjs/query/utils.js
CHANGED
package/dist/cli/index.js
CHANGED
|
@@ -20,14 +20,14 @@
|
|
|
20
20
|
* npx turbine init --url postgres://...
|
|
21
21
|
* npx turbine migrate create add_users_table
|
|
22
22
|
*/
|
|
23
|
-
import { appendFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
23
|
+
import { appendFileSync, existsSync, mkdirSync, readFileSync, realpathSync, writeFileSync } from 'node:fs';
|
|
24
24
|
import { dirname, relative, resolve } from 'node:path';
|
|
25
25
|
import { pathToFileURL } from 'node:url';
|
|
26
26
|
import { generate } from '../generate.js';
|
|
27
27
|
import { introspect } from '../introspect.js';
|
|
28
28
|
import { schemaDiff, schemaPush } from '../schema-sql.js';
|
|
29
29
|
import { configTemplate, findConfigFile, loadConfig, resolveConfig } from './config.js';
|
|
30
|
-
import { needsTsLoader, registerTsLoader } from './loader.js';
|
|
30
|
+
import { canResolveTsx, getTsLoaderError, needsTsLoader, registerTsLoader } from './loader.js';
|
|
31
31
|
import { createMigration, listMigrationFiles, migrateDown, migrateStatus, migrateUp } from './migrate.js';
|
|
32
32
|
import { startObserve } from './observe.js';
|
|
33
33
|
import { startStudio } from './studio.js';
|
|
@@ -132,6 +132,15 @@ function failMissingTsLoader(filePath, reason) {
|
|
|
132
132
|
console.log(` ${dim('Your Node.js version does not support')} ${cyan('module.register()')}.`);
|
|
133
133
|
console.log(` ${dim('Upgrade to Node.js')} ${cyan('20.6+')} ${dim('or use a')} ${cyan('.js')} ${dim('/')} ${cyan('.mjs')} ${dim('config file.')}`);
|
|
134
134
|
}
|
|
135
|
+
else if (reason === 'failed') {
|
|
136
|
+
// tsx IS installed but registering its loader threw. Report the real
|
|
137
|
+
// cause — telling the user to install tsx here would be a misdiagnosis.
|
|
138
|
+
console.log(` ${dim('tsx is installed, but registering its TypeScript loader failed:')}`);
|
|
139
|
+
newline();
|
|
140
|
+
console.log(` ${getTsLoaderError() ?? '(unknown error)'}`);
|
|
141
|
+
newline();
|
|
142
|
+
console.log(` ${dim('Try upgrading tsx:')} ${cyan('npm install --save-dev tsx@latest')}${dim(', or rename your file to')} ${cyan('.mjs')}.`);
|
|
143
|
+
}
|
|
135
144
|
else {
|
|
136
145
|
console.log(` ${dim('Loading .ts config / schema files requires')} ${cyan('tsx')} ${dim('to be installed.')}`);
|
|
137
146
|
newline();
|
|
@@ -175,7 +184,7 @@ async function loadSchemaFile(schemaFile) {
|
|
|
175
184
|
// ERR_UNKNOWN_FILE_EXTENSION for `.ts`.
|
|
176
185
|
if (needsTsLoader(absPath)) {
|
|
177
186
|
const status = await registerTsLoader();
|
|
178
|
-
if (status === 'missing' || status === 'unsupported') {
|
|
187
|
+
if (status === 'missing' || status === 'unsupported' || status === 'failed') {
|
|
179
188
|
failMissingTsLoader(schemaFile, status);
|
|
180
189
|
}
|
|
181
190
|
}
|
|
@@ -311,7 +320,7 @@ export default defineSchema({
|
|
|
311
320
|
// id: { type: 'serial', primaryKey: true },
|
|
312
321
|
// email: { type: 'text', notNull: true, unique: true },
|
|
313
322
|
// name: { type: 'text', notNull: true },
|
|
314
|
-
// created_at: { type: '
|
|
323
|
+
// created_at: { type: 'timestamp', default: 'NOW()' },
|
|
315
324
|
// },
|
|
316
325
|
});
|
|
317
326
|
`, 'utf-8');
|
|
@@ -374,6 +383,9 @@ export default defineSchema({
|
|
|
374
383
|
console.log(` ${dim('or create a')} ${cyan('.env')} ${dim('file with')} ${cyan('DATABASE_URL=postgres://...')}`);
|
|
375
384
|
}
|
|
376
385
|
console.log(` ${dim('2.')} Run ${cyan('npx turbine generate')} to introspect your DB`);
|
|
386
|
+
if (!canResolveTsx()) {
|
|
387
|
+
console.log(` ${dim('Note: the TypeScript config requires')} ${cyan('tsx')} ${dim('—')} ${cyan('npm install --save-dev tsx')}`);
|
|
388
|
+
}
|
|
377
389
|
}
|
|
378
390
|
else {
|
|
379
391
|
console.log(` ${dim('1.')} Import the generated client:`);
|
|
@@ -1117,13 +1129,15 @@ function showMigrateHelp() {
|
|
|
1117
1129
|
newline();
|
|
1118
1130
|
console.log(` ${bold('Options:')}`);
|
|
1119
1131
|
console.log(` ${cyan('--url, -u')} ${dim('<url>')} Postgres connection string`);
|
|
1132
|
+
console.log(` ${cyan('--auto')} Auto-generate UP/DOWN SQL from schema diff ${dim('(create only)')}`);
|
|
1120
1133
|
console.log(` ${cyan('--step, -n')} ${dim('<N>')} Number of migrations to apply/rollback`);
|
|
1121
|
-
console.log(` ${cyan('--dry-run')}
|
|
1122
|
-
console.log(` ${cyan('--allow-drift')}
|
|
1123
|
-
console.log(` ${cyan('--verbose, -v')}
|
|
1134
|
+
console.log(` ${cyan('--dry-run')} Show SQL without executing`);
|
|
1135
|
+
console.log(` ${cyan('--allow-drift')} Bypass checksum validation ${dim('(migrate up only — advanced)')}`);
|
|
1136
|
+
console.log(` ${cyan('--verbose, -v')} Show detailed output`);
|
|
1124
1137
|
newline();
|
|
1125
1138
|
console.log(` ${bold('Examples:')}`);
|
|
1126
1139
|
console.log(` ${dim('$')} npx turbine migrate create add_users_table`);
|
|
1140
|
+
console.log(` ${dim('$')} npx turbine migrate create add_email_index --auto`);
|
|
1127
1141
|
console.log(` ${dim('$')} npx turbine migrate up`);
|
|
1128
1142
|
console.log(` ${dim('$')} npx turbine migrate down --step 2`);
|
|
1129
1143
|
console.log(` ${dim('$')} npx turbine migrate status`);
|
|
@@ -1168,16 +1182,17 @@ function showHelp() {
|
|
|
1168
1182
|
newline();
|
|
1169
1183
|
console.log(` ${bold('Commands:')}`);
|
|
1170
1184
|
console.log(` ${cyan('init')} Initialize a Turbine project`);
|
|
1171
|
-
console.log(` ${cyan('generate')} ${dim('| pull')}
|
|
1185
|
+
console.log(` ${cyan('generate')} ${dim('| pull')} Introspect database ${symbols.arrow} generate types`);
|
|
1172
1186
|
console.log(` ${cyan('push')} Apply schema definitions to database`);
|
|
1173
|
-
console.log(` ${cyan('migrate')} ${dim('<sub>')}
|
|
1174
|
-
console.log(` ${dim('create <name>')}
|
|
1187
|
+
console.log(` ${cyan('migrate')} ${dim('<sub>')} SQL migration management`);
|
|
1188
|
+
console.log(` ${dim('create <name>')} Create a new migration file`);
|
|
1175
1189
|
console.log(` ${dim('up')} Apply pending migrations`);
|
|
1176
1190
|
console.log(` ${dim('down')} Rollback last migration`);
|
|
1177
1191
|
console.log(` ${dim('status')} Show applied/pending migrations`);
|
|
1178
1192
|
console.log(` ${cyan('seed')} Run seed file`);
|
|
1179
|
-
console.log(` ${cyan('status')} ${dim('| info')}
|
|
1193
|
+
console.log(` ${cyan('status')} ${dim('| info')} Show schema summary`);
|
|
1180
1194
|
console.log(` ${cyan('studio')} Launch local read-only web UI`);
|
|
1195
|
+
console.log(` ${cyan('observe')} Launch metrics dashboard ${dim('(requires TURBINE_OBSERVE_URL)')}`);
|
|
1181
1196
|
newline();
|
|
1182
1197
|
console.log(` ${bold('Options:')}`);
|
|
1183
1198
|
console.log(` ${cyan('--url, -u')} ${dim('<url>')} Postgres connection string`);
|
|
@@ -1189,8 +1204,13 @@ function showHelp() {
|
|
|
1189
1204
|
console.log(` ${cyan('--verbose, -v')} Show detailed output`);
|
|
1190
1205
|
console.log(` ${cyan('--force, -f')} Overwrite existing files`);
|
|
1191
1206
|
newline();
|
|
1192
|
-
console.log(` ${bold('
|
|
1193
|
-
console.log(` ${cyan('--
|
|
1207
|
+
console.log(` ${bold('Migrate options:')}`);
|
|
1208
|
+
console.log(` ${cyan('--auto')} Auto-generate UP/DOWN SQL from schema diff ${dim('(create)')}`);
|
|
1209
|
+
console.log(` ${cyan('--step, -n')} ${dim('<N>')} Number of migrations to apply/rollback`);
|
|
1210
|
+
console.log(` ${cyan('--allow-drift')} Bypass checksum validation on ${cyan('migrate up')} ${dim('(advanced)')}`);
|
|
1211
|
+
newline();
|
|
1212
|
+
console.log(` ${bold('Studio / observe options:')}`);
|
|
1213
|
+
console.log(` ${cyan('--port')} ${dim('<n>')} HTTP port ${dim('(default: 4983 studio, 4984 observe)')}`);
|
|
1194
1214
|
console.log(` ${cyan('--host')} ${dim('<addr>')} Bind address ${dim('(default: 127.0.0.1)')}`);
|
|
1195
1215
|
console.log(` ${cyan('--no-open')} Don't auto-open the browser`);
|
|
1196
1216
|
newline();
|
|
@@ -1214,7 +1234,17 @@ function showVersion() {
|
|
|
1214
1234
|
// Using process.argv[1] instead of import.meta.url so the same code compiles
|
|
1215
1235
|
// cleanly for both the ESM and CJS builds.
|
|
1216
1236
|
try {
|
|
1217
|
-
|
|
1237
|
+
// Resolve symlinks first: `npx turbine` runs via node_modules/.bin/turbine,
|
|
1238
|
+
// a symlink whose dirname would walk the CONSUMER's tree and never find
|
|
1239
|
+
// turbine-orm's package.json (printing no version number at all).
|
|
1240
|
+
let entry = process.argv[1] ?? '';
|
|
1241
|
+
try {
|
|
1242
|
+
entry = realpathSync(entry);
|
|
1243
|
+
}
|
|
1244
|
+
catch {
|
|
1245
|
+
// keep the raw path if realpath fails (e.g. deleted cwd)
|
|
1246
|
+
}
|
|
1247
|
+
let dir = dirname(entry);
|
|
1218
1248
|
for (let i = 0; i < 6; i++) {
|
|
1219
1249
|
const candidate = resolve(dir, 'package.json');
|
|
1220
1250
|
if (existsSync(candidate)) {
|
|
@@ -1262,7 +1292,7 @@ async function main() {
|
|
|
1262
1292
|
const configPath = findConfigFile();
|
|
1263
1293
|
if (needsTsLoader(configPath)) {
|
|
1264
1294
|
const status = await registerTsLoader();
|
|
1265
|
-
if (status === 'missing' || status === 'unsupported') {
|
|
1295
|
+
if (status === 'missing' || status === 'unsupported' || status === 'failed') {
|
|
1266
1296
|
failMissingTsLoader(configPath ?? 'turbine.config.ts', status);
|
|
1267
1297
|
}
|
|
1268
1298
|
}
|
package/dist/cli/loader.d.ts
CHANGED
|
@@ -8,9 +8,18 @@
|
|
|
8
8
|
*
|
|
9
9
|
* Strategy:
|
|
10
10
|
* 1. If the file we're about to import ends in `.ts` / `.mts` / `.cts`,
|
|
11
|
-
* probe whether `tsx
|
|
12
|
-
* 2.
|
|
13
|
-
*
|
|
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".
|
|
14
23
|
*
|
|
15
24
|
* `tsx` is intentionally NOT a runtime dependency — many projects already
|
|
16
25
|
* have it, and adding a heavy dev tool to a 1-dependency ORM would be silly.
|
|
@@ -28,7 +37,12 @@ export declare function needsTsLoader(filePath: string | null | undefined): bool
|
|
|
28
37
|
* Accepts an injected `resolver` so unit tests don't need a real filesystem.
|
|
29
38
|
*/
|
|
30
39
|
export declare function canResolveTsx(resolver?: (id: string) => string): boolean;
|
|
31
|
-
export type TsLoaderStatus = 'registered' | 'already' | 'unsupported' | 'missing';
|
|
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;
|
|
32
46
|
/**
|
|
33
47
|
* Register the tsx ESM loader so subsequent dynamic imports of `.ts` files
|
|
34
48
|
* work. Safe to call multiple times — internal flag prevents double registration.
|
|
@@ -36,8 +50,11 @@ export type TsLoaderStatus = 'registered' | 'already' | 'unsupported' | 'missing
|
|
|
36
50
|
* Returns:
|
|
37
51
|
* - 'registered' loader was successfully registered this call
|
|
38
52
|
* - 'already' a loader was previously registered (idempotent)
|
|
39
|
-
* - 'unsupported' Node lacks `module.register()` (Node < 20.6)
|
|
53
|
+
* - 'unsupported' Node lacks `module.register()` (Node < 20.6) and tsx has
|
|
54
|
+
* no programmatic API to fall back to
|
|
40
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
|
|
41
58
|
*/
|
|
42
59
|
export declare function registerTsLoader(): Promise<TsLoaderStatus>;
|
|
43
60
|
/** Reset the loader state — used by unit tests only. */
|
package/dist/cli/loader.js
CHANGED
|
@@ -8,9 +8,18 @@
|
|
|
8
8
|
*
|
|
9
9
|
* Strategy:
|
|
10
10
|
* 1. If the file we're about to import ends in `.ts` / `.mts` / `.cts`,
|
|
11
|
-
* probe whether `tsx
|
|
12
|
-
* 2.
|
|
13
|
-
*
|
|
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".
|
|
14
23
|
*
|
|
15
24
|
* `tsx` is intentionally NOT a runtime dependency — many projects already
|
|
16
25
|
* have it, and adding a heavy dev tool to a 1-dependency ORM would be silly.
|
|
@@ -50,6 +59,14 @@ export function canResolveTsx(resolver) {
|
|
|
50
59
|
}
|
|
51
60
|
}
|
|
52
61
|
let tsLoaderState = null;
|
|
62
|
+
let tsLoaderError = null;
|
|
63
|
+
/**
|
|
64
|
+
* The underlying error message from the last failed registration attempt,
|
|
65
|
+
* or null. Lets the CLI report the REAL cause instead of guessing.
|
|
66
|
+
*/
|
|
67
|
+
export function getTsLoaderError() {
|
|
68
|
+
return tsLoaderError;
|
|
69
|
+
}
|
|
53
70
|
/**
|
|
54
71
|
* Register the tsx ESM loader so subsequent dynamic imports of `.ts` files
|
|
55
72
|
* work. Safe to call multiple times — internal flag prevents double registration.
|
|
@@ -57,17 +74,51 @@ let tsLoaderState = null;
|
|
|
57
74
|
* Returns:
|
|
58
75
|
* - 'registered' loader was successfully registered this call
|
|
59
76
|
* - 'already' a loader was previously registered (idempotent)
|
|
60
|
-
* - 'unsupported' Node lacks `module.register()` (Node < 20.6)
|
|
77
|
+
* - 'unsupported' Node lacks `module.register()` (Node < 20.6) and tsx has
|
|
78
|
+
* no programmatic API to fall back to
|
|
61
79
|
* - 'missing' `tsx` is not installed in the user's project
|
|
80
|
+
* - 'failed' tsx IS installed but registration threw — see
|
|
81
|
+
* {@link getTsLoaderError} for the underlying message
|
|
62
82
|
*/
|
|
63
83
|
export async function registerTsLoader() {
|
|
64
84
|
if (tsLoaderState === 'registered' || tsLoaderState === 'already') {
|
|
65
85
|
return 'already';
|
|
66
86
|
}
|
|
87
|
+
const userRequire = createRequire(`${process.cwd()}/`);
|
|
88
|
+
// Preferred: tsx's supported programmatic API (tsx >= 4.0).
|
|
89
|
+
let apiPath = null;
|
|
90
|
+
try {
|
|
91
|
+
apiPath = userRequire.resolve('tsx/esm/api');
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
apiPath = null;
|
|
95
|
+
}
|
|
96
|
+
if (apiPath) {
|
|
97
|
+
try {
|
|
98
|
+
const api = (await import(pathToFileURL(apiPath).href));
|
|
99
|
+
if (typeof api.register !== 'function') {
|
|
100
|
+
throw new Error(`tsx/esm/api resolved at ${apiPath} but exports no register() function`);
|
|
101
|
+
}
|
|
102
|
+
api.register();
|
|
103
|
+
tsLoaderState = 'registered';
|
|
104
|
+
tsLoaderError = null;
|
|
105
|
+
return 'registered';
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
tsLoaderState = 'failed';
|
|
109
|
+
tsLoaderError = err instanceof Error ? err.message : String(err);
|
|
110
|
+
return 'failed';
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// tsx/esm/api not resolvable — is tsx installed at all?
|
|
67
114
|
if (!canResolveTsx()) {
|
|
68
115
|
tsLoaderState = 'missing';
|
|
69
116
|
return 'missing';
|
|
70
117
|
}
|
|
118
|
+
// Legacy fallback for tsx < 4.0 (no tsx/esm/api): Node's module.register.
|
|
119
|
+
// On tsx >= 4.19 this path throws ("tsx must be loaded with --import
|
|
120
|
+
// instead of --loader") — but those versions all ship tsx/esm/api, so we
|
|
121
|
+
// only land here for genuinely old installs.
|
|
71
122
|
try {
|
|
72
123
|
const mod = await import('node:module');
|
|
73
124
|
const register = mod.register;
|
|
@@ -77,15 +128,18 @@ export async function registerTsLoader() {
|
|
|
77
128
|
}
|
|
78
129
|
register('tsx/esm', pathToFileURL(`${process.cwd()}/`));
|
|
79
130
|
tsLoaderState = 'registered';
|
|
131
|
+
tsLoaderError = null;
|
|
80
132
|
return 'registered';
|
|
81
133
|
}
|
|
82
|
-
catch {
|
|
83
|
-
tsLoaderState = '
|
|
84
|
-
|
|
134
|
+
catch (err) {
|
|
135
|
+
tsLoaderState = 'failed';
|
|
136
|
+
tsLoaderError = err instanceof Error ? err.message : String(err);
|
|
137
|
+
return 'failed';
|
|
85
138
|
}
|
|
86
139
|
}
|
|
87
140
|
/** Reset the loader state — used by unit tests only. */
|
|
88
141
|
export function _resetTsLoaderStateForTests() {
|
|
89
142
|
tsLoaderState = null;
|
|
143
|
+
tsLoaderError = null;
|
|
90
144
|
}
|
|
91
145
|
//# sourceMappingURL=loader.js.map
|
package/dist/cli/migrate.d.ts
CHANGED
|
@@ -113,8 +113,8 @@ export declare function deriveLockId(databaseName: string): number;
|
|
|
113
113
|
*/
|
|
114
114
|
export declare function migrateUp(connectionString: string, migrationsDir: string, options?: {
|
|
115
115
|
step?: number;
|
|
116
|
-
allowDrift?: boolean
|
|
117
|
-
force?: boolean
|
|
116
|
+
allowDrift?: boolean;
|
|
117
|
+
force?: boolean /** @deprecated use allowDrift */;
|
|
118
118
|
adapter?: DatabaseAdapter;
|
|
119
119
|
dialect?: Dialect;
|
|
120
120
|
}): Promise<{
|