turbine-orm 0.5.0 → 0.7.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/README.md +194 -26
- package/dist/cjs/cli/config.js +5 -15
- package/dist/cjs/cli/index.js +240 -41
- package/dist/cjs/cli/migrate.js +71 -46
- package/dist/cjs/cli/ui.js +5 -9
- package/dist/cjs/client.js +109 -46
- package/dist/cjs/errors.js +293 -0
- package/dist/cjs/generate.js +33 -13
- package/dist/cjs/index.js +39 -20
- package/dist/cjs/introspect.js +3 -5
- package/dist/cjs/pipeline.js +9 -2
- package/dist/cjs/query.js +442 -109
- package/dist/cjs/schema-builder.js +93 -24
- package/dist/cjs/schema-sql.js +157 -19
- package/dist/cjs/schema.js +5 -2
- package/dist/cjs/serverless.js +87 -176
- package/dist/cli/config.js +6 -16
- package/dist/cli/index.js +245 -46
- package/dist/cli/migrate.d.ts +6 -1
- package/dist/cli/migrate.js +72 -47
- package/dist/cli/ui.js +5 -9
- package/dist/client.d.ts +77 -4
- package/dist/client.js +109 -46
- package/dist/errors.d.ts +138 -0
- package/dist/errors.js +278 -0
- package/dist/generate.d.ts +1 -1
- package/dist/generate.js +36 -16
- package/dist/index.d.ts +11 -9
- package/dist/index.js +16 -12
- package/dist/introspect.d.ts +1 -1
- package/dist/introspect.js +4 -6
- package/dist/pipeline.d.ts +1 -1
- package/dist/pipeline.js +9 -2
- package/dist/query.d.ts +257 -36
- package/dist/query.js +443 -110
- package/dist/schema-builder.d.ts +2 -2
- package/dist/schema-builder.js +93 -25
- package/dist/schema-sql.d.ts +7 -3
- package/dist/schema-sql.js +157 -19
- package/dist/schema.d.ts +1 -1
- package/dist/schema.js +5 -2
- package/dist/serverless.d.ts +91 -139
- package/dist/serverless.js +86 -173
- package/package.json +33 -16
- package/dist/types.d.ts +0 -93
- package/dist/types.js +0 -126
package/dist/cjs/serverless.js
CHANGED
|
@@ -1,199 +1,110 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* turbine-orm/serverless — edge / serverless driver integration
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* Turbine runs on any Postgres driver that speaks the node-postgres API.
|
|
6
|
+
* This module exposes a thin factory (`turbineHttp`) that binds an external
|
|
7
|
+
* pg-compatible pool to a schema, so you can use Turbine on Vercel Edge,
|
|
8
|
+
* Cloudflare Workers, Deno Deploy, Netlify Edge, or any other environment
|
|
9
|
+
* where a direct TCP connection is unavailable.
|
|
7
10
|
*
|
|
8
|
-
*
|
|
9
|
-
* them against the actual database and returns typed results.
|
|
11
|
+
* ## Supported drivers
|
|
10
12
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
13
|
+
* Any driver whose `Pool` satisfies `PgCompatPool` will work. The ones
|
|
14
|
+
* below are verified:
|
|
15
|
+
*
|
|
16
|
+
* - **Neon** (`@neondatabase/serverless`) — HTTP and WebSocket transports
|
|
17
|
+
* - **Vercel Postgres** (`@vercel/postgres`) — wraps Neon
|
|
18
|
+
* - **Cloudflare Hyperdrive** — exposes a pg-compatible driver
|
|
19
|
+
* - **Supabase** — use the regular `pg` package; Supabase is Postgres-native
|
|
20
|
+
*
|
|
21
|
+
* Turbine does NOT bundle any of these — install whichever you need and
|
|
22
|
+
* pass its pool directly.
|
|
23
|
+
*
|
|
24
|
+
* ## Limitations over HTTP
|
|
25
|
+
*
|
|
26
|
+
* - **Streaming cursors** (`findManyStream`, `findManyCursor`) require
|
|
27
|
+
* server-side `DECLARE CURSOR`, which most HTTP drivers do not support.
|
|
28
|
+
* If you call these on an HTTP pool the underlying driver will error.
|
|
29
|
+
* - **LISTEN/NOTIFY** is not available over HTTP.
|
|
30
|
+
* - **Transactions** are supported but each transaction holds an HTTP
|
|
31
|
+
* connection for its duration — keep them short.
|
|
32
|
+
*
|
|
33
|
+
* ## Example — Neon on Vercel Edge
|
|
34
|
+
*
|
|
35
|
+
* ```ts
|
|
36
|
+
* // app/api/users/route.ts
|
|
37
|
+
* import { Pool } from '@neondatabase/serverless';
|
|
38
|
+
* import { turbineHttp } from 'turbine-orm/serverless';
|
|
39
|
+
* import { schema } from '@/generated/turbine/metadata';
|
|
40
|
+
*
|
|
41
|
+
* export const runtime = 'edge';
|
|
42
|
+
*
|
|
43
|
+
* const pool = new Pool({ connectionString: process.env.DATABASE_URL });
|
|
44
|
+
* const db = turbineHttp(pool, schema);
|
|
45
|
+
*
|
|
46
|
+
* export async function GET() {
|
|
47
|
+
* const users = await db.table('users').findMany({ limit: 10 });
|
|
48
|
+
* return Response.json(users);
|
|
49
|
+
* }
|
|
50
|
+
* ```
|
|
51
|
+
*
|
|
52
|
+
* ## Example — Supabase (direct Postgres, no HTTP proxy needed)
|
|
14
53
|
*
|
|
15
|
-
* @example
|
|
16
54
|
* ```ts
|
|
17
|
-
* import {
|
|
55
|
+
* import { TurbineClient } from 'turbine-orm';
|
|
56
|
+
* import { schema } from './generated/turbine/metadata.js';
|
|
57
|
+
*
|
|
58
|
+
* const db = new TurbineClient({
|
|
59
|
+
* connectionString: process.env.SUPABASE_DB_URL,
|
|
60
|
+
* ssl: { rejectUnauthorized: false },
|
|
61
|
+
* }, schema);
|
|
62
|
+
* ```
|
|
18
63
|
*
|
|
19
|
-
*
|
|
20
|
-
* endpoint: 'https://your-turbine-proxy.fly.dev/query',
|
|
21
|
-
* authToken: process.env.TURBINE_AUTH_TOKEN!,
|
|
22
|
-
* });
|
|
64
|
+
* ## Example — Cloudflare Workers
|
|
23
65
|
*
|
|
24
|
-
*
|
|
66
|
+
* ```ts
|
|
67
|
+
* // Use the Neon HTTP driver which works in Workers runtime
|
|
68
|
+
* import { Pool } from '@neondatabase/serverless';
|
|
69
|
+
* import { turbineHttp } from 'turbine-orm/serverless';
|
|
70
|
+
*
|
|
71
|
+
* export default {
|
|
72
|
+
* async fetch(req: Request, env: Env) {
|
|
73
|
+
* const pool = new Pool({ connectionString: env.DATABASE_URL });
|
|
74
|
+
* const db = turbineHttp(pool, schema);
|
|
75
|
+
* const users = await db.table('users').findMany({ limit: 10 });
|
|
76
|
+
* return Response.json(users);
|
|
77
|
+
* }
|
|
78
|
+
* };
|
|
25
79
|
* ```
|
|
26
80
|
*/
|
|
27
81
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
28
|
-
exports.
|
|
29
|
-
|
|
30
|
-
// ---------------------------------------------------------------------------
|
|
31
|
-
// Serverless client
|
|
32
|
-
// ---------------------------------------------------------------------------
|
|
82
|
+
exports.turbineHttp = turbineHttp;
|
|
83
|
+
const client_js_1 = require("./client.js");
|
|
33
84
|
/**
|
|
34
|
-
*
|
|
85
|
+
* Create a TurbineClient bound to an external pg-compatible pool.
|
|
35
86
|
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
|
|
39
|
-
class ServerlessClient {
|
|
40
|
-
config;
|
|
41
|
-
fetchFn;
|
|
42
|
-
constructor(config) {
|
|
43
|
-
if (!config.endpoint) {
|
|
44
|
-
throw new Error('[turbine/serverless] endpoint is required');
|
|
45
|
-
}
|
|
46
|
-
if (!config.authToken) {
|
|
47
|
-
throw new Error('[turbine/serverless] authToken is required');
|
|
48
|
-
}
|
|
49
|
-
this.config = {
|
|
50
|
-
...config,
|
|
51
|
-
timeout: config.timeout ?? 10_000,
|
|
52
|
-
};
|
|
53
|
-
this.fetchFn = config.fetch ?? globalThis.fetch;
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* Execute a single SQL query.
|
|
57
|
-
*
|
|
58
|
-
* @param sql - SQL string with $1, $2, ... placeholders
|
|
59
|
-
* @param params - Parameter values
|
|
60
|
-
* @returns Query result with typed rows
|
|
61
|
-
*
|
|
62
|
-
* @example
|
|
63
|
-
* ```ts
|
|
64
|
-
* const result = await client.query<{ id: number; name: string }>(
|
|
65
|
-
* 'SELECT id, name FROM users WHERE org_id = $1',
|
|
66
|
-
* [42]
|
|
67
|
-
* );
|
|
68
|
-
* console.log(result.rows);
|
|
69
|
-
* ```
|
|
70
|
-
*/
|
|
71
|
-
async query(sql, params) {
|
|
72
|
-
const request = { sql, params, mode: 'rows' };
|
|
73
|
-
const response = await this.post('/query', request);
|
|
74
|
-
return response;
|
|
75
|
-
}
|
|
76
|
-
/**
|
|
77
|
-
* Execute a single SQL query and return the first row, or null.
|
|
78
|
-
*/
|
|
79
|
-
async queryOne(sql, params) {
|
|
80
|
-
const request = { sql, params, mode: 'one' };
|
|
81
|
-
const response = await this.post('/query', request);
|
|
82
|
-
return response.rows[0] ?? null;
|
|
83
|
-
}
|
|
84
|
-
/**
|
|
85
|
-
* Execute a batch of queries in a single HTTP request.
|
|
86
|
-
* Optionally wraps them in a transaction.
|
|
87
|
-
*
|
|
88
|
-
* @param queries - Array of queries to execute
|
|
89
|
-
* @param options - Batch options
|
|
90
|
-
* @returns Array of results, one per query
|
|
91
|
-
*
|
|
92
|
-
* @example
|
|
93
|
-
* ```ts
|
|
94
|
-
* const results = await client.batch([
|
|
95
|
-
* { sql: 'SELECT * FROM users WHERE id = $1', params: [1] },
|
|
96
|
-
* { sql: 'SELECT COUNT(*) FROM posts WHERE user_id = $1', params: [1] },
|
|
97
|
-
* ], { transaction: true });
|
|
98
|
-
* ```
|
|
99
|
-
*/
|
|
100
|
-
async batch(queries, options) {
|
|
101
|
-
const request = {
|
|
102
|
-
queries,
|
|
103
|
-
transaction: options?.transaction ?? false,
|
|
104
|
-
};
|
|
105
|
-
return this.post('/batch', request);
|
|
106
|
-
}
|
|
107
|
-
/**
|
|
108
|
-
* Tagged template helper for SQL queries.
|
|
109
|
-
*
|
|
110
|
-
* @example
|
|
111
|
-
* ```ts
|
|
112
|
-
* const users = await client.sql<{ id: number; name: string }>`
|
|
113
|
-
* SELECT id, name FROM users WHERE org_id = ${orgId}
|
|
114
|
-
* `;
|
|
115
|
-
* ```
|
|
116
|
-
*/
|
|
117
|
-
async sql(strings, ...values) {
|
|
118
|
-
let sqlStr = '';
|
|
119
|
-
strings.forEach((str, i) => {
|
|
120
|
-
sqlStr += str;
|
|
121
|
-
if (i < values.length) {
|
|
122
|
-
sqlStr += `$${i + 1}`;
|
|
123
|
-
}
|
|
124
|
-
});
|
|
125
|
-
const result = await this.query(sqlStr, values);
|
|
126
|
-
return result.rows;
|
|
127
|
-
}
|
|
128
|
-
// -------------------------------------------------------------------------
|
|
129
|
-
// Internal HTTP transport
|
|
130
|
-
// -------------------------------------------------------------------------
|
|
131
|
-
async post(path, body) {
|
|
132
|
-
const url = this.config.endpoint.replace(/\/$/, '') + path;
|
|
133
|
-
const controller = new AbortController();
|
|
134
|
-
const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
|
|
135
|
-
try {
|
|
136
|
-
const response = await this.fetchFn(url, {
|
|
137
|
-
method: 'POST',
|
|
138
|
-
headers: {
|
|
139
|
-
'Content-Type': 'application/json',
|
|
140
|
-
'Authorization': `Bearer ${this.config.authToken}`,
|
|
141
|
-
'User-Agent': '@batadata/turbine-serverless',
|
|
142
|
-
...this.config.headers,
|
|
143
|
-
},
|
|
144
|
-
body: JSON.stringify(body),
|
|
145
|
-
signal: controller.signal,
|
|
146
|
-
});
|
|
147
|
-
if (!response.ok) {
|
|
148
|
-
const errorBody = await response.text();
|
|
149
|
-
let parsed;
|
|
150
|
-
try {
|
|
151
|
-
parsed = JSON.parse(errorBody);
|
|
152
|
-
}
|
|
153
|
-
catch {
|
|
154
|
-
// Not JSON
|
|
155
|
-
}
|
|
156
|
-
const message = parsed?.error ?? `HTTP ${response.status}: ${errorBody.slice(0, 200)}`;
|
|
157
|
-
const err = new Error(`[turbine/serverless] ${message}`);
|
|
158
|
-
err['status'] = response.status;
|
|
159
|
-
err['code'] = parsed?.code;
|
|
160
|
-
throw err;
|
|
161
|
-
}
|
|
162
|
-
return (await response.json());
|
|
163
|
-
}
|
|
164
|
-
catch (err) {
|
|
165
|
-
if (err instanceof DOMException && err.name === 'AbortError') {
|
|
166
|
-
throw new Error(`[turbine/serverless] Request timed out after ${this.config.timeout}ms`);
|
|
167
|
-
}
|
|
168
|
-
throw err;
|
|
169
|
-
}
|
|
170
|
-
finally {
|
|
171
|
-
clearTimeout(timeoutId);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
exports.ServerlessClient = ServerlessClient;
|
|
176
|
-
// ---------------------------------------------------------------------------
|
|
177
|
-
// Factory function
|
|
178
|
-
// ---------------------------------------------------------------------------
|
|
179
|
-
/**
|
|
180
|
-
* Create a serverless Turbine client for edge/serverless environments.
|
|
87
|
+
* Use this for serverless/edge environments where Turbine should NOT
|
|
88
|
+
* manage its own `pg.Pool`. The caller retains ownership of the pool's
|
|
89
|
+
* lifecycle — `db.disconnect()` is a no-op.
|
|
181
90
|
*
|
|
182
|
-
* @param
|
|
183
|
-
* @
|
|
91
|
+
* @param pool - Any pg-compatible pool (Neon, Vercel Postgres, etc.)
|
|
92
|
+
* @param schema - Introspected or hand-written schema metadata
|
|
93
|
+
* @param options - Optional logging / defaultLimit / warnOnUnlimited
|
|
94
|
+
* @returns A TurbineClient instance
|
|
184
95
|
*
|
|
185
96
|
* @example
|
|
186
97
|
* ```ts
|
|
187
|
-
* import {
|
|
98
|
+
* import { Pool } from '@neondatabase/serverless';
|
|
99
|
+
* import { turbineHttp } from 'turbine-orm/serverless';
|
|
100
|
+
* import { schema } from './generated/turbine/metadata.js';
|
|
188
101
|
*
|
|
189
|
-
* const
|
|
190
|
-
*
|
|
191
|
-
* authToken: process.env.TURBINE_AUTH_TOKEN!,
|
|
192
|
-
* });
|
|
102
|
+
* const pool = new Pool({ connectionString: process.env.DATABASE_URL });
|
|
103
|
+
* const db = turbineHttp(pool, schema);
|
|
193
104
|
*
|
|
194
|
-
* const users = await db.
|
|
105
|
+
* const users = await db.table('users').findMany({ limit: 10 });
|
|
195
106
|
* ```
|
|
196
107
|
*/
|
|
197
|
-
function
|
|
198
|
-
return new
|
|
108
|
+
function turbineHttp(pool, schema, options = {}) {
|
|
109
|
+
return new client_js_1.TurbineClient({ pool, ...options }, schema);
|
|
199
110
|
}
|
package/dist/cli/config.js
CHANGED
|
@@ -5,17 +5,12 @@
|
|
|
5
5
|
* Falls back to CLI args and environment variables.
|
|
6
6
|
*/
|
|
7
7
|
import { existsSync } from 'node:fs';
|
|
8
|
-
import {
|
|
8
|
+
import { join, resolve } from 'node:path';
|
|
9
9
|
import { pathToFileURL } from 'node:url';
|
|
10
10
|
// ---------------------------------------------------------------------------
|
|
11
11
|
// Config file names, in priority order
|
|
12
12
|
// ---------------------------------------------------------------------------
|
|
13
|
-
const CONFIG_FILES = [
|
|
14
|
-
'turbine.config.ts',
|
|
15
|
-
'turbine.config.mts',
|
|
16
|
-
'turbine.config.js',
|
|
17
|
-
'turbine.config.mjs',
|
|
18
|
-
];
|
|
13
|
+
const CONFIG_FILES = ['turbine.config.ts', 'turbine.config.mts', 'turbine.config.js', 'turbine.config.mjs'];
|
|
19
14
|
// ---------------------------------------------------------------------------
|
|
20
15
|
// Load config
|
|
21
16
|
// ---------------------------------------------------------------------------
|
|
@@ -67,10 +62,7 @@ export function findConfigFile(cwd) {
|
|
|
67
62
|
*/
|
|
68
63
|
export function resolveConfig(fileConfig, overrides) {
|
|
69
64
|
return {
|
|
70
|
-
url: overrides.url ??
|
|
71
|
-
process.env['DATABASE_URL'] ??
|
|
72
|
-
fileConfig.url ??
|
|
73
|
-
'',
|
|
65
|
+
url: overrides.url ?? process.env.DATABASE_URL ?? fileConfig.url ?? '',
|
|
74
66
|
out: overrides.out ?? fileConfig.out ?? './generated/turbine',
|
|
75
67
|
schema: overrides.schema ?? fileConfig.schema ?? 'public',
|
|
76
68
|
include: overrides.include ?? fileConfig.include ?? [],
|
|
@@ -84,15 +76,13 @@ export function resolveConfig(fileConfig, overrides) {
|
|
|
84
76
|
// Config file template (for `turbine init`)
|
|
85
77
|
// ---------------------------------------------------------------------------
|
|
86
78
|
export function configTemplate(connectionString) {
|
|
87
|
-
const
|
|
88
|
-
const urlLine = connectionString
|
|
89
|
-
? ` url: '${connectionString}',`
|
|
90
|
-
: ` url: process.env.DATABASE_URL,`;
|
|
79
|
+
const _url = connectionString ?? 'process.env.DATABASE_URL';
|
|
80
|
+
const urlLine = connectionString ? ` url: '${connectionString}',` : ` url: process.env.DATABASE_URL,`;
|
|
91
81
|
return `import type { TurbineCliConfig } from 'turbine-orm/cli';
|
|
92
82
|
|
|
93
83
|
/**
|
|
94
84
|
* Turbine configuration
|
|
95
|
-
* @see https://
|
|
85
|
+
* @see https://turbineorm.dev
|
|
96
86
|
*/
|
|
97
87
|
const config: TurbineCliConfig = {
|
|
98
88
|
/** Postgres connection string */
|