turbine-orm 0.9.2 → 0.10.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.
package/dist/index.js CHANGED
@@ -32,6 +32,7 @@
32
32
  * await db.disconnect();
33
33
  * ```
34
34
  */
35
+ export { alloydb, cockroachdb, postgresql, timescale, yugabytedb } from './adapters/index.js';
35
36
  // Client
36
37
  export { TransactionClient, TurbineClient, } from './client.js';
37
38
  // Error types
@@ -45,7 +46,7 @@ export { executePipeline, pipelineSupported } from './pipeline.js';
45
46
  // Query builder
46
47
  export { QueryInterface, } from './query/index.js';
47
48
  // Schema utilities
48
- export { camelToSnake, isDateType, pgArrayType, pgTypeToTs, singularize, snakeToCamel, snakeToPascal, } from './schema.js';
49
+ export { camelToSnake, isDateType, normalizeKeyColumns, pgArrayType, pgTypeToTs, singularize, snakeToCamel, snakeToPascal, } from './schema.js';
49
50
  // Schema builder — define schemas in TypeScript
50
51
  export { ColumnBuilder, column, defineSchema,
51
52
  // Legacy compat (deprecated — use object format with defineSchema)
@@ -66,13 +66,16 @@ const SQL_FOREIGN_KEYS = `
66
66
  const SQL_UNIQUE_CONSTRAINTS = `
67
67
  SELECT
68
68
  tc.table_name,
69
- kcu.column_name
69
+ tc.constraint_name,
70
+ kcu.column_name,
71
+ kcu.ordinal_position
70
72
  FROM information_schema.table_constraints tc
71
73
  JOIN information_schema.key_column_usage kcu
72
74
  ON tc.constraint_name = kcu.constraint_name
73
75
  AND tc.table_schema = kcu.table_schema
74
76
  WHERE tc.constraint_type = 'UNIQUE'
75
77
  AND tc.table_schema = $1
78
+ ORDER BY tc.table_name, tc.constraint_name, kcu.ordinal_position
76
79
  `;
77
80
  const SQL_INDEXES = `
78
81
  SELECT tablename, indexname, indexdef
@@ -153,14 +156,22 @@ export async function introspect(options) {
153
156
  pkByTable.get(row.table_name).push(row.column_name);
154
157
  }
155
158
  // ----- Group unique constraints by table -----
159
+ // Group rows by (table_name, constraint_name) to correctly handle multi-column unique constraints
156
160
  const uniqueByTable = new Map();
161
+ const uniqueConstraintGroups = new Map();
157
162
  for (const row of uniqueResult.rows) {
158
163
  if (!tableSet.has(row.table_name))
159
164
  continue;
160
- if (!uniqueByTable.has(row.table_name))
161
- uniqueByTable.set(row.table_name, []);
162
- // Each unique constraint may be multi-column; for simplicity, treat as single-col here
163
- uniqueByTable.get(row.table_name).push([row.column_name]);
165
+ const key = `${row.table_name}::${row.constraint_name}`;
166
+ if (!uniqueConstraintGroups.has(key)) {
167
+ uniqueConstraintGroups.set(key, { table: row.table_name, columns: [] });
168
+ }
169
+ uniqueConstraintGroups.get(key).columns.push(row.column_name);
170
+ }
171
+ for (const { table, columns } of uniqueConstraintGroups.values()) {
172
+ if (!uniqueByTable.has(table))
173
+ uniqueByTable.set(table, []);
174
+ uniqueByTable.get(table).push(columns);
164
175
  }
165
176
  // ----- Group indexes by table -----
166
177
  const indexesByTable = new Map();
@@ -187,17 +198,25 @@ export async function introspect(options) {
187
198
  enums[row.typname] = [];
188
199
  enums[row.typname].push(row.enumlabel);
189
200
  }
190
- const foreignKeys = [];
201
+ const fkGroups = new Map();
191
202
  for (const row of fkResult.rows) {
192
203
  if (!tableSet.has(row.source_table) || !tableSet.has(row.target_table))
193
204
  continue;
194
- foreignKeys.push({
195
- sourceTable: row.source_table,
196
- sourceColumn: row.source_column,
197
- targetTable: row.target_table,
198
- targetColumn: row.target_column,
199
- });
205
+ const key = row.constraint_name;
206
+ if (!fkGroups.has(key)) {
207
+ fkGroups.set(key, {
208
+ sourceTable: row.source_table,
209
+ sourceColumns: [],
210
+ targetTable: row.target_table,
211
+ targetColumns: [],
212
+ constraintName: key,
213
+ });
214
+ }
215
+ const entry = fkGroups.get(key);
216
+ entry.sourceColumns.push(row.source_column);
217
+ entry.targetColumns.push(row.target_column);
200
218
  }
219
+ const foreignKeys = Array.from(fkGroups.values());
201
220
  // ----- Build relations from foreign keys -----
202
221
  // Count FKs per (source, target) pair for disambiguation
203
222
  const fkCounts = new Map();
@@ -209,10 +228,17 @@ export async function introspect(options) {
209
228
  for (const fk of foreignKeys) {
210
229
  const pairKey = `${fk.sourceTable}→${fk.targetTable}`;
211
230
  const needsDisambiguation = (fkCounts.get(pairKey) ?? 0) > 1;
231
+ // For single-column FKs, keep string form for backwards compatibility.
232
+ // For multi-column (composite) FKs, use array form.
233
+ const foreignKey = fk.sourceColumns.length === 1 ? fk.sourceColumns[0] : fk.sourceColumns;
234
+ const referenceKey = fk.targetColumns.length === 1 ? fk.targetColumns[0] : fk.targetColumns;
212
235
  // --- belongsTo on the source (child) table ---
213
236
  // e.g. posts.user_id → users.id creates posts.user (belongsTo)
237
+ // For composite FKs with disambiguation, use the constraint name
214
238
  const belongsToName = needsDisambiguation
215
- ? snakeToCamel(fk.sourceColumn.replace(/_id$/, ''))
239
+ ? fk.sourceColumns.length === 1
240
+ ? snakeToCamel(fk.sourceColumns[0].replace(/_id$/, ''))
241
+ : snakeToCamel(fk.constraintName.replace(/^fk_/, '').replace(/_fkey$/, ''))
216
242
  : singularize(snakeToCamel(fk.targetTable));
217
243
  if (!relationsByTable.has(fk.sourceTable))
218
244
  relationsByTable.set(fk.sourceTable, {});
@@ -221,13 +247,15 @@ export async function introspect(options) {
221
247
  name: belongsToName,
222
248
  from: fk.sourceTable,
223
249
  to: fk.targetTable,
224
- foreignKey: fk.sourceColumn,
225
- referenceKey: fk.targetColumn,
250
+ foreignKey,
251
+ referenceKey,
226
252
  };
227
253
  // --- hasMany on the target (parent) table ---
228
254
  // e.g. posts.user_id → users.id creates users.posts (hasMany)
229
255
  const hasManyName = needsDisambiguation
230
- ? snakeToCamel(`${fk.sourceTable}_by_${fk.sourceColumn.replace(/_id$/, '')}`)
256
+ ? fk.sourceColumns.length === 1
257
+ ? snakeToCamel(`${fk.sourceTable}_by_${fk.sourceColumns[0].replace(/_id$/, '')}`)
258
+ : snakeToCamel(`${fk.sourceTable}_by_${fk.constraintName.replace(/^fk_/, '').replace(/_fkey$/, '')}`)
231
259
  : snakeToCamel(fk.sourceTable);
232
260
  if (!relationsByTable.has(fk.targetTable))
233
261
  relationsByTable.set(fk.targetTable, {});
@@ -236,8 +264,8 @@ export async function introspect(options) {
236
264
  name: hasManyName,
237
265
  from: fk.targetTable,
238
266
  to: fk.sourceTable,
239
- foreignKey: fk.sourceColumn,
240
- referenceKey: fk.targetColumn,
267
+ foreignKey,
268
+ referenceKey,
241
269
  };
242
270
  }
243
271
  // ----- Assemble TableMetadata for each table -----
@@ -12,7 +12,7 @@
12
12
  */
13
13
  import { CircularRelationError, NotFoundError, RelationError, TimeoutError, ValidationError, wrapPgError, } from '../errors.js';
14
14
  import { camelToSnake, snakeToCamel } from '../schema.js';
15
- import { escapeLike, escSingleQuote, LRUCache, OPERATOR_KEYS, quoteIdent, sqlToPreparedName, } from './utils.js';
15
+ import { buildCorrelation, escapeLike, escSingleQuote, LRUCache, OPERATOR_KEYS, quoteIdent, sqlToPreparedName, } from './utils.js';
16
16
  // ---------------------------------------------------------------------------
17
17
  // Internal detection helpers — used by QueryInterface
18
18
  // ---------------------------------------------------------------------------
@@ -96,6 +96,7 @@ function findArrayUniqueKey(value) {
96
96
  }
97
97
  return null;
98
98
  }
99
+ // biome-ignore lint/complexity/noBannedTypes: {} means "no relations known" — intentional for untyped table access
99
100
  export class QueryInterface {
100
101
  pool;
101
102
  table;
@@ -258,6 +259,7 @@ export class QueryInterface {
258
259
  // -------------------------------------------------------------------------
259
260
  // findUnique
260
261
  // -------------------------------------------------------------------------
262
+ // biome-ignore lint/complexity/noBannedTypes: {} means "no with clause" — matches TypedWithClause default
261
263
  async findUnique(args) {
262
264
  return this.executeWithMiddleware('findUnique', args, async () => {
263
265
  const deferred = this.buildFindUnique(args);
@@ -265,6 +267,7 @@ export class QueryInterface {
265
267
  return deferred.transform(result);
266
268
  });
267
269
  }
270
+ // biome-ignore lint/complexity/noBannedTypes: {} means "no with clause" — matches TypedWithClause default
268
271
  buildFindUnique(args) {
269
272
  const columnsList = this.resolveColumns(args.select, args.omit);
270
273
  const whereObj = args.where;
@@ -360,6 +363,7 @@ export class QueryInterface {
360
363
  // -------------------------------------------------------------------------
361
364
  // findMany
362
365
  // -------------------------------------------------------------------------
366
+ // biome-ignore lint/complexity/noBannedTypes: {} means "no with clause" — matches TypedWithClause default
363
367
  async findMany(args) {
364
368
  this.maybeWarnUnlimited(args);
365
369
  // Dev-only: warn on deeply nested with clauses
@@ -417,6 +421,7 @@ export class QueryInterface {
417
421
  }
418
422
  return maxDepth;
419
423
  }
424
+ // biome-ignore lint/complexity/noBannedTypes: {} means "no with clause" — matches TypedWithClause default
420
425
  buildFindMany(args) {
421
426
  const columnsList = this.resolveColumns(args?.select, args?.omit);
422
427
  const colKey = columnsList ? columnsList.join(',') : '*';
@@ -560,6 +565,7 @@ export class QueryInterface {
560
565
  * }
561
566
  * ```
562
567
  */
568
+ // biome-ignore lint/complexity/noBannedTypes: {} means "no with clause" — matches TypedWithClause default
563
569
  async *findManyStream(args) {
564
570
  const batchSize = Math.max(1, Math.floor(Number(args?.batchSize ?? 1000)));
565
571
  const hasRelations = !!args?.with;
@@ -616,6 +622,7 @@ export class QueryInterface {
616
622
  // -------------------------------------------------------------------------
617
623
  // findFirst — like findMany but returns a single row or null
618
624
  // -------------------------------------------------------------------------
625
+ // biome-ignore lint/complexity/noBannedTypes: {} means "no with clause" — matches TypedWithClause default
619
626
  async findFirst(args) {
620
627
  return this.executeWithMiddleware('findFirst', (args ?? {}), async () => {
621
628
  const deferred = this.buildFindFirst(args);
@@ -623,6 +630,7 @@ export class QueryInterface {
623
630
  return deferred.transform(result);
624
631
  });
625
632
  }
633
+ // biome-ignore lint/complexity/noBannedTypes: {} means "no with clause" — matches TypedWithClause default
626
634
  buildFindFirst(args) {
627
635
  // Reuse findMany's SQL builder but force LIMIT 1
628
636
  const findManyArgs = { ...args, limit: 1 };
@@ -640,6 +648,7 @@ export class QueryInterface {
640
648
  // -------------------------------------------------------------------------
641
649
  // findFirstOrThrow — like findFirst but throws if no record found
642
650
  // -------------------------------------------------------------------------
651
+ // biome-ignore lint/complexity/noBannedTypes: {} means "no with clause" — matches TypedWithClause default
643
652
  async findFirstOrThrow(args) {
644
653
  return this.executeWithMiddleware('findFirstOrThrow', (args ?? {}), async () => {
645
654
  const deferred = this.buildFindFirstOrThrow(args);
@@ -647,6 +656,7 @@ export class QueryInterface {
647
656
  return deferred.transform(result);
648
657
  });
649
658
  }
659
+ // biome-ignore lint/complexity/noBannedTypes: {} means "no with clause" — matches TypedWithClause default
650
660
  buildFindFirstOrThrow(args) {
651
661
  const inner = this.buildFindFirst(args);
652
662
  return {
@@ -669,6 +679,7 @@ export class QueryInterface {
669
679
  // -------------------------------------------------------------------------
670
680
  // findUniqueOrThrow — like findUnique but throws if no record found
671
681
  // -------------------------------------------------------------------------
682
+ // biome-ignore lint/complexity/noBannedTypes: {} means "no with clause" — matches TypedWithClause default
672
683
  async findUniqueOrThrow(args) {
673
684
  return this.executeWithMiddleware('findUniqueOrThrow', args, async () => {
674
685
  const deferred = this.buildFindUniqueOrThrow(args);
@@ -676,6 +687,7 @@ export class QueryInterface {
676
687
  return deferred.transform(result);
677
688
  });
678
689
  }
690
+ // biome-ignore lint/complexity/noBannedTypes: {} means "no with clause" — matches TypedWithClause default
679
691
  buildFindUniqueOrThrow(args) {
680
692
  const inner = this.buildFindUnique(args);
681
693
  return {
@@ -2013,15 +2025,15 @@ export class QueryInterface {
2013
2025
  const qt = quoteIdent(targetTable);
2014
2026
  const qSelf = quoteIdent(this.table);
2015
2027
  const clauses = [];
2016
- // Correlation: link child table to parent table
2028
+ // Correlation: link child table to parent table (supports composite FKs)
2017
2029
  let correlation;
2018
2030
  if (relDef.type === 'hasMany' || relDef.type === 'hasOne') {
2019
2031
  // parent.pk = child.fk
2020
- correlation = `${qt}.${quoteIdent(relDef.foreignKey)} = ${qSelf}.${quoteIdent(relDef.referenceKey)}`;
2032
+ correlation = buildCorrelation(qt, relDef.foreignKey, qSelf, relDef.referenceKey);
2021
2033
  }
2022
2034
  else {
2023
2035
  // belongsTo: parent.fk = child.pk
2024
- correlation = `${qt}.${quoteIdent(relDef.referenceKey)} = ${qSelf}.${quoteIdent(relDef.foreignKey)}`;
2036
+ correlation = buildCorrelation(qt, relDef.referenceKey, qSelf, relDef.foreignKey);
2025
2037
  }
2026
2038
  // "some": EXISTS (SELECT 1 FROM target WHERE correlation AND filter)
2027
2039
  if (filterObj.some !== undefined) {
@@ -2064,6 +2076,10 @@ export class QueryInterface {
2064
2076
  if (value === undefined)
2065
2077
  continue;
2066
2078
  const col = meta.columnMap[field] ?? camelToSnake(field);
2079
+ if (!meta.allColumns.includes(col)) {
2080
+ throw new ValidationError(`[turbine] Unknown field "${field}" in relation filter for table "${targetTable}". ` +
2081
+ `Known fields: ${Object.keys(meta.columnMap).join(', ') || '(none)'}.`);
2082
+ }
2067
2083
  const qCol = `${qt}.${quoteIdent(col)}`;
2068
2084
  if (value === null) {
2069
2085
  conditions.push(`${qCol} IS NULL`);
@@ -2471,12 +2487,13 @@ export class QueryInterface {
2471
2487
  // Build WHERE — correlate to parent via parentRef (alias or table name).
2472
2488
  // For hasMany: target has FK, so alias.fk = parentRef.pk
2473
2489
  // For belongsTo: source has FK, so alias.pk = parentRef.fk (reversed)
2490
+ // Supports composite foreign keys (string[]) via buildCorrelation.
2474
2491
  let whereClause;
2475
2492
  if (relDef.type === 'belongsTo' || relDef.type === 'hasOne') {
2476
- whereClause = `${alias}.${quoteIdent(relDef.referenceKey)} = ${qParent}.${quoteIdent(relDef.foreignKey)}`;
2493
+ whereClause = buildCorrelation(alias, relDef.referenceKey, qParent, relDef.foreignKey);
2477
2494
  }
2478
2495
  else {
2479
- whereClause = `${alias}.${quoteIdent(relDef.foreignKey)} = ${qParent}.${quoteIdent(relDef.referenceKey)}`;
2496
+ whereClause = buildCorrelation(alias, relDef.foreignKey, qParent, relDef.referenceKey);
2480
2497
  }
2481
2498
  // Additional filters — properly parameterized
2482
2499
  if (spec !== true && spec.where) {
@@ -7,7 +7,7 @@
7
7
  */
8
8
  export type { AggregateArgs, AggregateResult, ArrayFilter, CountArgs, CreateArgs, CreateManyArgs, DeleteArgs, DeleteManyArgs, FindManyArgs, FindManyStreamArgs, FindUniqueArgs, GroupByArgs, JsonFilter, OrderDirection, RelationDescriptor, RelationFilter, TypedWithClause, UpdateArgs, UpdateInput, UpdateManyArgs, UpdateOperatorInput, UpsertArgs, WhereClause, WhereOperator, WhereValue, WithClause, WithOptions, WithResult, } from './types.js';
9
9
  export type { SqlCacheEntry } from './utils.js';
10
- export { escapeLike, escSingleQuote, fnv1a64Hex, LRUCache, OPERATOR_KEYS, quoteIdent, sqlToPreparedName, } from './utils.js';
10
+ export { buildCorrelation, escapeLike, escSingleQuote, fnv1a64Hex, LRUCache, OPERATOR_KEYS, quoteIdent, sqlToPreparedName, } from './utils.js';
11
11
  export type { DeferredQuery, MiddlewareFn, QueryInterfaceOptions } from './builder.js';
12
12
  export { QueryInterface } from './builder.js';
13
13
  //# sourceMappingURL=index.d.ts.map
@@ -5,6 +5,6 @@
5
5
  * `import { … } from './query/index.js'` is a drop-in replacement for the
6
6
  * former monolithic `import { … } from './query.js'`.
7
7
  */
8
- export { escapeLike, escSingleQuote, fnv1a64Hex, LRUCache, OPERATOR_KEYS, quoteIdent, sqlToPreparedName, } from './utils.js';
8
+ export { buildCorrelation, escapeLike, escSingleQuote, fnv1a64Hex, LRUCache, OPERATOR_KEYS, quoteIdent, sqlToPreparedName, } from './utils.js';
9
9
  export { QueryInterface } from './builder.js';
10
10
  //# sourceMappingURL=index.js.map
@@ -57,4 +57,12 @@ export declare function fnv1a64Hex(s: string): string;
57
57
  export declare function sqlToPreparedName(sql: string): string;
58
58
  /** Known operator keys — used to detect operator objects vs plain values */
59
59
  export declare const OPERATOR_KEYS: Set<string>;
60
+ /**
61
+ * Build a correlation clause joining columns between two table references.
62
+ * Handles both single-column (string) and multi-column (string[]) foreign keys.
63
+ *
64
+ * For single-column: `"alias"."col" = "parent"."col"`
65
+ * For multi-column: `"alias"."col_a" = "parent"."ref_a" AND "alias"."col_b" = "parent"."ref_b"`
66
+ */
67
+ export declare function buildCorrelation(leftRef: string, leftColumns: string | string[], rightRef: string, rightColumns: string | string[]): string;
60
68
  //# sourceMappingURL=utils.d.ts.map
@@ -111,4 +111,21 @@ export const OPERATOR_KEYS = new Set([
111
111
  'endsWith',
112
112
  'mode',
113
113
  ]);
114
+ // ---------------------------------------------------------------------------
115
+ // Composite key correlation helper
116
+ // ---------------------------------------------------------------------------
117
+ /**
118
+ * Build a correlation clause joining columns between two table references.
119
+ * Handles both single-column (string) and multi-column (string[]) foreign keys.
120
+ *
121
+ * For single-column: `"alias"."col" = "parent"."col"`
122
+ * For multi-column: `"alias"."col_a" = "parent"."ref_a" AND "alias"."col_b" = "parent"."ref_b"`
123
+ */
124
+ export function buildCorrelation(leftRef, leftColumns, rightRef, rightColumns) {
125
+ const leftCols = Array.isArray(leftColumns) ? leftColumns : [leftColumns];
126
+ const rightCols = Array.isArray(rightColumns) ? rightColumns : [rightColumns];
127
+ return leftCols
128
+ .map((col, i) => `${leftRef}.${quoteIdent(col)} = ${rightRef}.${quoteIdent(rightCols[i])}`)
129
+ .join(' AND ');
130
+ }
114
131
  //# sourceMappingURL=utils.js.map
package/dist/schema.d.ts CHANGED
@@ -62,11 +62,13 @@ export interface RelationDef {
62
62
  from: string;
63
63
  /** Target table */
64
64
  to: string;
65
- /** FK column on the "many" / "child" side (snake_case) */
66
- foreignKey: string;
67
- /** Referenced column on the "one" / "parent" side (snake_case) */
68
- referenceKey: string;
65
+ /** FK column(s) on the "many" / "child" side (snake_case). Array for composite FKs. */
66
+ foreignKey: string | string[];
67
+ /** Referenced column(s) on the "one" / "parent" side (snake_case). Array for composite FKs. */
68
+ referenceKey: string | string[];
69
69
  }
70
+ /** Normalize foreignKey/referenceKey to always be an array for uniform processing */
71
+ export declare function normalizeKeyColumns(key: string | string[]): string[];
70
72
  export interface IndexMetadata {
71
73
  name: string;
72
74
  columns: string[];
package/dist/schema.js CHANGED
@@ -5,6 +5,13 @@
5
5
  * They're used by the query builder, code generator, and CLI.
6
6
  */
7
7
  // ---------------------------------------------------------------------------
8
+ // Helpers for composite key handling
9
+ // ---------------------------------------------------------------------------
10
+ /** Normalize foreignKey/referenceKey to always be an array for uniform processing */
11
+ export function normalizeKeyColumns(key) {
12
+ return Array.isArray(key) ? key : [key];
13
+ }
14
+ // ---------------------------------------------------------------------------
8
15
  // Type mapping: Postgres → TypeScript
9
16
  // ---------------------------------------------------------------------------
10
17
  const PG_TO_TS = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "turbine-orm",
3
- "version": "0.9.2",
3
+ "version": "0.10.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": {
@@ -18,6 +18,11 @@
18
18
  "import": "./dist/cli/config.js",
19
19
  "require": "./dist/cjs/cli/config.js",
20
20
  "types": "./dist/cli/config.d.ts"
21
+ },
22
+ "./adapters": {
23
+ "import": "./dist/adapters/index.js",
24
+ "require": "./dist/cjs/adapters/index.js",
25
+ "types": "./dist/adapters/index.d.ts"
21
26
  }
22
27
  },
23
28
  "main": "./dist/cjs/index.js",
@@ -44,7 +49,7 @@
44
49
  "status": "tsx src/cli/index.ts status",
45
50
  "examples": "tsx examples/examples.ts",
46
51
  "test": "tsx --test src/test/*.test.ts",
47
- "test:unit": "tsx --test src/test/schema-builder.test.ts src/test/errors.test.ts src/test/stress.test.ts src/test/migrate.test.ts src/test/update-operators.test.ts src/test/empty-where-guard.test.ts src/test/cli.test.ts src/test/serverless.test.ts src/test/pipeline.test.ts src/test/pipeline-submittable.test.ts src/test/with-inference.test.ts src/test/operator-validation.test.ts src/test/unlimited-warning.test.ts src/test/generate-relations.test.ts src/test/stream-and-parse.test.ts src/test/sql-cache.test.ts src/test/studio.test.ts src/test/sql-injection.test.ts src/test/cli-flags.test.ts",
52
+ "test:unit": "tsx --test src/test/schema-builder.test.ts src/test/errors.test.ts src/test/stress.test.ts src/test/migrate.test.ts src/test/update-operators.test.ts src/test/empty-where-guard.test.ts src/test/cli.test.ts src/test/serverless.test.ts src/test/pipeline.test.ts src/test/pipeline-submittable.test.ts src/test/with-inference.test.ts src/test/operator-validation.test.ts src/test/unlimited-warning.test.ts src/test/generate-relations.test.ts src/test/stream-and-parse.test.ts src/test/sql-cache.test.ts src/test/studio.test.ts src/test/sql-injection.test.ts src/test/cli-flags.test.ts src/test/cockroachdb-adapter.test.ts src/test/yugabytedb-adapter.test.ts src/test/pg-compat.test.ts src/test/relation-filter-validation.test.ts src/test/client-coverage.test.ts src/test/schema-diff.test.ts src/test/composite-fk.test.ts",
48
53
  "test:coverage": "c8 tsx --test src/test/schema-builder.test.ts src/test/errors.test.ts src/test/stress.test.ts src/test/migrate.test.ts src/test/update-operators.test.ts src/test/empty-where-guard.test.ts src/test/cli.test.ts src/test/serverless.test.ts src/test/pipeline.test.ts src/test/pipeline-submittable.test.ts src/test/with-inference.test.ts src/test/operator-validation.test.ts src/test/unlimited-warning.test.ts src/test/generate-relations.test.ts src/test/stream-and-parse.test.ts src/test/sql-cache.test.ts src/test/studio.test.ts src/test/sql-injection.test.ts src/test/cli-flags.test.ts",
49
54
  "lint": "biome check src/",
50
55
  "lint:fix": "biome check --write src/",