turbine-orm 0.50.0 → 0.51.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 (186) hide show
  1. package/README.md +66 -66
  2. package/dist/adapters/cockroachdb.d.ts +5 -5
  3. package/dist/adapters/cockroachdb.js +10 -10
  4. package/dist/adapters/index.d.ts +5 -5
  5. package/dist/adapters/index.js +7 -7
  6. package/dist/adapters/yugabytedb.d.ts +7 -7
  7. package/dist/adapters/yugabytedb.js +10 -10
  8. package/dist/cjs/adapters/cockroachdb.d.ts +5 -5
  9. package/dist/cjs/adapters/cockroachdb.js +10 -10
  10. package/dist/cjs/adapters/index.d.ts +5 -5
  11. package/dist/cjs/adapters/index.js +7 -7
  12. package/dist/cjs/adapters/yugabytedb.d.ts +7 -7
  13. package/dist/cjs/adapters/yugabytedb.js +10 -10
  14. package/dist/cjs/cli/config.d.ts +13 -2
  15. package/dist/cjs/cli/config.js +3 -2
  16. package/dist/cjs/cli/destructive.d.ts +1 -1
  17. package/dist/cjs/cli/destructive.js +1 -1
  18. package/dist/cjs/cli/index.d.ts +10 -10
  19. package/dist/cjs/cli/index.js +49 -45
  20. package/dist/cjs/cli/loader.d.ts +7 -7
  21. package/dist/cjs/cli/loader.js +9 -9
  22. package/dist/cjs/cli/mcp.js +4 -4
  23. package/dist/cjs/cli/migrate.d.ts +5 -5
  24. package/dist/cjs/cli/migrate.js +11 -11
  25. package/dist/cjs/cli/studio-ui.generated.js +1 -1
  26. package/dist/cjs/cli/ui.d.ts +2 -2
  27. package/dist/cjs/cli/ui.js +2 -2
  28. package/dist/cjs/client.d.ts +49 -38
  29. package/dist/cjs/client.js +57 -56
  30. package/dist/cjs/dialect.d.ts +62 -18
  31. package/dist/cjs/dialect.js +40 -2
  32. package/dist/cjs/errors.d.ts +5 -5
  33. package/dist/cjs/errors.js +11 -11
  34. package/dist/cjs/generate.d.ts +6 -6
  35. package/dist/cjs/generate.js +31 -29
  36. package/dist/cjs/index-advisor.d.ts +5 -5
  37. package/dist/cjs/index-advisor.js +0 -0
  38. package/dist/cjs/index.d.ts +1 -1
  39. package/dist/cjs/index.js +7 -7
  40. package/dist/cjs/introspect.d.ts +35 -9
  41. package/dist/cjs/introspect.js +83 -32
  42. package/dist/cjs/mssql.d.ts +11 -11
  43. package/dist/cjs/mssql.js +64 -29
  44. package/dist/cjs/mysql.d.ts +8 -8
  45. package/dist/cjs/mysql.js +61 -23
  46. package/dist/cjs/nested-write.d.ts +21 -2
  47. package/dist/cjs/nested-write.js +51 -14
  48. package/dist/cjs/optional-peer-import.cjs +7 -7
  49. package/dist/cjs/optional-peer-import.d.cts +7 -7
  50. package/dist/cjs/pipeline-submittable.d.ts +2 -2
  51. package/dist/cjs/pipeline-submittable.js +6 -6
  52. package/dist/cjs/pipeline.d.ts +1 -1
  53. package/dist/cjs/pipeline.js +4 -4
  54. package/dist/cjs/powdb-introspect.d.ts +1 -1
  55. package/dist/cjs/powdb-introspect.js +1 -1
  56. package/dist/cjs/powdb.d.ts +28 -28
  57. package/dist/cjs/powdb.js +66 -66
  58. package/dist/cjs/powql.d.ts +27 -27
  59. package/dist/cjs/powql.js +73 -52
  60. package/dist/cjs/query/aggregates.d.ts +1 -1
  61. package/dist/cjs/query/aggregates.js +5 -5
  62. package/dist/cjs/query/batched-loader.d.ts +11 -11
  63. package/dist/cjs/query/batched-loader.js +24 -24
  64. package/dist/cjs/query/builder.d.ts +39 -21
  65. package/dist/cjs/query/builder.js +99 -57
  66. package/dist/cjs/query/compound-unique.d.ts +1 -1
  67. package/dist/cjs/query/compound-unique.js +0 -0
  68. package/dist/cjs/query/deferred.d.ts +12 -6
  69. package/dist/cjs/query/deferred.js +1 -1
  70. package/dist/cjs/query/filters.d.ts +31 -11
  71. package/dist/cjs/query/filters.js +67 -14
  72. package/dist/cjs/query/index.d.ts +1 -1
  73. package/dist/cjs/query/index.js +1 -1
  74. package/dist/cjs/query/relations.d.ts +9 -9
  75. package/dist/cjs/query/relations.js +164 -57
  76. package/dist/cjs/query/types.d.ts +86 -35
  77. package/dist/cjs/query/types.js +1 -1
  78. package/dist/cjs/query/utils.d.ts +27 -10
  79. package/dist/cjs/query/utils.js +86 -14
  80. package/dist/cjs/query/where.d.ts +47 -28
  81. package/dist/cjs/query/where.js +130 -31
  82. package/dist/cjs/query/writes.d.ts +24 -5
  83. package/dist/cjs/query/writes.js +102 -13
  84. package/dist/cjs/realtime.d.ts +7 -7
  85. package/dist/cjs/realtime.js +9 -9
  86. package/dist/cjs/schema-builder.d.ts +18 -7
  87. package/dist/cjs/schema-builder.js +17 -10
  88. package/dist/cjs/schema-metadata.d.ts +3 -3
  89. package/dist/cjs/schema-metadata.js +9 -9
  90. package/dist/cjs/schema-sql.d.ts +9 -9
  91. package/dist/cjs/schema-sql.js +20 -20
  92. package/dist/cjs/schema.d.ts +19 -9
  93. package/dist/cjs/schema.js +6 -6
  94. package/dist/cjs/serverless.d.ts +15 -15
  95. package/dist/cjs/serverless.js +16 -16
  96. package/dist/cjs/sqlite.d.ts +8 -8
  97. package/dist/cjs/sqlite.js +53 -22
  98. package/dist/cjs/typed-sql.d.ts +4 -4
  99. package/dist/cjs/typed-sql.js +5 -5
  100. package/dist/cli/config.d.ts +13 -2
  101. package/dist/cli/config.js +3 -2
  102. package/dist/cli/destructive.d.ts +1 -1
  103. package/dist/cli/destructive.js +1 -1
  104. package/dist/cli/index.d.ts +10 -10
  105. package/dist/cli/index.js +49 -45
  106. package/dist/cli/loader.d.ts +7 -7
  107. package/dist/cli/loader.js +9 -9
  108. package/dist/cli/mcp.js +4 -4
  109. package/dist/cli/migrate.d.ts +5 -5
  110. package/dist/cli/migrate.js +11 -11
  111. package/dist/cli/studio-ui.generated.js +1 -1
  112. package/dist/cli/ui.d.ts +2 -2
  113. package/dist/cli/ui.js +2 -2
  114. package/dist/client.d.ts +49 -38
  115. package/dist/client.js +57 -56
  116. package/dist/dialect.d.ts +62 -18
  117. package/dist/dialect.js +40 -2
  118. package/dist/errors.d.ts +5 -5
  119. package/dist/errors.js +11 -11
  120. package/dist/generate.d.ts +6 -6
  121. package/dist/generate.js +31 -29
  122. package/dist/index-advisor.d.ts +5 -5
  123. package/dist/index-advisor.js +0 -0
  124. package/dist/index.d.ts +1 -1
  125. package/dist/index.js +7 -7
  126. package/dist/introspect.d.ts +35 -9
  127. package/dist/introspect.js +82 -32
  128. package/dist/mssql.d.ts +11 -11
  129. package/dist/mssql.js +64 -29
  130. package/dist/mysql.d.ts +8 -8
  131. package/dist/mysql.js +61 -23
  132. package/dist/nested-write.d.ts +21 -2
  133. package/dist/nested-write.js +51 -14
  134. package/dist/optional-peer-import.cjs +7 -7
  135. package/dist/optional-peer-import.d.cts +7 -7
  136. package/dist/pipeline-submittable.d.ts +2 -2
  137. package/dist/pipeline-submittable.js +6 -6
  138. package/dist/pipeline.d.ts +1 -1
  139. package/dist/pipeline.js +4 -4
  140. package/dist/powdb-introspect.d.ts +1 -1
  141. package/dist/powdb-introspect.js +1 -1
  142. package/dist/powdb.d.ts +28 -28
  143. package/dist/powdb.js +66 -66
  144. package/dist/powql.d.ts +27 -27
  145. package/dist/powql.js +73 -52
  146. package/dist/query/aggregates.d.ts +1 -1
  147. package/dist/query/aggregates.js +5 -5
  148. package/dist/query/batched-loader.d.ts +11 -11
  149. package/dist/query/batched-loader.js +24 -24
  150. package/dist/query/builder.d.ts +39 -21
  151. package/dist/query/builder.js +100 -58
  152. package/dist/query/compound-unique.d.ts +1 -1
  153. package/dist/query/compound-unique.js +0 -0
  154. package/dist/query/deferred.d.ts +12 -6
  155. package/dist/query/deferred.js +1 -1
  156. package/dist/query/filters.d.ts +31 -11
  157. package/dist/query/filters.js +66 -13
  158. package/dist/query/index.d.ts +1 -1
  159. package/dist/query/index.js +1 -1
  160. package/dist/query/relations.d.ts +9 -9
  161. package/dist/query/relations.js +165 -58
  162. package/dist/query/types.d.ts +86 -35
  163. package/dist/query/types.js +1 -1
  164. package/dist/query/utils.d.ts +27 -10
  165. package/dist/query/utils.js +84 -14
  166. package/dist/query/where.d.ts +47 -28
  167. package/dist/query/where.js +129 -32
  168. package/dist/query/writes.d.ts +24 -5
  169. package/dist/query/writes.js +101 -13
  170. package/dist/realtime.d.ts +7 -7
  171. package/dist/realtime.js +9 -9
  172. package/dist/schema-builder.d.ts +18 -7
  173. package/dist/schema-builder.js +17 -10
  174. package/dist/schema-metadata.d.ts +3 -3
  175. package/dist/schema-metadata.js +9 -9
  176. package/dist/schema-sql.d.ts +9 -9
  177. package/dist/schema-sql.js +20 -20
  178. package/dist/schema.d.ts +19 -9
  179. package/dist/schema.js +6 -6
  180. package/dist/serverless.d.ts +15 -15
  181. package/dist/serverless.js +16 -16
  182. package/dist/sqlite.d.ts +8 -8
  183. package/dist/sqlite.js +53 -22
  184. package/dist/typed-sql.d.ts +4 -4
  185. package/dist/typed-sql.js +5 -5
  186. package/package.json +2 -2
@@ -1,17 +1,17 @@
1
1
  "use strict";
2
2
  /**
3
- * turbine-orm/sqlite — zero-dependency SQLite engine
3
+ * turbine-orm/sqlite, zero-dependency SQLite engine
4
4
  *
5
5
  * Binds Turbine to SQLite via Node's built-in `node:sqlite` driver
6
6
  * (`DatabaseSync`), so SQLite is a **zero new dependency** engine: the root
7
7
  * package's runtime dependency stays exactly `pg`. This is the in-process
8
- * test / edge / "try it in 10 seconds" engine — `:memory:` databases run
8
+ * test / edge / "try it in 10 seconds" engine, `:memory:` databases run
9
9
  * entirely in-process with no service container.
10
10
  *
11
11
  * ## Driver
12
12
  *
13
13
  * - **Primary:** `node:sqlite` `DatabaseSync` (Node ≥ 22.5, experimental). Emits
14
- * an `ExperimentalWarning` — harmless. No native build, no extra dependency.
14
+ * an `ExperimentalWarning`, harmless. No native build, no extra dependency.
15
15
  * - **Fallback:** `better-sqlite3` for Node < 22.5. Not bundled and not required;
16
16
  * wrap a `better-sqlite3` handle in the same `PgCompatPool` shape if needed.
17
17
  *
@@ -25,7 +25,7 @@
25
25
  * WAL` is enabled for file databases to allow concurrent readers.
26
26
  * - **Unsupported (throw `UnsupportedFeatureError`):** pgvector distance ops,
27
27
  * LISTEN/NOTIFY (`$listen` / `$notify`), RLS `sessionContext`. Advisory-lock
28
- * migration locking is unavailable — SQLite is single-writer, so migrations
28
+ * migration locking is unavailable, SQLite is single-writer, so migrations
29
29
  * serialize naturally.
30
30
  * - **Type affinity caveats:** SQLite has no native `BOOLEAN` (0/1 integers) or
31
31
  * `DATE` (TEXT/INTEGER). Booleans bind as 1/0; `Date` values bind as ISO-8601
@@ -35,7 +35,7 @@
35
35
  * - **Case-insensitive matching** uses `COLLATE NOCASE`, which is **ASCII-only**
36
36
  * (no Unicode case folding).
37
37
  *
38
- * ## Example — `:memory:` database
38
+ * ## Example, `:memory:` database
39
39
  *
40
40
  * ```ts
41
41
  * import { turbineSqlite } from 'turbine-orm/sqlite';
@@ -63,8 +63,8 @@ let cachedDatabaseSync;
63
63
  * Lazily load `node:sqlite`'s `DatabaseSync` constructor.
64
64
  *
65
65
  * `node:sqlite` is a built-in only on Node >= 22.5, so importing it at module
66
- * top-level would make `import 'turbine-orm/sqlite'` — and any module that
67
- * merely re-exports `sqliteDialect` (e.g. the dialect test suite) — throw
66
+ * top-level would make `import 'turbine-orm/sqlite'`, and any module that
67
+ * merely re-exports `sqliteDialect` (e.g. the dialect test suite), throw
68
68
  * `ERR_UNKNOWN_BUILTIN_MODULE` on Node 20. Deferring the require to the moment a
69
69
  * connection is actually opened keeps the dialect (pure SQL generation) usable
70
70
  * everywhere and scopes the Node-version requirement to `turbineSqlite()`.
@@ -86,7 +86,7 @@ function loadDatabaseSync() {
86
86
  `Upgrade Node to >= 22.5, or pass an already-open better-sqlite3-compatible handle. (${err.message})`);
87
87
  }
88
88
  if (typeof ctor !== 'function') {
89
- throw new errors_js_1.ConnectionError("[turbine] 'node:sqlite' loaded but did not export a DatabaseSync constructor — this Node build may lack SQLite support.");
89
+ throw new errors_js_1.ConnectionError("[turbine] 'node:sqlite' loaded but did not export a DatabaseSync constructor, this Node build may lack SQLite support.");
90
90
  }
91
91
  cachedDatabaseSync = ctor;
92
92
  return cachedDatabaseSync;
@@ -119,7 +119,7 @@ function toSqliteParam(value) {
119
119
  /**
120
120
  * Normalize a single column value read from SQLite. With `setReadBigInts(true)`
121
121
  * every integer column comes back as a `bigint`; apply the same safe-integer
122
- * policy Turbine uses for Postgres `int8` — number when it fits in a JS safe
122
+ * policy Turbine uses for Postgres `int8`, number when it fits in a JS safe
123
123
  * integer, otherwise the decimal string to avoid precision loss. Never mutates
124
124
  * any global parser state (the policy lives entirely in this shim).
125
125
  */
@@ -160,7 +160,7 @@ function statementReturnsRows(sql) {
160
160
  * recognizable SQLite error.
161
161
  *
162
162
  * `wrapPgError` is invoked downstream (in the query executor and the
163
- * transaction proxy), so we only annotate here — we never throw a `new`
163
+ * transaction proxy), so we only annotate here, we never throw a `new`
164
164
  * Turbine error from the driver itself.
165
165
  */
166
166
  function augmentSqliteError(err) {
@@ -228,7 +228,7 @@ function normalizeQueryArgs(arg, values) {
228
228
  /**
229
229
  * Bind the positional `params[]` (in 1-indexed generation order) to the named
230
230
  * `:p1`, `:p2`, … placeholders the dialect emits. Mapping by NAME makes binding
231
- * independent of where each placeholder lands in the SQL text — the same
231
+ * independent of where each placeholder lands in the SQL text, the same
232
232
  * guarantee Postgres' numbered `$N` gives. Returns `undefined` when there are no
233
233
  * params so parameter-less statements (BEGIN/COMMIT/DDL) bind nothing.
234
234
  */
@@ -268,12 +268,12 @@ function runStatement(db, sql, values) {
268
268
  /**
269
269
  * A `PgCompatPool` backed by a single `node:sqlite` `DatabaseSync` connection.
270
270
  * SQLite is single-connection by nature (a `:memory:` database is per-handle),
271
- * so `connect()` hands back a client over the **same** handle — transactions
271
+ * so `connect()` hands back a client over the **same** handle, transactions
272
272
  * (`BEGIN`/`COMMIT`/`ROLLBACK`, `SAVEPOINT` nesting) just run on it. Queries are
273
273
  * serialized; this is the documented single-writer model.
274
274
  */
275
275
  class SqlitePool {
276
- /** The underlying `node:sqlite` handle — exposed as an escape hatch (seed/DDL). */
276
+ /** The underlying `node:sqlite` handle, exposed as an escape hatch (seed/DDL). */
277
277
  db;
278
278
  closed = false;
279
279
  constructor(db) {
@@ -293,7 +293,7 @@ class SqlitePool {
293
293
  return runStatement(db, sql, params);
294
294
  },
295
295
  release: () => {
296
- // Single shared connection — nothing to return to a pool.
296
+ // Single shared connection, nothing to return to a pool.
297
297
  },
298
298
  };
299
299
  }
@@ -357,7 +357,7 @@ function sqliteColumnAffinity(type) {
357
357
  return 'TEXT';
358
358
  }
359
359
  // ---------------------------------------------------------------------------
360
- // sqliteDialect — the full Dialect contract for SQLite
360
+ // sqliteDialect, the full Dialect contract for SQLite
361
361
  // ---------------------------------------------------------------------------
362
362
  /**
363
363
  * SQLite implementation of the {@link Dialect} contract. Standardizes on `"…"`
@@ -394,7 +394,7 @@ exports.sqliteDialect = {
394
394
  jsonPathSupport: 'function',
395
395
  emptyJsonArrayLiteral: "json('[]')",
396
396
  nullJsonLiteral: 'NULL',
397
- // Named placeholders (`:p1`, `:p2`, …) — NOT positional `?`. Turbine pushes
397
+ // Named placeholders (`:p1`, `:p2`, …), NOT positional `?`. Turbine pushes
398
398
  // params in 1-indexed generation order but may EMIT them in a different SQL
399
399
  // text position (e.g. a `with`-relation LIMIT lands in the SELECT list, ahead
400
400
  // of the outer WHERE). Postgres reconciles this via numbered `$N`; positional
@@ -426,6 +426,37 @@ exports.sqliteDialect = {
426
426
  wrapJsonSubresult(subquery, fallback) {
427
427
  return `COALESCE(json((${subquery})), ${fallback})`;
428
428
  },
429
+ jsonWireRule(columnType) {
430
+ // SQLite's storage classes, as introspection records them.
431
+ const t = columnType.toUpperCase();
432
+ // INTEGER is 64-bit, and `json_object` renders it as a JSON number, which
433
+ // is an IEEE double: 9007199254740993 came back through a `with` join as
434
+ // …992 while a top-level read and the batched loader both returned the
435
+ // exact decimal string. Carry the text and re-apply the SAME safe-integer
436
+ // policy `normalizeValue` applies to the driver's bigint, so all three
437
+ // paths agree for both small and large values.
438
+ if (t.includes('INT')) {
439
+ return {
440
+ sql: (ref) => `CAST(${ref} AS TEXT)`,
441
+ decode: (value) => {
442
+ if (typeof value !== 'string' || !/^-?\d+$/.test(value))
443
+ return value;
444
+ const asNumber = Number(value);
445
+ return Number.isSafeInteger(asNumber) ? asNumber : value;
446
+ },
447
+ };
448
+ }
449
+ // A BLOB cannot go into JSON at all: SQLite raises "JSON cannot hold BLOB
450
+ // values" and the whole query fails with a raw SQL logic error, where the
451
+ // batched loader returns the row fine. Carry hex and rebuild the bytes.
452
+ if (t.includes('BLOB')) {
453
+ return {
454
+ sql: (ref) => `hex(${ref})`,
455
+ decode: (value) => (typeof value === 'string' ? Uint8Array.from(Buffer.from(value, 'hex')) : value),
456
+ };
457
+ }
458
+ return undefined;
459
+ },
429
460
  castAggregate(expr, target) {
430
461
  return `CAST(${expr} AS ${target === 'int' ? 'INTEGER' : 'REAL'})`;
431
462
  },
@@ -446,7 +477,7 @@ exports.sqliteDialect = {
446
477
  `VALUES (${input.valuePlaceholders.join(', ')})${this.buildReturningClause(input.returning)}`);
447
478
  },
448
479
  buildBulkInsertStatement(input) {
449
- // No UNNEST in SQLite — emit multi-row VALUES with flattened, named
480
+ // No UNNEST in SQLite, emit multi-row VALUES with flattened, named
450
481
  // placeholders (`:p1`, `:p2`, …) matching the flat param order.
451
482
  let n = 0;
452
483
  const placeholders = input.rowValues
@@ -469,12 +500,12 @@ exports.sqliteDialect = {
469
500
  this.buildReturningClause(input.returning));
470
501
  },
471
502
  buildInsensitiveLike(column, paramRef) {
472
- // COLLATE NOCASE is ASCII-only (no Unicode case folding) — documented limit.
503
+ // COLLATE NOCASE is ASCII-only (no Unicode case folding), documented limit.
473
504
  return `${column} LIKE ${paramRef} COLLATE NOCASE`;
474
505
  },
475
506
  buildJsonContains(column, paramRef) {
476
507
  // Emulated containment: true when any top-level JSON value equals the param.
477
- // Limited vs Postgres `@>` (no deep/object containment) — jsonPathSupport='function'.
508
+ // Limited vs Postgres `@>` (no deep/object containment), jsonPathSupport='function'.
478
509
  return `EXISTS (SELECT 1 FROM json_each(${column}) WHERE json_each.value = ${paramRef})`;
479
510
  },
480
511
  buildJsonPathExtract(column, pathParamRef) {
@@ -555,7 +586,7 @@ exports.sqliteDialect = {
555
586
  },
556
587
  };
557
588
  function pragma(db, sql) {
558
- // PRAGMA / SELECT against sqlite_master — read-only, identifiers are SQLite
589
+ // PRAGMA / SELECT against sqlite_master, read-only, identifiers are SQLite
559
590
  // catalog names (never user input here), values normalized for safe ints.
560
591
  return db.prepare(sql).all().map(normalizeRow);
561
592
  }
@@ -658,7 +689,7 @@ function introspectSqliteDatabase(db, options = {}) {
658
689
  // ----- Build relations from foreign keys (belongsTo + hasMany + m2m) -----
659
690
  // Delegated to the SHARED introspection pipeline (introspect.ts) so SQLite
660
691
  // derives IDENTICAL relation names to the Postgres introspector for the
661
- // same logical schema — legacy-first naming, per-column disambiguation,
692
+ // same logical schema, legacy-first naming, per-column disambiguation,
662
693
  // collision resolution against scalar column fields, and the conservative
663
694
  // pure-junction manyToMany auto-detection included.
664
695
  const relationsByTable = (0, introspect_js_1.deriveEngineRelations)(tableNames, foreignKeys, pkByTable, columnsByTable);
@@ -740,7 +771,7 @@ function openSqliteDatabase(target, options) {
740
771
  db.exec('PRAGMA journal_mode = WAL');
741
772
  }
742
773
  catch {
743
- // Some filesystems (network mounts) reject WAL — fall back silently.
774
+ // Some filesystems (network mounts) reject WAL, fall back silently.
744
775
  }
745
776
  }
746
777
  return db;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * turbine-orm — Typed raw SQL (Turbine's answer to Prisma's TypedSQL)
2
+ * turbine-orm, Typed raw SQL (Turbine's answer to Prisma's TypedSQL)
3
3
  *
4
4
  * `client.raw()` returns untyped rows. This module adds a *typed* escape hatch:
5
5
  * a generic tagged template where the caller supplies the row shape, and the
@@ -9,13 +9,13 @@
9
9
  * Design goals & guarantees:
10
10
  *
11
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
12
+ * validated at runtime, exactly like Prisma's TypedSQL and the existing
13
13
  * `raw<T>()`. Postgres still returns whatever the SQL selects; the generic
14
14
  * is a convenience for autocomplete and downstream type-checking.
15
15
  *
16
16
  * 2. **Mandatory parameterization.** Only the *static* string segments of the
17
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
18
+ * becomes a `$N` placeholder and is passed in the params array, it is
19
19
  * impossible to string-concatenate a value into the query through this API.
20
20
  * This is the whole point of the tagged-template shape: the literal segments
21
21
  * are frozen by the compiler (`TemplateStringsArray`), and the only way to
@@ -69,7 +69,7 @@ export declare function buildTypedSql(strings: TemplateStringsArray, values: rea
69
69
  * `await`ed directly to get `T[]`, or refined via `.one()` / `.scalar()` first.
70
70
  *
71
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
72
+ * `one`, `scalar`). Each terminal method runs the query independently, this is
73
73
  * an escape hatch, not a cached query object, so don't call two terminals on
74
74
  * the same builder expecting a single round-trip; build a fresh template each
75
75
  * time (the common pattern is `await db.sql\`...\`` inline).
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /**
3
- * turbine-orm — Typed raw SQL (Turbine's answer to Prisma's TypedSQL)
3
+ * turbine-orm, Typed raw SQL (Turbine's answer to Prisma's TypedSQL)
4
4
  *
5
5
  * `client.raw()` returns untyped rows. This module adds a *typed* escape hatch:
6
6
  * a generic tagged template where the caller supplies the row shape, and the
@@ -10,13 +10,13 @@
10
10
  * Design goals & guarantees:
11
11
  *
12
12
  * 1. **Compile-time only types.** `T` is supplied by the caller and never
13
- * validated at runtime — exactly like Prisma's TypedSQL and the existing
13
+ * validated at runtime, exactly like Prisma's TypedSQL and the existing
14
14
  * `raw<T>()`. Postgres still returns whatever the SQL selects; the generic
15
15
  * is a convenience for autocomplete and downstream type-checking.
16
16
  *
17
17
  * 2. **Mandatory parameterization.** Only the *static* string segments of the
18
18
  * template literal ever reach the SQL text. Every interpolated `${value}`
19
- * becomes a `$N` placeholder and is passed in the params array — it is
19
+ * becomes a `$N` placeholder and is passed in the params array, it is
20
20
  * impossible to string-concatenate a value into the query through this API.
21
21
  * This is the whole point of the tagged-template shape: the literal segments
22
22
  * are frozen by the compiler (`TemplateStringsArray`), and the only way to
@@ -86,7 +86,7 @@ function buildTypedSql(strings, values, dialect = dialect_js_1.postgresDialect)
86
86
  * `await`ed directly to get `T[]`, or refined via `.one()` / `.scalar()` first.
87
87
  *
88
88
  * The query is executed lazily and exactly once per terminal call (`then`,
89
- * `one`, `scalar`). Each terminal method runs the query independently — this is
89
+ * `one`, `scalar`). Each terminal method runs the query independently, this is
90
90
  * an escape hatch, not a cached query object, so don't call two terminals on
91
91
  * the same builder expecting a single round-trip; build a fresh template each
92
92
  * time (the common pattern is `await db.sql\`...\`` inline).
@@ -118,7 +118,7 @@ class TypedSqlQuery {
118
118
  /**
119
119
  * PromiseLike implementation: `await db.sql<T>\`...\`` resolves to `T[]`.
120
120
  */
121
- // biome-ignore lint/suspicious/noThenProperty: intentional thenable — this IS the PromiseLike contract that makes `await db.sql\`...\`` resolve to rows
121
+ // biome-ignore lint/suspicious/noThenProperty: intentional thenable, this IS the PromiseLike contract that makes `await db.sql\`...\`` resolve to rows
122
122
  then(onfulfilled, onrejected) {
123
123
  return this.run().then(onfulfilled, onrejected);
124
124
  }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * turbine-orm CLI — Configuration file support
2
+ * turbine-orm CLI, Configuration file support
3
3
  *
4
4
  * Loads turbine.config.ts (or .js/.mjs) via dynamic import.
5
5
  * Falls back to CLI args and environment variables.
@@ -15,6 +15,15 @@ export interface TurbineCliConfig {
15
15
  include?: string[];
16
16
  /** Tables to exclude */
17
17
  exclude?: string[];
18
+ /**
19
+ * Rename derived relations, as `{ table: { derivedName: desiredName } }`.
20
+ *
21
+ * Relation names are composed by introspection (a database does not name its
22
+ * relationships), so two foreign keys to the same table produce names nobody
23
+ * would predict, and a port from another ORM that named them differently has
24
+ * to hand-edit every call site. A typo here is an error, not a silent no-op.
25
+ */
26
+ relationNames?: Record<string, Record<string, string>>;
18
27
  /**
19
28
  * Extension for the generated `index.ts` sibling imports (F3):
20
29
  * `'js'` (`./types.js`), `'none'` (`./types`), or `'auto'` (default:
@@ -69,7 +78,7 @@ export type TurbineConfig = TurbineCliConfig;
69
78
  * path rather than a Postgres schema name? `schema` is the Postgres namespace
70
79
  * to introspect (default `public`); the schema-builder file goes in `schemaFile`.
71
80
  * 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'`
81
+ * mis-set `schemaFile`, introspecting `WHERE table_schema = './turbine/schema.ts'`
73
82
  * silently matches zero tables. Used by `turbine generate` to fail loudly.
74
83
  */
75
84
  export declare function looksLikeSchemaFilePath(schema: string): boolean;
@@ -156,6 +165,8 @@ export interface ResolvedConfig {
156
165
  keepColumnNames: boolean;
157
166
  /** Resolved opt-out of the unique-FK → hasOne introspection flip (F2). */
158
167
  legacyToManyUniques: boolean;
168
+ /** Resolved derived-relation rename map, or undefined when none is declared. */
169
+ relationNames?: Record<string, Record<string, string>>;
159
170
  }
160
171
  export interface CliOverrides {
161
172
  url?: string;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * turbine-orm CLI — Configuration file support
2
+ * turbine-orm CLI, Configuration file support
3
3
  *
4
4
  * Loads turbine.config.ts (or .js/.mjs) via dynamic import.
5
5
  * Falls back to CLI args and environment variables.
@@ -12,7 +12,7 @@ import { pathToFileURL } from 'node:url';
12
12
  * path rather than a Postgres schema name? `schema` is the Postgres namespace
13
13
  * to introspect (default `public`); the schema-builder file goes in `schemaFile`.
14
14
  * A value containing a path separator or a JS/TS extension is almost certainly a
15
- * mis-set `schemaFile` — introspecting `WHERE table_schema = './turbine/schema.ts'`
15
+ * mis-set `schemaFile`, introspecting `WHERE table_schema = './turbine/schema.ts'`
16
16
  * silently matches zero tables. Used by `turbine generate` to fail loudly.
17
17
  */
18
18
  export function looksLikeSchemaFilePath(schema) {
@@ -158,6 +158,7 @@ export function resolveConfig(fileConfig, overrides) {
158
158
  out: overrides.out ?? fileConfig.out ?? './generated/turbine',
159
159
  schema: overrides.schema ?? fileConfig.schema ?? 'public',
160
160
  include: overrides.include ?? fileConfig.include ?? [],
161
+ relationNames: fileConfig.relationNames,
161
162
  exclude: overrides.exclude ?? fileConfig.exclude ?? [],
162
163
  migrationsDir: fileConfig.migrationsDir ?? './turbine/migrations',
163
164
  // `seedFile` is canonical (what the docs and `turbine init` use); `seed` is a
@@ -23,7 +23,7 @@
23
23
  */
24
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
25
  export interface DestructiveStatement {
26
- /** The offending SQL statement (trimmed, possibly long — display truncated) */
26
+ /** The offending SQL statement (trimmed, possibly long, display truncated) */
27
27
  statement: string;
28
28
  kind: DestructiveKind;
29
29
  /** Best-effort extracted object name (table, schema, or table.column) */
@@ -129,7 +129,7 @@ function isEscapeStringPrefix(sql, quoteAt) {
129
129
  /** Unquote a "quoted" identifier for display. */
130
130
  const ident = (raw) => (raw ?? '?').replace(/^"|"$/g, '');
131
131
  const IDENT = String.raw `("[^"]+"|[a-zA-Z_][\w$]*)(\.("[^"]+"|[a-zA-Z_][\w$]*))?`;
132
- /** Ordered rules — first match per statement wins. */
132
+ /** Ordered rules, first match per statement wins. */
133
133
  const RULES = [
134
134
  {
135
135
  kind: 'drop-table',
@@ -3,21 +3,21 @@
3
3
  * turbine-orm CLI
4
4
  *
5
5
  * Commands:
6
- * turbine init — Initialize a Turbine project
7
- * turbine generate | pull — Introspect database and generate TypeScript types
6
+ * turbine init , Initialize a Turbine project
7
+ * turbine generate | pull , Introspect database and generate TypeScript types
8
8
  * turbine migrate-from-prisma - Parse a schema.prisma and emit a Prisma->Turbine name map + report
9
9
  * turbine push - Apply schema-builder definitions to database (destructive ops gated)
10
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
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
17
  * turbine doctor - Cost-aware missing-FK-index triage (--fix, --json, --no-concurrently, --unused, --audit)
18
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)
19
+ * turbine mcp , Start read-only MCP server over JSON-RPC stdio
20
+ * turbine observe , Launch metrics dashboard (requires TURBINE_OBSERVE_URL)
21
21
  *
22
22
  * Usage:
23
23
  * DATABASE_URL=postgres://... npx turbine generate