turbine-orm 0.49.0 → 0.50.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (157) hide show
  1. package/README.md +122 -39
  2. package/dist/cjs/adapters/cockroachdb.d.ts +39 -0
  3. package/dist/cjs/adapters/index.d.ts +110 -0
  4. package/dist/cjs/adapters/yugabytedb.d.ts +51 -0
  5. package/dist/cjs/cli/config.d.ts +181 -0
  6. package/dist/cjs/cli/config.js +32 -6
  7. package/dist/cjs/cli/destructive.d.ts +38 -0
  8. package/dist/cjs/cli/index.d.ts +359 -0
  9. package/dist/cjs/cli/index.js +228 -56
  10. package/dist/cjs/cli/loader.d.ts +61 -0
  11. package/dist/cjs/cli/mcp.d.ts +42 -0
  12. package/dist/cjs/cli/migrate.d.ts +356 -0
  13. package/dist/cjs/cli/migrate.js +131 -40
  14. package/dist/cjs/cli/observe-ui.d.ts +1 -0
  15. package/dist/cjs/cli/observe-ui.js +14 -5
  16. package/dist/cjs/cli/observe.d.ts +25 -0
  17. package/dist/cjs/cli/observe.js +49 -12
  18. package/dist/cjs/cli/pii-tags.d.ts +53 -0
  19. package/dist/cjs/cli/prisma-report.d.ts +33 -0
  20. package/dist/cjs/cli/prisma-report.js +73 -0
  21. package/dist/cjs/cli/prisma-resolve.d.ts +106 -0
  22. package/dist/cjs/cli/prisma-resolve.js +1 -0
  23. package/dist/cjs/cli/prisma-schema.d.ts +176 -0
  24. package/dist/cjs/cli/prisma-schema.js +82 -4
  25. package/dist/cjs/cli/rate-limit.d.ts +32 -0
  26. package/dist/cjs/cli/rate-limit.js +45 -0
  27. package/dist/cjs/cli/studio-demo.d.ts +43 -0
  28. package/dist/cjs/cli/studio-ui.generated.d.ts +1 -0
  29. package/dist/cjs/cli/studio.d.ts +207 -0
  30. package/dist/cjs/cli/studio.js +136 -71
  31. package/dist/cjs/cli/ui.d.ts +73 -0
  32. package/dist/cjs/cli/ui.js +51 -9
  33. package/dist/cjs/client.d.ts +837 -0
  34. package/dist/cjs/client.js +3 -0
  35. package/dist/cjs/dialect.d.ts +516 -0
  36. package/dist/cjs/dialect.js +37 -12
  37. package/dist/cjs/errors.d.ts +370 -0
  38. package/dist/cjs/generate.d.ts +137 -0
  39. package/dist/cjs/generate.js +39 -6
  40. package/dist/cjs/index-advisor.d.ts +153 -0
  41. package/dist/cjs/index-stats.d.ts +384 -0
  42. package/dist/cjs/index.d.ts +55 -0
  43. package/dist/cjs/index.js +7 -2
  44. package/dist/cjs/introspect.d.ts +269 -0
  45. package/dist/cjs/mssql.d.ts +232 -0
  46. package/dist/cjs/mssql.js +6 -0
  47. package/dist/cjs/mysql.d.ts +173 -0
  48. package/dist/cjs/mysql.js +16 -0
  49. package/dist/cjs/nested-write.d.ts +96 -0
  50. package/dist/cjs/nested-write.js +414 -24
  51. package/dist/cjs/observe.d.ts +115 -0
  52. package/dist/cjs/optional-peer-import.d.cts +72 -0
  53. package/dist/cjs/pipeline-submittable.d.ts +93 -0
  54. package/dist/cjs/pipeline.d.ts +71 -0
  55. package/dist/cjs/powdb-introspect.d.ts +84 -0
  56. package/dist/cjs/powdb.d.ts +931 -0
  57. package/dist/cjs/powdb.js +106 -21
  58. package/dist/cjs/powql.d.ts +592 -0
  59. package/dist/cjs/powql.js +42 -6
  60. package/dist/cjs/prisma-compat.d.ts +283 -0
  61. package/dist/cjs/prisma-compat.js +167 -9
  62. package/dist/cjs/query/aggregates.d.ts +92 -0
  63. package/dist/cjs/query/aggregates.js +7 -3
  64. package/dist/cjs/query/batched-loader.d.ts +193 -0
  65. package/dist/cjs/query/builder.d.ts +849 -0
  66. package/dist/cjs/query/builder.js +571 -65
  67. package/dist/cjs/query/compound-unique.d.ts +51 -0
  68. package/dist/cjs/query/deferred.d.ts +223 -0
  69. package/dist/cjs/query/filters.d.ts +201 -0
  70. package/dist/cjs/query/index.d.ts +14 -0
  71. package/dist/cjs/query/index.js +6 -1
  72. package/dist/cjs/query/relations.d.ts +609 -0
  73. package/dist/cjs/query/relations.js +693 -46
  74. package/dist/cjs/query/types.d.ts +1300 -0
  75. package/dist/cjs/query/utils.d.ts +209 -0
  76. package/dist/cjs/query/utils.js +208 -1
  77. package/dist/cjs/query/warn-registry.d.ts +68 -0
  78. package/dist/cjs/query/warn-registry.js +9 -0
  79. package/dist/cjs/query/where-compile.d.ts +139 -0
  80. package/dist/cjs/query/where.d.ts +548 -0
  81. package/dist/cjs/query/where.js +58 -22
  82. package/dist/cjs/query/writes.d.ts +172 -0
  83. package/dist/cjs/query/writes.js +105 -12
  84. package/dist/cjs/realtime.d.ts +70 -0
  85. package/dist/cjs/schema-builder.d.ts +354 -0
  86. package/dist/cjs/schema-metadata.d.ts +83 -0
  87. package/dist/cjs/schema-sql.d.ts +217 -0
  88. package/dist/cjs/schema-sql.js +23 -5
  89. package/dist/cjs/schema.d.ts +356 -0
  90. package/dist/cjs/schema.js +125 -0
  91. package/dist/cjs/seed.d.ts +15 -0
  92. package/dist/cjs/serverless.d.ts +142 -0
  93. package/dist/cjs/sqlite.d.ts +143 -0
  94. package/dist/cjs/sqlite.js +4 -0
  95. package/dist/cjs/typed-sql.d.ts +102 -0
  96. package/dist/cli/config.d.ts +18 -4
  97. package/dist/cli/config.js +31 -6
  98. package/dist/cli/index.d.ts +123 -0
  99. package/dist/cli/index.js +223 -58
  100. package/dist/cli/migrate.d.ts +59 -10
  101. package/dist/cli/migrate.js +128 -41
  102. package/dist/cli/observe-ui.d.ts +1 -1
  103. package/dist/cli/observe-ui.js +14 -5
  104. package/dist/cli/observe.d.ts +7 -1
  105. package/dist/cli/observe.js +48 -12
  106. package/dist/cli/prisma-report.d.ts +14 -0
  107. package/dist/cli/prisma-report.js +72 -0
  108. package/dist/cli/prisma-resolve.d.ts +6 -0
  109. package/dist/cli/prisma-resolve.js +1 -0
  110. package/dist/cli/prisma-schema.d.ts +62 -2
  111. package/dist/cli/prisma-schema.js +81 -4
  112. package/dist/cli/rate-limit.d.ts +32 -0
  113. package/dist/cli/rate-limit.js +40 -0
  114. package/dist/cli/studio.d.ts +5 -5
  115. package/dist/cli/studio.js +135 -70
  116. package/dist/cli/ui.d.ts +1 -1
  117. package/dist/cli/ui.js +51 -9
  118. package/dist/client.d.ts +40 -0
  119. package/dist/client.js +3 -0
  120. package/dist/dialect.d.ts +17 -1
  121. package/dist/dialect.js +37 -12
  122. package/dist/generate.js +40 -7
  123. package/dist/index.d.ts +1 -1
  124. package/dist/index.js +1 -1
  125. package/dist/mssql.js +6 -0
  126. package/dist/mysql.js +16 -0
  127. package/dist/nested-write.d.ts +2 -0
  128. package/dist/nested-write.js +415 -25
  129. package/dist/powdb.d.ts +4 -2
  130. package/dist/powdb.js +106 -21
  131. package/dist/powql.d.ts +5 -0
  132. package/dist/powql.js +42 -6
  133. package/dist/prisma-compat.d.ts +2 -0
  134. package/dist/prisma-compat.js +166 -8
  135. package/dist/query/aggregates.js +7 -3
  136. package/dist/query/builder.d.ts +292 -21
  137. package/dist/query/builder.js +570 -64
  138. package/dist/query/deferred.d.ts +39 -0
  139. package/dist/query/index.d.ts +1 -1
  140. package/dist/query/index.js +1 -1
  141. package/dist/query/relations.d.ts +173 -5
  142. package/dist/query/relations.js +688 -47
  143. package/dist/query/types.d.ts +123 -39
  144. package/dist/query/utils.d.ts +116 -0
  145. package/dist/query/utils.js +198 -0
  146. package/dist/query/warn-registry.d.ts +9 -0
  147. package/dist/query/warn-registry.js +9 -0
  148. package/dist/query/where.d.ts +38 -1
  149. package/dist/query/where.js +58 -23
  150. package/dist/query/writes.d.ts +42 -1
  151. package/dist/query/writes.js +104 -13
  152. package/dist/schema-sql.d.ts +14 -0
  153. package/dist/schema-sql.js +23 -5
  154. package/dist/schema.d.ts +38 -0
  155. package/dist/schema.js +123 -0
  156. package/dist/sqlite.js +4 -0
  157. package/package.json +77 -28
@@ -0,0 +1,181 @@
1
+ /**
2
+ * turbine-orm CLI — Configuration file support
3
+ *
4
+ * Loads turbine.config.ts (or .js/.mjs) via dynamic import.
5
+ * Falls back to CLI args and environment variables.
6
+ */
7
+ export interface TurbineCliConfig {
8
+ /** Postgres connection string */
9
+ url?: string;
10
+ /** Output directory for generated files (default: ./generated/turbine) */
11
+ out?: string;
12
+ /** Postgres schema to introspect (default: public) */
13
+ schema?: string;
14
+ /** Tables to include (empty = all) */
15
+ include?: string[];
16
+ /** Tables to exclude */
17
+ exclude?: string[];
18
+ /**
19
+ * Extension for the generated `index.ts` sibling imports (F3):
20
+ * `'js'` (`./types.js`), `'none'` (`./types`), or `'auto'` (default:
21
+ * tsconfig-detected, falling back to `'js'`).
22
+ */
23
+ importExtension?: 'js' | 'none' | 'auto';
24
+ /**
25
+ * Keep raw database column names as generated field names (snake_case)
26
+ * instead of camelCase (F4). Opt-in; default `false`.
27
+ */
28
+ keepColumnNames?: boolean;
29
+ /**
30
+ * Opt OUT of the unique-FK → one-to-one (`hasOne`) introspection flip (F2),
31
+ * emitting the pre-0.41 `hasMany` shape for unique-covered child relations.
32
+ * Default `false` (detection on).
33
+ */
34
+ legacyToManyUniques?: boolean;
35
+ /** Directory for migration files (default: ./turbine/migrations) */
36
+ migrationsDir?: string;
37
+ /**
38
+ * Path to the seed file (canonical key). With no value, discovery falls back
39
+ * to seed.ts, seed.js, seed.sql, then the same three under `turbine/`.
40
+ */
41
+ seedFile?: string;
42
+ /**
43
+ * Path to the seed file. Back-compat alias for {@link TurbineCliConfig.seedFile}:
44
+ * older `turbine init` runs scaffolded this spelling. `seedFile` wins when both
45
+ * are set.
46
+ */
47
+ seed?: string;
48
+ /** Schema builder file path (for push command) */
49
+ schemaFile?: string;
50
+ /**
51
+ * Database adapter for PostgreSQL-compatible databases that need
52
+ * dialect-specific behavior (e.g. CockroachDB, YugabyteDB).
53
+ *
54
+ * @example
55
+ * ```ts
56
+ * import { cockroachdb } from 'turbine-orm/adapters';
57
+ * export default { url: process.env.DATABASE_URL, adapter: cockroachdb };
58
+ * ```
59
+ */
60
+ adapter?: import('../adapters/index.js').DatabaseAdapter;
61
+ }
62
+ /**
63
+ * Alias for {@link TurbineCliConfig}. Some docs and examples import the config
64
+ * type as `TurbineConfig`; both names refer to the same shape.
65
+ */
66
+ export type TurbineConfig = TurbineCliConfig;
67
+ /**
68
+ * Heuristic: does a configured `schema` value actually look like a schema FILE
69
+ * path rather than a Postgres schema name? `schema` is the Postgres namespace
70
+ * to introspect (default `public`); the schema-builder file goes in `schemaFile`.
71
+ * A value containing a path separator or a JS/TS extension is almost certainly a
72
+ * mis-set `schemaFile` — introspecting `WHERE table_schema = './turbine/schema.ts'`
73
+ * silently matches zero tables. Used by `turbine generate` to fail loudly.
74
+ */
75
+ export declare function looksLikeSchemaFilePath(schema: string): boolean;
76
+ /**
77
+ * Where `turbine init` scaffolds the seed file, and the `seedFile` value it
78
+ * writes into the generated config. Kept next to the schema file so a new
79
+ * project's Turbine files live in one directory.
80
+ */
81
+ export declare const DEFAULT_INIT_SEED_FILE = "./turbine/seed.ts";
82
+ /** A config-file load attempt that failed, kept so the CLI can surface it. */
83
+ export interface ConfigLoadError {
84
+ /** The config file whose import threw (e.g. `turbine.config.ts`). */
85
+ filename: string;
86
+ /** The underlying error thrown by the dynamic import. */
87
+ error: unknown;
88
+ }
89
+ /** Result of {@link loadConfigResult}: the resolved config plus any load failure. */
90
+ export interface ConfigLoadResult {
91
+ config: TurbineCliConfig;
92
+ /**
93
+ * Set when a config file existed but failed to import. The config is still
94
+ * returned as `{}` so resolution falls through to env vars and CLI flags, but
95
+ * the CLI should surface this rather than let it masquerade as a missing URL.
96
+ */
97
+ loadError?: ConfigLoadError;
98
+ }
99
+ /**
100
+ * Unwrap the module object returned by `import(configFile)` down to the actual
101
+ * config value.
102
+ *
103
+ * With `"type": "commonjs"` in the consumer's package.json (the `npm init -y`
104
+ * default) plus the tsx loader, importing `turbine.config.ts` yields a
105
+ * CJS-interop DOUBLE-wrapped default: `mod.default` is itself `{ default: config }`.
106
+ * A naive `mod.default ?? mod` then reads every field as `undefined`, so every
107
+ * command fails with a misleading "No database URL provided".
108
+ *
109
+ * This prefers `default` when present (the historical behavior) and then keeps
110
+ * descending through any additional pure `{ default: … }` wrappers, so both the
111
+ * correct single-default shape and the double-wrapped shape resolve to the same
112
+ * config. A genuine config object (which has real fields, never a lone
113
+ * `default`) is returned untouched.
114
+ */
115
+ export declare function unwrapModuleDefault(mod: unknown): unknown;
116
+ /**
117
+ * {@link unwrapModuleDefault} specialized for config files: a non-object export
118
+ * collapses to `{}` so downstream resolution falls through to env vars/flags.
119
+ */
120
+ export declare function unwrapConfigModule(mod: unknown): TurbineCliConfig;
121
+ /**
122
+ * Attempt to load a turbine config file from the given directory, returning the
123
+ * resolved config together with any load failure so the caller can surface it.
124
+ *
125
+ * Candidates are tried in {@link CONFIG_FILES} priority order. The first one
126
+ * that imports successfully wins. If a candidate exists but throws (syntax
127
+ * error, ESM/CJS interop failure, etc.) we remember the first such error and
128
+ * keep trying lower-priority candidates; if none load, the remembered error is
129
+ * returned in `loadError` while `config` stays `{}` so env/flag resolution can
130
+ * still proceed.
131
+ */
132
+ export declare function loadConfigResult(cwd?: string): Promise<ConfigLoadResult>;
133
+ /**
134
+ * Attempt to load a turbine config file from the current directory.
135
+ * Returns the config if found, or an empty object. Load failures are swallowed
136
+ * here; callers that need to surface them should use {@link loadConfigResult}.
137
+ */
138
+ export declare function loadConfig(cwd?: string): Promise<TurbineCliConfig>;
139
+ /**
140
+ * Find the config file path (for display purposes).
141
+ * Returns null if no config file is found.
142
+ */
143
+ export declare function findConfigFile(cwd?: string): string | null;
144
+ export interface ResolvedConfig {
145
+ url: string;
146
+ out: string;
147
+ schema: string;
148
+ include: string[];
149
+ exclude: string[];
150
+ migrationsDir: string;
151
+ seedFile?: string;
152
+ schemaFile: string;
153
+ /** Resolved generator import-extension mode (F3). */
154
+ importExtension: 'js' | 'none' | 'auto';
155
+ /** Resolved keep-column-names generator flag (F4). */
156
+ keepColumnNames: boolean;
157
+ /** Resolved opt-out of the unique-FK → hasOne introspection flip (F2). */
158
+ legacyToManyUniques: boolean;
159
+ }
160
+ export interface CliOverrides {
161
+ url?: string;
162
+ out?: string;
163
+ schema?: string;
164
+ include?: string[];
165
+ exclude?: string[];
166
+ importExtension?: 'js' | 'none' | 'auto';
167
+ keepColumnNames?: boolean;
168
+ legacyToManyUniques?: boolean;
169
+ }
170
+ /**
171
+ * Merge config file values with CLI overrides and env vars.
172
+ * Priority: CLI flags > env vars > config file > defaults.
173
+ */
174
+ export declare function resolveConfig(fileConfig: TurbineCliConfig, overrides: CliOverrides): ResolvedConfig;
175
+ /**
176
+ * Resolve the seed file path. An explicit config value wins even if the file
177
+ * does not exist yet; otherwise {@link DEFAULT_SEED_CANDIDATES} is tried in
178
+ * order (root-level first, then the `turbine/` location `init` scaffolds).
179
+ */
180
+ export declare function resolveSeedFile(config: Pick<TurbineCliConfig, 'seed' | 'seedFile'>, cwd?: string): string | null;
181
+ export declare function configTemplate(connectionString?: string): string;
@@ -39,6 +39,7 @@ var __importStar = (this && this.__importStar) || (function () {
39
39
  };
40
40
  })();
41
41
  Object.defineProperty(exports, "__esModule", { value: true });
42
+ exports.DEFAULT_INIT_SEED_FILE = void 0;
42
43
  exports.looksLikeSchemaFilePath = looksLikeSchemaFilePath;
43
44
  exports.unwrapModuleDefault = unwrapModuleDefault;
44
45
  exports.unwrapConfigModule = unwrapConfigModule;
@@ -68,7 +69,28 @@ function looksLikeSchemaFilePath(schema) {
68
69
  // Config file names, in priority order
69
70
  // ---------------------------------------------------------------------------
70
71
  const CONFIG_FILES = ['turbine.config.ts', 'turbine.config.mts', 'turbine.config.js', 'turbine.config.mjs'];
71
- const DEFAULT_SEED_CANDIDATES = ['seed.ts', 'seed.js', 'seed.sql'];
72
+ /**
73
+ * Config-less fallback locations for the seed file, in priority order.
74
+ *
75
+ * Root-level candidates stay FIRST so no project that already relies on
76
+ * `./seed.ts` changes behavior. The `turbine/` candidates are appended so a
77
+ * project that drops the `seedFile` key can still auto-discover the file
78
+ * `turbine init` scaffolds (see {@link DEFAULT_INIT_SEED_FILE}).
79
+ */
80
+ const DEFAULT_SEED_CANDIDATES = [
81
+ 'seed.ts',
82
+ 'seed.js',
83
+ 'seed.sql',
84
+ 'turbine/seed.ts',
85
+ 'turbine/seed.js',
86
+ 'turbine/seed.sql',
87
+ ];
88
+ /**
89
+ * Where `turbine init` scaffolds the seed file, and the `seedFile` value it
90
+ * writes into the generated config. Kept next to the schema file so a new
91
+ * project's Turbine files live in one directory.
92
+ */
93
+ exports.DEFAULT_INIT_SEED_FILE = './turbine/seed.ts';
72
94
  function isPlainObject(value) {
73
95
  return typeof value === 'object' && value !== null && !Array.isArray(value);
74
96
  }
@@ -183,7 +205,9 @@ function resolveConfig(fileConfig, overrides) {
183
205
  include: overrides.include ?? fileConfig.include ?? [],
184
206
  exclude: overrides.exclude ?? fileConfig.exclude ?? [],
185
207
  migrationsDir: fileConfig.migrationsDir ?? './turbine/migrations',
186
- seedFile: fileConfig.seed ?? fileConfig.seedFile,
208
+ // `seedFile` is canonical (what the docs and `turbine init` use); `seed` is a
209
+ // back-compat alias kept working for configs scaffolded before 0.50.
210
+ seedFile: fileConfig.seedFile ?? fileConfig.seed,
187
211
  schemaFile: fileConfig.schemaFile ?? './turbine/schema.ts',
188
212
  importExtension: overrides.importExtension ?? fileConfig.importExtension ?? 'auto',
189
213
  keepColumnNames: overrides.keepColumnNames ?? fileConfig.keepColumnNames ?? false,
@@ -192,10 +216,12 @@ function resolveConfig(fileConfig, overrides) {
192
216
  }
193
217
  /**
194
218
  * Resolve the seed file path. An explicit config value wins even if the file
195
- * does not exist yet; otherwise the root-level defaults are tried in order.
219
+ * does not exist yet; otherwise {@link DEFAULT_SEED_CANDIDATES} is tried in
220
+ * order (root-level first, then the `turbine/` location `init` scaffolds).
196
221
  */
197
222
  function resolveSeedFile(config, cwd = process.cwd()) {
198
- const explicit = config.seed ?? config.seedFile;
223
+ // Canonical `seedFile` first, then the back-compat `seed` alias (see resolveConfig).
224
+ const explicit = config.seedFile ?? config.seed;
199
225
  if (explicit)
200
226
  return (0, node_path_1.resolve)(cwd, explicit);
201
227
  for (const candidate of DEFAULT_SEED_CANDIDATES) {
@@ -233,8 +259,8 @@ ${urlLine}
233
259
  /** Directory for SQL migration files */
234
260
  migrationsDir: './turbine/migrations',
235
261
 
236
- /** Path to seed file (defaults: ./seed.ts, ./seed.js, ./seed.sql) */
237
- seed: './seed.ts',
262
+ /** Path to seed file (defaults: ./seed.ts, ./seed.js, ./seed.sql, ./turbine/seed.ts) */
263
+ seedFile: '${exports.DEFAULT_INIT_SEED_FILE}',
238
264
 
239
265
  /** Path to schema builder file (for turbine push) */
240
266
  schemaFile: './turbine/schema.ts',
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Destructive-migration detection.
3
+ *
4
+ * `migrate up`/`down` execute user-authored SQL files verbatim, which is the
5
+ * one place data loss can hide: a `DROP TABLE` or a `DELETE FROM` in a
6
+ * migration runs with no ceremony. This module scans migration SQL for
7
+ * statements that can destroy data so the CLI can force an explicit,
8
+ * interactive confirmation (and the programmatic API can refuse by default).
9
+ *
10
+ * Deliberately conservative in BOTH directions:
11
+ * - comments and string literals are stripped first, so `-- DROP TABLE foo`
12
+ * or `INSERT ... VALUES ('DROP TABLE x')` never false-positive;
13
+ * - anything that removes rows, columns, tables, or schemas, or rewrites a
14
+ * column's type (a potentially lossy cast), is flagged. `DROP INDEX`,
15
+ * `DROP CONSTRAINT`, and `DROP TRIGGER` are NOT flagged (recreatable
16
+ * structures; no row data lost).
17
+ *
18
+ * Row removal hides in more than a leading `DELETE`, so the scan also covers:
19
+ * the optional-`COLUMN` shorthand (`ALTER TABLE t DROP email`), data-modifying
20
+ * CTEs (`WITH d AS (DELETE ...) SELECT ...`), `MERGE ... THEN DELETE`, dynamic
21
+ * SQL inside a `DO`/function body, and an `UPDATE` whose only WHERE sits inside
22
+ * a subquery (which restricts nothing).
23
+ */
24
+ export type DestructiveKind = 'drop-table' | 'drop-schema' | 'drop-database' | 'drop-owned' | 'drop-matview' | 'drop-column' | 'truncate' | 'delete' | 'update-without-where' | 'alter-column-type' | 'merge-delete';
25
+ export interface DestructiveStatement {
26
+ /** The offending SQL statement (trimmed, possibly long — display truncated) */
27
+ statement: string;
28
+ kind: DestructiveKind;
29
+ /** Best-effort extracted object name (table, schema, or table.column) */
30
+ target: string;
31
+ }
32
+ /** Human explanation per kind, used in CLI output. */
33
+ export declare const DESTRUCTIVE_KIND_LABEL: Record<DestructiveKind, string>;
34
+ /**
35
+ * Scan SQL (one file's worth; may contain many `;`-separated statements) and
36
+ * return every statement that can destroy data.
37
+ */
38
+ export declare function scanDestructiveSql(sql: string): DestructiveStatement[];
@@ -0,0 +1,359 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * turbine-orm CLI
4
+ *
5
+ * Commands:
6
+ * turbine init — Initialize a Turbine project
7
+ * turbine generate | pull — Introspect database and generate TypeScript types
8
+ * turbine migrate-from-prisma - Parse a schema.prisma and emit a Prisma->Turbine name map + report
9
+ * turbine push - Apply schema-builder definitions to database (destructive ops gated)
10
+ * turbine migrate create <name> - Create a new SQL migration file (--auto | --from-diff | --recipe <name>)
11
+ * turbine migrate up — Apply pending migrations
12
+ * turbine migrate deploy — Apply pending migrations without prompts
13
+ * turbine migrate down — Rollback last migration
14
+ * turbine migrate status — Show migration status
15
+ * turbine seed — Run seed file
16
+ * turbine status — Show schema summary
17
+ * turbine doctor - Cost-aware missing-FK-index triage (--fix, --json, --no-concurrently, --unused, --audit)
18
+ * turbine studio : Launch local read-only web UI (--demo for a seeded sample DB)
19
+ * turbine mcp — Start read-only MCP server over JSON-RPC stdio
20
+ * turbine observe — Launch metrics dashboard (requires TURBINE_OBSERVE_URL)
21
+ *
22
+ * Usage:
23
+ * DATABASE_URL=postgres://... npx turbine generate
24
+ * npx turbine init --url postgres://...
25
+ * npx turbine migrate create add_users_table
26
+ */
27
+ import type { CliOverrides, ConfigLoadError, ResolvedConfig, TurbineCliConfig } from './config.js';
28
+ import { type PrismaSchemaAst, type ResolvedPrismaDatasourceUrl } from './prisma-schema.js';
29
+ export interface CliArgs {
30
+ command: string;
31
+ subcommand?: string;
32
+ positional: string[];
33
+ url?: string;
34
+ out?: string;
35
+ schema?: string;
36
+ include?: string[];
37
+ exclude?: string[];
38
+ step?: number;
39
+ dryRun?: boolean;
40
+ force?: boolean;
41
+ verbose?: boolean;
42
+ help?: boolean;
43
+ auto?: boolean;
44
+ /** `migrate create --from-diff`: scaffold UP/DOWN from the schema diff, destructive statements flagged. */
45
+ fromDiff?: boolean;
46
+ allowDrift?: boolean;
47
+ allowEmpty?: boolean;
48
+ allowDestructive?: boolean;
49
+ /** `migrate create --recipe <name>` scaffold selector. */
50
+ recipe?: string;
51
+ fix?: boolean;
52
+ /** `doctor --json`: emit a stable, versioned machine-readable report. */
53
+ json?: boolean;
54
+ /** `doctor --fix --no-concurrently`: emit plain CREATE INDEX instead of the CONCURRENTLY + no-transaction form. */
55
+ noConcurrently?: boolean;
56
+ /** `doctor --unused`: report-only never-scanned / redundant / invalid indexes with DROP suggestions. */
57
+ unused?: boolean;
58
+ /** `doctor --audit`: unused report scoped to doctor's own previously-suggested index names. */
59
+ audit?: boolean;
60
+ /** `doctor --min-scans <n>`: idx_scan below this counts as never-scanned (default 1 = idx_scan 0). */
61
+ minScans?: number;
62
+ /** `doctor --metrics-url <url>`: read _turbine_metrics for the table-heat boost from a separate DB. */
63
+ metricsUrl?: string;
64
+ /** `init --yes`/`-y`: accept every step's default non-interactively. */
65
+ yes?: boolean;
66
+ /** `init --skip-schema`: don't scaffold the schema file. */
67
+ skipSchema?: boolean;
68
+ /** `init --skip-seed`: don't scaffold the seed file or offer to run it. */
69
+ skipSeed?: boolean;
70
+ /** `init --skip-push`: don't offer to push the schema to the database. */
71
+ skipPush?: boolean;
72
+ /** `init --skip-generate`: don't offer to generate the typed client. */
73
+ skipGenerate?: boolean;
74
+ zod?: boolean;
75
+ includeViews?: boolean;
76
+ /** Omit the `Generated at:` header line for reproducible (diff-stable) output. */
77
+ noTimestamp?: boolean;
78
+ /** `generate --import-ext <js|none|auto>`: sibling-import extension mode (F3). */
79
+ importExtension?: 'js' | 'none' | 'auto';
80
+ /** `generate --keep-column-names`: keep raw DB column names as field names (F4). */
81
+ keepColumnNames?: boolean;
82
+ /** `generate --legacy-to-many-uniques`: opt out of the unique-FK → hasOne flip (F2). */
83
+ legacyToManyUniques?: boolean;
84
+ port?: number;
85
+ host?: string;
86
+ noOpen?: boolean;
87
+ /** Opt-in to bind Studio/Observe on a non-loopback host. */
88
+ allowRemote?: boolean;
89
+ /** Opt-in to Studio single-row write mode (`studio --write`). */
90
+ write?: boolean;
91
+ /** Reveal PII-tagged column values in Studio instead of redacting (`--show-pii`). */
92
+ showPii?: boolean;
93
+ /** Launch Studio with a seeded in-memory sample database (`studio --demo`). */
94
+ demo?: boolean;
95
+ /** `migrate-from-prisma --allow-partial`: exit 0 even when some items are UNRESOLVED. */
96
+ allowPartial?: boolean;
97
+ /** `migrate-from-prisma --no-db`: parse-only, skip database resolution. */
98
+ noDb?: boolean;
99
+ }
100
+ export declare function parseArgs(argv?: string[]): CliArgs;
101
+ /**
102
+ * Does this invocation need a `turbine.config.*` file?
103
+ *
104
+ * Everything does, with one deliberate exception: `turbine studio --demo` boots
105
+ * a seeded in-memory database, needs no `DATABASE_URL` and no config file, and
106
+ * is the very next command the quickstart suggests after `turbine init`.
107
+ * Resolving the config anyway means a freshly scaffolded directory (a
108
+ * `turbine.config.ts` with `tsx` not installed yet) dies with "Cannot load
109
+ * TypeScript file" before demo mode ever starts.
110
+ *
111
+ * @internal exported for tests.
112
+ */
113
+ export declare function usesProjectConfig(args: Pick<CliArgs, 'command' | 'demo'>): boolean;
114
+ /**
115
+ * Outcome of {@link bootstrapCliConfig}.
116
+ *
117
+ * @internal exported for tests.
118
+ */
119
+ export interface CliConfigBootstrap {
120
+ /** Merged config: CLI flags > env vars > config file > defaults. */
121
+ config: ResolvedConfig;
122
+ /** The raw config-file contents (`{}` when none was loaded). */
123
+ fileConfig: TurbineCliConfig;
124
+ /** Set when a config file existed but failed to import. */
125
+ loadError?: ConfigLoadError;
126
+ /** True when config resolution was deliberately skipped (see {@link usesProjectConfig}). */
127
+ skipped: boolean;
128
+ }
129
+ /**
130
+ * Resolve the effective CLI config: register the tsx loader when the config file
131
+ * is TypeScript, import it, then merge it with env vars and CLI flags. Exits with
132
+ * the actionable "Cannot load TypeScript file" error when a `.ts` config cannot
133
+ * be loaded. Config-free invocations short-circuit without touching the disk.
134
+ *
135
+ * @internal exported for tests.
136
+ */
137
+ export declare function bootstrapCliConfig(args: Pick<CliArgs, 'command' | 'demo'>, overrides: CliOverrides): Promise<CliConfigBootstrap>;
138
+ /** Where a resolved `DATABASE_URL` came from, after the `.env` load. */
139
+ export type DotEnvProvenance = 'shell' | 'dotenv' | 'none';
140
+ /** Structured outcome of {@link loadDotEnvForCli}. */
141
+ export interface DotEnvLoadResult {
142
+ /** A `.env` file was present in the working directory. */
143
+ fileExists: boolean;
144
+ /** The `.env` was actually read into the environment. */
145
+ loaded: boolean;
146
+ /** A `.env` exists but this runtime cannot auto-load it (Node < 20.12). */
147
+ unsupported: boolean;
148
+ /** Where `DATABASE_URL` ended up coming from once the load settled. */
149
+ databaseUrlProvenance: DotEnvProvenance;
150
+ /** Set when the loader threw (e.g. EACCES / a directory named `.env`). */
151
+ loadError?: string;
152
+ }
153
+ /**
154
+ * Load a local `.env` into `process.env` for the CLI, mirroring what
155
+ * `node --env-file=.env` does. Loaded UNCONDITIONALLY when a `.env` is present,
156
+ * so every variable it defines (not just `DATABASE_URL`) reaches the config
157
+ * file and user scripts.
158
+ *
159
+ * A pre-existing variable ALWAYS wins: `process.loadEnvFile()` never overrides
160
+ * an already-set variable, so a real shell/CI `DATABASE_URL` beats the file.
161
+ * Provenance is tracked so callers can warn when an `.env`-sourced
162
+ * `DATABASE_URL` silently overrides a differing `url` in `turbine.config.ts`:
163
+ * `DATABASE_URL` is `'dotenv'`-sourced only when it was absent before the load
164
+ * and present after.
165
+ *
166
+ * `process.loadEnvFile` is Node 20.12+. Turbine's engines allow `>=20.0.0`, so
167
+ * on older runtimes this no-ops with `unsupported: true` (never throws). A
168
+ * loader that throws (unreadable file, a directory named `.env`) is caught and
169
+ * surfaced as `loadError`, never a raw unhandled rejection. Deliberately
170
+ * CLI-only: the library must never read files.
171
+ *
172
+ * Dependencies are injectable purely so this is unit-testable without mutating
173
+ * the real process environment.
174
+ */
175
+ export declare function loadDotEnvForCli(deps?: {
176
+ env?: NodeJS.ProcessEnv;
177
+ cwd?: string;
178
+ fileExists?: (path: string) => boolean;
179
+ loadEnvFile?: ((path: string) => void) | null;
180
+ }): DotEnvLoadResult;
181
+ /**
182
+ * Decide whether to warn that an `.env`-sourced `DATABASE_URL` is overriding a
183
+ * differing, non-empty `url` in the config file. Pure so it is unit-testable.
184
+ *
185
+ * Precedence is unchanged (`.env` `DATABASE_URL` still wins), this only decides
186
+ * whether that override is silent or loud. We warn ONLY when all hold:
187
+ * - no CLI `--url` override (an explicit override is the user's clear intent),
188
+ * - `DATABASE_URL` came from `.env` (shell-exported stays silent, as before),
189
+ * - the config file has a non-empty `url`, and
190
+ * - the two URLs actually differ.
191
+ *
192
+ * Returns the warning message (URLs redacted), or `null` for no warning.
193
+ */
194
+ export declare function dotEnvUrlConflictWarning(input: {
195
+ provenance: DotEnvProvenance;
196
+ envUrl: string | undefined;
197
+ fileConfigUrl: string | undefined;
198
+ overrideUrl: string | undefined;
199
+ }): string | null;
200
+ /** Package managers we can name an exact install command for. */
201
+ export type PackageManager = 'npm' | 'pnpm' | 'yarn' | 'bun';
202
+ /**
203
+ * Detect the consumer's package manager from its lockfile, defaulting to npm.
204
+ * Used only to print an exact, copy-pasteable install command.
205
+ *
206
+ * @internal exported for tests.
207
+ */
208
+ export declare function detectPackageManager(cwd?: string): PackageManager;
209
+ /**
210
+ * The exact "add tsx as a dev dependency" command for a package manager.
211
+ *
212
+ * @internal exported for tests.
213
+ */
214
+ export declare function tsxInstallCommand(pm: PackageManager): string;
215
+ /**
216
+ * The heads-up `turbine init` prints when it has just scaffolded TypeScript
217
+ * files and `tsx` is not resolvable: without it the CLI cannot load them, and
218
+ * the very next command the user runs dies on "Cannot load TypeScript file".
219
+ * Pure (returns the lines, prints nothing) so it can be asserted in tests.
220
+ *
221
+ * @internal exported for tests.
222
+ */
223
+ export declare function tsxRequiredNotice(tsFiles: string[], installCommand: string): string[];
224
+ /**
225
+ * Read the consumer's `package.json` `"type"` field. Returns `'module'` for an
226
+ * ESM project, `'commonjs'` for an explicit or absent (defaulted) CommonJS
227
+ * project, and `'none'` when there is no readable/parseable package.json.
228
+ */
229
+ export declare function detectConsumerModuleType(cwd?: string): 'module' | 'commonjs' | 'none';
230
+ /** A single step in the `turbine init` flow. */
231
+ export type InitStepId = 'config' | 'schema' | 'seed-file' | 'push' | 'generate' | 'seed-run';
232
+ /** What the planner decided to do with a step. */
233
+ export type InitStepAction = 'run' | 'prompt' | 'skip';
234
+ /** Why a step was skipped (only set when `action` is `skip`). */
235
+ export type InitStepSkipReason = 'exists' | 'flag' | 'no-url' | 'unreachable' | 'no-seed-file' | 'non-interactive' | 'default-no';
236
+ export interface InitPlanStep {
237
+ id: InitStepId;
238
+ action: InitStepAction;
239
+ /** Prompt default; also the value used to decide auto-run under `--yes`. */
240
+ defaultYes: boolean;
241
+ skipReason?: InitStepSkipReason;
242
+ }
243
+ /** Detected project state (all IO done by the caller). */
244
+ export interface InitPlanState {
245
+ configExists: boolean;
246
+ schemaExists: boolean;
247
+ seedFileExists: boolean;
248
+ hasUrl: boolean;
249
+ dbReachable: boolean;
250
+ }
251
+ /** Effective flags for the planner. */
252
+ export interface InitPlanFlags {
253
+ yes: boolean;
254
+ force: boolean;
255
+ interactive: boolean;
256
+ skipSchema: boolean;
257
+ skipSeed: boolean;
258
+ skipPush: boolean;
259
+ skipGenerate: boolean;
260
+ }
261
+ /**
262
+ * Pure step planner for `turbine init`. Given the detected project state and the
263
+ * effective flags, decide for each step whether to run it, prompt for it, or
264
+ * skip it (and why). No IO: every input is precomputed by the caller: so the
265
+ * whole decision matrix is unit-testable without a TTY or a database.
266
+ *
267
+ * Three modes:
268
+ * - `prompt` (interactive TTY, no `--yes`): scaffold + DB steps are prompted.
269
+ * - `auto-yes` (`--yes`): accept each step's default; the yes-defaults run.
270
+ * - `auto-legacy` (non-TTY, no `--yes`): reproduce the pre-existing init
271
+ * behavior. Scaffold files + generate run; push + seed-run do not.
272
+ *
273
+ * Steps that create files (config, schema, seed) are skipped when the file
274
+ * already exists, so re-runs are safe. DB steps (push, generate, seed-run) are
275
+ * skipped when there is no URL or the database is unreachable.
276
+ */
277
+ export declare function planInitSteps(state: InitPlanState, flags: InitPlanFlags): InitPlanStep[];
278
+ /**
279
+ * The one-line connection heads-up `turbine init` opens with.
280
+ *
281
+ * @internal exported for tests.
282
+ */
283
+ export interface InitEnvNotice {
284
+ kind: 'success' | 'info';
285
+ message: string;
286
+ }
287
+ /**
288
+ * Decide which connection notice `turbine init` prints. Pure so the whole
289
+ * decision matrix is testable.
290
+ *
291
+ * "No DATABASE_URL found in environment" is reserved for the case where NO
292
+ * source supplied one: printing it while happily using `--url` (or a config
293
+ * `url`) reads like a failure the user then goes looking for.
294
+ *
295
+ * @internal exported for tests.
296
+ */
297
+ export declare function initEnvNotice(input: {
298
+ envUrl: string | undefined;
299
+ hasEnvFile: boolean;
300
+ hasEnvLocal: boolean;
301
+ canAutoLoadEnv: boolean;
302
+ flagUrl: string | undefined;
303
+ configUrl: string | undefined;
304
+ }): InitEnvNotice;
305
+ /**
306
+ * `turbine migrate-from-prisma --schema prisma/schema.prisma` parses a Prisma
307
+ * schema, resolve its models/fields/relations/compound-uniques against the live
308
+ * database (unless `--no-db`), and emit (a) a Markdown resolution report and
309
+ * (b) a typed `prisma-map.ts` name map next to the generated client.
310
+ *
311
+ * NOTE: within THIS command `--schema` names the Prisma schema FILE (not the
312
+ * Postgres namespace, which the rest of the CLI's `--schema` means). The
313
+ * Postgres namespace is `public` here; multi-schema (`@@schema`) is unsupported
314
+ * in v1 and listed as a parser note in the report.
315
+ */
316
+ /** Outcome of {@link resolveMigrateFromPrismaUrl}. */
317
+ export interface MigrateFromPrismaUrl {
318
+ /** The connection string to use, or undefined when none could be found. */
319
+ url?: string;
320
+ /** Where it came from: the normal CLI resolution, or the Prisma datasource. */
321
+ source: 'config' | 'datasource' | 'none';
322
+ /** Datasource detail, set only when `source` is `'datasource'`. */
323
+ datasource?: ResolvedPrismaDatasourceUrl;
324
+ /** Datasource `env(...)` variable names that were declared but unset. */
325
+ missingVariables: string[];
326
+ }
327
+ /**
328
+ * Pick the connection string for `migrate-from-prisma`.
329
+ *
330
+ * `configUrl` is what {@link resolveConfig} already produced (`--url`, then
331
+ * `DATABASE_URL`, then `turbine.config.ts`) and always wins: an explicit flag
332
+ * must never be overridden by a value declared in someone else's schema file.
333
+ * Only when that is empty do we fall back to the `datasource` block, which
334
+ * removes the flag a project with a non-standard variable name would otherwise
335
+ * pass on every run.
336
+ */
337
+ export declare function resolveMigrateFromPrismaUrl(configUrl: string | undefined, ast: Pick<PrismaSchemaAst, 'datasources'>, env: Record<string, string | undefined>): MigrateFromPrismaUrl;
338
+ export declare function buildMigrateDeployOptions(args: CliArgs): {
339
+ allowDrift: boolean;
340
+ allowDestructive: true;
341
+ step: undefined;
342
+ };
343
+ export type SeedExecutionPlan = {
344
+ kind: 'tsx';
345
+ command: 'npx';
346
+ args: string[];
347
+ } | {
348
+ kind: 'js';
349
+ file: string;
350
+ } | {
351
+ kind: 'sql';
352
+ file: string;
353
+ };
354
+ export declare function getSeedExecutionPlan(seedFile: string): SeedExecutionPlan;
355
+ /**
356
+ * True when `host` is a loopback address Studio/Observe may bind without
357
+ * `--allow-remote`. Accepts IPv4, IPv6, and the common bracket form.
358
+ */
359
+ export declare function isLoopbackHost(host: string): boolean;