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.
- package/README.md +292 -26
- package/dist/cjs/cli/config.js +5 -15
- package/dist/cjs/cli/index.js +311 -43
- package/dist/cjs/cli/loader.js +129 -0
- package/dist/cjs/cli/migrate.js +96 -47
- package/dist/cjs/cli/ui.js +5 -9
- package/dist/cjs/client.js +158 -49
- package/dist/cjs/errors.js +424 -0
- package/dist/cjs/generate.js +145 -14
- package/dist/cjs/index.js +43 -20
- package/dist/cjs/introspect.js +3 -5
- package/dist/cjs/pipeline.js +9 -2
- package/dist/cjs/query.js +544 -115
- package/dist/cjs/schema-builder.js +150 -30
- package/dist/cjs/schema-sql.js +241 -37
- package/dist/cjs/schema.js +5 -2
- package/dist/cjs/serverless.js +88 -176
- package/dist/cli/config.js +6 -16
- package/dist/cli/index.js +316 -48
- package/dist/cli/loader.d.ts +45 -0
- package/dist/cli/loader.js +91 -0
- package/dist/cli/migrate.d.ts +13 -2
- package/dist/cli/migrate.js +97 -48
- package/dist/cli/ui.d.ts +1 -1
- package/dist/cli/ui.js +5 -9
- package/dist/client.d.ts +92 -4
- package/dist/client.js +158 -49
- package/dist/errors.d.ts +225 -0
- package/dist/errors.js +405 -0
- package/dist/generate.d.ts +7 -1
- package/dist/generate.js +148 -18
- 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 +374 -38
- package/dist/query.js +545 -116
- package/dist/schema-builder.d.ts +38 -5
- package/dist/schema-builder.js +150 -31
- package/dist/schema-sql.d.ts +7 -3
- package/dist/schema-sql.js +241 -37
- package/dist/schema.d.ts +1 -1
- package/dist/schema.js +5 -2
- package/dist/serverless.d.ts +92 -139
- package/dist/serverless.js +87 -173
- package/package.json +33 -16
- package/dist/types.d.ts +0 -93
- package/dist/types.js +0 -126
package/dist/serverless.d.ts
CHANGED
|
@@ -1,162 +1,115 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* turbine-orm/serverless — edge / serverless driver integration
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Turbine runs on any Postgres driver that speaks the node-postgres API.
|
|
5
|
+
* This module exposes a thin factory (`turbineHttp`) that binds an external
|
|
6
|
+
* pg-compatible pool to a schema, so you can use Turbine on Vercel Edge,
|
|
7
|
+
* Cloudflare Workers, Deno Deploy, Netlify Edge, or any other environment
|
|
8
|
+
* where a direct TCP connection is unavailable.
|
|
6
9
|
*
|
|
7
|
-
*
|
|
8
|
-
* them against the actual database and returns typed results.
|
|
10
|
+
* ## Supported drivers
|
|
9
11
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
12
|
+
* Any driver whose `Pool` satisfies `PgCompatPool` will work. The ones
|
|
13
|
+
* below are verified:
|
|
14
|
+
*
|
|
15
|
+
* - **Neon** (`@neondatabase/serverless`) — HTTP and WebSocket transports
|
|
16
|
+
* - **Vercel Postgres** (`@vercel/postgres`) — wraps Neon
|
|
17
|
+
* - **Cloudflare Hyperdrive** — exposes a pg-compatible driver
|
|
18
|
+
* - **Supabase** — use the regular `pg` package; Supabase is Postgres-native
|
|
19
|
+
*
|
|
20
|
+
* Turbine does NOT bundle any of these — install whichever you need and
|
|
21
|
+
* pass its pool directly.
|
|
22
|
+
*
|
|
23
|
+
* ## Limitations over HTTP
|
|
24
|
+
*
|
|
25
|
+
* - **Streaming cursors** (`findManyStream`, `findManyCursor`) require
|
|
26
|
+
* server-side `DECLARE CURSOR`, which most HTTP drivers do not support.
|
|
27
|
+
* If you call these on an HTTP pool the underlying driver will error.
|
|
28
|
+
* - **LISTEN/NOTIFY** is not available over HTTP.
|
|
29
|
+
* - **Transactions** are supported but each transaction holds an HTTP
|
|
30
|
+
* connection for its duration — keep them short.
|
|
31
|
+
*
|
|
32
|
+
* ## Example — Neon on Vercel Edge
|
|
13
33
|
*
|
|
14
|
-
* @example
|
|
15
34
|
* ```ts
|
|
16
|
-
*
|
|
35
|
+
* // app/api/users/route.ts
|
|
36
|
+
* import { Pool } from '@neondatabase/serverless';
|
|
37
|
+
* import { turbineHttp } from 'turbine-orm/serverless';
|
|
38
|
+
* import { SCHEMA } from '../../generated/turbine/metadata';
|
|
39
|
+
*
|
|
40
|
+
* export const runtime = 'edge';
|
|
17
41
|
*
|
|
18
|
-
* const
|
|
19
|
-
*
|
|
20
|
-
* authToken: process.env.TURBINE_AUTH_TOKEN!,
|
|
21
|
-
* });
|
|
42
|
+
* const pool = new Pool({ connectionString: process.env.DATABASE_URL });
|
|
43
|
+
* const db = turbineHttp(pool, SCHEMA);
|
|
22
44
|
*
|
|
23
|
-
*
|
|
45
|
+
* export async function GET() {
|
|
46
|
+
* const users = await db.table('users').findMany({ limit: 10 });
|
|
47
|
+
* return Response.json(users);
|
|
48
|
+
* }
|
|
49
|
+
* ```
|
|
50
|
+
*
|
|
51
|
+
* ## Example — Supabase (direct Postgres, no HTTP proxy needed)
|
|
52
|
+
*
|
|
53
|
+
* ```ts
|
|
54
|
+
* import { TurbineClient } from 'turbine-orm';
|
|
55
|
+
* import { SCHEMA } from './generated/turbine/metadata.js';
|
|
56
|
+
*
|
|
57
|
+
* const db = new TurbineClient({
|
|
58
|
+
* connectionString: process.env.SUPABASE_DB_URL,
|
|
59
|
+
* ssl: { rejectUnauthorized: false },
|
|
60
|
+
* }, SCHEMA);
|
|
61
|
+
* ```
|
|
62
|
+
*
|
|
63
|
+
* ## Example — Cloudflare Workers
|
|
64
|
+
*
|
|
65
|
+
* ```ts
|
|
66
|
+
* // Use the Neon HTTP driver which works in Workers runtime
|
|
67
|
+
* import { Pool } from '@neondatabase/serverless';
|
|
68
|
+
* import { turbineHttp } from 'turbine-orm/serverless';
|
|
69
|
+
* import { SCHEMA } from './generated/turbine/metadata';
|
|
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
|
+
* };
|
|
24
79
|
* ```
|
|
25
80
|
*/
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
endpoint: string;
|
|
30
|
-
/** Authentication token for the endpoint */
|
|
31
|
-
authToken: string;
|
|
32
|
-
/** Request timeout in milliseconds (default: 10000) */
|
|
33
|
-
timeout?: number;
|
|
34
|
-
/** Custom fetch implementation (defaults to global fetch) */
|
|
35
|
-
fetch?: typeof globalThis.fetch;
|
|
36
|
-
/** Custom headers to include with every request */
|
|
37
|
-
headers?: Record<string, string>;
|
|
38
|
-
}
|
|
39
|
-
/** A single SQL query to execute */
|
|
40
|
-
export interface QueryRequest {
|
|
41
|
-
/** SQL query string with $1, $2, ... parameter placeholders */
|
|
42
|
-
sql: string;
|
|
43
|
-
/** Parameter values */
|
|
44
|
-
params?: unknown[];
|
|
45
|
-
/** Hint for the server about expected result shape */
|
|
46
|
-
mode?: 'rows' | 'one' | 'count' | 'void';
|
|
47
|
-
}
|
|
48
|
-
/** Batch of queries to execute in a single round-trip */
|
|
49
|
-
export interface BatchRequest {
|
|
50
|
-
queries: QueryRequest[];
|
|
51
|
-
/** Whether to wrap the batch in a transaction */
|
|
52
|
-
transaction?: boolean;
|
|
53
|
-
}
|
|
54
|
-
/** Response from the query endpoint for a single query */
|
|
55
|
-
export interface QueryResponse<T = Record<string, unknown>> {
|
|
56
|
-
/** Returned rows */
|
|
57
|
-
rows: T[];
|
|
58
|
-
/** Number of rows affected (for INSERT/UPDATE/DELETE) */
|
|
59
|
-
rowCount: number;
|
|
60
|
-
/** Column metadata */
|
|
61
|
-
fields?: Array<{
|
|
62
|
-
name: string;
|
|
63
|
-
dataTypeID: number;
|
|
64
|
-
}>;
|
|
65
|
-
/** Server-side execution time in milliseconds */
|
|
66
|
-
durationMs?: number;
|
|
67
|
-
}
|
|
68
|
-
/** Response from the query endpoint for a batch */
|
|
69
|
-
export interface BatchResponse {
|
|
70
|
-
results: QueryResponse[];
|
|
71
|
-
/** Total server-side execution time in milliseconds */
|
|
72
|
-
totalDurationMs?: number;
|
|
73
|
-
}
|
|
74
|
-
/** Error response from the query endpoint */
|
|
75
|
-
export interface QueryError {
|
|
76
|
-
error: string;
|
|
77
|
-
code?: string;
|
|
78
|
-
detail?: string;
|
|
79
|
-
}
|
|
81
|
+
import { type PgCompatPool, TurbineClient, type TurbineConfig } from './client.js';
|
|
82
|
+
import type { SchemaMetadata } from './schema.js';
|
|
83
|
+
export type { PgCompatPool, PgCompatPoolClient, PgCompatQueryResult } from './client.js';
|
|
80
84
|
/**
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
* Sends SQL queries as JSON POST requests to a Turbine query endpoint.
|
|
84
|
-
* Does not require a direct TCP connection to Postgres.
|
|
85
|
+
* Options for `turbineHttp()`. Mirrors the fields of `TurbineConfig`
|
|
86
|
+
* that are relevant for externally-managed pools.
|
|
85
87
|
*/
|
|
86
|
-
export
|
|
87
|
-
private readonly config;
|
|
88
|
-
private readonly fetchFn;
|
|
89
|
-
constructor(config: ServerlessConfig);
|
|
90
|
-
/**
|
|
91
|
-
* Execute a single SQL query.
|
|
92
|
-
*
|
|
93
|
-
* @param sql - SQL string with $1, $2, ... placeholders
|
|
94
|
-
* @param params - Parameter values
|
|
95
|
-
* @returns Query result with typed rows
|
|
96
|
-
*
|
|
97
|
-
* @example
|
|
98
|
-
* ```ts
|
|
99
|
-
* const result = await client.query<{ id: number; name: string }>(
|
|
100
|
-
* 'SELECT id, name FROM users WHERE org_id = $1',
|
|
101
|
-
* [42]
|
|
102
|
-
* );
|
|
103
|
-
* console.log(result.rows);
|
|
104
|
-
* ```
|
|
105
|
-
*/
|
|
106
|
-
query<T = Record<string, unknown>>(sql: string, params?: unknown[]): Promise<QueryResponse<T>>;
|
|
107
|
-
/**
|
|
108
|
-
* Execute a single SQL query and return the first row, or null.
|
|
109
|
-
*/
|
|
110
|
-
queryOne<T = Record<string, unknown>>(sql: string, params?: unknown[]): Promise<T | null>;
|
|
111
|
-
/**
|
|
112
|
-
* Execute a batch of queries in a single HTTP request.
|
|
113
|
-
* Optionally wraps them in a transaction.
|
|
114
|
-
*
|
|
115
|
-
* @param queries - Array of queries to execute
|
|
116
|
-
* @param options - Batch options
|
|
117
|
-
* @returns Array of results, one per query
|
|
118
|
-
*
|
|
119
|
-
* @example
|
|
120
|
-
* ```ts
|
|
121
|
-
* const results = await client.batch([
|
|
122
|
-
* { sql: 'SELECT * FROM users WHERE id = $1', params: [1] },
|
|
123
|
-
* { sql: 'SELECT COUNT(*) FROM posts WHERE user_id = $1', params: [1] },
|
|
124
|
-
* ], { transaction: true });
|
|
125
|
-
* ```
|
|
126
|
-
*/
|
|
127
|
-
batch(queries: QueryRequest[], options?: {
|
|
128
|
-
transaction?: boolean;
|
|
129
|
-
}): Promise<BatchResponse>;
|
|
130
|
-
/**
|
|
131
|
-
* Tagged template helper for SQL queries.
|
|
132
|
-
*
|
|
133
|
-
* @example
|
|
134
|
-
* ```ts
|
|
135
|
-
* const users = await client.sql<{ id: number; name: string }>`
|
|
136
|
-
* SELECT id, name FROM users WHERE org_id = ${orgId}
|
|
137
|
-
* `;
|
|
138
|
-
* ```
|
|
139
|
-
*/
|
|
140
|
-
sql<T = Record<string, unknown>>(strings: TemplateStringsArray, ...values: unknown[]): Promise<T[]>;
|
|
141
|
-
private post;
|
|
88
|
+
export interface TurbineHttpOptions extends Pick<TurbineConfig, 'logging' | 'defaultLimit' | 'warnOnUnlimited'> {
|
|
142
89
|
}
|
|
143
90
|
/**
|
|
144
|
-
* Create a
|
|
91
|
+
* Create a TurbineClient bound to an external pg-compatible pool.
|
|
92
|
+
*
|
|
93
|
+
* Use this for serverless/edge environments where Turbine should NOT
|
|
94
|
+
* manage its own `pg.Pool`. The caller retains ownership of the pool's
|
|
95
|
+
* lifecycle — `db.disconnect()` is a no-op.
|
|
145
96
|
*
|
|
146
|
-
* @param
|
|
147
|
-
* @
|
|
97
|
+
* @param pool - Any pg-compatible pool (Neon, Vercel Postgres, etc.)
|
|
98
|
+
* @param schema - Introspected or hand-written schema metadata
|
|
99
|
+
* @param options - Optional logging / defaultLimit / warnOnUnlimited
|
|
100
|
+
* @returns A TurbineClient instance
|
|
148
101
|
*
|
|
149
102
|
* @example
|
|
150
103
|
* ```ts
|
|
151
|
-
* import {
|
|
104
|
+
* import { Pool } from '@neondatabase/serverless';
|
|
105
|
+
* import { turbineHttp } from 'turbine-orm/serverless';
|
|
106
|
+
* import { SCHEMA } from './generated/turbine/metadata.js';
|
|
152
107
|
*
|
|
153
|
-
* const
|
|
154
|
-
*
|
|
155
|
-
* authToken: process.env.TURBINE_AUTH_TOKEN!,
|
|
156
|
-
* });
|
|
108
|
+
* const pool = new Pool({ connectionString: process.env.DATABASE_URL });
|
|
109
|
+
* const db = turbineHttp(pool, SCHEMA);
|
|
157
110
|
*
|
|
158
|
-
* const users = await db.
|
|
111
|
+
* const users = await db.table('users').findMany({ limit: 10 });
|
|
159
112
|
* ```
|
|
160
113
|
*/
|
|
161
|
-
export declare function
|
|
114
|
+
export declare function turbineHttp(pool: PgCompatPool, schema: SchemaMetadata, options?: TurbineHttpOptions): TurbineClient;
|
|
162
115
|
//# sourceMappingURL=serverless.d.ts.map
|
package/dist/serverless.js
CHANGED
|
@@ -1,195 +1,109 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* turbine-orm/serverless — edge / serverless driver integration
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Turbine runs on any Postgres driver that speaks the node-postgres API.
|
|
5
|
+
* This module exposes a thin factory (`turbineHttp`) that binds an external
|
|
6
|
+
* pg-compatible pool to a schema, so you can use Turbine on Vercel Edge,
|
|
7
|
+
* Cloudflare Workers, Deno Deploy, Netlify Edge, or any other environment
|
|
8
|
+
* where a direct TCP connection is unavailable.
|
|
6
9
|
*
|
|
7
|
-
*
|
|
8
|
-
* them against the actual database and returns typed results.
|
|
10
|
+
* ## Supported drivers
|
|
9
11
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
12
|
+
* Any driver whose `Pool` satisfies `PgCompatPool` will work. The ones
|
|
13
|
+
* below are verified:
|
|
14
|
+
*
|
|
15
|
+
* - **Neon** (`@neondatabase/serverless`) — HTTP and WebSocket transports
|
|
16
|
+
* - **Vercel Postgres** (`@vercel/postgres`) — wraps Neon
|
|
17
|
+
* - **Cloudflare Hyperdrive** — exposes a pg-compatible driver
|
|
18
|
+
* - **Supabase** — use the regular `pg` package; Supabase is Postgres-native
|
|
19
|
+
*
|
|
20
|
+
* Turbine does NOT bundle any of these — install whichever you need and
|
|
21
|
+
* pass its pool directly.
|
|
22
|
+
*
|
|
23
|
+
* ## Limitations over HTTP
|
|
24
|
+
*
|
|
25
|
+
* - **Streaming cursors** (`findManyStream`, `findManyCursor`) require
|
|
26
|
+
* server-side `DECLARE CURSOR`, which most HTTP drivers do not support.
|
|
27
|
+
* If you call these on an HTTP pool the underlying driver will error.
|
|
28
|
+
* - **LISTEN/NOTIFY** is not available over HTTP.
|
|
29
|
+
* - **Transactions** are supported but each transaction holds an HTTP
|
|
30
|
+
* connection for its duration — keep them short.
|
|
31
|
+
*
|
|
32
|
+
* ## Example — Neon on Vercel Edge
|
|
13
33
|
*
|
|
14
|
-
* @example
|
|
15
34
|
* ```ts
|
|
16
|
-
*
|
|
35
|
+
* // app/api/users/route.ts
|
|
36
|
+
* import { Pool } from '@neondatabase/serverless';
|
|
37
|
+
* import { turbineHttp } from 'turbine-orm/serverless';
|
|
38
|
+
* import { SCHEMA } from '../../generated/turbine/metadata';
|
|
39
|
+
*
|
|
40
|
+
* export const runtime = 'edge';
|
|
17
41
|
*
|
|
18
|
-
* const
|
|
19
|
-
*
|
|
20
|
-
* authToken: process.env.TURBINE_AUTH_TOKEN!,
|
|
21
|
-
* });
|
|
42
|
+
* const pool = new Pool({ connectionString: process.env.DATABASE_URL });
|
|
43
|
+
* const db = turbineHttp(pool, SCHEMA);
|
|
22
44
|
*
|
|
23
|
-
*
|
|
45
|
+
* export async function GET() {
|
|
46
|
+
* const users = await db.table('users').findMany({ limit: 10 });
|
|
47
|
+
* return Response.json(users);
|
|
48
|
+
* }
|
|
24
49
|
* ```
|
|
25
|
-
*/
|
|
26
|
-
// ---------------------------------------------------------------------------
|
|
27
|
-
// Serverless client
|
|
28
|
-
// ---------------------------------------------------------------------------
|
|
29
|
-
/**
|
|
30
|
-
* HTTP-based Postgres query client for serverless/edge environments.
|
|
31
50
|
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
51
|
+
* ## Example — Supabase (direct Postgres, no HTTP proxy needed)
|
|
52
|
+
*
|
|
53
|
+
* ```ts
|
|
54
|
+
* import { TurbineClient } from 'turbine-orm';
|
|
55
|
+
* import { SCHEMA } from './generated/turbine/metadata.js';
|
|
56
|
+
*
|
|
57
|
+
* const db = new TurbineClient({
|
|
58
|
+
* connectionString: process.env.SUPABASE_DB_URL,
|
|
59
|
+
* ssl: { rejectUnauthorized: false },
|
|
60
|
+
* }, SCHEMA);
|
|
61
|
+
* ```
|
|
62
|
+
*
|
|
63
|
+
* ## Example — Cloudflare Workers
|
|
64
|
+
*
|
|
65
|
+
* ```ts
|
|
66
|
+
* // Use the Neon HTTP driver which works in Workers runtime
|
|
67
|
+
* import { Pool } from '@neondatabase/serverless';
|
|
68
|
+
* import { turbineHttp } from 'turbine-orm/serverless';
|
|
69
|
+
* import { SCHEMA } from './generated/turbine/metadata';
|
|
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
|
+
* };
|
|
79
|
+
* ```
|
|
34
80
|
*/
|
|
35
|
-
|
|
36
|
-
config;
|
|
37
|
-
fetchFn;
|
|
38
|
-
constructor(config) {
|
|
39
|
-
if (!config.endpoint) {
|
|
40
|
-
throw new Error('[turbine/serverless] endpoint is required');
|
|
41
|
-
}
|
|
42
|
-
if (!config.authToken) {
|
|
43
|
-
throw new Error('[turbine/serverless] authToken is required');
|
|
44
|
-
}
|
|
45
|
-
this.config = {
|
|
46
|
-
...config,
|
|
47
|
-
timeout: config.timeout ?? 10_000,
|
|
48
|
-
};
|
|
49
|
-
this.fetchFn = config.fetch ?? globalThis.fetch;
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Execute a single SQL query.
|
|
53
|
-
*
|
|
54
|
-
* @param sql - SQL string with $1, $2, ... placeholders
|
|
55
|
-
* @param params - Parameter values
|
|
56
|
-
* @returns Query result with typed rows
|
|
57
|
-
*
|
|
58
|
-
* @example
|
|
59
|
-
* ```ts
|
|
60
|
-
* const result = await client.query<{ id: number; name: string }>(
|
|
61
|
-
* 'SELECT id, name FROM users WHERE org_id = $1',
|
|
62
|
-
* [42]
|
|
63
|
-
* );
|
|
64
|
-
* console.log(result.rows);
|
|
65
|
-
* ```
|
|
66
|
-
*/
|
|
67
|
-
async query(sql, params) {
|
|
68
|
-
const request = { sql, params, mode: 'rows' };
|
|
69
|
-
const response = await this.post('/query', request);
|
|
70
|
-
return response;
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* Execute a single SQL query and return the first row, or null.
|
|
74
|
-
*/
|
|
75
|
-
async queryOne(sql, params) {
|
|
76
|
-
const request = { sql, params, mode: 'one' };
|
|
77
|
-
const response = await this.post('/query', request);
|
|
78
|
-
return response.rows[0] ?? null;
|
|
79
|
-
}
|
|
80
|
-
/**
|
|
81
|
-
* Execute a batch of queries in a single HTTP request.
|
|
82
|
-
* Optionally wraps them in a transaction.
|
|
83
|
-
*
|
|
84
|
-
* @param queries - Array of queries to execute
|
|
85
|
-
* @param options - Batch options
|
|
86
|
-
* @returns Array of results, one per query
|
|
87
|
-
*
|
|
88
|
-
* @example
|
|
89
|
-
* ```ts
|
|
90
|
-
* const results = await client.batch([
|
|
91
|
-
* { sql: 'SELECT * FROM users WHERE id = $1', params: [1] },
|
|
92
|
-
* { sql: 'SELECT COUNT(*) FROM posts WHERE user_id = $1', params: [1] },
|
|
93
|
-
* ], { transaction: true });
|
|
94
|
-
* ```
|
|
95
|
-
*/
|
|
96
|
-
async batch(queries, options) {
|
|
97
|
-
const request = {
|
|
98
|
-
queries,
|
|
99
|
-
transaction: options?.transaction ?? false,
|
|
100
|
-
};
|
|
101
|
-
return this.post('/batch', request);
|
|
102
|
-
}
|
|
103
|
-
/**
|
|
104
|
-
* Tagged template helper for SQL queries.
|
|
105
|
-
*
|
|
106
|
-
* @example
|
|
107
|
-
* ```ts
|
|
108
|
-
* const users = await client.sql<{ id: number; name: string }>`
|
|
109
|
-
* SELECT id, name FROM users WHERE org_id = ${orgId}
|
|
110
|
-
* `;
|
|
111
|
-
* ```
|
|
112
|
-
*/
|
|
113
|
-
async sql(strings, ...values) {
|
|
114
|
-
let sqlStr = '';
|
|
115
|
-
strings.forEach((str, i) => {
|
|
116
|
-
sqlStr += str;
|
|
117
|
-
if (i < values.length) {
|
|
118
|
-
sqlStr += `$${i + 1}`;
|
|
119
|
-
}
|
|
120
|
-
});
|
|
121
|
-
const result = await this.query(sqlStr, values);
|
|
122
|
-
return result.rows;
|
|
123
|
-
}
|
|
124
|
-
// -------------------------------------------------------------------------
|
|
125
|
-
// Internal HTTP transport
|
|
126
|
-
// -------------------------------------------------------------------------
|
|
127
|
-
async post(path, body) {
|
|
128
|
-
const url = this.config.endpoint.replace(/\/$/, '') + path;
|
|
129
|
-
const controller = new AbortController();
|
|
130
|
-
const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
|
|
131
|
-
try {
|
|
132
|
-
const response = await this.fetchFn(url, {
|
|
133
|
-
method: 'POST',
|
|
134
|
-
headers: {
|
|
135
|
-
'Content-Type': 'application/json',
|
|
136
|
-
'Authorization': `Bearer ${this.config.authToken}`,
|
|
137
|
-
'User-Agent': '@batadata/turbine-serverless',
|
|
138
|
-
...this.config.headers,
|
|
139
|
-
},
|
|
140
|
-
body: JSON.stringify(body),
|
|
141
|
-
signal: controller.signal,
|
|
142
|
-
});
|
|
143
|
-
if (!response.ok) {
|
|
144
|
-
const errorBody = await response.text();
|
|
145
|
-
let parsed;
|
|
146
|
-
try {
|
|
147
|
-
parsed = JSON.parse(errorBody);
|
|
148
|
-
}
|
|
149
|
-
catch {
|
|
150
|
-
// Not JSON
|
|
151
|
-
}
|
|
152
|
-
const message = parsed?.error ?? `HTTP ${response.status}: ${errorBody.slice(0, 200)}`;
|
|
153
|
-
const err = new Error(`[turbine/serverless] ${message}`);
|
|
154
|
-
err['status'] = response.status;
|
|
155
|
-
err['code'] = parsed?.code;
|
|
156
|
-
throw err;
|
|
157
|
-
}
|
|
158
|
-
return (await response.json());
|
|
159
|
-
}
|
|
160
|
-
catch (err) {
|
|
161
|
-
if (err instanceof DOMException && err.name === 'AbortError') {
|
|
162
|
-
throw new Error(`[turbine/serverless] Request timed out after ${this.config.timeout}ms`);
|
|
163
|
-
}
|
|
164
|
-
throw err;
|
|
165
|
-
}
|
|
166
|
-
finally {
|
|
167
|
-
clearTimeout(timeoutId);
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
// ---------------------------------------------------------------------------
|
|
172
|
-
// Factory function
|
|
173
|
-
// ---------------------------------------------------------------------------
|
|
81
|
+
import { TurbineClient } from './client.js';
|
|
174
82
|
/**
|
|
175
|
-
* Create a
|
|
83
|
+
* Create a TurbineClient bound to an external pg-compatible pool.
|
|
84
|
+
*
|
|
85
|
+
* Use this for serverless/edge environments where Turbine should NOT
|
|
86
|
+
* manage its own `pg.Pool`. The caller retains ownership of the pool's
|
|
87
|
+
* lifecycle — `db.disconnect()` is a no-op.
|
|
176
88
|
*
|
|
177
|
-
* @param
|
|
178
|
-
* @
|
|
89
|
+
* @param pool - Any pg-compatible pool (Neon, Vercel Postgres, etc.)
|
|
90
|
+
* @param schema - Introspected or hand-written schema metadata
|
|
91
|
+
* @param options - Optional logging / defaultLimit / warnOnUnlimited
|
|
92
|
+
* @returns A TurbineClient instance
|
|
179
93
|
*
|
|
180
94
|
* @example
|
|
181
95
|
* ```ts
|
|
182
|
-
* import {
|
|
96
|
+
* import { Pool } from '@neondatabase/serverless';
|
|
97
|
+
* import { turbineHttp } from 'turbine-orm/serverless';
|
|
98
|
+
* import { SCHEMA } from './generated/turbine/metadata.js';
|
|
183
99
|
*
|
|
184
|
-
* const
|
|
185
|
-
*
|
|
186
|
-
* authToken: process.env.TURBINE_AUTH_TOKEN!,
|
|
187
|
-
* });
|
|
100
|
+
* const pool = new Pool({ connectionString: process.env.DATABASE_URL });
|
|
101
|
+
* const db = turbineHttp(pool, SCHEMA);
|
|
188
102
|
*
|
|
189
|
-
* const users = await db.
|
|
103
|
+
* const users = await db.table('users').findMany({ limit: 10 });
|
|
190
104
|
* ```
|
|
191
105
|
*/
|
|
192
|
-
export function
|
|
193
|
-
return new
|
|
106
|
+
export function turbineHttp(pool, schema, options = {}) {
|
|
107
|
+
return new TurbineClient({ pool, ...options }, schema);
|
|
194
108
|
}
|
|
195
109
|
//# sourceMappingURL=serverless.js.map
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "turbine-orm",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "TypeScript ORM
|
|
3
|
+
"version": "0.7.1",
|
|
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": {
|
|
7
7
|
".": {
|
|
@@ -9,15 +9,15 @@
|
|
|
9
9
|
"require": "./dist/cjs/index.js",
|
|
10
10
|
"types": "./dist/index.d.ts"
|
|
11
11
|
},
|
|
12
|
-
"./cli": {
|
|
13
|
-
"import": "./dist/cli/config.js",
|
|
14
|
-
"require": "./dist/cjs/cli/config.js",
|
|
15
|
-
"types": "./dist/cli/config.d.ts"
|
|
16
|
-
},
|
|
17
12
|
"./serverless": {
|
|
18
13
|
"import": "./dist/serverless.js",
|
|
19
14
|
"require": "./dist/cjs/serverless.js",
|
|
20
15
|
"types": "./dist/serverless.d.ts"
|
|
16
|
+
},
|
|
17
|
+
"./cli": {
|
|
18
|
+
"import": "./dist/cli/config.js",
|
|
19
|
+
"require": "./dist/cjs/cli/config.js",
|
|
20
|
+
"types": "./dist/cli/config.d.ts"
|
|
21
21
|
}
|
|
22
22
|
},
|
|
23
23
|
"main": "./dist/cjs/index.js",
|
|
@@ -38,12 +38,18 @@
|
|
|
38
38
|
"scripts": {
|
|
39
39
|
"build": "tsc && tsc --project tsconfig.cjs.json && echo '{\"type\":\"commonjs\"}' > dist/cjs/package.json",
|
|
40
40
|
"dev": "tsc --watch",
|
|
41
|
-
"typecheck": "tsc --noEmit",
|
|
41
|
+
"typecheck": "tsc --noEmit --project tsconfig.test.json",
|
|
42
42
|
"generate": "tsx src/cli/index.ts generate",
|
|
43
43
|
"status": "tsx src/cli/index.ts status",
|
|
44
44
|
"examples": "tsx examples/examples.ts",
|
|
45
45
|
"test": "tsx --test src/test/*.test.ts",
|
|
46
|
-
"test:unit": "tsx --test src/test/schema-builder.test.ts"
|
|
46
|
+
"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/with-inference.test.ts src/test/operator-validation.test.ts src/test/unlimited-warning.test.ts src/test/generate-relations.test.ts",
|
|
47
|
+
"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/with-inference.test.ts src/test/operator-validation.test.ts src/test/unlimited-warning.test.ts src/test/generate-relations.test.ts",
|
|
48
|
+
"lint": "biome check src/",
|
|
49
|
+
"lint:fix": "biome check --write src/",
|
|
50
|
+
"format": "biome format --write src/",
|
|
51
|
+
"prepublishOnly": "npm run build && npm run typecheck && npm run lint && npm run test:unit",
|
|
52
|
+
"prepare": "husky"
|
|
47
53
|
},
|
|
48
54
|
"engines": {
|
|
49
55
|
"node": ">=18.0.0"
|
|
@@ -52,10 +58,14 @@
|
|
|
52
58
|
"pg": "^8.13.1"
|
|
53
59
|
},
|
|
54
60
|
"devDependencies": {
|
|
55
|
-
"@
|
|
61
|
+
"@biomejs/biome": "^2.4.10",
|
|
56
62
|
"@types/node": "^22.10.0",
|
|
57
|
-
"
|
|
58
|
-
"
|
|
63
|
+
"@types/pg": "^8.11.11",
|
|
64
|
+
"c8": "^11.0.0",
|
|
65
|
+
"husky": "^9.1.7",
|
|
66
|
+
"lint-staged": "^16.4.0",
|
|
67
|
+
"tsx": "^4.19.0",
|
|
68
|
+
"typescript": "^5.7.0"
|
|
59
69
|
},
|
|
60
70
|
"keywords": [
|
|
61
71
|
"orm",
|
|
@@ -67,15 +77,22 @@
|
|
|
67
77
|
"database",
|
|
68
78
|
"sql",
|
|
69
79
|
"turbine",
|
|
70
|
-
"
|
|
71
|
-
"
|
|
80
|
+
"nested-queries",
|
|
81
|
+
"prisma-alternative"
|
|
72
82
|
],
|
|
73
83
|
"author": "ZVN DEV <dev@zvndev.com>",
|
|
74
84
|
"license": "MIT",
|
|
75
85
|
"homepage": "https://turbineorm.dev",
|
|
76
86
|
"repository": {
|
|
77
87
|
"type": "git",
|
|
78
|
-
"url": "https://github.com/zvndev/
|
|
79
|
-
|
|
88
|
+
"url": "https://github.com/zvndev/turbine-orm.git"
|
|
89
|
+
},
|
|
90
|
+
"bugs": {
|
|
91
|
+
"url": "https://github.com/zvndev/turbine-orm/issues"
|
|
92
|
+
},
|
|
93
|
+
"lint-staged": {
|
|
94
|
+
"*.ts": [
|
|
95
|
+
"biome check --no-errors-on-unmatched"
|
|
96
|
+
]
|
|
80
97
|
}
|
|
81
98
|
}
|