turbine-orm 0.70.0 → 0.71.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 +164 -1041
- package/dist/cjs/cli/compile-query.d.ts +198 -0
- package/dist/cjs/cli/compile-query.js +529 -0
- package/dist/cjs/cli/index.d.ts +25 -1
- package/dist/cjs/cli/index.js +49 -1
- package/dist/cjs/cli/mcp.js +198 -16
- package/dist/cjs/client.d.ts +45 -10
- package/dist/cjs/client.js +21 -3
- package/dist/cjs/connection-url.d.ts +160 -0
- package/dist/cjs/connection-url.js +296 -0
- package/dist/cjs/index-stats.d.ts +4 -1
- package/dist/cjs/index-stats.js +27 -11
- package/dist/cjs/index.d.ts +1 -1
- package/dist/cjs/plan-flip-probe.js +17 -1
- package/dist/cjs/powql.d.ts +1 -0
- package/dist/cjs/powql.js +9 -0
- package/dist/cjs/query/builder.d.ts +133 -2
- package/dist/cjs/query/builder.js +288 -64
- package/dist/cjs/query/deferred.d.ts +12 -6
- package/dist/cjs/query/index.d.ts +1 -1
- package/dist/cjs/query/option-surface.js +6 -0
- package/dist/cjs/query/types.d.ts +47 -0
- package/dist/cjs/query/where.d.ts +11 -2
- package/dist/cli/compile-query.d.ts +198 -0
- package/dist/cli/compile-query.js +522 -0
- package/dist/cli/index.d.ts +25 -1
- package/dist/cli/index.js +48 -1
- package/dist/cli/mcp.js +198 -16
- package/dist/client.d.ts +45 -10
- package/dist/client.js +19 -1
- package/dist/connection-url.d.ts +160 -0
- package/dist/connection-url.js +289 -0
- package/dist/index-stats.d.ts +4 -1
- package/dist/index-stats.js +27 -11
- package/dist/index.d.ts +1 -1
- package/dist/plan-flip-probe.js +17 -1
- package/dist/powql.d.ts +1 -0
- package/dist/powql.js +9 -0
- package/dist/query/builder.d.ts +133 -2
- package/dist/query/builder.js +288 -64
- package/dist/query/deferred.d.ts +12 -6
- package/dist/query/index.d.ts +1 -1
- package/dist/query/option-surface.js +6 -0
- package/dist/query/types.d.ts +47 -0
- package/dist/query/where.d.ts +11 -2
- package/package.json +8 -6
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* turbine-orm CLI: compile a read query WITHOUT executing it.
|
|
3
|
+
*
|
|
4
|
+
* Turbine already separates BUILD from EXECUTE: every `build*()` method on
|
|
5
|
+
* `QueryInterface` returns a {@link DeferredQuery} (`{ sql, params, transform,
|
|
6
|
+
* tag }`) and nothing runs until something calls `execute()`. This module is
|
|
7
|
+
* that seam turned into a tool: hand it schema metadata and a set of `findMany`
|
|
8
|
+
* -shaped args, get back the exact statement the ORM would send, the bound
|
|
9
|
+
* parameter list, and a read of whether the query is a mistake.
|
|
10
|
+
*
|
|
11
|
+
* ZERO DATABASE ACCESS, ENFORCED BY CONSTRUCTION RATHER THAN BY REVIEW.
|
|
12
|
+
* `QueryInterface` takes a pool in its constructor and never touches it on the
|
|
13
|
+
* build path, so this module hands it {@link SEALED_POOL}, whose every method
|
|
14
|
+
* THROWS. That is the whole guarantee: a future build path that grew a query
|
|
15
|
+
* would fail loudly here instead of quietly opening a connection, and the
|
|
16
|
+
* "compiles nothing, runs nothing" claim in the tool description does not
|
|
17
|
+
* depend on anyone re-reading the builder. The caller is responsible for
|
|
18
|
+
* obtaining `SchemaMetadata`; this module reads no catalog of its own.
|
|
19
|
+
*
|
|
20
|
+
* WHAT IT REFUSES TO DO. Read operations only, and that is a property of this
|
|
21
|
+
* module, not of its caller: {@link COMPILE_OPERATIONS} is the closed set, and
|
|
22
|
+
* a name outside it never reaches a builder. A write's SQL text is arguably as
|
|
23
|
+
* harmless to display as a read's, but the TOOL SURFACE is the security
|
|
24
|
+
* boundary on an agent-facing server, and "we also compile deletes, we just
|
|
25
|
+
* don't run them" is an argument, where "there is no write path" is a fact.
|
|
26
|
+
*
|
|
27
|
+
* A COMPILE FAILURE IS AN ANSWER, NOT AN ERROR. `where: { titel: 'x' }` throws
|
|
28
|
+
* `ValidationError` (E003) and `with: { autor: true }` throws `RelationError`
|
|
29
|
+
* (E005); both are exactly what the caller asked to find out, one round trip
|
|
30
|
+
* before the code is written. So a `TurbineError` out of the builder comes back
|
|
31
|
+
* as a successful result carrying the code, the message, the docs URL and the
|
|
32
|
+
* catalog's causes/fixes. Anything that is NOT a TurbineError propagates: it is
|
|
33
|
+
* a bug or a malformed request, and dressing it up as a query verdict would
|
|
34
|
+
* hide it.
|
|
35
|
+
*/
|
|
36
|
+
import type { PgCompatPool } from '../pg-types.js';
|
|
37
|
+
import type { RelationLoadStrategy } from '../query/index.js';
|
|
38
|
+
import type { RelationDef, SchemaMetadata, TableMetadata } from '../schema.js';
|
|
39
|
+
/**
|
|
40
|
+
* The read operations this module compiles. A CLOSED set, checked before any
|
|
41
|
+
* builder is reached, which is what keeps the read-only stance a fact rather
|
|
42
|
+
* than a convention: there is no branch here that reaches `buildCreate`,
|
|
43
|
+
* `buildUpdate`, `buildDelete` or `buildUpsert`.
|
|
44
|
+
*/
|
|
45
|
+
export declare const COMPILE_OPERATIONS: readonly ["findMany", "findUnique", "findFirst", "count", "aggregate", "groupBy"];
|
|
46
|
+
export type CompileOperation = (typeof COMPILE_OPERATIONS)[number];
|
|
47
|
+
/** True for the two operations whose args are NOT findMany-shaped. */
|
|
48
|
+
export declare function isAggregateShaped(operation: CompileOperation): boolean;
|
|
49
|
+
/**
|
|
50
|
+
* A pool that refuses every call.
|
|
51
|
+
*
|
|
52
|
+
* `QueryInterface`'s constructor requires one, and the build path never uses
|
|
53
|
+
* it. Passing a sealed object rather than the caller's real pool is what makes
|
|
54
|
+
* "this compiles and does not execute" structurally true: there is no live
|
|
55
|
+
* connection in scope for a statement to escape down, and a future build path
|
|
56
|
+
* that tried to read something would throw a message naming this file rather
|
|
57
|
+
* than silently issuing a query on an agent's behalf.
|
|
58
|
+
*/
|
|
59
|
+
export declare const SEALED_POOL: PgCompatPool;
|
|
60
|
+
/**
|
|
61
|
+
* Query-arg keys that name a column or an ordering over one, in any of the six
|
|
62
|
+
* operations. Used only by the caller's fail-closed check for an unreadable PII
|
|
63
|
+
* tag scan: with no trustworthy tag list, a compile carrying any of these
|
|
64
|
+
* cannot be shown not to name a hidden column.
|
|
65
|
+
*
|
|
66
|
+
* `select` / `omit` / `with` are deliberately absent, matching the guard's own
|
|
67
|
+
* rule: they return values, and this tool returns no values at all.
|
|
68
|
+
*/
|
|
69
|
+
export declare const COLUMN_NAMING_ARG_KEYS: readonly string[];
|
|
70
|
+
/** Does this args object carry a key that names a column? */
|
|
71
|
+
export declare function carriesColumnNamingArg(args: Record<string, unknown>): boolean;
|
|
72
|
+
/** One relation reached by the query's `with` clause. */
|
|
73
|
+
export interface CompiledRelation {
|
|
74
|
+
/** Dotted path from the queried table, e.g. `author.org`. */
|
|
75
|
+
path: string;
|
|
76
|
+
name: string;
|
|
77
|
+
type: RelationDef['type'];
|
|
78
|
+
from: string;
|
|
79
|
+
to: string;
|
|
80
|
+
/** 1 for a top-level `with` entry. */
|
|
81
|
+
depth: number;
|
|
82
|
+
/** hasMany / manyToMany return arrays; hasOne / belongsTo return one row or null. */
|
|
83
|
+
returnsArray: boolean;
|
|
84
|
+
/** Per-relation `limit`, when the caller set one. */
|
|
85
|
+
limit: number | null;
|
|
86
|
+
/**
|
|
87
|
+
* The table + column(s) this relation probes per parent row, when the probe
|
|
88
|
+
* has no index to serve it. `null` means indexed, or that the schema carries
|
|
89
|
+
* no index information to judge against (see `schemaHasIndexInfo`).
|
|
90
|
+
*/
|
|
91
|
+
unindexedProbe: {
|
|
92
|
+
table: string;
|
|
93
|
+
columns: string[];
|
|
94
|
+
createSql: string;
|
|
95
|
+
} | null;
|
|
96
|
+
}
|
|
97
|
+
export interface CompiledAdvice {
|
|
98
|
+
code: string;
|
|
99
|
+
severity: 'warn' | 'info';
|
|
100
|
+
message: string;
|
|
101
|
+
}
|
|
102
|
+
export interface CompiledQueryError {
|
|
103
|
+
code: string;
|
|
104
|
+
message: string;
|
|
105
|
+
docsUrl: string;
|
|
106
|
+
className?: string;
|
|
107
|
+
whenThrown?: string;
|
|
108
|
+
likelyCauses?: string[];
|
|
109
|
+
howToFix?: string[];
|
|
110
|
+
}
|
|
111
|
+
export interface CompileQueryInput {
|
|
112
|
+
metadata: SchemaMetadata;
|
|
113
|
+
table: TableMetadata;
|
|
114
|
+
operation: CompileOperation;
|
|
115
|
+
args: Record<string, unknown>;
|
|
116
|
+
/**
|
|
117
|
+
* The relation-load strategy the APPLICATION's client is configured with, so
|
|
118
|
+
* the report can say which plan an unspecified query would take. Defaults to
|
|
119
|
+
* `'auto'`, which is core's own default.
|
|
120
|
+
*/
|
|
121
|
+
clientRelationLoadStrategy?: RelationLoadStrategy;
|
|
122
|
+
}
|
|
123
|
+
export type CompileQueryReport = ({
|
|
124
|
+
ok: true;
|
|
125
|
+
} & CompiledQuerySuccess) | {
|
|
126
|
+
ok: false;
|
|
127
|
+
table: string;
|
|
128
|
+
operation: CompileOperation;
|
|
129
|
+
error: CompiledQueryError;
|
|
130
|
+
};
|
|
131
|
+
export interface CompiledQuerySuccess {
|
|
132
|
+
table: string;
|
|
133
|
+
operation: CompileOperation;
|
|
134
|
+
sql: string;
|
|
135
|
+
params: unknown[];
|
|
136
|
+
paramCount: number;
|
|
137
|
+
paramsTruncated: boolean;
|
|
138
|
+
paramNote: string;
|
|
139
|
+
preparedStatement: {
|
|
140
|
+
named: boolean;
|
|
141
|
+
name: string | null;
|
|
142
|
+
note: string;
|
|
143
|
+
};
|
|
144
|
+
statements: {
|
|
145
|
+
compiled: number;
|
|
146
|
+
atExecution: number | null;
|
|
147
|
+
note: string;
|
|
148
|
+
};
|
|
149
|
+
plan: {
|
|
150
|
+
relationLoadStrategy: {
|
|
151
|
+
requested: RelationLoadStrategy | null;
|
|
152
|
+
effective: RelationLoadStrategy;
|
|
153
|
+
note: string;
|
|
154
|
+
};
|
|
155
|
+
relationCount: number;
|
|
156
|
+
relationDepth: number;
|
|
157
|
+
relations: CompiledRelation[];
|
|
158
|
+
relationsTruncated: boolean;
|
|
159
|
+
hasWhere: boolean;
|
|
160
|
+
hasOrderBy: boolean;
|
|
161
|
+
bounded: boolean;
|
|
162
|
+
limit: number | null;
|
|
163
|
+
boundedBy: string | null;
|
|
164
|
+
readsEveryRow: boolean;
|
|
165
|
+
};
|
|
166
|
+
advice: CompiledAdvice[];
|
|
167
|
+
note: string;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Compile one read query and describe it. Never touches a database; see the
|
|
171
|
+
* module header for why that is enforced rather than promised.
|
|
172
|
+
*/
|
|
173
|
+
export declare function compileQueryPlan(input: CompileQueryInput): CompileQueryReport;
|
|
174
|
+
/**
|
|
175
|
+
* Every string that could be a column name anywhere in an `aggregate` /
|
|
176
|
+
* `groupBy` args object, EXCLUDING `where` (which the shared PII predicate
|
|
177
|
+
* guard walks properly).
|
|
178
|
+
*
|
|
179
|
+
* DELIBERATELY BLUNTER THAN THE SHARED WALKER, and blunt in the fail-closed
|
|
180
|
+
* direction. The aggregate arg surface names columns in positions the where /
|
|
181
|
+
* orderBy walker was never built for: `by` names them as ARRAY ELEMENTS,
|
|
182
|
+
* `_min` / `_max` / `_sum` / `_avg` as KEYS one level under an aggregate block,
|
|
183
|
+
* `having` as keys under both, and a JSON-path group key names one inside a
|
|
184
|
+
* `{ field, path }` object. Teaching the shared walker each of those shapes
|
|
185
|
+
* means a second place that has to stay in step with the aggregate compiler,
|
|
186
|
+
* which is the exact failure mode `cli/pii-predicate-guard.ts` exists to end.
|
|
187
|
+
*
|
|
188
|
+
* So this harvests EVERY string in key or value position and hands the lot to
|
|
189
|
+
* the caller's column resolver. A string that is not a column resolves to
|
|
190
|
+
* nothing and is ignored; a string that IS a hidden column is refused wherever
|
|
191
|
+
* it appears. The cost is a false refusal on a schema whose hidden column is
|
|
192
|
+
* named `desc` or `_all`; the benefit is that a new aggregate arg shape is
|
|
193
|
+
* covered the day it ships, with no edit here.
|
|
194
|
+
*
|
|
195
|
+
* `where` is excluded because the shared walker already covers it with the
|
|
196
|
+
* relation-aware precision this cannot have.
|
|
197
|
+
*/
|
|
198
|
+
export declare function collectAggregateColumnNames(args: Record<string, unknown>): string[];
|