@vantreeseba/drizzle-graphql 7.2.0 → 7.4.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
@@ -103,6 +103,10 @@ var getOrCreateLoader = (context, key, batchFn) => {
103
103
  import pluralize from "pluralize";
104
104
  var uncapitalize = (input) => input?.length ? `${input[0].toLocaleLowerCase()}${input.length > 1 ? input.slice(1, input.length) : ""}` : input;
105
105
  var capitalize = (input) => input?.length ? `${input[0].toLocaleUpperCase()}${input.length > 1 ? input.slice(1, input.length) : ""}` : input;
106
+ var singularizeMapper = (tableName) => ({
107
+ singular: uncapitalize(pluralize.singular(tableName)),
108
+ plural: uncapitalize(pluralize.plural(tableName))
109
+ });
106
110
 
107
111
  // src/util/data-mappers/index.ts
108
112
  import { getTableColumns as getTableColumns2, is as is2, One } from "drizzle-orm";
@@ -1012,10 +1016,10 @@ var runMutation = async (db, context, info, txCtx, body) => {
1012
1016
  }
1013
1017
  return runOnSharedTx(state, body);
1014
1018
  };
1015
- var batchedPaginatedRelationQuery = async (db, targetTable, foreignCol, whereCondition, orderByArg, limit, offset, pkNames, orderCtx) => {
1019
+ var batchedPaginatedRelationQuery = async (db, targetTable, foreignCol, whereCondition, orderByArg, limit, offset, pkNames, orderCtx, whereArgs) => {
1016
1020
  const cols = getColumns2(targetTable);
1017
1021
  const orderExprs = [
1018
- ...orderByArg ? extractOrderBy(targetTable, orderByArg, orderCtx) : [],
1022
+ ...orderByArg ? extractOrderBy(targetTable, orderByArg, orderCtx, whereArgs) : [],
1019
1023
  ...primaryKeyOrderExprs(targetTable, pkNames)
1020
1024
  ];
1021
1025
  const orderClause = orderExprs.length ? sql2` order by ${sql2.join(orderExprs, sql2`, `)}` : sql2``;
@@ -1126,11 +1130,12 @@ var createRelationResolverFactory = (db, tables, nullOrdering, filterCtx, limits
1126
1130
  limit ?? null,
1127
1131
  offset ?? null,
1128
1132
  targetPkNames,
1129
- relationFilterCtx(filterCtx, targetTableName)
1133
+ relationFilterCtx(filterCtx, targetTableName),
1134
+ whereArg
1130
1135
  );
1131
1136
  } else {
1132
1137
  let q = executor.select().from(targetTable).where(whereCondition);
1133
- const orderExprs = cursorEntries ? cursorOrderExprs(targetTable, cursorEntries) : orderByArg ? extractOrderBy(targetTable, orderByArg, relationFilterCtx(filterCtx, targetTableName)) : [];
1138
+ const orderExprs = cursorEntries ? cursorOrderExprs(targetTable, cursorEntries) : orderByArg ? extractOrderBy(targetTable, orderByArg, relationFilterCtx(filterCtx, targetTableName), whereArg) : [];
1134
1139
  if (orderExprs.length) {
1135
1140
  q = q.orderBy(...orderExprs);
1136
1141
  }
@@ -1341,6 +1346,10 @@ var innerOrder = new GraphQLInputObjectType3({
1341
1346
  nulls: {
1342
1347
  type: orderNulls,
1343
1348
  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)"
1349
+ },
1350
+ matchFilterOrder: {
1351
+ type: GraphQLBoolean2,
1352
+ 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
1353
  }
1345
1354
  }
1346
1355
  });
@@ -1406,10 +1415,59 @@ var jsonFilterFields = (column, colType) => {
1406
1415
  description: "Value structurally contains this JSON (Postgres `@>` / MySQL JSON_CONTAINS)"
1407
1416
  }
1408
1417
  } : {},
1418
+ path: {
1419
+ type: new GraphQLList3(new GraphQLNonNull3(jsonPathFilter)),
1420
+ description: "Compares the value at one path inside the document. Several entries are ANDed; a single object may be passed without the list brackets."
1421
+ },
1409
1422
  isNull: { type: GraphQLBoolean2 },
1410
1423
  isNotNull: { type: GraphQLBoolean2 }
1411
1424
  };
1412
1425
  };
1426
+ var jsonPathCast = new GraphQLEnumType2({
1427
+ name: "JSONPathCast",
1428
+ description: "How to read the value at a JSON path before comparing it",
1429
+ values: {
1430
+ TEXT: { value: "text", description: "Compare as text (lexicographic ordering)" },
1431
+ NUMBER: { value: "number", description: "Compare as a number; a non-numeric value never matches" },
1432
+ BOOLEAN: { value: "boolean", description: "Compare as a boolean" }
1433
+ }
1434
+ });
1435
+ var jsonPathFilter = new GraphQLInputObjectType3({
1436
+ name: "JSONPathFilter",
1437
+ fields: {
1438
+ path: {
1439
+ type: new GraphQLNonNull3(new GraphQLList3(new GraphQLNonNull3(GraphQLString2))),
1440
+ description: 'Keys to walk from the document root, e.g. `["profile", "level"]`. An all-digits key indexes an array.'
1441
+ },
1442
+ as: { type: jsonPathCast, description: "Overrides how the value is read before comparing" },
1443
+ eq: { type: GraphQLJSON, description: "Equal to" },
1444
+ ne: { type: GraphQLJSON, description: "Not equal to" },
1445
+ lt: { type: GraphQLJSON, description: "Less than" },
1446
+ lte: { type: GraphQLJSON, description: "Less than or equal to" },
1447
+ gt: { type: GraphQLJSON, description: "Greater than" },
1448
+ gte: { type: GraphQLJSON, description: "Greater than or equal to" },
1449
+ startsWith: {
1450
+ type: GraphQLString2,
1451
+ description: "Extracted value starts with this string. `%`, `_` and `\\` are matched literally."
1452
+ },
1453
+ endsWith: {
1454
+ type: GraphQLString2,
1455
+ description: "Extracted value ends with this string. `%`, `_` and `\\` are matched literally."
1456
+ },
1457
+ contains: {
1458
+ type: GraphQLString2,
1459
+ description: "Extracted value contains this string. `%`, `_` and `\\` are matched literally."
1460
+ },
1461
+ iStartsWith: { type: GraphQLString2, description: "Case-insensitive `startsWith`." },
1462
+ iEndsWith: { type: GraphQLString2, description: "Case-insensitive `endsWith`." },
1463
+ iContains: { type: GraphQLString2, description: "Case-insensitive `contains`." },
1464
+ isNull: {
1465
+ type: GraphQLBoolean2,
1466
+ description: "When true, matches rows where the path is missing or holds JSON null"
1467
+ },
1468
+ isNotNull: { type: GraphQLBoolean2, description: "When true, matches rows where the path holds a value" }
1469
+ }
1470
+ });
1413
1471
  var IN_ARRAY_DESCRIPTION = "Matches any one of these values (SQL `IN`)";
1414
1472
  var NOT_IN_ARRAY_DESCRIPTION = "Matches none of these values (SQL `NOT IN`)";
1415
1473
  var arrayFilterFields = (colType, colArr) => {
@@ -1844,7 +1902,7 @@ var generateSelectFields = (tables, tableName, relationMap, fromTableName, fromR
1844
1902
  ]);
1845
1903
  continue;
1846
1904
  }
1847
- const targetDistinctEnum = cacheCtx.features.distinct ? generateDistinctEnum(tables[targetTableName], resolveTypeName(targetTableName, typeNameMapper)) : void 0;
1905
+ const targetDistinctEnum = cacheCtx.featureOf(targetTableName).distinct ? generateDistinctEnum(tables[targetTableName], resolveTypeName(targetTableName, typeNameMapper)) : void 0;
1848
1906
  rawRelationFields.push([
1849
1907
  relationName,
1850
1908
  {
@@ -1954,7 +2012,7 @@ var generateTableTypes = (tableName, tables, relationMap, withReturning, relatio
1954
2012
  const updateFields = Object.fromEntries(
1955
2013
  columnEntries.map(([columnName, column]) => {
1956
2014
  const converted = drizzleColumnToGraphQLType(column, columnName, tableName, true, false, true);
1957
- const operations = cacheCtx.features.fieldUpdateOperations ? fieldUpdateInputType(column, columnName, tableName) : void 0;
2015
+ const operations = cacheCtx.featureOf(tableName).fieldUpdateOperations ? fieldUpdateInputType(column, columnName, tableName) : void 0;
1958
2016
  const field = operations ? { ...converted, type: operations } : converted;
1959
2017
  return [columnName, { ...field, ...inputFieldDocs(cacheCtx.docs, column, tableName, columnName, field.type) }];
1960
2018
  })
@@ -2003,6 +2061,9 @@ var orderByEntries = (orderArgs) => Object.entries(orderArgs).sort((a, b) => (b[
2003
2061
  if (typeof config === "object" && config.direction === void 0) {
2004
2062
  throw new GraphQLError4(`ORDER BY ${column}: ordering through a relation is not supported in this query`);
2005
2063
  }
2064
+ if (config.matchFilterOrder) {
2065
+ throw new GraphQLError4(`ORDER BY ${column}: 'matchFilterOrder' is not supported in this query`);
2066
+ }
2006
2067
  return [column, config.direction, config.nulls ?? void 0];
2007
2068
  });
2008
2069
  var orderExpressions = (expr, direction, nulls, dialectColumn) => {
@@ -2037,7 +2098,22 @@ var extendRelationOrderChain = (parentTable, relationName, relEntry, ctx, chain)
2037
2098
  table: aliasedTarget
2038
2099
  };
2039
2100
  };
2040
- var collectOrderEntries = (table, tableKey, orderArgs, ctx, chain, out) => {
2101
+ var filterOrderExpression = (column, columnName, whereArgs) => {
2102
+ const values = whereArgs?.[columnName]?.inArray;
2103
+ if (!Array.isArray(values)) {
2104
+ throw new GraphQLError4(
2105
+ `ORDER BY ${columnName}: 'matchFilterOrder' needs an 'inArray' filter on the same column in this query's 'where'`
2106
+ );
2107
+ }
2108
+ if (!values.length) {
2109
+ return sql2`0`;
2110
+ }
2111
+ const branches = values.map(
2112
+ (value, index) => sql2`when ${column} = ${remapFromGraphQLCore(value, column, columnName)} then ${sql2.raw(String(index))}`
2113
+ );
2114
+ return sql2`case ${sql2.join(branches, sql2` `)} else ${sql2.raw(String(values.length))} end`;
2115
+ };
2116
+ var collectOrderEntries = (table, tableKey, orderArgs, ctx, chain, out, whereArgs) => {
2041
2117
  const columns = getColumns2(table);
2042
2118
  const relations = ctx?.relationMap[tableKey];
2043
2119
  for (const [key, config] of Object.entries(orderArgs)) {
@@ -2046,8 +2122,11 @@ var collectOrderEntries = (table, tableKey, orderArgs, ctx, chain, out) => {
2046
2122
  }
2047
2123
  const column = columns[key];
2048
2124
  if (column) {
2125
+ if (config.matchFilterOrder && chain) {
2126
+ throw new GraphQLError4(`ORDER BY ${key}: 'matchFilterOrder' is not supported through a relation`);
2127
+ }
2049
2128
  out.push({
2050
- expression: chain ? sql2`(select ${column} from ${sql2.join(chain.fromParts, sql2`, `)} where ${and(...chain.conditions)})` : column,
2129
+ expression: config.matchFilterOrder ? filterOrderExpression(column, key, whereArgs) : chain ? sql2`(select ${column} from ${sql2.join(chain.fromParts, sql2`, `)} where ${and(...chain.conditions)})` : column,
2051
2130
  column,
2052
2131
  direction: config.direction,
2053
2132
  nulls: config.nulls ?? void 0,
@@ -2060,12 +2139,12 @@ var collectOrderEntries = (table, tableKey, orderArgs, ctx, chain, out) => {
2060
2139
  throw new GraphQLError4(`ORDER BY ${key}: Unknown column or relation`);
2061
2140
  }
2062
2141
  const nextChain = extendRelationOrderChain(chain?.table ?? table, key, relEntry, ctx, chain);
2063
- collectOrderEntries(nextChain.table, relEntry.targetTableName, config, ctx, nextChain, out);
2142
+ collectOrderEntries(nextChain.table, relEntry.targetTableName, config, ctx, nextChain, out, void 0);
2064
2143
  }
2065
2144
  };
2066
- var extractOrderBy = (table, orderArgs, ctx) => {
2145
+ var extractOrderBy = (table, orderArgs, ctx, whereArgs) => {
2067
2146
  const entries = [];
2068
- collectOrderEntries(table, ctx?.tableKey ?? "", orderArgs, ctx, void 0, entries);
2147
+ collectOrderEntries(table, ctx?.tableKey ?? "", orderArgs, ctx, void 0, entries, whereArgs);
2069
2148
  return entries.sort((a, b) => b.priority - a.priority).flatMap((entry) => orderExpressions(entry.expression, entry.direction, entry.nulls, entry.column));
2070
2149
  };
2071
2150
  var LIKE_ESCAPE_CHAR = "\\";
@@ -2097,6 +2176,105 @@ var jsonContains = (column, columnName, value) => {
2097
2176
  throw new GraphQLError4(`WHERE ${columnName}: Operator 'contains' is not supported for this dialect!`);
2098
2177
  }
2099
2178
  };
2179
+ var sqlFalse = () => sql2`1 = 0`;
2180
+ var sqlTrue = () => sql2`1 = 1`;
2181
+ var jsonPathString = (path) => `$${path.map((part) => /^\d+$/.test(part) ? `[${part}]` : `."${part.replace(/(["\\])/g, "\\$1")}"`).join("")}`;
2182
+ var NUMERIC_TEXT_PATTERN = "^\\s*-?(\\d+\\.?\\d*|\\.\\d+)([eE][-+]?\\d+)?\\s*$";
2183
+ var jsonPathExprs = (column, path) => {
2184
+ switch (columnDialect(column)) {
2185
+ case "pg": {
2186
+ const pathArray = sql2`array[${sql2.join(
2187
+ path.map((part) => sql2`${part}`),
2188
+ sql2`, `
2189
+ )}]::text[]`;
2190
+ const text = sql2`(${column} #>> ${pathArray})`;
2191
+ return {
2192
+ text,
2193
+ number: sql2`(case when ${text} ~ ${NUMERIC_TEXT_PATTERN} then ${text}::numeric end)`,
2194
+ boolean: text,
2195
+ encodeBoolean: (value) => String(value)
2196
+ };
2197
+ }
2198
+ case "mysql": {
2199
+ const extracted = sql2`json_extract(${column}, ${jsonPathString(path)})`;
2200
+ const text = sql2`(case when json_type(${extracted}) = 'NULL' then null else json_unquote(${extracted}) end)`;
2201
+ return {
2202
+ text,
2203
+ number: sql2`(case when ${text} regexp ${NUMERIC_TEXT_PATTERN} then cast(${text} as decimal(65, 30)) end)`,
2204
+ boolean: text,
2205
+ encodeBoolean: (value) => String(value)
2206
+ };
2207
+ }
2208
+ default: {
2209
+ const extracted = sql2`json_extract(${column}, ${jsonPathString(path)})`;
2210
+ return {
2211
+ text: sql2`cast(${extracted} as text)`,
2212
+ number: sql2`(case when typeof(${extracted}) in ('integer', 'real') then ${extracted} end)`,
2213
+ // SQLite has no boolean type: a JSON `true` comes back as the integer 1.
2214
+ boolean: extracted,
2215
+ encodeBoolean: (value) => value ? 1 : 0
2216
+ };
2217
+ }
2218
+ }
2219
+ };
2220
+ var JSON_PATH_COMPARISONS = {
2221
+ eq: "=",
2222
+ ne: "<>",
2223
+ lt: "<",
2224
+ lte: "<=",
2225
+ gt: ">",
2226
+ gte: ">="
2227
+ };
2228
+ var inferJsonPathCast = (value) => typeof value === "number" ? "number" : typeof value === "boolean" ? "boolean" : "text";
2229
+ var jsonPathCondition = (column, columnName, filter) => {
2230
+ const { path, as: castOverride, ...operators } = filter;
2231
+ const locator = `${columnName}.path`;
2232
+ if (!Array.isArray(path) || !path.length) {
2233
+ throw new GraphQLError4(`WHERE ${locator}: 'path' must name at least one key`);
2234
+ }
2235
+ const exprs = jsonPathExprs(column, path);
2236
+ const variants = [];
2237
+ for (const [operatorName, operatorValue] of Object.entries(operators)) {
2238
+ if (operatorValue === null || operatorValue === void 0) {
2239
+ continue;
2240
+ }
2241
+ if (operatorName === "isNull" || operatorName === "isNotNull") {
2242
+ if (operatorValue === false) {
2243
+ continue;
2244
+ }
2245
+ variants.push(operatorName === "isNull" ? sql2`${exprs.text} is null` : sql2`${exprs.text} is not null`);
2246
+ continue;
2247
+ }
2248
+ if (operatorName in safeLikeOps) {
2249
+ const { buildPattern, insensitive } = safeLikeOps[operatorName];
2250
+ if (typeof operatorValue !== "string") {
2251
+ throw new GraphQLError4(`WHERE ${locator}: operator '${operatorName}' takes a string`);
2252
+ }
2253
+ variants.push(safeLikeCondition(exprs.text, buildPattern(operatorValue), insensitive));
2254
+ continue;
2255
+ }
2256
+ const comparison = JSON_PATH_COMPARISONS[operatorName];
2257
+ if (!comparison) {
2258
+ throw new GraphQLError4(`WHERE ${locator}: Unknown operator: ${operatorName}`);
2259
+ }
2260
+ const cast = castOverride ?? inferJsonPathCast(operatorValue);
2261
+ if (cast === "number") {
2262
+ const numeric = Number(operatorValue);
2263
+ if (Number.isNaN(numeric)) {
2264
+ throw new GraphQLError4(
2265
+ `WHERE ${locator}: operator '${operatorName}' compares as a number, so its value must be one`
2266
+ );
2267
+ }
2268
+ variants.push(sql2`${exprs.number} ${sql2.raw(comparison)} ${numeric}`);
2269
+ } else if (cast === "boolean") {
2270
+ const truthy = operatorValue === true || operatorValue === "true";
2271
+ variants.push(sql2`${exprs.boolean} ${sql2.raw(comparison)} ${exprs.encodeBoolean(truthy)}`);
2272
+ } else {
2273
+ variants.push(sql2`${exprs.text} ${sql2.raw(comparison)} ${String(operatorValue)}`);
2274
+ }
2275
+ }
2276
+ return variants.length ? variants.length > 1 ? and(...variants) : variants[0] : void 0;
2277
+ };
2100
2278
  var extractFiltersColumn = (column, columnName, operators) => {
2101
2279
  const { OR, AND, NOT, ...restOperators } = operators;
2102
2280
  const entries = Object.entries(restOperators);
@@ -2118,6 +2296,13 @@ var extractFiltersColumn = (column, columnName, operators) => {
2118
2296
  variants.push(singleValueOps[operatorName](column, singleValue));
2119
2297
  } else if (operatorName in stringValueOps) {
2120
2298
  variants.push(stringValueOps[operatorName](column, operatorValue));
2299
+ } else if (operatorName === "path" && isJsonColumn2(column)) {
2300
+ for (const pathFilter of operatorValue) {
2301
+ const extracted = jsonPathCondition(column, columnName, pathFilter);
2302
+ if (extracted) {
2303
+ variants.push(extracted);
2304
+ }
2305
+ }
2121
2306
  } else if (operatorName === "contains" && isJsonColumn2(column)) {
2122
2307
  variants.push(jsonContains(column, columnName, operatorValue));
2123
2308
  } else if (operatorName in safeLikeOps) {
@@ -2125,7 +2310,8 @@ var extractFiltersColumn = (column, columnName, operators) => {
2125
2310
  variants.push(safeLikeCondition(column, buildPattern(operatorValue), insensitive));
2126
2311
  } else if (operatorName in arrayValueOps) {
2127
2312
  if (!operatorValue.length) {
2128
- throw new GraphQLError4(`WHERE ${columnName}: Unable to use operator ${operatorName} with an empty array!`);
2313
+ variants.push(operatorName === "inArray" ? sqlFalse() : sqlTrue());
2314
+ continue;
2129
2315
  }
2130
2316
  const arrayValue = operatorValue.map((val) => remapFromGraphQLCore(val, column, columnName));
2131
2317
  variants.push(arrayValueOps[operatorName](column, arrayValue));
@@ -2133,7 +2319,8 @@ var extractFiltersColumn = (column, columnName, operators) => {
2133
2319
  variants.push(arrayContains(column, [operatorValue]));
2134
2320
  } else if (operatorName in arrayMembershipOps) {
2135
2321
  if (!operatorValue.length) {
2136
- throw new GraphQLError4(`WHERE ${columnName}: Unable to use operator ${operatorName} with an empty array!`);
2322
+ variants.push(operatorName === "hasSome" ? sqlFalse() : sqlTrue());
2323
+ continue;
2137
2324
  }
2138
2325
  variants.push(arrayMembershipOps[operatorName](column, operatorValue));
2139
2326
  } else if (operatorName === "isEmpty") {
@@ -2396,7 +2583,7 @@ var extractRelationsParamsInner = (relationMap, tables, tableName, typeName, ori
2396
2583
  } : void 0;
2397
2584
  const hasPagination = offset != null || limit != null;
2398
2585
  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;
2586
+ thisRecord.orderBy = relationArgs?.orderBy ? (aliasedTable2) => extractOrderBy(aliasedTable2, relationArgs.orderBy, relationFilterCtx(filterCtx, targetTableName), relWhere) : hasPagination && pkNames.length ? (aliasedTable2) => primaryKeyOrderExprs(aliasedTable2, pkNames) : void 0;
2400
2587
  thisRecord.offset = offset;
2401
2588
  thisRecord.limit = limit;
2402
2589
  const relWith = relationField ? extractRelationsParamsInner(
@@ -2476,6 +2663,15 @@ var primaryKeyOrderExprs = (table, pkNames) => {
2476
2663
  var CURSOR_FIELD_NAME = "cursor";
2477
2664
  var ROW_CURSOR_PROP = "__drizzle_graphql_cursor";
2478
2665
  var orderByHasRelationEntry = (orderBy) => !!orderBy && Object.values(orderBy).some((config) => config && typeof config === "object" && config.direction === void 0);
2666
+ var orderByCursorObstacle = (orderBy) => {
2667
+ if (orderByHasRelationEntry(orderBy)) {
2668
+ 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.";
2669
+ }
2670
+ if (orderBy && Object.values(orderBy).some((config) => config && typeof config === "object" && config.matchFilterOrder)) {
2671
+ 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.";
2672
+ }
2673
+ return void 0;
2674
+ };
2479
2675
  var cursorOrderingEntries = (orderBy, pkNames) => {
2480
2676
  const entries = orderBy ? orderByEntries(orderBy) : [];
2481
2677
  const seen = new Set(entries.map(([column]) => column));
@@ -3072,11 +3268,10 @@ var runRelationalSelect = async (opts) => {
3072
3268
  if (after != null && distinct) {
3073
3269
  throw new GraphQLError4("'after' cannot be combined with 'distinct'.");
3074
3270
  }
3075
- if (orderByHasRelationEntry(orderBy)) {
3271
+ const cursorObstacle = orderByCursorObstacle(orderBy);
3272
+ if (cursorObstacle) {
3076
3273
  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
- );
3274
+ throw new GraphQLError4(cursorObstacle);
3080
3275
  }
3081
3276
  } else if (!pkNames?.length) {
3082
3277
  if (after != null) {
@@ -3116,13 +3311,13 @@ var runRelationalSelect = async (opts) => {
3116
3311
  // drizzle-orm v1 RQB calls orderBy/where with the aliased table proxy — use it
3117
3312
  // directly so column refs match the CTE alias.
3118
3313
  orderBy: distinctKeys ? (aliasedTable2) => [
3119
- ...orderBy ? extractOrderBy(aliasedTable2, orderBy, relationFilterCtx(filterCtx, tableName)) : [],
3314
+ ...orderBy ? extractOrderBy(aliasedTable2, orderBy, relationFilterCtx(filterCtx, tableName), where) : [],
3120
3315
  ...primaryKeyOrderExprs(aliasedTable2, pkNames)
3121
3316
  ] : cursorEntries ? (
3122
3317
  // Cursor pagination needs a total order: the request's orderBy plus the PK tiebreak,
3123
3318
  // exactly the ordering the cursor encodes and the keyset predicate compares against.
3124
3319
  (aliasedTable2) => cursorOrderExprs(aliasedTable2, cursorEntries)
3125
- ) : orderBy ? (aliasedTable2) => extractOrderBy(aliasedTable2, orderBy, relationFilterCtx(filterCtx, tableName)) : needsDefaultOrder && pkNames?.length ? (aliasedTable2) => primaryKeyOrderExprs(aliasedTable2, pkNames) : void 0,
3320
+ ) : orderBy ? (aliasedTable2) => extractOrderBy(aliasedTable2, orderBy, relationFilterCtx(filterCtx, tableName), where) : needsDefaultOrder && pkNames?.length ? (aliasedTable2) => primaryKeyOrderExprs(aliasedTable2, pkNames) : void 0,
3126
3321
  where: distinctKeys ? { RAW: (aliasedTable2) => primaryKeyRestriction(aliasedTable2, pkNames, distinctKeys) } : where || cursorValues ? {
3127
3322
  RAW: (aliasedTable2) => and(
3128
3323
  where ? extractFilters(aliasedTable2, tableName, where, relationFilterCtx(filterCtx, tableName)) : void 0,
@@ -3242,6 +3437,38 @@ import {
3242
3437
  } from "graphql";
3243
3438
  import { parseResolveInfo as parseResolveInfo2 } from "graphql-parse-resolve-info";
3244
3439
 
3440
+ // src/util/features.ts
3441
+ var featureDefaults = {
3442
+ aggregates: true,
3443
+ groupBy: true,
3444
+ relationAggregates: true,
3445
+ distinct: true,
3446
+ insert: true,
3447
+ update: true,
3448
+ updateMany: true,
3449
+ delete: true,
3450
+ upsert: false,
3451
+ fieldUpdateOperations: false,
3452
+ countMutations: false,
3453
+ requireWhere: false
3454
+ };
3455
+ var resolveTableFeatures = (features) => {
3456
+ const cache = /* @__PURE__ */ new Map();
3457
+ const names = Object.keys(featureDefaults);
3458
+ return (tableName) => {
3459
+ let resolved = cache.get(tableName);
3460
+ if (!resolved) {
3461
+ resolved = {};
3462
+ for (const name of names) {
3463
+ const configured = features?.[name];
3464
+ resolved[name] = configured === void 0 ? featureDefaults[name] : typeof configured === "function" ? !!configured(tableName) : !!configured;
3465
+ }
3466
+ cache.set(tableName, resolved);
3467
+ }
3468
+ return resolved;
3469
+ };
3470
+ };
3471
+
3245
3472
  // src/util/builders/aggregates.ts
3246
3473
  import {
3247
3474
  and as and2,
@@ -4001,6 +4228,8 @@ var generateSchemaData = (db, schema, relations, options) => {
4001
4228
  const excludedTables = new Set(options.exclude?.tables ?? []);
4002
4229
  const tableEntries = schemaEntries.filter(([key, value]) => is4(value, MySqlTable) && !excludedTables.has(key));
4003
4230
  const tables = Object.fromEntries(tableEntries);
4231
+ const featureOf = resolveTableFeatures(features);
4232
+ const anyTable = (feature) => tableEntries.some(([name]) => featureOf(name)[feature]);
4004
4233
  if (!tableEntries.length) {
4005
4234
  throw new Error(
4006
4235
  "Drizzle-GraphQL Error: No tables detected in Drizzle-ORM's database instance. Did you forget to pass schema to drizzle constructor?"
@@ -4043,9 +4272,9 @@ var generateSchemaData = (db, schema, relations, options) => {
4043
4272
  complexity,
4044
4273
  limits,
4045
4274
  docs: options.docs ?? {},
4046
- features
4275
+ featureOf
4047
4276
  };
4048
- const relationAggregateFactory = features.relationAggregates ? createRelationAggregateFactory(db, tables, cacheCtx, typeNameMapper, filterCtx) : void 0;
4277
+ const relationAggregateFactory = anyTable("relationAggregates") ? createRelationAggregateFactory(db, tables, cacheCtx, typeNameMapper, filterCtx) : void 0;
4049
4278
  const queries = {};
4050
4279
  const mutations = {};
4051
4280
  const mutationTxCtx = createMutationTxCtx(options.transactions);
@@ -4063,7 +4292,7 @@ var generateSchemaData = (db, schema, relations, options) => {
4063
4292
  prefixes.insert,
4064
4293
  prefixes.update,
4065
4294
  resolverFactory,
4066
- relationAggregateFactory
4295
+ featureOf(tableName).relationAggregates ? relationAggregateFactory : void 0
4067
4296
  )
4068
4297
  ])
4069
4298
  );
@@ -4077,10 +4306,11 @@ var generateSchemaData = (db, schema, relations, options) => {
4077
4306
  });
4078
4307
  const inputs = {};
4079
4308
  const outputs = {};
4080
- if (features.insert || features.upsert || features.update || features.delete) {
4309
+ if (["insert", "upsert", "update", "delete"].some((feature) => anyTable(feature))) {
4081
4310
  outputs.MutationReturn = mutationReturnType;
4082
4311
  }
4083
4312
  for (const [tableName, tableTypes] of Object.entries(gqlSchemaTypes)) {
4313
+ const tableFeatures = featureOf(tableName);
4084
4314
  const { insertInput, updateInput, tableFilters, tableOrder } = tableTypes.inputs;
4085
4315
  const { selectSingleOutput, selectArrOutput } = tableTypes.outputs;
4086
4316
  const {
@@ -4112,7 +4342,7 @@ var generateSchemaData = (db, schema, relations, options) => {
4112
4342
  typeName,
4113
4343
  typeNameMapper,
4114
4344
  filterCtx,
4115
- features.distinct,
4345
+ tableFeatures.distinct,
4116
4346
  limits
4117
4347
  );
4118
4348
  const selectSingleGenerated = generateSelectSingle(
@@ -4128,7 +4358,7 @@ var generateSchemaData = (db, schema, relations, options) => {
4128
4358
  filterCtx,
4129
4359
  limits
4130
4360
  );
4131
- const insertArrGenerated = features.insert ? generateInsertArray(
4361
+ const insertArrGenerated = tableFeatures.insert ? generateInsertArray(
4132
4362
  db,
4133
4363
  tableName,
4134
4364
  schema[tableName],
@@ -4136,7 +4366,7 @@ var generateSchemaData = (db, schema, relations, options) => {
4136
4366
  createArrayFieldName,
4137
4367
  mutationTxCtx
4138
4368
  ) : void 0;
4139
- const insertSingleGenerated = features.insert ? generateInsertSingle(
4369
+ const insertSingleGenerated = tableFeatures.insert ? generateInsertSingle(
4140
4370
  db,
4141
4371
  tableName,
4142
4372
  schema[tableName],
@@ -4144,7 +4374,7 @@ var generateSchemaData = (db, schema, relations, options) => {
4144
4374
  createSingleFieldName,
4145
4375
  mutationTxCtx
4146
4376
  ) : void 0;
4147
- const onConflictInput = features.upsert ? generateOnConflictInput({
4377
+ const onConflictInput = tableFeatures.upsert ? generateOnConflictInput({
4148
4378
  table: schema[tableName],
4149
4379
  typeName,
4150
4380
  uniqueSets: [],
@@ -4169,7 +4399,7 @@ var generateSchemaData = (db, schema, relations, options) => {
4169
4399
  true,
4170
4400
  mutationTxCtx
4171
4401
  ) : void 0;
4172
- const updateGenerated = features.update ? generateUpdate(
4402
+ const updateGenerated = tableFeatures.update ? generateUpdate(
4173
4403
  db,
4174
4404
  tableName,
4175
4405
  schema[tableName],
@@ -4177,11 +4407,11 @@ var generateSchemaData = (db, schema, relations, options) => {
4177
4407
  tableFilters,
4178
4408
  updateFieldName,
4179
4409
  false,
4180
- features.requireWhere,
4410
+ tableFeatures.requireWhere,
4181
4411
  filterCtx,
4182
4412
  mutationTxCtx
4183
4413
  ) : void 0;
4184
- const updateSingleGenerated = features.update ? generateUpdate(
4414
+ const updateSingleGenerated = tableFeatures.update ? generateUpdate(
4185
4415
  db,
4186
4416
  tableName,
4187
4417
  schema[tableName],
@@ -4189,11 +4419,11 @@ var generateSchemaData = (db, schema, relations, options) => {
4189
4419
  tableFilters,
4190
4420
  updateSingleFieldName,
4191
4421
  true,
4192
- features.requireWhere,
4422
+ tableFeatures.requireWhere,
4193
4423
  filterCtx,
4194
4424
  mutationTxCtx
4195
4425
  ) : void 0;
4196
- const updateManyInput = features.update && features.updateMany ? generateUpdateManyInput({ typeName, updatePrefix: prefixes.update, updateInput, tableFilters }) : void 0;
4426
+ const updateManyInput = tableFeatures.update && tableFeatures.updateMany ? generateUpdateManyInput({ typeName, updatePrefix: prefixes.update, updateInput, tableFilters }) : void 0;
4197
4427
  const updateManyGenerated = updateManyInput ? generateUpdateMany(
4198
4428
  db,
4199
4429
  tableName,
@@ -4203,29 +4433,29 @@ var generateSchemaData = (db, schema, relations, options) => {
4203
4433
  filterCtx,
4204
4434
  mutationTxCtx
4205
4435
  ) : void 0;
4206
- const deleteGenerated = features.delete ? generateDelete(
4436
+ const deleteGenerated = tableFeatures.delete ? generateDelete(
4207
4437
  db,
4208
4438
  tableName,
4209
4439
  schema[tableName],
4210
4440
  tableFilters,
4211
4441
  deleteFieldName,
4212
4442
  false,
4213
- features.requireWhere,
4443
+ tableFeatures.requireWhere,
4214
4444
  filterCtx,
4215
4445
  mutationTxCtx
4216
4446
  ) : void 0;
4217
- const deleteSingleGenerated = features.delete ? generateDelete(
4447
+ const deleteSingleGenerated = tableFeatures.delete ? generateDelete(
4218
4448
  db,
4219
4449
  tableName,
4220
4450
  schema[tableName],
4221
4451
  tableFilters,
4222
4452
  deleteSingleFieldName,
4223
4453
  true,
4224
- features.requireWhere,
4454
+ tableFeatures.requireWhere,
4225
4455
  filterCtx,
4226
4456
  mutationTxCtx
4227
4457
  ) : void 0;
4228
- const updateCountGenerated = features.update && features.countMutations ? generateWriteCount({
4458
+ const updateCountGenerated = tableFeatures.update && tableFeatures.countMutations ? generateWriteCount({
4229
4459
  db,
4230
4460
  tableName,
4231
4461
  table: schema[tableName],
@@ -4233,23 +4463,23 @@ var generateSchemaData = (db, schema, relations, options) => {
4233
4463
  setArgs: updateInput,
4234
4464
  filterArgs: tableFilters,
4235
4465
  fieldName: updateCountFieldName,
4236
- requireWhere: features.requireWhere,
4466
+ requireWhere: tableFeatures.requireWhere,
4237
4467
  filterCtx,
4238
4468
  txCtx: mutationTxCtx
4239
4469
  }) : void 0;
4240
- const deleteCountGenerated = features.delete && features.countMutations ? generateWriteCount({
4470
+ const deleteCountGenerated = tableFeatures.delete && tableFeatures.countMutations ? generateWriteCount({
4241
4471
  db,
4242
4472
  tableName,
4243
4473
  table: schema[tableName],
4244
4474
  kind: "delete",
4245
4475
  filterArgs: tableFilters,
4246
4476
  fieldName: deleteCountFieldName,
4247
- requireWhere: features.requireWhere,
4477
+ requireWhere: tableFeatures.requireWhere,
4248
4478
  filterCtx,
4249
4479
  txCtx: mutationTxCtx
4250
4480
  }) : void 0;
4251
- const aggregateType = features.aggregates ? generateAggregateTypes(schema[tableName], tableName, typeName, cacheCtx) : void 0;
4252
- const aggregateGenerated = features.aggregates ? generateAggregate(
4481
+ const aggregateType = tableFeatures.aggregates ? generateAggregateTypes(schema[tableName], tableName, typeName, cacheCtx) : void 0;
4482
+ const aggregateGenerated = tableFeatures.aggregates ? generateAggregate(
4253
4483
  db,
4254
4484
  tableName,
4255
4485
  schema[tableName],
@@ -4258,7 +4488,7 @@ var generateSchemaData = (db, schema, relations, options) => {
4258
4488
  tableFilters,
4259
4489
  filterCtx
4260
4490
  ) : void 0;
4261
- const groupByType = features.aggregates && features.groupBy ? generateGroupByType(schema[tableName], tableName, typeName, cacheCtx) : void 0;
4491
+ const groupByType = tableFeatures.aggregates && tableFeatures.groupBy ? generateGroupByType(schema[tableName], tableName, typeName, cacheCtx) : void 0;
4262
4492
  const groupByEnum = groupByType ? generateGroupByEnum(schema[tableName], tableName, typeName) : void 0;
4263
4493
  const havingInput = groupByEnum ? generateHavingInput(schema[tableName], tableName, typeName) : void 0;
4264
4494
  const groupByGenerated = groupByType && groupByEnum && havingInput ? generateGroupBy(
@@ -4336,9 +4566,9 @@ var generateSchemaData = (db, schema, relations, options) => {
4336
4566
  }
4337
4567
  const activeInputs = [
4338
4568
  // The insert input types the upsert mutations too, so either feature keeps it.
4339
- ...features.insert || onConflictInput ? [insertInput] : [],
4569
+ ...tableFeatures.insert || onConflictInput ? [insertInput] : [],
4340
4570
  ...onConflictInput ? [onConflictInput] : [],
4341
- ...features.update ? [updateInput] : [],
4571
+ ...tableFeatures.update ? [updateInput] : [],
4342
4572
  ...updateManyInput ? [updateManyInput] : [],
4343
4573
  tableFilters,
4344
4574
  tableOrder
@@ -4822,11 +5052,10 @@ var generateSelectArray2 = (db, tableName, tables, relationMap, orderArgs, filte
4822
5052
  if (after != null && distinct?.length) {
4823
5053
  throw new GraphQLError8("'after' cannot be combined with 'distinct'.");
4824
5054
  }
4825
- if (orderByHasRelationEntry(orderBy)) {
5055
+ const cursorObstacle = orderByCursorObstacle(orderBy);
5056
+ if (cursorObstacle) {
4826
5057
  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
- );
5058
+ throw new GraphQLError8(cursorObstacle);
4830
5059
  }
4831
5060
  } else if (!pkNames.length) {
4832
5061
  if (after != null) {
@@ -4874,7 +5103,7 @@ var generateSelectArray2 = (db, tableName, tables, relationMap, orderArgs, filte
4874
5103
  q = q.orderBy(...cursorOrderExprs(table, cursorEntries));
4875
5104
  } else if (orderBy) {
4876
5105
  q = q.orderBy(
4877
- ...extractOrderBy(table, orderBy, relationFilterCtx(filterCtx, tableName)),
5106
+ ...extractOrderBy(table, orderBy, relationFilterCtx(filterCtx, tableName), where),
4878
5107
  ...distinctKeys ? primaryKeyOrderExprs(table, pkNames) : []
4879
5108
  );
4880
5109
  } else if ((distinctKeys || offset != null || limit != null) && pkNames.length) {
@@ -4940,7 +5169,7 @@ var generateSelectSingle2 = (db, tableName, tables, relationMap, orderArgs, filt
4940
5169
  q = q.where(extractFilters(table, tableName, where, relationFilterCtx(filterCtx, tableName)));
4941
5170
  }
4942
5171
  if (orderBy) {
4943
- q = q.orderBy(...extractOrderBy(table, orderBy, relationFilterCtx(filterCtx, tableName)));
5172
+ q = q.orderBy(...extractOrderBy(table, orderBy, relationFilterCtx(filterCtx, tableName), where));
4944
5173
  } else if (pkNames.length) {
4945
5174
  q = q.orderBy(...primaryKeyOrderExprs(table, pkNames));
4946
5175
  }
@@ -5377,6 +5606,8 @@ function generateSchemaData2(db, schema, relations, options) {
5377
5606
  const excludedTables = new Set(options.exclude?.tables ?? []);
5378
5607
  const tableEntries = schemaEntries.filter(([key, value]) => is6(value, PgTable) && !excludedTables.has(key));
5379
5608
  const tables = Object.fromEntries(tableEntries);
5609
+ const featureOf = resolveTableFeatures(features);
5610
+ const anyTable = (feature) => tableEntries.some(([name]) => featureOf(name)[feature]);
5380
5611
  if (!tableEntries.length) {
5381
5612
  throw new Error(
5382
5613
  "Drizzle-GraphQL Error: No tables detected in Drizzle-ORM's database instance. Did you forget to pass schema to drizzle constructor?"
@@ -5414,7 +5645,7 @@ function generateSchemaData2(db, schema, relations, options) {
5414
5645
  complexity,
5415
5646
  limits,
5416
5647
  docs: options.docs ?? {},
5417
- features
5648
+ featureOf
5418
5649
  };
5419
5650
  const nestedPlans = features.nestedWrites ? buildNestedWritePlans(
5420
5651
  tables,
@@ -5425,7 +5656,7 @@ function generateSchemaData2(db, schema, relations, options) {
5425
5656
  ) : void 0;
5426
5657
  const nestedTypes = nestedPlans ? createNestedWriteTypes({ plans: nestedPlans, cacheCtx, typeNameMapper, insertPrefix: prefixes.insert }) : void 0;
5427
5658
  const nestedRuntime = nestedPlans ? createNestedWriteRuntime({ plans: nestedPlans, filterCtx }) : void 0;
5428
- const relationAggregateFactory = features.relationAggregates ? createRelationAggregateFactory(db, tables, cacheCtx, typeNameMapper, filterCtx) : void 0;
5659
+ const relationAggregateFactory = anyTable("relationAggregates") ? createRelationAggregateFactory(db, tables, cacheCtx, typeNameMapper, filterCtx) : void 0;
5429
5660
  const queries = {};
5430
5661
  const mutations = {};
5431
5662
  const mutationTxCtx = createMutationTxCtx(options.transactions);
@@ -5443,7 +5674,7 @@ function generateSchemaData2(db, schema, relations, options) {
5443
5674
  prefixes.insert,
5444
5675
  prefixes.update,
5445
5676
  resolverFactory,
5446
- relationAggregateFactory,
5677
+ featureOf(tableName).relationAggregates ? relationAggregateFactory : void 0,
5447
5678
  nestedTypes
5448
5679
  )
5449
5680
  ])
@@ -5451,6 +5682,7 @@ function generateSchemaData2(db, schema, relations, options) {
5451
5682
  const inputs = {};
5452
5683
  const outputs = {};
5453
5684
  for (const [tableName, tableTypes] of Object.entries(gqlSchemaTypes)) {
5685
+ const tableFeatures = featureOf(tableName);
5454
5686
  const { insertInput, updateInput, tableFilters, tableOrder } = tableTypes.inputs;
5455
5687
  const { selectSingleOutput, selectArrOutput, singleTableItemOutput, arrTableItemOutput } = tableTypes.outputs;
5456
5688
  const {
@@ -5482,7 +5714,7 @@ function generateSchemaData2(db, schema, relations, options) {
5482
5714
  typeName,
5483
5715
  typeNameMapper,
5484
5716
  filterCtx,
5485
- features.distinct,
5717
+ tableFeatures.distinct,
5486
5718
  limits
5487
5719
  );
5488
5720
  const selectSingleGenerated = generateSelectSingle2(
@@ -5498,7 +5730,7 @@ function generateSchemaData2(db, schema, relations, options) {
5498
5730
  filterCtx,
5499
5731
  limits
5500
5732
  );
5501
- const insertArrGenerated = features.insert ? generateInsertArray2(
5733
+ const insertArrGenerated = tableFeatures.insert ? generateInsertArray2(
5502
5734
  db,
5503
5735
  tableName,
5504
5736
  schema[tableName],
@@ -5513,7 +5745,7 @@ function generateSchemaData2(db, schema, relations, options) {
5513
5745
  nestedRuntime,
5514
5746
  limits
5515
5747
  ) : void 0;
5516
- const insertSingleGenerated = features.insert ? generateInsertSingle2(
5748
+ const insertSingleGenerated = tableFeatures.insert ? generateInsertSingle2(
5517
5749
  db,
5518
5750
  tableName,
5519
5751
  schema[tableName],
@@ -5528,8 +5760,8 @@ function generateSchemaData2(db, schema, relations, options) {
5528
5760
  nestedRuntime,
5529
5761
  limits
5530
5762
  ) : void 0;
5531
- const uniqueSets = features.upsert ? getUniqueColumnSets(schema[tableName], getTableConfig2) : [];
5532
- const onConflictInput = features.upsert ? generateOnConflictInput({
5763
+ const uniqueSets = tableFeatures.upsert ? getUniqueColumnSets(schema[tableName], getTableConfig2) : [];
5764
+ const onConflictInput = tableFeatures.upsert ? generateOnConflictInput({
5533
5765
  table: schema[tableName],
5534
5766
  typeName,
5535
5767
  uniqueSets,
@@ -5572,7 +5804,7 @@ function generateSchemaData2(db, schema, relations, options) {
5572
5804
  nestedRuntime,
5573
5805
  limits
5574
5806
  ) : void 0;
5575
- const updateGenerated = features.update ? generateUpdate2(
5807
+ const updateGenerated = tableFeatures.update ? generateUpdate2(
5576
5808
  db,
5577
5809
  tableName,
5578
5810
  schema[tableName],
@@ -5583,14 +5815,14 @@ function generateSchemaData2(db, schema, relations, options) {
5583
5815
  updateFieldName,
5584
5816
  typeName,
5585
5817
  false,
5586
- features.requireWhere,
5818
+ tableFeatures.requireWhere,
5587
5819
  typeNameMapper,
5588
5820
  filterCtx,
5589
5821
  mutationTxCtx,
5590
5822
  nestedRuntime,
5591
5823
  limits
5592
5824
  ) : void 0;
5593
- const updateSingleGenerated = features.update ? generateUpdate2(
5825
+ const updateSingleGenerated = tableFeatures.update ? generateUpdate2(
5594
5826
  db,
5595
5827
  tableName,
5596
5828
  schema[tableName],
@@ -5601,14 +5833,14 @@ function generateSchemaData2(db, schema, relations, options) {
5601
5833
  updateSingleFieldName,
5602
5834
  typeName,
5603
5835
  true,
5604
- features.requireWhere,
5836
+ tableFeatures.requireWhere,
5605
5837
  typeNameMapper,
5606
5838
  filterCtx,
5607
5839
  mutationTxCtx,
5608
5840
  nestedRuntime,
5609
5841
  limits
5610
5842
  ) : void 0;
5611
- const updateManyInput = features.update && features.updateMany ? generateUpdateManyInput({ typeName, updatePrefix: prefixes.update, updateInput, tableFilters }) : void 0;
5843
+ const updateManyInput = tableFeatures.update && tableFeatures.updateMany ? generateUpdateManyInput({ typeName, updatePrefix: prefixes.update, updateInput, tableFilters }) : void 0;
5612
5844
  const updateManyGenerated = updateManyInput ? generateUpdateMany2(
5613
5845
  db,
5614
5846
  tableName,
@@ -5624,7 +5856,7 @@ function generateSchemaData2(db, schema, relations, options) {
5624
5856
  nestedRuntime,
5625
5857
  limits
5626
5858
  ) : void 0;
5627
- const deleteGenerated = features.delete ? generateDelete2(
5859
+ const deleteGenerated = tableFeatures.delete ? generateDelete2(
5628
5860
  db,
5629
5861
  tableName,
5630
5862
  schema[tableName],
@@ -5632,12 +5864,12 @@ function generateSchemaData2(db, schema, relations, options) {
5632
5864
  deleteFieldName,
5633
5865
  typeName,
5634
5866
  false,
5635
- features.requireWhere,
5867
+ tableFeatures.requireWhere,
5636
5868
  filterCtx,
5637
5869
  { tableName, relationMap: namedRelations, tables },
5638
5870
  mutationTxCtx
5639
5871
  ) : void 0;
5640
- const deleteSingleGenerated = features.delete ? generateDelete2(
5872
+ const deleteSingleGenerated = tableFeatures.delete ? generateDelete2(
5641
5873
  db,
5642
5874
  tableName,
5643
5875
  schema[tableName],
@@ -5645,12 +5877,12 @@ function generateSchemaData2(db, schema, relations, options) {
5645
5877
  deleteSingleFieldName,
5646
5878
  typeName,
5647
5879
  true,
5648
- features.requireWhere,
5880
+ tableFeatures.requireWhere,
5649
5881
  filterCtx,
5650
5882
  { tableName, relationMap: namedRelations, tables },
5651
5883
  mutationTxCtx
5652
5884
  ) : void 0;
5653
- const updateCountGenerated = features.update && features.countMutations ? generateWriteCount({
5885
+ const updateCountGenerated = tableFeatures.update && tableFeatures.countMutations ? generateWriteCount({
5654
5886
  db,
5655
5887
  tableName,
5656
5888
  table: schema[tableName],
@@ -5658,24 +5890,24 @@ function generateSchemaData2(db, schema, relations, options) {
5658
5890
  setArgs: updateInput,
5659
5891
  filterArgs: tableFilters,
5660
5892
  fieldName: updateCountFieldName,
5661
- requireWhere: features.requireWhere,
5893
+ requireWhere: tableFeatures.requireWhere,
5662
5894
  filterCtx,
5663
5895
  txCtx: mutationTxCtx,
5664
5896
  nested: nestedRuntime
5665
5897
  }) : void 0;
5666
- const deleteCountGenerated = features.delete && features.countMutations ? generateWriteCount({
5898
+ const deleteCountGenerated = tableFeatures.delete && tableFeatures.countMutations ? generateWriteCount({
5667
5899
  db,
5668
5900
  tableName,
5669
5901
  table: schema[tableName],
5670
5902
  kind: "delete",
5671
5903
  filterArgs: tableFilters,
5672
5904
  fieldName: deleteCountFieldName,
5673
- requireWhere: features.requireWhere,
5905
+ requireWhere: tableFeatures.requireWhere,
5674
5906
  filterCtx,
5675
5907
  txCtx: mutationTxCtx
5676
5908
  }) : void 0;
5677
- const aggregateType = features.aggregates ? generateAggregateTypes(schema[tableName], tableName, typeName, cacheCtx) : void 0;
5678
- const aggregateGenerated = features.aggregates ? generateAggregate(
5909
+ const aggregateType = tableFeatures.aggregates ? generateAggregateTypes(schema[tableName], tableName, typeName, cacheCtx) : void 0;
5910
+ const aggregateGenerated = tableFeatures.aggregates ? generateAggregate(
5679
5911
  db,
5680
5912
  tableName,
5681
5913
  schema[tableName],
@@ -5684,7 +5916,7 @@ function generateSchemaData2(db, schema, relations, options) {
5684
5916
  tableFilters,
5685
5917
  filterCtx
5686
5918
  ) : void 0;
5687
- const groupByType = features.aggregates && features.groupBy ? generateGroupByType(schema[tableName], tableName, typeName, cacheCtx) : void 0;
5919
+ const groupByType = tableFeatures.aggregates && tableFeatures.groupBy ? generateGroupByType(schema[tableName], tableName, typeName, cacheCtx) : void 0;
5688
5920
  const groupByEnum = groupByType ? generateGroupByEnum(schema[tableName], tableName, typeName) : void 0;
5689
5921
  const havingInput = groupByEnum ? generateHavingInput(schema[tableName], tableName, typeName) : void 0;
5690
5922
  const groupByGenerated = groupByType && groupByEnum && havingInput ? generateGroupBy(
@@ -5812,9 +6044,9 @@ function generateSchemaData2(db, schema, relations, options) {
5812
6044
  }
5813
6045
  const activeInputs = [
5814
6046
  // The insert input types the upsert mutations too, so either feature keeps it.
5815
- ...features.insert || onConflictInput ? [insertInput] : [],
6047
+ ...tableFeatures.insert || onConflictInput ? [insertInput] : [],
5816
6048
  ...onConflictInput ? [onConflictInput] : [],
5817
- ...features.update ? [updateInput] : [],
6049
+ ...tableFeatures.update ? [updateInput] : [],
5818
6050
  ...updateManyInput ? [updateManyInput] : [],
5819
6051
  tableFilters,
5820
6052
  tableOrder
@@ -6399,6 +6631,8 @@ var generateSchemaData3 = (db, schema, relations, options) => {
6399
6631
  const excludedTables = new Set(options.exclude?.tables ?? []);
6400
6632
  const tableEntries = schemaEntries.filter(([key, value]) => is7(value, SQLiteTable) && !excludedTables.has(key));
6401
6633
  const tables = Object.fromEntries(tableEntries);
6634
+ const featureOf = resolveTableFeatures(features);
6635
+ const anyTable = (feature) => tableEntries.some(([name]) => featureOf(name)[feature]);
6402
6636
  if (!tableEntries.length) {
6403
6637
  throw new Error(
6404
6638
  "Drizzle-GraphQL Error: No tables detected in Drizzle-ORM's database instance. Did you forget to pass schema to drizzle constructor?"
@@ -6436,7 +6670,7 @@ var generateSchemaData3 = (db, schema, relations, options) => {
6436
6670
  complexity,
6437
6671
  limits,
6438
6672
  docs: options.docs ?? {},
6439
- features
6673
+ featureOf
6440
6674
  };
6441
6675
  if (features.nestedWrites && db.resultKind === "sync") {
6442
6676
  throw new Error(
@@ -6452,7 +6686,7 @@ var generateSchemaData3 = (db, schema, relations, options) => {
6452
6686
  ) : void 0;
6453
6687
  const nestedTypes = nestedPlans ? createNestedWriteTypes({ plans: nestedPlans, cacheCtx, typeNameMapper, insertPrefix: prefixes.insert }) : void 0;
6454
6688
  const nestedRuntime = nestedPlans ? createNestedWriteRuntime({ plans: nestedPlans, filterCtx }) : void 0;
6455
- const relationAggregateFactory = features.relationAggregates ? createRelationAggregateFactory(db, tables, cacheCtx, typeNameMapper, filterCtx) : void 0;
6689
+ const relationAggregateFactory = anyTable("relationAggregates") ? createRelationAggregateFactory(db, tables, cacheCtx, typeNameMapper, filterCtx) : void 0;
6456
6690
  const queries = {};
6457
6691
  const mutations = {};
6458
6692
  if (options.transactions && db.resultKind === "sync") {
@@ -6475,7 +6709,7 @@ var generateSchemaData3 = (db, schema, relations, options) => {
6475
6709
  prefixes.insert,
6476
6710
  prefixes.update,
6477
6711
  resolverFactory,
6478
- relationAggregateFactory,
6712
+ featureOf(tableName).relationAggregates ? relationAggregateFactory : void 0,
6479
6713
  nestedTypes
6480
6714
  )
6481
6715
  ])
@@ -6483,6 +6717,7 @@ var generateSchemaData3 = (db, schema, relations, options) => {
6483
6717
  const inputs = {};
6484
6718
  const outputs = {};
6485
6719
  for (const [tableName, tableTypes] of Object.entries(gqlSchemaTypes)) {
6720
+ const tableFeatures = featureOf(tableName);
6486
6721
  const { insertInput, updateInput, tableFilters, tableOrder } = tableTypes.inputs;
6487
6722
  const { selectSingleOutput, selectArrOutput, singleTableItemOutput, arrTableItemOutput } = tableTypes.outputs;
6488
6723
  const {
@@ -6514,7 +6749,7 @@ var generateSchemaData3 = (db, schema, relations, options) => {
6514
6749
  typeName,
6515
6750
  typeNameMapper,
6516
6751
  filterCtx,
6517
- features.distinct,
6752
+ tableFeatures.distinct,
6518
6753
  limits
6519
6754
  );
6520
6755
  const selectSingleGenerated = generateSelectSingle3(
@@ -6530,7 +6765,7 @@ var generateSchemaData3 = (db, schema, relations, options) => {
6530
6765
  filterCtx,
6531
6766
  limits
6532
6767
  );
6533
- const insertArrGenerated = features.insert ? generateInsertArray3(
6768
+ const insertArrGenerated = tableFeatures.insert ? generateInsertArray3(
6534
6769
  db,
6535
6770
  tableName,
6536
6771
  schema[tableName],
@@ -6545,7 +6780,7 @@ var generateSchemaData3 = (db, schema, relations, options) => {
6545
6780
  nestedRuntime,
6546
6781
  limits
6547
6782
  ) : void 0;
6548
- const insertSingleGenerated = features.insert ? generateInsertSingle3(
6783
+ const insertSingleGenerated = tableFeatures.insert ? generateInsertSingle3(
6549
6784
  db,
6550
6785
  tableName,
6551
6786
  schema[tableName],
@@ -6560,8 +6795,8 @@ var generateSchemaData3 = (db, schema, relations, options) => {
6560
6795
  nestedRuntime,
6561
6796
  limits
6562
6797
  ) : void 0;
6563
- const uniqueSets = features.upsert ? getUniqueColumnSets(schema[tableName], getTableConfig3) : [];
6564
- const onConflictInput = features.upsert ? generateOnConflictInput({
6798
+ const uniqueSets = tableFeatures.upsert ? getUniqueColumnSets(schema[tableName], getTableConfig3) : [];
6799
+ const onConflictInput = tableFeatures.upsert ? generateOnConflictInput({
6565
6800
  table: schema[tableName],
6566
6801
  typeName,
6567
6802
  uniqueSets,
@@ -6604,7 +6839,7 @@ var generateSchemaData3 = (db, schema, relations, options) => {
6604
6839
  nestedRuntime,
6605
6840
  limits
6606
6841
  ) : void 0;
6607
- const updateGenerated = features.update ? generateUpdate3(
6842
+ const updateGenerated = tableFeatures.update ? generateUpdate3(
6608
6843
  db,
6609
6844
  tableName,
6610
6845
  schema[tableName],
@@ -6615,14 +6850,14 @@ var generateSchemaData3 = (db, schema, relations, options) => {
6615
6850
  updateFieldName,
6616
6851
  typeName,
6617
6852
  false,
6618
- features.requireWhere,
6853
+ tableFeatures.requireWhere,
6619
6854
  typeNameMapper,
6620
6855
  filterCtx,
6621
6856
  mutationTxCtx,
6622
6857
  nestedRuntime,
6623
6858
  limits
6624
6859
  ) : void 0;
6625
- const updateSingleGenerated = features.update ? generateUpdate3(
6860
+ const updateSingleGenerated = tableFeatures.update ? generateUpdate3(
6626
6861
  db,
6627
6862
  tableName,
6628
6863
  schema[tableName],
@@ -6633,14 +6868,14 @@ var generateSchemaData3 = (db, schema, relations, options) => {
6633
6868
  updateSingleFieldName,
6634
6869
  typeName,
6635
6870
  true,
6636
- features.requireWhere,
6871
+ tableFeatures.requireWhere,
6637
6872
  typeNameMapper,
6638
6873
  filterCtx,
6639
6874
  mutationTxCtx,
6640
6875
  nestedRuntime,
6641
6876
  limits
6642
6877
  ) : void 0;
6643
- const updateManyInput = features.update && features.updateMany ? generateUpdateManyInput({ typeName, updatePrefix: prefixes.update, updateInput, tableFilters }) : void 0;
6878
+ const updateManyInput = tableFeatures.update && tableFeatures.updateMany ? generateUpdateManyInput({ typeName, updatePrefix: prefixes.update, updateInput, tableFilters }) : void 0;
6644
6879
  const updateManyGenerated = updateManyInput ? generateUpdateMany3(
6645
6880
  db,
6646
6881
  tableName,
@@ -6656,7 +6891,7 @@ var generateSchemaData3 = (db, schema, relations, options) => {
6656
6891
  nestedRuntime,
6657
6892
  limits
6658
6893
  ) : void 0;
6659
- const deleteGenerated = features.delete ? generateDelete3(
6894
+ const deleteGenerated = tableFeatures.delete ? generateDelete3(
6660
6895
  db,
6661
6896
  tableName,
6662
6897
  schema[tableName],
@@ -6664,12 +6899,12 @@ var generateSchemaData3 = (db, schema, relations, options) => {
6664
6899
  deleteFieldName,
6665
6900
  typeName,
6666
6901
  false,
6667
- features.requireWhere,
6902
+ tableFeatures.requireWhere,
6668
6903
  filterCtx,
6669
6904
  { tableName, relationMap: namedRelations, tables },
6670
6905
  mutationTxCtx
6671
6906
  ) : void 0;
6672
- const deleteSingleGenerated = features.delete ? generateDelete3(
6907
+ const deleteSingleGenerated = tableFeatures.delete ? generateDelete3(
6673
6908
  db,
6674
6909
  tableName,
6675
6910
  schema[tableName],
@@ -6677,12 +6912,12 @@ var generateSchemaData3 = (db, schema, relations, options) => {
6677
6912
  deleteSingleFieldName,
6678
6913
  typeName,
6679
6914
  true,
6680
- features.requireWhere,
6915
+ tableFeatures.requireWhere,
6681
6916
  filterCtx,
6682
6917
  { tableName, relationMap: namedRelations, tables },
6683
6918
  mutationTxCtx
6684
6919
  ) : void 0;
6685
- const updateCountGenerated = features.update && features.countMutations ? generateWriteCount({
6920
+ const updateCountGenerated = tableFeatures.update && tableFeatures.countMutations ? generateWriteCount({
6686
6921
  db,
6687
6922
  tableName,
6688
6923
  table: schema[tableName],
@@ -6690,24 +6925,24 @@ var generateSchemaData3 = (db, schema, relations, options) => {
6690
6925
  setArgs: updateInput,
6691
6926
  filterArgs: tableFilters,
6692
6927
  fieldName: updateCountFieldName,
6693
- requireWhere: features.requireWhere,
6928
+ requireWhere: tableFeatures.requireWhere,
6694
6929
  filterCtx,
6695
6930
  txCtx: mutationTxCtx,
6696
6931
  nested: nestedRuntime
6697
6932
  }) : void 0;
6698
- const deleteCountGenerated = features.delete && features.countMutations ? generateWriteCount({
6933
+ const deleteCountGenerated = tableFeatures.delete && tableFeatures.countMutations ? generateWriteCount({
6699
6934
  db,
6700
6935
  tableName,
6701
6936
  table: schema[tableName],
6702
6937
  kind: "delete",
6703
6938
  filterArgs: tableFilters,
6704
6939
  fieldName: deleteCountFieldName,
6705
- requireWhere: features.requireWhere,
6940
+ requireWhere: tableFeatures.requireWhere,
6706
6941
  filterCtx,
6707
6942
  txCtx: mutationTxCtx
6708
6943
  }) : void 0;
6709
- const aggregateType = features.aggregates ? generateAggregateTypes(schema[tableName], tableName, typeName, cacheCtx) : void 0;
6710
- const aggregateGenerated = features.aggregates ? generateAggregate(
6944
+ const aggregateType = tableFeatures.aggregates ? generateAggregateTypes(schema[tableName], tableName, typeName, cacheCtx) : void 0;
6945
+ const aggregateGenerated = tableFeatures.aggregates ? generateAggregate(
6711
6946
  db,
6712
6947
  tableName,
6713
6948
  schema[tableName],
@@ -6716,7 +6951,7 @@ var generateSchemaData3 = (db, schema, relations, options) => {
6716
6951
  tableFilters,
6717
6952
  filterCtx
6718
6953
  ) : void 0;
6719
- const groupByType = features.aggregates && features.groupBy ? generateGroupByType(schema[tableName], tableName, typeName, cacheCtx) : void 0;
6954
+ const groupByType = tableFeatures.aggregates && tableFeatures.groupBy ? generateGroupByType(schema[tableName], tableName, typeName, cacheCtx) : void 0;
6720
6955
  const groupByEnum = groupByType ? generateGroupByEnum(schema[tableName], tableName, typeName) : void 0;
6721
6956
  const havingInput = groupByEnum ? generateHavingInput(schema[tableName], tableName, typeName) : void 0;
6722
6957
  const groupByGenerated = groupByType && groupByEnum && havingInput ? generateGroupBy(
@@ -6844,9 +7079,9 @@ var generateSchemaData3 = (db, schema, relations, options) => {
6844
7079
  }
6845
7080
  const activeInputs = [
6846
7081
  // The insert input types the upsert mutations too, so either feature keeps it.
6847
- ...features.insert || onConflictInput ? [insertInput] : [],
7082
+ ...tableFeatures.insert || onConflictInput ? [insertInput] : [],
6848
7083
  ...onConflictInput ? [onConflictInput] : [],
6849
- ...features.update ? [updateInput] : [],
7084
+ ...tableFeatures.update ? [updateInput] : [],
6850
7085
  ...updateManyInput ? [updateManyInput] : [],
6851
7086
  tableFilters,
6852
7087
  tableOrder
@@ -6907,22 +7142,11 @@ var buildSchema = (db, config) => {
6907
7142
  list: config?.suffixes?.list ?? "",
6908
7143
  single: config?.suffixes?.single ?? "Single"
6909
7144
  };
6910
- const typeNameMapper = config?.typeNameMapper;
6911
- const features = {
6912
- aggregates: config?.features?.aggregates ?? true,
6913
- groupBy: config?.features?.groupBy ?? true,
6914
- relationAggregates: config?.features?.relationAggregates ?? true,
6915
- distinct: config?.features?.distinct ?? true,
6916
- insert: config?.features?.insert ?? true,
6917
- update: config?.features?.update ?? true,
6918
- updateMany: config?.features?.updateMany ?? true,
6919
- delete: config?.features?.delete ?? true,
6920
- upsert: config?.features?.upsert ?? false,
6921
- nestedWrites: config?.features?.nestedWrites ?? false,
6922
- fieldUpdateOperations: config?.features?.fieldUpdateOperations ?? false,
6923
- countMutations: config?.features?.countMutations ?? false,
6924
- requireWhere: config?.features?.requireWhere ?? false
6925
- };
7145
+ const typeNameMapper = config?.typeNameMapper === "singularize" ? singularizeMapper : config?.typeNameMapper;
7146
+ const tableKeys = Object.entries(schema).filter(([, value]) => is8(value, Table)).map(([key]) => key);
7147
+ const excludedTableNames = new Set(config?.exclude?.tables ?? []);
7148
+ const featureTables = tableKeys.filter((key) => !excludedTableNames.has(key));
7149
+ const forTable = resolveTableFeatures(config?.features);
6926
7150
  const complexityConfig = config?.complexity ?? true;
6927
7151
  const complexity = complexityConfig === false ? void 0 : {
6928
7152
  defaultListSize: (complexityConfig === true ? void 0 : complexityConfig.defaultListSize) ?? 10,
@@ -6959,10 +7183,8 @@ var buildSchema = (db, config) => {
6959
7183
  } : void 0;
6960
7184
  const exclude = config?.exclude;
6961
7185
  if (exclude) {
6962
- const tableNames = new Set(
6963
- Object.entries(schema).filter(([, value]) => is8(value, Table)).map(([key]) => key)
6964
- );
6965
- const excludedTables = new Set(exclude.tables ?? []);
7186
+ const tableNames = new Set(tableKeys);
7187
+ const excludedTables = excludedTableNames;
6966
7188
  for (const tableName of excludedTables) {
6967
7189
  if (!tableNames.has(tableName)) {
6968
7190
  throw new Error(
@@ -7000,6 +7222,49 @@ var buildSchema = (db, config) => {
7000
7222
  }
7001
7223
  }
7002
7224
  }
7225
+ const featureConflicts = /* @__PURE__ */ new Map();
7226
+ const noteConflict = (message, tableName) => {
7227
+ const tables = featureConflicts.get(message);
7228
+ if (tables) {
7229
+ tables.push(tableName);
7230
+ } else {
7231
+ featureConflicts.set(message, [tableName]);
7232
+ }
7233
+ };
7234
+ for (const tableName of featureTables) {
7235
+ const tableFeatures = forTable(tableName);
7236
+ if (tableFeatures.upsert) {
7237
+ const missing = [!tableFeatures.insert ? "insert" : void 0, !tableFeatures.update ? "update" : void 0].filter(Boolean).join(" and ");
7238
+ if (missing) {
7239
+ noteConflict(
7240
+ `upsert is on while ${missing} is off, so the upsert mutations are a second write path past the operation you turned off`,
7241
+ tableName
7242
+ );
7243
+ }
7244
+ }
7245
+ if (config?.features?.nestedWrites && (!tableFeatures.insert || !tableFeatures.update)) {
7246
+ noteConflict(
7247
+ "nestedWrites is on while this table's own insert or update is off, so a nested `create` or `connect` under another table's mutation can still write it",
7248
+ tableName
7249
+ );
7250
+ }
7251
+ if (config?.features?.updateMany !== void 0 && tableFeatures.updateMany && !tableFeatures.update) {
7252
+ noteConflict("updateMany is on while update is off, so no batch update is generated", tableName);
7253
+ }
7254
+ if (config?.features?.groupBy !== void 0 && tableFeatures.groupBy && !tableFeatures.aggregates) {
7255
+ noteConflict("groupBy is on while aggregates is off, so no grouped query is generated", tableName);
7256
+ }
7257
+ if (tableFeatures.countMutations && !tableFeatures.update && !tableFeatures.delete) {
7258
+ noteConflict(
7259
+ "countMutations is on while update and delete are both off, so no count mutation is generated",
7260
+ tableName
7261
+ );
7262
+ }
7263
+ }
7264
+ for (const [message, tables] of featureConflicts) {
7265
+ const listed = tables.length > 5 ? `${tables.slice(0, 5).join(", ")} and ${tables.length - 5} more` : tables.join(", ");
7266
+ console.warn(`Drizzle-GraphQL Warning: config.features \u2014 ${message} (${listed}).`);
7267
+ }
7003
7268
  const eagerOpt = config?.eagerLoadRelations;
7004
7269
  const shouldEagerLoad = eagerOpt === void 0 || eagerOpt === true ? () => true : eagerOpt === false ? () => false : eagerOpt;
7005
7270
  if (!typeNameMapper && suffixes.list === suffixes.single) {
@@ -7026,7 +7291,7 @@ var buildSchema = (db, config) => {
7026
7291
  conflictDoNothing: config?.conflictDoNothing ?? false,
7027
7292
  typeNameMapper,
7028
7293
  shouldEagerLoad,
7029
- features,
7294
+ features: config?.features ?? {},
7030
7295
  complexity,
7031
7296
  scalars: config?.scalars,
7032
7297
  mapColumnType: config?.mapColumnType,
@@ -7087,6 +7352,7 @@ export {
7087
7352
  drizzleExecutorKey,
7088
7353
  extractFilters,
7089
7354
  extractOrderBy,
7090
- extractRelationJoinColumns
7355
+ extractRelationJoinColumns,
7356
+ singularizeMapper
7091
7357
  };
7092
7358
  //# sourceMappingURL=index.js.map