turbine-orm 0.31.0 → 0.32.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/dist/cjs/powql.js +38 -1
- package/dist/cjs/query/batched-loader.js +34 -0
- package/dist/cjs/query/builder.js +451 -80
- package/dist/cjs/query/filters.js +28 -0
- package/dist/index.d.ts +1 -1
- package/dist/powql.js +38 -1
- package/dist/query/batched-loader.d.ts +12 -0
- package/dist/query/batched-loader.js +33 -0
- package/dist/query/builder.d.ts +86 -0
- package/dist/query/builder.js +453 -82
- package/dist/query/filters.d.ts +13 -1
- package/dist/query/filters.js +27 -0
- package/dist/query/index.d.ts +1 -1
- package/dist/query/types.d.ts +148 -12
- package/package.json +1 -1
package/dist/cjs/powql.js
CHANGED
|
@@ -74,6 +74,7 @@ const node_crypto_1 = require("node:crypto");
|
|
|
74
74
|
const errors_js_1 = require("./errors.js");
|
|
75
75
|
const nested_write_js_1 = require("./nested-write.js");
|
|
76
76
|
const powdb_js_1 = require("./powdb.js");
|
|
77
|
+
const filters_js_1 = require("./query/filters.js");
|
|
77
78
|
const utils_js_1 = require("./query/utils.js");
|
|
78
79
|
const schema_js_1 = require("./schema.js");
|
|
79
80
|
/**
|
|
@@ -511,7 +512,22 @@ class PowqlInterface {
|
|
|
511
512
|
return '';
|
|
512
513
|
const parts = keys.map(([field, dir]) => {
|
|
513
514
|
if (dir && typeof dir === 'object') {
|
|
514
|
-
|
|
515
|
+
// Name the actual feature in the refusal — a pick-row ordering
|
|
516
|
+
// reported as "vector / distance ordering" sends users hunting for
|
|
517
|
+
// pgvector docs. All object-valued orderings stay E017 on PowDB.
|
|
518
|
+
const o = dir;
|
|
519
|
+
const feature = (0, filters_js_1.isRelationPickOrderBy)(dir)
|
|
520
|
+
? 'relation pick-row ordering'
|
|
521
|
+
: 'distance' in o
|
|
522
|
+
? 'vector / distance ordering'
|
|
523
|
+
: Array.isArray(o.path)
|
|
524
|
+
? 'JSON-path ordering'
|
|
525
|
+
: '_count' in o
|
|
526
|
+
? 'relation _count ordering'
|
|
527
|
+
: 'sort' in o || 'nulls' in o
|
|
528
|
+
? 'NULLS placement / sort-spec ordering'
|
|
529
|
+
: 'object-valued ordering';
|
|
530
|
+
throw new errors_js_1.UnsupportedFeatureError(feature, 'PowDB', `field "${field}"`);
|
|
515
531
|
}
|
|
516
532
|
return `${this.ref(field)} ${dir === 'desc' ? 'desc' : 'asc'}`;
|
|
517
533
|
});
|
|
@@ -1155,6 +1171,27 @@ class PowqlInterface {
|
|
|
1155
1171
|
}
|
|
1156
1172
|
async groupBy(args) {
|
|
1157
1173
|
return this.withMiddleware('groupBy', args, async () => {
|
|
1174
|
+
// The SQL-only groupBy extensions (DISTINCT ON row source, JSON-path
|
|
1175
|
+
// group keys / aggregate targets) have no PowQL equivalent: refuse
|
|
1176
|
+
// clearly instead of emitting broken PowQL.
|
|
1177
|
+
if (args.distinctOn) {
|
|
1178
|
+
throw new errors_js_1.UnsupportedFeatureError('groupBy distinctOn row source', 'PowDB');
|
|
1179
|
+
}
|
|
1180
|
+
for (const entry of args.by) {
|
|
1181
|
+
if (typeof entry !== 'string') {
|
|
1182
|
+
throw new errors_js_1.UnsupportedFeatureError('JSON-path groupBy keys', 'PowDB');
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
for (const fn of ['_sum', '_avg', '_min', '_max']) {
|
|
1186
|
+
const spec = args[fn];
|
|
1187
|
+
if (!spec)
|
|
1188
|
+
continue;
|
|
1189
|
+
for (const value of Object.values(spec)) {
|
|
1190
|
+
if (value !== undefined && typeof value !== 'boolean') {
|
|
1191
|
+
throw new errors_js_1.UnsupportedFeatureError(`JSON-path ${fn} aggregate targets`, 'PowDB');
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1158
1195
|
const params = [];
|
|
1159
1196
|
const resolvedWhere = await this.resolveRelationFilters(args.where, args.timeout);
|
|
1160
1197
|
const where = this.buildWhere(resolvedWhere, params);
|
|
@@ -52,9 +52,11 @@ exports.includeKeysForBatching = includeKeysForBatching;
|
|
|
52
52
|
exports.stripFields = stripFields;
|
|
53
53
|
exports.neededParentKeyFields = neededParentKeyFields;
|
|
54
54
|
exports.resolveCountRelations = resolveCountRelations;
|
|
55
|
+
exports.rejectNestedPickOrder = rejectNestedPickOrder;
|
|
55
56
|
exports.loadRelationsBatched = loadRelationsBatched;
|
|
56
57
|
const errors_js_1 = require("../errors.js");
|
|
57
58
|
const schema_js_1 = require("../schema.js");
|
|
59
|
+
const filters_js_1 = require("./filters.js");
|
|
58
60
|
/**
|
|
59
61
|
* Max parent keys per follow-up query. On Postgres the whole key set travels as
|
|
60
62
|
* ONE array parameter (`= ANY($1)`), so this is not a bind-parameter limit — it
|
|
@@ -184,6 +186,34 @@ function resolveCountRelations(parentMeta, countSpec) {
|
|
|
184
186
|
function keyOf(value) {
|
|
185
187
|
return String(value);
|
|
186
188
|
}
|
|
189
|
+
/**
|
|
190
|
+
* Reject pick-row relation ordering anywhere inside a `with` tree's orderBy —
|
|
191
|
+
* strategy parity with the join path, which throws this exact E003 at SQL
|
|
192
|
+
* build time (`pickOrderNestedError` in builder.ts). Without this guard the
|
|
193
|
+
* loaders would forward `options.orderBy` as the child reader's TOP-LEVEL
|
|
194
|
+
* findMany orderBy, where the pick shape compiles fine — so the same query
|
|
195
|
+
* would execute on 'batched' but throw on 'join'. Walks the whole tree up
|
|
196
|
+
* front so acceptance never depends on which levels have rows — the batched
|
|
197
|
+
* runners in builder.ts call this BEFORE the base query (a zero-row base
|
|
198
|
+
* result must still reject, exactly like the join strategy's build-time throw).
|
|
199
|
+
*/
|
|
200
|
+
function rejectNestedPickOrder(withClause) {
|
|
201
|
+
for (const spec of Object.values(withClause)) {
|
|
202
|
+
if (!spec || spec === true)
|
|
203
|
+
continue;
|
|
204
|
+
const options = spec;
|
|
205
|
+
if (options.orderBy) {
|
|
206
|
+
for (const [key, value] of Object.entries(options.orderBy)) {
|
|
207
|
+
if ((0, filters_js_1.isRelationPickOrderBy)(value)) {
|
|
208
|
+
throw new errors_js_1.ValidationError(`[turbine] Pick-row ordering on relation "${key}" is only supported in a top-level ` +
|
|
209
|
+
'findMany orderBy: nested `with` orderBy does not support it.');
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
if (options.with)
|
|
214
|
+
rejectNestedPickOrder(options.with);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
187
217
|
/**
|
|
188
218
|
* Load every relation in `withClause` for `parents` and attach it onto each row
|
|
189
219
|
* in place. Mirrors the join strategy's output shape exactly. Recurses for nested
|
|
@@ -192,6 +222,10 @@ function keyOf(value) {
|
|
|
192
222
|
async function loadRelationsBatched(ctx, parents, withClause, timeout, depth = 0, path = [ctx.parentMeta.name]) {
|
|
193
223
|
if (depth >= MAX_DEPTH)
|
|
194
224
|
throw new errors_js_1.CircularRelationError([...path, '…']);
|
|
225
|
+
// Scope-rule parity with the join strategy: validate the whole tree BEFORE
|
|
226
|
+
// the empty-parents early return, so accept/reject never depends on data.
|
|
227
|
+
if (depth === 0)
|
|
228
|
+
rejectNestedPickOrder(withClause);
|
|
195
229
|
if (parents.length === 0)
|
|
196
230
|
return;
|
|
197
231
|
// Sibling relations are independent (each writes only its own parent[relName]
|