turbine-orm 0.81.0 → 0.90.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 +26 -25
- package/dist/checkout.d.ts +27 -2
- package/dist/checkout.js +75 -4
- package/dist/cjs/checkout.d.ts +27 -2
- package/dist/cjs/checkout.js +76 -3
- package/dist/cjs/cli/code-first-tags.d.ts +62 -0
- package/dist/cjs/cli/code-first-tags.js +124 -0
- package/dist/cjs/cli/compile-query.js +9 -0
- package/dist/cjs/cli/config.d.ts +35 -6
- package/dist/cjs/cli/config.js +17 -1
- package/dist/cjs/cli/destructive.d.ts +7 -4
- package/dist/cjs/cli/destructive.js +224 -20
- package/dist/cjs/cli/error-catalog.js +36 -0
- package/dist/cjs/cli/error-text.d.ts +15 -0
- package/dist/cjs/cli/error-text.js +40 -0
- package/dist/cjs/cli/index.d.ts +62 -4
- package/dist/cjs/cli/index.js +568 -108
- package/dist/cjs/cli/local-server-guard.d.ts +53 -0
- package/dist/cjs/cli/local-server-guard.js +119 -0
- package/dist/cjs/cli/local-server-output.d.ts +41 -0
- package/dist/cjs/cli/local-server-output.js +103 -0
- package/dist/cjs/cli/mcp.d.ts +8 -0
- package/dist/cjs/cli/mcp.js +121 -32
- package/dist/cjs/cli/migrate.d.ts +113 -5
- package/dist/cjs/cli/migrate.js +270 -21
- package/dist/cjs/cli/observe.d.ts +6 -1
- package/dist/cjs/cli/observe.js +85 -55
- package/dist/cjs/cli/pii-predicate-guard.d.ts +43 -23
- package/dist/cjs/cli/pii-predicate-guard.js +98 -37
- package/dist/cjs/cli/prisma-resolve.js +17 -13
- package/dist/cjs/cli/prisma-schema.js +12 -4
- package/dist/cjs/cli/studio-ui.generated.js +1 -1
- package/dist/cjs/cli/studio.d.ts +14 -2
- package/dist/cjs/cli/studio.js +244 -69
- package/dist/cjs/cli/ui.d.ts +1 -1
- package/dist/cjs/client.d.ts +558 -85
- package/dist/cjs/client.js +1270 -395
- package/dist/cjs/connection-guard.d.ts +13 -0
- package/dist/cjs/connection-guard.js +28 -0
- package/dist/cjs/connection-url.d.ts +51 -16
- package/dist/cjs/connection-url.js +67 -34
- package/dist/cjs/diagnostics.d.ts +81 -0
- package/dist/cjs/diagnostics.js +147 -0
- package/dist/cjs/dialect.d.ts +449 -20
- package/dist/cjs/dialect.js +117 -76
- package/dist/cjs/engine-config.d.ts +7 -6
- package/dist/cjs/errors.d.ts +126 -6
- package/dist/cjs/errors.js +256 -13
- package/dist/cjs/generate.d.ts +57 -7
- package/dist/cjs/generate.js +635 -279
- package/dist/cjs/global-filters-config.d.ts +59 -0
- package/dist/cjs/global-filters-config.js +367 -0
- package/dist/cjs/index.d.ts +9 -4
- package/dist/cjs/index.js +10 -2
- package/dist/cjs/introspect.d.ts +31 -6
- package/dist/cjs/introspect.js +108 -50
- package/dist/cjs/log-codes.d.ts +12 -0
- package/dist/cjs/log-codes.js +77 -0
- package/dist/cjs/logger.d.ts +87 -0
- package/dist/cjs/logger.js +98 -0
- package/dist/cjs/mssql.d.ts +8 -5
- package/dist/cjs/mssql.js +352 -65
- package/dist/cjs/mysql.d.ts +3 -5
- package/dist/cjs/mysql.js +420 -70
- package/dist/cjs/nested-write.d.ts +3 -0
- package/dist/cjs/nested-write.js +179 -114
- package/dist/cjs/pipeline-submittable.d.ts +12 -0
- package/dist/cjs/pipeline-submittable.js +118 -44
- package/dist/cjs/pipeline.js +21 -26
- package/dist/cjs/powdb-introspect.d.ts +5 -2
- package/dist/cjs/powdb-introspect.js +20 -15
- package/dist/cjs/powdb-shared.d.ts +36 -5
- package/dist/cjs/powdb-shared.js +67 -8
- package/dist/cjs/powdb.d.ts +4 -0
- package/dist/cjs/powdb.js +183 -51
- package/dist/cjs/powql.d.ts +394 -51
- package/dist/cjs/powql.js +1829 -284
- package/dist/cjs/prisma-compat.d.ts +23 -15
- package/dist/cjs/prisma-compat.js +507 -372
- package/dist/cjs/query/aggregates.d.ts +17 -1
- package/dist/cjs/query/aggregates.js +261 -29
- package/dist/cjs/query/batched-loader.d.ts +25 -6
- package/dist/cjs/query/batched-loader.js +103 -20
- package/dist/cjs/query/builder.d.ts +325 -184
- package/dist/cjs/query/builder.js +1452 -490
- package/dist/cjs/query/column-ref.d.ts +61 -0
- package/dist/cjs/query/column-ref.js +73 -0
- package/dist/cjs/query/compound-unique.d.ts +21 -0
- package/dist/cjs/query/compound-unique.js +98 -18
- package/dist/cjs/query/deferred.d.ts +53 -16
- package/dist/cjs/query/expr.d.ts +217 -0
- package/dist/cjs/query/expr.js +859 -0
- package/dist/cjs/query/filters.d.ts +155 -53
- package/dist/cjs/query/filters.js +613 -76
- package/dist/cjs/query/index.d.ts +2 -0
- package/dist/cjs/query/index.js +7 -1
- package/dist/cjs/query/json-value.d.ts +43 -0
- package/dist/cjs/query/json-value.js +59 -0
- package/dist/cjs/query/normalize-args.d.ts +87 -0
- package/dist/cjs/query/normalize-args.js +527 -0
- package/dist/cjs/query/option-surface.d.ts +16 -4
- package/dist/cjs/query/option-surface.js +57 -14
- package/dist/cjs/query/relation-names.d.ts +14 -0
- package/dist/cjs/query/relation-names.js +77 -18
- package/dist/cjs/query/relations.d.ts +30 -3
- package/dist/cjs/query/relations.js +219 -63
- package/dist/cjs/query/row-lock.d.ts +49 -0
- package/dist/cjs/query/row-lock.js +138 -0
- package/dist/cjs/query/sql-comment.d.ts +161 -0
- package/dist/cjs/query/sql-comment.js +381 -0
- package/dist/cjs/query/stream-idle.d.ts +132 -0
- package/dist/cjs/query/stream-idle.js +212 -0
- package/dist/cjs/query/table-client.d.ts +101 -0
- package/dist/cjs/query/table-client.js +74 -0
- package/dist/cjs/query/types.d.ts +677 -298
- package/dist/cjs/query/utils.d.ts +105 -6
- package/dist/cjs/query/utils.js +426 -19
- package/dist/cjs/query/warn-registry.d.ts +18 -0
- package/dist/cjs/query/warn-registry.js +18 -0
- package/dist/cjs/query/where-compile.d.ts +138 -14
- package/dist/cjs/query/where-compile.js +513 -29
- package/dist/cjs/query/where.d.ts +159 -59
- package/dist/cjs/query/where.js +716 -246
- package/dist/cjs/query/writes.d.ts +112 -50
- package/dist/cjs/query/writes.js +1498 -200
- package/dist/cjs/query-events.d.ts +4 -2
- package/dist/cjs/query-events.js +3 -1
- package/dist/cjs/realtime.d.ts +7 -3
- package/dist/cjs/realtime.js +13 -2
- package/dist/cjs/schema-builder.d.ts +55 -4
- package/dist/cjs/schema-builder.js +382 -18
- package/dist/cjs/schema-metadata.d.ts +21 -1
- package/dist/cjs/schema-metadata.js +29 -7
- package/dist/cjs/schema-sql.d.ts +75 -5
- package/dist/cjs/schema-sql.js +497 -45
- package/dist/cjs/schema.d.ts +26 -5
- package/dist/cjs/schema.js +110 -13
- package/dist/cjs/sql-placeholders.d.ts +56 -0
- package/dist/cjs/sql-placeholders.js +204 -0
- package/dist/cjs/sqlite.d.ts +75 -20
- package/dist/cjs/sqlite.js +593 -95
- package/dist/cjs/supplied-pool-types.d.ts +48 -0
- package/dist/cjs/supplied-pool-types.js +256 -0
- package/dist/cjs/transaction-control.d.ts +51 -0
- package/dist/cjs/transaction-control.js +316 -0
- package/dist/cjs/transaction-state.d.ts +342 -0
- package/dist/cjs/transaction-state.js +707 -0
- package/dist/cjs/typed-sql.d.ts +37 -3
- package/dist/cjs/typed-sql.js +102 -8
- package/dist/cli/code-first-tags.d.ts +62 -0
- package/dist/cli/code-first-tags.js +120 -0
- package/dist/cli/compile-query.js +9 -0
- package/dist/cli/config.d.ts +35 -6
- package/dist/cli/config.js +16 -1
- package/dist/cli/destructive.d.ts +7 -4
- package/dist/cli/destructive.js +224 -20
- package/dist/cli/error-catalog.js +36 -0
- package/dist/cli/error-text.d.ts +15 -0
- package/dist/cli/error-text.js +37 -0
- package/dist/cli/index.d.ts +62 -4
- package/dist/cli/index.js +569 -112
- package/dist/cli/local-server-guard.d.ts +53 -0
- package/dist/cli/local-server-guard.js +113 -0
- package/dist/cli/local-server-output.d.ts +41 -0
- package/dist/cli/local-server-output.js +98 -0
- package/dist/cli/mcp.d.ts +8 -0
- package/dist/cli/mcp.js +121 -34
- package/dist/cli/migrate.d.ts +113 -5
- package/dist/cli/migrate.js +261 -21
- package/dist/cli/observe.d.ts +6 -1
- package/dist/cli/observe.js +84 -54
- package/dist/cli/pii-predicate-guard.d.ts +43 -23
- package/dist/cli/pii-predicate-guard.js +100 -35
- package/dist/cli/prisma-resolve.js +15 -11
- package/dist/cli/prisma-schema.js +12 -4
- package/dist/cli/studio-ui.generated.js +1 -1
- package/dist/cli/studio.d.ts +14 -2
- package/dist/cli/studio.js +246 -71
- package/dist/cli/ui.d.ts +1 -1
- package/dist/client.d.ts +558 -85
- package/dist/client.js +1275 -400
- package/dist/connection-guard.d.ts +13 -0
- package/dist/connection-guard.js +27 -0
- package/dist/connection-url.d.ts +51 -16
- package/dist/connection-url.js +63 -34
- package/dist/diagnostics.d.ts +81 -0
- package/dist/diagnostics.js +144 -0
- package/dist/dialect.d.ts +449 -20
- package/dist/dialect.js +115 -43
- package/dist/engine-config.d.ts +7 -6
- package/dist/errors.d.ts +126 -6
- package/dist/errors.js +252 -12
- package/dist/generate.d.ts +57 -7
- package/dist/generate.js +635 -281
- package/dist/global-filters-config.d.ts +59 -0
- package/dist/global-filters-config.js +361 -0
- package/dist/index.d.ts +9 -4
- package/dist/index.js +4 -2
- package/dist/introspect.d.ts +31 -6
- package/dist/introspect.js +106 -50
- package/dist/log-codes.d.ts +12 -0
- package/dist/log-codes.js +74 -0
- package/dist/logger.d.ts +87 -0
- package/dist/logger.js +93 -0
- package/dist/mssql.d.ts +8 -5
- package/dist/mssql.js +354 -67
- package/dist/mysql.d.ts +3 -5
- package/dist/mysql.js +423 -73
- package/dist/nested-write.d.ts +3 -0
- package/dist/nested-write.js +177 -112
- package/dist/pipeline-submittable.d.ts +12 -0
- package/dist/pipeline-submittable.js +117 -44
- package/dist/pipeline.js +21 -26
- package/dist/powdb-introspect.d.ts +5 -2
- package/dist/powdb-introspect.js +21 -16
- package/dist/powdb-shared.d.ts +36 -5
- package/dist/powdb-shared.js +65 -8
- package/dist/powdb.d.ts +4 -0
- package/dist/powdb.js +183 -51
- package/dist/powql.d.ts +394 -51
- package/dist/powql.js +1836 -291
- package/dist/prisma-compat.d.ts +23 -15
- package/dist/prisma-compat.js +508 -373
- package/dist/query/aggregates.d.ts +17 -1
- package/dist/query/aggregates.js +263 -32
- package/dist/query/batched-loader.d.ts +25 -6
- package/dist/query/batched-loader.js +101 -21
- package/dist/query/builder.d.ts +325 -184
- package/dist/query/builder.js +1458 -495
- package/dist/query/column-ref.d.ts +61 -0
- package/dist/query/column-ref.js +66 -0
- package/dist/query/compound-unique.d.ts +21 -0
- package/dist/query/compound-unique.js +97 -19
- package/dist/query/deferred.d.ts +53 -16
- package/dist/query/expr.d.ts +217 -0
- package/dist/query/expr.js +848 -0
- package/dist/query/filters.d.ts +155 -53
- package/dist/query/filters.js +597 -72
- package/dist/query/index.d.ts +2 -0
- package/dist/query/index.js +4 -0
- package/dist/query/json-value.d.ts +43 -0
- package/dist/query/json-value.js +54 -0
- package/dist/query/normalize-args.d.ts +87 -0
- package/dist/query/normalize-args.js +517 -0
- package/dist/query/option-surface.d.ts +16 -4
- package/dist/query/option-surface.js +56 -13
- package/dist/query/relation-names.d.ts +14 -0
- package/dist/query/relation-names.js +77 -19
- package/dist/query/relations.d.ts +30 -3
- package/dist/query/relations.js +220 -67
- package/dist/query/row-lock.d.ts +49 -0
- package/dist/query/row-lock.js +131 -0
- package/dist/query/sql-comment.d.ts +161 -0
- package/dist/query/sql-comment.js +374 -0
- package/dist/query/stream-idle.d.ts +132 -0
- package/dist/query/stream-idle.js +203 -0
- package/dist/query/table-client.d.ts +101 -0
- package/dist/query/table-client.js +70 -0
- package/dist/query/types.d.ts +677 -298
- package/dist/query/utils.d.ts +105 -6
- package/dist/query/utils.js +414 -18
- package/dist/query/warn-registry.d.ts +18 -0
- package/dist/query/warn-registry.js +18 -0
- package/dist/query/where-compile.d.ts +138 -14
- package/dist/query/where-compile.js +508 -31
- package/dist/query/where.d.ts +159 -59
- package/dist/query/where.js +711 -248
- package/dist/query/writes.d.ts +112 -50
- package/dist/query/writes.js +1496 -202
- package/dist/query-events.d.ts +4 -2
- package/dist/query-events.js +3 -1
- package/dist/realtime.d.ts +7 -3
- package/dist/realtime.js +13 -2
- package/dist/schema-builder.d.ts +55 -4
- package/dist/schema-builder.js +381 -18
- package/dist/schema-metadata.d.ts +21 -1
- package/dist/schema-metadata.js +28 -7
- package/dist/schema-sql.d.ts +75 -5
- package/dist/schema-sql.js +494 -44
- package/dist/schema.d.ts +26 -5
- package/dist/schema.js +109 -13
- package/dist/sql-placeholders.d.ts +56 -0
- package/dist/sql-placeholders.js +200 -0
- package/dist/sqlite.d.ts +75 -20
- package/dist/sqlite.js +597 -99
- package/dist/supplied-pool-types.d.ts +48 -0
- package/dist/supplied-pool-types.js +247 -0
- package/dist/transaction-control.d.ts +51 -0
- package/dist/transaction-control.js +311 -0
- package/dist/transaction-state.d.ts +342 -0
- package/dist/transaction-state.js +696 -0
- package/dist/typed-sql.d.ts +37 -3
- package/dist/typed-sql.js +100 -8
- package/package.json +7 -4
- package/skills/turbine-orm/SKILL.md +56 -6
package/dist/cjs/typed-sql.d.ts
CHANGED
|
@@ -19,7 +19,9 @@
|
|
|
19
19
|
* impossible to string-concatenate a value into the query through this API.
|
|
20
20
|
* This is the whole point of the tagged-template shape: the literal segments
|
|
21
21
|
* are frozen by the compiler (`TemplateStringsArray`), and the only way to
|
|
22
|
-
* get a runtime value into the query is via `${...}`, which we bind.
|
|
22
|
+
* get a runtime value into the query is via `${...}`, which we bind. A
|
|
23
|
+
* plain array passed where the template belongs is refused at runtime
|
|
24
|
+
* ({@link assertTemplateStrings}).
|
|
23
25
|
*
|
|
24
26
|
* 3. **Rows are returned as-is (no snake→camel mapping).** This matches the
|
|
25
27
|
* existing `client.raw()` behavior: a typed raw query is a literal escape
|
|
@@ -49,6 +51,7 @@
|
|
|
49
51
|
*/
|
|
50
52
|
import type { PgCompatPool } from './client.js';
|
|
51
53
|
import { type Dialect } from './dialect.js';
|
|
54
|
+
import { type TurbineLogger } from './logger.js';
|
|
52
55
|
import type { QueryEvent } from './query/deferred.js';
|
|
53
56
|
/**
|
|
54
57
|
* Build a `(sql, params)` pair from a tagged-template invocation.
|
|
@@ -61,10 +64,37 @@ import type { QueryEvent } from './query/deferred.js';
|
|
|
61
64
|
*
|
|
62
65
|
* Exported for unit testing the parameterization invariant without a database.
|
|
63
66
|
*/
|
|
64
|
-
export declare function buildTypedSql(strings: TemplateStringsArray, values: readonly unknown[], dialect?: Pick<Dialect, 'paramPlaceholder'
|
|
67
|
+
export declare function buildTypedSql(strings: TemplateStringsArray, values: readonly unknown[], dialect?: Pick<Dialect, 'paramPlaceholder'>, api?: string): {
|
|
65
68
|
sql: string;
|
|
66
69
|
params: unknown[];
|
|
67
70
|
};
|
|
71
|
+
/**
|
|
72
|
+
* Refuse a "template" that did not come from a tagged-template call.
|
|
73
|
+
*
|
|
74
|
+
* Only the static segments of a template reach the SQL text, and that is safe
|
|
75
|
+
* because the compiler wrote them. A plain array passed in their place (a
|
|
76
|
+
* JSON-parsed request field, a string split by hand) puts its text into the
|
|
77
|
+
* SQL with nothing escaped. A real `TemplateStringsArray` is frozen and carries
|
|
78
|
+
* a frozen `raw` array of the same length; `JSON.parse` and array literals
|
|
79
|
+
* produce neither.
|
|
80
|
+
*
|
|
81
|
+
* `unsafeApi` names the method that takes statement text instead, for the
|
|
82
|
+
* error message.
|
|
83
|
+
*/
|
|
84
|
+
export declare function assertTemplateStrings(strings: unknown, api: string, unsafeApi?: string): asserts strings is TemplateStringsArray;
|
|
85
|
+
/**
|
|
86
|
+
* Raw-statement params with every `Date` (in arrays too) bound as UTC text,
|
|
87
|
+
* for a client reading zone-less temporal types as UTC (`utcTimestamps`).
|
|
88
|
+
*
|
|
89
|
+
* node-postgres renders a Date in the process's zone with its offset. A
|
|
90
|
+
* `timestamptz` keeps the instant either way, but a `timestamp` or `date`
|
|
91
|
+
* drops the offset and stores the local wall time, which then reads back
|
|
92
|
+
* shifted by the process offset. UTC text stores the instant's UTC fields,
|
|
93
|
+
* which is what a UTC read returns. Returns `params` itself when it holds no
|
|
94
|
+
* Date. An Invalid Date is refused (E003) instead of reaching the server as
|
|
95
|
+
* unparseable text.
|
|
96
|
+
*/
|
|
97
|
+
export declare function utcDateParams(params: unknown[], api: string): unknown[];
|
|
68
98
|
/**
|
|
69
99
|
* A pending typed raw SQL query. Implements the thenable contract, so it can be
|
|
70
100
|
* `await`ed directly to get `T[]`, or refined via `.one()` / `.scalar()` first.
|
|
@@ -100,6 +130,8 @@ export declare class TypedSqlQuery<T extends Record<string, unknown>> implements
|
|
|
100
130
|
* built outside a client, which then emits nothing, as before.
|
|
101
131
|
*/
|
|
102
132
|
private readonly onQuery?;
|
|
133
|
+
/** Where the `logging` preview line goes. Defaults to the console logger. */
|
|
134
|
+
private readonly logger;
|
|
103
135
|
constructor(pool: PgCompatPool, sql: string, params: unknown[], logging: boolean,
|
|
104
136
|
/**
|
|
105
137
|
* Runs the execution under the owning client's `errorMessages` mode. Passed
|
|
@@ -120,7 +152,9 @@ export declare class TypedSqlQuery<T extends Record<string, unknown>> implements
|
|
|
120
152
|
* with model `'$raw'` and action `'sql'`. Absent for a `TypedSqlQuery`
|
|
121
153
|
* built outside a client, which then emits nothing, as before.
|
|
122
154
|
*/
|
|
123
|
-
onQuery?: ((event: QueryEvent) => void) | undefined
|
|
155
|
+
onQuery?: ((event: QueryEvent) => void) | undefined,
|
|
156
|
+
/** Where the `logging` preview line goes. Defaults to the console logger. */
|
|
157
|
+
logger?: TurbineLogger);
|
|
124
158
|
/** Execute and return all rows. Internal; powers `then`, `one`, and `scalar`. */
|
|
125
159
|
private run;
|
|
126
160
|
/**
|
package/dist/cjs/typed-sql.js
CHANGED
|
@@ -20,7 +20,9 @@
|
|
|
20
20
|
* impossible to string-concatenate a value into the query through this API.
|
|
21
21
|
* This is the whole point of the tagged-template shape: the literal segments
|
|
22
22
|
* are frozen by the compiler (`TemplateStringsArray`), and the only way to
|
|
23
|
-
* get a runtime value into the query is via `${...}`, which we bind.
|
|
23
|
+
* get a runtime value into the query is via `${...}`, which we bind. A
|
|
24
|
+
* plain array passed where the template belongs is refused at runtime
|
|
25
|
+
* ({@link assertTemplateStrings}).
|
|
24
26
|
*
|
|
25
27
|
* 3. **Rows are returned as-is (no snake→camel mapping).** This matches the
|
|
26
28
|
* existing `client.raw()` behavior: a typed raw query is a literal escape
|
|
@@ -51,8 +53,11 @@
|
|
|
51
53
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
52
54
|
exports.TypedSqlQuery = void 0;
|
|
53
55
|
exports.buildTypedSql = buildTypedSql;
|
|
56
|
+
exports.assertTemplateStrings = assertTemplateStrings;
|
|
57
|
+
exports.utcDateParams = utcDateParams;
|
|
54
58
|
const dialect_js_1 = require("./dialect.js");
|
|
55
59
|
const errors_js_1 = require("./errors.js");
|
|
60
|
+
const logger_js_1 = require("./logger.js");
|
|
56
61
|
const query_events_js_1 = require("./query-events.js");
|
|
57
62
|
/**
|
|
58
63
|
* Build a `(sql, params)` pair from a tagged-template invocation.
|
|
@@ -65,11 +70,10 @@ const query_events_js_1 = require("./query-events.js");
|
|
|
65
70
|
*
|
|
66
71
|
* Exported for unit testing the parameterization invariant without a database.
|
|
67
72
|
*/
|
|
68
|
-
function buildTypedSql(strings, values, dialect = dialect_js_1.postgresDialect) {
|
|
69
|
-
|
|
70
|
-
//
|
|
71
|
-
//
|
|
72
|
-
// loudly here instead).
|
|
73
|
+
function buildTypedSql(strings, values, dialect = dialect_js_1.postgresDialect, api = 'sql') {
|
|
74
|
+
assertTemplateStrings(strings, api);
|
|
75
|
+
// A tagged template always has `strings.length === values.length + 1`; a
|
|
76
|
+
// hand-built frozen one may not, and would desync placeholders from params.
|
|
73
77
|
if (strings.length !== values.length + 1) {
|
|
74
78
|
throw new errors_js_1.ValidationError(`sql template segment/value count mismatch: ${strings.length} segments, ${values.length} values.`);
|
|
75
79
|
}
|
|
@@ -82,6 +86,92 @@ function buildTypedSql(strings, values, dialect = dialect_js_1.postgresDialect)
|
|
|
82
86
|
}
|
|
83
87
|
return { sql, params: values.slice() };
|
|
84
88
|
}
|
|
89
|
+
/**
|
|
90
|
+
* Refuse a "template" that did not come from a tagged-template call.
|
|
91
|
+
*
|
|
92
|
+
* Only the static segments of a template reach the SQL text, and that is safe
|
|
93
|
+
* because the compiler wrote them. A plain array passed in their place (a
|
|
94
|
+
* JSON-parsed request field, a string split by hand) puts its text into the
|
|
95
|
+
* SQL with nothing escaped. A real `TemplateStringsArray` is frozen and carries
|
|
96
|
+
* a frozen `raw` array of the same length; `JSON.parse` and array literals
|
|
97
|
+
* produce neither.
|
|
98
|
+
*
|
|
99
|
+
* `unsafeApi` names the method that takes statement text instead, for the
|
|
100
|
+
* error message.
|
|
101
|
+
*/
|
|
102
|
+
function assertTemplateStrings(strings, api, unsafeApi = 'rawUnsafe') {
|
|
103
|
+
if (Array.isArray(strings) && Object.isFrozen(strings)) {
|
|
104
|
+
const raw = strings.raw;
|
|
105
|
+
if (Array.isArray(raw) && Object.isFrozen(raw) && raw.length === strings.length) {
|
|
106
|
+
if (strings.every((s) => typeof s === 'string'))
|
|
107
|
+
return;
|
|
108
|
+
throw new errors_js_1.ValidationError(`${api}: a segment of the template contains an invalid escape sequence, so it has no text. ` +
|
|
109
|
+
'Fix the escape (write a literal backslash as `\\\\`).');
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
const received = Array.isArray(strings) ? 'a plain array' : strings === null ? 'null' : typeof strings;
|
|
113
|
+
throw new errors_js_1.ValidationError(`${api} must be called as a tagged template, for example ${api}\`SELECT * FROM users WHERE id = \${id}\`, ` +
|
|
114
|
+
`so that every interpolated value is sent as a bound parameter. It received ${received} instead, whose ` +
|
|
115
|
+
'text would reach the SQL unescaped. To run statement text built at runtime (DDL, a migration file), use ' +
|
|
116
|
+
`\`${unsafeApi}(text, ...values)\` and keep untrusted input out of the text.`);
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* A `Date` in the form node-postgres sends under `parseInputDatesAsUTC`:
|
|
120
|
+
* `YYYY-MM-DDTHH:MM:SS.mmm+00:00`, with ` BC` for years before 1.
|
|
121
|
+
*/
|
|
122
|
+
function utcDateText(date) {
|
|
123
|
+
const pad = (n, width = 2) => String(n).padStart(width, '0');
|
|
124
|
+
let year = date.getUTCFullYear();
|
|
125
|
+
const bc = year < 1;
|
|
126
|
+
if (bc)
|
|
127
|
+
year = 1 - year;
|
|
128
|
+
return (`${pad(year, 4)}-${pad(date.getUTCMonth() + 1)}-${pad(date.getUTCDate())}` +
|
|
129
|
+
`T${pad(date.getUTCHours())}:${pad(date.getUTCMinutes())}:${pad(date.getUTCSeconds())}` +
|
|
130
|
+
`.${pad(date.getUTCMilliseconds(), 3)}+00:00${bc ? ' BC' : ''}`);
|
|
131
|
+
}
|
|
132
|
+
function utcDateValue(value, api, position, nested) {
|
|
133
|
+
if (value instanceof Date) {
|
|
134
|
+
if (Number.isNaN(value.getTime())) {
|
|
135
|
+
throw new errors_js_1.ValidationError(`${api}: parameter $${position} ${nested ? 'contains' : 'is'} an Invalid Date, which has no SQL value. ` +
|
|
136
|
+
'Pass a valid Date, or null for SQL NULL.');
|
|
137
|
+
}
|
|
138
|
+
return utcDateText(value);
|
|
139
|
+
}
|
|
140
|
+
if (!Array.isArray(value))
|
|
141
|
+
return value;
|
|
142
|
+
let out;
|
|
143
|
+
for (let i = 0; i < value.length; i++) {
|
|
144
|
+
const converted = utcDateValue(value[i], api, position, true);
|
|
145
|
+
if (converted !== value[i]) {
|
|
146
|
+
out ??= value.slice();
|
|
147
|
+
out[i] = converted;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return out ?? value;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Raw-statement params with every `Date` (in arrays too) bound as UTC text,
|
|
154
|
+
* for a client reading zone-less temporal types as UTC (`utcTimestamps`).
|
|
155
|
+
*
|
|
156
|
+
* node-postgres renders a Date in the process's zone with its offset. A
|
|
157
|
+
* `timestamptz` keeps the instant either way, but a `timestamp` or `date`
|
|
158
|
+
* drops the offset and stores the local wall time, which then reads back
|
|
159
|
+
* shifted by the process offset. UTC text stores the instant's UTC fields,
|
|
160
|
+
* which is what a UTC read returns. Returns `params` itself when it holds no
|
|
161
|
+
* Date. An Invalid Date is refused (E003) instead of reaching the server as
|
|
162
|
+
* unparseable text.
|
|
163
|
+
*/
|
|
164
|
+
function utcDateParams(params, api) {
|
|
165
|
+
let out;
|
|
166
|
+
for (let i = 0; i < params.length; i++) {
|
|
167
|
+
const converted = utcDateValue(params[i], api, i + 1, false);
|
|
168
|
+
if (converted !== params[i]) {
|
|
169
|
+
out ??= params.slice();
|
|
170
|
+
out[i] = converted;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return out ?? params;
|
|
174
|
+
}
|
|
85
175
|
/**
|
|
86
176
|
* A pending typed raw SQL query. Implements the thenable contract, so it can be
|
|
87
177
|
* `await`ed directly to get `T[]`, or refined via `.one()` / `.scalar()` first.
|
|
@@ -99,6 +189,7 @@ class TypedSqlQuery {
|
|
|
99
189
|
logging;
|
|
100
190
|
runScoped;
|
|
101
191
|
onQuery;
|
|
192
|
+
logger;
|
|
102
193
|
constructor(pool, sql, params, logging,
|
|
103
194
|
/**
|
|
104
195
|
* Runs the execution under the owning client's `errorMessages` mode. Passed
|
|
@@ -119,19 +210,22 @@ class TypedSqlQuery {
|
|
|
119
210
|
* with model `'$raw'` and action `'sql'`. Absent for a `TypedSqlQuery`
|
|
120
211
|
* built outside a client, which then emits nothing, as before.
|
|
121
212
|
*/
|
|
122
|
-
onQuery
|
|
213
|
+
onQuery,
|
|
214
|
+
/** Where the `logging` preview line goes. Defaults to the console logger. */
|
|
215
|
+
logger = logger_js_1.consoleLogger) {
|
|
123
216
|
this.pool = pool;
|
|
124
217
|
this.sql = sql;
|
|
125
218
|
this.params = params;
|
|
126
219
|
this.logging = logging;
|
|
127
220
|
this.runScoped = runScoped;
|
|
128
221
|
this.onQuery = onQuery;
|
|
222
|
+
this.logger = logger;
|
|
129
223
|
}
|
|
130
224
|
/** Execute and return all rows. Internal; powers `then`, `one`, and `scalar`. */
|
|
131
225
|
run() {
|
|
132
226
|
return this.runScoped(async () => {
|
|
133
227
|
if (this.logging) {
|
|
134
|
-
|
|
228
|
+
(0, logger_js_1.logTo)(this.logger, 'debug', `Typed SQL: ${this.sql.trim().substring(0, 120)}...`, { code: 'typed-sql' });
|
|
135
229
|
}
|
|
136
230
|
const result = await (0, query_events_js_1.runReportedStatement)(this.onQuery, (0, query_events_js_1.rawIdentity)('sql'), this.sql, this.params, () => this.pool.query(this.sql, this.params), errors_js_1.wrapPgError);
|
|
137
231
|
return result.rows;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code-first column tags for `turbine generate`.
|
|
3
|
+
*
|
|
4
|
+
* `pii`, `updatedAt`, `maxLength` and `tsType` exist only in the `defineSchema()`
|
|
5
|
+
* file: the database has no column for them, so a client generated from
|
|
6
|
+
* introspection alone returns PII by default, never stamps `updatedAt`, and
|
|
7
|
+
* types a `jsonb` column as `unknown`. `generate` reads the schema file and
|
|
8
|
+
* overlays its tags onto the introspected schema with the two functions here.
|
|
9
|
+
*
|
|
10
|
+
* Lives under `cli/` because only the CLI uses it. `schemaDefToMetadata` (the
|
|
11
|
+
* library path) shares the tag list through `declaredTagsOf`.
|
|
12
|
+
*/
|
|
13
|
+
import { type SchemaMetadata } from '../schema.js';
|
|
14
|
+
import type { SchemaDef } from '../schema-builder.js';
|
|
15
|
+
import { type CodeFirstColumnTags } from '../schema-metadata.js';
|
|
16
|
+
/** Declared tags per table (database name), per column (database name). */
|
|
17
|
+
export type CodeFirstTagMap = Map<string, Map<string, CodeFirstColumnTags>>;
|
|
18
|
+
/**
|
|
19
|
+
* Collect the code-first-only tags a {@link SchemaDef} declares, keyed by the
|
|
20
|
+
* database table and column they land on (the same `camelToSnake(field)` rule
|
|
21
|
+
* {@link schemaDefToMetadata} uses). Tables and columns that declare no tag are
|
|
22
|
+
* absent from the result.
|
|
23
|
+
*
|
|
24
|
+
* Introspection can not see these flags, because the database has no column for
|
|
25
|
+
* them. `turbine generate` overlays them onto the introspected schema with
|
|
26
|
+
* {@link applyCodeFirstColumnTags}.
|
|
27
|
+
*/
|
|
28
|
+
export declare function extractCodeFirstColumnTags(def: SchemaDef): CodeFirstTagMap;
|
|
29
|
+
/** What {@link applyCodeFirstColumnTags} changed. */
|
|
30
|
+
export interface AppliedColumnTags {
|
|
31
|
+
/** Tables that received at least one tag. */
|
|
32
|
+
tables: number;
|
|
33
|
+
pii: number;
|
|
34
|
+
updatedAt: number;
|
|
35
|
+
maxLength: number;
|
|
36
|
+
/**
|
|
37
|
+
* Declared `tsType`s, as generator `columnTypes` entries
|
|
38
|
+
* (`'<table>.<column>'` by database name). Returned rather than written onto
|
|
39
|
+
* the schema so they go through the generator's own key and type-text
|
|
40
|
+
* validation, the same as a `columnTypes` entry in the config file.
|
|
41
|
+
*/
|
|
42
|
+
columnTypes: Record<string, string>;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Overlay declared tags onto an introspected schema, in place, matching tables
|
|
46
|
+
* and columns by their database names.
|
|
47
|
+
*
|
|
48
|
+
* `pii` and `updatedAt` are set wherever declared. `maxLength` only fills a gap:
|
|
49
|
+
* the live column's own length wins when introspection read one.
|
|
50
|
+
*
|
|
51
|
+
* A `pii` or `updatedAt` tag that names a table or column the database does not
|
|
52
|
+
* have is an error, never a skip: a typo or an un-pushed schema would otherwise
|
|
53
|
+
* generate a client that silently returns the column it was tagged to hide.
|
|
54
|
+
* Every problem is reported at once. `maxLength` is also in the DDL, so a
|
|
55
|
+
* missing table or column only costs that fill-in. `inScope` lets the caller say
|
|
56
|
+
* that a missing table was deliberately left out of the client (an `exclude`
|
|
57
|
+
* filter), which is not an error either.
|
|
58
|
+
*/
|
|
59
|
+
export declare function applyCodeFirstColumnTags(schema: SchemaMetadata, tags: CodeFirstTagMap, options?: {
|
|
60
|
+
source?: string;
|
|
61
|
+
inScope?: (table: string) => boolean;
|
|
62
|
+
}): AppliedColumnTags;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code-first column tags for `turbine generate`.
|
|
3
|
+
*
|
|
4
|
+
* `pii`, `updatedAt`, `maxLength` and `tsType` exist only in the `defineSchema()`
|
|
5
|
+
* file: the database has no column for them, so a client generated from
|
|
6
|
+
* introspection alone returns PII by default, never stamps `updatedAt`, and
|
|
7
|
+
* types a `jsonb` column as `unknown`. `generate` reads the schema file and
|
|
8
|
+
* overlays its tags onto the introspected schema with the two functions here.
|
|
9
|
+
*
|
|
10
|
+
* Lives under `cli/` because only the CLI uses it. `schemaDefToMetadata` (the
|
|
11
|
+
* library path) shares the tag list through `declaredTagsOf`.
|
|
12
|
+
*/
|
|
13
|
+
import { ValidationError } from '../errors.js';
|
|
14
|
+
import { printableCatalogName } from '../introspect.js';
|
|
15
|
+
import { closestName } from '../query/utils.js';
|
|
16
|
+
import { camelToSnake } from '../schema.js';
|
|
17
|
+
import { declaredTagsOf } from '../schema-metadata.js';
|
|
18
|
+
/**
|
|
19
|
+
* Collect the code-first-only tags a {@link SchemaDef} declares, keyed by the
|
|
20
|
+
* database table and column they land on (the same `camelToSnake(field)` rule
|
|
21
|
+
* {@link schemaDefToMetadata} uses). Tables and columns that declare no tag are
|
|
22
|
+
* absent from the result.
|
|
23
|
+
*
|
|
24
|
+
* Introspection can not see these flags, because the database has no column for
|
|
25
|
+
* them. `turbine generate` overlays them onto the introspected schema with
|
|
26
|
+
* {@link applyCodeFirstColumnTags}.
|
|
27
|
+
*/
|
|
28
|
+
export function extractCodeFirstColumnTags(def) {
|
|
29
|
+
const out = new Map();
|
|
30
|
+
for (const tableDef of Object.values(def.tables)) {
|
|
31
|
+
for (const [field, config] of Object.entries(tableDef.columns)) {
|
|
32
|
+
const tags = declaredTagsOf(config);
|
|
33
|
+
if (Object.keys(tags).length === 0)
|
|
34
|
+
continue;
|
|
35
|
+
let columns = out.get(tableDef.name);
|
|
36
|
+
if (!columns) {
|
|
37
|
+
columns = new Map();
|
|
38
|
+
out.set(tableDef.name, columns);
|
|
39
|
+
}
|
|
40
|
+
columns.set(camelToSnake(field), tags);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return out;
|
|
44
|
+
}
|
|
45
|
+
/** A tag the database has no column for, so only the schema file can supply it. */
|
|
46
|
+
function isCodeFirstOnly(tags) {
|
|
47
|
+
return tags.pii === true || tags.updatedAt === true;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Overlay declared tags onto an introspected schema, in place, matching tables
|
|
51
|
+
* and columns by their database names.
|
|
52
|
+
*
|
|
53
|
+
* `pii` and `updatedAt` are set wherever declared. `maxLength` only fills a gap:
|
|
54
|
+
* the live column's own length wins when introspection read one.
|
|
55
|
+
*
|
|
56
|
+
* A `pii` or `updatedAt` tag that names a table or column the database does not
|
|
57
|
+
* have is an error, never a skip: a typo or an un-pushed schema would otherwise
|
|
58
|
+
* generate a client that silently returns the column it was tagged to hide.
|
|
59
|
+
* Every problem is reported at once. `maxLength` is also in the DDL, so a
|
|
60
|
+
* missing table or column only costs that fill-in. `inScope` lets the caller say
|
|
61
|
+
* that a missing table was deliberately left out of the client (an `exclude`
|
|
62
|
+
* filter), which is not an error either.
|
|
63
|
+
*/
|
|
64
|
+
export function applyCodeFirstColumnTags(schema, tags, options = {}) {
|
|
65
|
+
const applied = { tables: 0, pii: 0, updatedAt: 0, maxLength: 0, columnTypes: {} };
|
|
66
|
+
const problems = [];
|
|
67
|
+
for (const [tableName, columns] of tags) {
|
|
68
|
+
const shown = printableCatalogName(tableName);
|
|
69
|
+
if (!Object.hasOwn(schema.tables, tableName)) {
|
|
70
|
+
if (options.inScope && !options.inScope(tableName))
|
|
71
|
+
continue;
|
|
72
|
+
if (![...columns.values()].some(isCodeFirstOnly))
|
|
73
|
+
continue;
|
|
74
|
+
const near = closestName(tableName, Object.keys(schema.tables));
|
|
75
|
+
problems.push(`table "${shown}" is not in the database${near ? ` (did you mean "${printableCatalogName(near)}"?)` : ''}`);
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
const table = schema.tables[tableName];
|
|
79
|
+
let touched = false;
|
|
80
|
+
for (const [columnName, declared] of columns) {
|
|
81
|
+
const column = table.columns.find((c) => c.name === columnName);
|
|
82
|
+
if (!column) {
|
|
83
|
+
if (!isCodeFirstOnly(declared))
|
|
84
|
+
continue;
|
|
85
|
+
const near = closestName(columnName, table.columns.map((c) => c.name));
|
|
86
|
+
problems.push(`column "${printableCatalogName(columnName)}" on table "${shown}" is not in the database` +
|
|
87
|
+
`${near ? ` (did you mean "${printableCatalogName(near)}"?)` : ''}`);
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
if (declared.pii) {
|
|
91
|
+
column.pii = true;
|
|
92
|
+
applied.pii++;
|
|
93
|
+
touched = true;
|
|
94
|
+
}
|
|
95
|
+
if (declared.updatedAt) {
|
|
96
|
+
column.updatedAt = true;
|
|
97
|
+
applied.updatedAt++;
|
|
98
|
+
touched = true;
|
|
99
|
+
}
|
|
100
|
+
if (declared.maxLength !== undefined && column.maxLength === undefined) {
|
|
101
|
+
column.maxLength = declared.maxLength;
|
|
102
|
+
applied.maxLength++;
|
|
103
|
+
touched = true;
|
|
104
|
+
}
|
|
105
|
+
if (declared.tsType !== undefined) {
|
|
106
|
+
applied.columnTypes[`${tableName}.${columnName}`] = declared.tsType;
|
|
107
|
+
touched = true;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
if (touched)
|
|
111
|
+
applied.tables++;
|
|
112
|
+
}
|
|
113
|
+
if (problems.length > 0) {
|
|
114
|
+
const from = options.source ? ` in ${options.source}` : '';
|
|
115
|
+
throw new ValidationError(`Cannot apply the column tags declared${from}: ${problems.join('; ')}. ` +
|
|
116
|
+
'Run `turbine push` so the database has them, or fix the name in the schema file. ' +
|
|
117
|
+
'Generating without the tags would return the columns they mark as PII.');
|
|
118
|
+
}
|
|
119
|
+
return applied;
|
|
120
|
+
}
|
|
@@ -101,6 +101,9 @@ export const COLUMN_NAMING_ARG_KEYS = [
|
|
|
101
101
|
'_avg',
|
|
102
102
|
'_min',
|
|
103
103
|
'_max',
|
|
104
|
+
// Keys are column names and the result is a stored value (or one equal to it),
|
|
105
|
+
// the same reason `_min` / `_max` are listed.
|
|
106
|
+
'_percentile',
|
|
104
107
|
];
|
|
105
108
|
/**
|
|
106
109
|
* Does this args object carry a key that names a column, AT ANY DEPTH?
|
|
@@ -147,6 +150,12 @@ export function carriesColumnNamingArg(args, depth = 0) {
|
|
|
147
150
|
// ---------------------------------------------------------------------------
|
|
148
151
|
// Compile
|
|
149
152
|
// ---------------------------------------------------------------------------
|
|
153
|
+
/**
|
|
154
|
+
* Any non-array object, class instances included. Deliberately NOT the shared
|
|
155
|
+
* `isPlainRecord`: the builder walks a `with` (and a `where`) by its own keys
|
|
156
|
+
* whatever its prototype, so a report that skipped a class-instance `with`
|
|
157
|
+
* would list no relation while the SQL loads one.
|
|
158
|
+
*/
|
|
150
159
|
function isPlainObject(value) {
|
|
151
160
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
152
161
|
}
|
package/dist/cli/config.d.ts
CHANGED
|
@@ -24,12 +24,22 @@ export interface TurbineCliConfig {
|
|
|
24
24
|
* to hand-edit every call site. A typo here is an error, not a silent no-op.
|
|
25
25
|
*/
|
|
26
26
|
relationNames?: Record<string, Record<string, string>>;
|
|
27
|
+
/**
|
|
28
|
+
* TypeScript type overrides for generated row and input types, as
|
|
29
|
+
* `{ '<table>.<column>': '<type>' }` with database names. The main use is a
|
|
30
|
+
* `json` / `jsonb` column, which otherwise generates as `unknown`:
|
|
31
|
+
* `{ 'users.settings': '{ theme: string; beta?: boolean }' }`. The type text
|
|
32
|
+
* is emitted verbatim. A key naming no table or column is an error, not a
|
|
33
|
+
* silent no-op. A code-first column's `tsType` feeds the same map.
|
|
34
|
+
*/
|
|
35
|
+
columnTypes?: Record<string, string>;
|
|
27
36
|
/**
|
|
28
37
|
* Extension for the generated `index.ts` sibling imports (F3):
|
|
29
|
-
* `'js'` (`./types.js`), `'
|
|
30
|
-
*
|
|
38
|
+
* `'js'` (`./types.js`), `'ts'` (`./types.ts`, for Node's native type
|
|
39
|
+
* stripping), `'none'` (`./types`), or `'auto'` (default: tsconfig-detected,
|
|
40
|
+
* falling back to `'js'`; never picks `'ts'`).
|
|
31
41
|
*/
|
|
32
|
-
importExtension?: 'js' | 'none' | 'auto';
|
|
42
|
+
importExtension?: 'js' | 'ts' | 'none' | 'auto';
|
|
33
43
|
/**
|
|
34
44
|
* Keep raw database column names as generated field names (snake_case)
|
|
35
45
|
* instead of camelCase (F4). Opt-in; default `false`.
|
|
@@ -54,7 +64,11 @@ export interface TurbineCliConfig {
|
|
|
54
64
|
* are set.
|
|
55
65
|
*/
|
|
56
66
|
seed?: string;
|
|
57
|
-
/**
|
|
67
|
+
/**
|
|
68
|
+
* The `defineSchema()` file (default `./turbine/schema.ts`): what `push` applies,
|
|
69
|
+
* and where `generate` reads the `pii` / `updatedAt` / `maxLength` tags the
|
|
70
|
+
* database cannot store.
|
|
71
|
+
*/
|
|
58
72
|
schemaFile?: string;
|
|
59
73
|
/**
|
|
60
74
|
* Database adapter for PostgreSQL-compatible databases that need
|
|
@@ -159,14 +173,22 @@ export interface ResolvedConfig {
|
|
|
159
173
|
migrationsDir: string;
|
|
160
174
|
seedFile?: string;
|
|
161
175
|
schemaFile: string;
|
|
176
|
+
/**
|
|
177
|
+
* True when `--schema-file` or the config `schemaFile` key named the file, as
|
|
178
|
+
* opposed to the `./turbine/schema.ts` default. A named file that is missing is
|
|
179
|
+
* an error; a missing default just means a database-first project.
|
|
180
|
+
*/
|
|
181
|
+
schemaFileConfigured?: boolean;
|
|
162
182
|
/** Resolved generator import-extension mode (F3). */
|
|
163
|
-
importExtension: 'js' | 'none' | 'auto';
|
|
183
|
+
importExtension: 'js' | 'ts' | 'none' | 'auto';
|
|
164
184
|
/** Resolved keep-column-names generator flag (F4). */
|
|
165
185
|
keepColumnNames: boolean;
|
|
166
186
|
/** Resolved opt-out of the unique-FK → hasOne introspection flip (F2). */
|
|
167
187
|
legacyToManyUniques: boolean;
|
|
168
188
|
/** Resolved derived-relation rename map, or undefined when none is declared. */
|
|
169
189
|
relationNames?: Record<string, Record<string, string>>;
|
|
190
|
+
/** Column type overrides for the generator, or undefined when none is declared. */
|
|
191
|
+
columnTypes?: Record<string, string>;
|
|
170
192
|
}
|
|
171
193
|
export interface CliOverrides {
|
|
172
194
|
url?: string;
|
|
@@ -176,10 +198,17 @@ export interface CliOverrides {
|
|
|
176
198
|
schemaFile?: string;
|
|
177
199
|
include?: string[];
|
|
178
200
|
exclude?: string[];
|
|
179
|
-
importExtension?: 'js' | 'none' | 'auto';
|
|
201
|
+
importExtension?: 'js' | 'ts' | 'none' | 'auto';
|
|
180
202
|
keepColumnNames?: boolean;
|
|
181
203
|
legacyToManyUniques?: boolean;
|
|
182
204
|
}
|
|
205
|
+
/**
|
|
206
|
+
* The first candidate that is a non-blank string. An empty or whitespace-only
|
|
207
|
+
* value counts as unset, because an exported-but-empty variable (`DATABASE_URL=`
|
|
208
|
+
* in a shell profile or a `.env` template) is how "not configured" is usually
|
|
209
|
+
* spelled, and `??` would let it beat a real value further down the chain.
|
|
210
|
+
*/
|
|
211
|
+
export declare function firstNonEmpty(...candidates: Array<string | undefined>): string | undefined;
|
|
183
212
|
/**
|
|
184
213
|
* Merge config file values with CLI overrides and env vars.
|
|
185
214
|
* Priority: CLI flags > env vars > config file > defaults.
|
package/dist/cli/config.js
CHANGED
|
@@ -149,23 +149,38 @@ export function findConfigFile(cwd) {
|
|
|
149
149
|
}
|
|
150
150
|
return null;
|
|
151
151
|
}
|
|
152
|
+
/**
|
|
153
|
+
* The first candidate that is a non-blank string. An empty or whitespace-only
|
|
154
|
+
* value counts as unset, because an exported-but-empty variable (`DATABASE_URL=`
|
|
155
|
+
* in a shell profile or a `.env` template) is how "not configured" is usually
|
|
156
|
+
* spelled, and `??` would let it beat a real value further down the chain.
|
|
157
|
+
*/
|
|
158
|
+
export function firstNonEmpty(...candidates) {
|
|
159
|
+
for (const value of candidates) {
|
|
160
|
+
if (typeof value === 'string' && value.trim() !== '')
|
|
161
|
+
return value;
|
|
162
|
+
}
|
|
163
|
+
return undefined;
|
|
164
|
+
}
|
|
152
165
|
/**
|
|
153
166
|
* Merge config file values with CLI overrides and env vars.
|
|
154
167
|
* Priority: CLI flags > env vars > config file > defaults.
|
|
155
168
|
*/
|
|
156
169
|
export function resolveConfig(fileConfig, overrides) {
|
|
157
170
|
return {
|
|
158
|
-
url: overrides.url
|
|
171
|
+
url: firstNonEmpty(overrides.url, process.env.DATABASE_URL, fileConfig.url) ?? '',
|
|
159
172
|
out: overrides.out ?? fileConfig.out ?? './generated/turbine',
|
|
160
173
|
schema: overrides.schema ?? fileConfig.schema ?? 'public',
|
|
161
174
|
include: overrides.include ?? fileConfig.include ?? [],
|
|
162
175
|
relationNames: fileConfig.relationNames,
|
|
176
|
+
columnTypes: fileConfig.columnTypes,
|
|
163
177
|
exclude: overrides.exclude ?? fileConfig.exclude ?? [],
|
|
164
178
|
migrationsDir: fileConfig.migrationsDir ?? './turbine/migrations',
|
|
165
179
|
// `seedFile` is canonical (what the docs and `turbine init` use); `seed` is a
|
|
166
180
|
// back-compat alias kept working for configs scaffolded before 0.50.
|
|
167
181
|
seedFile: fileConfig.seedFile ?? fileConfig.seed,
|
|
168
182
|
schemaFile: overrides.schemaFile ?? fileConfig.schemaFile ?? './turbine/schema.ts',
|
|
183
|
+
schemaFileConfigured: (overrides.schemaFile ?? fileConfig.schemaFile) !== undefined,
|
|
169
184
|
importExtension: overrides.importExtension ?? fileConfig.importExtension ?? 'auto',
|
|
170
185
|
keepColumnNames: overrides.keepColumnNames ?? fileConfig.keepColumnNames ?? false,
|
|
171
186
|
legacyToManyUniques: overrides.legacyToManyUniques ?? fileConfig.legacyToManyUniques ?? false,
|
|
@@ -19,16 +19,19 @@
|
|
|
19
19
|
* the optional-`COLUMN` shorthand (`ALTER TABLE t DROP email`), data-modifying
|
|
20
20
|
* CTEs (`WITH d AS (DELETE ...) SELECT ...`), `MERGE ... THEN DELETE`, dynamic
|
|
21
21
|
* SQL inside a `DO`/function body, an `UPDATE` whose only WHERE sits inside a
|
|
22
|
-
* subquery (which restricts nothing)
|
|
23
|
-
*
|
|
24
|
-
*
|
|
22
|
+
* subquery (which restricts nothing) or is a constant-true predicate (`WHERE
|
|
23
|
+
* true`, `WHERE 1 = 1`), a `DROP ... CASCADE` of a non-table object (which takes
|
|
24
|
+
* dependent COLUMNS with it), a detached partition, an `ON CONFLICT DO UPDATE`
|
|
25
|
+
* with no `WHERE` (it rewrites every conflicting row), a `PREPARE`d destructive
|
|
26
|
+
* statement, `lo_unlink` (it deletes large objects), and an `EXPLAIN ANALYZE` of
|
|
27
|
+
* any of the above (which really executes it).
|
|
25
28
|
*
|
|
26
29
|
* The lexing, "where does a statement end" and "which characters are code",
|
|
27
30
|
* is NOT done here: it lives in `sql-statements.ts` and is shared with the
|
|
28
31
|
* migration runner. Two lexers is how this guard came to disagree with the
|
|
29
32
|
* executor about what a file contained (see that module's header).
|
|
30
33
|
*/
|
|
31
|
-
export type DestructiveKind = 'drop-table' | 'drop-schema' | 'drop-database' | 'drop-owned' | 'drop-matview' | 'drop-column' | 'drop-cascade' | 'detach-partition' | 'truncate' | 'delete' | 'update-without-where' | 'alter-column-type' | 'merge-delete' | 'rename' | 'rewrite-rule' | 'dynamic-destructive' | 'dynamic-unclassified';
|
|
34
|
+
export type DestructiveKind = 'drop-table' | 'drop-schema' | 'drop-database' | 'drop-owned' | 'drop-matview' | 'drop-column' | 'drop-cascade' | 'detach-partition' | 'truncate' | 'delete' | 'update-without-where' | 'overwrite-on-conflict' | 'large-object-unlink' | 'alter-column-type' | 'merge-delete' | 'rename' | 'rewrite-rule' | 'dynamic-destructive' | 'dynamic-unclassified';
|
|
32
35
|
export interface DestructiveStatement {
|
|
33
36
|
/** The offending SQL statement (trimmed, possibly long, display truncated) */
|
|
34
37
|
statement: string;
|