turbine-orm 0.13.3 → 0.14.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.
@@ -9,6 +9,7 @@
9
9
  Object.defineProperty(exports, "__esModule", { value: true });
10
10
  exports.postgresDialect = void 0;
11
11
  const errors_js_1 = require("./errors.js");
12
+ const schema_js_1 = require("./schema.js");
12
13
  /** PostgreSQL implementation of the dialect contract. */
13
14
  exports.postgresDialect = {
14
15
  name: 'postgresql',
@@ -72,10 +73,11 @@ exports.postgresDialect = {
72
73
  .map((col, i) => `${leftRef}.${this.quoteIdentifier(col)} = ${rightRef}.${this.quoteIdentifier(rightCols[i])}`)
73
74
  .join(' AND ');
74
75
  },
75
- typeToTypeScript(_dialectType, _nullable) {
76
- // Existing PostgreSQL type mapping remains in schema.ts/generate.ts for now.
77
- // This hook is the package boundary MySQL/SQLite implementations will fill.
78
- return 'unknown';
76
+ typeToTypeScript(dialectType, nullable) {
77
+ return (0, schema_js_1.pgTypeToTs)(dialectType, nullable);
78
+ },
79
+ arrayType(baseType) {
80
+ return (0, schema_js_1.pgArrayType)(baseType);
79
81
  },
80
82
  buildColumnType(input) {
81
83
  if (input.type === 'VARCHAR' && input.maxLength != null) {
@@ -218,7 +218,13 @@ function generateMetadata(schema) {
218
218
  // dateColumns
219
219
  const dateCols = [...table.dateColumns];
220
220
  lines.push(` dateColumns: new Set([${dateCols.map((c) => `'${escSQ(c)}'`).join(', ')}]),`);
221
- // pgTypes
221
+ // dialectTypes + pgTypes (pgTypes is kept for backwards compatibility)
222
+ const dialectTypes = table.dialectTypes ?? table.pgTypes;
223
+ lines.push(' dialectTypes: {');
224
+ for (const [col, dialectType] of Object.entries(dialectTypes)) {
225
+ lines.push(` ${quoteIfNeeded(col)}: '${escSQ(dialectType)}',`);
226
+ }
227
+ lines.push(' },');
222
228
  lines.push(' pgTypes: {');
223
229
  for (const [col, pgType] of Object.entries(table.pgTypes)) {
224
230
  lines.push(` ${quoteIfNeeded(col)}: '${escSQ(pgType)}',`);
@@ -392,11 +398,13 @@ function serializeColumn(col) {
392
398
  const parts = [
393
399
  `name: '${escSQ(col.name)}'`,
394
400
  `field: '${escSQ(col.field)}'`,
401
+ `dialectType: '${escSQ(col.dialectType ?? col.pgType)}'`,
395
402
  `pgType: '${escSQ(col.pgType)}'`,
396
403
  `tsType: '${escSQ(col.tsType)}'`,
397
404
  `nullable: ${col.nullable}`,
398
405
  `hasDefault: ${col.hasDefault}`,
399
406
  `isArray: ${col.isArray}`,
407
+ `arrayType: '${escSQ(col.arrayType ?? col.pgArrayType)}'`,
400
408
  `pgArrayType: '${escSQ(col.pgArrayType)}'`,
401
409
  ];
402
410
  if (col.maxLength !== undefined)
@@ -14,6 +14,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.introspect = introspect;
16
16
  const pg_1 = __importDefault(require("pg"));
17
+ const dialect_js_1 = require("./dialect.js");
17
18
  const schema_js_1 = require("./schema.js");
18
19
  // ---------------------------------------------------------------------------
19
20
  // SQL queries (all parameterized, no interpolation)
@@ -101,6 +102,7 @@ const SQL_ENUMS = `
101
102
  // ---------------------------------------------------------------------------
102
103
  async function introspect(options) {
103
104
  const schema = options.schema ?? 'public';
105
+ const dialect = dialect_js_1.postgresDialect;
104
106
  const pool = new pg_1.default.Pool({
105
107
  connectionString: options.connectionString,
106
108
  max: 1,
@@ -137,15 +139,20 @@ async function introspect(options) {
137
139
  const isNullable = row.is_nullable === 'YES';
138
140
  const isArray = row.data_type === 'ARRAY';
139
141
  const baseType = isArray ? row.udt_name.slice(1) : row.udt_name;
142
+ const dialectType = row.udt_name;
143
+ const arrayType = dialect.arrayType?.(baseType) ?? 'text[]';
140
144
  const col = {
141
145
  name: row.column_name,
142
146
  field: (0, schema_js_1.snakeToCamel)(row.column_name),
143
- pgType: row.udt_name,
144
- tsType: (0, schema_js_1.pgTypeToTs)(isArray ? row.udt_name : baseType, isNullable),
147
+ dialectType,
148
+ pgType: dialectType,
149
+ tsType: dialect.typeToTypeScript?.(isArray ? dialectType : baseType, isNullable) ??
150
+ (0, schema_js_1.pgTypeToTs)(isArray ? dialectType : baseType, isNullable),
145
151
  nullable: isNullable,
146
152
  hasDefault: row.column_default !== null,
147
153
  isArray,
148
- pgArrayType: (0, schema_js_1.pgArrayType)(baseType),
154
+ arrayType,
155
+ pgArrayType: arrayType,
149
156
  maxLength: row.character_maximum_length ?? undefined,
150
157
  };
151
158
  if (!columnsByTable.has(tableName))
@@ -281,14 +288,16 @@ async function introspect(options) {
281
288
  const columnMap = {};
282
289
  const reverseColumnMap = {};
283
290
  const dateColumns = new Set();
291
+ const dialectTypes = {};
284
292
  const pgTypes = {};
285
293
  const allColumns = [];
286
294
  for (const col of columns) {
287
295
  columnMap[col.field] = col.name;
288
296
  reverseColumnMap[col.name] = col.field;
289
297
  allColumns.push(col.name);
298
+ dialectTypes[col.name] = col.dialectType ?? col.pgType;
290
299
  pgTypes[col.name] = col.pgType;
291
- const baseType = col.isArray ? col.pgType.slice(1) : col.pgType;
300
+ const baseType = col.isArray ? (col.dialectType ?? col.pgType).slice(1) : (col.dialectType ?? col.pgType);
292
301
  if ((0, schema_js_1.isDateType)(baseType)) {
293
302
  dateColumns.add(col.name);
294
303
  }
@@ -299,6 +308,7 @@ async function introspect(options) {
299
308
  columnMap,
300
309
  reverseColumnMap,
301
310
  dateColumns,
311
+ dialectTypes,
302
312
  pgTypes,
303
313
  allColumns,
304
314
  primaryKey: pkByTable.get(tableName) ?? [],
@@ -153,8 +153,8 @@ class QueryInterface {
153
153
  this.columnPgTypeMap = new Map();
154
154
  this.columnArrayTypeMap = new Map();
155
155
  for (const col of this.tableMeta.columns) {
156
- this.columnPgTypeMap.set(col.name, col.pgType);
157
- this.columnArrayTypeMap.set(col.name, col.pgArrayType);
156
+ this.columnPgTypeMap.set(col.name, col.dialectType ?? col.pgType);
157
+ this.columnArrayTypeMap.set(col.name, col.arrayType ?? col.pgArrayType);
158
158
  }
159
159
  }
160
160
  /** Quote an identifier through the active SQL dialect. */
@@ -2680,12 +2680,13 @@ class QueryInterface {
2680
2680
  const arrayType = this.columnArrayTypeMap.get(column);
2681
2681
  if (arrayType)
2682
2682
  return arrayType;
2683
- // Fallback heuristic for unknown columns
2683
+ // Fallback heuristic for unknown columns, routed through the active dialect
2684
+ // so non-Postgres packages can supply their own bulk-insert cast shape.
2684
2685
  if (column === 'id' || column.endsWith('_id'))
2685
- return 'bigint[]';
2686
+ return this.dialect.arrayType?.('int8') ?? 'text[]';
2686
2687
  if (column.endsWith('_at'))
2687
- return 'timestamptz[]';
2688
- return 'text[]';
2688
+ return this.dialect.arrayType?.('timestamptz') ?? 'text[]';
2689
+ return this.dialect.arrayType?.('text') ?? 'text[]';
2689
2690
  }
2690
2691
  }
2691
2692
  exports.QueryInterface = QueryInterface;
package/dist/dialect.d.ts CHANGED
@@ -5,7 +5,7 @@
5
5
  * PostgreSQL-native by default, but query generation now depends on this
6
6
  * contract for the SQL primitives that vary across MySQL and SQLite.
7
7
  */
8
- import type { SchemaMetadata } from './schema.js';
8
+ import { type SchemaMetadata } from './schema.js';
9
9
  export type DialectName = 'postgresql' | 'mysql' | 'sqlite' | (string & {});
10
10
  export interface InsertStatementInput {
11
11
  /** SQL-ready quoted table name. */
@@ -125,8 +125,8 @@ export interface Dialect {
125
125
  buildJsonPathExtract(column: string, pathParamRef: string): string;
126
126
  /** Build a correlation clause across single or composite keys. */
127
127
  buildCorrelation(leftRef: string, leftColumns: string | string[], rightRef: string, rightColumns: string | string[]): string;
128
- /** Type mapping hook for code generation. */
129
- typeToTypeScript(dialectType: string, nullable: boolean): string;
128
+ /** Optional type mapping hook for code generation/introspection. */
129
+ typeToTypeScript?(dialectType: string, nullable: boolean): string;
130
130
  /** Optional array-cast hook for bulk insert implementations. */
131
131
  arrayType?(baseType: string): string;
132
132
  /** Map a schema-builder column type to dialect DDL. */
package/dist/dialect.js CHANGED
@@ -6,6 +6,7 @@
6
6
  * contract for the SQL primitives that vary across MySQL and SQLite.
7
7
  */
8
8
  import { ValidationError } from './errors.js';
9
+ import { pgArrayType, pgTypeToTs } from './schema.js';
9
10
  /** PostgreSQL implementation of the dialect contract. */
10
11
  export const postgresDialect = {
11
12
  name: 'postgresql',
@@ -69,10 +70,11 @@ export const postgresDialect = {
69
70
  .map((col, i) => `${leftRef}.${this.quoteIdentifier(col)} = ${rightRef}.${this.quoteIdentifier(rightCols[i])}`)
70
71
  .join(' AND ');
71
72
  },
72
- typeToTypeScript(_dialectType, _nullable) {
73
- // Existing PostgreSQL type mapping remains in schema.ts/generate.ts for now.
74
- // This hook is the package boundary MySQL/SQLite implementations will fill.
75
- return 'unknown';
73
+ typeToTypeScript(dialectType, nullable) {
74
+ return pgTypeToTs(dialectType, nullable);
75
+ },
76
+ arrayType(baseType) {
77
+ return pgArrayType(baseType);
76
78
  },
77
79
  buildColumnType(input) {
78
80
  if (input.type === 'VARCHAR' && input.maxLength != null) {
package/dist/generate.js CHANGED
@@ -214,7 +214,13 @@ function generateMetadata(schema) {
214
214
  // dateColumns
215
215
  const dateCols = [...table.dateColumns];
216
216
  lines.push(` dateColumns: new Set([${dateCols.map((c) => `'${escSQ(c)}'`).join(', ')}]),`);
217
- // pgTypes
217
+ // dialectTypes + pgTypes (pgTypes is kept for backwards compatibility)
218
+ const dialectTypes = table.dialectTypes ?? table.pgTypes;
219
+ lines.push(' dialectTypes: {');
220
+ for (const [col, dialectType] of Object.entries(dialectTypes)) {
221
+ lines.push(` ${quoteIfNeeded(col)}: '${escSQ(dialectType)}',`);
222
+ }
223
+ lines.push(' },');
218
224
  lines.push(' pgTypes: {');
219
225
  for (const [col, pgType] of Object.entries(table.pgTypes)) {
220
226
  lines.push(` ${quoteIfNeeded(col)}: '${escSQ(pgType)}',`);
@@ -388,11 +394,13 @@ function serializeColumn(col) {
388
394
  const parts = [
389
395
  `name: '${escSQ(col.name)}'`,
390
396
  `field: '${escSQ(col.field)}'`,
397
+ `dialectType: '${escSQ(col.dialectType ?? col.pgType)}'`,
391
398
  `pgType: '${escSQ(col.pgType)}'`,
392
399
  `tsType: '${escSQ(col.tsType)}'`,
393
400
  `nullable: ${col.nullable}`,
394
401
  `hasDefault: ${col.hasDefault}`,
395
402
  `isArray: ${col.isArray}`,
403
+ `arrayType: '${escSQ(col.arrayType ?? col.pgArrayType)}'`,
396
404
  `pgArrayType: '${escSQ(col.pgArrayType)}'`,
397
405
  ];
398
406
  if (col.maxLength !== undefined)
@@ -8,7 +8,8 @@
8
8
  * This is the foundation of `npx turbine generate`.
9
9
  */
10
10
  import pg from 'pg';
11
- import { isDateType, pgArrayType, pgTypeToTs, singularize, snakeToCamel, } from './schema.js';
11
+ import { postgresDialect } from './dialect.js';
12
+ import { isDateType, pgTypeToTs, singularize, snakeToCamel, } from './schema.js';
12
13
  // ---------------------------------------------------------------------------
13
14
  // SQL queries (all parameterized, no interpolation)
14
15
  // ---------------------------------------------------------------------------
@@ -95,6 +96,7 @@ const SQL_ENUMS = `
95
96
  // ---------------------------------------------------------------------------
96
97
  export async function introspect(options) {
97
98
  const schema = options.schema ?? 'public';
99
+ const dialect = postgresDialect;
98
100
  const pool = new pg.Pool({
99
101
  connectionString: options.connectionString,
100
102
  max: 1,
@@ -131,15 +133,20 @@ export async function introspect(options) {
131
133
  const isNullable = row.is_nullable === 'YES';
132
134
  const isArray = row.data_type === 'ARRAY';
133
135
  const baseType = isArray ? row.udt_name.slice(1) : row.udt_name;
136
+ const dialectType = row.udt_name;
137
+ const arrayType = dialect.arrayType?.(baseType) ?? 'text[]';
134
138
  const col = {
135
139
  name: row.column_name,
136
140
  field: snakeToCamel(row.column_name),
137
- pgType: row.udt_name,
138
- tsType: pgTypeToTs(isArray ? row.udt_name : baseType, isNullable),
141
+ dialectType,
142
+ pgType: dialectType,
143
+ tsType: dialect.typeToTypeScript?.(isArray ? dialectType : baseType, isNullable) ??
144
+ pgTypeToTs(isArray ? dialectType : baseType, isNullable),
139
145
  nullable: isNullable,
140
146
  hasDefault: row.column_default !== null,
141
147
  isArray,
142
- pgArrayType: pgArrayType(baseType),
148
+ arrayType,
149
+ pgArrayType: arrayType,
143
150
  maxLength: row.character_maximum_length ?? undefined,
144
151
  };
145
152
  if (!columnsByTable.has(tableName))
@@ -275,14 +282,16 @@ export async function introspect(options) {
275
282
  const columnMap = {};
276
283
  const reverseColumnMap = {};
277
284
  const dateColumns = new Set();
285
+ const dialectTypes = {};
278
286
  const pgTypes = {};
279
287
  const allColumns = [];
280
288
  for (const col of columns) {
281
289
  columnMap[col.field] = col.name;
282
290
  reverseColumnMap[col.name] = col.field;
283
291
  allColumns.push(col.name);
292
+ dialectTypes[col.name] = col.dialectType ?? col.pgType;
284
293
  pgTypes[col.name] = col.pgType;
285
- const baseType = col.isArray ? col.pgType.slice(1) : col.pgType;
294
+ const baseType = col.isArray ? (col.dialectType ?? col.pgType).slice(1) : (col.dialectType ?? col.pgType);
286
295
  if (isDateType(baseType)) {
287
296
  dateColumns.add(col.name);
288
297
  }
@@ -293,6 +302,7 @@ export async function introspect(options) {
293
302
  columnMap,
294
303
  reverseColumnMap,
295
304
  dateColumns,
305
+ dialectTypes,
296
306
  pgTypes,
297
307
  allColumns,
298
308
  primaryKey: pkByTable.get(tableName) ?? [],
@@ -150,8 +150,8 @@ export class QueryInterface {
150
150
  this.columnPgTypeMap = new Map();
151
151
  this.columnArrayTypeMap = new Map();
152
152
  for (const col of this.tableMeta.columns) {
153
- this.columnPgTypeMap.set(col.name, col.pgType);
154
- this.columnArrayTypeMap.set(col.name, col.pgArrayType);
153
+ this.columnPgTypeMap.set(col.name, col.dialectType ?? col.pgType);
154
+ this.columnArrayTypeMap.set(col.name, col.arrayType ?? col.pgArrayType);
155
155
  }
156
156
  }
157
157
  /** Quote an identifier through the active SQL dialect. */
@@ -2677,12 +2677,13 @@ export class QueryInterface {
2677
2677
  const arrayType = this.columnArrayTypeMap.get(column);
2678
2678
  if (arrayType)
2679
2679
  return arrayType;
2680
- // Fallback heuristic for unknown columns
2680
+ // Fallback heuristic for unknown columns, routed through the active dialect
2681
+ // so non-Postgres packages can supply their own bulk-insert cast shape.
2681
2682
  if (column === 'id' || column.endsWith('_id'))
2682
- return 'bigint[]';
2683
+ return this.dialect.arrayType?.('int8') ?? 'text[]';
2683
2684
  if (column.endsWith('_at'))
2684
- return 'timestamptz[]';
2685
- return 'text[]';
2685
+ return this.dialect.arrayType?.('timestamptz') ?? 'text[]';
2686
+ return this.dialect.arrayType?.('text') ?? 'text[]';
2686
2687
  }
2687
2688
  }
2688
2689
  //# sourceMappingURL=builder.js.map
package/dist/schema.d.ts CHANGED
@@ -21,7 +21,9 @@ export interface TableMetadata {
21
21
  reverseColumnMap: Record<string, string>;
22
22
  /** snake_case columns that are timestamp/date types (need Date parsing) */
23
23
  dateColumns: Set<string>;
24
- /** snake_case column → Postgres type for UNNEST casts */
24
+ /** snake_case column → dialect-native database type. */
25
+ dialectTypes?: Record<string, string>;
26
+ /** snake_case column → Postgres type for UNNEST casts. Back-compat alias for dialectTypes. */
25
27
  pgTypes: Record<string, string>;
26
28
  /** All snake_case column names in ordinal order */
27
29
  allColumns: string[];
@@ -39,7 +41,9 @@ export interface ColumnMetadata {
39
41
  name: string;
40
42
  /** camelCase field name for TypeScript */
41
43
  field: string;
42
- /** Postgres base type (e.g. 'int8', 'text', 'timestamptz') */
44
+ /** Dialect-native database type (e.g. PostgreSQL 'int8', MySQL 'bigint', SQLite 'INTEGER'). */
45
+ dialectType?: string;
46
+ /** Postgres base type (e.g. 'int8', 'text', 'timestamptz'). Back-compat alias for dialectType. */
43
47
  pgType: string;
44
48
  /** TypeScript type string (e.g. 'number', 'string', 'Date') */
45
49
  tsType: string;
@@ -49,7 +53,9 @@ export interface ColumnMetadata {
49
53
  hasDefault: boolean;
50
54
  /** Whether this is an array column */
51
55
  isArray: boolean;
52
- /** Postgres array type for UNNEST (e.g. 'bigint[]') */
56
+ /** Dialect-specific array/bulk-insert type token when needed. */
57
+ arrayType?: string;
58
+ /** Postgres array type for UNNEST (e.g. 'bigint[]'). Back-compat alias for arrayType. */
53
59
  pgArrayType: string;
54
60
  /** Max character length (for varchar) */
55
61
  maxLength?: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "turbine-orm",
3
- "version": "0.13.3",
3
+ "version": "0.14.0",
4
4
  "description": "Postgres-native TypeScript ORM — runs on Neon, Vercel Postgres, Cloudflare, Supabase. Streaming cursors, typed errors, single-query nested relations. 1 dependency, ~110KB",
5
5
  "type": "module",
6
6
  "exports": {