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,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,
@@ -9,6 +9,7 @@
9
9
  */
10
10
  import pg from 'pg';
11
11
  import { postgresDialect } from './dialect.js';
12
+ import { ValidationError } from './errors.js';
12
13
  import { isDateType, pgTypeToTs, singularize, snakeToCamel, } from './schema.js';
13
14
  /**
14
15
  * Map a `pg_constraint.confdeltype` / `confupdtype` character to a
@@ -125,21 +126,21 @@ const SQL_CHECKS = `
125
126
  WHERE con.contype = 'c'
126
127
  AND n.nspname = $1
127
128
  `;
128
- // Views (relkind 'v') — column metadata comes free from information_schema.columns.
129
+ // Views (relkind 'v'), column metadata comes free from information_schema.columns.
129
130
  const SQL_VIEWS = `
130
131
  SELECT table_name
131
132
  FROM information_schema.views
132
133
  WHERE table_schema = $1
133
134
  ORDER BY table_name
134
135
  `;
135
- // Materialized views (relkind 'm') — NOT in information_schema; read from pg_catalog.
136
+ // Materialized views (relkind 'm'), NOT in information_schema; read from pg_catalog.
136
137
  const SQL_MATVIEWS = `
137
138
  SELECT matviewname AS table_name
138
139
  FROM pg_matviews
139
140
  WHERE schemaname = $1
140
141
  ORDER BY matviewname
141
142
  `;
142
- // Materialized-view columns — information_schema.columns omits matviews, so pull
143
+ // Materialized-view columns, information_schema.columns omits matviews, so pull
143
144
  // them from pg_attribute. Aliased to mirror SQL_COLUMNS so the same row-mapping
144
145
  // applies (array types surface as data_type 'ARRAY' + a '_'-prefixed udt_name).
145
146
  const SQL_MATVIEW_COLUMNS = `
@@ -241,11 +242,60 @@ export function defaultExcludedTablesPresent(names, options = {}) {
241
242
  export async function introspect(options) {
242
243
  const dialect = options.dialect ?? postgresDialect;
243
244
  const introspector = dialect.introspector;
244
- if (introspector) {
245
- return introspector.introspect(options);
245
+ const schema = introspector
246
+ ? await introspector.introspect(options)
247
+ : // Dialects without an introspector fall back to the Postgres catalog reader.
248
+ await introspectPostgresCatalog(options);
249
+ // Applied here rather than inside each introspector so every engine gets it.
250
+ return options.relationNames ? applyRelationRenames(schema, options.relationNames) : schema;
251
+ }
252
+ /**
253
+ * Rename introspected relations, per table, from the name turbine derived to
254
+ * the name the caller wants.
255
+ *
256
+ * Relation names are DERIVED, not declared: the database has no name for a
257
+ * foreign key's relationship, so introspection composes one, and two foreign
258
+ * keys pointing at the same table produce composed names (`msgsBySender`,
259
+ * `msgsByRecipient`) that nobody would predict. A codebase migrating from
260
+ * another ORM already has names for these, chosen by different rules, so every
261
+ * call site has to be hand-edited. This map turns that into a mechanical
262
+ * mapping done once in the config.
263
+ *
264
+ * Renames are validated rather than best-effort: an unknown table or an
265
+ * unknown source relation is an ERROR, because silently ignoring a typo here
266
+ * means the call sites it was supposed to fix break at runtime instead.
267
+ */
268
+ export function applyRelationRenames(schema, renames) {
269
+ const tables = { ...schema.tables };
270
+ for (const [table, mapping] of Object.entries(renames)) {
271
+ const meta = tables[table];
272
+ if (!meta) {
273
+ throw new ValidationError(`[turbine] relationNames: unknown table "${table}". Known tables: ${Object.keys(tables).join(', ')}.`);
274
+ }
275
+ const relations = { ...meta.relations };
276
+ const columnFields = new Set(Object.values(meta.columnMap));
277
+ for (const [from, to] of Object.entries(mapping)) {
278
+ if (!Object.hasOwn(relations, from)) {
279
+ throw new ValidationError(`[turbine] relationNames: table "${table}" has no relation "${from}". ` +
280
+ `Derived relations: ${Object.keys(relations).join(', ') || '(none)'}.`);
281
+ }
282
+ if (from === to)
283
+ continue;
284
+ if (Object.hasOwn(relations, to)) {
285
+ throw new ValidationError(`[turbine] relationNames: cannot rename "${table}.${from}" to "${to}", that relation already exists.`);
286
+ }
287
+ if (columnFields.has(to)) {
288
+ throw new ValidationError(`[turbine] relationNames: cannot rename "${table}.${from}" to "${to}", a column on "${table}" ` +
289
+ `already uses that field name, and a relation must never shadow a column.`);
290
+ }
291
+ const def = relations[from];
292
+ delete relations[from];
293
+ // `RelationDef.name` is the relation's own identity, so it moves with it.
294
+ relations[to] = { ...def, name: to };
295
+ }
296
+ tables[table] = { ...meta, relations };
246
297
  }
247
- // Dialects without an introspector fall back to the Postgres catalog reader.
248
- return introspectPostgresCatalog(options);
298
+ return { ...schema, tables };
249
299
  }
250
300
  /**
251
301
  * PostgreSQL catalog introspector: reads information_schema + pg_catalog and
@@ -339,7 +389,7 @@ export async function introspectPostgresCatalog(options) {
339
389
  // (gen_random_uuid(), now()), which Turbine must still synthesize.
340
390
  isGenerated: (typeof row.column_default === 'string' && row.column_default.includes('nextval(')) ||
341
391
  row.is_identity === 'YES',
342
- // GENERATED ALWAYS AS (expr) STORED — computed by the database, never
392
+ // GENERATED ALWAYS AS (expr) STORED, computed by the database, never
343
393
  // writable. Distinct from isGenerated (serial/identity, which a client
344
394
  // MAY override). is_generated is 'ALWAYS' for STORED columns, else 'NEVER'.
345
395
  isGeneratedStored: row.is_generated === 'ALWAYS',
@@ -350,7 +400,7 @@ export async function introspectPostgresCatalog(options) {
350
400
  maxLength: row.character_maximum_length ?? undefined,
351
401
  // Record the type's schema ONLY when it lives outside the introspected
352
402
  // schema (and isn't a pg_catalog builtin). A same-named enum in another
353
- // schema must NOT get this schema's `::"enum"` cast — search_path would
403
+ // schema must NOT get this schema's `::"enum"` cast, search_path would
354
404
  // resolve the cast to the wrong type (see enumTypeForColumn). Omitting
355
405
  // it for the common case keeps generated metadata byte-identical.
356
406
  ...(typeof row.udt_schema === 'string' && row.udt_schema !== schema && row.udt_schema !== 'pg_catalog'
@@ -454,13 +504,13 @@ export async function introspectPostgresCatalog(options) {
454
504
  // per-FK-column when several FKs point at the same target, and every name
455
505
  // is collision-checked against the table's scalar column fields so a
456
506
  // relation can never shadow a column (which generated unsound types and
457
- // made both surfaces unusable — dogfood T-4).
507
+ // made both surfaces unusable, dogfood T-4).
458
508
  const columnFieldsByTable = new Map();
459
509
  const unknownTypedFieldsByTable = new Map();
460
510
  for (const [tbl, cols] of columnsByTable) {
461
511
  columnFieldsByTable.set(tbl, new Set(cols.map((c) => c.field)));
462
512
  // Enum-typed columns also report tsType 'unknown' here, but generate.ts
463
- // gives them a concrete union type — a shadow of one was type-broken on
513
+ // gives them a concrete union type, a shadow of one was type-broken on
464
514
  // main, so only genuine json/jsonb columns qualify as historical shadows.
465
515
  unknownTypedFieldsByTable.set(tbl, new Set(cols.filter((c) => isUnknownTsType(c.tsType) && !Object.hasOwn(enums, c.pgType)).map((c) => c.field)));
466
516
  }
@@ -482,14 +532,14 @@ export async function introspectPostgresCatalog(options) {
482
532
  // 1. J's primary key is exactly two columns.
483
533
  // 2. J has exactly two FKs, each single-column.
484
534
  // 3. Each FK's source column is one of J's two PK columns (the PK *is* the
485
- // two FK columns — no surrogate PK, no extra identity).
535
+ // two FK columns, no surrogate PK, no extra identity).
486
536
  // 4. The two FKs target two DISTINCT tables (A and B).
487
537
  // 5. J has no columns beyond those two FK/PK columns (no payload columns
488
538
  // like `grade` or `created_at`).
489
539
  //
490
540
  // For such a J linking A and B we ADD a `manyToMany` relation on A → B and
491
541
  // symmetrically on B → A, both routed `through` J. The existing belongsTo /
492
- // hasMany relations derived from J's FKs are left untouched — this block
542
+ // hasMany relations derived from J's FKs are left untouched, this block
493
543
  // never removes or renames anything. Naming/collision handling lives in the
494
544
  // shared addAutoManyToManyRelations helper.
495
545
  //
@@ -624,7 +674,7 @@ export function stripCheckWrapper(def) {
624
674
  }
625
675
  /**
626
676
  * Derive a belongsTo relation name from its FK column. Strips a trailing
627
- * `_id` (snake_case) or `Id` (camelCase column names — common in Prisma-ported
677
+ * `_id` (snake_case) or `Id` (camelCase column names, common in Prisma-ported
628
678
  * schemas where columns are quoted camelCase identifiers), then camelCases:
629
679
  * `current_version_id` and `currentVersionId` both yield `currentVersion`.
630
680
  * Stripping is what keeps the scalar FK field (`currentVersionId`) targetable
@@ -743,7 +793,7 @@ export function detectUniqueForeignKeySets(pkByTable, uniqueByTable, indexesByTa
743
793
  /**
744
794
  * Resolve a derived relation name against the names already taken on the
745
795
  * table (scalar column fields + previously assigned relations). On collision,
746
- * applies a deterministic `Rel` / `Rel2` / `Rel3`… suffix and warns — a
796
+ * applies a deterministic `Rel` / `Rel2` / `Rel3`… suffix and warns, a
747
797
  * colliding name would otherwise shadow a column field and generate types
748
798
  * that fail `tsc --strict` (TS2430/TS2322).
749
799
  */
@@ -753,12 +803,12 @@ function resolveRelationNameCollision(candidate, taken, table, source) {
753
803
  let name = `${candidate}Rel`;
754
804
  for (let i = 2; taken.has(name); i++)
755
805
  name = `${candidate}Rel${i}`;
756
- console.warn(`[turbine] Relation name "${candidate}" on table "${table}" (from ${source}) collides with an existing column or relation — using "${name}" instead.`);
806
+ console.warn(`[turbine] Relation name "${candidate}" on table "${table}" (from ${source}) collides with an existing column or relation, using "${name}" instead.`);
757
807
  return name;
758
808
  }
759
809
  /**
760
810
  * Build the belongsTo/hasMany relation maps for every table from its foreign
761
- * keys. Naming rules (LEGACY-FIRST — a relation name that previously worked at
811
+ * keys. Naming rules (LEGACY-FIRST, a relation name that previously worked at
762
812
  * runtime must never change out from under a regenerating app):
763
813
  *
764
814
  * 1. First compute the historical derivation exactly as it shipped before
@@ -766,7 +816,7 @@ function resolveRelationNameCollision(candidate, taken, table, source) {
766
816
  * suffix (`snakeToCamel(col.replace(/_id$/, ''))` when several FKs point
767
817
  * at the same target, else the singularized target table), and hasMany is
768
818
  * `snakeToCamel(`${source}_by_${strippedColumn}`)` (else the source
769
- * table). If that legacy name is free, KEEP IT — even when it looks odd
819
+ * table). If that legacy name is free, KEEP IT, even when it looks odd
770
820
  * (`blogPostsByAuthorId`, `postsBy_Author`): those names were collision-
771
821
  * free and worked, so regenerating must not rename them.
772
822
  * 2. If the legacy name collides ONLY with a scalar column whose tsType is
@@ -775,17 +825,17 @@ function resolveRelationNameCollision(candidate, taken, table, source) {
775
825
  * relation payload; generate.ts's typeSafeRelations omits the relation
776
826
  * from the type layer).
777
827
  * 3. On a genuine collision (concrete-typed column shadow, or a previously
778
- * assigned relation), fall back to the modern derivation — the `_id`/`Id`
828
+ * assigned relation), fall back to the modern derivation, the `_id`/`Id`
779
829
  * case-insensitive strip of {@link relationNameFromColumn} plus the
780
- * `By`-composed reverse name — which fixes the camelCase-FK shadowing
830
+ * `By`-composed reverse name, which fixes the camelCase-FK shadowing
781
831
  * shapes that were actually BROKEN before (relation name === scalar FK
782
832
  * field → unusable types).
783
833
  * 4. Last resort: deterministic `Rel`/`Rel2` suffix + warning.
784
834
  *
785
- * @param columnFieldsByTable camelCase column *fields* per table — used to
835
+ * @param columnFieldsByTable camelCase column *fields* per table, used to
786
836
  * guarantee relations never shadow concrete-typed scalar columns.
787
837
  * @param unknownTypedFieldsByTable subset of the column fields whose tsType is
788
- * `unknown` (json/jsonb) — legacy shadows of these are preserved (rule 2).
838
+ * `unknown` (json/jsonb), legacy shadows of these are preserved (rule 2).
789
839
  * @param uniqueSetsByTable when provided (F2), the child-table column sets that
790
840
  * guarantee at-most-one row (PK + unique constraints + plain unique indexes,
791
841
  * from {@link detectUniqueForeignKeySets}). A reverse relation whose FK column
@@ -814,7 +864,7 @@ export function buildRelationsFromForeignKeys(foreignKeys, columnFieldsByTable,
814
864
  }
815
865
  return taken;
816
866
  };
817
- // Relation names actually assigned so far (as opposed to column fields) —
867
+ // Relation names actually assigned so far (as opposed to column fields) -
818
868
  // needed to tell "collides only with a column" apart from "collides with an
819
869
  // already-assigned relation" for the legacy-shadow-preserving rule.
820
870
  const assignedByTable = new Map();
@@ -826,17 +876,17 @@ export function buildRelationsFromForeignKeys(foreignKeys, columnFieldsByTable,
826
876
  }
827
877
  return assigned;
828
878
  };
829
- /** Legacy-first name resolution — see the naming rules in the JSDoc above. */
879
+ /** Legacy-first name resolution, see the naming rules in the JSDoc above. */
830
880
  const resolveName = (legacy, modern, table, source) => {
831
881
  const taken = takenFor(table);
832
882
  if (!taken.has(legacy))
833
883
  return legacy;
834
884
  // Historical json/jsonb shadow: previously worked at runtime AND compiled
835
- // (tsType `unknown` absorbs the relation payload). Keep the name, warn —
885
+ // (tsType `unknown` absorbs the relation payload). Keep the name, warn -
836
886
  // typeSafeRelations() keeps the generated type layer sound.
837
887
  if (!assignedFor(table).has(legacy) && unknownTypedFieldsByTable?.get(table)?.has(legacy)) {
838
888
  console.warn(`[turbine] Relation "${legacy}" on table "${table}" (from ${source}) shadows the json/jsonb column ` +
839
- `"${legacy}" — keeping the historical name for runtime compatibility; the relation is omitted from ` +
889
+ `"${legacy}", keeping the historical name for runtime compatibility; the relation is omitted from ` +
840
890
  `the generated types. Rename the column to expose it.`);
841
891
  return legacy;
842
892
  }
@@ -852,7 +902,7 @@ export function buildRelationsFromForeignKeys(foreignKeys, columnFieldsByTable,
852
902
  // For multi-column (composite) FKs, use array form.
853
903
  const foreignKey = singleColumn ? fk.sourceColumns[0] : fk.sourceColumns;
854
904
  const referenceKey = fk.targetColumns.length === 1 ? fk.targetColumns[0] : fk.targetColumns;
855
- // Composite FKs have no single column to derive from — fall back to the
905
+ // Composite FKs have no single column to derive from, fall back to the
856
906
  // constraint name (with the usual fk_/-_fkey affixes stripped).
857
907
  const constraintBase = fk.constraintName.replace(/^fk_/, '').replace(/_fkey$/, '');
858
908
  // --- belongsTo on the source (child) table ---
@@ -956,7 +1006,7 @@ export function buildRelationsFromForeignKeys(foreignKeys, columnFieldsByTable,
956
1006
  */
957
1007
  export function addAutoManyToManyRelations(tableNames, foreignKeys, pkByTable, columnNamesByTable, relationsByTable, columnFieldsByTable, unknownTypedFieldsByTable, uniqueIndexColsByTable) {
958
1008
  for (const tableName of tableNames) {
959
- // FKs whose source is this table — both must be single-column.
1009
+ // FKs whose source is this table, both must be single-column.
960
1010
  const tableFks = foreignKeys.filter((fk) => fk.sourceTable === tableName);
961
1011
  if (tableFks.length !== 2)
962
1012
  continue;
@@ -1008,9 +1058,9 @@ export function addAutoManyToManyRelations(tableNames, foreignKeys, pkByTable, c
1008
1058
  const columnFields = columnFieldsByTable?.get(sourceTbl);
1009
1059
  if (columnFields?.has(relName)) {
1010
1060
  if (unknownTypedFieldsByTable?.get(sourceTbl)?.has(relName)) {
1011
- // Historical json/jsonb shadow — worked at runtime, compiled fine.
1061
+ // Historical json/jsonb shadow, worked at runtime, compiled fine.
1012
1062
  console.warn(`[turbine] Relation "${relName}" on table "${sourceTbl}" (junction ${tableName}) shadows the ` +
1013
- `json/jsonb column "${relName}" — keeping the historical name for runtime compatibility; ` +
1063
+ `json/jsonb column "${relName}", keeping the historical name for runtime compatibility; ` +
1014
1064
  `the relation is omitted from the generated types.`);
1015
1065
  }
1016
1066
  else {
@@ -1044,7 +1094,7 @@ export function addAutoManyToManyRelations(tableNames, foreignKeys, pkByTable, c
1044
1094
  * MSSQL): filters the FK list to the introspected table set, seeds the
1045
1095
  * taken-name / json-shadow maps from the engine's column metadata, and runs
1046
1096
  * the SAME `buildRelationsFromForeignKeys` + `addAutoManyToManyRelations`
1047
- * pipeline as the Postgres introspector — so every engine derives identical
1097
+ * pipeline as the Postgres introspector, so every engine derives identical
1048
1098
  * relation names for the same logical schema (the engines previously carried
1049
1099
  * stale copies of a retired naming scheme).
1050
1100
  */
package/dist/mssql.d.ts CHANGED
@@ -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