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/schema-builder.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* turbine-orm — Schema Builder
|
|
3
3
|
*
|
|
4
4
|
* TypeScript-first schema definition API. Define your database schema
|
|
5
5
|
* as plain objects — no method chaining, no DSL. Fully type-checked,
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*
|
|
8
8
|
* @example
|
|
9
9
|
* ```ts
|
|
10
|
-
* import { defineSchema } from '
|
|
10
|
+
* import { defineSchema } from 'turbine-orm';
|
|
11
11
|
*
|
|
12
12
|
* export default defineSchema({
|
|
13
13
|
* users: {
|
|
@@ -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
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* turbine-orm — Schema Builder
|
|
3
3
|
*
|
|
4
4
|
* TypeScript-first schema definition API. Define your database schema
|
|
5
5
|
* as plain objects — no method chaining, no DSL. Fully type-checked,
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*
|
|
8
8
|
* @example
|
|
9
9
|
* ```ts
|
|
10
|
-
* import { defineSchema } from '
|
|
10
|
+
* import { defineSchema } from 'turbine-orm';
|
|
11
11
|
*
|
|
12
12
|
* export default defineSchema({
|
|
13
13
|
* users: {
|
|
@@ -22,7 +22,6 @@
|
|
|
22
22
|
* });
|
|
23
23
|
* ```
|
|
24
24
|
*/
|
|
25
|
-
import { camelToSnake } from './schema.js';
|
|
26
25
|
/** Maps shorthand names to actual Postgres type strings */
|
|
27
26
|
const TYPE_MAP = {
|
|
28
27
|
serial: 'BIGSERIAL',
|
|
@@ -43,6 +42,9 @@ const TYPE_MAP = {
|
|
|
43
42
|
};
|
|
44
43
|
/** Convert a user-facing ColumnDef to the internal ColumnConfig */
|
|
45
44
|
function resolveColumn(def) {
|
|
45
|
+
if (!(def.type in TYPE_MAP)) {
|
|
46
|
+
throw new Error(`Invalid column type "${def.type}". Valid types: ${Object.keys(TYPE_MAP).join(', ')}`);
|
|
47
|
+
}
|
|
46
48
|
return {
|
|
47
49
|
type: TYPE_MAP[def.type],
|
|
48
50
|
isPrimaryKey: def.primaryKey ?? false,
|
|
@@ -79,23 +81,74 @@ function isTableDef(v) {
|
|
|
79
81
|
*/
|
|
80
82
|
export function defineSchema(input) {
|
|
81
83
|
const tables = {};
|
|
82
|
-
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);
|
|
83
88
|
if (isTableDef(value)) {
|
|
84
89
|
// Legacy format: defineSchema({ users: table({ ... }) })
|
|
85
|
-
|
|
86
|
-
|
|
90
|
+
// Stamp both the DDL name and the JS accessor.
|
|
91
|
+
value.name = dbName;
|
|
92
|
+
value.accessor = accessor;
|
|
93
|
+
tables[accessor] = value;
|
|
87
94
|
}
|
|
88
95
|
else {
|
|
89
96
|
// Object format: defineSchema({ users: { id: { type: 'serial' }, ... } })
|
|
97
|
+
const raw = value;
|
|
90
98
|
const columns = {};
|
|
91
|
-
|
|
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
|
|
92
112
|
columns[fieldName] = resolveColumn(def);
|
|
93
113
|
}
|
|
94
|
-
|
|
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
|
+
};
|
|
95
141
|
}
|
|
96
142
|
}
|
|
97
143
|
return { tables };
|
|
98
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
|
+
}
|
|
99
152
|
// ---------------------------------------------------------------------------
|
|
100
153
|
// Legacy compat — ColumnBuilder still works for existing code
|
|
101
154
|
// ---------------------------------------------------------------------------
|
|
@@ -113,28 +166,94 @@ export class ColumnBuilder {
|
|
|
113
166
|
maxLength: null,
|
|
114
167
|
};
|
|
115
168
|
}
|
|
116
|
-
serial() {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
169
|
+
serial() {
|
|
170
|
+
this._config.type = 'BIGSERIAL';
|
|
171
|
+
return this;
|
|
172
|
+
}
|
|
173
|
+
bigint() {
|
|
174
|
+
this._config.type = 'BIGINT';
|
|
175
|
+
return this;
|
|
176
|
+
}
|
|
177
|
+
integer() {
|
|
178
|
+
this._config.type = 'INTEGER';
|
|
179
|
+
return this;
|
|
180
|
+
}
|
|
181
|
+
smallint() {
|
|
182
|
+
this._config.type = 'SMALLINT';
|
|
183
|
+
return this;
|
|
184
|
+
}
|
|
185
|
+
text() {
|
|
186
|
+
this._config.type = 'TEXT';
|
|
187
|
+
return this;
|
|
188
|
+
}
|
|
189
|
+
varchar(length) {
|
|
190
|
+
this._config.type = 'VARCHAR';
|
|
191
|
+
this._config.maxLength = length;
|
|
192
|
+
return this;
|
|
193
|
+
}
|
|
194
|
+
boolean() {
|
|
195
|
+
this._config.type = 'BOOLEAN';
|
|
196
|
+
return this;
|
|
197
|
+
}
|
|
198
|
+
timestamp() {
|
|
199
|
+
this._config.type = 'TIMESTAMPTZ';
|
|
200
|
+
return this;
|
|
201
|
+
}
|
|
202
|
+
date() {
|
|
203
|
+
this._config.type = 'DATE';
|
|
204
|
+
return this;
|
|
205
|
+
}
|
|
206
|
+
json() {
|
|
207
|
+
this._config.type = 'JSONB';
|
|
208
|
+
return this;
|
|
209
|
+
}
|
|
210
|
+
uuid() {
|
|
211
|
+
this._config.type = 'UUID';
|
|
212
|
+
return this;
|
|
213
|
+
}
|
|
214
|
+
real() {
|
|
215
|
+
this._config.type = 'REAL';
|
|
216
|
+
return this;
|
|
217
|
+
}
|
|
218
|
+
doublePrecision() {
|
|
219
|
+
this._config.type = 'DOUBLE PRECISION';
|
|
220
|
+
return this;
|
|
221
|
+
}
|
|
222
|
+
numeric() {
|
|
223
|
+
this._config.type = 'NUMERIC';
|
|
224
|
+
return this;
|
|
225
|
+
}
|
|
226
|
+
bytea() {
|
|
227
|
+
this._config.type = 'BYTEA';
|
|
228
|
+
return this;
|
|
229
|
+
}
|
|
230
|
+
primaryKey() {
|
|
231
|
+
this._config.isPrimaryKey = true;
|
|
232
|
+
return this;
|
|
233
|
+
}
|
|
234
|
+
notNull() {
|
|
235
|
+
this._config.isNotNull = true;
|
|
236
|
+
return this;
|
|
237
|
+
}
|
|
238
|
+
nullable() {
|
|
239
|
+
this._config.isNullable = true;
|
|
240
|
+
return this;
|
|
241
|
+
}
|
|
242
|
+
unique() {
|
|
243
|
+
this._config.isUnique = true;
|
|
244
|
+
return this;
|
|
245
|
+
}
|
|
246
|
+
default(val) {
|
|
247
|
+
this._config.defaultValue = val;
|
|
248
|
+
return this;
|
|
249
|
+
}
|
|
250
|
+
references(target) {
|
|
251
|
+
this._config.referencesTarget = target;
|
|
252
|
+
return this;
|
|
253
|
+
}
|
|
254
|
+
build() {
|
|
255
|
+
return { ...this._config };
|
|
256
|
+
}
|
|
138
257
|
}
|
|
139
258
|
/** @deprecated Use defineSchema() with plain objects instead */
|
|
140
259
|
export const column = new Proxy({}, {
|
|
@@ -155,7 +274,7 @@ export function table(columns) {
|
|
|
155
274
|
for (const [fieldName, builder] of Object.entries(columns)) {
|
|
156
275
|
built[fieldName] = builder.build();
|
|
157
276
|
}
|
|
158
|
-
return { name: '', columns: built };
|
|
277
|
+
return { name: '', accessor: '', columns: built };
|
|
159
278
|
}
|
|
160
279
|
// ---------------------------------------------------------------------------
|
|
161
280
|
// Helpers
|
package/dist/schema-sql.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* turbine-orm — Schema SQL Generator
|
|
3
3
|
*
|
|
4
4
|
* Converts a SchemaDef (from defineSchema) into executable DDL statements.
|
|
5
5
|
* Also provides diff and push commands for syncing schema to a live database.
|
|
@@ -16,9 +16,11 @@ export interface AlterColumnDef {
|
|
|
16
16
|
/** Column name in snake_case */
|
|
17
17
|
column: string;
|
|
18
18
|
/** What changed */
|
|
19
|
-
action: 'add' | 'drop' | 'alter_type' | 'set_not_null' | 'drop_not_null' | 'set_default' | 'drop_default';
|
|
19
|
+
action: 'add' | 'drop' | 'alter_type' | 'set_not_null' | 'drop_not_null' | 'set_default' | 'drop_default' | 'add_unique' | 'drop_unique';
|
|
20
20
|
/** SQL fragment for the alteration */
|
|
21
21
|
sql: string;
|
|
22
|
+
/** SQL to reverse this change (for DOWN migrations) */
|
|
23
|
+
reverseSql: string;
|
|
22
24
|
}
|
|
23
25
|
export interface AlterDef {
|
|
24
26
|
/** Table name */
|
|
@@ -33,8 +35,10 @@ export interface DiffResult {
|
|
|
33
35
|
alter: AlterDef[];
|
|
34
36
|
/** Table names that exist in DB but not in schema — would need DROP TABLE */
|
|
35
37
|
drop: string[];
|
|
36
|
-
/** SQL statements to execute the diff */
|
|
38
|
+
/** SQL statements to execute the diff (UP direction) */
|
|
37
39
|
statements: string[];
|
|
40
|
+
/** SQL statements to reverse the diff (DOWN direction, for migrations) */
|
|
41
|
+
reverseStatements: string[];
|
|
38
42
|
}
|
|
39
43
|
/**
|
|
40
44
|
* Compare a SchemaDef against a live Postgres database and return the diff.
|