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,5 +1,5 @@
1
1
  /**
2
- * turbine-orm — Schema introspection
2
+ * turbine-orm, Schema introspection
3
3
  *
4
4
  * Connects to a live Postgres database, reads information_schema + pg_catalog,
5
5
  * and produces a SchemaMetadata object describing every table, column, relation,
@@ -65,6 +65,15 @@ export interface IntrospectOptions {
65
65
  include?: string[];
66
66
  /** Tables to exclude (default: none). Applied after include. */
67
67
  exclude?: string[];
68
+ /**
69
+ * Rename derived relations, as `{ table: { derivedName: desiredName } }`.
70
+ *
71
+ * Relation names are composed by introspection (the database does not name
72
+ * relationships), so a port from another ORM that named them differently has
73
+ * to touch every call site. Declaring the mapping here makes it mechanical.
74
+ * A typo is an error, not a silent no-op: see {@link applyRelationRenames}.
75
+ */
76
+ relationNames?: Record<string, Record<string, string>>;
68
77
  /**
69
78
  * Also introspect **views** and **materialized views** as read-only
70
79
  * {@link TableMetadata} entries (`isView: true`). Off by default. Write
@@ -102,6 +111,23 @@ export interface IntrospectOptions {
102
111
  * SQL. PostgreSQL is driven by {@link introspectPostgresCatalog}.
103
112
  */
104
113
  export declare function introspect(options: IntrospectOptions): Promise<SchemaMetadata>;
114
+ /**
115
+ * Rename introspected relations, per table, from the name turbine derived to
116
+ * the name the caller wants.
117
+ *
118
+ * Relation names are DERIVED, not declared: the database has no name for a
119
+ * foreign key's relationship, so introspection composes one, and two foreign
120
+ * keys pointing at the same table produce composed names (`msgsBySender`,
121
+ * `msgsByRecipient`) that nobody would predict. A codebase migrating from
122
+ * another ORM already has names for these, chosen by different rules, so every
123
+ * call site has to be hand-edited. This map turns that into a mechanical
124
+ * mapping done once in the config.
125
+ *
126
+ * Renames are validated rather than best-effort: an unknown table or an
127
+ * unknown source relation is an ERROR, because silently ignoring a typo here
128
+ * means the call sites it was supposed to fix break at runtime instead.
129
+ */
130
+ export declare function applyRelationRenames(schema: SchemaMetadata, renames: Record<string, Record<string, string>>): SchemaMetadata;
105
131
  /**
106
132
  * PostgreSQL catalog introspector: reads information_schema + pg_catalog and
107
133
  * produces {@link SchemaMetadata}. This is the implementation wrapped by
@@ -148,7 +174,7 @@ export interface ForeignKeyEntry {
148
174
  }
149
175
  /**
150
176
  * Derive a belongsTo relation name from its FK column. Strips a trailing
151
- * `_id` (snake_case) or `Id` (camelCase column names — common in Prisma-ported
177
+ * `_id` (snake_case) or `Id` (camelCase column names, common in Prisma-ported
152
178
  * schemas where columns are quoted camelCase identifiers), then camelCases:
153
179
  * `current_version_id` and `currentVersionId` both yield `currentVersion`.
154
180
  * Stripping is what keeps the scalar FK field (`currentVersionId`) targetable
@@ -189,7 +215,7 @@ export declare function parsePlainUniqueIndexColumns(indexdef: string): string[]
189
215
  export declare function detectUniqueForeignKeySets(pkByTable: Map<string, string[]>, uniqueByTable: Map<string, string[][]>, indexesByTable: Map<string, IndexMetadata[]>): Map<string, string[][]>;
190
216
  /**
191
217
  * Build the belongsTo/hasMany relation maps for every table from its foreign
192
- * keys. Naming rules (LEGACY-FIRST — a relation name that previously worked at
218
+ * keys. Naming rules (LEGACY-FIRST, a relation name that previously worked at
193
219
  * runtime must never change out from under a regenerating app):
194
220
  *
195
221
  * 1. First compute the historical derivation exactly as it shipped before
@@ -197,7 +223,7 @@ export declare function detectUniqueForeignKeySets(pkByTable: Map<string, string
197
223
  * suffix (`snakeToCamel(col.replace(/_id$/, ''))` when several FKs point
198
224
  * at the same target, else the singularized target table), and hasMany is
199
225
  * `snakeToCamel(`${source}_by_${strippedColumn}`)` (else the source
200
- * table). If that legacy name is free, KEEP IT — even when it looks odd
226
+ * table). If that legacy name is free, KEEP IT, even when it looks odd
201
227
  * (`blogPostsByAuthorId`, `postsBy_Author`): those names were collision-
202
228
  * free and worked, so regenerating must not rename them.
203
229
  * 2. If the legacy name collides ONLY with a scalar column whose tsType is
@@ -206,17 +232,17 @@ export declare function detectUniqueForeignKeySets(pkByTable: Map<string, string
206
232
  * relation payload; generate.ts's typeSafeRelations omits the relation
207
233
  * from the type layer).
208
234
  * 3. On a genuine collision (concrete-typed column shadow, or a previously
209
- * assigned relation), fall back to the modern derivation — the `_id`/`Id`
235
+ * assigned relation), fall back to the modern derivation, the `_id`/`Id`
210
236
  * case-insensitive strip of {@link relationNameFromColumn} plus the
211
- * `By`-composed reverse name — which fixes the camelCase-FK shadowing
237
+ * `By`-composed reverse name, which fixes the camelCase-FK shadowing
212
238
  * shapes that were actually BROKEN before (relation name === scalar FK
213
239
  * field → unusable types).
214
240
  * 4. Last resort: deterministic `Rel`/`Rel2` suffix + warning.
215
241
  *
216
- * @param columnFieldsByTable camelCase column *fields* per table — used to
242
+ * @param columnFieldsByTable camelCase column *fields* per table, used to
217
243
  * guarantee relations never shadow concrete-typed scalar columns.
218
244
  * @param unknownTypedFieldsByTable subset of the column fields whose tsType is
219
- * `unknown` (json/jsonb) — legacy shadows of these are preserved (rule 2).
245
+ * `unknown` (json/jsonb), legacy shadows of these are preserved (rule 2).
220
246
  * @param uniqueSetsByTable when provided (F2), the child-table column sets that
221
247
  * guarantee at-most-one row (PK + unique constraints + plain unique indexes,
222
248
  * from {@link detectUniqueForeignKeySets}). A reverse relation whose FK column
@@ -262,7 +288,7 @@ export declare function addAutoManyToManyRelations(tableNames: Iterable<string>,
262
288
  * MSSQL): filters the FK list to the introspected table set, seeds the
263
289
  * taken-name / json-shadow maps from the engine's column metadata, and runs
264
290
  * the SAME `buildRelationsFromForeignKeys` + `addAutoManyToManyRelations`
265
- * pipeline as the Postgres introspector — so every engine derives identical
291
+ * pipeline as the Postgres introspector, so every engine derives identical
266
292
  * relation names for the same logical schema (the engines previously carried
267
293
  * stale copies of a retired naming scheme).
268
294
  */
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /**
3
- * turbine-orm — Schema introspection
3
+ * turbine-orm, Schema introspection
4
4
  *
5
5
  * Connects to a live Postgres database, reads information_schema + pg_catalog,
6
6
  * and produces a SchemaMetadata object describing every table, column, relation,
@@ -17,6 +17,7 @@ exports.pgConfActionToReferential = pgConfActionToReferential;
17
17
  exports.applyTableFilters = applyTableFilters;
18
18
  exports.defaultExcludedTablesPresent = defaultExcludedTablesPresent;
19
19
  exports.introspect = introspect;
20
+ exports.applyRelationRenames = applyRelationRenames;
20
21
  exports.introspectPostgresCatalog = introspectPostgresCatalog;
21
22
  exports.parseIndexColumns = parseIndexColumns;
22
23
  exports.indexHasWhere = indexHasWhere;
@@ -30,6 +31,7 @@ exports.addAutoManyToManyRelations = addAutoManyToManyRelations;
30
31
  exports.deriveEngineRelations = deriveEngineRelations;
31
32
  const pg_1 = __importDefault(require("pg"));
32
33
  const dialect_js_1 = require("./dialect.js");
34
+ const errors_js_1 = require("./errors.js");
33
35
  const schema_js_1 = require("./schema.js");
34
36
  /**
35
37
  * Map a `pg_constraint.confdeltype` / `confupdtype` character to a
@@ -146,21 +148,21 @@ const SQL_CHECKS = `
146
148
  WHERE con.contype = 'c'
147
149
  AND n.nspname = $1
148
150
  `;
149
- // Views (relkind 'v') — column metadata comes free from information_schema.columns.
151
+ // Views (relkind 'v'), column metadata comes free from information_schema.columns.
150
152
  const SQL_VIEWS = `
151
153
  SELECT table_name
152
154
  FROM information_schema.views
153
155
  WHERE table_schema = $1
154
156
  ORDER BY table_name
155
157
  `;
156
- // Materialized views (relkind 'm') — NOT in information_schema; read from pg_catalog.
158
+ // Materialized views (relkind 'm'), NOT in information_schema; read from pg_catalog.
157
159
  const SQL_MATVIEWS = `
158
160
  SELECT matviewname AS table_name
159
161
  FROM pg_matviews
160
162
  WHERE schemaname = $1
161
163
  ORDER BY matviewname
162
164
  `;
163
- // Materialized-view columns — information_schema.columns omits matviews, so pull
165
+ // Materialized-view columns, information_schema.columns omits matviews, so pull
164
166
  // them from pg_attribute. Aliased to mirror SQL_COLUMNS so the same row-mapping
165
167
  // applies (array types surface as data_type 'ARRAY' + a '_'-prefixed udt_name).
166
168
  const SQL_MATVIEW_COLUMNS = `
@@ -262,11 +264,60 @@ function defaultExcludedTablesPresent(names, options = {}) {
262
264
  async function introspect(options) {
263
265
  const dialect = options.dialect ?? dialect_js_1.postgresDialect;
264
266
  const introspector = dialect.introspector;
265
- if (introspector) {
266
- return introspector.introspect(options);
267
+ const schema = introspector
268
+ ? await introspector.introspect(options)
269
+ : // Dialects without an introspector fall back to the Postgres catalog reader.
270
+ await introspectPostgresCatalog(options);
271
+ // Applied here rather than inside each introspector so every engine gets it.
272
+ return options.relationNames ? applyRelationRenames(schema, options.relationNames) : schema;
273
+ }
274
+ /**
275
+ * Rename introspected relations, per table, from the name turbine derived to
276
+ * the name the caller wants.
277
+ *
278
+ * Relation names are DERIVED, not declared: the database has no name for a
279
+ * foreign key's relationship, so introspection composes one, and two foreign
280
+ * keys pointing at the same table produce composed names (`msgsBySender`,
281
+ * `msgsByRecipient`) that nobody would predict. A codebase migrating from
282
+ * another ORM already has names for these, chosen by different rules, so every
283
+ * call site has to be hand-edited. This map turns that into a mechanical
284
+ * mapping done once in the config.
285
+ *
286
+ * Renames are validated rather than best-effort: an unknown table or an
287
+ * unknown source relation is an ERROR, because silently ignoring a typo here
288
+ * means the call sites it was supposed to fix break at runtime instead.
289
+ */
290
+ function applyRelationRenames(schema, renames) {
291
+ const tables = { ...schema.tables };
292
+ for (const [table, mapping] of Object.entries(renames)) {
293
+ const meta = tables[table];
294
+ if (!meta) {
295
+ throw new errors_js_1.ValidationError(`[turbine] relationNames: unknown table "${table}". Known tables: ${Object.keys(tables).join(', ')}.`);
296
+ }
297
+ const relations = { ...meta.relations };
298
+ const columnFields = new Set(Object.values(meta.columnMap));
299
+ for (const [from, to] of Object.entries(mapping)) {
300
+ if (!Object.hasOwn(relations, from)) {
301
+ throw new errors_js_1.ValidationError(`[turbine] relationNames: table "${table}" has no relation "${from}". ` +
302
+ `Derived relations: ${Object.keys(relations).join(', ') || '(none)'}.`);
303
+ }
304
+ if (from === to)
305
+ continue;
306
+ if (Object.hasOwn(relations, to)) {
307
+ throw new errors_js_1.ValidationError(`[turbine] relationNames: cannot rename "${table}.${from}" to "${to}", that relation already exists.`);
308
+ }
309
+ if (columnFields.has(to)) {
310
+ throw new errors_js_1.ValidationError(`[turbine] relationNames: cannot rename "${table}.${from}" to "${to}", a column on "${table}" ` +
311
+ `already uses that field name, and a relation must never shadow a column.`);
312
+ }
313
+ const def = relations[from];
314
+ delete relations[from];
315
+ // `RelationDef.name` is the relation's own identity, so it moves with it.
316
+ relations[to] = { ...def, name: to };
317
+ }
318
+ tables[table] = { ...meta, relations };
267
319
  }
268
- // Dialects without an introspector fall back to the Postgres catalog reader.
269
- return introspectPostgresCatalog(options);
320
+ return { ...schema, tables };
270
321
  }
271
322
  /**
272
323
  * PostgreSQL catalog introspector: reads information_schema + pg_catalog and
@@ -360,7 +411,7 @@ async function introspectPostgresCatalog(options) {
360
411
  // (gen_random_uuid(), now()), which Turbine must still synthesize.
361
412
  isGenerated: (typeof row.column_default === 'string' && row.column_default.includes('nextval(')) ||
362
413
  row.is_identity === 'YES',
363
- // GENERATED ALWAYS AS (expr) STORED — computed by the database, never
414
+ // GENERATED ALWAYS AS (expr) STORED, computed by the database, never
364
415
  // writable. Distinct from isGenerated (serial/identity, which a client
365
416
  // MAY override). is_generated is 'ALWAYS' for STORED columns, else 'NEVER'.
366
417
  isGeneratedStored: row.is_generated === 'ALWAYS',
@@ -371,7 +422,7 @@ async function introspectPostgresCatalog(options) {
371
422
  maxLength: row.character_maximum_length ?? undefined,
372
423
  // Record the type's schema ONLY when it lives outside the introspected
373
424
  // schema (and isn't a pg_catalog builtin). A same-named enum in another
374
- // schema must NOT get this schema's `::"enum"` cast — search_path would
425
+ // schema must NOT get this schema's `::"enum"` cast, search_path would
375
426
  // resolve the cast to the wrong type (see enumTypeForColumn). Omitting
376
427
  // it for the common case keeps generated metadata byte-identical.
377
428
  ...(typeof row.udt_schema === 'string' && row.udt_schema !== schema && row.udt_schema !== 'pg_catalog'
@@ -475,13 +526,13 @@ async function introspectPostgresCatalog(options) {
475
526
  // per-FK-column when several FKs point at the same target, and every name
476
527
  // is collision-checked against the table's scalar column fields so a
477
528
  // relation can never shadow a column (which generated unsound types and
478
- // made both surfaces unusable — dogfood T-4).
529
+ // made both surfaces unusable, dogfood T-4).
479
530
  const columnFieldsByTable = new Map();
480
531
  const unknownTypedFieldsByTable = new Map();
481
532
  for (const [tbl, cols] of columnsByTable) {
482
533
  columnFieldsByTable.set(tbl, new Set(cols.map((c) => c.field)));
483
534
  // Enum-typed columns also report tsType 'unknown' here, but generate.ts
484
- // gives them a concrete union type — a shadow of one was type-broken on
535
+ // gives them a concrete union type, a shadow of one was type-broken on
485
536
  // main, so only genuine json/jsonb columns qualify as historical shadows.
486
537
  unknownTypedFieldsByTable.set(tbl, new Set(cols.filter((c) => isUnknownTsType(c.tsType) && !Object.hasOwn(enums, c.pgType)).map((c) => c.field)));
487
538
  }
@@ -503,14 +554,14 @@ async function introspectPostgresCatalog(options) {
503
554
  // 1. J's primary key is exactly two columns.
504
555
  // 2. J has exactly two FKs, each single-column.
505
556
  // 3. Each FK's source column is one of J's two PK columns (the PK *is* the
506
- // two FK columns — no surrogate PK, no extra identity).
557
+ // two FK columns, no surrogate PK, no extra identity).
507
558
  // 4. The two FKs target two DISTINCT tables (A and B).
508
559
  // 5. J has no columns beyond those two FK/PK columns (no payload columns
509
560
  // like `grade` or `created_at`).
510
561
  //
511
562
  // For such a J linking A and B we ADD a `manyToMany` relation on A → B and
512
563
  // symmetrically on B → A, both routed `through` J. The existing belongsTo /
513
- // hasMany relations derived from J's FKs are left untouched — this block
564
+ // hasMany relations derived from J's FKs are left untouched, this block
514
565
  // never removes or renames anything. Naming/collision handling lives in the
515
566
  // shared addAutoManyToManyRelations helper.
516
567
  //
@@ -645,7 +696,7 @@ function stripCheckWrapper(def) {
645
696
  }
646
697
  /**
647
698
  * Derive a belongsTo relation name from its FK column. Strips a trailing
648
- * `_id` (snake_case) or `Id` (camelCase column names — common in Prisma-ported
699
+ * `_id` (snake_case) or `Id` (camelCase column names, common in Prisma-ported
649
700
  * schemas where columns are quoted camelCase identifiers), then camelCases:
650
701
  * `current_version_id` and `currentVersionId` both yield `currentVersion`.
651
702
  * Stripping is what keeps the scalar FK field (`currentVersionId`) targetable
@@ -764,7 +815,7 @@ function detectUniqueForeignKeySets(pkByTable, uniqueByTable, indexesByTable) {
764
815
  /**
765
816
  * Resolve a derived relation name against the names already taken on the
766
817
  * table (scalar column fields + previously assigned relations). On collision,
767
- * applies a deterministic `Rel` / `Rel2` / `Rel3`… suffix and warns — a
818
+ * applies a deterministic `Rel` / `Rel2` / `Rel3`… suffix and warns, a
768
819
  * colliding name would otherwise shadow a column field and generate types
769
820
  * that fail `tsc --strict` (TS2430/TS2322).
770
821
  */
@@ -774,12 +825,12 @@ function resolveRelationNameCollision(candidate, taken, table, source) {
774
825
  let name = `${candidate}Rel`;
775
826
  for (let i = 2; taken.has(name); i++)
776
827
  name = `${candidate}Rel${i}`;
777
- console.warn(`[turbine] Relation name "${candidate}" on table "${table}" (from ${source}) collides with an existing column or relation — using "${name}" instead.`);
828
+ console.warn(`[turbine] Relation name "${candidate}" on table "${table}" (from ${source}) collides with an existing column or relation, using "${name}" instead.`);
778
829
  return name;
779
830
  }
780
831
  /**
781
832
  * Build the belongsTo/hasMany relation maps for every table from its foreign
782
- * keys. Naming rules (LEGACY-FIRST — a relation name that previously worked at
833
+ * keys. Naming rules (LEGACY-FIRST, a relation name that previously worked at
783
834
  * runtime must never change out from under a regenerating app):
784
835
  *
785
836
  * 1. First compute the historical derivation exactly as it shipped before
@@ -787,7 +838,7 @@ function resolveRelationNameCollision(candidate, taken, table, source) {
787
838
  * suffix (`snakeToCamel(col.replace(/_id$/, ''))` when several FKs point
788
839
  * at the same target, else the singularized target table), and hasMany is
789
840
  * `snakeToCamel(`${source}_by_${strippedColumn}`)` (else the source
790
- * table). If that legacy name is free, KEEP IT — even when it looks odd
841
+ * table). If that legacy name is free, KEEP IT, even when it looks odd
791
842
  * (`blogPostsByAuthorId`, `postsBy_Author`): those names were collision-
792
843
  * free and worked, so regenerating must not rename them.
793
844
  * 2. If the legacy name collides ONLY with a scalar column whose tsType is
@@ -796,17 +847,17 @@ function resolveRelationNameCollision(candidate, taken, table, source) {
796
847
  * relation payload; generate.ts's typeSafeRelations omits the relation
797
848
  * from the type layer).
798
849
  * 3. On a genuine collision (concrete-typed column shadow, or a previously
799
- * assigned relation), fall back to the modern derivation — the `_id`/`Id`
850
+ * assigned relation), fall back to the modern derivation, the `_id`/`Id`
800
851
  * case-insensitive strip of {@link relationNameFromColumn} plus the
801
- * `By`-composed reverse name — which fixes the camelCase-FK shadowing
852
+ * `By`-composed reverse name, which fixes the camelCase-FK shadowing
802
853
  * shapes that were actually BROKEN before (relation name === scalar FK
803
854
  * field → unusable types).
804
855
  * 4. Last resort: deterministic `Rel`/`Rel2` suffix + warning.
805
856
  *
806
- * @param columnFieldsByTable camelCase column *fields* per table — used to
857
+ * @param columnFieldsByTable camelCase column *fields* per table, used to
807
858
  * guarantee relations never shadow concrete-typed scalar columns.
808
859
  * @param unknownTypedFieldsByTable subset of the column fields whose tsType is
809
- * `unknown` (json/jsonb) — legacy shadows of these are preserved (rule 2).
860
+ * `unknown` (json/jsonb), legacy shadows of these are preserved (rule 2).
810
861
  * @param uniqueSetsByTable when provided (F2), the child-table column sets that
811
862
  * guarantee at-most-one row (PK + unique constraints + plain unique indexes,
812
863
  * from {@link detectUniqueForeignKeySets}). A reverse relation whose FK column
@@ -835,7 +886,7 @@ function buildRelationsFromForeignKeys(foreignKeys, columnFieldsByTable, fkActio
835
886
  }
836
887
  return taken;
837
888
  };
838
- // Relation names actually assigned so far (as opposed to column fields) —
889
+ // Relation names actually assigned so far (as opposed to column fields) -
839
890
  // needed to tell "collides only with a column" apart from "collides with an
840
891
  // already-assigned relation" for the legacy-shadow-preserving rule.
841
892
  const assignedByTable = new Map();
@@ -847,17 +898,17 @@ function buildRelationsFromForeignKeys(foreignKeys, columnFieldsByTable, fkActio
847
898
  }
848
899
  return assigned;
849
900
  };
850
- /** Legacy-first name resolution — see the naming rules in the JSDoc above. */
901
+ /** Legacy-first name resolution, see the naming rules in the JSDoc above. */
851
902
  const resolveName = (legacy, modern, table, source) => {
852
903
  const taken = takenFor(table);
853
904
  if (!taken.has(legacy))
854
905
  return legacy;
855
906
  // Historical json/jsonb shadow: previously worked at runtime AND compiled
856
- // (tsType `unknown` absorbs the relation payload). Keep the name, warn —
907
+ // (tsType `unknown` absorbs the relation payload). Keep the name, warn -
857
908
  // typeSafeRelations() keeps the generated type layer sound.
858
909
  if (!assignedFor(table).has(legacy) && unknownTypedFieldsByTable?.get(table)?.has(legacy)) {
859
910
  console.warn(`[turbine] Relation "${legacy}" on table "${table}" (from ${source}) shadows the json/jsonb column ` +
860
- `"${legacy}" — keeping the historical name for runtime compatibility; the relation is omitted from ` +
911
+ `"${legacy}", keeping the historical name for runtime compatibility; the relation is omitted from ` +
861
912
  `the generated types. Rename the column to expose it.`);
862
913
  return legacy;
863
914
  }
@@ -873,7 +924,7 @@ function buildRelationsFromForeignKeys(foreignKeys, columnFieldsByTable, fkActio
873
924
  // For multi-column (composite) FKs, use array form.
874
925
  const foreignKey = singleColumn ? fk.sourceColumns[0] : fk.sourceColumns;
875
926
  const referenceKey = fk.targetColumns.length === 1 ? fk.targetColumns[0] : fk.targetColumns;
876
- // Composite FKs have no single column to derive from — fall back to the
927
+ // Composite FKs have no single column to derive from, fall back to the
877
928
  // constraint name (with the usual fk_/-_fkey affixes stripped).
878
929
  const constraintBase = fk.constraintName.replace(/^fk_/, '').replace(/_fkey$/, '');
879
930
  // --- belongsTo on the source (child) table ---
@@ -977,7 +1028,7 @@ function buildRelationsFromForeignKeys(foreignKeys, columnFieldsByTable, fkActio
977
1028
  */
978
1029
  function addAutoManyToManyRelations(tableNames, foreignKeys, pkByTable, columnNamesByTable, relationsByTable, columnFieldsByTable, unknownTypedFieldsByTable, uniqueIndexColsByTable) {
979
1030
  for (const tableName of tableNames) {
980
- // FKs whose source is this table — both must be single-column.
1031
+ // FKs whose source is this table, both must be single-column.
981
1032
  const tableFks = foreignKeys.filter((fk) => fk.sourceTable === tableName);
982
1033
  if (tableFks.length !== 2)
983
1034
  continue;
@@ -1029,9 +1080,9 @@ function addAutoManyToManyRelations(tableNames, foreignKeys, pkByTable, columnNa
1029
1080
  const columnFields = columnFieldsByTable?.get(sourceTbl);
1030
1081
  if (columnFields?.has(relName)) {
1031
1082
  if (unknownTypedFieldsByTable?.get(sourceTbl)?.has(relName)) {
1032
- // Historical json/jsonb shadow — worked at runtime, compiled fine.
1083
+ // Historical json/jsonb shadow, worked at runtime, compiled fine.
1033
1084
  console.warn(`[turbine] Relation "${relName}" on table "${sourceTbl}" (junction ${tableName}) shadows the ` +
1034
- `json/jsonb column "${relName}" — keeping the historical name for runtime compatibility; ` +
1085
+ `json/jsonb column "${relName}", keeping the historical name for runtime compatibility; ` +
1035
1086
  `the relation is omitted from the generated types.`);
1036
1087
  }
1037
1088
  else {
@@ -1065,7 +1116,7 @@ function addAutoManyToManyRelations(tableNames, foreignKeys, pkByTable, columnNa
1065
1116
  * MSSQL): filters the FK list to the introspected table set, seeds the
1066
1117
  * taken-name / json-shadow maps from the engine's column metadata, and runs
1067
1118
  * the SAME `buildRelationsFromForeignKeys` + `addAutoManyToManyRelations`
1068
- * pipeline as the Postgres introspector — so every engine derives identical
1119
+ * pipeline as the Postgres introspector, so every engine derives identical
1069
1120
  * relation names for the same logical schema (the engines previously carried
1070
1121
  * stale copies of a retired naming scheme).
1071
1122
  */
@@ -1,8 +1,8 @@
1
1
  /**
2
- * turbine-orm/mssql — Microsoft SQL Server engine (driver-injected, optional peer)
2
+ * turbine-orm/mssql, Microsoft SQL Server engine (driver-injected, optional peer)
3
3
  *
4
4
  * Binds Turbine to SQL Server 2016+ via the `mssql` driver (which wraps
5
- * `tedious`). `mssql` is **not** a root dependency — it is an **optional peer**:
5
+ * `tedious`). `mssql` is **not** a root dependency, it is an **optional peer**:
6
6
  * `npm i turbine-orm` pulls nothing extra, and only consumers who
7
7
  * `import 'turbine-orm/mssql'` install `mssql` themselves. The factory loads it
8
8
  * through a dynamic `import('mssql')` so importing this module never crashes when
@@ -33,14 +33,14 @@
33
33
  * real JSON instead of being escaped as a string. `INCLUDE_NULL_VALUES`
34
34
  * keeps NULL columns present (matching PostgreSQL `json_build_object`).
35
35
  * 3. **No `LIMIT`.** Paging is `ORDER BY … OFFSET n ROWS FETCH NEXT m ROWS ONLY`,
36
- * which requires an ORDER BY — a stable `ORDER BY (SELECT NULL)` is injected
36
+ * which requires an ORDER BY, a stable `ORDER BY (SELECT NULL)` is injected
37
37
  * when the query has none (`Dialect.buildLimitOffset`).
38
38
  *
39
39
  * ## Named `@pN` placeholders (no positional `?`)
40
40
  *
41
41
  * `mssqlDialect.paramPlaceholder = (i) => '@p' + i`. The driver shim binds via
42
42
  * `request.input('p' + i, value)`, so binding is by NAME and independent of where
43
- * each placeholder lands in the SQL text — exactly the guarantee PostgreSQL's
43
+ * each placeholder lands in the SQL text, exactly the guarantee PostgreSQL's
44
44
  * numbered `$N` gives. (SQL Server is naturally named-param friendly, sidestepping
45
45
  * the positional-`?` mis-bind bug the SQLite/MySQL phases hit.)
46
46
  *
@@ -48,12 +48,12 @@
48
48
  *
49
49
  * - **Single query nested relations preserved** via `FOR JSON PATH` (SQL Server
50
50
  * 2016+). Ordered/limited to-many uses `ORDER BY … OFFSET/FETCH` inside the FOR
51
- * JSON subquery (no inner-subquery rewrite needed — FOR JSON aggregates AFTER
51
+ * JSON subquery (no inner-subquery rewrite needed, FOR JSON aggregates AFTER
52
52
  * the row selection).
53
53
  * - **Result strategy `'output'`:** create/update/delete/upsert return their rows
54
54
  * from the same statement. `createMany` returns the inserted rows via
55
55
  * `OUTPUT INSERTED.*` on the multi-row VALUES insert (≤ 1000 rows / 2100 params
56
- * per statement — exceeding either throws a clear `ValidationError`; chunk
56
+ * per statement, exceeding either throws a clear `ValidationError`; chunk
57
57
  * yourself or use single `create`s).
58
58
  * - **MERGE concurrency caveat:** `MERGE` is the upsert primitive; under high
59
59
  * concurrency a `MERGE` can still race (it is NOT a substitute for a unique
@@ -62,20 +62,20 @@
62
62
  * loser of a race.
63
63
  * - **Unsupported (throw `UnsupportedFeatureError`):** pgvector distance ops,
64
64
  * LISTEN/NOTIFY (`$listen`/`$notify`), RLS `sessionContext` (sp_set_session_context
65
- * exists but is connection-scoped, not transaction-local, so it is not wired —
65
+ * exists but is connection-scoped, not transaction-local, so it is not wired -
66
66
  * throws rather than silently leaking context across pooled connections).
67
67
  * - **Advisory-lock migration locking** is available in principle via
68
68
  * `sp_getapplock`/`sp_releaseapplock` (`supportsAdvisoryLock = true`); the
69
69
  * migrate CLI is still PostgreSQL-only, so this flag documents intent for a
70
70
  * future adapter.
71
- * - **Case-insensitive matching** uses `LOWER(col) LIKE LOWER(ref)` — deterministic
71
+ * - **Case-insensitive matching** uses `LOWER(col) LIKE LOWER(ref)`, deterministic
72
72
  * regardless of the column's collation (note this can defeat an index unless a
73
73
  * computed/persisted `LOWER()` index exists).
74
74
  * - **bignum:** the shim applies the same safe-int policy Turbine uses for Postgres
75
75
  * `int8` (number when it fits in 2^53, decimal string otherwise) WITHOUT mutating
76
76
  * any global driver state. `DECIMAL`/`NUMERIC`/`MONEY` come back as strings;
77
77
  * `BIT` binds/returns booleans.
78
- * - **`DISTINCT ON`** is PostgreSQL-only and is not translated — avoid `distinct`
78
+ * - **`DISTINCT ON`** is PostgreSQL-only and is not translated, avoid `distinct`
79
79
  * on SQL Server.
80
80
  *
81
81
  * ## Example
@@ -146,7 +146,7 @@ type QueryArg = string | {
146
146
  * physical connection.
147
147
  */
148
148
  export declare class MssqlPool implements PgCompatPool {
149
- /** The underlying `mssql` ConnectionPool — exposed as an escape hatch (seed / DDL / advanced ops). */
149
+ /** The underlying `mssql` ConnectionPool, exposed as an escape hatch (seed / DDL / advanced ops). */
150
150
  readonly pool: MssqlConnectionPool;
151
151
  private readonly sqlNS;
152
152
  private closed;
@@ -215,7 +215,7 @@ export interface TurbineMssqlOptions extends Pick<TurbineConfig, 'logging' | 'de
215
215
  * Pass one of:
216
216
  * - a connection string (`'mssql://sa:pass@host:1433/db'`),
217
217
  * - an `mssql` config object (`{ server, user, password, database, options }`),
218
- * - an existing `MssqlPool` (injection — you own its lifecycle, `disconnect()` is
218
+ * - an existing `MssqlPool` (injection, you own its lifecycle, `disconnect()` is
219
219
  * a no-op).
220
220
  *
221
221
  * When Turbine builds the pool (string/config), it probes