turbine-orm 0.40.1 → 0.41.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 (63) hide show
  1. package/README.md +22 -4
  2. package/dist/cjs/cli/config.js +3 -0
  3. package/dist/cjs/cli/index.js +179 -0
  4. package/dist/cjs/cli/prisma-report.js +216 -0
  5. package/dist/cjs/cli/prisma-resolve.js +335 -0
  6. package/dist/cjs/cli/prisma-schema.js +484 -0
  7. package/dist/cjs/client.js +1 -0
  8. package/dist/cjs/generate.js +279 -22
  9. package/dist/cjs/index.js +3 -2
  10. package/dist/cjs/introspect.js +203 -26
  11. package/dist/cjs/mssql.js +9 -10
  12. package/dist/cjs/mysql.js +3 -9
  13. package/dist/cjs/powdb-introspect.js +5 -10
  14. package/dist/cjs/powql.js +13 -0
  15. package/dist/cjs/prisma-compat.js +1147 -0
  16. package/dist/cjs/query/aggregates.js +67 -7
  17. package/dist/cjs/query/builder.js +388 -17
  18. package/dist/cjs/query/compound-unique.js +0 -0
  19. package/dist/cjs/query/relations.js +7 -5
  20. package/dist/cjs/query/warn-registry.js +98 -0
  21. package/dist/cjs/query/writes.js +13 -5
  22. package/dist/cjs/schema.js +47 -0
  23. package/dist/cjs/sqlite.js +4 -9
  24. package/dist/cli/config.d.ts +26 -0
  25. package/dist/cli/config.js +3 -0
  26. package/dist/cli/index.d.ts +11 -0
  27. package/dist/cli/index.js +180 -1
  28. package/dist/cli/prisma-report.d.ts +19 -0
  29. package/dist/cli/prisma-report.js +211 -0
  30. package/dist/cli/prisma-resolve.d.ts +87 -0
  31. package/dist/cli/prisma-resolve.js +330 -0
  32. package/dist/cli/prisma-schema.d.ts +116 -0
  33. package/dist/cli/prisma-schema.js +479 -0
  34. package/dist/cli/ui.d.ts +1 -1
  35. package/dist/client.d.ts +18 -2
  36. package/dist/client.js +1 -0
  37. package/dist/generate.d.ts +80 -1
  38. package/dist/generate.js +277 -25
  39. package/dist/index.d.ts +2 -2
  40. package/dist/index.js +1 -1
  41. package/dist/introspect.d.ts +92 -2
  42. package/dist/introspect.js +198 -26
  43. package/dist/mssql.js +10 -11
  44. package/dist/mysql.js +4 -10
  45. package/dist/powdb-introspect.js +5 -10
  46. package/dist/powql.js +13 -0
  47. package/dist/prisma-compat.d.ts +281 -0
  48. package/dist/prisma-compat.js +1143 -0
  49. package/dist/query/aggregates.js +67 -7
  50. package/dist/query/builder.d.ts +77 -4
  51. package/dist/query/builder.js +390 -19
  52. package/dist/query/compound-unique.d.ts +49 -0
  53. package/dist/query/compound-unique.js +0 -0
  54. package/dist/query/deferred.d.ts +18 -0
  55. package/dist/query/relations.js +7 -5
  56. package/dist/query/types.d.ts +70 -9
  57. package/dist/query/warn-registry.d.ts +57 -0
  58. package/dist/query/warn-registry.js +92 -0
  59. package/dist/query/writes.js +13 -5
  60. package/dist/schema.d.ts +75 -0
  61. package/dist/schema.js +46 -0
  62. package/dist/sqlite.js +5 -10
  63. package/package.json +6 -1
@@ -11,10 +11,15 @@
11
11
  */
12
12
  Object.defineProperty(exports, "__esModule", { value: true });
13
13
  exports.generate = generate;
14
+ exports.resolveImportExtension = resolveImportExtension;
15
+ exports.detectTsconfigExtension = detectTsconfigExtension;
16
+ exports.classifyTsconfig = classifyTsconfig;
17
+ exports.stripJsonComments = stripJsonComments;
14
18
  exports.generateTypes = generateTypes;
15
19
  exports.generateZod = generateZod;
16
20
  exports.generateMetadata = generateMetadata;
17
21
  exports.generateIndex = generateIndex;
22
+ exports.generatePrismaMap = generatePrismaMap;
18
23
  const node_fs_1 = require("node:fs");
19
24
  const node_path_1 = require("node:path");
20
25
  const schema_js_1 = require("./schema.js");
@@ -56,29 +61,177 @@ function generate(options) {
56
61
  throw new Error(`Output directory must be within the project root. Got: ${outDir}`);
57
62
  }
58
63
  (0, node_fs_1.mkdirSync)(outDir, { recursive: true });
64
+ // F4: keep raw DB column names as field names (opt-in). Pure transform: when
65
+ // the flag is off `withDbFieldNames` is never called and output is unchanged.
66
+ const schema = options.keepColumnNames ? (0, schema_js_1.withDbFieldNames)(options.schema) : options.schema;
67
+ // F3: resolve which extension sibling imports get in index.ts.
68
+ const { ext: importExt, mode: importMode } = resolveImportExtension(outDir, options.importExtension ?? 'auto');
59
69
  const files = [];
60
- const fileOptions = { noTimestamp: options.noTimestamp };
70
+ const fileOptions = { noTimestamp: options.noTimestamp, importExt, importMode };
61
71
  // Generate types.ts
62
- const typesContent = generateTypes(options.schema, fileOptions);
72
+ const typesContent = generateTypes(schema, fileOptions);
63
73
  (0, node_fs_1.writeFileSync)((0, node_path_1.join)(outDir, 'types.ts'), typesContent, 'utf-8');
64
74
  files.push('types.ts');
65
75
  // Generate metadata.ts
66
- const metadataContent = generateMetadata(options.schema, fileOptions);
76
+ const metadataContent = generateMetadata(schema, fileOptions);
67
77
  (0, node_fs_1.writeFileSync)((0, node_path_1.join)(outDir, 'metadata.ts'), metadataContent, 'utf-8');
68
78
  files.push('metadata.ts');
69
79
  // Generate index.ts (configured client)
70
- const indexContent = generateIndex(options.schema, fileOptions);
80
+ const indexContent = generateIndex(schema, fileOptions);
71
81
  (0, node_fs_1.writeFileSync)((0, node_path_1.join)(outDir, 'index.ts'), indexContent, 'utf-8');
72
82
  files.push('index.ts');
73
83
  // Generate zod.ts (optional — --zod flag)
74
84
  if (options.zod) {
75
- const zodContent = generateZod(options.schema, fileOptions);
85
+ const zodContent = generateZod(schema, fileOptions);
76
86
  (0, node_fs_1.writeFileSync)((0, node_path_1.join)(outDir, 'zod.ts'), zodContent, 'utf-8');
77
87
  files.push('zod.ts');
78
88
  }
79
89
  return { outDir, files };
80
90
  }
81
91
  // ---------------------------------------------------------------------------
92
+ // Import-extension resolution (F3)
93
+ // ---------------------------------------------------------------------------
94
+ /**
95
+ * Resolve the requested {@link GenerateOptions.importExtension} to the concrete
96
+ * extension string used on `index.ts`'s sibling imports plus a human-readable
97
+ * mode label for the generated comment.
98
+ *
99
+ * - `'js'` → `.js`
100
+ * - `'none'` → `''`
101
+ * - `'auto'` → tsconfig-driven ({@link detectTsconfigExtension}); falls back
102
+ * to `.js` (the pre-0.41 default) when detection is uncertain, so NodeNext
103
+ * consumers can never regress.
104
+ */
105
+ function resolveImportExtension(outDir, requested) {
106
+ if (requested === 'js')
107
+ return { ext: '.js', mode: 'js' };
108
+ if (requested === 'none')
109
+ return { ext: '', mode: 'none' };
110
+ const detected = detectTsconfigExtension((0, node_path_1.resolve)(outDir));
111
+ if (detected === 'js')
112
+ return { ext: '.js', mode: 'auto (nodenext → .js)' };
113
+ if (detected === 'none')
114
+ return { ext: '', mode: 'auto (bundler → no extension)' };
115
+ return { ext: '.js', mode: 'auto (fallback → .js)' };
116
+ }
117
+ /**
118
+ * Walk up from `startDir` to the nearest `tsconfig.json` and classify its module
119
+ * resolution. Does NOT follow `extends` chains (a monorepo whose base config
120
+ * sets `moduleResolution` needs the explicit flag). Returns:
121
+ * - `'js'`: `module`/`moduleResolution` is `node16`/`nodenext`;
122
+ * - `'none'`: both fields present and neither is node16/nodenext (bundler,
123
+ * node, node10, classic, esnext, commonjs, preserve);
124
+ * - `null`: no tsconfig found, unparseable, or the fields are absent
125
+ * (possibly hidden behind `extends`) → caller falls back to `.js`.
126
+ */
127
+ function detectTsconfigExtension(startDir) {
128
+ let dir = startDir;
129
+ for (;;) {
130
+ const candidate = (0, node_path_1.join)(dir, 'tsconfig.json');
131
+ if ((0, node_fs_1.existsSync)(candidate)) {
132
+ try {
133
+ return classifyTsconfig((0, node_fs_1.readFileSync)(candidate, 'utf-8'));
134
+ }
135
+ catch {
136
+ return null;
137
+ }
138
+ }
139
+ const parent = (0, node_path_1.dirname)(dir);
140
+ if (parent === dir)
141
+ return null; // reached the filesystem root
142
+ dir = parent;
143
+ }
144
+ }
145
+ /**
146
+ * Classify a tsconfig's `compilerOptions.module` / `moduleResolution` (see
147
+ * {@link detectTsconfigExtension}). Tolerant of `//` and block comments and
148
+ * trailing commas (real-world tsconfigs use JSONC).
149
+ */
150
+ function classifyTsconfig(text) {
151
+ let parsed;
152
+ try {
153
+ parsed = JSON.parse(stripJsonComments(text));
154
+ }
155
+ catch {
156
+ return null;
157
+ }
158
+ if (typeof parsed !== 'object' || parsed === null)
159
+ return null;
160
+ const co = parsed.compilerOptions;
161
+ if (typeof co !== 'object' || co === null)
162
+ return null;
163
+ const opts = co;
164
+ const mod = typeof opts.module === 'string' ? opts.module.toLowerCase() : undefined;
165
+ const modRes = typeof opts.moduleResolution === 'string' ? opts.moduleResolution.toLowerCase() : undefined;
166
+ const isNodeNext = (v) => v === 'node16' || v === 'nodenext';
167
+ if (isNodeNext(mod) || isNodeNext(modRes))
168
+ return 'js';
169
+ if (mod !== undefined && modRes !== undefined)
170
+ return 'none';
171
+ return null;
172
+ }
173
+ /**
174
+ * Strip `//` line comments and block comments from JSONC text, leaving anything
175
+ * inside a double-quoted string untouched. Trailing commas are then removed so
176
+ * `JSON.parse` accepts the result.
177
+ */
178
+ function stripJsonComments(text) {
179
+ let out = '';
180
+ let inString = false;
181
+ let inLine = false;
182
+ let inBlock = false;
183
+ for (let i = 0; i < text.length; i++) {
184
+ const ch = text[i];
185
+ const next = text[i + 1];
186
+ if (inLine) {
187
+ if (ch === '\n') {
188
+ inLine = false;
189
+ out += ch;
190
+ }
191
+ continue;
192
+ }
193
+ if (inBlock) {
194
+ if (ch === '*' && next === '/') {
195
+ inBlock = false;
196
+ i++;
197
+ }
198
+ continue;
199
+ }
200
+ if (inString) {
201
+ out += ch;
202
+ if (ch === '\\') {
203
+ // Preserve the escaped character verbatim.
204
+ if (next !== undefined) {
205
+ out += next;
206
+ i++;
207
+ }
208
+ }
209
+ else if (ch === '"') {
210
+ inString = false;
211
+ }
212
+ continue;
213
+ }
214
+ if (ch === '"') {
215
+ inString = true;
216
+ out += ch;
217
+ continue;
218
+ }
219
+ if (ch === '/' && next === '/') {
220
+ inLine = true;
221
+ i++;
222
+ continue;
223
+ }
224
+ if (ch === '/' && next === '*') {
225
+ inBlock = true;
226
+ i++;
227
+ continue;
228
+ }
229
+ out += ch;
230
+ }
231
+ // Drop trailing commas before } or ].
232
+ return out.replace(/,(\s*[}\]])/g, '$1');
233
+ }
234
+ // ---------------------------------------------------------------------------
82
235
  // types.ts generator
83
236
  // ---------------------------------------------------------------------------
84
237
  function generatedFileHeader(options) {
@@ -174,7 +327,7 @@ function generateTypes(schema, options) {
174
327
  const piiNote = col.pii ? ' (PII: absent unless selected or includePii)' : '';
175
328
  const optional = col.pii ? '?' : '';
176
329
  lines.push(` /** Column: ${col.name}, ${col.pgType}${pkNote}${nullNote}${piiNote} */`);
177
- lines.push(` ${col.field}${optional}: ${columnTsType(col, schema.enums)};`);
330
+ lines.push(` ${quoteIfNeeded(col.field)}${optional}: ${columnTsType(col, schema.enums)};`);
178
331
  }
179
332
  lines.push('}');
180
333
  lines.push('');
@@ -192,10 +345,10 @@ function generateTypes(schema, options) {
192
345
  if (isOptional) {
193
346
  const reason = isPk ? 'auto-generated' : col.hasDefault ? 'has default' : 'nullable';
194
347
  lines.push(` /** Optional: ${reason} */`);
195
- lines.push(` ${col.field}?: ${columnTsType(col, schema.enums)};`);
348
+ lines.push(` ${quoteIfNeeded(col.field)}?: ${columnTsType(col, schema.enums)};`);
196
349
  }
197
350
  else {
198
- lines.push(` ${col.field}: ${columnTsType(col, schema.enums)};`);
351
+ lines.push(` ${quoteIfNeeded(col.field)}: ${columnTsType(col, schema.enums)};`);
199
352
  }
200
353
  }
201
354
  lines.push('};');
@@ -207,7 +360,7 @@ function generateTypes(schema, options) {
207
360
  lines.push(`/** Input type for updating a row in \`${table.name}\` */`);
208
361
  lines.push(`export type ${typeName}Update = {`);
209
362
  for (const col of nonPkCols) {
210
- lines.push(` ${col.field}?: ${updateFieldType(columnTsType(col, schema.enums))};`);
363
+ lines.push(` ${quoteIfNeeded(col.field)}?: ${updateFieldType(columnTsType(col, schema.enums))};`);
211
364
  }
212
365
  lines.push('};');
213
366
  lines.push('');
@@ -276,15 +429,54 @@ function generateTypes(schema, options) {
276
429
  }
277
430
  }
278
431
  if (uniqueSets.length > 0) {
279
- const branches = uniqueSets.map((cols) => {
432
+ const memberType = (colName) => {
433
+ const col = table.columns.find((c) => c.name === colName);
434
+ return { field: col?.field ?? colName, tsType: col?.tsType ?? 'unknown' };
435
+ };
436
+ // Flat branches: one object per unique constraint carrying its columns.
437
+ const flatBranches = uniqueSets.map((cols) => {
280
438
  const fields = cols.map((colName) => {
281
- const col = table.columns.find((c) => c.name === colName);
282
- const field = col?.field ?? colName;
283
- const tsType = col?.tsType ?? 'unknown';
284
- return `${field}: ${tsType}`;
439
+ const m = memberType(colName);
440
+ return `${quoteIfNeeded(m.field)}: ${m.tsType}`;
285
441
  });
286
442
  return `{ ${fields.join('; ')} }`;
287
443
  });
444
+ // Prisma-style compound-unique SELECTOR branches: a synthetic key
445
+ // (`orgId_userId`) whose value holds the member columns, emitted for every
446
+ // COMPOSITE unique (PK, composite UNIQUE constraint, or composite UNIQUE
447
+ // index). Runtime expansion lives in query/compound-unique.ts.
448
+ const compoundSeen = new Set();
449
+ const compoundSets = [];
450
+ const addCompound = (cols) => {
451
+ if (cols.length < 2)
452
+ return;
453
+ const key = cols.join(',');
454
+ if (compoundSeen.has(key))
455
+ return;
456
+ compoundSeen.add(key);
457
+ compoundSets.push(cols);
458
+ };
459
+ addCompound(table.primaryKey);
460
+ for (const uc of table.uniqueColumns)
461
+ addCompound(uc);
462
+ for (const idx of table.indexes) {
463
+ if (idx.unique && !idx.docPath)
464
+ addCompound(idx.columns);
465
+ }
466
+ const selectorEntries = compoundSets.map((cols) => {
467
+ const members = cols.map(memberType);
468
+ return {
469
+ selectorName: members.map((m) => m.field).join('_'),
470
+ memberType: `{ ${members.map((m) => `${quoteIfNeeded(m.field)}: ${m.tsType}`).join('; ')} }`,
471
+ };
472
+ });
473
+ // Named helper type for annotating a compound selector by hand (Prisma parity).
474
+ if (selectorEntries.length > 0) {
475
+ const cuFields = selectorEntries.map((e) => `${e.selectorName}: ${e.memberType}`);
476
+ lines.push(`export type ${typeName}CompoundUniques = { ${cuFields.join('; ')} };`);
477
+ }
478
+ const selectorBranches = selectorEntries.map((e) => `{ ${e.selectorName}: ${e.memberType} }`);
479
+ const branches = [...flatBranches, ...selectorBranches];
288
480
  lines.push(`export type ${typeName}WhereUnique = ${branches.join(' | ')};`);
289
481
  lines.push('');
290
482
  }
@@ -414,7 +606,7 @@ function generateZod(schema, options) {
414
606
  let expr = zodBaseType(col, schema.enums);
415
607
  if (col.nullable)
416
608
  expr += '.nullable()';
417
- lines.push(` ${col.field}: ${expr},`);
609
+ lines.push(` ${quoteIfNeeded(col.field)}: ${expr},`);
418
610
  }
419
611
  lines.push('});');
420
612
  lines.push('');
@@ -431,7 +623,7 @@ function generateZod(schema, options) {
431
623
  expr += '.nullable()';
432
624
  if (col.hasDefault || col.nullable || isPk)
433
625
  expr += '.optional()';
434
- lines.push(` ${col.field}: ${expr},`);
626
+ lines.push(` ${quoteIfNeeded(col.field)}: ${expr},`);
435
627
  }
436
628
  lines.push('});');
437
629
  lines.push('');
@@ -447,7 +639,7 @@ function generateZod(schema, options) {
447
639
  if (col.nullable)
448
640
  expr += '.nullable()';
449
641
  expr += '.optional()';
450
- lines.push(` ${col.field}: ${expr},`);
642
+ lines.push(` ${quoteIfNeeded(col.field)}: ${expr},`);
451
643
  }
452
644
  lines.push('});');
453
645
  lines.push('');
@@ -477,7 +669,7 @@ function generateMetadata(schema, options) {
477
669
  // columnMap
478
670
  lines.push(' columnMap: {');
479
671
  for (const [field, col] of Object.entries(table.columnMap)) {
480
- lines.push(` ${field}: '${escSQ(col)}',`);
672
+ lines.push(` ${quoteIfNeeded(field)}: '${escSQ(col)}',`);
481
673
  }
482
674
  lines.push(' },');
483
675
  // reverseColumnMap
@@ -577,11 +769,17 @@ function generateIndex(schema, options) {
577
769
  const hasSafeRelations = new Map();
578
770
  for (const t of tableEntries)
579
771
  hasSafeRelations.set(t.name, typeSafeRelations(t, false).length > 0);
772
+ // F3: sibling-import extension. Defaults to '.js' for direct callers so their
773
+ // output stays byte-stable; `generate()` resolves it (auto / js / none).
774
+ const ext = options?.importExt ?? '.js';
580
775
  const lines = [
581
776
  ...generatedFileHeader(options),
777
+ // Record the resolved import mode for debuggability (only when generate()
778
+ // drove it, so direct generateIndex() callers stay byte-identical).
779
+ ...(options?.importMode ? [`// Sibling imports resolved with importExtension: ${options.importMode}.`, ''] : []),
582
780
  "import { TurbineClient as BaseTurbineClient, TransactionClient as BaseTransactionClient, QueryInterface } from 'turbine-orm';",
583
781
  "import type { TurbineConfig, TransactionOptions, DeferredQuery, PipelineResults } from 'turbine-orm';",
584
- "import { SCHEMA } from './metadata.js';",
782
+ `import { SCHEMA } from './metadata${ext}';`,
585
783
  ];
586
784
  // Import all entity types and relations maps
587
785
  const typeImports = [];
@@ -591,7 +789,7 @@ function generateIndex(schema, options) {
591
789
  typeImports.push(`${entityName(t.name)}Relations`);
592
790
  }
593
791
  }
594
- lines.push(`import type { ${typeImports.join(', ')} } from './types.js';`);
792
+ lines.push(`import type { ${typeImports.join(', ')} } from './types${ext}';`);
595
793
  lines.push('');
596
794
  // -------------------------------------------------------------------------
597
795
  // TypedTransactionClient — same typed table accessors as TurbineClient,
@@ -702,11 +900,70 @@ function generateIndex(schema, options) {
702
900
  lines.push('}');
703
901
  lines.push('');
704
902
  // Re-export everything
705
- lines.push("export * from './types.js';");
706
- lines.push("export { SCHEMA } from './metadata.js';");
903
+ lines.push(`export * from './types${ext}';`);
904
+ lines.push(`export { SCHEMA } from './metadata${ext}';`);
905
+ lines.push('');
906
+ return lines.join('\n');
907
+ }
908
+ // ---------------------------------------------------------------------------
909
+ // prisma-map.ts generator
910
+ // ---------------------------------------------------------------------------
911
+ /**
912
+ * Serialize a resolved {@link PrismaCompatMap} into a `prisma-map.ts` module
913
+ * that exports `export const PRISMA_MAP: PrismaCompatMap = {...}`. Written next
914
+ * to the generated client by `turbine migrate-from-prisma`; consumed later by
915
+ * the phase-2 `turbine-orm/prisma-compat` runtime adapter.
916
+ *
917
+ * Deterministic: uses the same reproducible header as the other emitters, so a
918
+ * stable input regenerates byte-identical output under `noTimestamp`.
919
+ */
920
+ function generatePrismaMap(map, options) {
921
+ const lines = [
922
+ ...generatedFileHeader(options),
923
+ "import type { PrismaCompatMap } from 'turbine-orm';",
924
+ '',
925
+ '/**',
926
+ ' * Name map from your Prisma schema onto this Turbine client. Every entry was',
927
+ ' * resolved against the live database; unresolved items are omitted (see the',
928
+ ' * migration report). Pass this to the phase-2 prisma-compat adapter, or read',
929
+ ' * it directly for hand-written compatibility wrappers.',
930
+ ' */',
931
+ 'export const PRISMA_MAP: PrismaCompatMap = {',
932
+ ' models: {',
933
+ ];
934
+ for (const [modelName, model] of Object.entries(map.models)) {
935
+ lines.push(` ${quoteIfNeeded(modelName)}: {`);
936
+ lines.push(` table: '${escSQ(model.table)}',`);
937
+ lines.push(` accessor: '${escSQ(model.accessor)}',`);
938
+ lines.push(` fields: ${serializeStringRecord(model.fields)},`);
939
+ lines.push(' relations: {');
940
+ for (const [rel, r] of Object.entries(model.relations)) {
941
+ lines.push(` ${quoteIfNeeded(rel)}: { name: '${escSQ(r.name)}', cardinality: '${r.cardinality}' },`);
942
+ }
943
+ lines.push(' },');
944
+ lines.push(` compoundUniques: ${serializeStringArrayRecord(model.compoundUniques)},`);
945
+ lines.push(' },');
946
+ }
947
+ lines.push(' },');
948
+ lines.push(` enums: ${serializeStringRecord(map.enums)},`);
949
+ lines.push('};');
707
950
  lines.push('');
708
951
  return lines.join('\n');
709
952
  }
953
+ /** Serialize a `Record<string, string>` as an inline object literal. */
954
+ function serializeStringRecord(rec) {
955
+ const entries = Object.entries(rec);
956
+ if (entries.length === 0)
957
+ return '{}';
958
+ return `{ ${entries.map(([k, v]) => `${quoteIfNeeded(k)}: '${escSQ(v)}'`).join(', ')} }`;
959
+ }
960
+ /** Serialize a `Record<string, string[]>` as an inline object literal. */
961
+ function serializeStringArrayRecord(rec) {
962
+ const entries = Object.entries(rec);
963
+ if (entries.length === 0)
964
+ return '{}';
965
+ return `{ ${entries.map(([k, v]) => `${quoteIfNeeded(k)}: [${v.map((s) => `'${escSQ(s)}'`).join(', ')}]`).join(', ')} }`;
966
+ }
710
967
  // ---------------------------------------------------------------------------
711
968
  // Helpers
712
969
  // ---------------------------------------------------------------------------
package/dist/cjs/index.js CHANGED
@@ -34,8 +34,8 @@
34
34
  * ```
35
35
  */
36
36
  Object.defineProperty(exports, "__esModule", { value: true });
37
- exports.applyManyToManyRelations = exports.snakeToPascal = exports.snakeToCamel = exports.singularize = exports.pgTypeToTs = exports.pgArrayType = exports.normalizeKeyColumns = exports.isDateType = exports.camelToSnake = exports.validateChannel = exports.QueryInterface = exports.pipelineSupported = exports.executePipeline = exports.hasRelationFields = exports.executeNestedUpdate = exports.executeNestedCreate = exports.introspect = exports.generate = exports.wrapPgError = exports.ValidationError = exports.UnsupportedFeatureError = exports.UniqueConstraintError = exports.TurbineErrorCode = exports.TurbineError = exports.TimeoutError = exports.setErrorMessageMode = exports.SerializationFailureError = exports.RelationError = exports.ReadOnlyError = exports.PipelineError = exports.OptimisticLockError = exports.NotNullViolationError = exports.NotFoundError = exports.MigrationError = exports.getErrorMessageMode = exports.ForeignKeyError = exports.ExclusionConstraintError = exports.DeadlockError = exports.ConnectionError = exports.CircularRelationError = exports.CheckConstraintError = exports.postgresDialect = exports.withRetry = exports.TurbineClient = exports.TransactionClient = exports.yugabytedb = exports.timescale = exports.postgresql = exports.cockroachdb = exports.alloydb = void 0;
38
- exports.TypedSqlQuery = exports.buildTypedSql = exports.turbineHttp = exports.defineSeed = exports.schemaToSQLString = exports.schemaToSQL = exports.schemaPush = exports.schemaDiff = exports.DestructivePushRefusal = exports.schemaDefToMetadata = exports.table = exports.isDocFieldIndexDef = exports.defineSchema = exports.column = exports.ColumnBuilder = void 0;
37
+ exports.withDbFieldNames = exports.snakeToPascal = exports.snakeToCamel = exports.singularize = exports.pgTypeToTs = exports.pgArrayType = exports.normalizeKeyColumns = exports.isDateType = exports.camelToSnake = exports.validateChannel = exports.QueryInterface = exports.pipelineSupported = exports.executePipeline = exports.hasRelationFields = exports.executeNestedUpdate = exports.executeNestedCreate = exports.introspect = exports.generate = exports.wrapPgError = exports.ValidationError = exports.UnsupportedFeatureError = exports.UniqueConstraintError = exports.TurbineErrorCode = exports.TurbineError = exports.TimeoutError = exports.setErrorMessageMode = exports.SerializationFailureError = exports.RelationError = exports.ReadOnlyError = exports.PipelineError = exports.OptimisticLockError = exports.NotNullViolationError = exports.NotFoundError = exports.MigrationError = exports.getErrorMessageMode = exports.ForeignKeyError = exports.ExclusionConstraintError = exports.DeadlockError = exports.ConnectionError = exports.CircularRelationError = exports.CheckConstraintError = exports.postgresDialect = exports.withRetry = exports.TurbineClient = exports.TransactionClient = exports.yugabytedb = exports.timescale = exports.postgresql = exports.cockroachdb = exports.alloydb = void 0;
38
+ exports.TypedSqlQuery = exports.buildTypedSql = exports.turbineHttp = exports.defineSeed = exports.schemaToSQLString = exports.schemaToSQL = exports.schemaPush = exports.schemaDiff = exports.DestructivePushRefusal = exports.schemaDefToMetadata = exports.table = exports.isDocFieldIndexDef = exports.defineSchema = exports.column = exports.ColumnBuilder = exports.applyManyToManyRelations = void 0;
39
39
  var index_js_1 = require("./adapters/index.js");
40
40
  Object.defineProperty(exports, "alloydb", { enumerable: true, get: function () { return index_js_1.alloydb; } });
41
41
  Object.defineProperty(exports, "cockroachdb", { enumerable: true, get: function () { return index_js_1.cockroachdb; } });
@@ -105,6 +105,7 @@ Object.defineProperty(exports, "pgTypeToTs", { enumerable: true, get: function (
105
105
  Object.defineProperty(exports, "singularize", { enumerable: true, get: function () { return schema_js_1.singularize; } });
106
106
  Object.defineProperty(exports, "snakeToCamel", { enumerable: true, get: function () { return schema_js_1.snakeToCamel; } });
107
107
  Object.defineProperty(exports, "snakeToPascal", { enumerable: true, get: function () { return schema_js_1.snakeToPascal; } });
108
+ Object.defineProperty(exports, "withDbFieldNames", { enumerable: true, get: function () { return schema_js_1.withDbFieldNames; } });
108
109
  // Schema builder — define schemas in TypeScript
109
110
  var schema_builder_js_1 = require("./schema-builder.js");
110
111
  Object.defineProperty(exports, "applyManyToManyRelations", { enumerable: true, get: function () { return schema_builder_js_1.applyManyToManyRelations; } });