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/errors.d.ts
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* turbine-orm — Error types
|
|
3
|
+
*
|
|
4
|
+
* Typed errors with error codes for programmatic handling.
|
|
5
|
+
* All Turbine errors extend TurbineError which includes a `code` property.
|
|
6
|
+
*/
|
|
7
|
+
/** Error codes for all Turbine errors */
|
|
8
|
+
export declare const TurbineErrorCode: {
|
|
9
|
+
readonly NOT_FOUND: "TURBINE_E001";
|
|
10
|
+
readonly TIMEOUT: "TURBINE_E002";
|
|
11
|
+
readonly VALIDATION: "TURBINE_E003";
|
|
12
|
+
readonly CONNECTION: "TURBINE_E004";
|
|
13
|
+
readonly RELATION: "TURBINE_E005";
|
|
14
|
+
readonly MIGRATION: "TURBINE_E006";
|
|
15
|
+
readonly CIRCULAR_RELATION: "TURBINE_E007";
|
|
16
|
+
readonly UNIQUE_VIOLATION: "TURBINE_E008";
|
|
17
|
+
readonly FOREIGN_KEY_VIOLATION: "TURBINE_E009";
|
|
18
|
+
readonly NOT_NULL_VIOLATION: "TURBINE_E010";
|
|
19
|
+
readonly CHECK_VIOLATION: "TURBINE_E011";
|
|
20
|
+
};
|
|
21
|
+
export type TurbineErrorCode = (typeof TurbineErrorCode)[keyof typeof TurbineErrorCode];
|
|
22
|
+
/** Base error class for all Turbine errors */
|
|
23
|
+
export declare class TurbineError extends Error {
|
|
24
|
+
readonly code: TurbineErrorCode;
|
|
25
|
+
constructor(code: TurbineErrorCode, message: string, options?: {
|
|
26
|
+
cause?: unknown;
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Thrown when a record is not found (findUniqueOrThrow, findFirstOrThrow,
|
|
31
|
+
* update/delete against a non-matching row, etc.)
|
|
32
|
+
*
|
|
33
|
+
* Supports two call styles for back-compat:
|
|
34
|
+
* - `new NotFoundError()` / `new NotFoundError('custom message')`
|
|
35
|
+
* - `new NotFoundError({ table, where, operation, cause, message })`
|
|
36
|
+
*
|
|
37
|
+
* When called with an options object and no explicit `message`, a Prisma-style
|
|
38
|
+
* message is built automatically, e.g.:
|
|
39
|
+
* `[turbine] findUniqueOrThrow on "users" found no record matching where: {"id":1}`
|
|
40
|
+
*/
|
|
41
|
+
export declare class NotFoundError extends TurbineError {
|
|
42
|
+
readonly table?: string;
|
|
43
|
+
readonly where?: unknown;
|
|
44
|
+
readonly operation?: string;
|
|
45
|
+
constructor(input?: string | {
|
|
46
|
+
table?: string;
|
|
47
|
+
where?: unknown;
|
|
48
|
+
operation?: string;
|
|
49
|
+
cause?: unknown;
|
|
50
|
+
message?: string;
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
/** Thrown when a query or transaction exceeds the configured timeout */
|
|
54
|
+
export declare class TimeoutError extends TurbineError {
|
|
55
|
+
readonly timeoutMs: number;
|
|
56
|
+
constructor(timeoutMs: number, context?: string);
|
|
57
|
+
}
|
|
58
|
+
/** Thrown when query arguments fail validation (unknown column, invalid operator, etc.) */
|
|
59
|
+
export declare class ValidationError extends TurbineError {
|
|
60
|
+
constructor(message: string);
|
|
61
|
+
}
|
|
62
|
+
/** Thrown when a database connection fails */
|
|
63
|
+
export declare class ConnectionError extends TurbineError {
|
|
64
|
+
constructor(message: string);
|
|
65
|
+
}
|
|
66
|
+
/** Thrown when a relation reference is invalid */
|
|
67
|
+
export declare class RelationError extends TurbineError {
|
|
68
|
+
constructor(message: string);
|
|
69
|
+
}
|
|
70
|
+
/** Thrown when a migration operation fails */
|
|
71
|
+
export declare class MigrationError extends TurbineError {
|
|
72
|
+
constructor(message: string);
|
|
73
|
+
}
|
|
74
|
+
/** Thrown when circular relation nesting is detected */
|
|
75
|
+
export declare class CircularRelationError extends TurbineError {
|
|
76
|
+
readonly path: string[];
|
|
77
|
+
constructor(path: string[]);
|
|
78
|
+
}
|
|
79
|
+
/** Thrown when a UNIQUE constraint is violated (pg code 23505) */
|
|
80
|
+
export declare class UniqueConstraintError extends TurbineError {
|
|
81
|
+
readonly constraint?: string;
|
|
82
|
+
readonly columns?: string[];
|
|
83
|
+
readonly table?: string;
|
|
84
|
+
constructor(opts?: {
|
|
85
|
+
constraint?: string;
|
|
86
|
+
columns?: string[];
|
|
87
|
+
table?: string;
|
|
88
|
+
message?: string;
|
|
89
|
+
cause?: unknown;
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
/** Thrown when a FOREIGN KEY constraint is violated (pg code 23503) */
|
|
93
|
+
export declare class ForeignKeyError extends TurbineError {
|
|
94
|
+
readonly constraint?: string;
|
|
95
|
+
readonly table?: string;
|
|
96
|
+
constructor(opts?: {
|
|
97
|
+
constraint?: string;
|
|
98
|
+
table?: string;
|
|
99
|
+
message?: string;
|
|
100
|
+
cause?: unknown;
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
/** Thrown when a NOT NULL constraint is violated (pg code 23502) */
|
|
104
|
+
export declare class NotNullViolationError extends TurbineError {
|
|
105
|
+
readonly column?: string;
|
|
106
|
+
readonly table?: string;
|
|
107
|
+
constructor(opts?: {
|
|
108
|
+
column?: string;
|
|
109
|
+
table?: string;
|
|
110
|
+
message?: string;
|
|
111
|
+
cause?: unknown;
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
/** Thrown when a CHECK constraint is violated (pg code 23514) */
|
|
115
|
+
export declare class CheckConstraintError extends TurbineError {
|
|
116
|
+
readonly constraint?: string;
|
|
117
|
+
readonly table?: string;
|
|
118
|
+
constructor(opts?: {
|
|
119
|
+
constraint?: string;
|
|
120
|
+
table?: string;
|
|
121
|
+
message?: string;
|
|
122
|
+
cause?: unknown;
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Translate a pg driver error into a typed Turbine error.
|
|
127
|
+
* If the error doesn't match a known constraint code, returns it unchanged.
|
|
128
|
+
*
|
|
129
|
+
* Maps:
|
|
130
|
+
* 23505 (unique_violation) -> UniqueConstraintError
|
|
131
|
+
* 23503 (foreign_key_violation) -> ForeignKeyError
|
|
132
|
+
* 23502 (not_null_violation) -> NotNullViolationError
|
|
133
|
+
* 23514 (check_violation) -> CheckConstraintError
|
|
134
|
+
*
|
|
135
|
+
* The original pg error is preserved as `.cause` on the wrapped error.
|
|
136
|
+
*/
|
|
137
|
+
export declare function wrapPgError(err: unknown): unknown;
|
|
138
|
+
//# sourceMappingURL=errors.d.ts.map
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* turbine-orm — Error types
|
|
3
|
+
*
|
|
4
|
+
* Typed errors with error codes for programmatic handling.
|
|
5
|
+
* All Turbine errors extend TurbineError which includes a `code` property.
|
|
6
|
+
*/
|
|
7
|
+
/** Error codes for all Turbine errors */
|
|
8
|
+
export const TurbineErrorCode = {
|
|
9
|
+
NOT_FOUND: 'TURBINE_E001',
|
|
10
|
+
TIMEOUT: 'TURBINE_E002',
|
|
11
|
+
VALIDATION: 'TURBINE_E003',
|
|
12
|
+
CONNECTION: 'TURBINE_E004',
|
|
13
|
+
RELATION: 'TURBINE_E005',
|
|
14
|
+
MIGRATION: 'TURBINE_E006',
|
|
15
|
+
CIRCULAR_RELATION: 'TURBINE_E007',
|
|
16
|
+
UNIQUE_VIOLATION: 'TURBINE_E008',
|
|
17
|
+
FOREIGN_KEY_VIOLATION: 'TURBINE_E009',
|
|
18
|
+
NOT_NULL_VIOLATION: 'TURBINE_E010',
|
|
19
|
+
CHECK_VIOLATION: 'TURBINE_E011',
|
|
20
|
+
};
|
|
21
|
+
/** Base error class for all Turbine errors */
|
|
22
|
+
export class TurbineError extends Error {
|
|
23
|
+
code;
|
|
24
|
+
constructor(code, message, options) {
|
|
25
|
+
super(message, options);
|
|
26
|
+
this.name = 'TurbineError';
|
|
27
|
+
this.code = code;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Thrown when a record is not found (findUniqueOrThrow, findFirstOrThrow,
|
|
32
|
+
* update/delete against a non-matching row, etc.)
|
|
33
|
+
*
|
|
34
|
+
* Supports two call styles for back-compat:
|
|
35
|
+
* - `new NotFoundError()` / `new NotFoundError('custom message')`
|
|
36
|
+
* - `new NotFoundError({ table, where, operation, cause, message })`
|
|
37
|
+
*
|
|
38
|
+
* When called with an options object and no explicit `message`, a Prisma-style
|
|
39
|
+
* message is built automatically, e.g.:
|
|
40
|
+
* `[turbine] findUniqueOrThrow on "users" found no record matching where: {"id":1}`
|
|
41
|
+
*/
|
|
42
|
+
export class NotFoundError extends TurbineError {
|
|
43
|
+
table;
|
|
44
|
+
where;
|
|
45
|
+
operation;
|
|
46
|
+
constructor(input) {
|
|
47
|
+
// Back-compat: string argument (or undefined) — replicate legacy behavior.
|
|
48
|
+
if (typeof input === 'string' || input === undefined) {
|
|
49
|
+
super(TurbineErrorCode.NOT_FOUND, input ?? 'Record not found');
|
|
50
|
+
this.name = 'NotFoundError';
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const { table, where, operation, cause } = input;
|
|
54
|
+
let message = input.message;
|
|
55
|
+
if (!message) {
|
|
56
|
+
if (operation && table) {
|
|
57
|
+
const wherePart = where !== undefined ? ` matching where: ${JSON.stringify(where)}` : '';
|
|
58
|
+
message = `[turbine] ${operation} on "${table}" found no record${wherePart}`;
|
|
59
|
+
}
|
|
60
|
+
else if (table) {
|
|
61
|
+
const wherePart = where !== undefined ? ` matching where ${JSON.stringify(where)}` : '';
|
|
62
|
+
message = `[turbine] No record found in "${table}"${wherePart}`;
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
message = '[turbine] Record not found';
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
super(TurbineErrorCode.NOT_FOUND, message, { cause });
|
|
69
|
+
this.name = 'NotFoundError';
|
|
70
|
+
this.table = table;
|
|
71
|
+
this.where = where;
|
|
72
|
+
this.operation = operation;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/** Thrown when a query or transaction exceeds the configured timeout */
|
|
76
|
+
export class TimeoutError extends TurbineError {
|
|
77
|
+
timeoutMs;
|
|
78
|
+
constructor(timeoutMs, context = 'Query') {
|
|
79
|
+
super(TurbineErrorCode.TIMEOUT, `[turbine] ${context} timed out after ${timeoutMs}ms`);
|
|
80
|
+
this.name = 'TimeoutError';
|
|
81
|
+
this.timeoutMs = timeoutMs;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/** Thrown when query arguments fail validation (unknown column, invalid operator, etc.) */
|
|
85
|
+
export class ValidationError extends TurbineError {
|
|
86
|
+
constructor(message) {
|
|
87
|
+
super(TurbineErrorCode.VALIDATION, message);
|
|
88
|
+
this.name = 'ValidationError';
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/** Thrown when a database connection fails */
|
|
92
|
+
export class ConnectionError extends TurbineError {
|
|
93
|
+
constructor(message) {
|
|
94
|
+
super(TurbineErrorCode.CONNECTION, message);
|
|
95
|
+
this.name = 'ConnectionError';
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/** Thrown when a relation reference is invalid */
|
|
99
|
+
export class RelationError extends TurbineError {
|
|
100
|
+
constructor(message) {
|
|
101
|
+
super(TurbineErrorCode.RELATION, message);
|
|
102
|
+
this.name = 'RelationError';
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/** Thrown when a migration operation fails */
|
|
106
|
+
export class MigrationError extends TurbineError {
|
|
107
|
+
constructor(message) {
|
|
108
|
+
super(TurbineErrorCode.MIGRATION, message);
|
|
109
|
+
this.name = 'MigrationError';
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/** Thrown when circular relation nesting is detected */
|
|
113
|
+
export class CircularRelationError extends TurbineError {
|
|
114
|
+
path;
|
|
115
|
+
constructor(path) {
|
|
116
|
+
super(TurbineErrorCode.CIRCULAR_RELATION, `[turbine] Circular or too-deep relation nesting detected: ${path.join(' → ')}. Maximum nesting depth is 10.`);
|
|
117
|
+
this.name = 'CircularRelationError';
|
|
118
|
+
this.path = path;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// ---------------------------------------------------------------------------
|
|
122
|
+
// Database constraint violation errors
|
|
123
|
+
// ---------------------------------------------------------------------------
|
|
124
|
+
/**
|
|
125
|
+
* Extract the `detail` string from a pg-style error stored as `cause`.
|
|
126
|
+
* Returns undefined if the cause is not an object or has no detail.
|
|
127
|
+
*/
|
|
128
|
+
function detailFromCause(cause) {
|
|
129
|
+
if (!cause || typeof cause !== 'object')
|
|
130
|
+
return undefined;
|
|
131
|
+
const d = cause.detail;
|
|
132
|
+
return typeof d === 'string' && d.length > 0 ? d : undefined;
|
|
133
|
+
}
|
|
134
|
+
/** Thrown when a UNIQUE constraint is violated (pg code 23505) */
|
|
135
|
+
export class UniqueConstraintError extends TurbineError {
|
|
136
|
+
constraint;
|
|
137
|
+
columns;
|
|
138
|
+
table;
|
|
139
|
+
constructor(opts = {}) {
|
|
140
|
+
const { constraint, columns, table, cause } = opts;
|
|
141
|
+
let message = opts.message;
|
|
142
|
+
if (!message) {
|
|
143
|
+
const constraintPart = constraint ? ` on ${constraint}` : '';
|
|
144
|
+
const columnsPart = columns && columns.length > 0 ? ` (${columns.join(', ')})` : '';
|
|
145
|
+
message = `[turbine] Unique constraint violation${constraintPart}${columnsPart}`;
|
|
146
|
+
const detail = detailFromCause(cause);
|
|
147
|
+
if (detail)
|
|
148
|
+
message += `: ${detail}`;
|
|
149
|
+
}
|
|
150
|
+
super(TurbineErrorCode.UNIQUE_VIOLATION, message, { cause });
|
|
151
|
+
this.name = 'UniqueConstraintError';
|
|
152
|
+
this.constraint = constraint;
|
|
153
|
+
this.columns = columns;
|
|
154
|
+
this.table = table;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
/** Thrown when a FOREIGN KEY constraint is violated (pg code 23503) */
|
|
158
|
+
export class ForeignKeyError extends TurbineError {
|
|
159
|
+
constraint;
|
|
160
|
+
table;
|
|
161
|
+
constructor(opts = {}) {
|
|
162
|
+
const { constraint, table, cause } = opts;
|
|
163
|
+
let message = opts.message;
|
|
164
|
+
if (!message) {
|
|
165
|
+
const constraintPart = constraint ? ` on ${constraint}` : '';
|
|
166
|
+
message = `[turbine] Foreign key constraint violation${constraintPart}`;
|
|
167
|
+
const detail = detailFromCause(cause);
|
|
168
|
+
if (detail)
|
|
169
|
+
message += `: ${detail}`;
|
|
170
|
+
}
|
|
171
|
+
super(TurbineErrorCode.FOREIGN_KEY_VIOLATION, message, { cause });
|
|
172
|
+
this.name = 'ForeignKeyError';
|
|
173
|
+
this.constraint = constraint;
|
|
174
|
+
this.table = table;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
/** Thrown when a NOT NULL constraint is violated (pg code 23502) */
|
|
178
|
+
export class NotNullViolationError extends TurbineError {
|
|
179
|
+
column;
|
|
180
|
+
table;
|
|
181
|
+
constructor(opts = {}) {
|
|
182
|
+
const { column, table, cause } = opts;
|
|
183
|
+
let message = opts.message;
|
|
184
|
+
if (!message) {
|
|
185
|
+
const columnPart = column ? ` on column "${column}"` : '';
|
|
186
|
+
message = `[turbine] NOT NULL constraint violation${columnPart}`;
|
|
187
|
+
const detail = detailFromCause(cause);
|
|
188
|
+
if (detail)
|
|
189
|
+
message += `: ${detail}`;
|
|
190
|
+
}
|
|
191
|
+
super(TurbineErrorCode.NOT_NULL_VIOLATION, message, { cause });
|
|
192
|
+
this.name = 'NotNullViolationError';
|
|
193
|
+
this.column = column;
|
|
194
|
+
this.table = table;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
/** Thrown when a CHECK constraint is violated (pg code 23514) */
|
|
198
|
+
export class CheckConstraintError extends TurbineError {
|
|
199
|
+
constraint;
|
|
200
|
+
table;
|
|
201
|
+
constructor(opts = {}) {
|
|
202
|
+
const { constraint, table, cause } = opts;
|
|
203
|
+
let message = opts.message;
|
|
204
|
+
if (!message) {
|
|
205
|
+
const constraintPart = constraint ? ` on ${constraint}` : '';
|
|
206
|
+
message = `[turbine] Check constraint violation${constraintPart}`;
|
|
207
|
+
const detail = detailFromCause(cause);
|
|
208
|
+
if (detail)
|
|
209
|
+
message += `: ${detail}`;
|
|
210
|
+
}
|
|
211
|
+
super(TurbineErrorCode.CHECK_VIOLATION, message, { cause });
|
|
212
|
+
this.name = 'CheckConstraintError';
|
|
213
|
+
this.constraint = constraint;
|
|
214
|
+
this.table = table;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Parse column names out of a pg `detail` string like:
|
|
219
|
+
* "Key (email)=(foo@bar) already exists."
|
|
220
|
+
* "Key (col1, col2)=(v1, v2) already exists."
|
|
221
|
+
*/
|
|
222
|
+
function parseColumnsFromDetail(detail) {
|
|
223
|
+
const m = detail.match(/^Key \(([^)]+)\)/);
|
|
224
|
+
if (!m)
|
|
225
|
+
return undefined;
|
|
226
|
+
return m[1].split(',').map((s) => s.trim());
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Translate a pg driver error into a typed Turbine error.
|
|
230
|
+
* If the error doesn't match a known constraint code, returns it unchanged.
|
|
231
|
+
*
|
|
232
|
+
* Maps:
|
|
233
|
+
* 23505 (unique_violation) -> UniqueConstraintError
|
|
234
|
+
* 23503 (foreign_key_violation) -> ForeignKeyError
|
|
235
|
+
* 23502 (not_null_violation) -> NotNullViolationError
|
|
236
|
+
* 23514 (check_violation) -> CheckConstraintError
|
|
237
|
+
*
|
|
238
|
+
* The original pg error is preserved as `.cause` on the wrapped error.
|
|
239
|
+
*/
|
|
240
|
+
export function wrapPgError(err) {
|
|
241
|
+
if (!err || typeof err !== 'object')
|
|
242
|
+
return err;
|
|
243
|
+
const e = err;
|
|
244
|
+
if (!e.code)
|
|
245
|
+
return err;
|
|
246
|
+
switch (e.code) {
|
|
247
|
+
case '23505': {
|
|
248
|
+
const cols = e.detail ? parseColumnsFromDetail(e.detail) : undefined;
|
|
249
|
+
return new UniqueConstraintError({
|
|
250
|
+
constraint: e.constraint,
|
|
251
|
+
columns: cols,
|
|
252
|
+
table: e.table,
|
|
253
|
+
cause: err,
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
case '23503':
|
|
257
|
+
return new ForeignKeyError({
|
|
258
|
+
constraint: e.constraint,
|
|
259
|
+
table: e.table,
|
|
260
|
+
cause: err,
|
|
261
|
+
});
|
|
262
|
+
case '23502':
|
|
263
|
+
return new NotNullViolationError({
|
|
264
|
+
column: e.column,
|
|
265
|
+
table: e.table,
|
|
266
|
+
cause: err,
|
|
267
|
+
});
|
|
268
|
+
case '23514':
|
|
269
|
+
return new CheckConstraintError({
|
|
270
|
+
constraint: e.constraint,
|
|
271
|
+
table: e.table,
|
|
272
|
+
cause: err,
|
|
273
|
+
});
|
|
274
|
+
default:
|
|
275
|
+
return err;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
//# sourceMappingURL=errors.js.map
|
package/dist/generate.d.ts
CHANGED
package/dist/generate.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* turbine-orm — Code generator
|
|
3
3
|
*
|
|
4
4
|
* Takes an IntrospectedSchema and emits TypeScript files:
|
|
5
5
|
* - types.ts — Entity interfaces, Create/Update input types
|
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
*
|
|
9
9
|
* Output goes to the specified directory (default: ./generated/turbine/).
|
|
10
10
|
*/
|
|
11
|
-
import {
|
|
12
|
-
import { join,
|
|
13
|
-
import {
|
|
11
|
+
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
12
|
+
import { join, relative, resolve } from 'node:path';
|
|
13
|
+
import { singularize, snakeToPascal } from './schema.js';
|
|
14
14
|
/** Get the TypeScript type name for a table (singularized PascalCase) */
|
|
15
15
|
function entityName(tableName) {
|
|
16
16
|
return snakeToPascal(singularize(tableName));
|
|
@@ -52,18 +52,16 @@ export function generate(options) {
|
|
|
52
52
|
function generatedFileHeader() {
|
|
53
53
|
return [
|
|
54
54
|
'/**',
|
|
55
|
-
' * Auto-generated by
|
|
55
|
+
' * Auto-generated by turbine-orm — DO NOT EDIT',
|
|
56
56
|
' *',
|
|
57
57
|
` * Generated at: ${new Date().toISOString()}`,
|
|
58
|
-
' * @see https://
|
|
58
|
+
' * @see https://turbineorm.dev',
|
|
59
59
|
' */',
|
|
60
60
|
'',
|
|
61
61
|
];
|
|
62
62
|
}
|
|
63
63
|
function generateTypes(schema) {
|
|
64
|
-
const lines = [
|
|
65
|
-
...generatedFileHeader(),
|
|
66
|
-
];
|
|
64
|
+
const lines = [...generatedFileHeader()];
|
|
67
65
|
// Generate enum types
|
|
68
66
|
for (const [enumName, labels] of Object.entries(schema.enums)) {
|
|
69
67
|
const typeName = snakeToPascal(enumName);
|
|
@@ -113,9 +111,23 @@ function generateTypes(schema) {
|
|
|
113
111
|
}
|
|
114
112
|
lines.push('};');
|
|
115
113
|
lines.push('');
|
|
116
|
-
// ---
|
|
114
|
+
// --- Relations map (for type-safe `with` clauses) ---
|
|
117
115
|
const hasRelations = Object.keys(table.relations).length > 0;
|
|
118
116
|
if (hasRelations) {
|
|
117
|
+
lines.push(`/** Available relations for the \`${table.name}\` table */`);
|
|
118
|
+
lines.push(`export interface ${typeName}Relations {`);
|
|
119
|
+
for (const [relName, rel] of Object.entries(table.relations)) {
|
|
120
|
+
const targetType = entityName(rel.to);
|
|
121
|
+
if (rel.type === 'hasMany') {
|
|
122
|
+
lines.push(` ${relName}: ${targetType}[];`);
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
lines.push(` ${relName}: ${targetType} | null;`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
lines.push('}');
|
|
129
|
+
lines.push('');
|
|
130
|
+
// --- Legacy per-relation interfaces (kept for backward compatibility) ---
|
|
119
131
|
for (const [relName, rel] of Object.entries(table.relations)) {
|
|
120
132
|
const targetType = entityName(rel.to);
|
|
121
133
|
if (rel.type === 'hasMany') {
|
|
@@ -142,7 +154,7 @@ function generateTypes(schema) {
|
|
|
142
154
|
function generateMetadata(schema) {
|
|
143
155
|
const lines = [
|
|
144
156
|
...generatedFileHeader(),
|
|
145
|
-
"import type { SchemaMetadata } from '
|
|
157
|
+
"import type { SchemaMetadata } from 'turbine-orm';",
|
|
146
158
|
'',
|
|
147
159
|
'export const SCHEMA: SchemaMetadata = {',
|
|
148
160
|
' tables: {',
|
|
@@ -215,12 +227,18 @@ function generateIndex(schema) {
|
|
|
215
227
|
const tableEntries = Object.values(schema.tables);
|
|
216
228
|
const lines = [
|
|
217
229
|
...generatedFileHeader(),
|
|
218
|
-
"import { TurbineClient as BaseTurbineClient, QueryInterface } from '
|
|
219
|
-
"import type { TurbineConfig } from '
|
|
230
|
+
"import { TurbineClient as BaseTurbineClient, QueryInterface } from 'turbine-orm';",
|
|
231
|
+
"import type { TurbineConfig } from 'turbine-orm';",
|
|
220
232
|
"import { SCHEMA } from './metadata.js';",
|
|
221
233
|
];
|
|
222
|
-
// Import all entity types
|
|
223
|
-
const typeImports =
|
|
234
|
+
// Import all entity types and relations maps
|
|
235
|
+
const typeImports = [];
|
|
236
|
+
for (const t of tableEntries) {
|
|
237
|
+
typeImports.push(entityName(t.name));
|
|
238
|
+
if (Object.keys(t.relations).length > 0) {
|
|
239
|
+
typeImports.push(`${entityName(t.name)}Relations`);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
224
242
|
lines.push(`import type { ${typeImports.join(', ')} } from './types.js';`);
|
|
225
243
|
lines.push('');
|
|
226
244
|
// Generate the client class with JSDoc
|
|
@@ -246,8 +264,10 @@ function generateIndex(schema) {
|
|
|
246
264
|
for (const table of tableEntries) {
|
|
247
265
|
const typeName = entityName(table.name);
|
|
248
266
|
const accessor = snakeToCamelStr(table.name);
|
|
267
|
+
const hasRelations = Object.keys(table.relations).length > 0;
|
|
268
|
+
const genericArgs = hasRelations ? `${typeName}, ${typeName}Relations` : typeName;
|
|
249
269
|
lines.push(` /** Query interface for the \`${table.name}\` table */`);
|
|
250
|
-
lines.push(` declare readonly ${accessor}: QueryInterface<${
|
|
270
|
+
lines.push(` declare readonly ${accessor}: QueryInterface<${genericArgs}>;`);
|
|
251
271
|
}
|
|
252
272
|
lines.push('');
|
|
253
273
|
lines.push(' constructor(config?: TurbineConfig) {');
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* turbine-orm
|
|
3
3
|
*
|
|
4
4
|
* Turbine TypeScript SDK — type-safe Postgres queries with nested relations
|
|
5
5
|
* and pipeline batching. Feels like Prisma, runs at raw-SQL speed.
|
|
@@ -32,13 +32,15 @@
|
|
|
32
32
|
* await db.disconnect();
|
|
33
33
|
* ```
|
|
34
34
|
*/
|
|
35
|
-
export {
|
|
36
|
-
export {
|
|
35
|
+
export { type Middleware, type MiddlewareNext, type MiddlewareParams, type PgCompatPool, type PgCompatPoolClient, type PgCompatQueryResult, TransactionClient, type TransactionOptions, TurbineClient, type TurbineConfig, } from './client.js';
|
|
36
|
+
export { CheckConstraintError, CircularRelationError, ConnectionError, ForeignKeyError, MigrationError, NotFoundError, NotNullViolationError, RelationError, TimeoutError, TurbineError, TurbineErrorCode, UniqueConstraintError, ValidationError, wrapPgError, } from './errors.js';
|
|
37
|
+
export { type GenerateOptions, generate } from './generate.js';
|
|
38
|
+
export { type IntrospectOptions, introspect } from './introspect.js';
|
|
37
39
|
export { executePipeline, type PipelineResults } from './pipeline.js';
|
|
38
|
-
export type
|
|
39
|
-
export {
|
|
40
|
-
export {
|
|
41
|
-
export {
|
|
42
|
-
export {
|
|
43
|
-
export {
|
|
40
|
+
export { type AggregateArgs, type AggregateResult, type ArrayFilter, type CountArgs, type CreateArgs, type CreateManyArgs, type DeferredQuery, type DeleteArgs, type DeleteManyArgs, type FindManyArgs, type FindManyStreamArgs, type FindUniqueArgs, type GroupByArgs, type JsonFilter, type OrderDirection, QueryInterface, type RelationFilter, type TypedWithClause, type UpdateArgs, type UpdateInput, type UpdateManyArgs, type UpdateOperatorInput, type UpsertArgs, type WithClause, type WithOptions, type WithResult, } from './query.js';
|
|
41
|
+
export type { ColumnMetadata, IndexMetadata, RelationDef, SchemaMetadata, TableMetadata, } from './schema.js';
|
|
42
|
+
export { camelToSnake, isDateType, pgArrayType, pgTypeToTs, singularize, snakeToCamel, snakeToPascal, } from './schema.js';
|
|
43
|
+
export { ColumnBuilder, type ColumnConfig, type ColumnDef, type ColumnType, type ColumnTypeName, column, defineSchema, type SchemaDef, type TableDef, table, } from './schema-builder.js';
|
|
44
|
+
export { type AlterColumnDef, type AlterDef, type DiffResult, type PushResult, schemaDiff, schemaPush, schemaToSQL, schemaToSQLString, } from './schema-sql.js';
|
|
45
|
+
export { type TurbineHttpOptions, turbineHttp } from './serverless.js';
|
|
44
46
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* turbine-orm
|
|
3
3
|
*
|
|
4
4
|
* Turbine TypeScript SDK — type-safe Postgres queries with nested relations
|
|
5
5
|
* and pipeline batching. Feels like Prisma, runs at raw-SQL speed.
|
|
@@ -33,21 +33,25 @@
|
|
|
33
33
|
* ```
|
|
34
34
|
*/
|
|
35
35
|
// Client
|
|
36
|
-
export {
|
|
37
|
-
//
|
|
38
|
-
export {
|
|
36
|
+
export { TransactionClient, TurbineClient, } from './client.js';
|
|
37
|
+
// Error types
|
|
38
|
+
export { CheckConstraintError, CircularRelationError, ConnectionError, ForeignKeyError, MigrationError, NotFoundError, NotNullViolationError, RelationError, TimeoutError, TurbineError, TurbineErrorCode, UniqueConstraintError, ValidationError, wrapPgError, } from './errors.js';
|
|
39
|
+
// Code generation
|
|
40
|
+
export { generate } from './generate.js';
|
|
41
|
+
// Introspection
|
|
42
|
+
export { introspect } from './introspect.js';
|
|
39
43
|
// Pipeline
|
|
40
44
|
export { executePipeline } from './pipeline.js';
|
|
45
|
+
// Query builder
|
|
46
|
+
export { QueryInterface, } from './query.js';
|
|
41
47
|
// Schema utilities
|
|
42
|
-
export {
|
|
43
|
-
// Introspection
|
|
44
|
-
export { introspect } from './introspect.js';
|
|
45
|
-
// Code generation
|
|
46
|
-
export { generate } from './generate.js';
|
|
48
|
+
export { camelToSnake, isDateType, pgArrayType, pgTypeToTs, singularize, snakeToCamel, snakeToPascal, } from './schema.js';
|
|
47
49
|
// Schema builder — define schemas in TypeScript
|
|
48
|
-
export { defineSchema,
|
|
50
|
+
export { ColumnBuilder, column, defineSchema,
|
|
49
51
|
// Legacy compat (deprecated — use object format with defineSchema)
|
|
50
|
-
table,
|
|
52
|
+
table, } from './schema-builder.js';
|
|
51
53
|
// Schema SQL — generate DDL, diff, and push
|
|
52
|
-
export {
|
|
54
|
+
export { schemaDiff, schemaPush, schemaToSQL, schemaToSQLString, } from './schema-sql.js';
|
|
55
|
+
// Serverless / edge factory
|
|
56
|
+
export { turbineHttp } from './serverless.js';
|
|
53
57
|
//# sourceMappingURL=index.js.map
|
package/dist/introspect.d.ts
CHANGED
package/dist/introspect.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* turbine-orm — Schema introspection
|
|
3
3
|
*
|
|
4
4
|
* Connects to a live Postgres database, reads information_schema + pg_catalog,
|
|
5
5
|
* and produces a SchemaMetadata object describing every table, column, relation,
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* This is the foundation of `npx turbine generate`.
|
|
9
9
|
*/
|
|
10
10
|
import pg from 'pg';
|
|
11
|
-
import {
|
|
11
|
+
import { isDateType, pgArrayType, pgTypeToTs, singularize, snakeToCamel, } from './schema.js';
|
|
12
12
|
// ---------------------------------------------------------------------------
|
|
13
13
|
// SQL queries (all parameterized, no interpolation)
|
|
14
14
|
// ---------------------------------------------------------------------------
|
|
@@ -99,7 +99,7 @@ export async function introspect(options) {
|
|
|
99
99
|
});
|
|
100
100
|
try {
|
|
101
101
|
// Run all information_schema queries in parallel
|
|
102
|
-
const [tablesResult, columnsResult, pkResult, fkResult, uniqueResult, indexResult, enumResult
|
|
102
|
+
const [tablesResult, columnsResult, pkResult, fkResult, uniqueResult, indexResult, enumResult] = await Promise.all([
|
|
103
103
|
pool.query(SQL_TABLES, [schema]),
|
|
104
104
|
pool.query(SQL_COLUMNS, [schema]),
|
|
105
105
|
pool.query(SQL_PRIMARY_KEYS, [schema]),
|
|
@@ -172,9 +172,7 @@ export async function introspect(options) {
|
|
|
172
172
|
const isUnique = row.indexdef.includes('UNIQUE');
|
|
173
173
|
// Extract column names from indexdef (e.g. "CREATE INDEX idx ON tbl USING btree (col1, col2)")
|
|
174
174
|
const colMatch = row.indexdef.match(/\((.+)\)/);
|
|
175
|
-
const columns = colMatch
|
|
176
|
-
? colMatch[1].split(',').map((c) => c.trim().replace(/ (ASC|DESC)/i, ''))
|
|
177
|
-
: [];
|
|
175
|
+
const columns = colMatch ? colMatch[1].split(',').map((c) => c.trim().replace(/ (ASC|DESC)/i, '')) : [];
|
|
178
176
|
indexesByTable.get(row.tablename).push({
|
|
179
177
|
name: row.indexname,
|
|
180
178
|
columns,
|
package/dist/pipeline.d.ts
CHANGED