@vantreeseba/drizzle-graphql 5.2.0 → 5.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.cjs CHANGED
@@ -1013,6 +1013,13 @@ var generateTableSelectTypeFieldsCached = (table, tableName) => {
1013
1013
  drizzleColumnToGraphQLType(columnDescription, columnName, tableName)
1014
1014
  ])
1015
1015
  );
1016
+ if (!remapped[CURSOR_FIELD_NAME]) {
1017
+ remapped[CURSOR_FIELD_NAME] = {
1018
+ type: import_graphql4.GraphQLString,
1019
+ description: "Opaque cursor of this row's position in the query's ordering. Pass it as `after` to resume from here. Only set on rows returned by a list query.",
1020
+ resolve: rowCursorResolver
1021
+ };
1022
+ }
1016
1023
  fieldMap.set(table, remapped);
1017
1024
  return remapped;
1018
1025
  };
@@ -1840,6 +1847,117 @@ var primaryKeyOrderExprs = (table, pkNames) => {
1840
1847
  const cols = (0, import_drizzle_orm3.getColumns)(table);
1841
1848
  return pkNames.map((n) => cols[n]).filter(Boolean).map((col) => (0, import_drizzle_orm3.asc)(col));
1842
1849
  };
1850
+ var CURSOR_FIELD_NAME = "cursor";
1851
+ var ROW_CURSOR_PROP = "__drizzle_graphql_cursor";
1852
+ var orderByHasRelationEntry = (orderBy) => !!orderBy && Object.values(orderBy).some((config) => config && typeof config === "object" && config.direction === void 0);
1853
+ var cursorOrderingEntries = (orderBy, pkNames) => {
1854
+ const entries = orderBy ? orderByEntries(orderBy) : [];
1855
+ const seen = new Set(entries.map(([column]) => column));
1856
+ for (const pk of pkNames) {
1857
+ if (!seen.has(pk)) {
1858
+ entries.push([pk, "asc"]);
1859
+ }
1860
+ }
1861
+ return entries;
1862
+ };
1863
+ var cursorOrderExprs = (table, entries) => {
1864
+ const cols = (0, import_drizzle_orm3.getColumns)(table);
1865
+ return entries.flatMap(
1866
+ ([column, direction, nulls]) => orderExpressions(cols[column], direction, nulls ?? void 0, cols[column])
1867
+ );
1868
+ };
1869
+ var encodeCursorValue = (value) => {
1870
+ if (value instanceof Date) {
1871
+ return { $type: "date", value: value.toISOString() };
1872
+ }
1873
+ if (typeof value === "bigint") {
1874
+ return { $type: "bigint", value: value.toString() };
1875
+ }
1876
+ return value;
1877
+ };
1878
+ var decodeCursorValue = (value) => {
1879
+ if (value && typeof value === "object" && typeof value.$type === "string") {
1880
+ if (value.$type === "date") {
1881
+ return new Date(value.value);
1882
+ }
1883
+ if (value.$type === "bigint") {
1884
+ return BigInt(value.value);
1885
+ }
1886
+ }
1887
+ return value;
1888
+ };
1889
+ var encodeCursor = (entries, row) => {
1890
+ const payload = {
1891
+ o: entries,
1892
+ v: entries.map(([column]) => encodeCursorValue(row[column] ?? null))
1893
+ };
1894
+ return Buffer.from(JSON.stringify(payload), "utf8").toString("base64url");
1895
+ };
1896
+ var decodeCursor = (after, entries) => {
1897
+ let payload;
1898
+ try {
1899
+ payload = JSON.parse(Buffer.from(after, "base64url").toString("utf8"));
1900
+ } catch (_e) {
1901
+ throw new import_graphql4.GraphQLError("Invalid cursor: unable to decode it. Pass a cursor returned by a previous page.");
1902
+ }
1903
+ if (!payload || typeof payload !== "object" || !Array.isArray(payload.o) || !Array.isArray(payload.v) || payload.o.length !== payload.v.length) {
1904
+ throw new import_graphql4.GraphQLError("Invalid cursor: malformed payload. Pass a cursor returned by a previous page.");
1905
+ }
1906
+ const matchesOrdering = payload.o.length === entries.length && payload.o.every(
1907
+ (entry, i) => Array.isArray(entry) && entry[0] === entries[i][0] && entry[1] === entries[i][1] && // The nulls override changes where NULLs sort, so it is part of the ordering identity.
1908
+ // JSON round-trips undefined as null — compare the normalized forms.
1909
+ (entry[2] ?? null) === (entries[i][2] ?? null)
1910
+ );
1911
+ if (!matchesOrdering) {
1912
+ throw new import_graphql4.GraphQLError(
1913
+ "Invalid cursor: it was issued for a different ordering. Pass the same orderBy the cursor's page used."
1914
+ );
1915
+ }
1916
+ return payload.v.map(decodeCursorValue);
1917
+ };
1918
+ var buildCursorCondition = (table, entries, values, nullOrdering) => {
1919
+ const cols = (0, import_drizzle_orm3.getColumns)(table);
1920
+ const disjuncts = [];
1921
+ const equalities = [];
1922
+ entries.forEach(([column, direction, nulls], i) => {
1923
+ const col = cols[column];
1924
+ if (!col) {
1925
+ throw new import_graphql4.GraphQLError(`Invalid cursor: unknown column '${column}'.`);
1926
+ }
1927
+ const value = values[i];
1928
+ const nullsSortLast = nulls != null ? nulls === "last" : direction === "asc" === (nullOrdering === "nulls-largest");
1929
+ let strictlyAfter;
1930
+ if (value === null || value === void 0) {
1931
+ strictlyAfter = nullsSortLast ? void 0 : (0, import_drizzle_orm3.isNotNull)(col);
1932
+ } else {
1933
+ const comparison = direction === "asc" ? (0, import_drizzle_orm3.gt)(col, value) : (0, import_drizzle_orm3.lt)(col, value);
1934
+ strictlyAfter = nullsSortLast ? (0, import_drizzle_orm3.or)(comparison, (0, import_drizzle_orm3.isNull)(col)) : comparison;
1935
+ }
1936
+ if (strictlyAfter) {
1937
+ disjuncts.push(equalities.length ? (0, import_drizzle_orm3.and)(...equalities, strictlyAfter) : strictlyAfter);
1938
+ }
1939
+ equalities.push(value === null || value === void 0 ? (0, import_drizzle_orm3.isNull)(col) : (0, import_drizzle_orm3.eq)(col, value));
1940
+ });
1941
+ if (!disjuncts.length) {
1942
+ return import_drizzle_orm3.sql`1 = 0`;
1943
+ }
1944
+ return disjuncts.length > 1 ? (0, import_drizzle_orm3.or)(...disjuncts) : disjuncts[0];
1945
+ };
1946
+ var isCursorFieldSelected = (tree, table) => {
1947
+ if (!tree) {
1948
+ return false;
1949
+ }
1950
+ if ((0, import_drizzle_orm3.getColumns)(table)[CURSOR_FIELD_NAME]) {
1951
+ return false;
1952
+ }
1953
+ return Object.values(tree).some((field) => field.name === CURSOR_FIELD_NAME);
1954
+ };
1955
+ var attachRowCursors = (rows, entries) => {
1956
+ for (const row of rows) {
1957
+ row[ROW_CURSOR_PROP] = encodeCursor(entries, row);
1958
+ }
1959
+ };
1960
+ var rowCursorResolver = (source) => source?.[ROW_CURSOR_PROP] ?? null;
1843
1961
  var getUniqueColumnSets = (table, getTableConfig4) => {
1844
1962
  const cols = (0, import_drizzle_orm3.getColumns)(table);
1845
1963
  const propNameByColumnName = new Map(Object.entries(cols).map(([propName, col]) => [col.name, propName]));
@@ -2126,6 +2244,7 @@ var computeResolverFieldNames = (tableName, typeNameMapper, prefixes, suffixes)
2126
2244
  const upsertArrayFieldName = `${upsertPrefix}${mapped ? capitalize(mapped.plural) : capitalize(tableName)}`;
2127
2245
  const upsertSingleFieldName = mapped ? `${upsertPrefix}${capitalize(mapped.singular)}` : `${upsertPrefix}${capitalize(tableName)}${suffixes.single}`;
2128
2246
  const updateFieldName = `${prefixes.update}${mapped ? capitalize(mapped.singular) : capitalize(tableName)}`;
2247
+ const updateManyFieldName = `${prefixes.update}${mapped ? capitalize(mapped.plural) : capitalize(tableName)}Many`;
2129
2248
  const deleteFieldName = `${prefixes.delete}${mapped ? capitalize(mapped.singular) : capitalize(tableName)}`;
2130
2249
  const writeSingleSuffix = suffixes.single === "" ? "Single" : suffixes.single;
2131
2250
  const updateSingleFieldName = `${updateFieldName}${writeSingleSuffix}`;
@@ -2141,11 +2260,28 @@ var computeResolverFieldNames = (tableName, typeNameMapper, prefixes, suffixes)
2141
2260
  upsertArrayFieldName,
2142
2261
  upsertSingleFieldName,
2143
2262
  updateFieldName,
2263
+ updateManyFieldName,
2144
2264
  updateSingleFieldName,
2145
2265
  deleteFieldName,
2146
2266
  deleteSingleFieldName
2147
2267
  };
2148
2268
  };
2269
+ var generateUpdateManyInput = (params) => {
2270
+ const { typeName, updatePrefix, updateInput, tableFilters } = params;
2271
+ return new import_graphql4.GraphQLInputObjectType({
2272
+ name: `${capitalize(updatePrefix)}${typeName}ManyInput`,
2273
+ description: `One entry of a batch update of ${typeName}: the rows \`where\` matches get this entry's \`set\` applied.`,
2274
+ fields: {
2275
+ where: {
2276
+ type: tableFilters,
2277
+ description: "Rows this entry updates. An omitted filter updates every row."
2278
+ },
2279
+ set: {
2280
+ type: new import_graphql4.GraphQLNonNull(updateInput)
2281
+ }
2282
+ }
2283
+ });
2284
+ };
2149
2285
  var extractRequiredFilters = (table, tableName, where, fieldName, relationCtx) => {
2150
2286
  const filters = where ? extractFilters(table, tableName, where, relationCtx) : void 0;
2151
2287
  if (!filters) {
@@ -2164,6 +2300,10 @@ var selectArrayArgs = (orderArgs, filterArgs, distinctEnum) => ({
2164
2300
  limit: { type: import_graphql4.GraphQLInt },
2165
2301
  orderBy: { type: orderArgs },
2166
2302
  where: { type: filterArgs },
2303
+ after: {
2304
+ type: import_graphql4.GraphQLString,
2305
+ description: "Keyset pagination: only return rows strictly after this cursor (a row's `cursor` field from a previous page, under the same orderBy)."
2306
+ },
2167
2307
  ...distinctEnum ? { distinct: { type: new import_graphql4.GraphQLList(new import_graphql4.GraphQLNonNull(distinctEnum)) } } : {}
2168
2308
  });
2169
2309
  var selectSingleArgs = (orderArgs, filterArgs) => ({
@@ -2186,9 +2326,31 @@ var runRelationalSelect = async (opts) => {
2186
2326
  where,
2187
2327
  single,
2188
2328
  filterCtx,
2189
- pkNames
2329
+ pkNames,
2330
+ after
2190
2331
  } = opts;
2191
2332
  const distinct = opts.distinct?.length ? opts.distinct : void 0;
2333
+ const cursorSelected = !single && isCursorFieldSelected(parsedInfo.fieldsByTypeName[typeName], table);
2334
+ let cursorEntries;
2335
+ if (!single && (after != null || cursorSelected)) {
2336
+ if (after != null && distinct) {
2337
+ throw new import_graphql4.GraphQLError("'after' cannot be combined with 'distinct'.");
2338
+ }
2339
+ if (orderByHasRelationEntry(orderBy)) {
2340
+ if (after != null) {
2341
+ throw new import_graphql4.GraphQLError(
2342
+ "'after' cannot be combined with an orderBy that orders through a relation \u2014 a related row's value cannot be encoded into a cursor."
2343
+ );
2344
+ }
2345
+ } else if (!pkNames?.length) {
2346
+ if (after != null) {
2347
+ throw new import_graphql4.GraphQLError(`Table ${tableName} has no primary key, so cursor pagination cannot be used on it.`);
2348
+ }
2349
+ } else {
2350
+ cursorEntries = cursorOrderingEntries(orderBy, pkNames);
2351
+ }
2352
+ }
2353
+ const cursorValues = after != null && cursorEntries ? decodeCursor(after, cursorEntries) : void 0;
2192
2354
  const needsDefaultOrder = single || offset != null || opts.limit != null;
2193
2355
  let distinctKeys;
2194
2356
  if (distinct) {
@@ -2219,9 +2381,21 @@ var runRelationalSelect = async (opts) => {
2219
2381
  orderBy: distinctKeys ? (aliasedTable2) => [
2220
2382
  ...orderBy ? extractOrderBy(aliasedTable2, orderBy, relationFilterCtx(filterCtx, tableName)) : [],
2221
2383
  ...primaryKeyOrderExprs(aliasedTable2, pkNames)
2222
- ] : orderBy ? (aliasedTable2) => extractOrderBy(aliasedTable2, orderBy, relationFilterCtx(filterCtx, tableName)) : needsDefaultOrder && pkNames?.length ? (aliasedTable2) => primaryKeyOrderExprs(aliasedTable2, pkNames) : void 0,
2223
- where: distinctKeys ? { RAW: (aliasedTable2) => primaryKeyRestriction(aliasedTable2, pkNames, distinctKeys) } : where ? {
2224
- RAW: (aliasedTable2) => extractFilters(aliasedTable2, tableName, where, relationFilterCtx(filterCtx, tableName))
2384
+ ] : cursorEntries ? (
2385
+ // Cursor pagination needs a total order: the request's orderBy plus the PK tiebreak,
2386
+ // exactly the ordering the cursor encodes and the keyset predicate compares against.
2387
+ (aliasedTable2) => cursorOrderExprs(aliasedTable2, cursorEntries)
2388
+ ) : orderBy ? (aliasedTable2) => extractOrderBy(aliasedTable2, orderBy, relationFilterCtx(filterCtx, tableName)) : needsDefaultOrder && pkNames?.length ? (aliasedTable2) => primaryKeyOrderExprs(aliasedTable2, pkNames) : void 0,
2389
+ where: distinctKeys ? { RAW: (aliasedTable2) => primaryKeyRestriction(aliasedTable2, pkNames, distinctKeys) } : where || cursorValues ? {
2390
+ RAW: (aliasedTable2) => (0, import_drizzle_orm3.and)(
2391
+ where ? extractFilters(aliasedTable2, tableName, where, relationFilterCtx(filterCtx, tableName)) : void 0,
2392
+ cursorValues ? buildCursorCondition(
2393
+ aliasedTable2,
2394
+ cursorEntries,
2395
+ cursorValues,
2396
+ opts.nullOrdering ?? "nulls-smallest"
2397
+ ) : void 0
2398
+ )
2225
2399
  } : void 0,
2226
2400
  with: relationMap[tableName] ? extractRelationsParams(relationMap, tables, tableName, parsedInfo, typeName, typeNameMapper, filterCtx) : void 0
2227
2401
  };
@@ -2229,8 +2403,16 @@ var runRelationalSelect = async (opts) => {
2229
2403
  const result2 = await queryBase.findFirst(params);
2230
2404
  return result2 ? remapToGraphQLSingleOutput(result2, tableName, table, relationMap) : void 0;
2231
2405
  }
2406
+ if (cursorEntries && cursorSelected) {
2407
+ for (const [column] of cursorEntries) {
2408
+ params.columns[column] = true;
2409
+ }
2410
+ }
2232
2411
  params.limit = distinctKeys ? void 0 : opts.limit;
2233
2412
  const result = await queryBase.findMany(params);
2413
+ if (cursorEntries && cursorSelected) {
2414
+ attachRowCursors(result, cursorEntries);
2415
+ }
2234
2416
  return remapToGraphQLArrayOutput(result, tableName, table, relationMap);
2235
2417
  };
2236
2418
  var eagerLoadMutationRelations = async (db, tableName, rows, pkNames, withParams) => {
@@ -2778,7 +2960,9 @@ var generateSelectArray = (db, tableName, tables, relationMap, orderArgs, filter
2778
2960
  single: false,
2779
2961
  filterCtx,
2780
2962
  pkNames,
2781
- db: executor
2963
+ db: executor,
2964
+ // MySQL sorts NULLs as the smallest values (first in ASC).
2965
+ nullOrdering: "nulls-smallest"
2782
2966
  });
2783
2967
  } catch (e) {
2784
2968
  throw toGraphQLError(e);
@@ -2947,6 +3131,48 @@ var generateUpdate = (db, tableName, table, setArgs, filterArgs, fieldName, sing
2947
3131
  args: queryArgs
2948
3132
  };
2949
3133
  };
3134
+ var generateUpdateMany = (db, tableName, table, updateManyInput, fieldName, filterCtx) => {
3135
+ const queryArgs = {
3136
+ updates: {
3137
+ type: new import_graphql6.GraphQLNonNull(new import_graphql6.GraphQLList(new import_graphql6.GraphQLNonNull(updateManyInput)))
3138
+ }
3139
+ };
3140
+ return {
3141
+ name: fieldName,
3142
+ resolver: async (_source, args, context, _info) => {
3143
+ try {
3144
+ const { updates } = args;
3145
+ if (!updates.length) {
3146
+ throw new import_graphql6.GraphQLError("No updates were provided!");
3147
+ }
3148
+ const entries = updates.map(({ where, set }) => {
3149
+ const input = remapFromGraphQLSingleInput(set, table);
3150
+ if (!Object.keys(input).length) {
3151
+ throw new import_graphql6.GraphQLError("Unable to update with no values specified!");
3152
+ }
3153
+ return {
3154
+ set: input,
3155
+ filters: where ? extractFilters(table, tableName, where, relationFilterCtx(filterCtx, tableName)) : void 0
3156
+ };
3157
+ });
3158
+ const executor = resolveExecutor(db, context);
3159
+ await executor.transaction(async (tx) => {
3160
+ for (const entry of entries) {
3161
+ let query = tx.update(table).set(entry.set);
3162
+ if (entry.filters) {
3163
+ query = query.where(entry.filters);
3164
+ }
3165
+ await query;
3166
+ }
3167
+ });
3168
+ return { isSuccess: true };
3169
+ } catch (e) {
3170
+ throw toGraphQLError(e);
3171
+ }
3172
+ },
3173
+ args: queryArgs
3174
+ };
3175
+ };
2950
3176
  var generateDelete = (db, tableName, table, filterArgs, fieldName, single, requireWhere, filterCtx) => {
2951
3177
  const queryArgs = {
2952
3178
  where: {
@@ -3054,6 +3280,7 @@ var generateSchemaData = (db, schema, relations, options) => {
3054
3280
  upsertArrayFieldName,
3055
3281
  upsertSingleFieldName,
3056
3282
  updateFieldName,
3283
+ updateManyFieldName,
3057
3284
  updateSingleFieldName,
3058
3285
  deleteFieldName,
3059
3286
  deleteSingleFieldName
@@ -3116,6 +3343,15 @@ var generateSchemaData = (db, schema, relations, options) => {
3116
3343
  features.requireWhere,
3117
3344
  filterCtx
3118
3345
  ) : void 0;
3346
+ const updateManyInput = features.update && features.updateMany ? generateUpdateManyInput({ typeName, updatePrefix: prefixes.update, updateInput, tableFilters }) : void 0;
3347
+ const updateManyGenerated = updateManyInput ? generateUpdateMany(
3348
+ db,
3349
+ tableName,
3350
+ schema[tableName],
3351
+ updateManyInput,
3352
+ updateManyFieldName,
3353
+ filterCtx
3354
+ ) : void 0;
3119
3355
  const deleteGenerated = features.delete ? generateDelete(
3120
3356
  db,
3121
3357
  tableName,
@@ -3193,6 +3429,7 @@ var generateSchemaData = (db, schema, relations, options) => {
3193
3429
  upsertArrGenerated,
3194
3430
  upsertSingleGenerated,
3195
3431
  updateGenerated,
3432
+ updateManyGenerated,
3196
3433
  updateSingleGenerated,
3197
3434
  deleteGenerated,
3198
3435
  deleteSingleGenerated
@@ -3210,6 +3447,7 @@ var generateSchemaData = (db, schema, relations, options) => {
3210
3447
  ...features.insert || onConflictInput ? [insertInput] : [],
3211
3448
  ...onConflictInput ? [onConflictInput] : [],
3212
3449
  ...features.update ? [updateInput] : [],
3450
+ ...updateManyInput ? [updateManyInput] : [],
3213
3451
  tableFilters,
3214
3452
  tableOrder
3215
3453
  ];
@@ -3276,16 +3514,48 @@ var generateSelectArray2 = (db, tableName, tables, relationMap, orderArgs, filte
3276
3514
  single: false,
3277
3515
  filterCtx,
3278
3516
  pkNames,
3279
- db: executor
3517
+ db: executor,
3518
+ // PostgreSQL sorts NULLs as the largest values (last in ASC).
3519
+ nullOrdering: "nulls-largest"
3280
3520
  });
3281
3521
  }
3282
- const { offset, limit, orderBy, where, distinct } = args;
3522
+ const { offset, limit, orderBy, where, distinct, after } = args;
3283
3523
  const selectedColumnsSql = extractSelectedColumnsFromTreeSQLFormat(
3284
3524
  parsedInfo.fieldsByTypeName[typeName],
3285
3525
  table,
3286
3526
  { tableName, relationMap, tables }
3287
3527
  );
3288
- const whereSql = where ? extractFilters(table, tableName, where, relationFilterCtx(filterCtx, tableName)) : void 0;
3528
+ const cursorSelected = isCursorFieldSelected(parsedInfo.fieldsByTypeName[typeName], table);
3529
+ let cursorEntries;
3530
+ if (after != null || cursorSelected) {
3531
+ if (after != null && distinct?.length) {
3532
+ throw new import_graphql7.GraphQLError("'after' cannot be combined with 'distinct'.");
3533
+ }
3534
+ if (orderByHasRelationEntry(orderBy)) {
3535
+ if (after != null) {
3536
+ throw new import_graphql7.GraphQLError(
3537
+ "'after' cannot be combined with an orderBy that orders through a relation \u2014 a related row's value cannot be encoded into a cursor."
3538
+ );
3539
+ }
3540
+ } else if (!pkNames.length) {
3541
+ if (after != null) {
3542
+ throw new import_graphql7.GraphQLError(
3543
+ `Table ${tableName} has no primary key, so cursor pagination cannot be used on it.`
3544
+ );
3545
+ }
3546
+ } else {
3547
+ cursorEntries = cursorOrderingEntries(orderBy, pkNames);
3548
+ if (cursorSelected) {
3549
+ const allColumns = (0, import_drizzle_orm6.getColumns)(table);
3550
+ for (const [column] of cursorEntries) {
3551
+ selectedColumnsSql[column] ??= allColumns[column];
3552
+ }
3553
+ }
3554
+ }
3555
+ }
3556
+ const cursorCondition = after != null && cursorEntries ? buildCursorCondition(table, cursorEntries, decodeCursor(after, cursorEntries), "nulls-largest") : void 0;
3557
+ const baseWhereSql = where ? extractFilters(table, tableName, where, relationFilterCtx(filterCtx, tableName)) : void 0;
3558
+ const whereSql = cursorCondition ? (0, import_drizzle_orm6.and)(baseWhereSql, cursorCondition) : baseWhereSql;
3289
3559
  let distinctKeys;
3290
3560
  if (distinct?.length) {
3291
3561
  distinctKeys = await selectDistinctKeys({
@@ -3309,7 +3579,9 @@ var generateSelectArray2 = (db, tableName, tables, relationMap, orderArgs, filte
3309
3579
  } else if (whereSql) {
3310
3580
  q = q.where(whereSql);
3311
3581
  }
3312
- if (orderBy) {
3582
+ if (cursorEntries && !distinctKeys) {
3583
+ q = q.orderBy(...cursorOrderExprs(table, cursorEntries));
3584
+ } else if (orderBy) {
3313
3585
  q = q.orderBy(
3314
3586
  ...extractOrderBy(table, orderBy, relationFilterCtx(filterCtx, tableName)),
3315
3587
  ...distinctKeys ? primaryKeyOrderExprs(table, pkNames) : []
@@ -3325,7 +3597,11 @@ var generateSelectArray2 = (db, tableName, tables, relationMap, orderArgs, filte
3325
3597
  q = q.limit(limit);
3326
3598
  }
3327
3599
  }
3328
- return remapToGraphQLArrayOutput(await q, tableName, table, relationMap);
3600
+ const rows = await q;
3601
+ if (cursorEntries && cursorSelected) {
3602
+ attachRowCursors(rows, cursorEntries);
3603
+ }
3604
+ return remapToGraphQLArrayOutput(rows, tableName, table, relationMap);
3329
3605
  } catch (e) {
3330
3606
  throw toGraphQLError(e);
3331
3607
  }
@@ -3591,6 +3867,78 @@ var generateUpdate2 = (db, tableName, table, tables, relationMap, setArgs, filte
3591
3867
  args: queryArgs
3592
3868
  };
3593
3869
  };
3870
+ var generateUpdateMany2 = (db, tableName, table, tables, relationMap, updateManyInput, fieldName, typeName, typeNameMapper, filterCtx) => {
3871
+ const queryArgs = {
3872
+ updates: {
3873
+ type: new import_graphql7.GraphQLNonNull(new import_graphql7.GraphQLList(new import_graphql7.GraphQLNonNull(updateManyInput)))
3874
+ }
3875
+ };
3876
+ const pkNames = pgPrimaryKeyPropNames(table);
3877
+ return {
3878
+ name: fieldName,
3879
+ resolver: async (_source, args, context, info) => {
3880
+ try {
3881
+ const { updates } = args;
3882
+ if (!updates.length) {
3883
+ throw new import_graphql7.GraphQLError("No updates were provided!");
3884
+ }
3885
+ const parsedInfo = (0, import_graphql_parse_resolve_info3.parseResolveInfo)(info, {
3886
+ deep: true
3887
+ });
3888
+ const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
3889
+ relationMap,
3890
+ tables,
3891
+ tableName,
3892
+ typeName,
3893
+ typeNameMapper,
3894
+ table,
3895
+ pkNames,
3896
+ parsedInfo
3897
+ });
3898
+ const entries = updates.map(({ where, set }) => {
3899
+ const input = remapFromGraphQLSingleInput(set, table);
3900
+ if (!Object.keys(input).length) {
3901
+ throw new import_graphql7.GraphQLError("Unable to update with no values specified!");
3902
+ }
3903
+ return {
3904
+ set: input,
3905
+ filters: where ? extractFilters(table, tableName, where, relationFilterCtx(filterCtx, tableName)) : void 0
3906
+ };
3907
+ });
3908
+ const executor = resolveExecutor(db, context);
3909
+ const perEntry = await executor.transaction(async (tx) => {
3910
+ const results = [];
3911
+ for (const entry of entries) {
3912
+ let query = tx.update(table).set(entry.set);
3913
+ if (entry.filters) {
3914
+ query = query.where(entry.filters);
3915
+ }
3916
+ results.push(await query.returning(columns));
3917
+ }
3918
+ return results;
3919
+ });
3920
+ const flatRows = perEntry.flat();
3921
+ const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, flatRows, pkNames, withParams) : flatRows;
3922
+ const output = [];
3923
+ let offset = 0;
3924
+ for (const rows of perEntry) {
3925
+ if (!rows.length) {
3926
+ output.push(null);
3927
+ continue;
3928
+ }
3929
+ for (let i = 0; i < rows.length; i++) {
3930
+ output.push(remapToGraphQLSingleOutput(enriched[offset + i], tableName, table, relationMap));
3931
+ }
3932
+ offset += rows.length;
3933
+ }
3934
+ return output;
3935
+ } catch (e) {
3936
+ throw toGraphQLError(e);
3937
+ }
3938
+ },
3939
+ args: queryArgs
3940
+ };
3941
+ };
3594
3942
  var generateDelete2 = (db, tableName, table, filterArgs, fieldName, typeName, single, requireWhere, filterCtx, selectionCtx) => {
3595
3943
  const queryArgs = {
3596
3944
  where: {
@@ -3709,6 +4057,7 @@ function generateSchemaData2(db, schema, relations, options) {
3709
4057
  upsertArrayFieldName,
3710
4058
  upsertSingleFieldName,
3711
4059
  updateFieldName,
4060
+ updateManyFieldName,
3712
4061
  updateSingleFieldName,
3713
4062
  deleteFieldName,
3714
4063
  deleteSingleFieldName
@@ -3830,6 +4179,19 @@ function generateSchemaData2(db, schema, relations, options) {
3830
4179
  typeNameMapper,
3831
4180
  filterCtx
3832
4181
  ) : void 0;
4182
+ const updateManyInput = features.update && features.updateMany ? generateUpdateManyInput({ typeName, updatePrefix: prefixes.update, updateInput, tableFilters }) : void 0;
4183
+ const updateManyGenerated = updateManyInput ? generateUpdateMany2(
4184
+ db,
4185
+ tableName,
4186
+ schema[tableName],
4187
+ tables,
4188
+ eagerRelations,
4189
+ updateManyInput,
4190
+ updateManyFieldName,
4191
+ typeName,
4192
+ typeNameMapper,
4193
+ filterCtx
4194
+ ) : void 0;
3833
4195
  const deleteGenerated = features.delete ? generateDelete2(
3834
4196
  db,
3835
4197
  tableName,
@@ -3940,6 +4302,14 @@ function generateSchemaData2(db, schema, relations, options) {
3940
4302
  resolve: updateGenerated.resolver
3941
4303
  };
3942
4304
  }
4305
+ if (updateManyGenerated) {
4306
+ mutations[updateManyGenerated.name] = {
4307
+ // Nullable items: a no-match entry yields `null` in its slot.
4308
+ type: new import_graphql7.GraphQLNonNull(new import_graphql7.GraphQLList(singleTableItemOutput)),
4309
+ args: updateManyGenerated.args,
4310
+ resolve: updateManyGenerated.resolver
4311
+ };
4312
+ }
3943
4313
  if (updateSingleGenerated) {
3944
4314
  mutations[updateSingleGenerated.name] = {
3945
4315
  type: singleTableItemOutput,
@@ -3966,6 +4336,7 @@ function generateSchemaData2(db, schema, relations, options) {
3966
4336
  ...features.insert || onConflictInput ? [insertInput] : [],
3967
4337
  ...onConflictInput ? [onConflictInput] : [],
3968
4338
  ...features.update ? [updateInput] : [],
4339
+ ...updateManyInput ? [updateManyInput] : [],
3969
4340
  tableFilters,
3970
4341
  tableOrder
3971
4342
  ];
@@ -4037,7 +4408,9 @@ var generateSelectArray3 = (db, tableName, tables, relationMap, orderArgs, filte
4037
4408
  single: false,
4038
4409
  filterCtx,
4039
4410
  pkNames,
4040
- db: executor
4411
+ db: executor,
4412
+ // SQLite sorts NULLs as the smallest values (first in ASC).
4413
+ nullOrdering: "nulls-smallest"
4041
4414
  });
4042
4415
  } catch (e) {
4043
4416
  throw toGraphQLError(e);
@@ -4286,6 +4659,91 @@ var generateUpdate3 = (db, tableName, table, tables, relationMap, setArgs, filte
4286
4659
  args: queryArgs
4287
4660
  };
4288
4661
  };
4662
+ var generateUpdateMany3 = (db, tableName, table, tables, relationMap, updateManyInput, fieldName, typeName, typeNameMapper, filterCtx) => {
4663
+ const queryArgs = {
4664
+ updates: {
4665
+ type: new import_graphql8.GraphQLNonNull(new import_graphql8.GraphQLList(new import_graphql8.GraphQLNonNull(updateManyInput)))
4666
+ }
4667
+ };
4668
+ const pkNames = sqlitePrimaryKeyPropNames(table);
4669
+ return {
4670
+ name: fieldName,
4671
+ resolver: async (_source, args, context, info) => {
4672
+ try {
4673
+ const { updates } = args;
4674
+ if (!updates.length) {
4675
+ throw new import_graphql8.GraphQLError("No updates were provided!");
4676
+ }
4677
+ const parsedInfo = (0, import_graphql_parse_resolve_info4.parseResolveInfo)(info, {
4678
+ deep: true
4679
+ });
4680
+ const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
4681
+ relationMap,
4682
+ tables,
4683
+ tableName,
4684
+ typeName,
4685
+ typeNameMapper,
4686
+ table,
4687
+ pkNames,
4688
+ parsedInfo
4689
+ });
4690
+ const entries = updates.map(({ where, set }) => {
4691
+ const input = remapFromGraphQLSingleInput(set, table);
4692
+ if (!Object.keys(input).length) {
4693
+ throw new import_graphql8.GraphQLError("Unable to update with no values specified!");
4694
+ }
4695
+ return {
4696
+ set: input,
4697
+ filters: where ? extractFilters(table, tableName, where, relationFilterCtx(filterCtx, tableName)) : void 0
4698
+ };
4699
+ });
4700
+ const runEntry = (tx, entry) => {
4701
+ let query = tx.update(table).set(entry.set);
4702
+ if (entry.filters) {
4703
+ query = query.where(entry.filters);
4704
+ }
4705
+ return query.returning(columns).all();
4706
+ };
4707
+ const executor = resolveExecutor(db, context);
4708
+ const perEntry = await executor.transaction((tx) => {
4709
+ const first = runEntry(tx, entries[0]);
4710
+ if (typeof first?.then === "function") {
4711
+ return (async () => {
4712
+ const results2 = [await first];
4713
+ for (let i = 1; i < entries.length; i++) {
4714
+ results2.push(await runEntry(tx, entries[i]));
4715
+ }
4716
+ return results2;
4717
+ })();
4718
+ }
4719
+ const results = [first];
4720
+ for (let i = 1; i < entries.length; i++) {
4721
+ results.push(runEntry(tx, entries[i]));
4722
+ }
4723
+ return results;
4724
+ });
4725
+ const flatRows = perEntry.flat();
4726
+ const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, flatRows, pkNames, withParams) : flatRows;
4727
+ const output = [];
4728
+ let offset = 0;
4729
+ for (const rows of perEntry) {
4730
+ if (!rows.length) {
4731
+ output.push(null);
4732
+ continue;
4733
+ }
4734
+ for (let i = 0; i < rows.length; i++) {
4735
+ output.push(remapToGraphQLSingleOutput(enriched[offset + i], tableName, table, relationMap));
4736
+ }
4737
+ offset += rows.length;
4738
+ }
4739
+ return output;
4740
+ } catch (e) {
4741
+ throw toGraphQLError(e);
4742
+ }
4743
+ },
4744
+ args: queryArgs
4745
+ };
4746
+ };
4289
4747
  var generateDelete3 = (db, tableName, table, filterArgs, fieldName, typeName, single, requireWhere, filterCtx, selectionCtx) => {
4290
4748
  const queryArgs = {
4291
4749
  where: {
@@ -4405,6 +4863,7 @@ var generateSchemaData3 = (db, schema, relations, options) => {
4405
4863
  upsertArrayFieldName,
4406
4864
  upsertSingleFieldName,
4407
4865
  updateFieldName,
4866
+ updateManyFieldName,
4408
4867
  updateSingleFieldName,
4409
4868
  deleteFieldName,
4410
4869
  deleteSingleFieldName
@@ -4526,6 +4985,19 @@ var generateSchemaData3 = (db, schema, relations, options) => {
4526
4985
  typeNameMapper,
4527
4986
  filterCtx
4528
4987
  ) : void 0;
4988
+ const updateManyInput = features.update && features.updateMany ? generateUpdateManyInput({ typeName, updatePrefix: prefixes.update, updateInput, tableFilters }) : void 0;
4989
+ const updateManyGenerated = updateManyInput ? generateUpdateMany3(
4990
+ db,
4991
+ tableName,
4992
+ schema[tableName],
4993
+ tables,
4994
+ eagerRelations,
4995
+ updateManyInput,
4996
+ updateManyFieldName,
4997
+ typeName,
4998
+ typeNameMapper,
4999
+ filterCtx
5000
+ ) : void 0;
4529
5001
  const deleteGenerated = features.delete ? generateDelete3(
4530
5002
  db,
4531
5003
  tableName,
@@ -4636,6 +5108,14 @@ var generateSchemaData3 = (db, schema, relations, options) => {
4636
5108
  resolve: updateGenerated.resolver
4637
5109
  };
4638
5110
  }
5111
+ if (updateManyGenerated) {
5112
+ mutations[updateManyGenerated.name] = {
5113
+ // Nullable items: a no-match entry yields `null` in its slot.
5114
+ type: new import_graphql8.GraphQLNonNull(new import_graphql8.GraphQLList(singleTableItemOutput)),
5115
+ args: updateManyGenerated.args,
5116
+ resolve: updateManyGenerated.resolver
5117
+ };
5118
+ }
4639
5119
  if (updateSingleGenerated) {
4640
5120
  mutations[updateSingleGenerated.name] = {
4641
5121
  type: singleTableItemOutput,
@@ -4662,6 +5142,7 @@ var generateSchemaData3 = (db, schema, relations, options) => {
4662
5142
  ...features.insert || onConflictInput ? [insertInput] : [],
4663
5143
  ...onConflictInput ? [onConflictInput] : [],
4664
5144
  ...features.update ? [updateInput] : [],
5145
+ ...updateManyInput ? [updateManyInput] : [],
4665
5146
  tableFilters,
4666
5147
  tableOrder
4667
5148
  ];
@@ -4724,6 +5205,7 @@ var buildSchema = (db, config) => {
4724
5205
  distinct: config?.features?.distinct ?? true,
4725
5206
  insert: config?.features?.insert ?? true,
4726
5207
  update: config?.features?.update ?? true,
5208
+ updateMany: config?.features?.updateMany ?? true,
4727
5209
  delete: config?.features?.delete ?? true,
4728
5210
  upsert: config?.features?.upsert ?? false,
4729
5211
  requireWhere: config?.features?.requireWhere ?? false