turbine-orm 0.29.0 → 0.31.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 +1 -1
- package/dist/cjs/cli/index.js +5 -0
- package/dist/cjs/cli/mcp.js +22 -92
- package/dist/cjs/client.js +47 -6
- package/dist/cjs/generate.js +71 -25
- package/dist/cjs/index.js +4 -1
- package/dist/cjs/introspect.js +350 -120
- package/dist/cjs/mssql.js +42 -136
- package/dist/cjs/mysql.js +16 -129
- package/dist/cjs/optional-peer-import.cjs +122 -0
- package/dist/cjs/powdb.js +579 -89
- package/dist/cjs/powql.js +56 -26
- package/dist/cjs/query/builder.js +601 -86
- package/dist/cjs/query/filters.js +80 -2
- package/dist/cjs/schema-metadata.js +316 -0
- package/dist/cjs/sqlite.js +8 -89
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +5 -0
- package/dist/cli/mcp.d.ts +18 -0
- package/dist/cli/mcp.js +22 -93
- package/dist/client.d.ts +19 -2
- package/dist/client.js +47 -6
- package/dist/generate.d.ts +16 -4
- package/dist/generate.js +71 -25
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -0
- package/dist/introspect.d.ts +94 -1
- package/dist/introspect.js +345 -120
- package/dist/mssql.js +40 -104
- package/dist/mysql.js +14 -97
- package/dist/optional-peer-import.cjs +89 -0
- package/dist/optional-peer-import.d.cts +53 -0
- package/dist/powdb.d.ts +118 -23
- package/dist/powdb.js +574 -88
- package/dist/powql.d.ts +6 -0
- package/dist/powql.js +58 -28
- package/dist/query/builder.d.ts +145 -8
- package/dist/query/builder.js +602 -87
- package/dist/query/deferred.d.ts +7 -2
- package/dist/query/filters.d.ts +46 -1
- package/dist/query/filters.js +76 -1
- package/dist/query/index.d.ts +1 -1
- package/dist/query/types.d.ts +85 -11
- package/dist/schema-metadata.d.ts +77 -0
- package/dist/schema-metadata.js +313 -0
- package/dist/schema.d.ts +10 -0
- package/dist/sqlite.js +9 -90
- package/package.json +3 -3
package/dist/query/deferred.d.ts
CHANGED
|
@@ -66,9 +66,14 @@ export interface QueryInterfaceOptions {
|
|
|
66
66
|
* without a `limit`. Defaults to `true` so that accidental unbounded
|
|
67
67
|
* queries are surfaced loudly during development. Pass `false` to silence
|
|
68
68
|
* the warning entirely (e.g. for CLI tooling that intentionally streams
|
|
69
|
-
* full tables)
|
|
69
|
+
* full tables), or a per-table map (`{ userProfiles: false }`) to silence
|
|
70
|
+
* only the tables that intentionally read full sets — unlisted tables keep
|
|
71
|
+
* the default. Map keys accept BOTH the camelCase accessor name
|
|
72
|
+
* (`userProfiles`) and the snake_case table name (`user_profiles`); the
|
|
73
|
+
* snake_case entry wins if both are present. Individual calls can also
|
|
74
|
+
* override via `findMany({ warnOnUnlimited: false })`.
|
|
70
75
|
*/
|
|
71
|
-
warnOnUnlimited?: boolean
|
|
76
|
+
warnOnUnlimited?: boolean | Record<string, boolean>;
|
|
72
77
|
/**
|
|
73
78
|
* Enable prepared statements. When true, queries are submitted with a
|
|
74
79
|
* `{ name, text, values }` object to the pg driver, which caches the
|
package/dist/query/filters.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* compiler. Kept out of builder.ts so the class file stays about SQL assembly
|
|
6
6
|
* and execution rather than filter-shape bookkeeping.
|
|
7
7
|
*/
|
|
8
|
-
import type { ArrayFilter, JsonFilter, OrderBySpec, OrderDirection, TextSearchFilter, VectorFilter, VectorOrderBy, WhereOperator } from './types.js';
|
|
8
|
+
import type { ArrayFilter, ColumnRef, JsonFilter, JsonPathOrderBy, OrderBySpec, OrderDirection, TextSearchFilter, VectorFilter, VectorOrderBy, WhereOperator } from './types.js';
|
|
9
9
|
/** Check if a value is a where operator object (has at least one known operator key) */
|
|
10
10
|
export declare function isWhereOperator(value: unknown): value is WhereOperator;
|
|
11
11
|
/**
|
|
@@ -15,11 +15,30 @@ export declare function isWhereOperator(value: unknown): value is WhereOperator;
|
|
|
15
15
|
* bind values and return false, as do arrays and Dates.
|
|
16
16
|
*/
|
|
17
17
|
export declare function isUnmatchedPlainObject(value: unknown): boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Operator keys that accept a {@link ColumnRef} (`{ col: 'otherField' }`)
|
|
20
|
+
* value for column-to-column comparison. `in`/`notIn` and the LIKE operators
|
|
21
|
+
* take values only.
|
|
22
|
+
*/
|
|
23
|
+
export declare const COLUMN_REF_OPERATORS: Set<string>;
|
|
24
|
+
/**
|
|
25
|
+
* Check if an operator value is a column reference: a plain object whose ONLY
|
|
26
|
+
* key is `col` with a string value. Anything else (extra keys, non-string
|
|
27
|
+
* `col`) is treated as a plain value so JSON payloads that merely contain a
|
|
28
|
+
* `col` property keep their equality meaning.
|
|
29
|
+
*/
|
|
30
|
+
export declare function isColumnRef(value: unknown): value is ColumnRef;
|
|
18
31
|
/**
|
|
19
32
|
* Fingerprint the SHAPE of a where-operator object. Null-valued `equals` /
|
|
20
33
|
* `not` compile to parameterless `IS NULL` / `IS NOT NULL` (different SQL, no
|
|
21
34
|
* param pushed), so null-ness is part of the shape — without it a cache entry
|
|
22
35
|
* warmed by `{ not: 5 }` would serve `{ not: null }` with a desynced param list.
|
|
36
|
+
*
|
|
37
|
+
* Column references ({@link ColumnRef}) compile the referenced column into the
|
|
38
|
+
* SQL TEXT (no param bound), so the referenced field name is part of the shape
|
|
39
|
+
*: `{ equals: { col: 'a' } }` and `{ equals: { col: 'b' } }` must never share
|
|
40
|
+
* a cache entry. The name is JSON-encoded so exotic field names cannot collide
|
|
41
|
+
* with other fingerprint tokens.
|
|
23
42
|
*/
|
|
24
43
|
export declare function fingerprintOperatorShape(value: WhereOperator): string;
|
|
25
44
|
/**
|
|
@@ -48,6 +67,24 @@ export declare function sortedEntries<V>(obj: Record<string, V>): [string, V][];
|
|
|
48
67
|
export declare const UPDATE_OPERATOR_KEYS: Set<string>;
|
|
49
68
|
/** Known JSONB operator keys */
|
|
50
69
|
export declare const JSONB_OPERATOR_KEYS: Set<string>;
|
|
70
|
+
/**
|
|
71
|
+
* JSON range comparison operators → SQL comparison tokens, in the FIXED order
|
|
72
|
+
* the build and collect paths iterate them. These keys are deliberately NOT in
|
|
73
|
+
* {@link JSONB_OPERATOR_KEYS}: `gt`/`gte`/`lt`/`lte` overlap with
|
|
74
|
+
* `WhereOperator`, so a bare `{ gt: 5 }` must keep its column-comparison
|
|
75
|
+
* meaning. They only compile as JSON range ops when the object is already a
|
|
76
|
+
* {@link JsonFilter} (detected via `path` / `equals` / `contains` / `hasKey`),
|
|
77
|
+
* and they always require `path`.
|
|
78
|
+
*/
|
|
79
|
+
export declare const JSON_RANGE_OPERATORS: Record<'gt' | 'gte' | 'lt' | 'lte', string>;
|
|
80
|
+
/**
|
|
81
|
+
* Value-invariant shape fingerprint for a {@link JsonFilter}. Range operators
|
|
82
|
+
* are annotated with the comparison value's kind (`#n` numeric / `#s` string)
|
|
83
|
+
* because a numeric comparison compiles to a `::numeric` cast — a different
|
|
84
|
+
* SQL text than the text comparison — so the two must never share a cached
|
|
85
|
+
* SQL entry.
|
|
86
|
+
*/
|
|
87
|
+
export declare function fingerprintJsonFilterShape(filter: JsonFilter): string;
|
|
51
88
|
/**
|
|
52
89
|
* JSONB operator keys that are *unique* to {@link JsonFilter} — they cannot
|
|
53
90
|
* appear in any other where-filter shape, so the presence of one of these is
|
|
@@ -109,6 +146,14 @@ export declare function isVectorFilter(value: unknown): value is VectorFilter;
|
|
|
109
146
|
export declare function isVectorOrderBy(value: unknown): value is VectorOrderBy;
|
|
110
147
|
/** Check if an orderBy value is an explicit `{ sort, nulls? }` spec. */
|
|
111
148
|
export declare function isOrderBySpec(value: unknown): value is OrderBySpec;
|
|
149
|
+
/**
|
|
150
|
+
* Check if an orderBy value is a JSON-path ordering: `{ path: [...] }` with an
|
|
151
|
+
* ARRAY path. The array requirement disambiguates from relation orderBy values
|
|
152
|
+
* (whose entries are directions/specs keyed by target column: a target column
|
|
153
|
+
* literally named `path` maps to a string direction, never an array), and the
|
|
154
|
+
* `distance`/`sort` exclusions keep vector and spec shapes out.
|
|
155
|
+
*/
|
|
156
|
+
export declare function isJsonPathOrderBy(value: unknown): value is JsonPathOrderBy;
|
|
112
157
|
/**
|
|
113
158
|
* Normalize an orderBy value into `{ direction, nulls }`. Accepts a plain
|
|
114
159
|
* direction string or an {@link OrderBySpec}. Used by every ORDER BY compile
|
package/dist/query/filters.js
CHANGED
|
@@ -36,17 +36,48 @@ export function isUnmatchedPlainObject(value) {
|
|
|
36
36
|
const proto = Object.getPrototypeOf(value);
|
|
37
37
|
return proto === Object.prototype || proto === null;
|
|
38
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* Operator keys that accept a {@link ColumnRef} (`{ col: 'otherField' }`)
|
|
41
|
+
* value for column-to-column comparison. `in`/`notIn` and the LIKE operators
|
|
42
|
+
* take values only.
|
|
43
|
+
*/
|
|
44
|
+
export const COLUMN_REF_OPERATORS = new Set(['equals', 'not', 'gt', 'gte', 'lt', 'lte']);
|
|
45
|
+
/**
|
|
46
|
+
* Check if an operator value is a column reference: a plain object whose ONLY
|
|
47
|
+
* key is `col` with a string value. Anything else (extra keys, non-string
|
|
48
|
+
* `col`) is treated as a plain value so JSON payloads that merely contain a
|
|
49
|
+
* `col` property keep their equality meaning.
|
|
50
|
+
*/
|
|
51
|
+
export function isColumnRef(value) {
|
|
52
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value) || value instanceof Date)
|
|
53
|
+
return false;
|
|
54
|
+
const keys = Object.keys(value);
|
|
55
|
+
return keys.length === 1 && keys[0] === 'col' && typeof value.col === 'string';
|
|
56
|
+
}
|
|
39
57
|
/**
|
|
40
58
|
* Fingerprint the SHAPE of a where-operator object. Null-valued `equals` /
|
|
41
59
|
* `not` compile to parameterless `IS NULL` / `IS NOT NULL` (different SQL, no
|
|
42
60
|
* param pushed), so null-ness is part of the shape — without it a cache entry
|
|
43
61
|
* warmed by `{ not: 5 }` would serve `{ not: null }` with a desynced param list.
|
|
62
|
+
*
|
|
63
|
+
* Column references ({@link ColumnRef}) compile the referenced column into the
|
|
64
|
+
* SQL TEXT (no param bound), so the referenced field name is part of the shape
|
|
65
|
+
*: `{ equals: { col: 'a' } }` and `{ equals: { col: 'b' } }` must never share
|
|
66
|
+
* a cache entry. The name is JSON-encoded so exotic field names cannot collide
|
|
67
|
+
* with other fingerprint tokens.
|
|
44
68
|
*/
|
|
45
69
|
export function fingerprintOperatorShape(value) {
|
|
46
70
|
const obj = value;
|
|
47
71
|
const opKeys = Object.keys(obj)
|
|
48
72
|
.filter((k) => k !== 'mode')
|
|
49
|
-
.map((k) =>
|
|
73
|
+
.map((k) => {
|
|
74
|
+
const v = obj[k];
|
|
75
|
+
if ((k === 'equals' || k === 'not') && v === null)
|
|
76
|
+
return `${k}:null`;
|
|
77
|
+
if (COLUMN_REF_OPERATORS.has(k) && isColumnRef(v))
|
|
78
|
+
return `${k}:col(${JSON.stringify(v.col)})`;
|
|
79
|
+
return k;
|
|
80
|
+
})
|
|
50
81
|
.sort();
|
|
51
82
|
const modeStr = value.mode === 'insensitive' ? ':i' : '';
|
|
52
83
|
return `op(${opKeys.join(',')}${modeStr})`;
|
|
@@ -90,6 +121,36 @@ export function sortedEntries(obj) {
|
|
|
90
121
|
export const UPDATE_OPERATOR_KEYS = new Set(['set', 'increment', 'decrement', 'multiply', 'divide']);
|
|
91
122
|
/** Known JSONB operator keys */
|
|
92
123
|
export const JSONB_OPERATOR_KEYS = new Set(['path', 'equals', 'contains', 'hasKey']);
|
|
124
|
+
/**
|
|
125
|
+
* JSON range comparison operators → SQL comparison tokens, in the FIXED order
|
|
126
|
+
* the build and collect paths iterate them. These keys are deliberately NOT in
|
|
127
|
+
* {@link JSONB_OPERATOR_KEYS}: `gt`/`gte`/`lt`/`lte` overlap with
|
|
128
|
+
* `WhereOperator`, so a bare `{ gt: 5 }` must keep its column-comparison
|
|
129
|
+
* meaning. They only compile as JSON range ops when the object is already a
|
|
130
|
+
* {@link JsonFilter} (detected via `path` / `equals` / `contains` / `hasKey`),
|
|
131
|
+
* and they always require `path`.
|
|
132
|
+
*/
|
|
133
|
+
export const JSON_RANGE_OPERATORS = {
|
|
134
|
+
gt: '>',
|
|
135
|
+
gte: '>=',
|
|
136
|
+
lt: '<',
|
|
137
|
+
lte: '<=',
|
|
138
|
+
};
|
|
139
|
+
/**
|
|
140
|
+
* Value-invariant shape fingerprint for a {@link JsonFilter}. Range operators
|
|
141
|
+
* are annotated with the comparison value's kind (`#n` numeric / `#s` string)
|
|
142
|
+
* because a numeric comparison compiles to a `::numeric` cast — a different
|
|
143
|
+
* SQL text than the text comparison — so the two must never share a cached
|
|
144
|
+
* SQL entry.
|
|
145
|
+
*/
|
|
146
|
+
export function fingerprintJsonFilterShape(filter) {
|
|
147
|
+
const obj = filter;
|
|
148
|
+
const parts = Object.keys(obj)
|
|
149
|
+
.filter((k) => obj[k] !== undefined)
|
|
150
|
+
.sort()
|
|
151
|
+
.map((k) => (k in JSON_RANGE_OPERATORS ? `${k}#${typeof obj[k] === 'number' ? 'n' : 's'}` : k));
|
|
152
|
+
return `json(${parts.join(',')})`;
|
|
153
|
+
}
|
|
93
154
|
/**
|
|
94
155
|
* JSONB operator keys that are *unique* to {@link JsonFilter} — they cannot
|
|
95
156
|
* appear in any other where-filter shape, so the presence of one of these is
|
|
@@ -219,6 +280,20 @@ export function isVectorOrderBy(value) {
|
|
|
219
280
|
export function isOrderBySpec(value) {
|
|
220
281
|
return typeof value === 'object' && value !== null && !Array.isArray(value) && 'sort' in value;
|
|
221
282
|
}
|
|
283
|
+
/**
|
|
284
|
+
* Check if an orderBy value is a JSON-path ordering: `{ path: [...] }` with an
|
|
285
|
+
* ARRAY path. The array requirement disambiguates from relation orderBy values
|
|
286
|
+
* (whose entries are directions/specs keyed by target column: a target column
|
|
287
|
+
* literally named `path` maps to a string direction, never an array), and the
|
|
288
|
+
* `distance`/`sort` exclusions keep vector and spec shapes out.
|
|
289
|
+
*/
|
|
290
|
+
export function isJsonPathOrderBy(value) {
|
|
291
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value))
|
|
292
|
+
return false;
|
|
293
|
+
if ('distance' in value || 'sort' in value)
|
|
294
|
+
return false;
|
|
295
|
+
return Array.isArray(value.path);
|
|
296
|
+
}
|
|
222
297
|
/**
|
|
223
298
|
* Normalize an orderBy value into `{ direction, nulls }`. Accepts a plain
|
|
224
299
|
* direction string or an {@link OrderBySpec}. Used by every ORDER BY compile
|
package/dist/query/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* `import { … } from './query/index.js'` is a drop-in replacement for the
|
|
6
6
|
* former monolithic `import { … } from './query.js'`.
|
|
7
7
|
*/
|
|
8
|
-
export type { AggregateArgs, AggregateResult, ArrayFilter, ConnectOrCreateOp, CountArgs, CreateArgs, CreateDataInput, CreateManyArgs, DeleteArgs, DeleteManyArgs, FieldResult, FindManyArgs, FindManyStreamArgs, FindUniqueArgs, GlobalFilters, GroupByArgs, HavingClause, JsonFilter, NestedCreateOp, NestedUpdateOp, NestedUpdateOpItem, NestedUpsertOpItem, OmitResult, OrderByClause, OrderDirection, QueryResult, RelationDescriptor, RelationFilter, RelationLoadStrategy, SelectResult, SkipGlobalFilters, TextSearchFilter, TypedWithClause, UpdateArgs, UpdateDataInput, UpdateInput, UpdateManyArgs, UpdateOperatorInput, UpsertArgs, VectorDistanceFilter, VectorFilter, VectorMetric, VectorOrderBy, VectorOrderByDistance, WhereClause, WhereOperator, WhereValue, WithClause, WithOptions, WithResult, } from './types.js';
|
|
8
|
+
export type { AggregateArgs, AggregateResult, ArrayFilter, ColumnRef, ConnectOrCreateOp, CountArgs, CreateArgs, CreateDataInput, CreateManyArgs, DeleteArgs, DeleteManyArgs, FieldResult, FindManyArgs, FindManyStreamArgs, FindUniqueArgs, GlobalFilters, GroupByArgs, HavingClause, JsonFilter, JsonPathOrderBy, NestedCreateOp, NestedUpdateOp, NestedUpdateOpItem, NestedUpsertOpItem, OmitResult, OrderByClause, OrderDirection, QueryResult, RelationDescriptor, RelationFilter, RelationLoadStrategy, SelectResult, SkipGlobalFilters, TextSearchFilter, TypedWithClause, UpdateArgs, UpdateDataInput, UpdateInput, UpdateManyArgs, UpdateOperatorInput, UpsertArgs, VectorDistanceFilter, VectorFilter, VectorMetric, VectorOrderBy, VectorOrderByDistance, WhereClause, WhereOperator, WhereValue, WithClause, WithOptions, WithResult, } from './types.js';
|
|
9
9
|
export type { BuiltStatement, BulkInsertStatementInput, ColumnDefinitionInput, ColumnTypeInput, CreateIndexStatementInput, CreateTableStatementInput, Dialect, InsertStatementInput, UpsertStatementInput, } from '../dialect.js';
|
|
10
10
|
export { postgresDialect } from '../dialect.js';
|
|
11
11
|
export type { SqlCacheEntry } from './utils.js';
|
package/dist/query/types.d.ts
CHANGED
|
@@ -18,20 +18,48 @@ export type OrderDirection = 'asc' | 'desc';
|
|
|
18
18
|
* Precedence: per-query arg > client `relationLoadStrategy` config > `'join'`.
|
|
19
19
|
*/
|
|
20
20
|
export type RelationLoadStrategy = 'join' | 'batched';
|
|
21
|
+
/**
|
|
22
|
+
* Reference to ANOTHER COLUMN of the same table inside a where operator,
|
|
23
|
+
* enabling column-to-column comparison:
|
|
24
|
+
*
|
|
25
|
+
* ```ts
|
|
26
|
+
* where: { currentVersionId: { equals: { col: 'publishedVersionId' } } }
|
|
27
|
+
* // → WHERE "current_version_id" = "published_version_id"
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* Accepted by `equals`, `not`, `gt`, `gte`, `lt`, and `lte`. The referenced
|
|
31
|
+
* field resolves through the table's columnMap (camelCase accepted, same as a
|
|
32
|
+
* where key) and compiles to a quoted identifier: NO parameter is bound.
|
|
33
|
+
* An unknown referenced field throws {@link ValidationError} (E003).
|
|
34
|
+
*
|
|
35
|
+
* Notes:
|
|
36
|
+
* - `mode: 'insensitive'` cannot be combined with a column reference: it
|
|
37
|
+
* throws E003 (use `client.sql` for `lower(a) = lower(b)`).
|
|
38
|
+
* - On json/jsonb columns `equals` routes to the JSONB containment filter
|
|
39
|
+
* first, so `{ equals: { col } }` there is treated as a JSON value, not a
|
|
40
|
+
* column reference.
|
|
41
|
+
*
|
|
42
|
+
* `F` narrows the referenced name to the table's field names when the
|
|
43
|
+
* surrounding {@link WhereClause} knows the entity type.
|
|
44
|
+
*/
|
|
45
|
+
export interface ColumnRef<F extends string = string> {
|
|
46
|
+
col: F;
|
|
47
|
+
}
|
|
21
48
|
/** Operator object for advanced where filtering */
|
|
22
|
-
export interface WhereOperator<V = unknown> {
|
|
49
|
+
export interface WhereOperator<V = unknown, F extends string = string> {
|
|
23
50
|
/**
|
|
24
51
|
* Explicit equality: `{ equals: value }` → `column = $n`.
|
|
25
52
|
* `{ equals: null }` → `column IS NULL`.
|
|
53
|
+
* `{ equals: { col: 'otherField' } }` → `column = "other_field"` ({@link ColumnRef}).
|
|
26
54
|
* On json/jsonb columns `equals` routes to the JSONB containment filter
|
|
27
55
|
* ({@link JsonFilter}) instead.
|
|
28
56
|
*/
|
|
29
|
-
equals?: V | null;
|
|
30
|
-
gt?: V
|
|
31
|
-
gte?: V
|
|
32
|
-
lt?: V
|
|
33
|
-
lte?: V
|
|
34
|
-
not?: V | null;
|
|
57
|
+
equals?: V | ColumnRef<F> | null;
|
|
58
|
+
gt?: V | ColumnRef<F>;
|
|
59
|
+
gte?: V | ColumnRef<F>;
|
|
60
|
+
lt?: V | ColumnRef<F>;
|
|
61
|
+
lte?: V | ColumnRef<F>;
|
|
62
|
+
not?: V | ColumnRef<F> | null;
|
|
35
63
|
in?: V[];
|
|
36
64
|
notIn?: V[];
|
|
37
65
|
contains?: string;
|
|
@@ -50,7 +78,7 @@ export interface WhereOperator<V = unknown> {
|
|
|
50
78
|
* - A text search filter object ({ search, config? })
|
|
51
79
|
* - A vector distance filter object ({ distance: { to, metric, lt } }) for pgvector columns
|
|
52
80
|
*/
|
|
53
|
-
export type WhereValue<V = unknown> = (V extends Array<infer U> ? TypedRelationFilter<U> : V extends Date ? V : V extends object ? V | TypedToOneFilter<V> | WhereClause<V> : V) | WhereOperator<V> | JsonFilter | ArrayFilter | TextSearchFilter | VectorFilter | null;
|
|
81
|
+
export type WhereValue<V = unknown, F extends string = string> = (V extends Array<infer U> ? TypedRelationFilter<U> : V extends Date ? V : V extends object ? V | TypedToOneFilter<V> | WhereClause<V> : V) | WhereOperator<V, F> | JsonFilter | ArrayFilter | TextSearchFilter | VectorFilter | null;
|
|
54
82
|
/** Relation filter on a to-many relation property. */
|
|
55
83
|
export interface TypedRelationFilter<U> {
|
|
56
84
|
some?: WhereClause<U>;
|
|
@@ -71,7 +99,7 @@ export interface TypedToOneFilter<V> {
|
|
|
71
99
|
* Relation names can be used with some/every/none sub-filters.
|
|
72
100
|
*/
|
|
73
101
|
export type WhereClause<T> = {
|
|
74
|
-
[K in keyof T]?: WhereValue<T[K]
|
|
102
|
+
[K in keyof T]?: WhereValue<T[K], Extract<keyof T, string>>;
|
|
75
103
|
} & {
|
|
76
104
|
OR?: WhereClause<T>[];
|
|
77
105
|
AND?: WhereClause<T>[];
|
|
@@ -148,7 +176,7 @@ export type TypedWithClause<R extends object = {}> = [keyof R] extends [never] ?
|
|
|
148
176
|
export interface WithOptions<NestedR extends object = {}> {
|
|
149
177
|
with?: TypedWithClause<NestedR>;
|
|
150
178
|
where?: Record<string, unknown>;
|
|
151
|
-
orderBy?: Record<string, OrderDirection | OrderBySpec>;
|
|
179
|
+
orderBy?: Record<string, OrderDirection | OrderBySpec | JsonPathOrderBy | RelationOrderBy>;
|
|
152
180
|
limit?: number;
|
|
153
181
|
/** Only include these fields from the relation */
|
|
154
182
|
select?: Record<string, boolean>;
|
|
@@ -310,6 +338,13 @@ export interface FindManyArgs<T, R extends object = {}, W extends TypedWithClaus
|
|
|
310
338
|
relationLoadStrategy?: RelationLoadStrategy;
|
|
311
339
|
/** Opt out of configured {@link GlobalFilters}. See {@link SkipGlobalFilters}. */
|
|
312
340
|
skipGlobalFilters?: SkipGlobalFilters;
|
|
341
|
+
/**
|
|
342
|
+
* Per-call override of the unbounded-findMany warning. Pass `false` when
|
|
343
|
+
* this call intentionally reads the full table (the config-level
|
|
344
|
+
* `warnOnUnlimited` stays in effect for every other call); pass `true` to
|
|
345
|
+
* force the warning even when it is disabled in config.
|
|
346
|
+
*/
|
|
347
|
+
warnOnUnlimited?: boolean;
|
|
313
348
|
}
|
|
314
349
|
export interface FindManyStreamArgs<T, R extends object = {}, W extends TypedWithClause<R> = TypedWithClause<R>, S extends Record<string, boolean> | undefined = undefined, O extends Record<string, boolean> | undefined = undefined> extends FindManyArgs<T, R, W, S, O> {
|
|
315
350
|
/**
|
|
@@ -628,6 +663,18 @@ export interface JsonFilter {
|
|
|
628
663
|
contains?: unknown;
|
|
629
664
|
/** Key existence check: column ? key */
|
|
630
665
|
hasKey?: string;
|
|
666
|
+
/**
|
|
667
|
+
* Greater-than comparison of the value at `path` (required). Numbers cast
|
|
668
|
+
* the extracted text to numeric — `(col #>> path)::numeric > $n` — while
|
|
669
|
+
* strings compare as text.
|
|
670
|
+
*/
|
|
671
|
+
gt?: number | string;
|
|
672
|
+
/** Greater-than-or-equal comparison of the value at `path` (required). See {@link JsonFilter.gt}. */
|
|
673
|
+
gte?: number | string;
|
|
674
|
+
/** Less-than comparison of the value at `path` (required). See {@link JsonFilter.gt}. */
|
|
675
|
+
lt?: number | string;
|
|
676
|
+
/** Less-than-or-equal comparison of the value at `path` (required). See {@link JsonFilter.gt}. */
|
|
677
|
+
lte?: number | string;
|
|
631
678
|
}
|
|
632
679
|
/** Array query operators for where clauses */
|
|
633
680
|
export interface ArrayFilter {
|
|
@@ -722,6 +769,32 @@ export interface OrderBySpec {
|
|
|
722
769
|
sort: OrderDirection;
|
|
723
770
|
nulls?: 'first' | 'last';
|
|
724
771
|
}
|
|
772
|
+
/**
|
|
773
|
+
* Ordering by a JSON path on a json/jsonb column of the SAME table:
|
|
774
|
+
*
|
|
775
|
+
* ```ts
|
|
776
|
+
* orderBy: { data: { path: ['weight'], direction: 'asc', type: 'numeric' } }
|
|
777
|
+
* // → ORDER BY ("data" #>> $n::text[])::numeric ASC
|
|
778
|
+
* ```
|
|
779
|
+
*
|
|
780
|
+
* The path is bound as a single text[] parameter (never interpolated).
|
|
781
|
+
* Comparison rule: values extracted from the path compare as TEXT by default;
|
|
782
|
+
* pass `type: 'numeric'` to cast for numeric comparison (`::numeric` on
|
|
783
|
+
* PostgreSQL). The column must be json/jsonb: anything else throws
|
|
784
|
+
* {@link ValidationError} (E003). Non-Postgres engines route through the same
|
|
785
|
+
* dialect JSON-extract hook the JSON where-filters use. Cross-relation
|
|
786
|
+
* (lateral) JSON ordering is NOT supported: same-table columns only.
|
|
787
|
+
*/
|
|
788
|
+
export interface JsonPathOrderBy {
|
|
789
|
+
/** JSON path into the column (each element a key or array index). Bound as one text[] param. */
|
|
790
|
+
path: (string | number)[];
|
|
791
|
+
/** Sort direction. Defaults to `'asc'`. */
|
|
792
|
+
direction?: OrderDirection;
|
|
793
|
+
/** Comparison kind for the extracted value. Defaults to `'text'`; `'numeric'` adds a numeric cast. */
|
|
794
|
+
type?: 'numeric' | 'text';
|
|
795
|
+
/** NULLS placement (PostgreSQL / SQLite only: see {@link OrderBySpec}). */
|
|
796
|
+
nulls?: 'first' | 'last';
|
|
797
|
+
}
|
|
725
798
|
/**
|
|
726
799
|
* Ordering by a relation, keyed by the relation name in an {@link OrderByClause}:
|
|
727
800
|
*
|
|
@@ -738,9 +811,10 @@ export type RelationOrderBy = {
|
|
|
738
811
|
* An orderBy clause maps each key to one of:
|
|
739
812
|
* - a plain direction (`'asc'` / `'desc'`),
|
|
740
813
|
* - an {@link OrderBySpec} (`{ sort, nulls }`) for NULLS placement,
|
|
814
|
+
* - for json/jsonb columns, a JSON-path ordering ({@link JsonPathOrderBy}),
|
|
741
815
|
* - for pgvector columns, a KNN distance ordering ({@link VectorOrderBy}),
|
|
742
816
|
* - for a relation name, a {@link RelationOrderBy} (`_count` for to-many, a
|
|
743
817
|
* target column for to-one).
|
|
744
818
|
*/
|
|
745
|
-
export type OrderByClause = Record<string, OrderDirection | OrderBySpec | VectorOrderBy | RelationOrderBy>;
|
|
819
|
+
export type OrderByClause = Record<string, OrderDirection | OrderBySpec | JsonPathOrderBy | VectorOrderBy | RelationOrderBy>;
|
|
746
820
|
export {};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* turbine-orm — defineSchema() → SchemaMetadata bridge
|
|
3
|
+
*
|
|
4
|
+
* Converts a code-first {@link SchemaDef} (the output of `defineSchema()`)
|
|
5
|
+
* into the runtime {@link SchemaMetadata} shape that the query builder,
|
|
6
|
+
* `TurbineClient`, and the non-SQL engines consume — without touching a
|
|
7
|
+
* live database.
|
|
8
|
+
*
|
|
9
|
+
* Why this exists: the historical converter path (`introspect()` +
|
|
10
|
+
* `generate()`) requires a running SQL database, but code-first engines
|
|
11
|
+
* (PowDB, in-memory SQLite bootstraps, tests) only have the `SchemaDef`.
|
|
12
|
+
* `schemaDefToMetadata()` is the pure-function equivalent: its output
|
|
13
|
+
* matches what `turbine generate` would emit into `metadata.ts` for the
|
|
14
|
+
* same schema, minus the pieces only a live catalog can know (real index
|
|
15
|
+
* names, constraint names, view flags).
|
|
16
|
+
*
|
|
17
|
+
* Parity notes (ground truth = introspect.ts + generate.ts):
|
|
18
|
+
* - Relations are derived from `references:` exactly like introspection
|
|
19
|
+
* derives them from foreign keys: a `belongsTo` on the child table and
|
|
20
|
+
* a `hasMany` on the parent, with the same disambiguation rules when
|
|
21
|
+
* multiple FKs point at the same target.
|
|
22
|
+
* - Pure junction tables (2-column composite PK that IS the two
|
|
23
|
+
* single-column FKs to two distinct tables, no payload columns) get
|
|
24
|
+
* the same conservative auto-`manyToMany` treatment as introspection.
|
|
25
|
+
* - Explicit `manyToMany` declarations on the SchemaDef are merged via
|
|
26
|
+
* {@link applyManyToManyRelations} (additive, never clobbering).
|
|
27
|
+
* - `indexes` is always `[]` — SchemaDef cannot express indexes, and an
|
|
28
|
+
* empty list keeps `schemaHasIndexInfo()` false so the index advisor
|
|
29
|
+
* and the dev-mode missing-index warning stay silent instead of
|
|
30
|
+
* producing blanket false positives.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```ts
|
|
34
|
+
* import { defineSchema, schemaDefToMetadata } from 'turbine-orm';
|
|
35
|
+
*
|
|
36
|
+
* const def = defineSchema({
|
|
37
|
+
* users: { id: { type: 'serial', primaryKey: true }, name: { type: 'text', notNull: true } },
|
|
38
|
+
* posts: { id: { type: 'serial', primaryKey: true },
|
|
39
|
+
* userId: { type: 'integer', notNull: true, references: 'users.id' } },
|
|
40
|
+
* });
|
|
41
|
+
* const metadata = schemaDefToMetadata(def);
|
|
42
|
+
* // → usable anywhere SchemaMetadata is expected (e.g. turbinePowDB, TurbineClient)
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
import { type SchemaMetadata } from './schema.js';
|
|
46
|
+
import { type SchemaDef } from './schema-builder.js';
|
|
47
|
+
/**
|
|
48
|
+
* Convert a code-first {@link SchemaDef} into runtime {@link SchemaMetadata}.
|
|
49
|
+
*
|
|
50
|
+
* Pure function — no database connection, no side effects, input untouched.
|
|
51
|
+
* The output is shaped identically to the `SCHEMA` constant `turbine generate`
|
|
52
|
+
* emits from introspection, so it can be handed to any consumer that expects
|
|
53
|
+
* introspected metadata: `new TurbineClient(config, metadata)`,
|
|
54
|
+
* `turbinePowDB(..., metadata)`, `QueryInterface`, the index advisor, etc.
|
|
55
|
+
*
|
|
56
|
+
* What maps:
|
|
57
|
+
* - Columns → full {@link ColumnMetadata} (snake_case name, camelCase field,
|
|
58
|
+
* pg type names, TS types, nullability, defaults, `isGenerated` for
|
|
59
|
+
* serial/bigserial, array + varchar length info, date-column tracking).
|
|
60
|
+
* - Column-level `primaryKey` and table-level composite `primaryKey`.
|
|
61
|
+
* - `unique: true` columns → single-column `uniqueColumns` entries.
|
|
62
|
+
* - `references:` FKs → `belongsTo` (child) + `hasMany` (parent) relations,
|
|
63
|
+
* including `onDelete`/`onUpdate` actions (the `'no action'` default is
|
|
64
|
+
* omitted, matching introspection).
|
|
65
|
+
* - Pure junction tables → auto-detected `manyToMany` relations (same
|
|
66
|
+
* conservative rules as introspection).
|
|
67
|
+
* - Explicit `manyToMany` declarations → merged additively.
|
|
68
|
+
* - Schema-level `enums`.
|
|
69
|
+
*
|
|
70
|
+
* What SchemaDef cannot express (and how it degrades):
|
|
71
|
+
* - Indexes → every table gets `indexes: []`, which keeps
|
|
72
|
+
* `schemaHasIndexInfo()` false so index-advisor consumers produce no
|
|
73
|
+
* false positives on code-first metadata.
|
|
74
|
+
* - Views → never marked (`isView` is introspection-only).
|
|
75
|
+
* - Composite foreign keys → `references:` is single-column by design.
|
|
76
|
+
*/
|
|
77
|
+
export declare function schemaDefToMetadata(def: SchemaDef): SchemaMetadata;
|