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 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
- throw new errors_js_1.UnsupportedFeatureError('vector / distance ordering', 'PowDB', `field "${field}"`);
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]