@vantreeseba/drizzle-graphql 7.1.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.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/index.ts
2
- import { is as is8 } from "drizzle-orm";
2
+ import { getColumns as getColumns6, is as is8, Table } from "drizzle-orm";
3
3
  import { MySqlDatabase } from "drizzle-orm/mysql-core";
4
4
  import { PgAsyncDatabase } from "drizzle-orm/pg-core";
5
5
  import { BaseSQLiteDatabase } from "drizzle-orm/sqlite-core";
@@ -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
  }
@@ -1198,6 +1199,30 @@ var resolveLimitPolicy = (global, table) => {
1198
1199
  }
1199
1200
  return { defaultLimit, maxLimit, clampToMax: table?.clampToMax ?? global?.clampToMax ?? false };
1200
1201
  };
1202
+ var excludedColumnRegistry = /* @__PURE__ */ new WeakMap();
1203
+ var registerColumnExclusions = (tables, exclude) => {
1204
+ for (const [tableName, table] of Object.entries(tables)) {
1205
+ const hidden = exclude?.columns?.[tableName];
1206
+ if (hidden?.length) {
1207
+ excludedColumnRegistry.set(table, new Set(hidden));
1208
+ } else {
1209
+ excludedColumnRegistry.delete(table);
1210
+ }
1211
+ }
1212
+ };
1213
+ var hasExcludedColumns = (table) => excludedColumnRegistry.has(table);
1214
+ var excludedColumnsKey = (table) => {
1215
+ const hidden = excludedColumnRegistry.get(table);
1216
+ return hidden ? [...hidden].sort().join(",") : "";
1217
+ };
1218
+ var visibleColumns = (table) => {
1219
+ const columns = getColumns2(table);
1220
+ const hidden = excludedColumnRegistry.get(table);
1221
+ if (!hidden) {
1222
+ return columns;
1223
+ }
1224
+ return Object.fromEntries(Object.entries(columns).filter(([columnName]) => !hidden.has(columnName)));
1225
+ };
1201
1226
  var listFieldComplexity = (options, policy) => ({ args, childComplexity }) => {
1202
1227
  const limit = args["limit"];
1203
1228
  const requested = typeof limit === "number" && limit > 0 ? limit : void 0;
@@ -1317,6 +1342,10 @@ var innerOrder = new GraphQLInputObjectType3({
1317
1342
  nulls: {
1318
1343
  type: orderNulls,
1319
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`."
1320
1349
  }
1321
1350
  }
1322
1351
  });
@@ -1382,10 +1411,59 @@ var jsonFilterFields = (column, colType) => {
1382
1411
  description: "Value structurally contains this JSON (Postgres `@>` / MySQL JSON_CONTAINS)"
1383
1412
  }
1384
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
+ },
1385
1418
  isNull: { type: GraphQLBoolean2 },
1386
1419
  isNotNull: { type: GraphQLBoolean2 }
1387
1420
  };
1388
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
+ });
1389
1467
  var IN_ARRAY_DESCRIPTION = "Matches any one of these values (SQL `IN`)";
1390
1468
  var NOT_IN_ARRAY_DESCRIPTION = "Matches none of these values (SQL `NOT IN`)";
1391
1469
  var arrayFilterFields = (colType, colArr) => {
@@ -1507,17 +1585,20 @@ var generateColumnFilterValues = (column, tableName, columnName, cacheCtx) => {
1507
1585
  };
1508
1586
  var orderMap = /* @__PURE__ */ new WeakMap();
1509
1587
  var generateTableOrderCached = (table) => {
1510
- if (orderMap.has(table)) {
1588
+ const cacheable = !hasExcludedColumns(table);
1589
+ if (cacheable && orderMap.has(table)) {
1511
1590
  return orderMap.get(table);
1512
1591
  }
1513
1592
  let remapped = {};
1514
1593
  try {
1515
- const columns = getColumns2(table);
1594
+ const columns = visibleColumns(table);
1516
1595
  const columnEntries = Object.entries(columns);
1517
1596
  remapped = Object.fromEntries(
1518
1597
  columnEntries.map(([columnName, _columnDescription]) => [columnName, { type: innerOrder }])
1519
1598
  );
1520
- orderMap.set(table, remapped);
1599
+ if (cacheable) {
1600
+ orderMap.set(table, remapped);
1601
+ }
1521
1602
  } catch (_err) {
1522
1603
  }
1523
1604
  return remapped;
@@ -1526,7 +1607,7 @@ var generateTableFilterValuesCached = (table, tableName, cacheCtx) => {
1526
1607
  if (cacheCtx.filterFieldCache.has(table)) {
1527
1608
  return cacheCtx.filterFieldCache.get(table);
1528
1609
  }
1529
- const columns = getColumns2(table);
1610
+ const columns = visibleColumns(table);
1530
1611
  const columnEntries = Object.entries(columns);
1531
1612
  const remapped = Object.fromEntries(
1532
1613
  columnEntries.map(([columnName, column]) => [
@@ -1547,7 +1628,7 @@ var generateTableSelectTypeFieldsCached = (table, tableName, cacheCtx) => {
1547
1628
  if (cacheCtx.selectFieldCache.has(table)) {
1548
1629
  return cacheCtx.selectFieldCache.get(table);
1549
1630
  }
1550
- const columns = getColumns2(table);
1631
+ const columns = visibleColumns(table);
1551
1632
  const columnEntries = Object.entries(columns);
1552
1633
  const remapped = Object.fromEntries(
1553
1634
  columnEntries.map(([columnName, column]) => [
@@ -1905,7 +1986,7 @@ var generateTableTypes = (tableName, tables, relationMap, withReturning, relatio
1905
1986
  relationAggregateFactory
1906
1987
  );
1907
1988
  const table = tables[tableName];
1908
- const columns = getColumns2(table);
1989
+ const columns = visibleColumns(table);
1909
1990
  const columnEntries = Object.entries(columns);
1910
1991
  const relaxedColumns = nestedWrites?.relaxedColumns(tableName);
1911
1992
  const insertFields = Object.fromEntries(
@@ -1976,6 +2057,9 @@ var orderByEntries = (orderArgs) => Object.entries(orderArgs).sort((a, b) => (b[
1976
2057
  if (typeof config === "object" && config.direction === void 0) {
1977
2058
  throw new GraphQLError4(`ORDER BY ${column}: ordering through a relation is not supported in this query`);
1978
2059
  }
2060
+ if (config.matchFilterOrder) {
2061
+ throw new GraphQLError4(`ORDER BY ${column}: 'matchFilterOrder' is not supported in this query`);
2062
+ }
1979
2063
  return [column, config.direction, config.nulls ?? void 0];
1980
2064
  });
1981
2065
  var orderExpressions = (expr, direction, nulls, dialectColumn) => {
@@ -2010,7 +2094,22 @@ var extendRelationOrderChain = (parentTable, relationName, relEntry, ctx, chain)
2010
2094
  table: aliasedTarget
2011
2095
  };
2012
2096
  };
2013
- 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) => {
2014
2113
  const columns = getColumns2(table);
2015
2114
  const relations = ctx?.relationMap[tableKey];
2016
2115
  for (const [key, config] of Object.entries(orderArgs)) {
@@ -2019,8 +2118,11 @@ var collectOrderEntries = (table, tableKey, orderArgs, ctx, chain, out) => {
2019
2118
  }
2020
2119
  const column = columns[key];
2021
2120
  if (column) {
2121
+ if (config.matchFilterOrder && chain) {
2122
+ throw new GraphQLError4(`ORDER BY ${key}: 'matchFilterOrder' is not supported through a relation`);
2123
+ }
2022
2124
  out.push({
2023
- 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,
2024
2126
  column,
2025
2127
  direction: config.direction,
2026
2128
  nulls: config.nulls ?? void 0,
@@ -2033,12 +2135,12 @@ var collectOrderEntries = (table, tableKey, orderArgs, ctx, chain, out) => {
2033
2135
  throw new GraphQLError4(`ORDER BY ${key}: Unknown column or relation`);
2034
2136
  }
2035
2137
  const nextChain = extendRelationOrderChain(chain?.table ?? table, key, relEntry, ctx, chain);
2036
- collectOrderEntries(nextChain.table, relEntry.targetTableName, config, ctx, nextChain, out);
2138
+ collectOrderEntries(nextChain.table, relEntry.targetTableName, config, ctx, nextChain, out, void 0);
2037
2139
  }
2038
2140
  };
2039
- var extractOrderBy = (table, orderArgs, ctx) => {
2141
+ var extractOrderBy = (table, orderArgs, ctx, whereArgs) => {
2040
2142
  const entries = [];
2041
- collectOrderEntries(table, ctx?.tableKey ?? "", orderArgs, ctx, void 0, entries);
2143
+ collectOrderEntries(table, ctx?.tableKey ?? "", orderArgs, ctx, void 0, entries, whereArgs);
2042
2144
  return entries.sort((a, b) => b.priority - a.priority).flatMap((entry) => orderExpressions(entry.expression, entry.direction, entry.nulls, entry.column));
2043
2145
  };
2044
2146
  var LIKE_ESCAPE_CHAR = "\\";
@@ -2070,6 +2172,105 @@ var jsonContains = (column, columnName, value) => {
2070
2172
  throw new GraphQLError4(`WHERE ${columnName}: Operator 'contains' is not supported for this dialect!`);
2071
2173
  }
2072
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
+ };
2073
2274
  var extractFiltersColumn = (column, columnName, operators) => {
2074
2275
  const { OR, AND, NOT, ...restOperators } = operators;
2075
2276
  const entries = Object.entries(restOperators);
@@ -2091,6 +2292,13 @@ var extractFiltersColumn = (column, columnName, operators) => {
2091
2292
  variants.push(singleValueOps[operatorName](column, singleValue));
2092
2293
  } else if (operatorName in stringValueOps) {
2093
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
+ }
2094
2302
  } else if (operatorName === "contains" && isJsonColumn2(column)) {
2095
2303
  variants.push(jsonContains(column, columnName, operatorValue));
2096
2304
  } else if (operatorName in safeLikeOps) {
@@ -2098,7 +2306,8 @@ var extractFiltersColumn = (column, columnName, operators) => {
2098
2306
  variants.push(safeLikeCondition(column, buildPattern(operatorValue), insensitive));
2099
2307
  } else if (operatorName in arrayValueOps) {
2100
2308
  if (!operatorValue.length) {
2101
- throw new GraphQLError4(`WHERE ${columnName}: Unable to use operator ${operatorName} with an empty array!`);
2309
+ variants.push(operatorName === "inArray" ? sqlFalse() : sqlTrue());
2310
+ continue;
2102
2311
  }
2103
2312
  const arrayValue = operatorValue.map((val) => remapFromGraphQLCore(val, column, columnName));
2104
2313
  variants.push(arrayValueOps[operatorName](column, arrayValue));
@@ -2106,7 +2315,8 @@ var extractFiltersColumn = (column, columnName, operators) => {
2106
2315
  variants.push(arrayContains(column, [operatorValue]));
2107
2316
  } else if (operatorName in arrayMembershipOps) {
2108
2317
  if (!operatorValue.length) {
2109
- throw new GraphQLError4(`WHERE ${columnName}: Unable to use operator ${operatorName} with an empty array!`);
2318
+ variants.push(operatorName === "hasSome" ? sqlFalse() : sqlTrue());
2319
+ continue;
2110
2320
  }
2111
2321
  variants.push(arrayMembershipOps[operatorName](column, operatorValue));
2112
2322
  } else if (operatorName === "isEmpty") {
@@ -2369,7 +2579,7 @@ var extractRelationsParamsInner = (relationMap, tables, tableName, typeName, ori
2369
2579
  } : void 0;
2370
2580
  const hasPagination = offset != null || limit != null;
2371
2581
  const pkNames = targetPkNames ?? [];
2372
- 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;
2373
2583
  thisRecord.offset = offset;
2374
2584
  thisRecord.limit = limit;
2375
2585
  const relWith = relationField ? extractRelationsParamsInner(
@@ -2449,6 +2659,15 @@ var primaryKeyOrderExprs = (table, pkNames) => {
2449
2659
  var CURSOR_FIELD_NAME = "cursor";
2450
2660
  var ROW_CURSOR_PROP = "__drizzle_graphql_cursor";
2451
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
+ };
2452
2671
  var cursorOrderingEntries = (orderBy, pkNames) => {
2453
2672
  const entries = orderBy ? orderByEntries(orderBy) : [];
2454
2673
  const seen = new Set(entries.map(([column]) => column));
@@ -2580,7 +2799,7 @@ var attachRowCursors = (rows, entries) => {
2580
2799
  };
2581
2800
  var rowCursorResolver = (source) => source?.[ROW_CURSOR_PROP] ?? null;
2582
2801
  var getUniqueColumnSets = (table, getTableConfig4) => {
2583
- const cols = getColumns2(table);
2802
+ const cols = visibleColumns(table);
2584
2803
  const propNameByColumnName = new Map(Object.entries(cols).map(([propName, col]) => [col.name, propName]));
2585
2804
  const toPropNames = (columnNames) => {
2586
2805
  const propNames = [];
@@ -2620,12 +2839,13 @@ var getUniqueColumnSets = (table, getTableConfig4) => {
2620
2839
  var DISTINCT_RN = "__drizzle_graphql_distinct_rn";
2621
2840
  var columnEnumCache = /* @__PURE__ */ new WeakMap();
2622
2841
  var generateColumnEnum = (table, enumName, description, predicate = () => true) => {
2842
+ const cacheKey = `${enumName}\0${excludedColumnsKey(table)}`;
2623
2843
  let tableCache = columnEnumCache.get(table);
2624
- const cached = tableCache?.get(enumName);
2844
+ const cached = tableCache?.get(cacheKey);
2625
2845
  if (cached) {
2626
2846
  return cached;
2627
2847
  }
2628
- const columnNames = Object.entries(getColumns2(table)).filter(([columnName, column]) => predicate(column, columnName)).map(([columnName]) => columnName);
2848
+ const columnNames = Object.entries(visibleColumns(table)).filter(([columnName, column]) => predicate(column, columnName)).map(([columnName]) => columnName);
2629
2849
  if (!columnNames.length) {
2630
2850
  return void 0;
2631
2851
  }
@@ -2638,7 +2858,7 @@ var generateColumnEnum = (table, enumName, description, predicate = () => true)
2638
2858
  tableCache = /* @__PURE__ */ new Map();
2639
2859
  columnEnumCache.set(table, tableCache);
2640
2860
  }
2641
- tableCache.set(enumName, enumType);
2861
+ tableCache.set(cacheKey, enumType);
2642
2862
  return enumType;
2643
2863
  };
2644
2864
  var generateDistinctEnum = (table, typeName) => generateColumnEnum(table, `${typeName}DistinctColumn`, `Columns of ${typeName} that a query can be made distinct on`);
@@ -3044,11 +3264,10 @@ var runRelationalSelect = async (opts) => {
3044
3264
  if (after != null && distinct) {
3045
3265
  throw new GraphQLError4("'after' cannot be combined with 'distinct'.");
3046
3266
  }
3047
- if (orderByHasRelationEntry(orderBy)) {
3267
+ const cursorObstacle = orderByCursorObstacle(orderBy);
3268
+ if (cursorObstacle) {
3048
3269
  if (after != null) {
3049
- throw new GraphQLError4(
3050
- "'after' cannot be combined with an orderBy that orders through a relation \u2014 a related row's value cannot be encoded into a cursor."
3051
- );
3270
+ throw new GraphQLError4(cursorObstacle);
3052
3271
  }
3053
3272
  } else if (!pkNames?.length) {
3054
3273
  if (after != null) {
@@ -3088,13 +3307,13 @@ var runRelationalSelect = async (opts) => {
3088
3307
  // drizzle-orm v1 RQB calls orderBy/where with the aliased table proxy — use it
3089
3308
  // directly so column refs match the CTE alias.
3090
3309
  orderBy: distinctKeys ? (aliasedTable2) => [
3091
- ...orderBy ? extractOrderBy(aliasedTable2, orderBy, relationFilterCtx(filterCtx, tableName)) : [],
3310
+ ...orderBy ? extractOrderBy(aliasedTable2, orderBy, relationFilterCtx(filterCtx, tableName), where) : [],
3092
3311
  ...primaryKeyOrderExprs(aliasedTable2, pkNames)
3093
3312
  ] : cursorEntries ? (
3094
3313
  // Cursor pagination needs a total order: the request's orderBy plus the PK tiebreak,
3095
3314
  // exactly the ordering the cursor encodes and the keyset predicate compares against.
3096
3315
  (aliasedTable2) => cursorOrderExprs(aliasedTable2, cursorEntries)
3097
- ) : 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,
3098
3317
  where: distinctKeys ? { RAW: (aliasedTable2) => primaryKeyRestriction(aliasedTable2, pkNames, distinctKeys) } : where || cursorValues ? {
3099
3318
  RAW: (aliasedTable2) => and(
3100
3319
  where ? extractFilters(aliasedTable2, tableName, where, relationFilterCtx(filterCtx, tableName)) : void 0,
@@ -3258,7 +3477,7 @@ var classifyAggregateColumns = (table, tableName) => {
3258
3477
  const numeric = {};
3259
3478
  const orderable = {};
3260
3479
  const all = {};
3261
- for (const [columnName, column] of Object.entries(getColumns3(table))) {
3480
+ for (const [columnName, column] of Object.entries(visibleColumns(table))) {
3262
3481
  all[columnName] = column;
3263
3482
  const converted = drizzleColumnToGraphQLType(column, columnName, tableName, true, false, false);
3264
3483
  const gqlType = converted.type;
@@ -3502,7 +3721,7 @@ var HAVING_OPS = { eq: eq2, ne: ne2, gt: gt2, gte: gte2, lt: lt2, lte: lte2 };
3502
3721
  var groupableColumns = (table, tableName) => {
3503
3722
  const { orderable } = classifyAggregateColumns(table, tableName);
3504
3723
  const groupable = { ...orderable };
3505
- for (const [columnName, column] of Object.entries(getColumns3(table))) {
3724
+ for (const [columnName, column] of Object.entries(visibleColumns(table))) {
3506
3725
  if (groupable[columnName]) {
3507
3726
  continue;
3508
3727
  }
@@ -3970,7 +4189,8 @@ var generateSchemaData = (db, schema, relations, options) => {
3970
4189
  const { relationsDepthLimit, prefixes, suffixes, typeNameMapper, shouldEagerLoad, features, complexity, limits } = options;
3971
4190
  const rawSchema = schema;
3972
4191
  const schemaEntries = Object.entries(rawSchema);
3973
- const tableEntries = schemaEntries.filter(([_key, value]) => is4(value, MySqlTable));
4192
+ const excludedTables = new Set(options.exclude?.tables ?? []);
4193
+ const tableEntries = schemaEntries.filter(([key, value]) => is4(value, MySqlTable) && !excludedTables.has(key));
3974
4194
  const tables = Object.fromEntries(tableEntries);
3975
4195
  if (!tableEntries.length) {
3976
4196
  throw new Error(
@@ -3984,7 +4204,11 @@ var generateSchemaData = (db, schema, relations, options) => {
3984
4204
  }
3985
4205
  registerScalarOverrides(tables, options);
3986
4206
  registerEnumConfig(options);
4207
+ registerColumnExclusions(tables, options.exclude);
3987
4208
  const namedRelations = buildNamedRelations(relations ?? {}, tableEntries);
4209
+ for (const excluded of excludedTables) {
4210
+ delete namedRelations[excluded];
4211
+ }
3988
4212
  attachTargetPrimaryKeys(namedRelations, tables, mysqlPrimaryKeyPropNames);
3989
4213
  const eagerRelations = pruneNonEagerRelations(namedRelations, shouldEagerLoad);
3990
4214
  const filterCtx = { tables, relationMap: namedRelations };
@@ -4463,7 +4687,7 @@ var createNestedWriteTypes = (params) => {
4463
4687
  }
4464
4688
  const omitted = plan.fkSide === "child" ? plan.foreignColPropName : void 0;
4465
4689
  const fields = Object.fromEntries(
4466
- Object.entries(getColumns4(plan.targetTable)).filter(([columnName]) => columnName !== omitted).map(([columnName, column]) => [
4690
+ Object.entries(visibleColumns(plan.targetTable)).filter(([columnName]) => columnName !== omitted).map(([columnName, column]) => [
4467
4691
  columnName,
4468
4692
  drizzleColumnToGraphQLType(column, columnName, plan.targetTableName, false, true, true)
4469
4693
  ])
@@ -4789,11 +5013,10 @@ var generateSelectArray2 = (db, tableName, tables, relationMap, orderArgs, filte
4789
5013
  if (after != null && distinct?.length) {
4790
5014
  throw new GraphQLError8("'after' cannot be combined with 'distinct'.");
4791
5015
  }
4792
- if (orderByHasRelationEntry(orderBy)) {
5016
+ const cursorObstacle = orderByCursorObstacle(orderBy);
5017
+ if (cursorObstacle) {
4793
5018
  if (after != null) {
4794
- throw new GraphQLError8(
4795
- "'after' cannot be combined with an orderBy that orders through a relation \u2014 a related row's value cannot be encoded into a cursor."
4796
- );
5019
+ throw new GraphQLError8(cursorObstacle);
4797
5020
  }
4798
5021
  } else if (!pkNames.length) {
4799
5022
  if (after != null) {
@@ -4841,7 +5064,7 @@ var generateSelectArray2 = (db, tableName, tables, relationMap, orderArgs, filte
4841
5064
  q = q.orderBy(...cursorOrderExprs(table, cursorEntries));
4842
5065
  } else if (orderBy) {
4843
5066
  q = q.orderBy(
4844
- ...extractOrderBy(table, orderBy, relationFilterCtx(filterCtx, tableName)),
5067
+ ...extractOrderBy(table, orderBy, relationFilterCtx(filterCtx, tableName), where),
4845
5068
  ...distinctKeys ? primaryKeyOrderExprs(table, pkNames) : []
4846
5069
  );
4847
5070
  } else if ((distinctKeys || offset != null || limit != null) && pkNames.length) {
@@ -4907,7 +5130,7 @@ var generateSelectSingle2 = (db, tableName, tables, relationMap, orderArgs, filt
4907
5130
  q = q.where(extractFilters(table, tableName, where, relationFilterCtx(filterCtx, tableName)));
4908
5131
  }
4909
5132
  if (orderBy) {
4910
- q = q.orderBy(...extractOrderBy(table, orderBy, relationFilterCtx(filterCtx, tableName)));
5133
+ q = q.orderBy(...extractOrderBy(table, orderBy, relationFilterCtx(filterCtx, tableName), where));
4911
5134
  } else if (pkNames.length) {
4912
5135
  q = q.orderBy(...primaryKeyOrderExprs(table, pkNames));
4913
5136
  }
@@ -5341,7 +5564,8 @@ function generateSchemaData2(db, schema, relations, options) {
5341
5564
  limits
5342
5565
  } = options;
5343
5566
  const schemaEntries = Object.entries(schema);
5344
- const tableEntries = schemaEntries.filter(([_key, value]) => is6(value, PgTable));
5567
+ const excludedTables = new Set(options.exclude?.tables ?? []);
5568
+ const tableEntries = schemaEntries.filter(([key, value]) => is6(value, PgTable) && !excludedTables.has(key));
5345
5569
  const tables = Object.fromEntries(tableEntries);
5346
5570
  if (!tableEntries.length) {
5347
5571
  throw new Error(
@@ -5350,7 +5574,11 @@ function generateSchemaData2(db, schema, relations, options) {
5350
5574
  }
5351
5575
  registerScalarOverrides(tables, options);
5352
5576
  registerEnumConfig(options);
5577
+ registerColumnExclusions(tables, options.exclude);
5353
5578
  const namedRelations = buildNamedRelations(relations ?? {}, tableEntries);
5579
+ for (const excluded of excludedTables) {
5580
+ delete namedRelations[excluded];
5581
+ }
5354
5582
  attachTargetPrimaryKeys(namedRelations, tables, pgPrimaryKeyPropNames);
5355
5583
  const eagerRelations = pruneNonEagerRelations(namedRelations, shouldEagerLoad);
5356
5584
  const filterCtx = { tables, relationMap: namedRelations };
@@ -6358,7 +6586,8 @@ var generateSchemaData3 = (db, schema, relations, options) => {
6358
6586
  } = options;
6359
6587
  const rawSchema = schema;
6360
6588
  const schemaEntries = Object.entries(rawSchema);
6361
- const tableEntries = schemaEntries.filter(([_key, value]) => is7(value, SQLiteTable));
6589
+ const excludedTables = new Set(options.exclude?.tables ?? []);
6590
+ const tableEntries = schemaEntries.filter(([key, value]) => is7(value, SQLiteTable) && !excludedTables.has(key));
6362
6591
  const tables = Object.fromEntries(tableEntries);
6363
6592
  if (!tableEntries.length) {
6364
6593
  throw new Error(
@@ -6367,7 +6596,11 @@ var generateSchemaData3 = (db, schema, relations, options) => {
6367
6596
  }
6368
6597
  registerScalarOverrides(tables, options);
6369
6598
  registerEnumConfig(options);
6599
+ registerColumnExclusions(tables, options.exclude);
6370
6600
  const namedRelations = buildNamedRelations(relations ?? {}, tableEntries);
6601
+ for (const excluded of excludedTables) {
6602
+ delete namedRelations[excluded];
6603
+ }
6371
6604
  attachTargetPrimaryKeys(namedRelations, tables, sqlitePrimaryKeyPropNames);
6372
6605
  const eagerRelations = pruneNonEagerRelations(namedRelations, shouldEagerLoad);
6373
6606
  const filterCtx = { tables, relationMap: namedRelations };
@@ -6914,6 +7147,49 @@ var buildSchema = (db, config) => {
6914
7147
  limitPolicyCache.set(tableName, resolved);
6915
7148
  return resolved;
6916
7149
  } : void 0;
7150
+ const exclude = config?.exclude;
7151
+ if (exclude) {
7152
+ const tableNames = new Set(
7153
+ Object.entries(schema).filter(([, value]) => is8(value, Table)).map(([key]) => key)
7154
+ );
7155
+ const excludedTables = new Set(exclude.tables ?? []);
7156
+ for (const tableName of excludedTables) {
7157
+ if (!tableNames.has(tableName)) {
7158
+ throw new Error(
7159
+ `Drizzle-GraphQL Error: config.exclude.tables names '${tableName}', which is not a table in the Drizzle schema.`
7160
+ );
7161
+ }
7162
+ }
7163
+ if (excludedTables.size >= tableNames.size) {
7164
+ throw new Error(
7165
+ "Drizzle-GraphQL Error: config.exclude.tables excludes every table in the schema, leaving nothing to generate."
7166
+ );
7167
+ }
7168
+ for (const [tableName, columnNames] of Object.entries(exclude.columns ?? {})) {
7169
+ if (!tableNames.has(tableName)) {
7170
+ throw new Error(
7171
+ `Drizzle-GraphQL Error: config.exclude.columns names table '${tableName}', which is not a table in the Drizzle schema.`
7172
+ );
7173
+ }
7174
+ if (excludedTables.has(tableName)) {
7175
+ continue;
7176
+ }
7177
+ const columns = getColumns6(schema[tableName]);
7178
+ for (const columnName of columnNames) {
7179
+ const column = columns[columnName];
7180
+ if (!column) {
7181
+ throw new Error(
7182
+ `Drizzle-GraphQL Error: config.exclude.columns names '${tableName}.${columnName}', which is not a column of that table.`
7183
+ );
7184
+ }
7185
+ if (column.notNull && !column.hasDefault && !column.defaultFn) {
7186
+ console.warn(
7187
+ `Drizzle-GraphQL Warning: excluded column '${tableName}.${columnName}' is NOT NULL with no default, so generated inserts for '${tableName}' can never succeed.`
7188
+ );
7189
+ }
7190
+ }
7191
+ }
7192
+ }
6917
7193
  const eagerOpt = config?.eagerLoadRelations;
6918
7194
  const shouldEagerLoad = eagerOpt === void 0 || eagerOpt === true ? () => true : eagerOpt === false ? () => false : eagerOpt;
6919
7195
  if (!typeNameMapper && suffixes.list === suffixes.single) {
@@ -6947,6 +7223,7 @@ var buildSchema = (db, config) => {
6947
7223
  enumNameMapper: config?.enumNameMapper,
6948
7224
  transactions,
6949
7225
  limits,
7226
+ exclude,
6950
7227
  docs: {
6951
7228
  describeColumn: config?.describeColumn,
6952
7229
  describeTable: config?.describeTable,