turbine-orm 0.21.0 → 0.23.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.
@@ -0,0 +1,205 @@
1
+ /**
2
+ * PowqlInterface — Turbine's PowQL query generator (the PowDB analogue of
3
+ * {@link QueryInterface}). It exposes the same public method surface as the SQL
4
+ * `QueryInterface` (`findMany`, `create`, `update`, …) but emits **PowQL** — a
5
+ * pipeline language, not SQL — executed through {@link PowdbPool}.
6
+ *
7
+ * It is a *parallel* implementation rather than a `Dialect` of the SQL builder:
8
+ * PowQL's grammar (`T filter <e> order <k> { .col }`) shares no surface with
9
+ * `SELECT … FROM … WHERE`, so the SQL `Dialect` seam cannot express it. Keeping
10
+ * it separate also means the four SQL engines are untouched.
11
+ *
12
+ * Behavioural deltas from the SQL path, all driven by PowDB's wire reality (see
13
+ * `docs/strategy/powdb-parity-matrix.md`, every row verified against a live
14
+ * server):
15
+ * - `create`/`createMany`/`update`/`delete` use PowDB 0.7.0's trailing
16
+ * `returning` keyword (`RETURNING *`, all columns) to surface affected rows
17
+ * in one round-trip. `upsert` is the lone exception — its statement does not
18
+ * accept `returning`, so it reselects the row by PK (a composite-PK upsert
19
+ * reselects-or-writes inside one flat transaction).
20
+ * - The PK is server-assigned when the column is `isGenerated` (PowDB's `auto`
21
+ * int — read back via `returning`); otherwise a defaulted **string** PK is
22
+ * generated client-side (UUID).
23
+ * - `with` (nested relations) uses **batched N+1 loaders** — D round-trips for
24
+ * depth D, not one query — including manyToMany (junction → targets).
25
+ * - **Relation filters** (`some`/`none`/`every`, all cardinalities incl. m2m)
26
+ * are resolved client-side to a literal `in (…)` list, never an IN-subquery:
27
+ * PowDB's executor caches a subquery's result by plan shape and would return
28
+ * a stale prior result for a later subquery of the same shape.
29
+ * - **Nested writes** (relation ops in `create`/`update` data) run through the
30
+ * shared nested-write engine as one flat top-level transaction (PowDB is
31
+ * single-writer, no savepoints).
32
+ * - pgvector / JSON / array filters and cursor streaming throw
33
+ * {@link UnsupportedFeatureError} (E017) — they have no PowDB equivalent.
34
+ *
35
+ * @module
36
+ */
37
+ import { type PowdbPool } from './powdb.js';
38
+ import type { MiddlewareFn, QueryInterfaceOptions } from './query/index.js';
39
+ import type { AggregateArgs, AggregateResult, CountArgs, CreateArgs, CreateManyArgs, DeleteArgs, DeleteManyArgs, FindManyArgs, FindUniqueArgs, GroupByArgs, UpdateArgs, UpdateManyArgs, UpsertArgs } from './query/types.js';
40
+ import { type SchemaMetadata } from './schema.js';
41
+ /**
42
+ * The PowQL query interface. Constructed by `turbinePowDB` via the
43
+ * `queryInterfaceFactory` seam and cast to `QueryInterface<object>` so
44
+ * `TurbineClient.table()` can return it transparently.
45
+ */
46
+ export declare class PowqlInterface<T extends object = Record<string, unknown>> {
47
+ private readonly pool;
48
+ private readonly table;
49
+ private readonly schema;
50
+ private readonly middlewares;
51
+ private readonly options;
52
+ private readonly meta;
53
+ private readonly defaultLimit?;
54
+ private readonly warnOnUnlimited;
55
+ private readonly onQuery?;
56
+ private currentAction;
57
+ private warnedUnlimited;
58
+ constructor(pool: PowdbPool, table: string, schema: SchemaMetadata, middlewares?: MiddlewareFn[], options?: QueryInterfaceOptions);
59
+ /** Resolve a camelCase field name (or raw snake) to its column metadata. */
60
+ private column;
61
+ /** PowQL column reference (`.snake_name`) for a field. */
62
+ private ref;
63
+ /**
64
+ * Push a value into the param array and return its `$N` placeholder. When the
65
+ * value targets a `float` column it is wrapped in {@link PowdbFloatParam} so
66
+ * the *embedded* literal encoder emits a float-form literal even for an
67
+ * integer value (PowQL's `42` is an int literal, `42.0` a float). The
68
+ * networked driver unwraps the marker back to the plain number in
69
+ * {@link toPowdbParam}, so the wire param is unchanged.
70
+ */
71
+ private param;
72
+ /**
73
+ * Render a value for a write *assignment* (`col := …`). Every value — float
74
+ * columns included — is sent as a positional `$N` param. PowDB ≥ 0.7.0 fixed
75
+ * the int→float UPDATE coercion bug (`score := $n` with an integer param now
76
+ * reads back the integer value, not the raw i64 bits), so the float-literal
77
+ * inlining workaround Turbine carried for ≤ 0.6.2 is gone. Marks the column as
78
+ * float-typed for the *embedded* literal encoder (which materializes params
79
+ * into PowQL text) so an integer-valued float still encodes as a float literal
80
+ * and coercion stays unambiguous. Non-finite floats are still rejected.
81
+ */
82
+ private writeRef;
83
+ private isFloatCol;
84
+ /** A predicate that is always false — the empty-`in` / contradiction sentinel. */
85
+ private alwaysFalse;
86
+ /**
87
+ * Compile a {@link WhereClause} into a PowQL filter expression, pushing every
88
+ * value as a positional `$N` param. Returns `''` when there are no conditions.
89
+ */
90
+ private buildWhere;
91
+ /** Build a single `field: value | operator` condition. */
92
+ private buildFieldCondition;
93
+ /** Bind a value, lowercasing for case-insensitive comparisons. */
94
+ private bind;
95
+ /** Bind a LIKE pattern (already escaped), lowercasing for insensitive mode. */
96
+ private bindLike;
97
+ /** `lhs [not] in ($1, $2, …)` — empty list collapses to a constant. */
98
+ private buildInList;
99
+ /**
100
+ * Pre-resolve every relation filter (`some`/`none`/`every`) in a where clause
101
+ * into a plain scalar `in`/`notIn` condition on the **local key**, by running
102
+ * the inner predicate as its own query and materializing the matching keys as
103
+ * a literal list. The compiled where is then relation-free and `buildWhere`
104
+ * emits only `in (<literal list>)`.
105
+ *
106
+ * Why not an IN-subquery (`.k in (Target filter <e> { .fk })`)? PowDB's
107
+ * executor caches a subquery's result by **plan shape, ignoring the literal**,
108
+ * so a second subquery of the same shape with a different value returns the
109
+ * first one's stale rows (reproduced live on the embedded engine; the
110
+ * single-statement literal `in (list)` form is always correct). Resolving
111
+ * client-side trades extra round-trips for correctness, and recurses — nested
112
+ * relation filters in the inner predicate resolve when the target query runs.
113
+ */
114
+ private resolveRelationFilters;
115
+ /** Resolve one hasMany/hasOne/belongsTo filter to `{ localField: { in|notIn: [...] } }`. */
116
+ private resolveRelationCondition;
117
+ /** Resolve a manyToMany filter through the junction to `{ sourceRefField: { in|notIn: [...] } }`. */
118
+ private resolveManyToManyCondition;
119
+ /** Resolve the set of columns to project, honouring `select` / `omit`. */
120
+ private projectedColumns;
121
+ /** `{ .c1, .c2, … }` projection clause. */
122
+ private projection;
123
+ /** `order .c1 asc, .c2 desc` clause (empty string when no orderBy). */
124
+ private buildOrder;
125
+ /** Run PowQL with optional timeout, emitting a query event either way. */
126
+ private exec;
127
+ private emit;
128
+ /** Run a method body through the middleware chain (mirrors QueryInterface). */
129
+ private withMiddleware;
130
+ /** Map raw rows to typed entities. */
131
+ private shape;
132
+ findMany(args?: FindManyArgs<T>): Promise<T[]>;
133
+ /** Build + run the flat findMany select; returns raw rows. */
134
+ private runFind;
135
+ findUnique(args: FindUniqueArgs<T>): Promise<T | null>;
136
+ findFirst(args?: FindManyArgs<T>): Promise<T | null>;
137
+ findUniqueOrThrow(args: FindUniqueArgs<T>): Promise<T>;
138
+ findFirstOrThrow(args?: FindManyArgs<T>): Promise<T>;
139
+ /** Load each requested relation for `parents` and attach it onto each row. */
140
+ private loadRelations;
141
+ /**
142
+ * manyToMany nested read — a three-hop batched loader (no `json_agg`/join
143
+ * pushdown): (1) read the junction rows for all parents in `sourceKey in (…)`
144
+ * chunks, (2) read the target rows for the collected `targetKey`s, (3) stitch
145
+ * each parent → its junction rows → its targets in memory. Mirrors the
146
+ * single-key N+1 loaders; the junction's source/target columns must be single
147
+ * (composite junction keys would need PowQL tuple-`in`, which it lacks).
148
+ */
149
+ private loadManyToMany;
150
+ /** Split `data` into scalar assignments; reject relation (nested-write) keys. */
151
+ private scalarData;
152
+ /**
153
+ * Fill in a client-generated UUID for a defaulted **string** PK that wasn't
154
+ * supplied. A server-generated PK ({@link ColumnMetadata.isGenerated}, e.g. an
155
+ * `int` column with PowDB's `auto` modifier) is left untouched — PowDB assigns
156
+ * it and the trailing `returning` reads it back — as is any non-string PK.
157
+ */
158
+ private applyPkDefault;
159
+ create(args: CreateArgs<T>): Promise<T>;
160
+ createMany(args: CreateManyArgs<T>): Promise<T[]>;
161
+ update(args: UpdateArgs<T>): Promise<T>;
162
+ updateMany(args: UpdateManyArgs<T>): Promise<{
163
+ count: number;
164
+ }>;
165
+ /** Compile `data` into PowQL update assignments, including atomic operators. */
166
+ private buildUpdateAssignments;
167
+ private isTxScoped;
168
+ private nestedCreate;
169
+ private nestedUpdate;
170
+ /** Open a flat PowDB transaction on a pinned connection and run `fn` inside it. */
171
+ private runInImplicitTx;
172
+ /** Already inside a transaction: build a context whose table accessors reuse the pinned pool. */
173
+ private buildNestedCtx;
174
+ delete(args: DeleteArgs<T>): Promise<T>;
175
+ deleteMany(args: DeleteManyArgs<T>): Promise<{
176
+ count: number;
177
+ }>;
178
+ upsert(args: UpsertArgs<T>): Promise<T>;
179
+ /**
180
+ * Composite-key upsert: PowQL's `upsert … on .col` only takes one conflict
181
+ * column, so reselect by the full composite PK and update-or-create inside one
182
+ * flat transaction (PowDB single-writer makes the read-then-write safe from
183
+ * concurrent writers; the transaction makes it atomic with the write).
184
+ */
185
+ private upsertComposite;
186
+ count(args?: CountArgs<T>): Promise<number>;
187
+ aggregate(args: AggregateArgs<T>): Promise<AggregateResult<T>>;
188
+ groupBy(args: GroupByArgs<T>): Promise<Record<string, unknown>[]>;
189
+ /** `having <expr>` over group aggregates (count/sum/avg/min/max). */
190
+ private buildHaving;
191
+ findManyStream(): AsyncGenerator<T>;
192
+ /** Reselect a single row by its single-column primary key value. */
193
+ private reselectByPk;
194
+ /**
195
+ * Empty-where guard — blocks accidental whole-table writes. Mirrors the SQL
196
+ * path's `assertMutationHasPredicate` (query/builder.ts): it gates on the
197
+ * *compiled* PowQL filter fragment, NOT the shape of the `where` object. A
198
+ * `where` whose conditions all evaporate during compilation — `{}`,
199
+ * `{ id: undefined }`, `{ OR: [] }`, `{ AND: [] }`, `{ NOT: {} }`,
200
+ * `{ OR: [{ f: undefined }] }` — compiles to the empty string and is refused,
201
+ * because emitting a filter-less write would hit every row.
202
+ */
203
+ private assertCompiledWhere;
204
+ }
205
+ //# sourceMappingURL=powql.d.ts.map