@vantreeseba/drizzle-graphql 7.2.0 → 7.3.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/index.d.cts CHANGED
@@ -188,6 +188,12 @@ type OrderByArgs<TTable extends Table> = {
188
188
  * emulated with an extra `IS NULL` sort key on MySQL.
189
189
  */
190
190
  nulls?: 'first' | 'last';
191
+ /**
192
+ * Sort by the column's position in the `inArray` list the same request's `where` gives
193
+ * it, rather than by the column's own value. Requires that `inArray` filter; cannot be
194
+ * combined with `after` or `distinct`.
195
+ */
196
+ matchFilterOrder?: boolean;
191
197
  };
192
198
  };
193
199
 
@@ -262,10 +268,13 @@ type LimitPolicyFor = (tableName: string) => ResolvedLimitPolicy | undefined;
262
268
  * across relation boundaries. Each entry may also carry `nulls: first | last`
263
269
  * (see {@link orderExpressions} for how MySQL emulates it).
264
270
  *
271
+ * An entry may instead set `matchFilterOrder`, which sorts by the column's position in the
272
+ * `inArray` list the same request's `where` gives it — `whereArgs` is that `where`.
273
+ *
265
274
  * `ctx` supplies the tables and relation map that relation ordering needs; callers whose
266
275
  * inputs cannot contain relation keys may omit it.
267
276
  */
268
- declare const extractOrderBy: <TTable extends Table, TArgs extends OrderByArgs<any> = OrderByArgs<TTable>>(table: TTable, orderArgs: TArgs, ctx?: RelationFilterContext) => SQL[];
277
+ declare const extractOrderBy: <TTable extends Table, TArgs extends OrderByArgs<any> = OrderByArgs<TTable>>(table: TTable, orderArgs: TArgs, ctx?: RelationFilterContext, whereArgs?: Record<string, any>) => SQL[];
269
278
  /**
270
279
  * Everything `extractFilters` needs to turn a relation key in a `where` argument into a
271
280
  * correlated subquery. Omitted by callers that don't generate relation filters, in which case
package/dist/index.d.ts CHANGED
@@ -188,6 +188,12 @@ type OrderByArgs<TTable extends Table> = {
188
188
  * emulated with an extra `IS NULL` sort key on MySQL.
189
189
  */
190
190
  nulls?: 'first' | 'last';
191
+ /**
192
+ * Sort by the column's position in the `inArray` list the same request's `where` gives
193
+ * it, rather than by the column's own value. Requires that `inArray` filter; cannot be
194
+ * combined with `after` or `distinct`.
195
+ */
196
+ matchFilterOrder?: boolean;
191
197
  };
192
198
  };
193
199
 
@@ -262,10 +268,13 @@ type LimitPolicyFor = (tableName: string) => ResolvedLimitPolicy | undefined;
262
268
  * across relation boundaries. Each entry may also carry `nulls: first | last`
263
269
  * (see {@link orderExpressions} for how MySQL emulates it).
264
270
  *
271
+ * An entry may instead set `matchFilterOrder`, which sorts by the column's position in the
272
+ * `inArray` list the same request's `where` gives it — `whereArgs` is that `where`.
273
+ *
265
274
  * `ctx` supplies the tables and relation map that relation ordering needs; callers whose
266
275
  * inputs cannot contain relation keys may omit it.
267
276
  */
268
- declare const extractOrderBy: <TTable extends Table, TArgs extends OrderByArgs<any> = OrderByArgs<TTable>>(table: TTable, orderArgs: TArgs, ctx?: RelationFilterContext) => SQL[];
277
+ declare const extractOrderBy: <TTable extends Table, TArgs extends OrderByArgs<any> = OrderByArgs<TTable>>(table: TTable, orderArgs: TArgs, ctx?: RelationFilterContext, whereArgs?: Record<string, any>) => SQL[];
269
278
  /**
270
279
  * Everything `extractFilters` needs to turn a relation key in a `where` argument into a
271
280
  * correlated subquery. Omitted by callers that don't generate relation filters, in which case
package/dist/index.js CHANGED
@@ -1012,10 +1012,10 @@ var runMutation = async (db, context, info, txCtx, body) => {
1012
1012
  }
1013
1013
  return runOnSharedTx(state, body);
1014
1014
  };
1015
- var batchedPaginatedRelationQuery = async (db, targetTable, foreignCol, whereCondition, orderByArg, limit, offset, pkNames, orderCtx) => {
1015
+ var batchedPaginatedRelationQuery = async (db, targetTable, foreignCol, whereCondition, orderByArg, limit, offset, pkNames, orderCtx, whereArgs) => {
1016
1016
  const cols = getColumns2(targetTable);
1017
1017
  const orderExprs = [
1018
- ...orderByArg ? extractOrderBy(targetTable, orderByArg, orderCtx) : [],
1018
+ ...orderByArg ? extractOrderBy(targetTable, orderByArg, orderCtx, whereArgs) : [],
1019
1019
  ...primaryKeyOrderExprs(targetTable, pkNames)
1020
1020
  ];
1021
1021
  const orderClause = orderExprs.length ? sql2` order by ${sql2.join(orderExprs, sql2`, `)}` : sql2``;
@@ -1126,11 +1126,12 @@ var createRelationResolverFactory = (db, tables, nullOrdering, filterCtx, limits
1126
1126
  limit ?? null,
1127
1127
  offset ?? null,
1128
1128
  targetPkNames,
1129
- relationFilterCtx(filterCtx, targetTableName)
1129
+ relationFilterCtx(filterCtx, targetTableName),
1130
+ whereArg
1130
1131
  );
1131
1132
  } else {
1132
1133
  let q = executor.select().from(targetTable).where(whereCondition);
1133
- const orderExprs = cursorEntries ? cursorOrderExprs(targetTable, cursorEntries) : orderByArg ? extractOrderBy(targetTable, orderByArg, relationFilterCtx(filterCtx, targetTableName)) : [];
1134
+ const orderExprs = cursorEntries ? cursorOrderExprs(targetTable, cursorEntries) : orderByArg ? extractOrderBy(targetTable, orderByArg, relationFilterCtx(filterCtx, targetTableName), whereArg) : [];
1134
1135
  if (orderExprs.length) {
1135
1136
  q = q.orderBy(...orderExprs);
1136
1137
  }
@@ -1341,6 +1342,10 @@ var innerOrder = new GraphQLInputObjectType3({
1341
1342
  nulls: {
1342
1343
  type: orderNulls,
1343
1344
  description: "Where NULL values sort. Defaults to the database's own rule (PostgreSQL: last on asc, first on desc; MySQL/SQLite: first on asc, last on desc)"
1345
+ },
1346
+ matchFilterOrder: {
1347
+ type: GraphQLBoolean2,
1348
+ description: "Sort by this column's position in the `inArray` list the same request's `where` gives it, rather than by the column's own value \u2014 `direction: asc` keeps the list's order, `desc` reverses it. Requires an `inArray` filter on the same column at the top level of `where`, and cannot be combined with `after` or `distinct`."
1344
1349
  }
1345
1350
  }
1346
1351
  });
@@ -1406,10 +1411,59 @@ var jsonFilterFields = (column, colType) => {
1406
1411
  description: "Value structurally contains this JSON (Postgres `@>` / MySQL JSON_CONTAINS)"
1407
1412
  }
1408
1413
  } : {},
1414
+ path: {
1415
+ type: new GraphQLList3(new GraphQLNonNull3(jsonPathFilter)),
1416
+ description: "Compares the value at one path inside the document. Several entries are ANDed; a single object may be passed without the list brackets."
1417
+ },
1409
1418
  isNull: { type: GraphQLBoolean2 },
1410
1419
  isNotNull: { type: GraphQLBoolean2 }
1411
1420
  };
1412
1421
  };
1422
+ var jsonPathCast = new GraphQLEnumType2({
1423
+ name: "JSONPathCast",
1424
+ description: "How to read the value at a JSON path before comparing it",
1425
+ values: {
1426
+ TEXT: { value: "text", description: "Compare as text (lexicographic ordering)" },
1427
+ NUMBER: { value: "number", description: "Compare as a number; a non-numeric value never matches" },
1428
+ BOOLEAN: { value: "boolean", description: "Compare as a boolean" }
1429
+ }
1430
+ });
1431
+ var jsonPathFilter = new GraphQLInputObjectType3({
1432
+ name: "JSONPathFilter",
1433
+ fields: {
1434
+ path: {
1435
+ type: new GraphQLNonNull3(new GraphQLList3(new GraphQLNonNull3(GraphQLString2))),
1436
+ description: 'Keys to walk from the document root, e.g. `["profile", "level"]`. An all-digits key indexes an array.'
1437
+ },
1438
+ as: { type: jsonPathCast, description: "Overrides how the value is read before comparing" },
1439
+ eq: { type: GraphQLJSON, description: "Equal to" },
1440
+ ne: { type: GraphQLJSON, description: "Not equal to" },
1441
+ lt: { type: GraphQLJSON, description: "Less than" },
1442
+ lte: { type: GraphQLJSON, description: "Less than or equal to" },
1443
+ gt: { type: GraphQLJSON, description: "Greater than" },
1444
+ gte: { type: GraphQLJSON, description: "Greater than or equal to" },
1445
+ startsWith: {
1446
+ type: GraphQLString2,
1447
+ description: "Extracted value starts with this string. `%`, `_` and `\\` are matched literally."
1448
+ },
1449
+ endsWith: {
1450
+ type: GraphQLString2,
1451
+ description: "Extracted value ends with this string. `%`, `_` and `\\` are matched literally."
1452
+ },
1453
+ contains: {
1454
+ type: GraphQLString2,
1455
+ description: "Extracted value contains this string. `%`, `_` and `\\` are matched literally."
1456
+ },
1457
+ iStartsWith: { type: GraphQLString2, description: "Case-insensitive `startsWith`." },
1458
+ iEndsWith: { type: GraphQLString2, description: "Case-insensitive `endsWith`." },
1459
+ iContains: { type: GraphQLString2, description: "Case-insensitive `contains`." },
1460
+ isNull: {
1461
+ type: GraphQLBoolean2,
1462
+ description: "When true, matches rows where the path is missing or holds JSON null"
1463
+ },
1464
+ isNotNull: { type: GraphQLBoolean2, description: "When true, matches rows where the path holds a value" }
1465
+ }
1466
+ });
1413
1467
  var IN_ARRAY_DESCRIPTION = "Matches any one of these values (SQL `IN`)";
1414
1468
  var NOT_IN_ARRAY_DESCRIPTION = "Matches none of these values (SQL `NOT IN`)";
1415
1469
  var arrayFilterFields = (colType, colArr) => {
@@ -2003,6 +2057,9 @@ var orderByEntries = (orderArgs) => Object.entries(orderArgs).sort((a, b) => (b[
2003
2057
  if (typeof config === "object" && config.direction === void 0) {
2004
2058
  throw new GraphQLError4(`ORDER BY ${column}: ordering through a relation is not supported in this query`);
2005
2059
  }
2060
+ if (config.matchFilterOrder) {
2061
+ throw new GraphQLError4(`ORDER BY ${column}: 'matchFilterOrder' is not supported in this query`);
2062
+ }
2006
2063
  return [column, config.direction, config.nulls ?? void 0];
2007
2064
  });
2008
2065
  var orderExpressions = (expr, direction, nulls, dialectColumn) => {
@@ -2037,7 +2094,22 @@ var extendRelationOrderChain = (parentTable, relationName, relEntry, ctx, chain)
2037
2094
  table: aliasedTarget
2038
2095
  };
2039
2096
  };
2040
- var collectOrderEntries = (table, tableKey, orderArgs, ctx, chain, out) => {
2097
+ var filterOrderExpression = (column, columnName, whereArgs) => {
2098
+ const values = whereArgs?.[columnName]?.inArray;
2099
+ if (!Array.isArray(values)) {
2100
+ throw new GraphQLError4(
2101
+ `ORDER BY ${columnName}: 'matchFilterOrder' needs an 'inArray' filter on the same column in this query's 'where'`
2102
+ );
2103
+ }
2104
+ if (!values.length) {
2105
+ return sql2`0`;
2106
+ }
2107
+ const branches = values.map(
2108
+ (value, index) => sql2`when ${column} = ${remapFromGraphQLCore(value, column, columnName)} then ${sql2.raw(String(index))}`
2109
+ );
2110
+ return sql2`case ${sql2.join(branches, sql2` `)} else ${sql2.raw(String(values.length))} end`;
2111
+ };
2112
+ var collectOrderEntries = (table, tableKey, orderArgs, ctx, chain, out, whereArgs) => {
2041
2113
  const columns = getColumns2(table);
2042
2114
  const relations = ctx?.relationMap[tableKey];
2043
2115
  for (const [key, config] of Object.entries(orderArgs)) {
@@ -2046,8 +2118,11 @@ var collectOrderEntries = (table, tableKey, orderArgs, ctx, chain, out) => {
2046
2118
  }
2047
2119
  const column = columns[key];
2048
2120
  if (column) {
2121
+ if (config.matchFilterOrder && chain) {
2122
+ throw new GraphQLError4(`ORDER BY ${key}: 'matchFilterOrder' is not supported through a relation`);
2123
+ }
2049
2124
  out.push({
2050
- expression: chain ? sql2`(select ${column} from ${sql2.join(chain.fromParts, sql2`, `)} where ${and(...chain.conditions)})` : column,
2125
+ expression: config.matchFilterOrder ? filterOrderExpression(column, key, whereArgs) : chain ? sql2`(select ${column} from ${sql2.join(chain.fromParts, sql2`, `)} where ${and(...chain.conditions)})` : column,
2051
2126
  column,
2052
2127
  direction: config.direction,
2053
2128
  nulls: config.nulls ?? void 0,
@@ -2060,12 +2135,12 @@ var collectOrderEntries = (table, tableKey, orderArgs, ctx, chain, out) => {
2060
2135
  throw new GraphQLError4(`ORDER BY ${key}: Unknown column or relation`);
2061
2136
  }
2062
2137
  const nextChain = extendRelationOrderChain(chain?.table ?? table, key, relEntry, ctx, chain);
2063
- collectOrderEntries(nextChain.table, relEntry.targetTableName, config, ctx, nextChain, out);
2138
+ collectOrderEntries(nextChain.table, relEntry.targetTableName, config, ctx, nextChain, out, void 0);
2064
2139
  }
2065
2140
  };
2066
- var extractOrderBy = (table, orderArgs, ctx) => {
2141
+ var extractOrderBy = (table, orderArgs, ctx, whereArgs) => {
2067
2142
  const entries = [];
2068
- collectOrderEntries(table, ctx?.tableKey ?? "", orderArgs, ctx, void 0, entries);
2143
+ collectOrderEntries(table, ctx?.tableKey ?? "", orderArgs, ctx, void 0, entries, whereArgs);
2069
2144
  return entries.sort((a, b) => b.priority - a.priority).flatMap((entry) => orderExpressions(entry.expression, entry.direction, entry.nulls, entry.column));
2070
2145
  };
2071
2146
  var LIKE_ESCAPE_CHAR = "\\";
@@ -2097,6 +2172,105 @@ var jsonContains = (column, columnName, value) => {
2097
2172
  throw new GraphQLError4(`WHERE ${columnName}: Operator 'contains' is not supported for this dialect!`);
2098
2173
  }
2099
2174
  };
2175
+ var sqlFalse = () => sql2`1 = 0`;
2176
+ var sqlTrue = () => sql2`1 = 1`;
2177
+ var jsonPathString = (path) => `$${path.map((part) => /^\d+$/.test(part) ? `[${part}]` : `."${part.replace(/(["\\])/g, "\\$1")}"`).join("")}`;
2178
+ var NUMERIC_TEXT_PATTERN = "^\\s*-?(\\d+\\.?\\d*|\\.\\d+)([eE][-+]?\\d+)?\\s*$";
2179
+ var jsonPathExprs = (column, path) => {
2180
+ switch (columnDialect(column)) {
2181
+ case "pg": {
2182
+ const pathArray = sql2`array[${sql2.join(
2183
+ path.map((part) => sql2`${part}`),
2184
+ sql2`, `
2185
+ )}]::text[]`;
2186
+ const text = sql2`(${column} #>> ${pathArray})`;
2187
+ return {
2188
+ text,
2189
+ number: sql2`(case when ${text} ~ ${NUMERIC_TEXT_PATTERN} then ${text}::numeric end)`,
2190
+ boolean: text,
2191
+ encodeBoolean: (value) => String(value)
2192
+ };
2193
+ }
2194
+ case "mysql": {
2195
+ const extracted = sql2`json_extract(${column}, ${jsonPathString(path)})`;
2196
+ const text = sql2`(case when json_type(${extracted}) = 'NULL' then null else json_unquote(${extracted}) end)`;
2197
+ return {
2198
+ text,
2199
+ number: sql2`(case when ${text} regexp ${NUMERIC_TEXT_PATTERN} then cast(${text} as decimal(65, 30)) end)`,
2200
+ boolean: text,
2201
+ encodeBoolean: (value) => String(value)
2202
+ };
2203
+ }
2204
+ default: {
2205
+ const extracted = sql2`json_extract(${column}, ${jsonPathString(path)})`;
2206
+ return {
2207
+ text: sql2`cast(${extracted} as text)`,
2208
+ number: sql2`(case when typeof(${extracted}) in ('integer', 'real') then ${extracted} end)`,
2209
+ // SQLite has no boolean type: a JSON `true` comes back as the integer 1.
2210
+ boolean: extracted,
2211
+ encodeBoolean: (value) => value ? 1 : 0
2212
+ };
2213
+ }
2214
+ }
2215
+ };
2216
+ var JSON_PATH_COMPARISONS = {
2217
+ eq: "=",
2218
+ ne: "<>",
2219
+ lt: "<",
2220
+ lte: "<=",
2221
+ gt: ">",
2222
+ gte: ">="
2223
+ };
2224
+ var inferJsonPathCast = (value) => typeof value === "number" ? "number" : typeof value === "boolean" ? "boolean" : "text";
2225
+ var jsonPathCondition = (column, columnName, filter) => {
2226
+ const { path, as: castOverride, ...operators } = filter;
2227
+ const locator = `${columnName}.path`;
2228
+ if (!Array.isArray(path) || !path.length) {
2229
+ throw new GraphQLError4(`WHERE ${locator}: 'path' must name at least one key`);
2230
+ }
2231
+ const exprs = jsonPathExprs(column, path);
2232
+ const variants = [];
2233
+ for (const [operatorName, operatorValue] of Object.entries(operators)) {
2234
+ if (operatorValue === null || operatorValue === void 0) {
2235
+ continue;
2236
+ }
2237
+ if (operatorName === "isNull" || operatorName === "isNotNull") {
2238
+ if (operatorValue === false) {
2239
+ continue;
2240
+ }
2241
+ variants.push(operatorName === "isNull" ? sql2`${exprs.text} is null` : sql2`${exprs.text} is not null`);
2242
+ continue;
2243
+ }
2244
+ if (operatorName in safeLikeOps) {
2245
+ const { buildPattern, insensitive } = safeLikeOps[operatorName];
2246
+ if (typeof operatorValue !== "string") {
2247
+ throw new GraphQLError4(`WHERE ${locator}: operator '${operatorName}' takes a string`);
2248
+ }
2249
+ variants.push(safeLikeCondition(exprs.text, buildPattern(operatorValue), insensitive));
2250
+ continue;
2251
+ }
2252
+ const comparison = JSON_PATH_COMPARISONS[operatorName];
2253
+ if (!comparison) {
2254
+ throw new GraphQLError4(`WHERE ${locator}: Unknown operator: ${operatorName}`);
2255
+ }
2256
+ const cast = castOverride ?? inferJsonPathCast(operatorValue);
2257
+ if (cast === "number") {
2258
+ const numeric = Number(operatorValue);
2259
+ if (Number.isNaN(numeric)) {
2260
+ throw new GraphQLError4(
2261
+ `WHERE ${locator}: operator '${operatorName}' compares as a number, so its value must be one`
2262
+ );
2263
+ }
2264
+ variants.push(sql2`${exprs.number} ${sql2.raw(comparison)} ${numeric}`);
2265
+ } else if (cast === "boolean") {
2266
+ const truthy = operatorValue === true || operatorValue === "true";
2267
+ variants.push(sql2`${exprs.boolean} ${sql2.raw(comparison)} ${exprs.encodeBoolean(truthy)}`);
2268
+ } else {
2269
+ variants.push(sql2`${exprs.text} ${sql2.raw(comparison)} ${String(operatorValue)}`);
2270
+ }
2271
+ }
2272
+ return variants.length ? variants.length > 1 ? and(...variants) : variants[0] : void 0;
2273
+ };
2100
2274
  var extractFiltersColumn = (column, columnName, operators) => {
2101
2275
  const { OR, AND, NOT, ...restOperators } = operators;
2102
2276
  const entries = Object.entries(restOperators);
@@ -2118,6 +2292,13 @@ var extractFiltersColumn = (column, columnName, operators) => {
2118
2292
  variants.push(singleValueOps[operatorName](column, singleValue));
2119
2293
  } else if (operatorName in stringValueOps) {
2120
2294
  variants.push(stringValueOps[operatorName](column, operatorValue));
2295
+ } else if (operatorName === "path" && isJsonColumn2(column)) {
2296
+ for (const pathFilter of operatorValue) {
2297
+ const extracted = jsonPathCondition(column, columnName, pathFilter);
2298
+ if (extracted) {
2299
+ variants.push(extracted);
2300
+ }
2301
+ }
2121
2302
  } else if (operatorName === "contains" && isJsonColumn2(column)) {
2122
2303
  variants.push(jsonContains(column, columnName, operatorValue));
2123
2304
  } else if (operatorName in safeLikeOps) {
@@ -2125,7 +2306,8 @@ var extractFiltersColumn = (column, columnName, operators) => {
2125
2306
  variants.push(safeLikeCondition(column, buildPattern(operatorValue), insensitive));
2126
2307
  } else if (operatorName in arrayValueOps) {
2127
2308
  if (!operatorValue.length) {
2128
- throw new GraphQLError4(`WHERE ${columnName}: Unable to use operator ${operatorName} with an empty array!`);
2309
+ variants.push(operatorName === "inArray" ? sqlFalse() : sqlTrue());
2310
+ continue;
2129
2311
  }
2130
2312
  const arrayValue = operatorValue.map((val) => remapFromGraphQLCore(val, column, columnName));
2131
2313
  variants.push(arrayValueOps[operatorName](column, arrayValue));
@@ -2133,7 +2315,8 @@ var extractFiltersColumn = (column, columnName, operators) => {
2133
2315
  variants.push(arrayContains(column, [operatorValue]));
2134
2316
  } else if (operatorName in arrayMembershipOps) {
2135
2317
  if (!operatorValue.length) {
2136
- throw new GraphQLError4(`WHERE ${columnName}: Unable to use operator ${operatorName} with an empty array!`);
2318
+ variants.push(operatorName === "hasSome" ? sqlFalse() : sqlTrue());
2319
+ continue;
2137
2320
  }
2138
2321
  variants.push(arrayMembershipOps[operatorName](column, operatorValue));
2139
2322
  } else if (operatorName === "isEmpty") {
@@ -2396,7 +2579,7 @@ var extractRelationsParamsInner = (relationMap, tables, tableName, typeName, ori
2396
2579
  } : void 0;
2397
2580
  const hasPagination = offset != null || limit != null;
2398
2581
  const pkNames = targetPkNames ?? [];
2399
- thisRecord.orderBy = relationArgs?.orderBy ? (aliasedTable2) => extractOrderBy(aliasedTable2, relationArgs.orderBy, relationFilterCtx(filterCtx, targetTableName)) : hasPagination && pkNames.length ? (aliasedTable2) => primaryKeyOrderExprs(aliasedTable2, pkNames) : void 0;
2582
+ thisRecord.orderBy = relationArgs?.orderBy ? (aliasedTable2) => extractOrderBy(aliasedTable2, relationArgs.orderBy, relationFilterCtx(filterCtx, targetTableName), relWhere) : hasPagination && pkNames.length ? (aliasedTable2) => primaryKeyOrderExprs(aliasedTable2, pkNames) : void 0;
2400
2583
  thisRecord.offset = offset;
2401
2584
  thisRecord.limit = limit;
2402
2585
  const relWith = relationField ? extractRelationsParamsInner(
@@ -2476,6 +2659,15 @@ var primaryKeyOrderExprs = (table, pkNames) => {
2476
2659
  var CURSOR_FIELD_NAME = "cursor";
2477
2660
  var ROW_CURSOR_PROP = "__drizzle_graphql_cursor";
2478
2661
  var orderByHasRelationEntry = (orderBy) => !!orderBy && Object.values(orderBy).some((config) => config && typeof config === "object" && config.direction === void 0);
2662
+ var orderByCursorObstacle = (orderBy) => {
2663
+ if (orderByHasRelationEntry(orderBy)) {
2664
+ return "'after' cannot be combined with an orderBy that orders through a relation \u2014 a related row's value cannot be encoded into a cursor.";
2665
+ }
2666
+ if (orderBy && Object.values(orderBy).some((config) => config && typeof config === "object" && config.matchFilterOrder)) {
2667
+ return "'after' cannot be combined with 'matchFilterOrder' \u2014 a row's position in the request's own filter list is not a value of the row, so it cannot be encoded into a cursor.";
2668
+ }
2669
+ return void 0;
2670
+ };
2479
2671
  var cursorOrderingEntries = (orderBy, pkNames) => {
2480
2672
  const entries = orderBy ? orderByEntries(orderBy) : [];
2481
2673
  const seen = new Set(entries.map(([column]) => column));
@@ -3072,11 +3264,10 @@ var runRelationalSelect = async (opts) => {
3072
3264
  if (after != null && distinct) {
3073
3265
  throw new GraphQLError4("'after' cannot be combined with 'distinct'.");
3074
3266
  }
3075
- if (orderByHasRelationEntry(orderBy)) {
3267
+ const cursorObstacle = orderByCursorObstacle(orderBy);
3268
+ if (cursorObstacle) {
3076
3269
  if (after != null) {
3077
- throw new GraphQLError4(
3078
- "'after' cannot be combined with an orderBy that orders through a relation \u2014 a related row's value cannot be encoded into a cursor."
3079
- );
3270
+ throw new GraphQLError4(cursorObstacle);
3080
3271
  }
3081
3272
  } else if (!pkNames?.length) {
3082
3273
  if (after != null) {
@@ -3116,13 +3307,13 @@ var runRelationalSelect = async (opts) => {
3116
3307
  // drizzle-orm v1 RQB calls orderBy/where with the aliased table proxy — use it
3117
3308
  // directly so column refs match the CTE alias.
3118
3309
  orderBy: distinctKeys ? (aliasedTable2) => [
3119
- ...orderBy ? extractOrderBy(aliasedTable2, orderBy, relationFilterCtx(filterCtx, tableName)) : [],
3310
+ ...orderBy ? extractOrderBy(aliasedTable2, orderBy, relationFilterCtx(filterCtx, tableName), where) : [],
3120
3311
  ...primaryKeyOrderExprs(aliasedTable2, pkNames)
3121
3312
  ] : cursorEntries ? (
3122
3313
  // Cursor pagination needs a total order: the request's orderBy plus the PK tiebreak,
3123
3314
  // exactly the ordering the cursor encodes and the keyset predicate compares against.
3124
3315
  (aliasedTable2) => cursorOrderExprs(aliasedTable2, cursorEntries)
3125
- ) : orderBy ? (aliasedTable2) => extractOrderBy(aliasedTable2, orderBy, relationFilterCtx(filterCtx, tableName)) : needsDefaultOrder && pkNames?.length ? (aliasedTable2) => primaryKeyOrderExprs(aliasedTable2, pkNames) : void 0,
3316
+ ) : orderBy ? (aliasedTable2) => extractOrderBy(aliasedTable2, orderBy, relationFilterCtx(filterCtx, tableName), where) : needsDefaultOrder && pkNames?.length ? (aliasedTable2) => primaryKeyOrderExprs(aliasedTable2, pkNames) : void 0,
3126
3317
  where: distinctKeys ? { RAW: (aliasedTable2) => primaryKeyRestriction(aliasedTable2, pkNames, distinctKeys) } : where || cursorValues ? {
3127
3318
  RAW: (aliasedTable2) => and(
3128
3319
  where ? extractFilters(aliasedTable2, tableName, where, relationFilterCtx(filterCtx, tableName)) : void 0,
@@ -4822,11 +5013,10 @@ var generateSelectArray2 = (db, tableName, tables, relationMap, orderArgs, filte
4822
5013
  if (after != null && distinct?.length) {
4823
5014
  throw new GraphQLError8("'after' cannot be combined with 'distinct'.");
4824
5015
  }
4825
- if (orderByHasRelationEntry(orderBy)) {
5016
+ const cursorObstacle = orderByCursorObstacle(orderBy);
5017
+ if (cursorObstacle) {
4826
5018
  if (after != null) {
4827
- throw new GraphQLError8(
4828
- "'after' cannot be combined with an orderBy that orders through a relation \u2014 a related row's value cannot be encoded into a cursor."
4829
- );
5019
+ throw new GraphQLError8(cursorObstacle);
4830
5020
  }
4831
5021
  } else if (!pkNames.length) {
4832
5022
  if (after != null) {
@@ -4874,7 +5064,7 @@ var generateSelectArray2 = (db, tableName, tables, relationMap, orderArgs, filte
4874
5064
  q = q.orderBy(...cursorOrderExprs(table, cursorEntries));
4875
5065
  } else if (orderBy) {
4876
5066
  q = q.orderBy(
4877
- ...extractOrderBy(table, orderBy, relationFilterCtx(filterCtx, tableName)),
5067
+ ...extractOrderBy(table, orderBy, relationFilterCtx(filterCtx, tableName), where),
4878
5068
  ...distinctKeys ? primaryKeyOrderExprs(table, pkNames) : []
4879
5069
  );
4880
5070
  } else if ((distinctKeys || offset != null || limit != null) && pkNames.length) {
@@ -4940,7 +5130,7 @@ var generateSelectSingle2 = (db, tableName, tables, relationMap, orderArgs, filt
4940
5130
  q = q.where(extractFilters(table, tableName, where, relationFilterCtx(filterCtx, tableName)));
4941
5131
  }
4942
5132
  if (orderBy) {
4943
- q = q.orderBy(...extractOrderBy(table, orderBy, relationFilterCtx(filterCtx, tableName)));
5133
+ q = q.orderBy(...extractOrderBy(table, orderBy, relationFilterCtx(filterCtx, tableName), where));
4944
5134
  } else if (pkNames.length) {
4945
5135
  q = q.orderBy(...primaryKeyOrderExprs(table, pkNames));
4946
5136
  }