turbine-orm 0.7.0 → 0.8.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 +134 -40
- package/dist/cjs/cli/index.js +72 -3
- package/dist/cjs/cli/loader.js +129 -0
- package/dist/cjs/cli/migrate.js +33 -9
- package/dist/cjs/client.js +92 -8
- package/dist/cjs/errors.js +177 -4
- package/dist/cjs/generate.js +120 -9
- package/dist/cjs/index.js +7 -1
- package/dist/cjs/pipeline-submittable.js +403 -0
- package/dist/cjs/pipeline.js +90 -37
- package/dist/cjs/query.js +943 -137
- package/dist/cjs/schema-builder.js +57 -6
- package/dist/cjs/schema-sql.js +85 -19
- package/dist/cjs/serverless.js +8 -7
- package/dist/cli/index.js +72 -3
- package/dist/cli/loader.d.ts +45 -0
- package/dist/cli/loader.js +91 -0
- package/dist/cli/migrate.d.ts +7 -1
- package/dist/cli/migrate.js +33 -9
- package/dist/cli/ui.d.ts +1 -1
- package/dist/client.d.ts +47 -3
- package/dist/client.js +94 -10
- package/dist/errors.d.ts +132 -1
- package/dist/errors.js +171 -3
- package/dist/generate.d.ts +6 -0
- package/dist/generate.js +120 -10
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -2
- package/dist/pipeline-submittable.d.ts +94 -0
- package/dist/pipeline-submittable.js +397 -0
- package/dist/pipeline.d.ts +37 -9
- package/dist/pipeline.js +89 -37
- package/dist/query.d.ts +268 -17
- package/dist/query.js +941 -137
- package/dist/schema-builder.d.ts +36 -3
- package/dist/schema-builder.js +57 -6
- package/dist/schema-sql.js +85 -19
- package/dist/serverless.d.ts +8 -7
- package/dist/serverless.js +8 -7
- package/package.json +3 -3
package/dist/schema-builder.d.ts
CHANGED
|
@@ -56,17 +56,50 @@ export interface ColumnConfig {
|
|
|
56
56
|
maxLength: number | null;
|
|
57
57
|
}
|
|
58
58
|
export interface TableDef {
|
|
59
|
-
/**
|
|
59
|
+
/**
|
|
60
|
+
* DDL-facing table name (snake_case). This is the name used when generating
|
|
61
|
+
* `CREATE TABLE` and other DDL statements. Set automatically during
|
|
62
|
+
* `defineSchema()` by converting the JS-facing accessor key from camelCase
|
|
63
|
+
* to snake_case (e.g. `postTags` → `post_tags`).
|
|
64
|
+
*/
|
|
60
65
|
name: string;
|
|
66
|
+
/**
|
|
67
|
+
* JS-facing accessor name (camelCase). This is the original key the user
|
|
68
|
+
* supplied to `defineSchema({ ... })` and is used as the property name on
|
|
69
|
+
* the generated `TurbineClient` (e.g. `db.postTags`). For schemas that
|
|
70
|
+
* already use snake_case keys, this matches `name`.
|
|
71
|
+
*/
|
|
72
|
+
accessor: string;
|
|
61
73
|
/** Column definitions keyed by camelCase field name */
|
|
62
74
|
columns: Record<string, ColumnConfig>;
|
|
75
|
+
/**
|
|
76
|
+
* Optional composite primary key. When present, takes precedence over any
|
|
77
|
+
* column-level `primaryKey: true` flags. Column names listed here are the
|
|
78
|
+
* camelCase JS-facing field names — they will be converted to snake_case
|
|
79
|
+
* when emitted as a `PRIMARY KEY (...)` table constraint.
|
|
80
|
+
*/
|
|
81
|
+
primaryKey?: readonly string[];
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* User-facing input shape for a single table when using the object format.
|
|
85
|
+
* The optional `primaryKey` field declares a composite primary key.
|
|
86
|
+
*/
|
|
87
|
+
export interface TableInput {
|
|
88
|
+
/** Optional composite primary key (camelCase field names) */
|
|
89
|
+
primaryKey?: readonly string[];
|
|
90
|
+
/** Column definitions keyed by camelCase field name */
|
|
91
|
+
[columnName: string]: ColumnDef | readonly string[] | undefined;
|
|
63
92
|
}
|
|
64
93
|
export interface SchemaDef {
|
|
65
|
-
/**
|
|
94
|
+
/**
|
|
95
|
+
* All tables keyed by their JS-facing accessor name (camelCase, exactly as
|
|
96
|
+
* the user wrote them in `defineSchema({ ... })`). The DDL-facing snake_case
|
|
97
|
+
* name is available as `tables[key].name`.
|
|
98
|
+
*/
|
|
66
99
|
tables: Record<string, TableDef>;
|
|
67
100
|
}
|
|
68
101
|
/** Input format: table name -> column defs (object format) or TableDef (legacy builder) */
|
|
69
|
-
type SchemaInput = Record<string, Record<string, ColumnDef> | TableDef>;
|
|
102
|
+
type SchemaInput = Record<string, Record<string, ColumnDef> | TableDef | TableInput>;
|
|
70
103
|
/**
|
|
71
104
|
* Define the full database schema using plain objects.
|
|
72
105
|
*
|
package/dist/schema-builder.js
CHANGED
|
@@ -81,23 +81,74 @@ function isTableDef(v) {
|
|
|
81
81
|
*/
|
|
82
82
|
export function defineSchema(input) {
|
|
83
83
|
const tables = {};
|
|
84
|
-
for (const [
|
|
84
|
+
for (const [accessor, value] of Object.entries(input)) {
|
|
85
|
+
// The user-facing key is the camelCase JS accessor; the DDL-facing
|
|
86
|
+
// table name is its snake_case form (e.g. `postTags` → `post_tags`).
|
|
87
|
+
const dbName = camelToSnakeLocal(accessor);
|
|
85
88
|
if (isTableDef(value)) {
|
|
86
89
|
// Legacy format: defineSchema({ users: table({ ... }) })
|
|
87
|
-
|
|
88
|
-
|
|
90
|
+
// Stamp both the DDL name and the JS accessor.
|
|
91
|
+
value.name = dbName;
|
|
92
|
+
value.accessor = accessor;
|
|
93
|
+
tables[accessor] = value;
|
|
89
94
|
}
|
|
90
95
|
else {
|
|
91
96
|
// Object format: defineSchema({ users: { id: { type: 'serial' }, ... } })
|
|
97
|
+
const raw = value;
|
|
92
98
|
const columns = {};
|
|
93
|
-
|
|
99
|
+
let pk;
|
|
100
|
+
for (const [fieldName, def] of Object.entries(raw)) {
|
|
101
|
+
if (fieldName === 'primaryKey') {
|
|
102
|
+
// Top-level composite primary key declaration
|
|
103
|
+
if (def !== undefined) {
|
|
104
|
+
if (!Array.isArray(def)) {
|
|
105
|
+
throw new Error(`Table "${accessor}": top-level "primaryKey" must be an array of column names (string[])`);
|
|
106
|
+
}
|
|
107
|
+
pk = def;
|
|
108
|
+
}
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
// Anything else is a ColumnDef
|
|
94
112
|
columns[fieldName] = resolveColumn(def);
|
|
95
113
|
}
|
|
96
|
-
|
|
114
|
+
// Validate composite PK references real columns and clear column-level PKs
|
|
115
|
+
// for those columns so we don't double-emit `PRIMARY KEY` clauses.
|
|
116
|
+
// Composite PK members are implicitly NOT NULL — preserve that even
|
|
117
|
+
// when the user clears the column-level `primaryKey: true` flag.
|
|
118
|
+
if (pk && pk.length > 0) {
|
|
119
|
+
for (const colName of pk) {
|
|
120
|
+
if (!(colName in columns)) {
|
|
121
|
+
throw new Error(`Table "${accessor}": composite primaryKey references unknown column "${colName}". ` +
|
|
122
|
+
`Known columns: ${Object.keys(columns).join(', ') || '(none)'}`);
|
|
123
|
+
}
|
|
124
|
+
// A composite PK at the table level supersedes any column-level
|
|
125
|
+
// `primaryKey: true` flag — silently clear it so DDL emission
|
|
126
|
+
// produces a single, valid table-level PRIMARY KEY constraint.
|
|
127
|
+
// Force NOT NULL since PK columns can never be nullable.
|
|
128
|
+
const c = columns[colName];
|
|
129
|
+
if (c) {
|
|
130
|
+
c.isPrimaryKey = false;
|
|
131
|
+
c.isNotNull = true;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
tables[accessor] = {
|
|
136
|
+
name: dbName,
|
|
137
|
+
accessor,
|
|
138
|
+
columns,
|
|
139
|
+
...(pk && pk.length > 0 ? { primaryKey: pk } : {}),
|
|
140
|
+
};
|
|
97
141
|
}
|
|
98
142
|
}
|
|
99
143
|
return { tables };
|
|
100
144
|
}
|
|
145
|
+
/**
|
|
146
|
+
* Local copy of camelToSnake to avoid a circular import dependency at the
|
|
147
|
+
* top of the file. Mirrors the implementation in `./schema.ts`.
|
|
148
|
+
*/
|
|
149
|
+
function camelToSnakeLocal(s) {
|
|
150
|
+
return s.replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`);
|
|
151
|
+
}
|
|
101
152
|
// ---------------------------------------------------------------------------
|
|
102
153
|
// Legacy compat — ColumnBuilder still works for existing code
|
|
103
154
|
// ---------------------------------------------------------------------------
|
|
@@ -223,7 +274,7 @@ export function table(columns) {
|
|
|
223
274
|
for (const [fieldName, builder] of Object.entries(columns)) {
|
|
224
275
|
built[fieldName] = builder.build();
|
|
225
276
|
}
|
|
226
|
-
return { name: '', columns: built };
|
|
277
|
+
return { name: '', accessor: '', columns: built };
|
|
227
278
|
}
|
|
228
279
|
// ---------------------------------------------------------------------------
|
|
229
280
|
// Helpers
|
package/dist/schema-sql.js
CHANGED
|
@@ -20,10 +20,11 @@ export function schemaToSQL(schema) {
|
|
|
20
20
|
const statements = [];
|
|
21
21
|
// Topologically sort tables by their foreign key references
|
|
22
22
|
const sorted = topologicalSort(schema);
|
|
23
|
+
const resolveRef = makeRefResolver(schema);
|
|
23
24
|
// Generate CREATE TABLE statements
|
|
24
25
|
for (const tableName of sorted) {
|
|
25
26
|
const table = schema.tables[tableName];
|
|
26
|
-
statements.push(generateCreateTable(table));
|
|
27
|
+
statements.push(generateCreateTable(table, resolveRef));
|
|
27
28
|
}
|
|
28
29
|
// Generate CREATE INDEX for foreign key columns
|
|
29
30
|
for (const tableName of sorted) {
|
|
@@ -33,12 +34,51 @@ export function schemaToSQL(schema) {
|
|
|
33
34
|
}
|
|
34
35
|
return statements;
|
|
35
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* Build a function that resolves a raw `references: 'foo.id'` target name
|
|
39
|
+
* to the snake_case DDL table name. Accepts both the JS-facing camelCase
|
|
40
|
+
* accessor name and the snake_case DDL name; passes through unknown names
|
|
41
|
+
* unchanged so existing call sites continue to work.
|
|
42
|
+
*/
|
|
43
|
+
function makeRefResolver(schema) {
|
|
44
|
+
const lookup = buildTableLookup(schema);
|
|
45
|
+
return (rawName) => {
|
|
46
|
+
const key = lookup[rawName];
|
|
47
|
+
if (key) {
|
|
48
|
+
const def = schema.tables[key];
|
|
49
|
+
if (def?.name)
|
|
50
|
+
return def.name;
|
|
51
|
+
}
|
|
52
|
+
return rawName;
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Build a lookup index from both DDL names (snake_case) and JS accessor
|
|
57
|
+
* names (camelCase) to table keys, so `references: 'post_tags.id'` and
|
|
58
|
+
* `references: 'postTags.id'` both resolve to the same TableDef.
|
|
59
|
+
*/
|
|
60
|
+
function buildTableLookup(schema) {
|
|
61
|
+
const lookup = {};
|
|
62
|
+
for (const [key, def] of Object.entries(schema.tables)) {
|
|
63
|
+
lookup[key] = key;
|
|
64
|
+
if (def.name && def.name !== key)
|
|
65
|
+
lookup[def.name] = key;
|
|
66
|
+
if (def.accessor && def.accessor !== key)
|
|
67
|
+
lookup[def.accessor] = key;
|
|
68
|
+
}
|
|
69
|
+
return lookup;
|
|
70
|
+
}
|
|
36
71
|
/**
|
|
37
72
|
* Topologically sort tables so that referenced tables come before referencing ones.
|
|
73
|
+
* Returns the table keys (the same keys used in `schema.tables`). The keys are
|
|
74
|
+
* the JS-facing accessor names; consumers should still call `table.name` to get
|
|
75
|
+
* the snake_case DDL name when emitting SQL.
|
|
76
|
+
*
|
|
38
77
|
* Falls back to input order for tables with no dependency ordering.
|
|
39
78
|
*/
|
|
40
79
|
function topologicalSort(schema) {
|
|
41
80
|
const tableNames = Object.keys(schema.tables);
|
|
81
|
+
const lookup = buildTableLookup(schema);
|
|
42
82
|
const resolved = new Set();
|
|
43
83
|
const result = [];
|
|
44
84
|
const visiting = new Set();
|
|
@@ -55,9 +95,10 @@ function topologicalSort(schema) {
|
|
|
55
95
|
// Visit all tables this table references
|
|
56
96
|
for (const col of Object.values(table.columns)) {
|
|
57
97
|
if (col.referencesTarget) {
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
98
|
+
const refRaw = col.referencesTarget.split('.')[0];
|
|
99
|
+
const refKey = lookup[refRaw];
|
|
100
|
+
if (refKey && refKey !== name) {
|
|
101
|
+
visit(refKey);
|
|
61
102
|
}
|
|
62
103
|
}
|
|
63
104
|
}
|
|
@@ -73,12 +114,28 @@ function topologicalSort(schema) {
|
|
|
73
114
|
}
|
|
74
115
|
/**
|
|
75
116
|
* Generate a CREATE TABLE statement for a single table definition.
|
|
117
|
+
*
|
|
118
|
+
* If `table.primaryKey` is set (composite primary key), emits a table-level
|
|
119
|
+
* `PRIMARY KEY ("col1", "col2", ...)` constraint instead of column-level
|
|
120
|
+
* `PRIMARY KEY` clauses on each member column. The composite PK column
|
|
121
|
+
* names are camelCase JS field names; they are converted to snake_case
|
|
122
|
+
* here.
|
|
123
|
+
*
|
|
124
|
+
* `resolveRef` (when supplied) maps raw `references: 'foo.id'` table names
|
|
125
|
+
* to their snake_case DDL form, so users can write either camelCase JS
|
|
126
|
+
* accessor names or snake_case DDL names.
|
|
76
127
|
*/
|
|
77
|
-
function generateCreateTable(table) {
|
|
128
|
+
function generateCreateTable(table, resolveRef) {
|
|
78
129
|
const tableName = table.name;
|
|
79
130
|
const columnDefs = [];
|
|
131
|
+
const compositePk = table.primaryKey && table.primaryKey.length > 0 ? table.primaryKey : null;
|
|
80
132
|
for (const [fieldName, config] of Object.entries(table.columns)) {
|
|
81
|
-
columnDefs.push(generateColumnDef(fieldName, config));
|
|
133
|
+
columnDefs.push(generateColumnDef(fieldName, config, resolveRef));
|
|
134
|
+
}
|
|
135
|
+
// Append a table-level PRIMARY KEY constraint when a composite PK is set.
|
|
136
|
+
if (compositePk) {
|
|
137
|
+
const cols = compositePk.map((c) => quoteIdent(camelToSnake(c))).join(', ');
|
|
138
|
+
columnDefs.push(`PRIMARY KEY (${cols})`);
|
|
82
139
|
}
|
|
83
140
|
const body = columnDefs.map((d) => ` ${d}`).join(',\n');
|
|
84
141
|
return `CREATE TABLE ${quoteIdent(tableName)} (\n${body}\n);`;
|
|
@@ -86,7 +143,7 @@ function generateCreateTable(table) {
|
|
|
86
143
|
/**
|
|
87
144
|
* Generate a single column definition line (e.g. "id BIGSERIAL PRIMARY KEY").
|
|
88
145
|
*/
|
|
89
|
-
function generateColumnDef(fieldName, config) {
|
|
146
|
+
function generateColumnDef(fieldName, config, resolveRef) {
|
|
90
147
|
const snakeName = camelToSnake(fieldName);
|
|
91
148
|
const parts = [quoteIdent(snakeName)];
|
|
92
149
|
// Type
|
|
@@ -120,11 +177,14 @@ function generateColumnDef(fieldName, config) {
|
|
|
120
177
|
const sqlDefault = normalizeDefault(config.defaultValue);
|
|
121
178
|
parts.push(`DEFAULT ${sqlDefault}`);
|
|
122
179
|
}
|
|
123
|
-
// REFERENCES
|
|
180
|
+
// REFERENCES — resolve the raw table name through the optional resolver so
|
|
181
|
+
// both camelCase accessor names and snake_case DDL names work.
|
|
124
182
|
if (config.referencesTarget) {
|
|
125
183
|
const refParts = config.referencesTarget.split('.');
|
|
126
184
|
if (refParts.length === 2) {
|
|
127
|
-
|
|
185
|
+
const rawTable = refParts[0];
|
|
186
|
+
const refTable = resolveRef ? resolveRef(rawTable) : rawTable;
|
|
187
|
+
parts.push(`REFERENCES ${quoteIdent(refTable)}(${quoteIdent(refParts[1])})`);
|
|
128
188
|
}
|
|
129
189
|
}
|
|
130
190
|
return parts.join(' ');
|
|
@@ -231,33 +291,39 @@ export async function schemaDiff(schema, connectionString) {
|
|
|
231
291
|
dbUniques[row.table_name] = {};
|
|
232
292
|
dbUniques[row.table_name][row.column_name] = row.constraint_name;
|
|
233
293
|
}
|
|
234
|
-
|
|
294
|
+
// Build a set of DDL-facing snake_case table names that the schema defines.
|
|
295
|
+
const schemaDdlNames = new Set();
|
|
296
|
+
for (const def of Object.values(schema.tables))
|
|
297
|
+
schemaDdlNames.add(def.name);
|
|
235
298
|
const result = { create: [], alter: [], drop: [], statements: [], reverseStatements: [] };
|
|
299
|
+
const resolveRef = makeRefResolver(schema);
|
|
236
300
|
// Tables to create (in schema but not in DB)
|
|
237
301
|
const sorted = topologicalSort(schema);
|
|
238
|
-
for (const
|
|
239
|
-
|
|
240
|
-
|
|
302
|
+
for (const tableKey of sorted) {
|
|
303
|
+
const tableDef = schema.tables[tableKey];
|
|
304
|
+
const ddlName = tableDef.name;
|
|
305
|
+
if (!existingTables.has(ddlName)) {
|
|
241
306
|
result.create.push(tableDef);
|
|
242
|
-
result.statements.push(generateCreateTable(tableDef));
|
|
307
|
+
result.statements.push(generateCreateTable(tableDef, resolveRef));
|
|
243
308
|
const fkIndexes = generateForeignKeyIndexes(tableDef);
|
|
244
309
|
result.statements.push(...fkIndexes);
|
|
245
310
|
// Reverse: DROP TABLE (with indexes — they drop automatically)
|
|
246
|
-
result.reverseStatements.unshift(`DROP TABLE IF EXISTS ${quoteIdent(
|
|
311
|
+
result.reverseStatements.unshift(`DROP TABLE IF EXISTS ${quoteIdent(ddlName)} CASCADE;`);
|
|
247
312
|
}
|
|
248
313
|
}
|
|
249
314
|
// Tables to drop (in DB but not in schema)
|
|
250
315
|
for (const existingTable of existingTables) {
|
|
251
|
-
if (!
|
|
316
|
+
if (!schemaDdlNames.has(existingTable)) {
|
|
252
317
|
result.drop.push(existingTable);
|
|
253
318
|
// We don't auto-generate DROP statements for safety
|
|
254
319
|
}
|
|
255
320
|
}
|
|
256
321
|
// Tables to alter (exist in both)
|
|
257
|
-
for (const
|
|
322
|
+
for (const tableKey of sorted) {
|
|
323
|
+
const tableDef = schema.tables[tableKey];
|
|
324
|
+
const tableName = tableDef.name;
|
|
258
325
|
if (!existingTables.has(tableName))
|
|
259
326
|
continue;
|
|
260
|
-
const tableDef = schema.tables[tableName];
|
|
261
327
|
const dbCols = dbColumns[tableName] ?? {};
|
|
262
328
|
const tableUniques = dbUniques[tableName] ?? {};
|
|
263
329
|
const alterDef = { table: tableName, columns: [] };
|
|
@@ -266,7 +332,7 @@ export async function schemaDiff(schema, connectionString) {
|
|
|
266
332
|
const dbCol = dbCols[snakeName];
|
|
267
333
|
if (!dbCol) {
|
|
268
334
|
// Column exists in schema but not in DB — ADD COLUMN
|
|
269
|
-
const colDef = generateColumnDef(fieldName, config);
|
|
335
|
+
const colDef = generateColumnDef(fieldName, config, resolveRef);
|
|
270
336
|
const sql = `ALTER TABLE ${quoteIdent(tableName)} ADD COLUMN ${colDef};`;
|
|
271
337
|
const reverseSql = `ALTER TABLE ${quoteIdent(tableName)} DROP COLUMN ${quoteIdent(snakeName)};`;
|
|
272
338
|
alterDef.columns.push({ column: snakeName, action: 'add', sql, reverseSql });
|
package/dist/serverless.d.ts
CHANGED
|
@@ -35,12 +35,12 @@
|
|
|
35
35
|
* // app/api/users/route.ts
|
|
36
36
|
* import { Pool } from '@neondatabase/serverless';
|
|
37
37
|
* import { turbineHttp } from 'turbine-orm/serverless';
|
|
38
|
-
* import {
|
|
38
|
+
* import { SCHEMA } from '../../generated/turbine/metadata';
|
|
39
39
|
*
|
|
40
40
|
* export const runtime = 'edge';
|
|
41
41
|
*
|
|
42
42
|
* const pool = new Pool({ connectionString: process.env.DATABASE_URL });
|
|
43
|
-
* const db = turbineHttp(pool,
|
|
43
|
+
* const db = turbineHttp(pool, SCHEMA);
|
|
44
44
|
*
|
|
45
45
|
* export async function GET() {
|
|
46
46
|
* const users = await db.table('users').findMany({ limit: 10 });
|
|
@@ -52,12 +52,12 @@
|
|
|
52
52
|
*
|
|
53
53
|
* ```ts
|
|
54
54
|
* import { TurbineClient } from 'turbine-orm';
|
|
55
|
-
* import {
|
|
55
|
+
* import { SCHEMA } from './generated/turbine/metadata.js';
|
|
56
56
|
*
|
|
57
57
|
* const db = new TurbineClient({
|
|
58
58
|
* connectionString: process.env.SUPABASE_DB_URL,
|
|
59
59
|
* ssl: { rejectUnauthorized: false },
|
|
60
|
-
* },
|
|
60
|
+
* }, SCHEMA);
|
|
61
61
|
* ```
|
|
62
62
|
*
|
|
63
63
|
* ## Example — Cloudflare Workers
|
|
@@ -66,11 +66,12 @@
|
|
|
66
66
|
* // Use the Neon HTTP driver which works in Workers runtime
|
|
67
67
|
* import { Pool } from '@neondatabase/serverless';
|
|
68
68
|
* import { turbineHttp } from 'turbine-orm/serverless';
|
|
69
|
+
* import { SCHEMA } from './generated/turbine/metadata';
|
|
69
70
|
*
|
|
70
71
|
* export default {
|
|
71
72
|
* async fetch(req: Request, env: Env) {
|
|
72
73
|
* const pool = new Pool({ connectionString: env.DATABASE_URL });
|
|
73
|
-
* const db = turbineHttp(pool,
|
|
74
|
+
* const db = turbineHttp(pool, SCHEMA);
|
|
74
75
|
* const users = await db.table('users').findMany({ limit: 10 });
|
|
75
76
|
* return Response.json(users);
|
|
76
77
|
* }
|
|
@@ -102,10 +103,10 @@ export interface TurbineHttpOptions extends Pick<TurbineConfig, 'logging' | 'def
|
|
|
102
103
|
* ```ts
|
|
103
104
|
* import { Pool } from '@neondatabase/serverless';
|
|
104
105
|
* import { turbineHttp } from 'turbine-orm/serverless';
|
|
105
|
-
* import {
|
|
106
|
+
* import { SCHEMA } from './generated/turbine/metadata.js';
|
|
106
107
|
*
|
|
107
108
|
* const pool = new Pool({ connectionString: process.env.DATABASE_URL });
|
|
108
|
-
* const db = turbineHttp(pool,
|
|
109
|
+
* const db = turbineHttp(pool, SCHEMA);
|
|
109
110
|
*
|
|
110
111
|
* const users = await db.table('users').findMany({ limit: 10 });
|
|
111
112
|
* ```
|
package/dist/serverless.js
CHANGED
|
@@ -35,12 +35,12 @@
|
|
|
35
35
|
* // app/api/users/route.ts
|
|
36
36
|
* import { Pool } from '@neondatabase/serverless';
|
|
37
37
|
* import { turbineHttp } from 'turbine-orm/serverless';
|
|
38
|
-
* import {
|
|
38
|
+
* import { SCHEMA } from '../../generated/turbine/metadata';
|
|
39
39
|
*
|
|
40
40
|
* export const runtime = 'edge';
|
|
41
41
|
*
|
|
42
42
|
* const pool = new Pool({ connectionString: process.env.DATABASE_URL });
|
|
43
|
-
* const db = turbineHttp(pool,
|
|
43
|
+
* const db = turbineHttp(pool, SCHEMA);
|
|
44
44
|
*
|
|
45
45
|
* export async function GET() {
|
|
46
46
|
* const users = await db.table('users').findMany({ limit: 10 });
|
|
@@ -52,12 +52,12 @@
|
|
|
52
52
|
*
|
|
53
53
|
* ```ts
|
|
54
54
|
* import { TurbineClient } from 'turbine-orm';
|
|
55
|
-
* import {
|
|
55
|
+
* import { SCHEMA } from './generated/turbine/metadata.js';
|
|
56
56
|
*
|
|
57
57
|
* const db = new TurbineClient({
|
|
58
58
|
* connectionString: process.env.SUPABASE_DB_URL,
|
|
59
59
|
* ssl: { rejectUnauthorized: false },
|
|
60
|
-
* },
|
|
60
|
+
* }, SCHEMA);
|
|
61
61
|
* ```
|
|
62
62
|
*
|
|
63
63
|
* ## Example — Cloudflare Workers
|
|
@@ -66,11 +66,12 @@
|
|
|
66
66
|
* // Use the Neon HTTP driver which works in Workers runtime
|
|
67
67
|
* import { Pool } from '@neondatabase/serverless';
|
|
68
68
|
* import { turbineHttp } from 'turbine-orm/serverless';
|
|
69
|
+
* import { SCHEMA } from './generated/turbine/metadata';
|
|
69
70
|
*
|
|
70
71
|
* export default {
|
|
71
72
|
* async fetch(req: Request, env: Env) {
|
|
72
73
|
* const pool = new Pool({ connectionString: env.DATABASE_URL });
|
|
73
|
-
* const db = turbineHttp(pool,
|
|
74
|
+
* const db = turbineHttp(pool, SCHEMA);
|
|
74
75
|
* const users = await db.table('users').findMany({ limit: 10 });
|
|
75
76
|
* return Response.json(users);
|
|
76
77
|
* }
|
|
@@ -94,10 +95,10 @@ import { TurbineClient } from './client.js';
|
|
|
94
95
|
* ```ts
|
|
95
96
|
* import { Pool } from '@neondatabase/serverless';
|
|
96
97
|
* import { turbineHttp } from 'turbine-orm/serverless';
|
|
97
|
-
* import {
|
|
98
|
+
* import { SCHEMA } from './generated/turbine/metadata.js';
|
|
98
99
|
*
|
|
99
100
|
* const pool = new Pool({ connectionString: process.env.DATABASE_URL });
|
|
100
|
-
* const db = turbineHttp(pool,
|
|
101
|
+
* const db = turbineHttp(pool, SCHEMA);
|
|
101
102
|
*
|
|
102
103
|
* const users = await db.table('users').findMany({ limit: 10 });
|
|
103
104
|
* ```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "turbine-orm",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.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": {
|
|
@@ -43,8 +43,8 @@
|
|
|
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 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",
|
|
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",
|
|
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/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",
|
|
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/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",
|
|
48
48
|
"lint": "biome check src/",
|
|
49
49
|
"lint:fix": "biome check --write src/",
|
|
50
50
|
"format": "biome format --write src/",
|