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,142 @@
1
+ /**
2
+ * turbine-orm/serverless — edge / serverless driver integration
3
+ *
4
+ * Turbine runs on any Postgres driver that speaks the node-postgres API.
5
+ * This module exposes a thin factory (`turbineHttp`) that binds an external
6
+ * pg-compatible pool to a schema, so you can use Turbine on Vercel Edge,
7
+ * Cloudflare Workers, Deno Deploy, Netlify Edge, or any other environment
8
+ * where a direct TCP connection is unavailable.
9
+ *
10
+ * ## Supported drivers
11
+ *
12
+ * Any driver whose `Pool` satisfies `PgCompatPool` will work. The ones
13
+ * below are verified:
14
+ *
15
+ * - **Neon** (`@neondatabase/serverless`) — HTTP and WebSocket transports
16
+ * - **Vercel Postgres** (`@vercel/postgres`) — wraps Neon
17
+ * - **Cloudflare Hyperdrive** — exposes a pg-compatible driver
18
+ * - **Supabase** — use the regular `pg` package; Supabase is Postgres-native
19
+ *
20
+ * Turbine does NOT bundle any of these — install whichever you need and
21
+ * pass its pool directly.
22
+ *
23
+ * ## Limitations over HTTP
24
+ *
25
+ * - **Streaming cursors** (`findManyStream`, `findManyCursor`) require
26
+ * server-side `DECLARE CURSOR`, which most HTTP drivers do not support.
27
+ * If you call these on an HTTP pool the underlying driver will error.
28
+ * - **LISTEN/NOTIFY** is not available over HTTP.
29
+ * - **Transactions** are supported but each transaction holds an HTTP
30
+ * connection for its duration — keep them short.
31
+ *
32
+ * ## Example — Neon on Vercel Edge
33
+ *
34
+ * ```ts
35
+ * // app/api/users/route.ts
36
+ * import { Pool } from '@neondatabase/serverless';
37
+ * import { turbineHttp } from 'turbine-orm/serverless';
38
+ * import { SCHEMA } from '../../generated/turbine/metadata';
39
+ *
40
+ * export const runtime = 'edge';
41
+ *
42
+ * const pool = new Pool({ connectionString: process.env.DATABASE_URL });
43
+ * const db = turbineHttp(pool, SCHEMA);
44
+ *
45
+ * export async function GET() {
46
+ * const users = await db.table('users').findMany({ limit: 10 });
47
+ * return Response.json(users);
48
+ * }
49
+ * ```
50
+ *
51
+ * ## Example — Supabase (direct Postgres, no HTTP proxy needed)
52
+ *
53
+ * ```ts
54
+ * import { TurbineClient } from 'turbine-orm';
55
+ * import { SCHEMA } from './generated/turbine/metadata.js';
56
+ *
57
+ * const db = new TurbineClient({
58
+ * connectionString: process.env.SUPABASE_DB_URL,
59
+ * ssl: { rejectUnauthorized: false },
60
+ * }, SCHEMA);
61
+ * ```
62
+ *
63
+ * ## Example — Cloudflare Workers
64
+ *
65
+ * ```ts
66
+ * // Use the Neon HTTP driver which works in Workers runtime
67
+ * import { Pool } from '@neondatabase/serverless';
68
+ * import { turbineHttp } from 'turbine-orm/serverless';
69
+ * import { SCHEMA } from './generated/turbine/metadata';
70
+ *
71
+ * export default {
72
+ * async fetch(req: Request, env: Env) {
73
+ * const pool = new Pool({ connectionString: env.DATABASE_URL });
74
+ * const db = turbineHttp(pool, SCHEMA);
75
+ * const users = await db.table('users').findMany({ limit: 10 });
76
+ * return Response.json(users);
77
+ * }
78
+ * };
79
+ * ```
80
+ */
81
+ import { type PgCompatPool, TurbineClient, type TurbineConfig } from './client.js';
82
+ import type { SchemaMetadata } from './schema.js';
83
+ export type { PgCompatPool, PgCompatPoolClient, PgCompatQueryResult } from './client.js';
84
+ /**
85
+ * Options for `turbineHttp()`. Mirrors the fields of `TurbineConfig`
86
+ * that are relevant for externally-managed pools.
87
+ */
88
+ export interface TurbineHttpOptions extends Pick<TurbineConfig, 'logging' | 'defaultLimit' | 'warnOnUnlimited'> {
89
+ }
90
+ /**
91
+ * Create a TurbineClient bound to an external pg-compatible pool.
92
+ *
93
+ * Use this for serverless/edge environments where Turbine should NOT
94
+ * manage its own `pg.Pool`. The caller retains ownership of the pool's
95
+ * lifecycle — `db.disconnect()` is a no-op.
96
+ *
97
+ * ## Typed table accessors
98
+ *
99
+ * By default `turbineHttp` returns the base {@link TurbineClient}, so you
100
+ * reach tables through `db.table('users')`. To get the *generated*, fully
101
+ * typed accessors (`db.users.findMany()`) — identical to what the TCP-path
102
+ * `turbine()` factory gives you — pass your generated client type as the
103
+ * `TClient` type argument. The runtime object is the same; the generated
104
+ * subclass only adds `declare readonly` accessor typings, and the base
105
+ * constructor already creates those accessors at runtime for every table in
106
+ * the schema, so the assertion is sound (not a lie about the shape).
107
+ *
108
+ * This closes the "identical typed code across transports" gap: the edge
109
+ * client is now as typed as the direct one, with no `as` casts at the call
110
+ * site.
111
+ *
112
+ * @typeParam TClient - The generated `TurbineClient` subclass (from
113
+ * `./generated/turbine`). Defaults to the base client for back-compat.
114
+ * @param pool - Any pg-compatible pool (Neon, Vercel Postgres, etc.)
115
+ * @param schema - Introspected or hand-written schema metadata
116
+ * @param options - Optional logging / defaultLimit / warnOnUnlimited
117
+ * @returns A TurbineClient instance (typed as `TClient`)
118
+ *
119
+ * @example Untyped (back-compat) — reach tables via `db.table(...)`
120
+ * ```ts
121
+ * import { Pool } from '@neondatabase/serverless';
122
+ * import { turbineHttp } from 'turbine-orm/serverless';
123
+ * import { SCHEMA } from './generated/turbine/metadata.js';
124
+ *
125
+ * const pool = new Pool({ connectionString: process.env.DATABASE_URL });
126
+ * const db = turbineHttp(pool, SCHEMA);
127
+ * const users = await db.table('users').findMany({ limit: 10 });
128
+ * ```
129
+ *
130
+ * @example Typed — generated accessors, identical to the TCP client
131
+ * ```ts
132
+ * import { Pool } from '@neondatabase/serverless';
133
+ * import { turbineHttp } from 'turbine-orm/serverless';
134
+ * import type { TurbineClient } from './generated/turbine';
135
+ * import { SCHEMA } from './generated/turbine/metadata.js';
136
+ *
137
+ * const pool = new Pool({ connectionString: process.env.DATABASE_URL });
138
+ * const db = turbineHttp<TurbineClient>(pool, SCHEMA);
139
+ * const users = await db.users.findMany({ limit: 10 }); // fully typed, no cast
140
+ * ```
141
+ */
142
+ export declare function turbineHttp<TClient extends TurbineClient = TurbineClient>(pool: PgCompatPool, schema: SchemaMetadata, options?: TurbineHttpOptions): TClient;
@@ -0,0 +1,143 @@
1
+ /**
2
+ * turbine-orm/sqlite — zero-dependency SQLite engine
3
+ *
4
+ * Binds Turbine to SQLite via Node's built-in `node:sqlite` driver
5
+ * (`DatabaseSync`), so SQLite is a **zero new dependency** engine: the root
6
+ * package's runtime dependency stays exactly `pg`. This is the in-process
7
+ * test / edge / "try it in 10 seconds" engine — `:memory:` databases run
8
+ * entirely in-process with no service container.
9
+ *
10
+ * ## Driver
11
+ *
12
+ * - **Primary:** `node:sqlite` `DatabaseSync` (Node ≥ 22.5, experimental). Emits
13
+ * an `ExperimentalWarning` — harmless. No native build, no extra dependency.
14
+ * - **Fallback:** `better-sqlite3` for Node < 22.5. Not bundled and not required;
15
+ * wrap a `better-sqlite3` handle in the same `PgCompatPool` shape if needed.
16
+ *
17
+ * ## Capabilities & limits (vs PostgreSQL)
18
+ *
19
+ * - `RETURNING` + `ON CONFLICT … DO UPDATE` (SQLite ≥ 3.35) → create / upsert /
20
+ * update / delete return real rows in a single statement (`resultStrategy =
21
+ * 'returning'`, same as Postgres).
22
+ * - **Single-writer:** one connection / one write transaction at a time;
23
+ * concurrent writers get `SQLITE_BUSY` (treated as retryable). `journal_mode =
24
+ * WAL` is enabled for file databases to allow concurrent readers.
25
+ * - **Unsupported (throw `UnsupportedFeatureError`):** pgvector distance ops,
26
+ * LISTEN/NOTIFY (`$listen` / `$notify`), RLS `sessionContext`. Advisory-lock
27
+ * migration locking is unavailable — SQLite is single-writer, so migrations
28
+ * serialize naturally.
29
+ * - **Type affinity caveats:** SQLite has no native `BOOLEAN` (0/1 integers) or
30
+ * `DATE` (TEXT/INTEGER). Booleans bind as 1/0; `Date` values bind as ISO-8601
31
+ * text; columns declared `TIMESTAMP`/`DATETIME`/`DATE` are coerced back to
32
+ * `Date`. Integers wider than `Number.MAX_SAFE_INTEGER` come back as strings
33
+ * (the same safe-int policy Turbine uses for Postgres `int8`).
34
+ * - **Case-insensitive matching** uses `COLLATE NOCASE`, which is **ASCII-only**
35
+ * (no Unicode case folding).
36
+ *
37
+ * ## Example — `:memory:` database
38
+ *
39
+ * ```ts
40
+ * import { turbineSqlite } from 'turbine-orm/sqlite';
41
+ * import { SCHEMA } from './generated/turbine/metadata.js';
42
+ *
43
+ * const db = turbineSqlite(':memory:', SCHEMA);
44
+ * const users = await db.users.findMany({ with: { posts: true }, limit: 10 });
45
+ * await db.disconnect();
46
+ * ```
47
+ */
48
+ import type { DatabaseSync } from 'node:sqlite';
49
+ import { type PgCompatPool, type PgCompatPoolClient, TurbineClient, type TurbineConfig } from './client.js';
50
+ import { type Dialect, type IntrospectOptions } from './dialect.js';
51
+ import { type SchemaMetadata, type TableMetadata } from './schema.js';
52
+ /** pg-style query argument: a SQL string or a `{ text, values }` config object. */
53
+ type QueryArg = string | {
54
+ name?: string;
55
+ text: string;
56
+ values?: unknown[];
57
+ };
58
+ /**
59
+ * A `PgCompatPool` backed by a single `node:sqlite` `DatabaseSync` connection.
60
+ * SQLite is single-connection by nature (a `:memory:` database is per-handle),
61
+ * so `connect()` hands back a client over the **same** handle — transactions
62
+ * (`BEGIN`/`COMMIT`/`ROLLBACK`, `SAVEPOINT` nesting) just run on it. Queries are
63
+ * serialized; this is the documented single-writer model.
64
+ */
65
+ export declare class SqlitePool implements PgCompatPool {
66
+ /** The underlying `node:sqlite` handle — exposed as an escape hatch (seed/DDL). */
67
+ readonly db: DatabaseSync;
68
+ private closed;
69
+ constructor(db: DatabaseSync);
70
+ query(text: QueryArg, values?: unknown[]): Promise<any>;
71
+ connect(): Promise<PgCompatPoolClient>;
72
+ end(): Promise<void>;
73
+ }
74
+ /**
75
+ * Map a SQLite declared column type (type affinity) to a TypeScript type.
76
+ * SQLite is dynamically typed; we read the declared `PRAGMA table_info` type.
77
+ */
78
+ export declare function sqliteTypeToTs(declaredType: string, nullable: boolean): string;
79
+ /**
80
+ * SQLite implementation of the {@link Dialect} contract. Standardizes on `"…"`
81
+ * identifier quoting and positional `?` placeholders, uses `json_object` /
82
+ * `json_group_array` for the single-query nested-relation engine (with the
83
+ * critical `json(...)` subresult wrap so nested objects are real JSON, not
84
+ * SQLite's strings-of-strings double-encoding), keeps `RETURNING` + `ON CONFLICT`
85
+ * (SQLite ≥ 3.35), and disables the Postgres-only capabilities (vector,
86
+ * LISTEN/NOTIFY, RLS, advisory locks).
87
+ */
88
+ export declare const sqliteDialect: Dialect;
89
+ /**
90
+ * Read a live SQLite database (an open `DatabaseSync` handle) into the same
91
+ * {@link SchemaMetadata} shape the Postgres catalog introspector produces.
92
+ * Exposed directly so callers (and tests) can introspect an in-process
93
+ * `:memory:` database without round-tripping through a file path.
94
+ */
95
+ export declare function introspectSqliteDatabase(db: DatabaseSync, options?: {
96
+ include?: string[];
97
+ exclude?: string[];
98
+ }): {
99
+ tables: Record<string, TableMetadata>;
100
+ enums: {};
101
+ };
102
+ /**
103
+ * Open the SQLite database named by `options.connectionString` (a file path or
104
+ * `':memory:'`), introspect it, and close it. Wraps
105
+ * {@link introspectSqliteDatabase} for the {@link DialectIntrospector} seam used
106
+ * by `introspect()` / `npx turbine generate`.
107
+ *
108
+ * Note: introspecting `':memory:'` opens a *fresh, empty* database (memory DBs
109
+ * are per-handle), so codegen should target a real file.
110
+ */
111
+ export declare function introspectSqlite(options: IntrospectOptions): Promise<SchemaMetadata>;
112
+ /** Options for {@link turbineSqlite}. Mirrors the relevant {@link TurbineConfig} fields. */
113
+ export interface TurbineSqliteOptions extends Pick<TurbineConfig, 'logging' | 'defaultLimit' | 'warnOnUnlimited'> {
114
+ /**
115
+ * Enable WAL journal mode for file databases (better read concurrency).
116
+ * Ignored for `':memory:'`. Default: `true`.
117
+ */
118
+ wal?: boolean;
119
+ /** `PRAGMA busy_timeout` in ms — how long a writer waits on `SQLITE_BUSY`. Default: 5000. */
120
+ busyTimeoutMs?: number;
121
+ /** Enable `PRAGMA foreign_keys` enforcement. Default: `true`. */
122
+ foreignKeys?: boolean;
123
+ }
124
+ /**
125
+ * Create a {@link TurbineClient} bound to SQLite via `node:sqlite`.
126
+ *
127
+ * Pass a file path, `':memory:'`, or an already-open `DatabaseSync` handle (so
128
+ * you can seed / introspect it first and reuse the same connection). The
129
+ * returned client uses {@link sqliteDialect} and disables prepared-statement
130
+ * names (SQLite caches plans internally).
131
+ *
132
+ * @param target A SQLite file path, `':memory:'`, or an open `DatabaseSync`.
133
+ * @param schema Introspected or hand-written {@link SchemaMetadata}.
134
+ * @param options Optional pragmas + logging / defaultLimit / warnOnUnlimited.
135
+ *
136
+ * @example
137
+ * ```ts
138
+ * import { turbineSqlite } from 'turbine-orm/sqlite';
139
+ * const db = turbineSqlite(':memory:', SCHEMA);
140
+ * ```
141
+ */
142
+ export declare function turbineSqlite(target: string | DatabaseSync, schema: SchemaMetadata, options?: TurbineSqliteOptions): TurbineClient;
143
+ export {};
@@ -462,6 +462,10 @@ exports.sqliteDialect = {
462
462
  buildUpsertStatement(input) {
463
463
  return (`INSERT INTO ${input.table} (${input.insertColumns.join(', ')}) VALUES (${input.valuePlaceholders.join(', ')})` +
464
464
  ` ON CONFLICT (${input.conflictColumns.join(', ')}) DO UPDATE SET ${input.updateSetClauses.join(', ')}` +
465
+ // SQLite's upsert takes the same trailing conflict-UPDATE predicate as
466
+ // Postgres, so `supportsUpsertUpdateWhere` (inherited true) is honest and
467
+ // global filters restrict the UPDATE branch here too.
468
+ (input.updateWhere ? ` WHERE ${input.updateWhere}` : '') +
465
469
  this.buildReturningClause(input.returning));
466
470
  },
467
471
  buildInsensitiveLike(column, paramRef) {
@@ -0,0 +1,102 @@
1
+ /**
2
+ * turbine-orm — Typed raw SQL (Turbine's answer to Prisma's TypedSQL)
3
+ *
4
+ * `client.raw()` returns untyped rows. This module adds a *typed* escape hatch:
5
+ * a generic tagged template where the caller supplies the row shape, and the
6
+ * builder yields a typed result that can be awaited as an array of rows, or
7
+ * narrowed to a single row (`.one()`) or a single scalar value (`.scalar()`).
8
+ *
9
+ * Design goals & guarantees:
10
+ *
11
+ * 1. **Compile-time only types.** `T` is supplied by the caller and never
12
+ * validated at runtime — exactly like Prisma's TypedSQL and the existing
13
+ * `raw<T>()`. Postgres still returns whatever the SQL selects; the generic
14
+ * is a convenience for autocomplete and downstream type-checking.
15
+ *
16
+ * 2. **Mandatory parameterization.** Only the *static* string segments of the
17
+ * template literal ever reach the SQL text. Every interpolated `${value}`
18
+ * becomes a `$N` placeholder and is passed in the params array — it is
19
+ * impossible to string-concatenate a value into the query through this API.
20
+ * This is the whole point of the tagged-template shape: the literal segments
21
+ * are frozen by the compiler (`TemplateStringsArray`), and the only way to
22
+ * get a runtime value into the query is via `${...}`, which we bind.
23
+ *
24
+ * 3. **Rows are returned as-is (no snake→camel mapping).** This matches the
25
+ * existing `client.raw()` behavior: a typed raw query is a literal escape
26
+ * hatch, so the result columns are whatever your `SELECT` names them. Alias
27
+ * columns in SQL (`SELECT created_at AS "createdAt"`) if you want camelCase.
28
+ *
29
+ * @example
30
+ * ```ts
31
+ * // Awaited directly -> rows
32
+ * const rows = await db.sql<{ id: number; name: string }>`
33
+ * SELECT id, name FROM users WHERE org_id = ${orgId}
34
+ * `;
35
+ * // ^? { id: number; name: string }[]
36
+ *
37
+ * // .one() -> single row or null
38
+ * const user = await db.sql<{ id: number; name: string }>`
39
+ * SELECT id, name FROM users WHERE id = ${userId}
40
+ * `.one();
41
+ * // ^? { id: number; name: string } | null
42
+ *
43
+ * // .scalar() -> first column of first row, or null
44
+ * const total = await db.sql<{ count: number }>`
45
+ * SELECT COUNT(*)::int AS count FROM users WHERE org_id = ${orgId}
46
+ * `.scalar();
47
+ * // ^? number | null
48
+ * ```
49
+ */
50
+ import type { PgCompatPool } from './client.js';
51
+ import { type Dialect } from './dialect.js';
52
+ /**
53
+ * Build a `(sql, params)` pair from a tagged-template invocation.
54
+ *
55
+ * Each interpolated value is replaced by a positional placeholder (the active
56
+ * dialect's `paramPlaceholder`, `$N` for PostgreSQL) and pushed to the params
57
+ * array in order. The static string segments are the only thing concatenated
58
+ * into the SQL text. This is the single point that guarantees parameterization
59
+ * for the entire typed-SQL surface.
60
+ *
61
+ * Exported for unit testing the parameterization invariant without a database.
62
+ */
63
+ export declare function buildTypedSql(strings: TemplateStringsArray, values: readonly unknown[], dialect?: Pick<Dialect, 'paramPlaceholder'>): {
64
+ sql: string;
65
+ params: unknown[];
66
+ };
67
+ /**
68
+ * A pending typed raw SQL query. Implements the thenable contract, so it can be
69
+ * `await`ed directly to get `T[]`, or refined via `.one()` / `.scalar()` first.
70
+ *
71
+ * The query is executed lazily and exactly once per terminal call (`then`,
72
+ * `one`, `scalar`). Each terminal method runs the query independently — this is
73
+ * an escape hatch, not a cached query object, so don't call two terminals on
74
+ * the same builder expecting a single round-trip; build a fresh template each
75
+ * time (the common pattern is `await db.sql\`...\`` inline).
76
+ */
77
+ export declare class TypedSqlQuery<T extends Record<string, unknown>> implements PromiseLike<T[]> {
78
+ private readonly pool;
79
+ private readonly sql;
80
+ private readonly params;
81
+ private readonly logging;
82
+ constructor(pool: PgCompatPool, sql: string, params: unknown[], logging: boolean);
83
+ /** Execute and return all rows. Internal; powers `then`, `one`, and `scalar`. */
84
+ private run;
85
+ /**
86
+ * PromiseLike implementation: `await db.sql<T>\`...\`` resolves to `T[]`.
87
+ */
88
+ then<TResult1 = T[], TResult2 = never>(onfulfilled?: ((value: T[]) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null): Promise<TResult1 | TResult2>;
89
+ /**
90
+ * Execute and return the first row, or `null` if the query returns no rows.
91
+ * Use for queries you expect to match at most one row.
92
+ */
93
+ one(): Promise<T | null>;
94
+ /**
95
+ * Execute and return the first column of the first row, or `null` if there
96
+ * are no rows. Useful for `SELECT COUNT(*)`, `SELECT EXISTS(...)`, etc.
97
+ *
98
+ * The generic `V` defaults to the value type of `T`'s first property, but you
99
+ * can override it: `db.sql<{ count: number }>\`...\`.scalar<number>()`.
100
+ */
101
+ scalar<V = T[keyof T]>(): Promise<V | null>;
102
+ }
@@ -34,10 +34,17 @@ export interface TurbineCliConfig {
34
34
  legacyToManyUniques?: boolean;
35
35
  /** Directory for migration files (default: ./turbine/migrations) */
36
36
  migrationsDir?: string;
37
- /** Path to seed file. Defaults are resolved from seed.ts, seed.js, then seed.sql. */
38
- seed?: string;
39
- /** Path to seed file. Deprecated alias for `seed`. */
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
+ */
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;
41
48
  /** Schema builder file path (for push command) */
42
49
  schemaFile?: string;
43
50
  /**
@@ -66,6 +73,12 @@ export type TurbineConfig = TurbineCliConfig;
66
73
  * silently matches zero tables. Used by `turbine generate` to fail loudly.
67
74
  */
68
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";
69
82
  /** A config-file load attempt that failed, kept so the CLI can surface it. */
70
83
  export interface ConfigLoadError {
71
84
  /** The config file whose import threw (e.g. `turbine.config.ts`). */
@@ -161,7 +174,8 @@ export interface CliOverrides {
161
174
  export declare function resolveConfig(fileConfig: TurbineCliConfig, overrides: CliOverrides): ResolvedConfig;
162
175
  /**
163
176
  * Resolve the seed file path. An explicit config value wins even if the file
164
- * does not exist yet; otherwise the root-level defaults are tried in order.
177
+ * does not exist yet; otherwise {@link DEFAULT_SEED_CANDIDATES} is tried in
178
+ * order (root-level first, then the `turbine/` location `init` scaffolds).
165
179
  */
166
180
  export declare function resolveSeedFile(config: Pick<TurbineCliConfig, 'seed' | 'seedFile'>, cwd?: string): string | null;
167
181
  export declare function configTemplate(connectionString?: string): string;
@@ -24,7 +24,28 @@ export function looksLikeSchemaFilePath(schema) {
24
24
  // Config file names, in priority order
25
25
  // ---------------------------------------------------------------------------
26
26
  const CONFIG_FILES = ['turbine.config.ts', 'turbine.config.mts', 'turbine.config.js', 'turbine.config.mjs'];
27
- const DEFAULT_SEED_CANDIDATES = ['seed.ts', 'seed.js', 'seed.sql'];
27
+ /**
28
+ * Config-less fallback locations for the seed file, in priority order.
29
+ *
30
+ * Root-level candidates stay FIRST so no project that already relies on
31
+ * `./seed.ts` changes behavior. The `turbine/` candidates are appended so a
32
+ * project that drops the `seedFile` key can still auto-discover the file
33
+ * `turbine init` scaffolds (see {@link DEFAULT_INIT_SEED_FILE}).
34
+ */
35
+ const DEFAULT_SEED_CANDIDATES = [
36
+ 'seed.ts',
37
+ 'seed.js',
38
+ 'seed.sql',
39
+ 'turbine/seed.ts',
40
+ 'turbine/seed.js',
41
+ 'turbine/seed.sql',
42
+ ];
43
+ /**
44
+ * Where `turbine init` scaffolds the seed file, and the `seedFile` value it
45
+ * writes into the generated config. Kept next to the schema file so a new
46
+ * project's Turbine files live in one directory.
47
+ */
48
+ export const DEFAULT_INIT_SEED_FILE = './turbine/seed.ts';
28
49
  function isPlainObject(value) {
29
50
  return typeof value === 'object' && value !== null && !Array.isArray(value);
30
51
  }
@@ -139,7 +160,9 @@ export function resolveConfig(fileConfig, overrides) {
139
160
  include: overrides.include ?? fileConfig.include ?? [],
140
161
  exclude: overrides.exclude ?? fileConfig.exclude ?? [],
141
162
  migrationsDir: fileConfig.migrationsDir ?? './turbine/migrations',
142
- seedFile: fileConfig.seed ?? fileConfig.seedFile,
163
+ // `seedFile` is canonical (what the docs and `turbine init` use); `seed` is a
164
+ // back-compat alias kept working for configs scaffolded before 0.50.
165
+ seedFile: fileConfig.seedFile ?? fileConfig.seed,
143
166
  schemaFile: fileConfig.schemaFile ?? './turbine/schema.ts',
144
167
  importExtension: overrides.importExtension ?? fileConfig.importExtension ?? 'auto',
145
168
  keepColumnNames: overrides.keepColumnNames ?? fileConfig.keepColumnNames ?? false,
@@ -148,10 +171,12 @@ export function resolveConfig(fileConfig, overrides) {
148
171
  }
149
172
  /**
150
173
  * Resolve the seed file path. An explicit config value wins even if the file
151
- * does not exist yet; otherwise the root-level defaults are tried in order.
174
+ * does not exist yet; otherwise {@link DEFAULT_SEED_CANDIDATES} is tried in
175
+ * order (root-level first, then the `turbine/` location `init` scaffolds).
152
176
  */
153
177
  export function resolveSeedFile(config, cwd = process.cwd()) {
154
- const explicit = config.seed ?? config.seedFile;
178
+ // Canonical `seedFile` first, then the back-compat `seed` alias (see resolveConfig).
179
+ const explicit = config.seedFile ?? config.seed;
155
180
  if (explicit)
156
181
  return resolve(cwd, explicit);
157
182
  for (const candidate of DEFAULT_SEED_CANDIDATES) {
@@ -189,8 +214,8 @@ ${urlLine}
189
214
  /** Directory for SQL migration files */
190
215
  migrationsDir: './turbine/migrations',
191
216
 
192
- /** Path to seed file (defaults: ./seed.ts, ./seed.js, ./seed.sql) */
193
- seed: './seed.ts',
217
+ /** Path to seed file (defaults: ./seed.ts, ./seed.js, ./seed.sql, ./turbine/seed.ts) */
218
+ seedFile: '${DEFAULT_INIT_SEED_FILE}',
194
219
 
195
220
  /** Path to schema builder file (for turbine push) */
196
221
  schemaFile: './turbine/schema.ts',
@@ -24,6 +24,8 @@
24
24
  * npx turbine init --url postgres://...
25
25
  * npx turbine migrate create add_users_table
26
26
  */
27
+ import type { CliOverrides, ConfigLoadError, ResolvedConfig, TurbineCliConfig } from './config.js';
28
+ import { type PrismaSchemaAst, type ResolvedPrismaDatasourceUrl } from './prisma-schema.js';
27
29
  export interface CliArgs {
28
30
  command: string;
29
31
  subcommand?: string;
@@ -96,6 +98,43 @@ export interface CliArgs {
96
98
  noDb?: boolean;
97
99
  }
98
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>;
99
138
  /** Where a resolved `DATABASE_URL` came from, after the `.env` load. */
100
139
  export type DotEnvProvenance = 'shell' | 'dotenv' | 'none';
101
140
  /** Structured outcome of {@link loadDotEnvForCli}. */
@@ -158,6 +197,30 @@ export declare function dotEnvUrlConflictWarning(input: {
158
197
  fileConfigUrl: string | undefined;
159
198
  overrideUrl: string | undefined;
160
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[];
161
224
  /**
162
225
  * Read the consumer's `package.json` `"type"` field. Returns `'module'` for an
163
226
  * ESM project, `'commonjs'` for an explicit or absent (defaulted) CommonJS
@@ -212,6 +275,66 @@ export interface InitPlanFlags {
212
275
  * skipped when there is no URL or the database is unreachable.
213
276
  */
214
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;
215
338
  export declare function buildMigrateDeployOptions(args: CliArgs): {
216
339
  allowDrift: boolean;
217
340
  allowDestructive: true;