turbine-orm 0.49.0 → 0.50.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 +122 -39
- package/dist/cjs/adapters/cockroachdb.d.ts +39 -0
- package/dist/cjs/adapters/index.d.ts +110 -0
- package/dist/cjs/adapters/yugabytedb.d.ts +51 -0
- package/dist/cjs/cli/config.d.ts +181 -0
- package/dist/cjs/cli/config.js +32 -6
- package/dist/cjs/cli/destructive.d.ts +38 -0
- package/dist/cjs/cli/index.d.ts +359 -0
- package/dist/cjs/cli/index.js +228 -56
- package/dist/cjs/cli/loader.d.ts +61 -0
- package/dist/cjs/cli/mcp.d.ts +42 -0
- package/dist/cjs/cli/migrate.d.ts +356 -0
- package/dist/cjs/cli/migrate.js +131 -40
- package/dist/cjs/cli/observe-ui.d.ts +1 -0
- package/dist/cjs/cli/observe-ui.js +14 -5
- package/dist/cjs/cli/observe.d.ts +25 -0
- package/dist/cjs/cli/observe.js +49 -12
- package/dist/cjs/cli/pii-tags.d.ts +53 -0
- package/dist/cjs/cli/prisma-report.d.ts +33 -0
- package/dist/cjs/cli/prisma-report.js +73 -0
- package/dist/cjs/cli/prisma-resolve.d.ts +106 -0
- package/dist/cjs/cli/prisma-resolve.js +1 -0
- package/dist/cjs/cli/prisma-schema.d.ts +176 -0
- package/dist/cjs/cli/prisma-schema.js +82 -4
- package/dist/cjs/cli/rate-limit.d.ts +32 -0
- package/dist/cjs/cli/rate-limit.js +45 -0
- package/dist/cjs/cli/studio-demo.d.ts +43 -0
- package/dist/cjs/cli/studio-ui.generated.d.ts +1 -0
- package/dist/cjs/cli/studio.d.ts +207 -0
- package/dist/cjs/cli/studio.js +136 -71
- package/dist/cjs/cli/ui.d.ts +73 -0
- package/dist/cjs/cli/ui.js +51 -9
- package/dist/cjs/client.d.ts +837 -0
- package/dist/cjs/client.js +3 -0
- package/dist/cjs/dialect.d.ts +516 -0
- package/dist/cjs/dialect.js +37 -12
- package/dist/cjs/errors.d.ts +370 -0
- package/dist/cjs/generate.d.ts +137 -0
- package/dist/cjs/generate.js +39 -6
- package/dist/cjs/index-advisor.d.ts +153 -0
- package/dist/cjs/index-stats.d.ts +384 -0
- package/dist/cjs/index.d.ts +55 -0
- package/dist/cjs/index.js +7 -2
- package/dist/cjs/introspect.d.ts +269 -0
- package/dist/cjs/mssql.d.ts +232 -0
- package/dist/cjs/mssql.js +6 -0
- package/dist/cjs/mysql.d.ts +173 -0
- package/dist/cjs/mysql.js +16 -0
- package/dist/cjs/nested-write.d.ts +96 -0
- package/dist/cjs/nested-write.js +414 -24
- package/dist/cjs/observe.d.ts +115 -0
- package/dist/cjs/optional-peer-import.d.cts +72 -0
- package/dist/cjs/pipeline-submittable.d.ts +93 -0
- package/dist/cjs/pipeline.d.ts +71 -0
- package/dist/cjs/powdb-introspect.d.ts +84 -0
- package/dist/cjs/powdb.d.ts +931 -0
- package/dist/cjs/powdb.js +106 -21
- package/dist/cjs/powql.d.ts +592 -0
- package/dist/cjs/powql.js +42 -6
- package/dist/cjs/prisma-compat.d.ts +283 -0
- package/dist/cjs/prisma-compat.js +167 -9
- package/dist/cjs/query/aggregates.d.ts +92 -0
- package/dist/cjs/query/aggregates.js +7 -3
- package/dist/cjs/query/batched-loader.d.ts +193 -0
- package/dist/cjs/query/builder.d.ts +849 -0
- package/dist/cjs/query/builder.js +571 -65
- package/dist/cjs/query/compound-unique.d.ts +51 -0
- package/dist/cjs/query/deferred.d.ts +223 -0
- package/dist/cjs/query/filters.d.ts +201 -0
- package/dist/cjs/query/index.d.ts +14 -0
- package/dist/cjs/query/index.js +6 -1
- package/dist/cjs/query/relations.d.ts +609 -0
- package/dist/cjs/query/relations.js +693 -46
- package/dist/cjs/query/types.d.ts +1300 -0
- package/dist/cjs/query/utils.d.ts +209 -0
- package/dist/cjs/query/utils.js +208 -1
- package/dist/cjs/query/warn-registry.d.ts +68 -0
- package/dist/cjs/query/warn-registry.js +9 -0
- package/dist/cjs/query/where-compile.d.ts +139 -0
- package/dist/cjs/query/where.d.ts +548 -0
- package/dist/cjs/query/where.js +58 -22
- package/dist/cjs/query/writes.d.ts +172 -0
- package/dist/cjs/query/writes.js +105 -12
- package/dist/cjs/realtime.d.ts +70 -0
- package/dist/cjs/schema-builder.d.ts +354 -0
- package/dist/cjs/schema-metadata.d.ts +83 -0
- package/dist/cjs/schema-sql.d.ts +217 -0
- package/dist/cjs/schema-sql.js +23 -5
- package/dist/cjs/schema.d.ts +356 -0
- package/dist/cjs/schema.js +125 -0
- package/dist/cjs/seed.d.ts +15 -0
- package/dist/cjs/serverless.d.ts +142 -0
- package/dist/cjs/sqlite.d.ts +143 -0
- package/dist/cjs/sqlite.js +4 -0
- package/dist/cjs/typed-sql.d.ts +102 -0
- package/dist/cli/config.d.ts +18 -4
- package/dist/cli/config.js +31 -6
- package/dist/cli/index.d.ts +123 -0
- package/dist/cli/index.js +223 -58
- package/dist/cli/migrate.d.ts +59 -10
- package/dist/cli/migrate.js +128 -41
- package/dist/cli/observe-ui.d.ts +1 -1
- package/dist/cli/observe-ui.js +14 -5
- package/dist/cli/observe.d.ts +7 -1
- package/dist/cli/observe.js +48 -12
- package/dist/cli/prisma-report.d.ts +14 -0
- package/dist/cli/prisma-report.js +72 -0
- package/dist/cli/prisma-resolve.d.ts +6 -0
- package/dist/cli/prisma-resolve.js +1 -0
- package/dist/cli/prisma-schema.d.ts +62 -2
- package/dist/cli/prisma-schema.js +81 -4
- package/dist/cli/rate-limit.d.ts +32 -0
- package/dist/cli/rate-limit.js +40 -0
- package/dist/cli/studio.d.ts +5 -5
- package/dist/cli/studio.js +135 -70
- package/dist/cli/ui.d.ts +1 -1
- package/dist/cli/ui.js +51 -9
- package/dist/client.d.ts +40 -0
- package/dist/client.js +3 -0
- package/dist/dialect.d.ts +17 -1
- package/dist/dialect.js +37 -12
- package/dist/generate.js +40 -7
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/mssql.js +6 -0
- package/dist/mysql.js +16 -0
- package/dist/nested-write.d.ts +2 -0
- package/dist/nested-write.js +415 -25
- package/dist/powdb.d.ts +4 -2
- package/dist/powdb.js +106 -21
- package/dist/powql.d.ts +5 -0
- package/dist/powql.js +42 -6
- package/dist/prisma-compat.d.ts +2 -0
- package/dist/prisma-compat.js +166 -8
- package/dist/query/aggregates.js +7 -3
- package/dist/query/builder.d.ts +292 -21
- package/dist/query/builder.js +570 -64
- package/dist/query/deferred.d.ts +39 -0
- package/dist/query/index.d.ts +1 -1
- package/dist/query/index.js +1 -1
- package/dist/query/relations.d.ts +173 -5
- package/dist/query/relations.js +688 -47
- package/dist/query/types.d.ts +123 -39
- package/dist/query/utils.d.ts +116 -0
- package/dist/query/utils.js +198 -0
- package/dist/query/warn-registry.d.ts +9 -0
- package/dist/query/warn-registry.js +9 -0
- package/dist/query/where.d.ts +38 -1
- package/dist/query/where.js +58 -23
- package/dist/query/writes.d.ts +42 -1
- package/dist/query/writes.js +104 -13
- package/dist/schema-sql.d.ts +14 -0
- package/dist/schema-sql.js +23 -5
- package/dist/schema.d.ts +38 -0
- package/dist/schema.js +123 -0
- package/dist/sqlite.js +4 -0
- package/package.json +77 -28
|
@@ -0,0 +1,370 @@
|
|
|
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
|
+
readonly DEADLOCK_DETECTED: "TURBINE_E012";
|
|
21
|
+
readonly SERIALIZATION_FAILURE: "TURBINE_E013";
|
|
22
|
+
readonly PIPELINE: "TURBINE_E014";
|
|
23
|
+
readonly OPTIMISTIC_LOCK: "TURBINE_E015";
|
|
24
|
+
readonly EXCLUSION_VIOLATION: "TURBINE_E016";
|
|
25
|
+
readonly UNSUPPORTED_FEATURE: "TURBINE_E017";
|
|
26
|
+
readonly READ_ONLY: "TURBINE_E018";
|
|
27
|
+
};
|
|
28
|
+
export type TurbineErrorCode = (typeof TurbineErrorCode)[keyof typeof TurbineErrorCode];
|
|
29
|
+
/** Base error class for all Turbine errors */
|
|
30
|
+
export declare class TurbineError extends Error {
|
|
31
|
+
readonly code: TurbineErrorCode;
|
|
32
|
+
constructor(code: TurbineErrorCode, message: string, options?: {
|
|
33
|
+
cause?: unknown;
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Controls whether NotFoundError messages include the actual `where` values
|
|
38
|
+
* (`'verbose'`) or only the where-clause keys (`'safe'`, the default).
|
|
39
|
+
*
|
|
40
|
+
* Defaults to `'safe'` to avoid leaking PII into error logs (Sentry, Datadog,
|
|
41
|
+
* etc.). The full `where` object is always available as `err.where` for
|
|
42
|
+
* programmatic access — only the human-readable message is redacted.
|
|
43
|
+
*
|
|
44
|
+
* Set via `setErrorMessageMode('verbose')` or by constructing TurbineClient
|
|
45
|
+
* with `{ errorMessages: 'verbose' }`.
|
|
46
|
+
*/
|
|
47
|
+
export type ErrorMessageMode = 'safe' | 'verbose';
|
|
48
|
+
/**
|
|
49
|
+
* Set the global NotFoundError message mode. Called from the TurbineClient
|
|
50
|
+
* constructor when `TurbineConfig.errorMessages` is provided.
|
|
51
|
+
*
|
|
52
|
+
* - `'safe'` (default): the message includes only the keys of the where
|
|
53
|
+
* clause (e.g. `where: { id, email }`). Values are redacted.
|
|
54
|
+
* - `'verbose'`: the message includes the full JSON-serialized where
|
|
55
|
+
* clause (e.g. `where: {"id":1,"email":"alice@x.com"}`).
|
|
56
|
+
*/
|
|
57
|
+
export declare function setErrorMessageMode(mode: ErrorMessageMode): void;
|
|
58
|
+
/** Returns the current NotFoundError message mode. Exported for tests. */
|
|
59
|
+
export declare function getErrorMessageMode(): ErrorMessageMode;
|
|
60
|
+
/**
|
|
61
|
+
* Render a user-supplied `where` / `connect` target for a "no row found" error
|
|
62
|
+
* message, honoring the global {@link ErrorMessageMode}. In 'safe' mode (the
|
|
63
|
+
* default) only the key names are shown (`keys [email, id]`) so that PII values
|
|
64
|
+
* never leak into logs; in 'verbose' mode the full JSON serialization is used.
|
|
65
|
+
*
|
|
66
|
+
* This mirrors {@link NotFoundError}'s redaction so that every "no row found"
|
|
67
|
+
* message in the library follows one convention, including the nested-write
|
|
68
|
+
* connect/update failures which historically embedded the raw values.
|
|
69
|
+
*/
|
|
70
|
+
export declare function describeTargetForMessage(target: unknown): string;
|
|
71
|
+
/**
|
|
72
|
+
* Thrown when a record is not found (findUniqueOrThrow, findFirstOrThrow,
|
|
73
|
+
* update/delete against a non-matching row, etc.)
|
|
74
|
+
*
|
|
75
|
+
* Supports two call styles for back-compat:
|
|
76
|
+
* - `new NotFoundError()` / `new NotFoundError('custom message')`
|
|
77
|
+
* - `new NotFoundError({ table, where, operation, cause, message })`
|
|
78
|
+
*
|
|
79
|
+
* When called with an options object and no explicit `message`, a Prisma-style
|
|
80
|
+
* message is built automatically. By default, only the where-clause keys are
|
|
81
|
+
* shown to avoid leaking PII into logs:
|
|
82
|
+
* `[turbine] findUniqueOrThrow on "users" found no record matching where: { id }`
|
|
83
|
+
*
|
|
84
|
+
* Set `setErrorMessageMode('verbose')` (or pass `errorMessages: 'verbose'` to
|
|
85
|
+
* the TurbineClient constructor) to include the full where values:
|
|
86
|
+
* `[turbine] findUniqueOrThrow on "users" found no record matching where: {"id":1}`
|
|
87
|
+
*
|
|
88
|
+
* The full `where` object, `table`, and `operation` are always available as
|
|
89
|
+
* structured properties on the error instance regardless of mode.
|
|
90
|
+
*/
|
|
91
|
+
export declare class NotFoundError extends TurbineError {
|
|
92
|
+
readonly table?: string;
|
|
93
|
+
readonly where?: unknown;
|
|
94
|
+
readonly operation?: string;
|
|
95
|
+
constructor(input?: string | {
|
|
96
|
+
table?: string;
|
|
97
|
+
where?: unknown;
|
|
98
|
+
operation?: string;
|
|
99
|
+
cause?: unknown;
|
|
100
|
+
message?: string;
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
/** Thrown when a query or transaction exceeds the configured timeout */
|
|
104
|
+
export declare class TimeoutError extends TurbineError {
|
|
105
|
+
readonly timeoutMs: number;
|
|
106
|
+
/**
|
|
107
|
+
* @param timeoutMs the client-side timeout budget in ms. Pass `0` when the
|
|
108
|
+
* duration is unknown (e.g. a server-side `statement_timeout` cancellation
|
|
109
|
+
* surfaced via `wrapPgError`, where Turbine did not set the deadline).
|
|
110
|
+
* @param context human label for the operation ("Query", "Transaction").
|
|
111
|
+
* @param options optional `message` override and pg `cause` to preserve, used
|
|
112
|
+
* when wrapping a driver error rather than a client-side timer expiry.
|
|
113
|
+
*/
|
|
114
|
+
constructor(timeoutMs: number, context?: string, options?: {
|
|
115
|
+
message?: string;
|
|
116
|
+
cause?: unknown;
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
/** Thrown when query arguments fail validation (unknown column, invalid operator, etc.) */
|
|
120
|
+
export declare class ValidationError extends TurbineError {
|
|
121
|
+
constructor(message: string);
|
|
122
|
+
}
|
|
123
|
+
/** Thrown when a database connection fails */
|
|
124
|
+
export declare class ConnectionError extends TurbineError {
|
|
125
|
+
/**
|
|
126
|
+
* @param message human-readable connection failure description.
|
|
127
|
+
* @param options optional pg/driver `cause` to preserve, used when wrapping a
|
|
128
|
+
* connection-class driver error via `wrapPgError`.
|
|
129
|
+
*/
|
|
130
|
+
constructor(message: string, options?: {
|
|
131
|
+
cause?: unknown;
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
/** Thrown when a relation reference is invalid */
|
|
135
|
+
export declare class RelationError extends TurbineError {
|
|
136
|
+
constructor(message: string);
|
|
137
|
+
}
|
|
138
|
+
/** Thrown when a migration operation fails */
|
|
139
|
+
export declare class MigrationError extends TurbineError {
|
|
140
|
+
constructor(message: string);
|
|
141
|
+
}
|
|
142
|
+
/** Thrown when circular relation nesting is detected */
|
|
143
|
+
export declare class CircularRelationError extends TurbineError {
|
|
144
|
+
readonly path: string[];
|
|
145
|
+
constructor(path: string[]);
|
|
146
|
+
}
|
|
147
|
+
/** Thrown when a UNIQUE constraint is violated (pg code 23505) */
|
|
148
|
+
export declare class UniqueConstraintError extends TurbineError {
|
|
149
|
+
readonly constraint?: string;
|
|
150
|
+
readonly columns?: string[];
|
|
151
|
+
readonly table?: string;
|
|
152
|
+
constructor(opts?: {
|
|
153
|
+
constraint?: string;
|
|
154
|
+
columns?: string[];
|
|
155
|
+
table?: string;
|
|
156
|
+
message?: string;
|
|
157
|
+
cause?: unknown;
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
/** Thrown when a FOREIGN KEY constraint is violated (pg code 23503) */
|
|
161
|
+
export declare class ForeignKeyError extends TurbineError {
|
|
162
|
+
readonly constraint?: string;
|
|
163
|
+
readonly table?: string;
|
|
164
|
+
constructor(opts?: {
|
|
165
|
+
constraint?: string;
|
|
166
|
+
table?: string;
|
|
167
|
+
message?: string;
|
|
168
|
+
cause?: unknown;
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
/** Thrown when a NOT NULL constraint is violated (pg code 23502) */
|
|
172
|
+
export declare class NotNullViolationError extends TurbineError {
|
|
173
|
+
readonly column?: string;
|
|
174
|
+
readonly table?: string;
|
|
175
|
+
constructor(opts?: {
|
|
176
|
+
column?: string;
|
|
177
|
+
table?: string;
|
|
178
|
+
message?: string;
|
|
179
|
+
cause?: unknown;
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Thrown when Postgres detects a deadlock (pg code 40P01).
|
|
184
|
+
*
|
|
185
|
+
* This error is **retryable** — when caught, callers can safely retry the
|
|
186
|
+
* transaction (typically with backoff). Catch it explicitly:
|
|
187
|
+
*
|
|
188
|
+
* ```ts
|
|
189
|
+
* try {
|
|
190
|
+
* await db.$transaction(async (tx) => { ... });
|
|
191
|
+
* } catch (err) {
|
|
192
|
+
* if (err instanceof DeadlockError) {
|
|
193
|
+
* // safe to retry
|
|
194
|
+
* }
|
|
195
|
+
* }
|
|
196
|
+
* ```
|
|
197
|
+
*/
|
|
198
|
+
export declare class DeadlockError extends TurbineError {
|
|
199
|
+
/** Marks this error as safe to retry */
|
|
200
|
+
readonly isRetryable: true;
|
|
201
|
+
readonly constraint?: string;
|
|
202
|
+
constructor(opts?: {
|
|
203
|
+
message?: string;
|
|
204
|
+
constraint?: string;
|
|
205
|
+
cause?: unknown;
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Thrown when a Serializable transaction fails due to a serialization
|
|
210
|
+
* conflict (pg code 40001 — `could not serialize access due to ...`).
|
|
211
|
+
*
|
|
212
|
+
* This error is **retryable** — by Postgres documentation, the recommended
|
|
213
|
+
* response is to re-run the entire transaction. Catch it explicitly:
|
|
214
|
+
*
|
|
215
|
+
* ```ts
|
|
216
|
+
* try {
|
|
217
|
+
* await db.$transaction(async (tx) => { ... }, { isolationLevel: 'Serializable' });
|
|
218
|
+
* } catch (err) {
|
|
219
|
+
* if (err instanceof SerializationFailureError) {
|
|
220
|
+
* // safe to retry the whole transaction
|
|
221
|
+
* }
|
|
222
|
+
* }
|
|
223
|
+
* ```
|
|
224
|
+
*/
|
|
225
|
+
export declare class SerializationFailureError extends TurbineError {
|
|
226
|
+
/** Marks this error as safe to retry */
|
|
227
|
+
readonly isRetryable: true;
|
|
228
|
+
constructor(opts?: {
|
|
229
|
+
message?: string;
|
|
230
|
+
cause?: unknown;
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
/** Thrown when a CHECK constraint is violated (pg code 23514) */
|
|
234
|
+
export declare class CheckConstraintError extends TurbineError {
|
|
235
|
+
readonly constraint?: string;
|
|
236
|
+
readonly table?: string;
|
|
237
|
+
constructor(opts?: {
|
|
238
|
+
constraint?: string;
|
|
239
|
+
table?: string;
|
|
240
|
+
message?: string;
|
|
241
|
+
cause?: unknown;
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
export declare class ExclusionConstraintError extends TurbineError {
|
|
245
|
+
readonly constraint?: string;
|
|
246
|
+
readonly table?: string;
|
|
247
|
+
constructor(opts?: {
|
|
248
|
+
constraint?: string;
|
|
249
|
+
table?: string;
|
|
250
|
+
message?: string;
|
|
251
|
+
cause?: unknown;
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
/** Result slot for a single query in a non-transactional pipeline */
|
|
255
|
+
export type PipelineResultSlot = {
|
|
256
|
+
status: 'ok';
|
|
257
|
+
value: unknown;
|
|
258
|
+
} | {
|
|
259
|
+
status: 'error';
|
|
260
|
+
error: Error;
|
|
261
|
+
};
|
|
262
|
+
/**
|
|
263
|
+
* Thrown when a non-transactional pipeline has partial failures.
|
|
264
|
+
*
|
|
265
|
+
* In non-transactional mode (`{ transactional: false }`), each query executes
|
|
266
|
+
* independently. If one or more queries fail, the pipeline rejects with a
|
|
267
|
+
* `PipelineError` that carries per-query results so callers can inspect which
|
|
268
|
+
* succeeded and which failed.
|
|
269
|
+
*
|
|
270
|
+
* ```ts
|
|
271
|
+
* try {
|
|
272
|
+
* await db.pipeline([q1, q2, q3], { transactional: false });
|
|
273
|
+
* } catch (err) {
|
|
274
|
+
* if (err instanceof PipelineError) {
|
|
275
|
+
* for (const slot of err.results) {
|
|
276
|
+
* if (slot.status === 'error') console.error(slot.error);
|
|
277
|
+
* }
|
|
278
|
+
* }
|
|
279
|
+
* }
|
|
280
|
+
* ```
|
|
281
|
+
*/
|
|
282
|
+
export declare class PipelineError extends TurbineError {
|
|
283
|
+
/** Per-query results: each slot is either `{status:'ok', value}` or `{status:'error', error}` */
|
|
284
|
+
readonly results: PipelineResultSlot[];
|
|
285
|
+
/** Zero-based index of the first query that failed */
|
|
286
|
+
readonly failedIndex?: number;
|
|
287
|
+
/** Tag of the first query that failed (from DeferredQuery.tag) */
|
|
288
|
+
readonly failedTag?: string;
|
|
289
|
+
constructor(opts: {
|
|
290
|
+
message?: string;
|
|
291
|
+
results: PipelineResultSlot[];
|
|
292
|
+
failedIndex?: number;
|
|
293
|
+
failedTag?: string;
|
|
294
|
+
cause?: unknown;
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
export declare class OptimisticLockError extends TurbineError {
|
|
298
|
+
readonly table: string;
|
|
299
|
+
readonly versionField: string;
|
|
300
|
+
readonly expectedVersion: unknown;
|
|
301
|
+
constructor(opts: {
|
|
302
|
+
table: string;
|
|
303
|
+
versionField: string;
|
|
304
|
+
expectedVersion: unknown;
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Thrown when a Postgres-only feature (pgvector distance ops, LISTEN/NOTIFY
|
|
309
|
+
* realtime, RLS session GUCs, advisory-lock migration locking, ...) is invoked
|
|
310
|
+
* on a dialect/engine whose capability flag reports it unsupported. Surfaces a
|
|
311
|
+
* clear `unsupported on <engine>` message instead of generating broken SQL.
|
|
312
|
+
*/
|
|
313
|
+
export declare class UnsupportedFeatureError extends TurbineError {
|
|
314
|
+
readonly feature: string;
|
|
315
|
+
readonly dialect: string;
|
|
316
|
+
constructor(feature: string, dialect: string, hint?: string);
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Thrown when a write or DDL statement is refused because the target is
|
|
320
|
+
* read-only. Two shapes reach here, both on PowDB:
|
|
321
|
+
* - an embedded database opened read-only for snapshot serving refuses a write
|
|
322
|
+
* with `readonly mode: statement requires a writer …`;
|
|
323
|
+
* - a networked read-only role refuses a write with `permission denied: role
|
|
324
|
+
* '<role>' cannot execute write statements` (translated by `wrapPowdbError`).
|
|
325
|
+
* It is also raised locally, before the wire, when a write is issued on a pool
|
|
326
|
+
* the caller marked read-only (fail-fast). The message carries the engine text
|
|
327
|
+
* plus a hint to route writes to a writable primary.
|
|
328
|
+
*
|
|
329
|
+
* NOT retryable: the same write against the same read-only target fails
|
|
330
|
+
* identically; route it to a writable primary instead.
|
|
331
|
+
*/
|
|
332
|
+
export declare class ReadOnlyError extends TurbineError {
|
|
333
|
+
/**
|
|
334
|
+
* Why the write was refused. `'snapshot'`: the database itself is read-only
|
|
335
|
+
* (snapshot serving, an embedded `readonly: true` open, or the client-level
|
|
336
|
+
* fail-fast flag), so NOTHING can write here and writes must route to the
|
|
337
|
+
* primary. `'rbac'`: the database is writable but THIS connection's role may
|
|
338
|
+
* not write (per-connection permission), so re-authenticating may suffice.
|
|
339
|
+
*/
|
|
340
|
+
readonly reason: 'snapshot' | 'rbac';
|
|
341
|
+
/**
|
|
342
|
+
* @param detail human-readable description of the refused write (the engine
|
|
343
|
+
* message, or a local fail-fast description). A "route writes to a writable
|
|
344
|
+
* primary" hint is always appended.
|
|
345
|
+
* @param options optional driver `cause` to preserve when wrapping a refusal,
|
|
346
|
+
* and the refusal `reason` (default `'snapshot'`).
|
|
347
|
+
*/
|
|
348
|
+
constructor(detail: string, options?: {
|
|
349
|
+
cause?: unknown;
|
|
350
|
+
reason?: 'snapshot' | 'rbac';
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* Translate a pg driver error into a typed Turbine error.
|
|
355
|
+
* If the error doesn't match a known constraint code, returns it unchanged.
|
|
356
|
+
*
|
|
357
|
+
* Maps:
|
|
358
|
+
* 23505 (unique_violation) -> UniqueConstraintError
|
|
359
|
+
* 23503 (foreign_key_violation) -> ForeignKeyError
|
|
360
|
+
* 23502 (not_null_violation) -> NotNullViolationError
|
|
361
|
+
* 23514 (check_violation) -> CheckConstraintError
|
|
362
|
+
* 23P01 (exclusion_violation) -> ExclusionConstraintError
|
|
363
|
+
* 40P01 (deadlock_detected) -> DeadlockError (retryable)
|
|
364
|
+
* 40001 (serialization_failure) -> SerializationFailureError (retryable)
|
|
365
|
+
* 57014 (query_canceled) -> TimeoutError (server-side statement_timeout)
|
|
366
|
+
* connection-class codes -> ConnectionError (see CONNECTION_ERROR_CODES)
|
|
367
|
+
*
|
|
368
|
+
* The original pg error is preserved as `.cause` on the wrapped error.
|
|
369
|
+
*/
|
|
370
|
+
export declare function wrapPgError(err: unknown): unknown;
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* turbine-orm — Code generator
|
|
3
|
+
*
|
|
4
|
+
* Takes an IntrospectedSchema and emits TypeScript files:
|
|
5
|
+
* - types.ts — Entity interfaces, Create/Update input types
|
|
6
|
+
* - metadata.ts — Runtime schema metadata (column maps, relations, etc.)
|
|
7
|
+
* - index.ts — Configured TurbineClient with typed table accessors
|
|
8
|
+
*
|
|
9
|
+
* Output goes to the specified directory (default: ./generated/turbine/).
|
|
10
|
+
*/
|
|
11
|
+
import { type PrismaCompatMap, type SchemaMetadata } from './schema.js';
|
|
12
|
+
export interface GenerateOptions {
|
|
13
|
+
/** The introspected schema to generate from */
|
|
14
|
+
schema: SchemaMetadata;
|
|
15
|
+
/** Output directory (default: './generated/turbine') */
|
|
16
|
+
outDir?: string;
|
|
17
|
+
/** Redact connection string from generated comments */
|
|
18
|
+
connectionString?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Also emit `zod.ts` with per-table `XSchema` / `XCreateSchema` /
|
|
21
|
+
* `XUpdateSchema` Zod validators (H1). The file imports the user-side `zod`
|
|
22
|
+
* package — it is never imported by Turbine's runtime, so Zod stays out of the
|
|
23
|
+
* library's dependency graph. Default: `false`.
|
|
24
|
+
*/
|
|
25
|
+
zod?: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Omit the `Generated at: <ISO timestamp>` line from every generated file
|
|
28
|
+
* header (T-8b — reproducible codegen). With this set, byte-identical
|
|
29
|
+
* schemas regenerate to byte-identical output, so regens produce empty
|
|
30
|
+
* diffs. Default: `false` (timestamp included, unchanged behavior).
|
|
31
|
+
*/
|
|
32
|
+
noTimestamp?: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Which extension the generated `index.ts` uses on its sibling import
|
|
35
|
+
* specifiers (`./types`, `./metadata`). F3.
|
|
36
|
+
*
|
|
37
|
+
* - `'js'`: always `./types.js` (required by NodeNext `tsc` and by
|
|
38
|
+
* tsc-compiled ESM run on Node; the pre-0.41 behavior).
|
|
39
|
+
* - `'none'`: always `./types` (correct for bundlers / `moduleResolution
|
|
40
|
+
* bundler | node10`: webpack, Next.js/SWC, Vite/esbuild).
|
|
41
|
+
* - `'auto'` (default): walk up from `outDir` to the nearest `tsconfig.json`
|
|
42
|
+
* (extends chains are NOT followed) and emit `'.js'` when its `module` /
|
|
43
|
+
* `moduleResolution` is `node16`/`nodenext`, `''` when both are present and
|
|
44
|
+
* neither is, and fall back to `'.js'` when the tsconfig is missing,
|
|
45
|
+
* unparseable, or ambiguous (so NodeNext consumers can never regress).
|
|
46
|
+
*/
|
|
47
|
+
importExtension?: 'js' | 'none' | 'auto';
|
|
48
|
+
/**
|
|
49
|
+
* Rewrite generated column FIELD names to the raw database column names
|
|
50
|
+
* (snake_case) instead of camelCase (F4). Opt-in; when unset the output is
|
|
51
|
+
* byte-identical to before. Implemented as the pure generate-time
|
|
52
|
+
* {@link withDbFieldNames} transform (identity `columnMap`/`reverseColumnMap`,
|
|
53
|
+
* zero runtime changes). Relation names, table accessors, and entity type
|
|
54
|
+
* names are unaffected.
|
|
55
|
+
*/
|
|
56
|
+
keepColumnNames?: boolean;
|
|
57
|
+
}
|
|
58
|
+
/** Per-file generator options (subset of {@link GenerateOptions} the emitters need). */
|
|
59
|
+
export interface GenerateFileOptions {
|
|
60
|
+
/** Omit the `Generated at:` header line for reproducible output. */
|
|
61
|
+
noTimestamp?: boolean;
|
|
62
|
+
/**
|
|
63
|
+
* Resolved sibling-import extension for `generateIndex` (`'.js'` or `''`).
|
|
64
|
+
* Defaults to `'.js'` when unset so direct callers stay byte-stable.
|
|
65
|
+
*/
|
|
66
|
+
importExt?: string;
|
|
67
|
+
/** Human-readable resolved import mode, recorded as a comment in `index.ts`. */
|
|
68
|
+
importMode?: string;
|
|
69
|
+
}
|
|
70
|
+
export declare function generate(options: GenerateOptions): {
|
|
71
|
+
outDir: string;
|
|
72
|
+
files: string[];
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* Resolve the requested {@link GenerateOptions.importExtension} to the concrete
|
|
76
|
+
* extension string used on `index.ts`'s sibling imports plus a human-readable
|
|
77
|
+
* mode label for the generated comment.
|
|
78
|
+
*
|
|
79
|
+
* - `'js'` → `.js`
|
|
80
|
+
* - `'none'` → `''`
|
|
81
|
+
* - `'auto'` → tsconfig-driven ({@link detectTsconfigExtension}); falls back
|
|
82
|
+
* to `.js` (the pre-0.41 default) when detection is uncertain, so NodeNext
|
|
83
|
+
* consumers can never regress.
|
|
84
|
+
*/
|
|
85
|
+
export declare function resolveImportExtension(outDir: string, requested: 'js' | 'none' | 'auto'): {
|
|
86
|
+
ext: string;
|
|
87
|
+
mode: string;
|
|
88
|
+
};
|
|
89
|
+
/**
|
|
90
|
+
* Walk up from `startDir` to the nearest `tsconfig.json` and classify its module
|
|
91
|
+
* resolution. Does NOT follow `extends` chains (a monorepo whose base config
|
|
92
|
+
* sets `moduleResolution` needs the explicit flag). Returns:
|
|
93
|
+
* - `'js'`: `module`/`moduleResolution` is `node16`/`nodenext`;
|
|
94
|
+
* - `'none'`: both fields present and neither is node16/nodenext (bundler,
|
|
95
|
+
* node, node10, classic, esnext, commonjs, preserve);
|
|
96
|
+
* - `null`: no tsconfig found, unparseable, or the fields are absent
|
|
97
|
+
* (possibly hidden behind `extends`) → caller falls back to `.js`.
|
|
98
|
+
*/
|
|
99
|
+
export declare function detectTsconfigExtension(startDir: string): 'js' | 'none' | null;
|
|
100
|
+
/**
|
|
101
|
+
* Classify a tsconfig's `compilerOptions.module` / `moduleResolution` (see
|
|
102
|
+
* {@link detectTsconfigExtension}). Tolerant of `//` and block comments and
|
|
103
|
+
* trailing commas (real-world tsconfigs use JSONC).
|
|
104
|
+
*/
|
|
105
|
+
export declare function classifyTsconfig(text: string): 'js' | 'none' | null;
|
|
106
|
+
/**
|
|
107
|
+
* Strip `//` line comments and block comments from JSONC text, leaving anything
|
|
108
|
+
* inside a double-quoted string untouched. Trailing commas are then removed so
|
|
109
|
+
* `JSON.parse` accepts the result.
|
|
110
|
+
*/
|
|
111
|
+
export declare function stripJsonComments(text: string): string;
|
|
112
|
+
/**
|
|
113
|
+
* Generate the contents of `types.ts` (entity interfaces, *Create / *Update,
|
|
114
|
+
* and *Relations brand-field interfaces). Exported so tests can pin the
|
|
115
|
+
* generator output without writing files to disk.
|
|
116
|
+
*/
|
|
117
|
+
export declare function generateTypes(schema: SchemaMetadata, options?: GenerateFileOptions): string;
|
|
118
|
+
/**
|
|
119
|
+
* Generate the contents of `zod.ts`. Emits, per table, `XSchema` (the full
|
|
120
|
+
* row), `XCreateSchema` (PK/defaulted/nullable columns optional, STORED
|
|
121
|
+
* generated columns omitted), and `XUpdateSchema` (PK + STORED generated
|
|
122
|
+
* columns omitted, every remaining column optional). Exported so tests can pin
|
|
123
|
+
* the output without writing files.
|
|
124
|
+
*/
|
|
125
|
+
export declare function generateZod(schema: SchemaMetadata, options?: GenerateFileOptions): string;
|
|
126
|
+
export declare function generateMetadata(schema: SchemaMetadata, options?: GenerateFileOptions): string;
|
|
127
|
+
export declare function generateIndex(schema: SchemaMetadata, options?: GenerateFileOptions): string;
|
|
128
|
+
/**
|
|
129
|
+
* Serialize a resolved {@link PrismaCompatMap} into a `prisma-map.ts` module
|
|
130
|
+
* that exports `export const PRISMA_MAP: PrismaCompatMap = {...}`. Written next
|
|
131
|
+
* to the generated client by `turbine migrate-from-prisma`; consumed later by
|
|
132
|
+
* the phase-2 `turbine-orm/prisma-compat` runtime adapter.
|
|
133
|
+
*
|
|
134
|
+
* Deterministic: uses the same reproducible header as the other emitters, so a
|
|
135
|
+
* stable input regenerates byte-identical output under `noTimestamp`.
|
|
136
|
+
*/
|
|
137
|
+
export declare function generatePrismaMap(map: PrismaCompatMap, options?: GenerateFileOptions): string;
|
package/dist/cjs/generate.js
CHANGED
|
@@ -45,6 +45,30 @@ function columnTsType(col, enums) {
|
|
|
45
45
|
}
|
|
46
46
|
return col.tsType;
|
|
47
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* Resolve the TypeScript type a column accepts on the WRITE path (the `*Create`
|
|
50
|
+
* / `*Update` input types), which is wider than the row type for time-of-day
|
|
51
|
+
* columns.
|
|
52
|
+
*
|
|
53
|
+
* A `time` / `timetz` column reads back as a `string` (`'09:00:00'`), so the
|
|
54
|
+
* row type stays `string`. On write the runtime also accepts a JS `Date` and
|
|
55
|
+
* narrows it to its UTC time of day (see `coerceWriteValue` in
|
|
56
|
+
* query/writes.ts), matching Prisma, which types a `DateTime @db.Time(6)`
|
|
57
|
+
* field as a `Date`. Consumers porting from Prisma pass Dates, so the input
|
|
58
|
+
* type has to admit them. Every other column is unchanged.
|
|
59
|
+
*/
|
|
60
|
+
function writeColumnTsType(col, enums) {
|
|
61
|
+
const tsType = columnTsType(col, enums);
|
|
62
|
+
const dt = col.dialectType ?? col.pgType;
|
|
63
|
+
const isArray = col.isArray || dt.startsWith('_');
|
|
64
|
+
const base = isArray && dt.startsWith('_') ? dt.slice(1) : dt;
|
|
65
|
+
if (!(0, schema_js_1.timeOfDayKind)(base))
|
|
66
|
+
return tsType;
|
|
67
|
+
// A `time[]` column accepts a Date per element (the bind rewrite runs
|
|
68
|
+
// element-wise, see coerceTemporalValue in query/utils.ts).
|
|
69
|
+
const widened = isArray ? '(string | Date)[]' : 'string | Date';
|
|
70
|
+
return col.nullable ? `${widened} | null` : widened;
|
|
71
|
+
}
|
|
48
72
|
/** Escape a value for embedding in a single-quoted TypeScript string literal */
|
|
49
73
|
function escSQ(value) {
|
|
50
74
|
return value.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
|
|
@@ -345,10 +369,10 @@ function generateTypes(schema, options) {
|
|
|
345
369
|
if (isOptional) {
|
|
346
370
|
const reason = isPk ? 'auto-generated' : col.hasDefault ? 'has default' : 'nullable';
|
|
347
371
|
lines.push(` /** Optional: ${reason} */`);
|
|
348
|
-
lines.push(` ${quoteIfNeeded(col.field)}?: ${
|
|
372
|
+
lines.push(` ${quoteIfNeeded(col.field)}?: ${writeColumnTsType(col, schema.enums)};`);
|
|
349
373
|
}
|
|
350
374
|
else {
|
|
351
|
-
lines.push(` ${quoteIfNeeded(col.field)}: ${
|
|
375
|
+
lines.push(` ${quoteIfNeeded(col.field)}: ${writeColumnTsType(col, schema.enums)};`);
|
|
352
376
|
}
|
|
353
377
|
}
|
|
354
378
|
lines.push('};');
|
|
@@ -360,7 +384,7 @@ function generateTypes(schema, options) {
|
|
|
360
384
|
lines.push(`/** Input type for updating a row in \`${table.name}\` */`);
|
|
361
385
|
lines.push(`export type ${typeName}Update = {`);
|
|
362
386
|
for (const col of nonPkCols) {
|
|
363
|
-
lines.push(` ${quoteIfNeeded(col.field)}?: ${updateFieldType(
|
|
387
|
+
lines.push(` ${quoteIfNeeded(col.field)}?: ${updateFieldType(writeColumnTsType(col, schema.enums))};`);
|
|
364
388
|
}
|
|
365
389
|
lines.push('};');
|
|
366
390
|
lines.push('');
|
|
@@ -578,8 +602,14 @@ function zodScalar(ts) {
|
|
|
578
602
|
* Base Zod expression for a column, resolving enums → `z.enum([...])`, arrays →
|
|
579
603
|
* `.array()`, and vectors → `z.array(z.number())`. Does NOT append
|
|
580
604
|
* `.nullable()` / `.optional()` — callers layer those on per-schema.
|
|
605
|
+
*
|
|
606
|
+
* `forWrite` mirrors {@link writeColumnTsType}: the Create/Update schemas
|
|
607
|
+
* validate WRITE input, where a `time` / `timetz` column also accepts a JS
|
|
608
|
+
* `Date` (narrowed to its UTC time of day at bind time), so a bare
|
|
609
|
+
* `z.string()` there would reject a value the runtime happily writes. The
|
|
610
|
+
* full-row schema keeps `z.string()`, because that is what a read returns.
|
|
581
611
|
*/
|
|
582
|
-
function zodBaseType(col, enums) {
|
|
612
|
+
function zodBaseType(col, enums, forWrite = false) {
|
|
583
613
|
const dt = col.dialectType ?? col.pgType;
|
|
584
614
|
const isArray = col.isArray || dt.startsWith('_');
|
|
585
615
|
const base = isArray && dt.startsWith('_') ? dt.slice(1) : dt;
|
|
@@ -587,6 +617,9 @@ function zodBaseType(col, enums) {
|
|
|
587
617
|
if (Object.hasOwn(enums, base)) {
|
|
588
618
|
expr = `z.enum([${enums[base].map((l) => `'${escSQ(l)}'`).join(', ')}])`;
|
|
589
619
|
}
|
|
620
|
+
else if (forWrite && (0, schema_js_1.timeOfDayKind)(base)) {
|
|
621
|
+
expr = 'z.union([z.string(), z.date()])';
|
|
622
|
+
}
|
|
590
623
|
else {
|
|
591
624
|
expr = zodScalar((0, schema_js_1.pgTypeToTs)(base, false));
|
|
592
625
|
}
|
|
@@ -628,7 +661,7 @@ function generateZod(schema, options) {
|
|
|
628
661
|
if (col.isGeneratedStored)
|
|
629
662
|
continue;
|
|
630
663
|
const isPk = table.primaryKey.includes(col.name);
|
|
631
|
-
let expr = zodBaseType(col, schema.enums);
|
|
664
|
+
let expr = zodBaseType(col, schema.enums, true);
|
|
632
665
|
if (col.nullable)
|
|
633
666
|
expr += '.nullable()';
|
|
634
667
|
if (col.hasDefault || col.nullable || isPk)
|
|
@@ -645,7 +678,7 @@ function generateZod(schema, options) {
|
|
|
645
678
|
continue;
|
|
646
679
|
if (table.primaryKey.includes(col.name))
|
|
647
680
|
continue;
|
|
648
|
-
let expr = zodBaseType(col, schema.enums);
|
|
681
|
+
let expr = zodBaseType(col, schema.enums, true);
|
|
649
682
|
if (col.nullable)
|
|
650
683
|
expr += '.nullable()';
|
|
651
684
|
expr += '.optional()';
|