turbine-orm 0.5.0 → 0.7.1

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 (50) hide show
  1. package/README.md +292 -26
  2. package/dist/cjs/cli/config.js +5 -15
  3. package/dist/cjs/cli/index.js +311 -43
  4. package/dist/cjs/cli/loader.js +129 -0
  5. package/dist/cjs/cli/migrate.js +96 -47
  6. package/dist/cjs/cli/ui.js +5 -9
  7. package/dist/cjs/client.js +158 -49
  8. package/dist/cjs/errors.js +424 -0
  9. package/dist/cjs/generate.js +145 -14
  10. package/dist/cjs/index.js +43 -20
  11. package/dist/cjs/introspect.js +3 -5
  12. package/dist/cjs/pipeline.js +9 -2
  13. package/dist/cjs/query.js +544 -115
  14. package/dist/cjs/schema-builder.js +150 -30
  15. package/dist/cjs/schema-sql.js +241 -37
  16. package/dist/cjs/schema.js +5 -2
  17. package/dist/cjs/serverless.js +88 -176
  18. package/dist/cli/config.js +6 -16
  19. package/dist/cli/index.js +316 -48
  20. package/dist/cli/loader.d.ts +45 -0
  21. package/dist/cli/loader.js +91 -0
  22. package/dist/cli/migrate.d.ts +13 -2
  23. package/dist/cli/migrate.js +97 -48
  24. package/dist/cli/ui.d.ts +1 -1
  25. package/dist/cli/ui.js +5 -9
  26. package/dist/client.d.ts +92 -4
  27. package/dist/client.js +158 -49
  28. package/dist/errors.d.ts +225 -0
  29. package/dist/errors.js +405 -0
  30. package/dist/generate.d.ts +7 -1
  31. package/dist/generate.js +148 -18
  32. package/dist/index.d.ts +11 -9
  33. package/dist/index.js +16 -12
  34. package/dist/introspect.d.ts +1 -1
  35. package/dist/introspect.js +4 -6
  36. package/dist/pipeline.d.ts +1 -1
  37. package/dist/pipeline.js +9 -2
  38. package/dist/query.d.ts +374 -38
  39. package/dist/query.js +545 -116
  40. package/dist/schema-builder.d.ts +38 -5
  41. package/dist/schema-builder.js +150 -31
  42. package/dist/schema-sql.d.ts +7 -3
  43. package/dist/schema-sql.js +241 -37
  44. package/dist/schema.d.ts +1 -1
  45. package/dist/schema.js +5 -2
  46. package/dist/serverless.d.ts +92 -139
  47. package/dist/serverless.js +87 -173
  48. package/package.json +33 -16
  49. package/dist/types.d.ts +0 -93
  50. package/dist/types.js +0 -126
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # turbine-orm
2
2
 
3
- The performance-first Postgres ORM. Prisma-compatible API, 2-3x faster nested queries, zero runtime overhead beyond `pg`.
3
+ Postgres-native TypeScript ORM runs on **Neon, Vercel Postgres, Cloudflare, Supabase**, and any pg-compatible driver. Streaming cursors, typed errors, single-query nested relations. 1 dependency, ~110KB.
4
4
 
5
5
  ```
6
6
  npm install turbine-orm
@@ -8,30 +8,35 @@ npm install turbine-orm
8
8
 
9
9
  ## Why Turbine?
10
10
 
11
- ORMs like Prisma fetch nested relations with N+1 queries -- one query per nesting level. Turbine uses Postgres `json_agg` to resolve the entire object graph in **a single SQL query**. The result is fewer round-trips, less connection overhead, and significantly lower latency.
11
+ Turbine is a PostgreSQL-native TypeScript ORM with features no other ORM offers together: **cursor-based streaming** through nested relations, **typed error classes** with PostgreSQL constraint mapping, **pipeline batching** (N queries, 1 round-trip), **middleware**, and a driver-agnostic core that plugs into any pg-compatible pool so it runs on Vercel Edge, Cloudflare Workers, Deno Deploy, and similar environments. It resolves nested relations in a single SQL query using `json_agg` an approach now shared by Prisma 7+ and Drizzle v2, but Turbine does it with 1 runtime dependency (`pg`) and ~110KB on npm.
12
12
 
13
- **One query instead of N+1.** When you write `db.users.findMany({ with: { posts: { with: { comments: true } } } })`, Turbine generates a single SQL statement that returns fully-nested JSON. Prisma sends 3 separate queries. Drizzle uses LATERAL joins which are competitive, but Turbine still wins on median latency.
13
+ **One query for nested relations.** When you write `db.users.findMany({ with: { posts: { with: { comments: true } } } })`, Turbine generates a single SQL statement using correlated subqueries with `json_agg`. Modern ORMs like Prisma 7+ and Drizzle v2 use similar single-query approaches (LATERAL JOINs). Turbine's advantage is architectural simplicity: 1 dependency, no code generation DSL, and PostgreSQL-native depth.
14
14
 
15
15
  ## Benchmarks
16
16
 
17
- Production results from Vercel Serverless hitting Neon Postgres (20 iterations, warm):
17
+ Tested against **Prisma 7.6** (adapter-pg, relationJoins) and **Drizzle 0.45** (relational queries) on the same PostgreSQL database. 200 iterations, 20 warmup, Node v22.
18
18
 
19
- | Scenario | Turbine | Drizzle | Prisma |
19
+ | Scenario | Turbine | Prisma 7 | Drizzle v2 |
20
20
  |---|---|---|---|
21
- | **Nested L3 (median)** | **5.3ms** | 6.5ms | 7.4ms |
22
- | Nested L3 (min) | **4.4ms** | 5.7ms | 6.0ms |
23
- | Nested L2 | **6.5ms** | 9.1ms | 10.2ms |
24
- | Simple select | 5.6ms | 7.1ms | 3.9ms |
25
-
26
- Local Docker results (50K iterations, HDR histograms):
27
-
28
- | Scenario | Turbine | Drizzle | Prisma |
21
+ | **findMany 100 rows (flat)** | **0.39 ms** | 0.58 ms | 0.44 ms |
22
+ | **findMany L2 nested (users + posts)** | **1.29 ms** | 1.84 ms | 1.30 ms |
23
+ | **findMany L3 nested (users → posts → comments)** | **0.50 ms** | 0.91 ms | 0.69 ms |
24
+ | **findUnique by PK** | **0.08 ms** | 0.13 ms | 0.14 ms |
25
+ | **findUnique — L3 nested** | **0.18 ms** | 0.32 ms | 0.34 ms |
26
+ | **count** | **0.06 ms** | 0.10 ms | 0.08 ms |
27
+
28
+ | Scenario | Turbine | Prisma 7 | Drizzle v2 |
29
29
  |---|---|---|---|
30
- | **L2 nested p50** | **201us** | 523us | 835us |
31
- | **L2 nested RPS (c=50)** | **24,041** | 6,360 | 3,784 |
32
- | L2 nested memory | 109MB | 117MB | 233MB |
30
+ | findMany flat | **1.00x** | 1.51x | 1.15x |
31
+ | findMany — L2 nested | **1.00x** | 1.43x | 1.01x |
32
+ | findMany — L3 nested | **1.00x** | 1.81x | 1.38x |
33
+ | findUnique by PK | **1.00x** | 1.67x | 1.69x |
34
+ | findUnique — L3 nested | **1.00x** | 1.81x | 1.93x |
35
+ | count | **1.00x** | 1.70x | 1.38x |
36
+
37
+ Turbine is fastest in every scenario. The advantage is largest on deep nesting (1.8x vs Prisma, up to 1.9x vs Drizzle) and single-record lookups (1.7x). All three ORMs now use single-query approaches for nested relations — Turbine's advantage comes from lower per-query overhead (minimal JS object allocation, no query plan compilation layer, direct pg driver access).
33
38
 
34
- Turbine is 2.6x faster than Drizzle and 4.2x faster than Prisma on nested queries at p50. Throughput is 3.8x higher than Drizzle and 6.3x higher than Prisma.
39
+ > Reproduce: `cd benchmarks && npm install && npx prisma generate && DATABASE_URL=... npx tsx bench.ts`
35
40
 
36
41
  ## Quick Start
37
42
 
@@ -142,6 +147,30 @@ const deleted = await db.users.delete({
142
147
  });
143
148
  ```
144
149
 
150
+ ### Atomic update operators
151
+
152
+ For race-free counter updates, pass an operator object instead of a literal. Turbine generates `col = col + $n` style SQL so concurrent updates are safe.
153
+
154
+ ```typescript
155
+ // Atomic increment — no read-modify-write race
156
+ await db.posts.update({
157
+ where: { id: 1 },
158
+ data: { viewCount: { increment: 1 } },
159
+ });
160
+
161
+ // Other supported operators on numeric columns
162
+ await db.posts.update({
163
+ where: { id: 1 },
164
+ data: {
165
+ viewCount: { increment: 5 },
166
+ likesCount: { decrement: 1 },
167
+ score: { multiply: 2 },
168
+ rank: { divide: 2 },
169
+ title: { set: 'New title' }, // explicit set, equivalent to a literal
170
+ },
171
+ });
172
+ ```
173
+
145
174
  ### Transactions
146
175
 
147
176
  ```typescript
@@ -188,6 +217,22 @@ const users = await db.users.findMany({
188
217
  // Generates: WHERE email ILIKE '%alice%'
189
218
  ```
190
219
 
220
+ ### Streaming large result sets
221
+
222
+ ```typescript
223
+ // Stream rows using PostgreSQL cursors — constant memory, no matter how many rows
224
+ for await (const user of db.users.findManyStream({
225
+ where: { orgId: 1 },
226
+ batchSize: 500, // internal FETCH batch size (default: 100)
227
+ orderBy: { id: 'asc' },
228
+ with: { posts: true }, // nested relations work too
229
+ })) {
230
+ process.stdout.write(`${user.email}\n`);
231
+ }
232
+ ```
233
+
234
+ Uses `DECLARE CURSOR` under the hood — rows are fetched in batches on a dedicated connection, parsed individually, and yielded via `AsyncGenerator`. Safe to `break` early; the cursor and connection are cleaned up automatically.
235
+
191
236
  ### Query timeout
192
237
 
193
238
  ```typescript
@@ -227,6 +272,98 @@ db.$use(async (params, next) => {
227
272
  });
228
273
  ```
229
274
 
275
+ ### Error handling
276
+
277
+ Turbine throws typed errors you can catch programmatically:
278
+
279
+ ```typescript
280
+ import { NotFoundError, ValidationError, TimeoutError } from 'turbine-orm';
281
+
282
+ try {
283
+ const user = await db.users.findUniqueOrThrow({ where: { id: 999 } });
284
+ } catch (err) {
285
+ if (err instanceof NotFoundError) {
286
+ // err.code === 'TURBINE_E001'
287
+ console.log('User not found');
288
+ } else if (err instanceof TimeoutError) {
289
+ // err.code === 'TURBINE_E002'
290
+ console.log('Query timed out');
291
+ } else if (err instanceof ValidationError) {
292
+ // err.code === 'TURBINE_E003'
293
+ console.log('Invalid query:', err.message);
294
+ }
295
+ }
296
+ ```
297
+
298
+ Error codes: `TURBINE_E001` (NotFound), `TURBINE_E002` (Timeout), `TURBINE_E003` (Validation), `TURBINE_E004` (Connection), `TURBINE_E005` (Relation), `TURBINE_E006` (Migration), `TURBINE_E007` (CircularRelation).
299
+
300
+ ## WHERE Operator Reference
301
+
302
+ Every operator supported by the `where` clause. Operators compose freely with `AND`, `OR`, `NOT`, and the relation filters `some` / `every` / `none`.
303
+
304
+ ### Equality
305
+
306
+ | Operator | Description | Example |
307
+ |---|---|---|
308
+ | literal | Implicit equality | `where: { email: 'a@b.com' }` |
309
+ | `equals` | Explicit equality | `where: { email: { equals: 'a@b.com' } }` |
310
+ | `not` | Inequality (or `not: null` for `IS NOT NULL`) | `where: { role: { not: 'admin' } }` |
311
+
312
+ ### Sets
313
+
314
+ | Operator | Description | Example |
315
+ |---|---|---|
316
+ | `in` | Match any value in the array | `where: { id: { in: [1, 2, 3] } }` |
317
+ | `notIn` | Match none of the values in the array | `where: { role: { notIn: ['banned', 'spam'] } }` |
318
+
319
+ ### Comparison
320
+
321
+ | Operator | Description | Example |
322
+ |---|---|---|
323
+ | `gt` | Greater than | `where: { score: { gt: 100 } }` |
324
+ | `gte` | Greater than or equal | `where: { score: { gte: 100 } }` |
325
+ | `lt` | Less than | `where: { score: { lt: 100 } }` |
326
+ | `lte` | Less than or equal | `where: { score: { lte: 100 } }` |
327
+
328
+ ### String
329
+
330
+ | Operator | Description | Example |
331
+ |---|---|---|
332
+ | `contains` | Substring match (`LIKE %v%`) | `where: { title: { contains: 'sql' } }` |
333
+ | `startsWith` | Prefix match (`LIKE v%`) | `where: { email: { startsWith: 'admin@' } }` |
334
+ | `endsWith` | Suffix match (`LIKE %v`) | `where: { email: { endsWith: '@acme.com' } }` |
335
+ | `mode: 'insensitive'` | Switch any string operator to `ILIKE` | `where: { title: { contains: 'SQL', mode: 'insensitive' } }` |
336
+
337
+ LIKE wildcards in user input are escaped automatically — `%`, `_`, and `\` are treated as literals.
338
+
339
+ ### Relation filters
340
+
341
+ Filter parent rows by predicates against their related child rows. Available on `hasMany` and `hasOne` relations.
342
+
343
+ | Operator | Description | Example |
344
+ |---|---|---|
345
+ | `some` | At least one related row matches | `where: { posts: { some: { published: true } } }` |
346
+ | `every` | Every related row matches | `where: { posts: { every: { published: true } } }` |
347
+ | `none` | No related row matches | `where: { posts: { none: { published: false } } }` |
348
+
349
+ ### Array columns
350
+
351
+ Operators for Postgres array columns (`text[]`, `int[]`, etc.).
352
+
353
+ | Operator | Description | Example |
354
+ |---|---|---|
355
+ | `has` | Array contains the given element | `where: { tags: { has: 'sql' } }` |
356
+ | `hasEvery` | Array contains every element in the list | `where: { tags: { hasEvery: ['sql', 'postgres'] } }` |
357
+ | `hasSome` | Array contains at least one element from the list | `where: { tags: { hasSome: ['sql', 'mysql'] } }` |
358
+
359
+ ### Combinators
360
+
361
+ | Operator | Description | Example |
362
+ |---|---|---|
363
+ | `AND` | All sub-clauses must match | `where: { AND: [{ orgId: 1 }, { role: 'admin' }] }` |
364
+ | `OR` | Any sub-clause matches | `where: { OR: [{ role: 'admin' }, { role: 'owner' }] }` |
365
+ | `NOT` | Negate a sub-clause | `where: { NOT: { role: 'banned' } }` |
366
+
230
367
  ## CLI
231
368
 
232
369
  ```
@@ -236,17 +373,19 @@ Commands:
236
373
  init Initialize a Turbine project (creates config, dirs, templates)
237
374
  generate | pull Introspect database and generate TypeScript types + client
238
375
  push Apply schema-builder definitions to database
239
- migrate create <name> Create a new SQL migration file
240
- migrate up Apply pending migrations
241
- migrate down Rollback last migration
242
- migrate status Show applied/pending migrations
243
- seed Run seed file
244
- status Show database schema summary
376
+ migrate create <name> Create a new SQL migration file
377
+ migrate create <name> --auto Auto-generate from schema diff
378
+ migrate up Apply pending migrations
379
+ migrate down Rollback last migration
380
+ migrate status Show applied/pending migrations
381
+ seed Run seed file
382
+ status Show database schema summary
245
383
 
246
384
  Options:
247
385
  --url, -u <url> Postgres connection string
248
386
  --out, -o <dir> Output directory (default: ./generated/turbine)
249
387
  --schema, -s <name> Postgres schema (default: public)
388
+ --auto Auto-generate migration from schema diff
250
389
  --dry-run Show SQL without executing
251
390
  --verbose, -v Detailed output
252
391
  ```
@@ -279,9 +418,12 @@ npx turbine generate # Regenerate typed client
279
418
  ### Migration workflow
280
419
 
281
420
  ```bash
282
- # Create a new migration
421
+ # Create a blank migration (write SQL manually)
283
422
  npx turbine migrate create add_users_table
284
- # -> Creates turbine/migrations/001_add_users_table.sql with UP/DOWN sections
423
+
424
+ # Auto-generate migration from schema diff (compares defineSchema() vs live DB)
425
+ npx turbine migrate create add_email_index --auto
426
+ # -> Generates UP (ALTER/CREATE) and DOWN (reverse) SQL automatically
285
427
 
286
428
  # Apply all pending migrations
287
429
  npx turbine migrate up
@@ -293,6 +435,80 @@ npx turbine migrate down
293
435
  npx turbine migrate status
294
436
  ```
295
437
 
438
+ ## Serverless / Edge
439
+
440
+ Turbine's core is driver-agnostic: pass any pg-compatible pool to `TurbineConfig.pool` (or use the `turbineHttp()` factory) and Turbine runs on **Vercel Edge**, **Cloudflare Workers**, **Deno Deploy**, **Netlify Edge**, or any other environment where a direct TCP connection is unavailable. No new dependencies — install whichever driver you already use.
441
+
442
+ ### Neon Serverless (HTTP / WebSocket)
443
+
444
+ ```ts
445
+ // app/api/users/route.ts
446
+ import { Pool } from '@neondatabase/serverless';
447
+ import { turbineHttp } from 'turbine-orm/serverless';
448
+ import { schema } from '@/generated/turbine/metadata';
449
+
450
+ export const runtime = 'edge';
451
+
452
+ const pool = new Pool({ connectionString: process.env.DATABASE_URL });
453
+ const db = turbineHttp(pool, schema);
454
+
455
+ export async function GET() {
456
+ const users = await db.table('users').findMany({
457
+ with: { posts: { with: { comments: true } } },
458
+ limit: 10,
459
+ });
460
+ return Response.json(users);
461
+ }
462
+ ```
463
+
464
+ ### Vercel Postgres
465
+
466
+ ```ts
467
+ import { createPool } from '@vercel/postgres';
468
+ import { turbineHttp } from 'turbine-orm/serverless';
469
+ import { schema } from './generated/turbine/metadata.js';
470
+
471
+ const pool = createPool({ connectionString: process.env.POSTGRES_URL });
472
+ const db = turbineHttp(pool, schema);
473
+ ```
474
+
475
+ ### Supabase (direct Postgres — no HTTP proxy needed)
476
+
477
+ ```ts
478
+ import { TurbineClient } from 'turbine-orm';
479
+ import { schema } from './generated/turbine/metadata.js';
480
+
481
+ const db = new TurbineClient({
482
+ connectionString: process.env.SUPABASE_DB_URL,
483
+ ssl: { rejectUnauthorized: false },
484
+ }, schema);
485
+ ```
486
+
487
+ ### Cloudflare Workers
488
+
489
+ ```ts
490
+ import { Pool } from '@neondatabase/serverless';
491
+ import { turbineHttp } from 'turbine-orm/serverless';
492
+ import { schema } from './generated/turbine/metadata';
493
+
494
+ export default {
495
+ async fetch(req: Request, env: Env) {
496
+ const pool = new Pool({ connectionString: env.DATABASE_URL });
497
+ const db = turbineHttp(pool, schema);
498
+ const users = await db.table('users').findMany({ limit: 10 });
499
+ return Response.json(users);
500
+ },
501
+ };
502
+ ```
503
+
504
+ ### Limitations on HTTP drivers
505
+
506
+ - **Streaming cursors** (`findManyStream`) require `DECLARE CURSOR`, which most HTTP drivers don't support. Use `findMany` with `limit` + pagination instead.
507
+ - **LISTEN/NOTIFY** is not available over HTTP.
508
+ - Transactions work but hold an HTTP connection for their duration — keep them short.
509
+
510
+ When Turbine receives an external pool, `db.disconnect()` is a no-op: the caller owns the pool's lifecycle.
511
+
296
512
  ## Configuration
297
513
 
298
514
  Create `turbine.config.ts` in your project root (or run `npx turbine init`):
@@ -331,7 +547,57 @@ SELECT u.*,
331
547
  FROM users u WHERE u.org_id = 1
332
548
  ```
333
549
 
334
- This resolves the entire 3-level object graph in one database round-trip. Prisma would send 3 queries. The performance difference scales with nesting depth and network latency.
550
+ This resolves the entire 3-level object graph in one database round-trip. Prisma 7+ and Drizzle v2 also use single-query approaches (LATERAL JOINs), but Turbine's correlated subquery strategy has lower per-query overhead — see [Benchmarks](#benchmarks).
551
+
552
+ ## Type Mapping
553
+
554
+ Turbine maps Postgres types to TypeScript:
555
+
556
+ | Postgres | TypeScript | Notes |
557
+ |---|---|---|
558
+ | `int2`, `int4`, `float4`, `float8` | `number` | Standard numeric types |
559
+ | `int8` / `bigint` | `number` | Values > `Number.MAX_SAFE_INTEGER` (2^53 - 1) are returned as `string` at runtime to avoid precision loss. This affects < 0.01% of use cases (auto-increment IDs, counts, etc. are all safe). |
560
+ | `numeric`, `money` | `string` | Arbitrary precision — kept as string to avoid JS float issues |
561
+ | `text`, `varchar`, `uuid`, `citext` | `string` | |
562
+ | `timestamptz`, `timestamp`, `date` | `Date` | |
563
+ | `boolean` | `boolean` | |
564
+ | `json`, `jsonb` | `unknown` | |
565
+ | `bytea` | `Buffer` | |
566
+ | Array types | `T[]` | e.g. `_text` → `string[]` |
567
+
568
+ ## Comparison
569
+
570
+ | | **Turbine** | **Prisma** | **Drizzle** | **Kysely** |
571
+ |---|---|---|---|---|
572
+ | **Nested relations** | 1 query (`json_agg`) | 1 query (LATERAL JOIN + json_agg, since v5.8) | 1 query (LATERAL JOINs) | Manual (`jsonArrayFrom`) |
573
+ | **API style** | `findMany`, `with` | `findMany`, `include` | SQL-like + relational | SQL builder |
574
+ | **Schema** | TypeScript | Custom DSL (`.prisma`) | TypeScript | Manual interfaces |
575
+ | **Runtime deps** | 1 (`pg`) | `@prisma/client` + adapter | 0 | 0 |
576
+ | **Multi-DB** | PostgreSQL only | PG, MySQL, SQLite, MSSQL | PG, MySQL, SQLite | PG, MySQL, SQLite |
577
+ | **Code generation** | `turbine generate` | `prisma generate` | Not needed | Not needed |
578
+
579
+ All three ORMs now use single-query approaches for nested relations. Turbine uses correlated subqueries with `json_agg`, Prisma 7 uses LATERAL JOIN + `json_agg`, and Drizzle uses LATERAL JOINs. Turbine is 1.4–1.9x faster due to lower per-query overhead — minimal JS object allocation, no query plan compilation layer, and direct pg driver access. See [Benchmarks](#benchmarks) for full results.
580
+
581
+ ## Limitations
582
+
583
+ Turbine is focused and opinionated. Here's what it doesn't do:
584
+
585
+ - **PostgreSQL only.** No MySQL, SQLite, or MSSQL. This is by design — the `json_agg` approach is PostgreSQL-specific, and going deep on one database enables the performance advantage.
586
+ - **No full-text search operators.** TSVECTOR/TSQUERY are not exposed in the query builder. Use `db.raw` for full-text queries.
587
+ - **Large nested result sets.** `json_agg` builds the entire JSON array in PostgreSQL memory. For relations with 10K+ rows, always use `limit` in your `with` clause to cap the aggregation size.
588
+ - **No admin UI.** Turbine Studio is planned but not yet available.
589
+
590
+ ## Examples
591
+
592
+ - **[Next.js](./examples/nextjs/)** — Server-rendered app with nested relations, streaming, and live code demos
593
+ - **[Neon Edge](./examples/neon-edge/)** — Vercel Edge route handler talking to Neon over HTTP via `@neondatabase/serverless`
594
+ - **[Vercel Postgres](./examples/vercel-postgres/)** — Next.js app router route handler on `@vercel/postgres`
595
+ - **[Cloudflare Worker](./examples/cloudflare-worker/)** — Worker `fetch` handler with `pg` over Cloudflare Hyperdrive
596
+ - **[Supabase](./examples/supabase/)** — Standalone script over the standard `pg` driver against Supabase
597
+
598
+ ## Guides
599
+
600
+ - **[Migrating from Prisma](./docs/migrate-from-prisma.md)** — API mapping table, side-by-side `findMany`, and notes on the differences
335
601
 
336
602
  ## Requirements
337
603
 
@@ -49,12 +49,7 @@ const node_url_1 = require("node:url");
49
49
  // ---------------------------------------------------------------------------
50
50
  // Config file names, in priority order
51
51
  // ---------------------------------------------------------------------------
52
- const CONFIG_FILES = [
53
- 'turbine.config.ts',
54
- 'turbine.config.mts',
55
- 'turbine.config.js',
56
- 'turbine.config.mjs',
57
- ];
52
+ const CONFIG_FILES = ['turbine.config.ts', 'turbine.config.mts', 'turbine.config.js', 'turbine.config.mjs'];
58
53
  // ---------------------------------------------------------------------------
59
54
  // Load config
60
55
  // ---------------------------------------------------------------------------
@@ -106,10 +101,7 @@ function findConfigFile(cwd) {
106
101
  */
107
102
  function resolveConfig(fileConfig, overrides) {
108
103
  return {
109
- url: overrides.url ??
110
- process.env['DATABASE_URL'] ??
111
- fileConfig.url ??
112
- '',
104
+ url: overrides.url ?? process.env.DATABASE_URL ?? fileConfig.url ?? '',
113
105
  out: overrides.out ?? fileConfig.out ?? './generated/turbine',
114
106
  schema: overrides.schema ?? fileConfig.schema ?? 'public',
115
107
  include: overrides.include ?? fileConfig.include ?? [],
@@ -123,15 +115,13 @@ function resolveConfig(fileConfig, overrides) {
123
115
  // Config file template (for `turbine init`)
124
116
  // ---------------------------------------------------------------------------
125
117
  function configTemplate(connectionString) {
126
- const url = connectionString ?? 'process.env.DATABASE_URL';
127
- const urlLine = connectionString
128
- ? ` url: '${connectionString}',`
129
- : ` url: process.env.DATABASE_URL,`;
118
+ const _url = connectionString ?? 'process.env.DATABASE_URL';
119
+ const urlLine = connectionString ? ` url: '${connectionString}',` : ` url: process.env.DATABASE_URL,`;
130
120
  return `import type { TurbineCliConfig } from 'turbine-orm/cli';
131
121
 
132
122
  /**
133
123
  * Turbine configuration
134
- * @see https://batadata.com/docs/turbine/config
124
+ * @see https://turbineorm.dev
135
125
  */
136
126
  const config: TurbineCliConfig = {
137
127
  /** Postgres connection string */