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,269 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* turbine-orm — Schema introspection
|
|
3
|
+
*
|
|
4
|
+
* Connects to a live Postgres database, reads information_schema + pg_catalog,
|
|
5
|
+
* and produces a SchemaMetadata object describing every table, column, relation,
|
|
6
|
+
* and index in the target schema.
|
|
7
|
+
*
|
|
8
|
+
* This is the foundation of `npx turbine generate`.
|
|
9
|
+
*/
|
|
10
|
+
import { type Dialect } from './dialect.js';
|
|
11
|
+
import { type ColumnMetadata, type IndexMetadata, type ReferentialAction, type RelationDef, type SchemaMetadata } from './schema.js';
|
|
12
|
+
/**
|
|
13
|
+
* Map a `pg_constraint.confdeltype` / `confupdtype` character to a
|
|
14
|
+
* {@link ReferentialAction}. Postgres encodes: `a` = NO ACTION, `r` = RESTRICT,
|
|
15
|
+
* `c` = CASCADE, `n` = SET NULL, `d` = SET DEFAULT.
|
|
16
|
+
*/
|
|
17
|
+
export declare function pgConfActionToReferential(ch: string): ReferentialAction;
|
|
18
|
+
/**
|
|
19
|
+
* Migration-bookkeeping tables that introspection drops by default: Turbine's
|
|
20
|
+
* own `_turbine_migrations` / `_turbine_metrics` and Prisma's
|
|
21
|
+
* `_prisma_migrations`. These are almost never meant to be surfaced as typed
|
|
22
|
+
* accessors, and a fresh migrate-from-Prisma introspection would otherwise emit
|
|
23
|
+
* a `PrismaMigrations` entity plus stray FK-derived relations on neighbours.
|
|
24
|
+
*
|
|
25
|
+
* A table named here is dropped UNLESS it is explicitly listed in
|
|
26
|
+
* `options.include` (`include` is the escape hatch, no separate flag), and
|
|
27
|
+
* naming a default-excluded table restores its old generated output byte for
|
|
28
|
+
* byte. The list is deliberately tight (exactly these three); leading-
|
|
29
|
+
* underscore tables are legitimate user tables and are never blanket-excluded.
|
|
30
|
+
*/
|
|
31
|
+
export declare const DEFAULT_EXCLUDED_TABLES: readonly ["_turbine_migrations", "_prisma_migrations", "_turbine_metrics"];
|
|
32
|
+
/** The include / exclude filters shared by every introspector's table selection. */
|
|
33
|
+
export interface TableFilterOptions {
|
|
34
|
+
/** Tables to include (empty/undefined = all). Applied first. */
|
|
35
|
+
include?: string[];
|
|
36
|
+
/** Tables the user asked to exclude. Applied after include. */
|
|
37
|
+
exclude?: string[];
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The single authority for turning a raw list of candidate table names into the
|
|
41
|
+
* introspected set, shared by the Postgres catalog reader and every engine
|
|
42
|
+
* introspector (SQLite / MySQL / MSSQL / PowDB) so all surfaces agree.
|
|
43
|
+
*
|
|
44
|
+
* Order of operations:
|
|
45
|
+
* 1. `include` filter: when non-empty, keep only the named tables.
|
|
46
|
+
* 2. user `exclude`: drop anything the caller listed.
|
|
47
|
+
* 3. {@link DEFAULT_EXCLUDED_TABLES}: drop migration bookkeeping tables,
|
|
48
|
+
* EXCEPT any that the caller explicitly named in `include` (the escape
|
|
49
|
+
* hatch that restores the pre-0.41 output for those tables).
|
|
50
|
+
*/
|
|
51
|
+
export declare function applyTableFilters(names: string[], options?: TableFilterOptions): string[];
|
|
52
|
+
/**
|
|
53
|
+
* The subset of {@link DEFAULT_EXCLUDED_TABLES} that were present in `names` but
|
|
54
|
+
* dropped by {@link applyTableFilters} (i.e. not re-added via `include`). Pure
|
|
55
|
+
* helper so the CLI can report "skipped internal table X" without re-deriving
|
|
56
|
+
* the filtering rule.
|
|
57
|
+
*/
|
|
58
|
+
export declare function defaultExcludedTablesPresent(names: string[], options?: TableFilterOptions): string[];
|
|
59
|
+
export interface IntrospectOptions {
|
|
60
|
+
/** Postgres connection string */
|
|
61
|
+
connectionString: string;
|
|
62
|
+
/** Schema to introspect (default: 'public') */
|
|
63
|
+
schema?: string;
|
|
64
|
+
/** Tables to include (default: all). Glob-like patterns not supported yet. */
|
|
65
|
+
include?: string[];
|
|
66
|
+
/** Tables to exclude (default: none). Applied after include. */
|
|
67
|
+
exclude?: string[];
|
|
68
|
+
/**
|
|
69
|
+
* Also introspect **views** and **materialized views** as read-only
|
|
70
|
+
* {@link TableMetadata} entries (`isView: true`). Off by default. Write
|
|
71
|
+
* builders reject views (E003); a view without a primary key is excluded from
|
|
72
|
+
* the generated `findUnique`-family accessor types.
|
|
73
|
+
*/
|
|
74
|
+
includeViews?: boolean;
|
|
75
|
+
/**
|
|
76
|
+
* Opt OUT of the unique-FK → `hasOne` flip (F2). By default (`false`)
|
|
77
|
+
* introspection emits a to-one (`hasOne`) relation on the parent side when a
|
|
78
|
+
* child's foreign-key column set is EXACTLY covered by a UNIQUE constraint or
|
|
79
|
+
* a non-partial, non-expression UNIQUE index, matching Prisma one-to-one
|
|
80
|
+
* introspection. Set to `true` to keep the pre-0.41 behavior where every such
|
|
81
|
+
* relation was emitted as `hasMany` (a to-many array). See
|
|
82
|
+
* {@link detectUniqueForeignKeySets}.
|
|
83
|
+
*/
|
|
84
|
+
legacyToManyUniques?: boolean;
|
|
85
|
+
/**
|
|
86
|
+
* Called with any {@link DEFAULT_EXCLUDED_TABLES} that were present in the
|
|
87
|
+
* database but dropped from this run (F12), so the CLI can print a
|
|
88
|
+
* "skipped internal table X (add it to include to keep it)" note. Not invoked
|
|
89
|
+
* when the set is empty. Postgres path only for now.
|
|
90
|
+
*/
|
|
91
|
+
onDefaultTableExclusion?: (tables: string[]) => void;
|
|
92
|
+
/**
|
|
93
|
+
* Dialect whose {@link Dialect.introspector} drives the catalog reads.
|
|
94
|
+
* Defaults to {@link postgresDialect}. Engines plug their own introspector
|
|
95
|
+
* here so `introspect()` works across databases.
|
|
96
|
+
*/
|
|
97
|
+
dialect?: Dialect;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Introspect a database into {@link SchemaMetadata}, routing through the active
|
|
101
|
+
* dialect's {@link Dialect.introspector} so each engine can override the catalog
|
|
102
|
+
* SQL. PostgreSQL is driven by {@link introspectPostgresCatalog}.
|
|
103
|
+
*/
|
|
104
|
+
export declare function introspect(options: IntrospectOptions): Promise<SchemaMetadata>;
|
|
105
|
+
/**
|
|
106
|
+
* PostgreSQL catalog introspector: reads information_schema + pg_catalog and
|
|
107
|
+
* produces {@link SchemaMetadata}. This is the implementation wrapped by
|
|
108
|
+
* `postgresDialect.introspector`; call {@link introspect} for dialect routing.
|
|
109
|
+
*/
|
|
110
|
+
export declare function introspectPostgresCatalog(options: IntrospectOptions): Promise<SchemaMetadata>;
|
|
111
|
+
/**
|
|
112
|
+
* Parse the indexed column names out of a `pg_indexes.indexdef` string.
|
|
113
|
+
*
|
|
114
|
+
* `indexdef` always reads `CREATE [UNIQUE] INDEX name ON tbl USING method
|
|
115
|
+
* (col, ...) [WHERE predicate]`. We anchor on the `USING` clause's parenthesised
|
|
116
|
+
* column list (the same precedent as `describeIndexDefMismatch` in
|
|
117
|
+
* schema-sql.ts) so a PARTIAL index's trailing `WHERE (...)` parentheses are
|
|
118
|
+
* never mistaken for the column list. The older greedy `/\((.+)\)/` swallowed
|
|
119
|
+
* `) WHERE (` and spliced a raw predicate fragment into the column names, which
|
|
120
|
+
* then leaked into generated compound-unique selector names.
|
|
121
|
+
*
|
|
122
|
+
* Each column is de-quoted (Postgres quotes non-lowercase identifiers such as a
|
|
123
|
+
* Prisma implicit m2m junction's `"A"` / `"B"`), so the names match the
|
|
124
|
+
* unquoted column names carried elsewhere in the metadata. Expression columns
|
|
125
|
+
* (anything containing a parenthesis) are dropped conservatively: a functional
|
|
126
|
+
* index does not name a plain column.
|
|
127
|
+
*/
|
|
128
|
+
export declare function parseIndexColumns(indexdef: string): string[];
|
|
129
|
+
/**
|
|
130
|
+
* Whether an `indexdef` carries a top-level `WHERE` predicate (a PARTIAL index).
|
|
131
|
+
* pg_indexes only ever emits `WHERE` as the partial predicate, so a keyword
|
|
132
|
+
* match is sufficient (matches the `describeIndexDefMismatch` precedent).
|
|
133
|
+
*/
|
|
134
|
+
export declare function indexHasWhere(indexdef: string): boolean;
|
|
135
|
+
/**
|
|
136
|
+
* Recover the raw check expression from `pg_get_constraintdef` output, which
|
|
137
|
+
* wraps it as `CHECK ((expr))`. Strips the leading `CHECK ` keyword and one
|
|
138
|
+
* balanced outer paren pair; leaves anything unexpected untouched.
|
|
139
|
+
*/
|
|
140
|
+
export declare function stripCheckWrapper(def: string): string;
|
|
141
|
+
/** A foreign-key constraint grouped by constraint name (composite FKs carry column arrays). */
|
|
142
|
+
export interface ForeignKeyEntry {
|
|
143
|
+
sourceTable: string;
|
|
144
|
+
sourceColumns: string[];
|
|
145
|
+
targetTable: string;
|
|
146
|
+
targetColumns: string[];
|
|
147
|
+
constraintName: string;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Derive a belongsTo relation name from its FK column. Strips a trailing
|
|
151
|
+
* `_id` (snake_case) or `Id` (camelCase column names — common in Prisma-ported
|
|
152
|
+
* schemas where columns are quoted camelCase identifiers), then camelCases:
|
|
153
|
+
* `current_version_id` and `currentVersionId` both yield `currentVersion`.
|
|
154
|
+
* Stripping is what keeps the scalar FK field (`currentVersionId`) targetable
|
|
155
|
+
* alongside the relation. A column literally named `id` (nothing left after
|
|
156
|
+
* stripping) keeps its own name.
|
|
157
|
+
*/
|
|
158
|
+
export declare function relationNameFromColumn(column: string): string;
|
|
159
|
+
/**
|
|
160
|
+
* True for the tsType forms a json/jsonb column maps to (`unknown`, nullable
|
|
161
|
+
* `unknown | null`). A relation shadowing such a column is a HISTORICAL shadow
|
|
162
|
+
* that worked at runtime and compiled (`unknown` absorbs the relation
|
|
163
|
+
* payload), so the legacy-first naming keeps it instead of renaming.
|
|
164
|
+
*/
|
|
165
|
+
export declare function isUnknownTsType(tsType: string): boolean;
|
|
166
|
+
/**
|
|
167
|
+
* Parse the column list of a PLAIN unique index from its `pg_indexes.indexdef`,
|
|
168
|
+
* returning `null` for anything that does NOT guarantee at-most-one child row:
|
|
169
|
+
*
|
|
170
|
+
* - a PARTIAL index (has a `WHERE` clause): only unique within the predicate;
|
|
171
|
+
* - an EXPRESSION index (`lower(email)`, `(a || b)`): the uniqueness is on the
|
|
172
|
+
* expression, not the raw FK column set.
|
|
173
|
+
*
|
|
174
|
+
* Anchors on the `USING <method> (` clause the same way
|
|
175
|
+
* {@link describeIndexDefMismatch} does, so a partial index's `WHERE (...)`
|
|
176
|
+
* parentheses are never mistaken for the column list. Every column token must be
|
|
177
|
+
* a bare or double-quoted identifier; anything else (a function call, an
|
|
178
|
+
* operator expression) fails the check and yields `null`.
|
|
179
|
+
*/
|
|
180
|
+
export declare function parsePlainUniqueIndexColumns(indexdef: string): string[] | null;
|
|
181
|
+
/**
|
|
182
|
+
* Assemble, per table, every column set that EXACTLY guarantees at-most-one row:
|
|
183
|
+
* the primary key, every UNIQUE constraint, and every PLAIN (non-partial,
|
|
184
|
+
* non-expression) UNIQUE index. Consumed by
|
|
185
|
+
* {@link buildRelationsFromForeignKeys} to flip a child relation whose FK column
|
|
186
|
+
* set matches one of these sets from `hasMany` to `hasOne` (F2, Prisma
|
|
187
|
+
* one-to-one parity).
|
|
188
|
+
*/
|
|
189
|
+
export declare function detectUniqueForeignKeySets(pkByTable: Map<string, string[]>, uniqueByTable: Map<string, string[][]>, indexesByTable: Map<string, IndexMetadata[]>): Map<string, string[][]>;
|
|
190
|
+
/**
|
|
191
|
+
* Build the belongsTo/hasMany relation maps for every table from its foreign
|
|
192
|
+
* keys. Naming rules (LEGACY-FIRST — a relation name that previously worked at
|
|
193
|
+
* runtime must never change out from under a regenerating app):
|
|
194
|
+
*
|
|
195
|
+
* 1. First compute the historical derivation exactly as it shipped before
|
|
196
|
+
* the collision guard existed: belongsTo strips a case-SENSITIVE `_id`
|
|
197
|
+
* suffix (`snakeToCamel(col.replace(/_id$/, ''))` when several FKs point
|
|
198
|
+
* at the same target, else the singularized target table), and hasMany is
|
|
199
|
+
* `snakeToCamel(`${source}_by_${strippedColumn}`)` (else the source
|
|
200
|
+
* table). If that legacy name is free, KEEP IT — even when it looks odd
|
|
201
|
+
* (`blogPostsByAuthorId`, `postsBy_Author`): those names were collision-
|
|
202
|
+
* free and worked, so regenerating must not rename them.
|
|
203
|
+
* 2. If the legacy name collides ONLY with a scalar column whose tsType is
|
|
204
|
+
* `unknown` (json/jsonb), keep it anyway with a warning: the shadow is
|
|
205
|
+
* historical, ran fine at runtime, and compiled (`unknown` absorbs the
|
|
206
|
+
* relation payload; generate.ts's typeSafeRelations omits the relation
|
|
207
|
+
* from the type layer).
|
|
208
|
+
* 3. On a genuine collision (concrete-typed column shadow, or a previously
|
|
209
|
+
* assigned relation), fall back to the modern derivation — the `_id`/`Id`
|
|
210
|
+
* case-insensitive strip of {@link relationNameFromColumn} plus the
|
|
211
|
+
* `By`-composed reverse name — which fixes the camelCase-FK shadowing
|
|
212
|
+
* shapes that were actually BROKEN before (relation name === scalar FK
|
|
213
|
+
* field → unusable types).
|
|
214
|
+
* 4. Last resort: deterministic `Rel`/`Rel2` suffix + warning.
|
|
215
|
+
*
|
|
216
|
+
* @param columnFieldsByTable camelCase column *fields* per table — used to
|
|
217
|
+
* guarantee relations never shadow concrete-typed scalar columns.
|
|
218
|
+
* @param unknownTypedFieldsByTable subset of the column fields whose tsType is
|
|
219
|
+
* `unknown` (json/jsonb) — legacy shadows of these are preserved (rule 2).
|
|
220
|
+
* @param uniqueSetsByTable when provided (F2), the child-table column sets that
|
|
221
|
+
* guarantee at-most-one row (PK + unique constraints + plain unique indexes,
|
|
222
|
+
* from {@link detectUniqueForeignKeySets}). A reverse relation whose FK column
|
|
223
|
+
* set EXACTLY matches one of the child's unique sets is emitted as `hasOne`
|
|
224
|
+
* (to-one) instead of `hasMany`, and named with the SINGULAR of the child
|
|
225
|
+
* table (falling back to the legacy plural name on collision). Omit it (the
|
|
226
|
+
* engine introspectors and `defineSchema` path do) to keep every reverse
|
|
227
|
+
* relation `hasMany`.
|
|
228
|
+
*/
|
|
229
|
+
export declare function buildRelationsFromForeignKeys(foreignKeys: ForeignKeyEntry[], columnFieldsByTable: Map<string, Set<string>>, fkActions?: Map<string, {
|
|
230
|
+
onDelete: ReferentialAction;
|
|
231
|
+
onUpdate: ReferentialAction;
|
|
232
|
+
}>, unknownTypedFieldsByTable?: Map<string, Set<string>>, uniqueSetsByTable?: Map<string, string[][]>): Map<string, Record<string, RelationDef>>;
|
|
233
|
+
/**
|
|
234
|
+
* Conservative auto-`manyToMany` detection over pure junction tables, shared
|
|
235
|
+
* by the Postgres introspector, the engine introspectors (SQLite / MySQL /
|
|
236
|
+
* MSSQL), the MCP server, and `schemaDefToMetadata()` so all surfaces derive
|
|
237
|
+
* IDENTICAL relation names for the same logical schema.
|
|
238
|
+
*
|
|
239
|
+
* A table J is a PURE junction only when ALL of these hold:
|
|
240
|
+
* 1. J's junction KEY is exactly two columns: either a two-column primary
|
|
241
|
+
* key, OR (Prisma implicit m2m junctions have NO primary key) a two-column
|
|
242
|
+
* UNIQUE index over exactly the two FK columns, supplied via the optional
|
|
243
|
+
* `uniqueIndexColsByTable`. When that map is absent the behavior is
|
|
244
|
+
* unchanged: only a two-column PK qualifies.
|
|
245
|
+
* 2. J has exactly two FKs, each single-column.
|
|
246
|
+
* 3. Each FK's source column is one of J's two key columns.
|
|
247
|
+
* 4. The two FKs target two DISTINCT tables (A and B).
|
|
248
|
+
* 5. J has no payload columns beyond the two FK/key columns.
|
|
249
|
+
*
|
|
250
|
+
* For such a J linking A and B this ADDS a `manyToMany` on A → B and B → A
|
|
251
|
+
* routed `through` J. It never removes or renames an existing relation:
|
|
252
|
+
* - an already-assigned relation with the same name → SKIP (additive-only,
|
|
253
|
+
* unchanged historical behavior);
|
|
254
|
+
* - a shadowed json/jsonb (`unknown`-typed) column → keep the historical
|
|
255
|
+
* name + warn (it worked at runtime and compiled);
|
|
256
|
+
* - a shadowed concrete-typed column → deterministic `Rel` suffix + warn
|
|
257
|
+
* instead of silently dropping the relation.
|
|
258
|
+
*/
|
|
259
|
+
export declare function addAutoManyToManyRelations(tableNames: Iterable<string>, foreignKeys: ForeignKeyEntry[], pkByTable: Map<string, string[]>, columnNamesByTable: Map<string, string[]>, relationsByTable: Map<string, Record<string, RelationDef>>, columnFieldsByTable?: Map<string, Set<string>>, unknownTypedFieldsByTable?: Map<string, Set<string>>, uniqueIndexColsByTable?: Map<string, string[][]>): void;
|
|
260
|
+
/**
|
|
261
|
+
* One-stop relation derivation for the engine introspectors (SQLite / MySQL /
|
|
262
|
+
* MSSQL): filters the FK list to the introspected table set, seeds the
|
|
263
|
+
* taken-name / json-shadow maps from the engine's column metadata, and runs
|
|
264
|
+
* the SAME `buildRelationsFromForeignKeys` + `addAutoManyToManyRelations`
|
|
265
|
+
* pipeline as the Postgres introspector — so every engine derives identical
|
|
266
|
+
* relation names for the same logical schema (the engines previously carried
|
|
267
|
+
* stale copies of a retired naming scheme).
|
|
268
|
+
*/
|
|
269
|
+
export declare function deriveEngineRelations(tableNames: string[], foreignKeys: ForeignKeyEntry[], pkByTable: Map<string, string[]>, columnsByTable: Map<string, Pick<ColumnMetadata, 'name' | 'field' | 'tsType'>[]>): Map<string, Record<string, RelationDef>>;
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* turbine-orm/mssql — Microsoft SQL Server engine (driver-injected, optional peer)
|
|
3
|
+
*
|
|
4
|
+
* Binds Turbine to SQL Server 2016+ via the `mssql` driver (which wraps
|
|
5
|
+
* `tedious`). `mssql` is **not** a root dependency — it is an **optional peer**:
|
|
6
|
+
* `npm i turbine-orm` pulls nothing extra, and only consumers who
|
|
7
|
+
* `import 'turbine-orm/mssql'` install `mssql` themselves. The factory loads it
|
|
8
|
+
* through a dynamic `import('mssql')` so importing this module never crashes when
|
|
9
|
+
* `mssql` is absent for a consumer who does not use it. Turbine's root runtime
|
|
10
|
+
* dependency stays exactly `pg`.
|
|
11
|
+
*
|
|
12
|
+
* ## The three hard SQL Server realities this engine solves
|
|
13
|
+
*
|
|
14
|
+
* 1. **No `RETURNING`.** `INSERT`/`UPDATE`/`DELETE` cannot trail a `RETURNING`
|
|
15
|
+
* clause; SQL Server returns affected rows via `OUTPUT INSERTED.*` /
|
|
16
|
+
* `OUTPUT DELETED.*` injected MID-statement (between the column list and
|
|
17
|
+
* `VALUES`, or between `SET …` and `WHERE …`). `mssqlDialect.resultStrategy =
|
|
18
|
+
* 'output'`: the statement returns its own rows in ONE round-trip (executed
|
|
19
|
+
* exactly like the PostgreSQL `'returning'` path). Upsert becomes a
|
|
20
|
+
* `MERGE … WHEN MATCHED … WHEN NOT MATCHED … OUTPUT INSERTED.* ;` (a MERGE
|
|
21
|
+
* must end with `;`). This is the first shipped engine to exercise the
|
|
22
|
+
* Phase-0 `'output'` result strategy.
|
|
23
|
+
* 2. **No `json_agg`.** SQL Server has no JSON aggregate function; the idiomatic
|
|
24
|
+
* single-query nested-relation path is `(SELECT child cols … FOR JSON PATH)`,
|
|
25
|
+
* whose object shape is expressed by the child SELECT's column ALIASES rather
|
|
26
|
+
* than an explicit `JSON_OBJECT(...)`. That does NOT map onto
|
|
27
|
+
* `buildJsonObject`/`buildJsonArrayAgg`, so `mssqlDialect` defines the
|
|
28
|
+
* additive `Dialect.buildRelationSubquery` override (the sanctioned Phase-3
|
|
29
|
+
* seam extension) and owns the whole correlated subquery. To-many wraps
|
|
30
|
+
* `ISNULL((… FOR JSON PATH), '[]')` (FOR JSON over zero rows is NULL, not
|
|
31
|
+
* `[]`); to-one adds `, WITHOUT_ARRAY_WRAPPER` and lets NULL be the no-row
|
|
32
|
+
* value. Nested relations are embedded with `JSON_QUERY(...)` so they stay
|
|
33
|
+
* real JSON instead of being escaped as a string. `INCLUDE_NULL_VALUES`
|
|
34
|
+
* keeps NULL columns present (matching PostgreSQL `json_build_object`).
|
|
35
|
+
* 3. **No `LIMIT`.** Paging is `ORDER BY … OFFSET n ROWS FETCH NEXT m ROWS ONLY`,
|
|
36
|
+
* which requires an ORDER BY — a stable `ORDER BY (SELECT NULL)` is injected
|
|
37
|
+
* when the query has none (`Dialect.buildLimitOffset`).
|
|
38
|
+
*
|
|
39
|
+
* ## Named `@pN` placeholders (no positional `?`)
|
|
40
|
+
*
|
|
41
|
+
* `mssqlDialect.paramPlaceholder = (i) => '@p' + i`. The driver shim binds via
|
|
42
|
+
* `request.input('p' + i, value)`, so binding is by NAME and independent of where
|
|
43
|
+
* each placeholder lands in the SQL text — exactly the guarantee PostgreSQL's
|
|
44
|
+
* numbered `$N` gives. (SQL Server is naturally named-param friendly, sidestepping
|
|
45
|
+
* the positional-`?` mis-bind bug the SQLite/MySQL phases hit.)
|
|
46
|
+
*
|
|
47
|
+
* ## Capabilities & limits (vs PostgreSQL)
|
|
48
|
+
*
|
|
49
|
+
* - **Single query nested relations preserved** via `FOR JSON PATH` (SQL Server
|
|
50
|
+
* 2016+). Ordered/limited to-many uses `ORDER BY … OFFSET/FETCH` inside the FOR
|
|
51
|
+
* JSON subquery (no inner-subquery rewrite needed — FOR JSON aggregates AFTER
|
|
52
|
+
* the row selection).
|
|
53
|
+
* - **Result strategy `'output'`:** create/update/delete/upsert return their rows
|
|
54
|
+
* from the same statement. `createMany` returns the inserted rows via
|
|
55
|
+
* `OUTPUT INSERTED.*` on the multi-row VALUES insert (≤ 1000 rows / 2100 params
|
|
56
|
+
* per statement — exceeding either throws a clear `ValidationError`; chunk
|
|
57
|
+
* yourself or use single `create`s).
|
|
58
|
+
* - **MERGE concurrency caveat:** `MERGE` is the upsert primitive; under high
|
|
59
|
+
* concurrency a `MERGE` can still race (it is NOT a substitute for a unique
|
|
60
|
+
* constraint). Keep the conflict target backed by a real `UNIQUE`/`PK` index,
|
|
61
|
+
* and rely on the typed `UniqueConstraintError` (2627/2601 → E008) for the
|
|
62
|
+
* loser of a race.
|
|
63
|
+
* - **Unsupported (throw `UnsupportedFeatureError`):** pgvector distance ops,
|
|
64
|
+
* LISTEN/NOTIFY (`$listen`/`$notify`), RLS `sessionContext` (sp_set_session_context
|
|
65
|
+
* exists but is connection-scoped, not transaction-local, so it is not wired —
|
|
66
|
+
* throws rather than silently leaking context across pooled connections).
|
|
67
|
+
* - **Advisory-lock migration locking** is available in principle via
|
|
68
|
+
* `sp_getapplock`/`sp_releaseapplock` (`supportsAdvisoryLock = true`); the
|
|
69
|
+
* migrate CLI is still PostgreSQL-only, so this flag documents intent for a
|
|
70
|
+
* future adapter.
|
|
71
|
+
* - **Case-insensitive matching** uses `LOWER(col) LIKE LOWER(ref)` — deterministic
|
|
72
|
+
* regardless of the column's collation (note this can defeat an index unless a
|
|
73
|
+
* computed/persisted `LOWER()` index exists).
|
|
74
|
+
* - **bignum:** the shim applies the same safe-int policy Turbine uses for Postgres
|
|
75
|
+
* `int8` (number when it fits in 2^53, decimal string otherwise) WITHOUT mutating
|
|
76
|
+
* any global driver state. `DECIMAL`/`NUMERIC`/`MONEY` come back as strings;
|
|
77
|
+
* `BIT` binds/returns booleans.
|
|
78
|
+
* - **`DISTINCT ON`** is PostgreSQL-only and is not translated — avoid `distinct`
|
|
79
|
+
* on SQL Server.
|
|
80
|
+
*
|
|
81
|
+
* ## Example
|
|
82
|
+
*
|
|
83
|
+
* ```ts
|
|
84
|
+
* import { turbineMssql } from 'turbine-orm/mssql';
|
|
85
|
+
* import { SCHEMA } from './generated/turbine/metadata.js';
|
|
86
|
+
*
|
|
87
|
+
* const db = await turbineMssql('mssql://sa:Passw0rd!@localhost:1433/app', SCHEMA);
|
|
88
|
+
* const users = await db.users.findMany({ with: { posts: true }, limit: 10 });
|
|
89
|
+
* await db.disconnect();
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
import { type PgCompatPool, type PgCompatPoolClient, TurbineClient, type TurbineConfig } from './client.js';
|
|
93
|
+
import { type Dialect, type IntrospectOptions } from './dialect.js';
|
|
94
|
+
import { type SchemaMetadata } from './schema.js';
|
|
95
|
+
interface MssqlRequest {
|
|
96
|
+
input(name: string, value: unknown): MssqlRequest;
|
|
97
|
+
query<R = Record<string, unknown>>(text: string): Promise<MssqlQueryResult<R>>;
|
|
98
|
+
batch<R = Record<string, unknown>>(text: string): Promise<MssqlQueryResult<R>>;
|
|
99
|
+
}
|
|
100
|
+
/** Per-column metadata `mssql` attaches to a recordset (`recordset.columns`). */
|
|
101
|
+
interface MssqlColumnMeta {
|
|
102
|
+
[name: string]: {
|
|
103
|
+
type?: {
|
|
104
|
+
name?: string;
|
|
105
|
+
declaration?: string;
|
|
106
|
+
};
|
|
107
|
+
} | undefined;
|
|
108
|
+
}
|
|
109
|
+
interface MssqlQueryResult<R = Record<string, unknown>> {
|
|
110
|
+
recordset?: R[] & {
|
|
111
|
+
columns?: MssqlColumnMeta;
|
|
112
|
+
};
|
|
113
|
+
recordsets?: R[][];
|
|
114
|
+
rowsAffected?: number[];
|
|
115
|
+
}
|
|
116
|
+
interface MssqlTransaction {
|
|
117
|
+
begin(isolationLevel?: number): Promise<unknown>;
|
|
118
|
+
commit(): Promise<unknown>;
|
|
119
|
+
rollback(): Promise<unknown>;
|
|
120
|
+
}
|
|
121
|
+
interface MssqlConnectionPool {
|
|
122
|
+
connect(): Promise<MssqlConnectionPool>;
|
|
123
|
+
request(): MssqlRequest;
|
|
124
|
+
close(): Promise<unknown>;
|
|
125
|
+
readonly connected?: boolean;
|
|
126
|
+
}
|
|
127
|
+
/** The subset of the `mssql` module namespace the shim constructs. */
|
|
128
|
+
interface MssqlModule {
|
|
129
|
+
connect(config: any): Promise<MssqlConnectionPool>;
|
|
130
|
+
ConnectionPool: new (config: any) => MssqlConnectionPool;
|
|
131
|
+
Request: new (parent: MssqlConnectionPool | MssqlTransaction) => MssqlRequest;
|
|
132
|
+
Transaction: new (pool: MssqlConnectionPool) => MssqlTransaction;
|
|
133
|
+
ISOLATION_LEVEL: Record<string, number>;
|
|
134
|
+
}
|
|
135
|
+
/** pg-style query argument: a SQL string or a `{ text, values }` config object. */
|
|
136
|
+
type QueryArg = string | {
|
|
137
|
+
name?: string;
|
|
138
|
+
text: string;
|
|
139
|
+
values?: unknown[];
|
|
140
|
+
};
|
|
141
|
+
/**
|
|
142
|
+
* A {@link PgCompatPool} backed by an `mssql` ConnectionPool. Non-transaction
|
|
143
|
+
* queries run on a fresh pooled `Request`; `connect()` returns a
|
|
144
|
+
* {@link MssqlTxClient} that drives a single transaction through the mssql
|
|
145
|
+
* `Transaction` API so `BEGIN`/`COMMIT`/`ROLLBACK`/savepoints all run on the same
|
|
146
|
+
* physical connection.
|
|
147
|
+
*/
|
|
148
|
+
export declare class MssqlPool implements PgCompatPool {
|
|
149
|
+
/** The underlying `mssql` ConnectionPool — exposed as an escape hatch (seed / DDL / advanced ops). */
|
|
150
|
+
readonly pool: MssqlConnectionPool;
|
|
151
|
+
private readonly sqlNS;
|
|
152
|
+
private closed;
|
|
153
|
+
constructor(pool: MssqlConnectionPool, sqlNS: MssqlModule);
|
|
154
|
+
query(text: QueryArg, values?: unknown[]): Promise<any>;
|
|
155
|
+
connect(): Promise<PgCompatPoolClient>;
|
|
156
|
+
end(): Promise<void>;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Map a SQL Server column type to a TypeScript type. `dialectType` is the
|
|
160
|
+
* `INFORMATION_SCHEMA.COLUMNS.DATA_TYPE` (lowercase, e.g. `bigint`, `nvarchar`,
|
|
161
|
+
* `datetime2`, `bit`, `uniqueidentifier`).
|
|
162
|
+
*/
|
|
163
|
+
export declare function mssqlTypeToTs(dialectType: string, nullable: boolean): string;
|
|
164
|
+
/**
|
|
165
|
+
* SQL Server 2016+ implementation of the {@link Dialect} contract. Bracket
|
|
166
|
+
* identifier quoting (`[…]`), named `@pN` placeholders, the `FOR JSON PATH`
|
|
167
|
+
* nested-relation override (no `json_agg`), no `RETURNING`
|
|
168
|
+
* (`resultStrategy = 'output'` via `OUTPUT INSERTED.*` / `MERGE`), `OFFSET/FETCH`
|
|
169
|
+
* paging, and the Postgres-only capabilities disabled (vector / LISTEN-NOTIFY /
|
|
170
|
+
* RLS).
|
|
171
|
+
*/
|
|
172
|
+
export declare const mssqlDialect: Dialect;
|
|
173
|
+
/** Async executor that returns plain row objects for a parameterized (`@pN`) query. */
|
|
174
|
+
export type MssqlRowExecutor = (sql: string, params: unknown[]) => Promise<Record<string, unknown>[]>;
|
|
175
|
+
/**
|
|
176
|
+
* Introspect a SQL Server database into the same {@link SchemaMetadata} shape the
|
|
177
|
+
* Postgres catalog introspector produces, using a caller-supplied query executor
|
|
178
|
+
* (so tests can dogfood an already-open `mssql` pool/connection). Reads
|
|
179
|
+
* `INFORMATION_SCHEMA.*` plus `sys.identity_columns` / `sys.foreign_keys` /
|
|
180
|
+
* `sys.indexes`.
|
|
181
|
+
*
|
|
182
|
+
* @param exec Runs a parameterized (`@p1`, `@p2`, …) query and returns rows.
|
|
183
|
+
* @param schema The SQL Server schema to introspect (default `dbo`).
|
|
184
|
+
*/
|
|
185
|
+
export declare function introspectMssqlWith(exec: MssqlRowExecutor, schema?: string, options?: {
|
|
186
|
+
include?: string[];
|
|
187
|
+
exclude?: string[];
|
|
188
|
+
}): Promise<SchemaMetadata>;
|
|
189
|
+
/**
|
|
190
|
+
* Open a short-lived `mssql` connection from `options.connectionString`, introspect
|
|
191
|
+
* the database (schema = `options.schema` or `dbo`), and close it. Wraps
|
|
192
|
+
* {@link introspectMssqlWith} for the {@link DialectIntrospector} seam used by
|
|
193
|
+
* `introspect()` / `npx turbine generate`.
|
|
194
|
+
*/
|
|
195
|
+
export declare function introspectMssql(options: IntrospectOptions): Promise<SchemaMetadata>;
|
|
196
|
+
interface MssqlConnectionConfig {
|
|
197
|
+
server?: string;
|
|
198
|
+
port?: number;
|
|
199
|
+
user?: string;
|
|
200
|
+
password?: string;
|
|
201
|
+
database?: string;
|
|
202
|
+
options?: {
|
|
203
|
+
encrypt?: boolean;
|
|
204
|
+
trustServerCertificate?: boolean;
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
/** Options for {@link turbineMssql}. Mirrors the relevant {@link TurbineConfig} fields. */
|
|
208
|
+
export interface TurbineMssqlOptions extends Pick<TurbineConfig, 'logging' | 'defaultLimit' | 'warnOnUnlimited'> {
|
|
209
|
+
/** SQL Server schema for introspection / DDL (default `dbo`). */
|
|
210
|
+
schema?: string;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Create a {@link TurbineClient} bound to SQL Server 2016+ via `mssql`.
|
|
214
|
+
*
|
|
215
|
+
* Pass one of:
|
|
216
|
+
* - a connection string (`'mssql://sa:pass@host:1433/db'`),
|
|
217
|
+
* - an `mssql` config object (`{ server, user, password, database, options }`),
|
|
218
|
+
* - an existing `MssqlPool` (injection — you own its lifecycle, `disconnect()` is
|
|
219
|
+
* a no-op).
|
|
220
|
+
*
|
|
221
|
+
* When Turbine builds the pool (string/config), it probes
|
|
222
|
+
* `SERVERPROPERTY('ProductMajorVersion')` to reject SQL Server < 2016, and
|
|
223
|
+
* `disconnect()` closes the pool it created.
|
|
224
|
+
*
|
|
225
|
+
* @example
|
|
226
|
+
* ```ts
|
|
227
|
+
* import { turbineMssql } from 'turbine-orm/mssql';
|
|
228
|
+
* const db = await turbineMssql('mssql://sa:Passw0rd!@localhost:1433/app', SCHEMA);
|
|
229
|
+
* ```
|
|
230
|
+
*/
|
|
231
|
+
export declare function turbineMssql(target: string | MssqlConnectionConfig | MssqlPool, schema: SchemaMetadata, options?: TurbineMssqlOptions): Promise<TurbineClient>;
|
|
232
|
+
export {};
|
package/dist/cjs/mssql.js
CHANGED
|
@@ -491,6 +491,12 @@ exports.mssqlDialect = {
|
|
|
491
491
|
// round-trip (Phase-0 'output' strategy, executed like 'returning').
|
|
492
492
|
resultStrategy: 'output',
|
|
493
493
|
supportsReturning: false,
|
|
494
|
+
// MERGE can carry `WHEN MATCHED AND <pred>`, but the predicate the builder
|
|
495
|
+
// hands over is written in UNQUALIFIED column references, which are ambiguous
|
|
496
|
+
// between the MERGE target (T) and source (S) aliases. `buildUpsertStatement`
|
|
497
|
+
// below therefore emits no predicate; reporting false keeps the builder from
|
|
498
|
+
// compiling one (and from binding its now-orphaned parameters).
|
|
499
|
+
supportsUpsertUpdateWhere: false,
|
|
494
500
|
supportsILike: false,
|
|
495
501
|
supportsVector: false,
|
|
496
502
|
// SQL Server full-text is `CONTAINS`/`FREETEXT` over a full-text catalog: a
|