@vantreeseba/drizzle-graphql 7.6.0 → 7.8.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
@@ -1106,7 +1106,7 @@ var runOnSharedTx = async (state, body) => {
1106
1106
  throw e;
1107
1107
  }
1108
1108
  };
1109
- var runMutation = async (db, context, info, txCtx, body) => {
1109
+ var runMutation = async (db, context, info, txCtx, body, forceTx) => {
1110
1110
  const contextIsObject = context !== null && (typeof context === "object" || typeof context === "function");
1111
1111
  const stateKey = contextIsObject ? context : info.operation;
1112
1112
  let state = txCtx?.states.get(stateKey);
@@ -1115,7 +1115,7 @@ var runMutation = async (db, context, info, txCtx, body) => {
1115
1115
  return body(context[drizzleExecutorKey]);
1116
1116
  }
1117
1117
  if (!txCtx || info.operation.operation !== "mutation") {
1118
- return body(db);
1118
+ return forceTx ? db.transaction((tx) => body(tx)) : body(db);
1119
1119
  }
1120
1120
  const keys = /* @__PURE__ */ new Set();
1121
1121
  const allKnown = collectRootMutationKeys(
@@ -1130,10 +1130,28 @@ var runMutation = async (db, context, info, txCtx, body) => {
1130
1130
  txCtx.states.set(stateKey, state);
1131
1131
  }
1132
1132
  if (state.kind === "passthrough") {
1133
- return body(resolveExecutor(db, context));
1133
+ const executor = resolveExecutor(db, context);
1134
+ return forceTx && executor === db ? db.transaction((tx) => body(tx)) : body(executor);
1134
1135
  }
1135
1136
  return runOnSharedTx(state, body);
1136
1137
  };
1138
+ var normalizeWriteHooks = (hooks) => {
1139
+ if (!hooks) {
1140
+ return void 0;
1141
+ }
1142
+ if (typeof hooks === "function") {
1143
+ return { after: hooks };
1144
+ }
1145
+ const resolved = { before: hooks.before, after: hooks.after };
1146
+ return resolved.before || resolved.after ? resolved : void 0;
1147
+ };
1148
+ var runWriteHook = (hooks, position, payload) => {
1149
+ const hook = position === "before" ? hooks?.before : hooks?.after;
1150
+ if (!hook) {
1151
+ return void 0;
1152
+ }
1153
+ return Promise.resolve(hook({ ...payload, position, rows: payload.rows ?? [] }));
1154
+ };
1137
1155
  var batchedPaginatedRelationQuery = async (db, targetTable, foreignCol, whereCondition, orderByArg, limit, offset, pkNames, orderCtx, whereArgs) => {
1138
1156
  const cols = getColumns2(targetTable);
1139
1157
  const orderExprs = [
@@ -1155,7 +1173,7 @@ var batchedPaginatedRelationQuery = async (db, targetTable, foreignCol, whereCon
1155
1173
  }
1156
1174
  return rows;
1157
1175
  };
1158
- var createRelationResolverFactory = (db, tables, nullOrdering, filterCtx, limits, scopes) => ({ tableName, relationName, relEntry, isOne }) => {
1176
+ var createRelationResolverFactory = (db, tables, nullOrdering, filterCtx, limits, policies) => ({ tableName, relationName, relEntry, isOne }) => {
1159
1177
  const parentTable = tables[tableName];
1160
1178
  const targetTableName = relEntry.targetTableName;
1161
1179
  const targetTable = tables[targetTableName];
@@ -1177,7 +1195,14 @@ var createRelationResolverFactory = (db, tables, nullOrdering, filterCtx, limits
1177
1195
  if (localValue == null) {
1178
1196
  return isOne ? null : [];
1179
1197
  }
1180
- const { where: whereArg, orderBy: orderByArg, limit: requestedLimit, offset, after } = args ?? {};
1198
+ const {
1199
+ where: whereArg,
1200
+ orderBy: orderByArg,
1201
+ limit: requestedLimit,
1202
+ offset,
1203
+ after,
1204
+ deleted
1205
+ } = args ?? {};
1181
1206
  const limit = applyLimitPolicy(requestedLimit, limitPolicy, `${tableName}.${relationName}`);
1182
1207
  const distinct = (args ?? {}).distinct?.length ? (args ?? {}).distinct : void 0;
1183
1208
  const cursorSelected = !isOne && !!info && selectsCursorField(info, targetTable);
@@ -1208,6 +1233,7 @@ var createRelationResolverFactory = (db, tables, nullOrdering, filterCtx, limits
1208
1233
  orderBy: orderByArg ?? null,
1209
1234
  limit: limit ?? null,
1210
1235
  offset: offset ?? null,
1236
+ deleted: deleted ?? null,
1211
1237
  after: after ?? null,
1212
1238
  distinct: distinct ?? null,
1213
1239
  cursor: cursorEntries ? 1 : 0
@@ -1216,7 +1242,7 @@ var createRelationResolverFactory = (db, tables, nullOrdering, filterCtx, limits
1216
1242
  const loader = getOrCreateLoader(context, loaderKey, async (parentIds) => {
1217
1243
  const executor = resolveExecutor(db, context);
1218
1244
  const uniqueIds = [...new Set(parentIds)];
1219
- const scope = resolveScope(scopes, context, filterCtx);
1245
+ const scope = resolveScope(policies, context, filterCtx);
1220
1246
  let whereCondition = withScope(
1221
1247
  scope,
1222
1248
  targetTableName,
@@ -1225,7 +1251,8 @@ var createRelationResolverFactory = (db, tables, nullOrdering, filterCtx, limits
1225
1251
  inArray(foreignCol, uniqueIds),
1226
1252
  whereArg ? extractFilters(targetTable, targetTableName, whereArg, relationFilterCtx(filterCtx, targetTableName)) : void 0,
1227
1253
  cursorValues ? buildCursorCondition(targetTable, cursorEntries, cursorValues, nullOrdering) : void 0
1228
- )
1254
+ ),
1255
+ deleted
1229
1256
  );
1230
1257
  if (distinct) {
1231
1258
  const keys = await selectDistinctKeys({
@@ -2030,7 +2057,8 @@ var generateSelectFields = (tables, tableName, relationMap, fromTableName, fromR
2030
2057
  {
2031
2058
  type: isRequired ? new GraphQLNonNull3(relType) : relType,
2032
2059
  args: {
2033
- where: { type: relSelectData.filters }
2060
+ where: { type: relSelectData.filters },
2061
+ ...deletedArg(cacheCtx.softDeleteOf, targetTableName)
2034
2062
  },
2035
2063
  resolve,
2036
2064
  ...relationDocs,
@@ -2049,6 +2077,7 @@ var generateSelectFields = (tables, tableName, relationMap, fromTableName, fromR
2049
2077
  orderBy: { type: relSelectData.order },
2050
2078
  offset: { type: GraphQLInt3 },
2051
2079
  limit: { type: GraphQLInt3 },
2080
+ ...deletedArg(cacheCtx.softDeleteOf, targetTableName),
2052
2081
  after: {
2053
2082
  type: GraphQLString2,
2054
2083
  description: "Keyset pagination: only return related rows strictly after this cursor (a row's `cursor` field from a previous page of this relation, under the same orderBy)."
@@ -2076,7 +2105,8 @@ var generateSelectFields = (tables, tableName, relationMap, fromTableName, fromR
2076
2105
  {
2077
2106
  type: new GraphQLNonNull3(relationAggregate.type),
2078
2107
  args: {
2079
- where: { type: relSelectData.filters }
2108
+ where: { type: relSelectData.filters },
2109
+ ...deletedArg(cacheCtx.softDeleteOf, targetTableName)
2080
2110
  },
2081
2111
  resolve: relationAggregate.resolve,
2082
2112
  extensions: {
@@ -2132,7 +2162,10 @@ var generateTableTypes = (tableName, tables, relationMap, withReturning, relatio
2132
2162
  const columns = visibleColumns(table);
2133
2163
  const columnEntries = Object.entries(columns);
2134
2164
  const contextColumns = cacheCtx.contextValuesOf?.(tableName);
2135
- const writableEntries = contextColumns ? columnEntries.filter(([columnName]) => !(columnName in contextColumns)) : columnEntries;
2165
+ const markerColumn = cacheCtx.softDeleteOf?.(tableName)?.columnName;
2166
+ const writableEntries = contextColumns || markerColumn ? columnEntries.filter(
2167
+ ([columnName]) => !(contextColumns && columnName in contextColumns) && columnName !== markerColumn
2168
+ ) : columnEntries;
2136
2169
  const relaxedColumns = nestedWrites?.relaxedColumns(tableName);
2137
2170
  const insertFields = Object.fromEntries(
2138
2171
  writableEntries.map(([columnName, column]) => {
@@ -2674,40 +2707,104 @@ var extractFilters = (table, tableName, filters, relationCtx) => {
2674
2707
  }
2675
2708
  return variants.length ? variants.length > 1 ? and(...variants) : variants[0] : void 0;
2676
2709
  };
2677
- var resolveScope = (scopes, context, filterCtx) => {
2678
- if (!scopes) {
2710
+ var deletedFilterEnum = new GraphQLEnumType2({
2711
+ name: "DeletedFilter",
2712
+ description: "Which rows a read over a soft-deleting table returns.",
2713
+ values: {
2714
+ EXCLUDE: { value: "EXCLUDE", description: "Only rows that are not marked deleted. The default." },
2715
+ INCLUDE: { value: "INCLUDE", description: "Marked and unmarked rows alike." },
2716
+ ONLY: { value: "ONLY", description: "Only rows that are marked deleted \u2014 a trash view." }
2717
+ }
2718
+ });
2719
+ var resolveSoftDeleteInfo = (table, tableName, declaration) => {
2720
+ const config = typeof declaration === "string" ? { column: declaration } : declaration;
2721
+ const columnName = config?.column;
2722
+ if (typeof columnName !== "string" || !columnName) {
2723
+ throw new Error(
2724
+ `Drizzle-GraphQL Error: config.softDelete.${tableName} must be a column name or an object with a 'column' property.`
2725
+ );
2726
+ }
2727
+ const column = getColumns2(table)[columnName];
2728
+ if (!column) {
2729
+ throw new Error(
2730
+ `Drizzle-GraphQL Error: config.softDelete names '${tableName}.${columnName}', which is not a column of that table.`
2731
+ );
2732
+ }
2733
+ const nullable = !column.notNull;
2734
+ const { type: baseType, constraint } = extractExtendedColumnType2(column);
2735
+ const hasDeleted = "deletedValue" in config && config.deletedValue !== void 0;
2736
+ const writeDeleted = hasDeleted ? typeof config.deletedValue === "function" ? config.deletedValue : () => config.deletedValue : constraint === "date" && baseType === "object" ? () => /* @__PURE__ */ new Date() : baseType === "string" ? () => (/* @__PURE__ */ new Date()).toISOString() : baseType === "number" ? () => Date.now() : baseType === "bigint" ? () => BigInt(Date.now()) : () => true;
2737
+ let marker;
2738
+ if (!nullable) {
2739
+ if (hasDeleted && typeof config.deletedValue !== "function") {
2740
+ marker = config.deletedValue;
2741
+ } else if (!hasDeleted && baseType === "boolean") {
2742
+ marker = true;
2743
+ } else {
2744
+ throw new Error(
2745
+ `Drizzle-GraphQL Error: config.softDelete.${tableName} marks a NOT NULL column ('${columnName}'), so 'deletedValue' must be a constant that means deleted \u2014 not a function, and not omitted for a non-boolean column.`
2746
+ );
2747
+ }
2748
+ }
2749
+ const hasRestored = "restoredValue" in config;
2750
+ const writeRestored = hasRestored ? config.restoredValue : nullable ? null : baseType === "boolean" ? false : void 0;
2751
+ if (!nullable && writeRestored === void 0) {
2752
+ throw new Error(
2753
+ `Drizzle-GraphQL Error: config.softDelete.${tableName} marks a NOT NULL column ('${columnName}'), so 'restoredValue' must say what restoring writes back.`
2754
+ );
2755
+ }
2756
+ return { columnName, column, nullable, writeDeleted, writeRestored, marker };
2757
+ };
2758
+ var softDeletePredicate = (info, table, mode) => {
2759
+ if (mode === "INCLUDE") {
2760
+ return void 0;
2761
+ }
2762
+ const column = table?.[info.columnName] ?? info.column;
2763
+ if (info.nullable) {
2764
+ return mode === "ONLY" ? isNotNull(column) : isNull(column);
2765
+ }
2766
+ return mode === "ONLY" ? eq(column, info.marker) : ne(column, info.marker);
2767
+ };
2768
+ var resolveScope = (policies, context, filterCtx) => {
2769
+ const scopes = policies?.scope;
2770
+ const softDelete = policies?.softDelete;
2771
+ if (!scopes && !softDelete) {
2679
2772
  return void 0;
2680
2773
  }
2681
2774
  return {
2682
- has: (tableName) => !!scopes(tableName),
2683
- on: (tableName, table) => {
2684
- const hook = scopes(tableName);
2775
+ has: (tableName, mode) => !!scopes?.(tableName) || !!softDelete?.(tableName) && (mode ?? "EXCLUDE") !== "INCLUDE",
2776
+ on: (tableName, table, mode) => {
2777
+ const marked = softDelete?.(tableName);
2778
+ const deleted = marked ? softDeletePredicate(marked, table, mode ?? "EXCLUDE") : void 0;
2779
+ const hook = scopes?.(tableName);
2685
2780
  if (!hook) {
2686
- return void 0;
2781
+ return deleted;
2687
2782
  }
2688
2783
  const predicate = hook(context, table);
2689
2784
  if (predicate === void 0 || predicate === null) {
2690
- return void 0;
2785
+ return deleted;
2691
2786
  }
2692
2787
  if (is3(predicate, SQL)) {
2693
- return predicate;
2788
+ return and(deleted, predicate);
2694
2789
  }
2695
2790
  if (typeof predicate !== "object") {
2696
2791
  throw new GraphQLError4(
2697
2792
  `Drizzle-GraphQL Error: the scope for '${tableName}' returned a ${typeof predicate}. A scope returns a filter object, a Drizzle SQL expression, or undefined.`
2698
2793
  );
2699
2794
  }
2700
- return extractFilters(table, tableName, predicate, relationFilterCtx(filterCtx, tableName));
2795
+ return and(deleted, extractFilters(table, tableName, predicate, relationFilterCtx(filterCtx, tableName)));
2701
2796
  }
2702
2797
  };
2703
2798
  };
2704
- var withScope = (scope, tableName, table, condition) => {
2705
- const predicate = scope?.on(tableName, table);
2799
+ var withScope = (scope, tableName, table, condition, mode) => {
2800
+ const predicate = scope?.on(tableName, table, mode);
2706
2801
  return predicate ? and(condition, predicate) : condition;
2707
2802
  };
2708
- var bindPolicies = (policies, filterCtx) => policies?.scope || policies?.contextValues ? {
2709
- scope: policies.scope ? (context) => resolveScope(policies.scope, context, filterCtx) : void 0,
2710
- contextValues: policies.contextValues
2803
+ var bindPolicies = (policies, filterCtx) => policies?.scope || policies?.contextValues || policies?.softDelete || policies?.onWrite ? {
2804
+ scope: policies.scope || policies.softDelete ? (context) => resolveScope(policies, context, filterCtx) : void 0,
2805
+ contextValues: policies.contextValues,
2806
+ softDelete: policies.softDelete,
2807
+ onWrite: policies.onWrite
2711
2808
  } : void 0;
2712
2809
  var applyContextValues = (values, hooks, context) => {
2713
2810
  if (!hooks) {
@@ -2782,12 +2879,14 @@ var extractRelationsParamsInner = (relationMap, tables, tableName, typeName, ori
2782
2879
  const offset = relationArgs?.offset ?? void 0;
2783
2880
  const limit = is3(relEntry.relation, One2) ? relationArgs?.limit ?? void 0 : applyLimitPolicy(relationArgs?.limit, limits?.(targetTableName), `${tableName}.${relName}`);
2784
2881
  const relWhere = relationArgs?.where;
2785
- thisRecord.where = relWhere || scope?.has(targetTableName) ? {
2882
+ const relDeleted = relationArgs?.deleted;
2883
+ thisRecord.where = relWhere || scope?.has(targetTableName, relDeleted) ? {
2786
2884
  RAW: (aliasedTable2) => withScope(
2787
2885
  scope,
2788
2886
  targetTableName,
2789
2887
  aliasedTable2,
2790
- relWhere ? extractFilters(aliasedTable2, relName, relWhere, relationFilterCtx(filterCtx, targetTableName)) : void 0
2888
+ relWhere ? extractFilters(aliasedTable2, relName, relWhere, relationFilterCtx(filterCtx, targetTableName)) : void 0,
2889
+ relDeleted
2791
2890
  )
2792
2891
  } : void 0;
2793
2892
  const hasPagination = offset != null || limit != null;
@@ -3313,9 +3412,11 @@ var computeResolverFieldNames = (tableName, typeNameMapper, prefixes, suffixes)
3313
3412
  const updateFieldName = `${prefixes.update}${mapped ? capitalize(mapped.singular) : capitalize(tableName)}`;
3314
3413
  const updateManyFieldName = `${prefixes.update}${mapped ? capitalize(mapped.plural) : capitalize(tableName)}Many`;
3315
3414
  const deleteFieldName = `${prefixes.delete}${mapped ? capitalize(mapped.singular) : capitalize(tableName)}`;
3415
+ const restoreFieldName = `${prefixes.restore ?? "restore"}${mapped ? capitalize(mapped.singular) : capitalize(tableName)}`;
3316
3416
  const writeSingleSuffix = suffixes.single === "" ? "Single" : suffixes.single;
3317
3417
  const updateSingleFieldName = `${updateFieldName}${writeSingleSuffix}`;
3318
3418
  const deleteSingleFieldName = `${deleteFieldName}${writeSingleSuffix}`;
3419
+ const restoreSingleFieldName = `${restoreFieldName}${writeSingleSuffix}`;
3319
3420
  const pluralNoun = mapped ? capitalize(mapped.plural) : capitalize(tableName);
3320
3421
  const updateCountFieldName = `${prefixes.update}${pluralNoun}Count`;
3321
3422
  const deleteCountFieldName = `${prefixes.delete}${pluralNoun}Count`;
@@ -3335,6 +3436,8 @@ var computeResolverFieldNames = (tableName, typeNameMapper, prefixes, suffixes)
3335
3436
  updateCountFieldName,
3336
3437
  deleteFieldName,
3337
3438
  deleteSingleFieldName,
3439
+ restoreFieldName,
3440
+ restoreSingleFieldName,
3338
3441
  deleteCountFieldName
3339
3442
  };
3340
3443
  };
@@ -3439,7 +3542,13 @@ var generateWriteCount = ({
3439
3542
  args: queryArgs
3440
3543
  };
3441
3544
  };
3442
- var selectArrayArgs = (orderArgs, filterArgs, distinctEnum) => ({
3545
+ var deletedArg = (softDelete, tableName) => softDelete?.(tableName) ? {
3546
+ deleted: {
3547
+ type: deletedFilterEnum,
3548
+ description: "Whether rows marked deleted are returned. Defaults to EXCLUDE."
3549
+ }
3550
+ } : {};
3551
+ var selectArrayArgs = (orderArgs, filterArgs, distinctEnum, softDelete, tableName) => ({
3443
3552
  offset: { type: GraphQLInt3 },
3444
3553
  limit: { type: GraphQLInt3 },
3445
3554
  orderBy: { type: orderArgs },
@@ -3448,12 +3557,14 @@ var selectArrayArgs = (orderArgs, filterArgs, distinctEnum) => ({
3448
3557
  type: GraphQLString2,
3449
3558
  description: "Keyset pagination: only return rows strictly after this cursor (a row's `cursor` field from a previous page, under the same orderBy)."
3450
3559
  },
3451
- ...distinctEnum ? { distinct: { type: new GraphQLList3(new GraphQLNonNull3(distinctEnum)) } } : {}
3560
+ ...distinctEnum ? { distinct: { type: new GraphQLList3(new GraphQLNonNull3(distinctEnum)) } } : {},
3561
+ ...deletedArg(softDelete, tableName)
3452
3562
  });
3453
- var selectSingleArgs = (orderArgs, filterArgs) => ({
3563
+ var selectSingleArgs = (orderArgs, filterArgs, softDelete, tableName) => ({
3454
3564
  offset: { type: GraphQLInt3 },
3455
3565
  orderBy: { type: orderArgs },
3456
- where: { type: filterArgs }
3566
+ where: { type: filterArgs },
3567
+ ...deletedArg(softDelete, tableName)
3457
3568
  });
3458
3569
  var runRelationalSelect = async (opts) => {
3459
3570
  const {
@@ -3472,7 +3583,8 @@ var runRelationalSelect = async (opts) => {
3472
3583
  filterCtx,
3473
3584
  pkNames,
3474
3585
  after,
3475
- scope
3586
+ scope,
3587
+ deleted
3476
3588
  } = opts;
3477
3589
  const distinct = opts.distinct?.length ? opts.distinct : void 0;
3478
3590
  const cursorSelected = !single && isCursorFieldSelected(parsedInfo.fieldsByTypeName[typeName], table);
@@ -3508,7 +3620,8 @@ var runRelationalSelect = async (opts) => {
3508
3620
  scope,
3509
3621
  tableName,
3510
3622
  table,
3511
- where ? extractFilters(table, tableName, where, relationFilterCtx(filterCtx, tableName)) : void 0
3623
+ where ? extractFilters(table, tableName, where, relationFilterCtx(filterCtx, tableName)) : void 0,
3624
+ deleted
3512
3625
  ),
3513
3626
  orderBy,
3514
3627
  limit: single ? 1 : opts.limit,
@@ -3539,7 +3652,7 @@ var runRelationalSelect = async (opts) => {
3539
3652
  where: distinctKeys ? (
3540
3653
  // The distinct pass already ran inside the scope, so the keys it picked are in it.
3541
3654
  { RAW: (aliasedTable2) => primaryKeyRestriction(aliasedTable2, pkNames, distinctKeys) }
3542
- ) : where || cursorValues || scope?.has(tableName) ? {
3655
+ ) : where || cursorValues || scope?.has(tableName, deleted) ? {
3543
3656
  RAW: (aliasedTable2) => withScope(
3544
3657
  scope,
3545
3658
  tableName,
@@ -3552,7 +3665,8 @@ var runRelationalSelect = async (opts) => {
3552
3665
  cursorValues,
3553
3666
  opts.nullOrdering ?? "nulls-smallest"
3554
3667
  ) : void 0
3555
- )
3668
+ ),
3669
+ deleted
3556
3670
  )
3557
3671
  } : void 0,
3558
3672
  with: relationMap[tableName] ? extractRelationsParams(
@@ -3901,10 +4015,11 @@ var assembleAggregateRow = (row, request, target) => {
3901
4015
  }
3902
4016
  return result;
3903
4017
  };
3904
- var generateAggregate = (db, tableName, table, typeName, fieldName, filterArgs, filterCtx, scopes) => {
4018
+ var generateAggregate = (db, tableName, table, typeName, fieldName, filterArgs, filterCtx, policies) => {
3905
4019
  const target = aggregateTarget(table, tableName, typeName);
3906
4020
  const queryArgs = {
3907
- where: { type: filterArgs }
4021
+ where: { type: filterArgs },
4022
+ ...deletedArg(policies?.softDelete, tableName)
3908
4023
  };
3909
4024
  return {
3910
4025
  name: fieldName,
@@ -3916,10 +4031,11 @@ var generateAggregate = (db, tableName, table, typeName, fieldName, filterArgs,
3916
4031
  }
3917
4032
  let query = resolveExecutor(db, context).select(request.selection).from(table);
3918
4033
  const where = withScope(
3919
- resolveScope(scopes, context, filterCtx),
4034
+ resolveScope(policies, context, filterCtx),
3920
4035
  tableName,
3921
4036
  table,
3922
- args.where ? extractFilters(table, tableName, args.where, relationFilterCtx(filterCtx, tableName)) : void 0
4037
+ args.where ? extractFilters(table, tableName, args.where, relationFilterCtx(filterCtx, tableName)) : void 0,
4038
+ args.deleted
3923
4039
  );
3924
4040
  if (where) {
3925
4041
  query = query.where(where);
@@ -3934,7 +4050,7 @@ var generateAggregate = (db, tableName, table, typeName, fieldName, filterArgs,
3934
4050
  };
3935
4051
  };
3936
4052
  var GROUP_KEY = "__dgql_group_key";
3937
- var createRelationAggregateFactory = (db, tables, cacheCtx, typeNameMapper, filterCtx, scopes) => {
4053
+ var createRelationAggregateFactory = (db, tables, cacheCtx, typeNameMapper, filterCtx, policies) => {
3938
4054
  return ({ tableName, relationName, relEntry }) => {
3939
4055
  const parentTable = tables[tableName];
3940
4056
  const targetTableName = relEntry.targetTableName;
@@ -3961,8 +4077,10 @@ var createRelationAggregateFactory = (db, tables, cacheCtx, typeNameMapper, filt
3961
4077
  return assembleAggregateRow({}, request, target);
3962
4078
  }
3963
4079
  const whereArg = args?.where;
4080
+ const deleted = args?.deleted;
3964
4081
  const argsKey = JSON.stringify({
3965
4082
  where: whereArg ?? null,
4083
+ deleted: deleted ?? null,
3966
4084
  selection: Object.keys(request.selection).sort()
3967
4085
  });
3968
4086
  const loaderKey = `${tableName}::${relationName}::aggregate::${argsKey}`;
@@ -3970,13 +4088,14 @@ var createRelationAggregateFactory = (db, tables, cacheCtx, typeNameMapper, filt
3970
4088
  const executor = resolveExecutor(db, context);
3971
4089
  const uniqueIds = [...new Set(parentIds)];
3972
4090
  const whereCondition = withScope(
3973
- resolveScope(scopes, context, filterCtx),
4091
+ resolveScope(policies, context, filterCtx),
3974
4092
  targetTableName,
3975
4093
  targetTable,
3976
4094
  and2(
3977
4095
  inArray2(foreignCol, uniqueIds),
3978
4096
  whereArg ? extractFilters(targetTable, targetTableName, whereArg, relationFilterCtx(filterCtx, targetTableName)) : void 0
3979
- )
4097
+ ),
4098
+ deleted
3980
4099
  );
3981
4100
  const rows = await executor.select({ [GROUP_KEY]: foreignCol, ...request.selection }).from(targetTable).where(whereCondition).groupBy(foreignCol);
3982
4101
  const byKey = new Map(rows.map((row) => [row[GROUP_KEY], row]));
@@ -4109,7 +4228,7 @@ var buildHavingCondition = (having, columns) => {
4109
4228
  }
4110
4229
  return conditions.length ? and2(...conditions) : void 0;
4111
4230
  };
4112
- var generateGroupBy = (db, tableName, table, typeName, fieldName, filterArgs, groupByEnum, havingInput, filterCtx, scopes) => {
4231
+ var generateGroupBy = (db, tableName, table, typeName, fieldName, filterArgs, groupByEnum, havingInput, filterCtx, policies) => {
4113
4232
  const target = aggregateTarget(table, tableName, typeName);
4114
4233
  const groupable = groupableColumns(table, tableName);
4115
4234
  const columns = getColumns3(table);
@@ -4120,7 +4239,8 @@ var generateGroupBy = (db, tableName, table, typeName, fieldName, filterArgs, gr
4120
4239
  description: "Columns to group by. One result row per distinct combination of their values."
4121
4240
  },
4122
4241
  where: { type: filterArgs, description: "Filters the rows before they are grouped." },
4123
- having: { type: havingInput, description: "Filters the groups after they are aggregated." }
4242
+ having: { type: havingInput, description: "Filters the groups after they are aggregated." },
4243
+ ...deletedArg(policies?.softDelete, tableName)
4124
4244
  };
4125
4245
  return {
4126
4246
  name: fieldName,
@@ -4144,10 +4264,11 @@ var generateGroupBy = (db, tableName, table, typeName, fieldName, filterArgs, gr
4144
4264
  };
4145
4265
  let query = resolveExecutor(db, context).select(selection).from(table);
4146
4266
  const where = withScope(
4147
- resolveScope(scopes, context, filterCtx),
4267
+ resolveScope(policies, context, filterCtx),
4148
4268
  tableName,
4149
4269
  table,
4150
- args.where ? extractFilters(table, tableName, args.where, relationFilterCtx(filterCtx, tableName)) : void 0
4270
+ args.where ? extractFilters(table, tableName, args.where, relationFilterCtx(filterCtx, tableName)) : void 0,
4271
+ args.deleted
4151
4272
  );
4152
4273
  if (where) {
4153
4274
  query = query.where(where);
@@ -4189,7 +4310,9 @@ var generateSelectArray = (db, tableName, tables, relationMap, orderArgs, filter
4189
4310
  const queryArgs = selectArrayArgs(
4190
4311
  orderArgs,
4191
4312
  filterArgs,
4192
- distinctEnabled ? generateDistinctEnum(table, typeName) : void 0
4313
+ distinctEnabled ? generateDistinctEnum(table, typeName) : void 0,
4314
+ policies?.softDelete,
4315
+ tableName
4193
4316
  );
4194
4317
  return {
4195
4318
  name: fieldName,
@@ -4231,7 +4354,7 @@ var generateSelectSingle = (db, tableName, tables, relationMap, orderArgs, filte
4231
4354
  `Drizzle-GraphQL Error: Table ${tableName} not found in drizzle instance. Did you forget to pass schema to drizzle constructor?`
4232
4355
  );
4233
4356
  }
4234
- const queryArgs = selectSingleArgs(orderArgs, filterArgs);
4357
+ const queryArgs = selectSingleArgs(orderArgs, filterArgs, policies?.softDelete, tableName);
4235
4358
  const table = tables[tableName];
4236
4359
  const pkNames = mysqlPrimaryKeyPropNames(table);
4237
4360
  return {
@@ -4270,22 +4393,49 @@ var generateInsertArray = (db, tableName, table, baseType, fieldName, txCtx, pol
4270
4393
  type: new GraphQLNonNull5(new GraphQLList5(new GraphQLNonNull5(baseType)))
4271
4394
  }
4272
4395
  };
4396
+ const hooks = policies?.onWrite?.(tableName, "insert");
4273
4397
  return {
4274
4398
  name: fieldName,
4275
4399
  resolver: async (_source, args, context, info) => {
4276
4400
  try {
4277
- return await runMutation(db, context, info, txCtx, async (executor) => {
4278
- const input = applyContextValuesAll(
4279
- remapFromGraphQLArrayInput(args.values, table),
4280
- policies?.contextValues?.(tableName),
4281
- context
4282
- );
4283
- if (!input.length) {
4284
- throw new GraphQLError6("No values were provided!");
4285
- }
4286
- await executor.insert(table).values(input);
4287
- return { isSuccess: true };
4288
- });
4401
+ return await runMutation(
4402
+ db,
4403
+ context,
4404
+ info,
4405
+ txCtx,
4406
+ async (executor) => {
4407
+ const input = applyContextValuesAll(
4408
+ remapFromGraphQLArrayInput(args.values, table),
4409
+ policies?.contextValues?.(tableName),
4410
+ context
4411
+ );
4412
+ if (!input.length) {
4413
+ throw new GraphQLError6("No values were provided!");
4414
+ }
4415
+ await runWriteHook(hooks, "before", {
4416
+ table: tableName,
4417
+ operation: "insert",
4418
+ single: false,
4419
+ args,
4420
+ context,
4421
+ info,
4422
+ tx: executor
4423
+ });
4424
+ await executor.insert(table).values(input);
4425
+ await runWriteHook(hooks, "after", {
4426
+ table: tableName,
4427
+ operation: "insert",
4428
+ single: false,
4429
+ args,
4430
+ rows: [],
4431
+ context,
4432
+ info,
4433
+ tx: executor
4434
+ });
4435
+ return { isSuccess: true };
4436
+ },
4437
+ !!hooks
4438
+ );
4289
4439
  } catch (e) {
4290
4440
  throw toGraphQLError(e);
4291
4441
  }
@@ -4299,19 +4449,46 @@ var generateInsertSingle = (db, tableName, table, baseType, fieldName, txCtx, po
4299
4449
  type: new GraphQLNonNull5(baseType)
4300
4450
  }
4301
4451
  };
4452
+ const hooks = policies?.onWrite?.(tableName, "insert");
4302
4453
  return {
4303
4454
  name: fieldName,
4304
4455
  resolver: async (_source, args, context, info) => {
4305
4456
  try {
4306
- return await runMutation(db, context, info, txCtx, async (executor) => {
4307
- const input = applyContextValues(
4308
- remapFromGraphQLSingleInput(args.values, table),
4309
- policies?.contextValues?.(tableName),
4310
- context
4311
- );
4312
- await executor.insert(table).values(input);
4313
- return { isSuccess: true };
4314
- });
4457
+ return await runMutation(
4458
+ db,
4459
+ context,
4460
+ info,
4461
+ txCtx,
4462
+ async (executor) => {
4463
+ await runWriteHook(hooks, "before", {
4464
+ table: tableName,
4465
+ operation: "insert",
4466
+ single: true,
4467
+ args,
4468
+ context,
4469
+ info,
4470
+ tx: executor
4471
+ });
4472
+ const input = applyContextValues(
4473
+ remapFromGraphQLSingleInput(args.values, table),
4474
+ policies?.contextValues?.(tableName),
4475
+ context
4476
+ );
4477
+ await executor.insert(table).values(input);
4478
+ await runWriteHook(hooks, "after", {
4479
+ table: tableName,
4480
+ operation: "insert",
4481
+ single: true,
4482
+ args,
4483
+ rows: [],
4484
+ context,
4485
+ info,
4486
+ tx: executor
4487
+ });
4488
+ return { isSuccess: true };
4489
+ },
4490
+ !!hooks
4491
+ );
4315
4492
  } catch (e) {
4316
4493
  throw toGraphQLError(e);
4317
4494
  }
@@ -4330,35 +4507,62 @@ var generateUpsert = (db, tableName, table, baseType, onConflictType, fieldName,
4330
4507
  }
4331
4508
  };
4332
4509
  const pkNames = mysqlPrimaryKeyPropNames(table);
4510
+ const hooks = policies?.onWrite?.(tableName, "upsert");
4333
4511
  return {
4334
4512
  name: fieldName,
4335
4513
  resolver: async (_source, args, context, info) => {
4336
4514
  try {
4337
- return await runMutation(db, context, info, txCtx, async (executor) => {
4338
- const input = applyContextValuesAll(
4339
- single ? [remapFromGraphQLSingleInput(args.values, table)] : remapFromGraphQLArrayInput(args.values, table),
4340
- policies?.contextValues?.(tableName),
4341
- context
4342
- );
4343
- if (!input.length) {
4344
- throw new GraphQLError6("No values were provided!");
4345
- }
4346
- const plan = resolveConflictPlan({
4347
- table,
4348
- values: input,
4349
- onConflict: args.onConflict,
4350
- pkNames,
4351
- uniqueSets: [],
4352
- excludedRef: mysqlValuesColumnRef,
4353
- withTarget: false
4354
- });
4355
- if (plan.action === "NOTHING") {
4356
- await executor.insert(table).ignore().values(input);
4357
- } else {
4358
- await executor.insert(table).values(input).onDuplicateKeyUpdate({ set: plan.set });
4359
- }
4360
- return { isSuccess: true };
4361
- });
4515
+ return await runMutation(
4516
+ db,
4517
+ context,
4518
+ info,
4519
+ txCtx,
4520
+ async (executor) => {
4521
+ const input = applyContextValuesAll(
4522
+ single ? [remapFromGraphQLSingleInput(args.values, table)] : remapFromGraphQLArrayInput(args.values, table),
4523
+ policies?.contextValues?.(tableName),
4524
+ context
4525
+ );
4526
+ if (!input.length) {
4527
+ throw new GraphQLError6("No values were provided!");
4528
+ }
4529
+ await runWriteHook(hooks, "before", {
4530
+ table: tableName,
4531
+ operation: "upsert",
4532
+ single,
4533
+ args,
4534
+ context,
4535
+ info,
4536
+ tx: executor
4537
+ });
4538
+ const plan = resolveConflictPlan({
4539
+ table,
4540
+ values: input,
4541
+ onConflict: args.onConflict,
4542
+ pkNames,
4543
+ uniqueSets: [],
4544
+ excludedRef: mysqlValuesColumnRef,
4545
+ withTarget: false
4546
+ });
4547
+ if (plan.action === "NOTHING") {
4548
+ await executor.insert(table).ignore().values(input);
4549
+ } else {
4550
+ await executor.insert(table).values(input).onDuplicateKeyUpdate({ set: plan.set });
4551
+ }
4552
+ await runWriteHook(hooks, "after", {
4553
+ table: tableName,
4554
+ operation: "upsert",
4555
+ single,
4556
+ args,
4557
+ rows: [],
4558
+ context,
4559
+ info,
4560
+ tx: executor
4561
+ });
4562
+ return { isSuccess: true };
4563
+ },
4564
+ !!hooks
4565
+ );
4362
4566
  } catch (e) {
4363
4567
  throw toGraphQLError(e);
4364
4568
  }
@@ -4375,37 +4579,64 @@ var generateUpdate = (db, tableName, table, setArgs, filterArgs, fieldName, sing
4375
4579
  type: single || requireWhere ? new GraphQLNonNull5(filterArgs) : filterArgs
4376
4580
  }
4377
4581
  };
4582
+ const hooks = policies?.onWrite?.(tableName, "update");
4378
4583
  return {
4379
4584
  name: fieldName,
4380
4585
  resolver: async (_source, args, context, info) => {
4381
4586
  try {
4382
- return await runMutation(db, context, info, txCtx, async (executor) => {
4383
- const { where, set } = args;
4384
- const scope = policies?.scope?.(context);
4385
- const input = stripContextValues(
4386
- remapUpdateInput(set, table, tableName),
4387
- policies?.contextValues?.(tableName)
4388
- );
4389
- if (!Object.keys(input).length) {
4390
- throw new GraphQLError6("Unable to update with no values specified!");
4391
- }
4392
- const relationCtx = relationFilterCtx(filterCtx, tableName);
4393
- const filters = withScope(
4394
- scope,
4395
- tableName,
4396
- table,
4397
- single || requireWhere ? extractRequiredFilters(table, tableName, where, fieldName, relationCtx) : where ? extractFilters(table, tableName, where, relationCtx) : void 0
4398
- );
4399
- if (single) {
4400
- await assertSingleMatch(executor, table, filters, fieldName);
4401
- }
4402
- let query = executor.update(table).set(input);
4403
- if (filters) {
4404
- query = query.where(filters);
4405
- }
4406
- await query;
4407
- return { isSuccess: true };
4408
- });
4587
+ return await runMutation(
4588
+ db,
4589
+ context,
4590
+ info,
4591
+ txCtx,
4592
+ async (executor) => {
4593
+ const { where, set } = args;
4594
+ const scope = policies?.scope?.(context);
4595
+ const input = stripContextValues(
4596
+ remapUpdateInput(set, table, tableName),
4597
+ policies?.contextValues?.(tableName)
4598
+ );
4599
+ if (!Object.keys(input).length) {
4600
+ throw new GraphQLError6("Unable to update with no values specified!");
4601
+ }
4602
+ await runWriteHook(hooks, "before", {
4603
+ table: tableName,
4604
+ operation: "update",
4605
+ single,
4606
+ args,
4607
+ context,
4608
+ info,
4609
+ tx: executor
4610
+ });
4611
+ const relationCtx = relationFilterCtx(filterCtx, tableName);
4612
+ const filters = withScope(
4613
+ scope,
4614
+ tableName,
4615
+ table,
4616
+ single || requireWhere ? extractRequiredFilters(table, tableName, where, fieldName, relationCtx) : where ? extractFilters(table, tableName, where, relationCtx) : void 0
4617
+ );
4618
+ if (single) {
4619
+ await assertSingleMatch(executor, table, filters, fieldName);
4620
+ }
4621
+ let query = executor.update(table).set(input);
4622
+ if (filters) {
4623
+ query = query.where(filters);
4624
+ }
4625
+ await query;
4626
+ await runWriteHook(hooks, "after", {
4627
+ table: tableName,
4628
+ operation: "update",
4629
+ single,
4630
+ args,
4631
+ rows: [],
4632
+ context,
4633
+ info,
4634
+ tx: executor
4635
+ });
4636
+ return { isSuccess: true };
4637
+ },
4638
+ !!hooks
4639
+ );
4409
4640
  } catch (e) {
4410
4641
  throw toGraphQLError(e);
4411
4642
  }
@@ -4419,43 +4650,70 @@ var generateUpdateMany = (db, tableName, table, updateManyInput, fieldName, filt
4419
4650
  type: new GraphQLNonNull5(new GraphQLList5(new GraphQLNonNull5(updateManyInput)))
4420
4651
  }
4421
4652
  };
4653
+ const hooks = policies?.onWrite?.(tableName, "updateMany");
4422
4654
  return {
4423
4655
  name: fieldName,
4424
4656
  resolver: async (_source, args, context, info) => {
4425
4657
  try {
4426
- return await runMutation(db, context, info, txCtx, async (executor) => {
4427
- const { updates } = args;
4428
- if (!updates.length) {
4429
- throw new GraphQLError6("No updates were provided!");
4430
- }
4431
- const scope = policies?.scope?.(context);
4432
- const contextColumns = policies?.contextValues?.(tableName);
4433
- const entries = updates.map(({ where, set }) => {
4434
- const input = stripContextValues(remapUpdateInput(set, table, tableName), contextColumns);
4435
- if (!Object.keys(input).length) {
4436
- throw new GraphQLError6("Unable to update with no values specified!");
4658
+ return await runMutation(
4659
+ db,
4660
+ context,
4661
+ info,
4662
+ txCtx,
4663
+ async (executor) => {
4664
+ const { updates } = args;
4665
+ if (!updates.length) {
4666
+ throw new GraphQLError6("No updates were provided!");
4437
4667
  }
4438
- return {
4439
- set: input,
4440
- filters: withScope(
4441
- scope,
4442
- tableName,
4443
- table,
4444
- where ? extractFilters(table, tableName, where, relationFilterCtx(filterCtx, tableName)) : void 0
4445
- )
4446
- };
4447
- });
4448
- await executor.transaction(async (tx) => {
4449
- for (const entry of entries) {
4450
- let query = tx.update(table).set(entry.set);
4451
- if (entry.filters) {
4452
- query = query.where(entry.filters);
4668
+ await runWriteHook(hooks, "before", {
4669
+ table: tableName,
4670
+ operation: "updateMany",
4671
+ single: false,
4672
+ args,
4673
+ context,
4674
+ info,
4675
+ tx: executor
4676
+ });
4677
+ const scope = policies?.scope?.(context);
4678
+ const contextColumns = policies?.contextValues?.(tableName);
4679
+ const entries = updates.map(({ where, set }) => {
4680
+ const input = stripContextValues(remapUpdateInput(set, table, tableName), contextColumns);
4681
+ if (!Object.keys(input).length) {
4682
+ throw new GraphQLError6("Unable to update with no values specified!");
4453
4683
  }
4454
- await query;
4455
- }
4456
- });
4457
- return { isSuccess: true };
4458
- });
4684
+ return {
4685
+ set: input,
4686
+ filters: withScope(
4687
+ scope,
4688
+ tableName,
4689
+ table,
4690
+ where ? extractFilters(table, tableName, where, relationFilterCtx(filterCtx, tableName)) : void 0
4691
+ )
4692
+ };
4693
+ });
4694
+ await executor.transaction(async (tx) => {
4695
+ for (const entry of entries) {
4696
+ let query = tx.update(table).set(entry.set);
4697
+ if (entry.filters) {
4698
+ query = query.where(entry.filters);
4699
+ }
4700
+ await query;
4701
+ }
4702
+ });
4703
+ await runWriteHook(hooks, "after", {
4704
+ table: tableName,
4705
+ operation: "updateMany",
4706
+ single: false,
4707
+ args,
4708
+ rows: [],
4709
+ context,
4710
+ info,
4711
+ tx: executor
4712
+ });
4713
+ return { isSuccess: true };
4714
+ },
4715
+ !!hooks
4716
+ );
4459
4717
  } catch (e) {
4460
4718
  throw toGraphQLError(e);
4461
4719
  }
@@ -4463,36 +4721,66 @@ var generateUpdateMany = (db, tableName, table, updateManyInput, fieldName, filt
4463
4721
  args: queryArgs
4464
4722
  };
4465
4723
  };
4466
- var generateDelete = (db, tableName, table, filterArgs, fieldName, single, requireWhere, filterCtx, txCtx, policies) => {
4724
+ var generateDelete = (db, tableName, table, filterArgs, fieldName, single, requireWhere, filterCtx, txCtx, policies, restore = false) => {
4725
+ const softDelete = policies?.softDelete?.(tableName);
4726
+ const operation = restore ? "restore" : "delete";
4467
4727
  const queryArgs = {
4468
4728
  where: {
4469
4729
  type: single || requireWhere ? new GraphQLNonNull5(filterArgs) : filterArgs
4470
4730
  }
4471
4731
  };
4732
+ const hooks = policies?.onWrite?.(tableName, operation);
4472
4733
  return {
4473
4734
  name: fieldName,
4474
4735
  resolver: async (_source, args, context, info) => {
4475
4736
  try {
4476
- return await runMutation(db, context, info, txCtx, async (executor) => {
4477
- const { where } = args;
4478
- const scope = policies?.scope?.(context);
4479
- const relationCtx = relationFilterCtx(filterCtx, tableName);
4480
- const filters = withScope(
4481
- scope,
4482
- tableName,
4483
- table,
4484
- single || requireWhere ? extractRequiredFilters(table, tableName, where, fieldName, relationCtx) : where ? extractFilters(table, tableName, where, relationCtx) : void 0
4485
- );
4486
- if (single) {
4487
- await assertSingleMatch(executor, table, filters, fieldName);
4488
- }
4489
- let query = executor.delete(table);
4490
- if (filters) {
4491
- query = query.where(filters);
4492
- }
4493
- await query;
4494
- return { isSuccess: true };
4495
- });
4737
+ return await runMutation(
4738
+ db,
4739
+ context,
4740
+ info,
4741
+ txCtx,
4742
+ async (executor) => {
4743
+ await runWriteHook(hooks, "before", {
4744
+ table: tableName,
4745
+ operation,
4746
+ single,
4747
+ args,
4748
+ context,
4749
+ info,
4750
+ tx: executor
4751
+ });
4752
+ const { where } = args;
4753
+ const scope = policies?.scope?.(context);
4754
+ const relationCtx = relationFilterCtx(filterCtx, tableName);
4755
+ const filters = withScope(
4756
+ scope,
4757
+ tableName,
4758
+ table,
4759
+ single || requireWhere ? extractRequiredFilters(table, tableName, where, fieldName, relationCtx) : where ? extractFilters(table, tableName, where, relationCtx) : void 0,
4760
+ restore ? "ONLY" : void 0
4761
+ );
4762
+ if (single) {
4763
+ await assertSingleMatch(executor, table, filters, fieldName);
4764
+ }
4765
+ let query = softDelete ? executor.update(table).set({ [softDelete.columnName]: restore ? softDelete.writeRestored : softDelete.writeDeleted() }) : executor.delete(table);
4766
+ if (filters) {
4767
+ query = query.where(filters);
4768
+ }
4769
+ await query;
4770
+ await runWriteHook(hooks, "after", {
4771
+ table: tableName,
4772
+ operation,
4773
+ single,
4774
+ args,
4775
+ rows: [],
4776
+ context,
4777
+ info,
4778
+ tx: executor
4779
+ });
4780
+ return { isSuccess: true };
4781
+ },
4782
+ !!hooks
4783
+ );
4496
4784
  } catch (e) {
4497
4785
  throw toGraphQLError(e);
4498
4786
  }
@@ -4530,16 +4818,17 @@ var generateSchemaData = (db, schema, relations, options) => {
4530
4818
  attachTargetPrimaryKeys(namedRelations, tables, mysqlPrimaryKeyPropNames);
4531
4819
  const eagerRelations = pruneNonEagerRelations(namedRelations, shouldEagerLoad);
4532
4820
  const filterCtx = { tables, relationMap: namedRelations };
4533
- const scopes = options.policies?.scope;
4534
- const contextValuesOf = options.policies?.contextValues;
4535
- const policies = bindPolicies(options.policies, filterCtx);
4821
+ const tablePolicies = options.policies;
4822
+ const contextValuesOf = tablePolicies?.contextValues;
4823
+ const softDeleteOf = tablePolicies?.softDelete;
4824
+ const policies = bindPolicies(tablePolicies, filterCtx);
4536
4825
  const resolverFactory = createRelationResolverFactory(
4537
4826
  db,
4538
4827
  tables,
4539
4828
  "nulls-smallest",
4540
4829
  filterCtx,
4541
4830
  limits,
4542
- scopes
4831
+ tablePolicies
4543
4832
  );
4544
4833
  const cacheCtx = {
4545
4834
  genericFilterCache: /* @__PURE__ */ new Map(),
@@ -4558,9 +4847,10 @@ var generateSchemaData = (db, schema, relations, options) => {
4558
4847
  docs: options.docs ?? {},
4559
4848
  primaryKeyOf: (name) => tables[name] ? mysqlPrimaryKeyPropNames(tables[name]) : [],
4560
4849
  contextValuesOf,
4850
+ softDeleteOf,
4561
4851
  featureOf
4562
4852
  };
4563
- const relationAggregateFactory = anyTable("relationAggregates") ? createRelationAggregateFactory(db, tables, cacheCtx, typeNameMapper, filterCtx, scopes) : void 0;
4853
+ const relationAggregateFactory = anyTable("relationAggregates") ? createRelationAggregateFactory(db, tables, cacheCtx, typeNameMapper, filterCtx, tablePolicies) : void 0;
4564
4854
  const queries = {};
4565
4855
  const mutations = {};
4566
4856
  const mutationTxCtx = createMutationTxCtx(options.transactions);
@@ -4616,8 +4906,11 @@ var generateSchemaData = (db, schema, relations, options) => {
4616
4906
  updateCountFieldName,
4617
4907
  deleteFieldName,
4618
4908
  deleteSingleFieldName,
4909
+ restoreFieldName,
4910
+ restoreSingleFieldName,
4619
4911
  deleteCountFieldName
4620
4912
  } = computeResolverFieldNames(tableName, typeNameMapper, prefixes, suffixes);
4913
+ const softDeleteInfo = softDeleteOf?.(tableName);
4621
4914
  const selectArrGenerated = generateSelectArray(
4622
4915
  db,
4623
4916
  tableName,
@@ -4755,6 +5048,32 @@ var generateSchemaData = (db, schema, relations, options) => {
4755
5048
  mutationTxCtx,
4756
5049
  policies
4757
5050
  ) : void 0;
5051
+ const restoreGenerated = softDeleteInfo ? generateDelete(
5052
+ db,
5053
+ tableName,
5054
+ schema[tableName],
5055
+ tableFilters,
5056
+ restoreFieldName,
5057
+ false,
5058
+ tableFeatures.requireWhere,
5059
+ filterCtx,
5060
+ mutationTxCtx,
5061
+ policies,
5062
+ true
5063
+ ) : void 0;
5064
+ const restoreSingleGenerated = softDeleteInfo ? generateDelete(
5065
+ db,
5066
+ tableName,
5067
+ schema[tableName],
5068
+ tableFilters,
5069
+ restoreSingleFieldName,
5070
+ true,
5071
+ tableFeatures.requireWhere,
5072
+ filterCtx,
5073
+ mutationTxCtx,
5074
+ policies,
5075
+ true
5076
+ ) : void 0;
4758
5077
  const updateCountGenerated = tableFeatures.update && tableFeatures.countMutations ? generateWriteCount({
4759
5078
  db,
4760
5079
  tableName,
@@ -4787,7 +5106,7 @@ var generateSchemaData = (db, schema, relations, options) => {
4787
5106
  aggregateFieldName,
4788
5107
  tableFilters,
4789
5108
  filterCtx,
4790
- scopes
5109
+ tablePolicies
4791
5110
  ) : void 0;
4792
5111
  const groupByType = tableFeatures.aggregates && tableFeatures.groupBy ? generateGroupByType(schema[tableName], tableName, typeName, cacheCtx) : void 0;
4793
5112
  const groupByEnum = groupByType ? generateGroupByEnum(schema[tableName], tableName, typeName) : void 0;
@@ -4802,7 +5121,7 @@ var generateSchemaData = (db, schema, relations, options) => {
4802
5121
  groupByEnum,
4803
5122
  havingInput,
4804
5123
  filterCtx,
4805
- scopes
5124
+ tablePolicies
4806
5125
  ) : void 0;
4807
5126
  queries[selectArrGenerated.name] = {
4808
5127
  type: selectArrOutput,
@@ -4852,7 +5171,9 @@ var generateSchemaData = (db, schema, relations, options) => {
4852
5171
  [updateManyGenerated, { operation: "updateMany", single: false, targetArg: "updates" }],
4853
5172
  [updateSingleGenerated, { operation: "update", single: true, targetArg: "where" }],
4854
5173
  [deleteGenerated, { operation: "delete", single: false, targetArg: "where" }],
4855
- [deleteSingleGenerated, { operation: "delete", single: true, targetArg: "where" }]
5174
+ [deleteSingleGenerated, { operation: "delete", single: true, targetArg: "where" }],
5175
+ [restoreGenerated, { operation: "restore", single: false, targetArg: "where" }],
5176
+ [restoreSingleGenerated, { operation: "restore", single: true, targetArg: "where" }]
4856
5177
  ];
4857
5178
  for (const [generated, meta] of generatedMutations) {
4858
5179
  if (generated) {
@@ -4935,7 +5256,7 @@ import {
4935
5256
  import { parseResolveInfo as parseResolveInfo3 } from "graphql-parse-resolve-info";
4936
5257
 
4937
5258
  // src/util/builders/nested-writes.ts
4938
- import { and as and3, eq as eq3, getColumns as getColumns4, is as is5, One as One4 } from "drizzle-orm";
5259
+ import { and as and3, Column, eq as eq3, getColumns as getColumns4, inArray as inArray3, is as is5, One as One4 } from "drizzle-orm";
4939
5260
  import {
4940
5261
  GraphQLBoolean as GraphQLBoolean4,
4941
5262
  GraphQLError as GraphQLError7,
@@ -4944,6 +5265,64 @@ import {
4944
5265
  GraphQLNonNull as GraphQLNonNull6
4945
5266
  } from "graphql";
4946
5267
  var isOptionalOnInsert = (column) => !column.notNull || !!column.hasDefault || !!column.defaultFn;
5268
+ var propNameOf = (table, column) => {
5269
+ if (!column) {
5270
+ return void 0;
5271
+ }
5272
+ for (const [propName, candidate] of Object.entries(getColumns4(table))) {
5273
+ if (candidate === column) {
5274
+ return propName;
5275
+ }
5276
+ }
5277
+ return void 0;
5278
+ };
5279
+ var throughColumn = (entry) => entry && is5(entry, Column) ? entry : entry?._?.column;
5280
+ var buildThroughPlan = (relationName, relation, table, targetTableName, targetTable) => {
5281
+ if (is5(relation, One4)) {
5282
+ return void 0;
5283
+ }
5284
+ const junction = relation.throughTable;
5285
+ const sourceEntries = relation.through?.source;
5286
+ const targetEntries = relation.through?.target;
5287
+ if (!junction || sourceEntries?.length !== 1 || targetEntries?.length !== 1) {
5288
+ return void 0;
5289
+ }
5290
+ if (relation.sourceColumns?.length !== 1 || relation.targetColumns?.length !== 1) {
5291
+ return void 0;
5292
+ }
5293
+ const localCol = relation.sourceColumns[0];
5294
+ const foreignCol = relation.targetColumns[0];
5295
+ const localColPropName = propNameOf(table, localCol);
5296
+ const foreignColPropName = propNameOf(targetTable, foreignCol);
5297
+ const sourceColPropName = propNameOf(junction, throughColumn(sourceEntries[0]));
5298
+ const targetColPropName = propNameOf(junction, throughColumn(targetEntries[0]));
5299
+ if (!localColPropName || !foreignColPropName || !sourceColPropName || !targetColPropName) {
5300
+ return void 0;
5301
+ }
5302
+ const junctionColumns = getColumns4(junction);
5303
+ const sourceCol = junctionColumns[sourceColPropName];
5304
+ const targetCol = junctionColumns[targetColPropName];
5305
+ const unsuppliable = Object.entries(junctionColumns).some(
5306
+ ([propName, column]) => propName !== sourceColPropName && propName !== targetColPropName && !isOptionalOnInsert(column)
5307
+ );
5308
+ if (unsuppliable) {
5309
+ return void 0;
5310
+ }
5311
+ return {
5312
+ relationName,
5313
+ targetTableName,
5314
+ targetTable,
5315
+ localColPropName,
5316
+ foreignColPropName,
5317
+ foreignCol,
5318
+ localCol,
5319
+ isOne: false,
5320
+ // The link is written after the parent row exists, exactly like a child-side foreign key.
5321
+ fkSide: "child",
5322
+ canDetach: true,
5323
+ through: { table: junction, sourceColPropName, targetColPropName, sourceCol, targetCol }
5324
+ };
5325
+ };
4947
5326
  var buildNestedWritePlans = (tables, relationMap, resolveUniqueSets, resolvePrimaryKeys, extractJoinColumns) => {
4948
5327
  const uniqueCache = /* @__PURE__ */ new Map();
4949
5328
  const singleColumnUniques = (tableName, table) => {
@@ -4973,14 +5352,18 @@ var buildNestedWritePlans = (tables, relationMap, resolveUniqueSets, resolvePrim
4973
5352
  const tablePlans = {};
4974
5353
  for (const [relationName, relEntry] of Object.entries(relations)) {
4975
5354
  const relation = relEntry.relation;
4976
- if (relation.through) {
4977
- continue;
4978
- }
4979
5355
  const targetTableName = relEntry.targetTableName;
4980
5356
  const targetTable = tables[targetTableName];
4981
5357
  if (!targetTable) {
4982
5358
  continue;
4983
5359
  }
5360
+ if (relation.through) {
5361
+ const throughPlan = buildThroughPlan(relationName, relation, table, targetTableName, targetTable);
5362
+ if (throughPlan) {
5363
+ tablePlans[relationName] = throughPlan;
5364
+ }
5365
+ continue;
5366
+ }
4984
5367
  const joinColumns = extractJoinColumns(relEntry, table, targetTable);
4985
5368
  if (!joinColumns) {
4986
5369
  continue;
@@ -5065,11 +5448,31 @@ var createNestedWriteTypes = (params) => {
5065
5448
  return cached;
5066
5449
  }
5067
5450
  const filters = targetFilters(plan);
5068
- const payload = payloadType(tableName, typeName, plan);
5069
5451
  const targetTypeName = resolveTypeName(plan.targetTableName, typeNameMapper);
5070
5452
  const fields = {};
5071
- if (plan.isOne) {
5072
- fields["create"] = { type: payload, description: `Insert a ${targetTypeName} row and attach it` };
5453
+ if (plan.through) {
5454
+ if (!filters) {
5455
+ return void 0;
5456
+ }
5457
+ fields["connect"] = {
5458
+ type: listOf(filters),
5459
+ description: `Link every existing ${targetTypeName} row these match. Already-linked rows are left alone.`
5460
+ };
5461
+ if (forUpdate) {
5462
+ fields["disconnect"] = {
5463
+ type: listOf(filters),
5464
+ description: `Unlink every linked ${targetTypeName} row these match, leaving the rows in place`
5465
+ };
5466
+ fields["set"] = {
5467
+ type: listOf(filters),
5468
+ description: `Replace the whole set: unlink everything linked, then link every ${targetTypeName} row these match. \`set: []\` clears the relation. Applied before \`disconnect\` and \`connect\`.`
5469
+ };
5470
+ }
5471
+ } else if (plan.isOne) {
5472
+ fields["create"] = {
5473
+ type: payloadType(tableName, typeName, plan),
5474
+ description: `Insert a ${targetTypeName} row and attach it`
5475
+ };
5073
5476
  if (filters) {
5074
5477
  fields["connect"] = {
5075
5478
  type: filters,
@@ -5083,7 +5486,10 @@ var createNestedWriteTypes = (params) => {
5083
5486
  };
5084
5487
  }
5085
5488
  } else {
5086
- fields["create"] = { type: listOf(payload), description: `Insert these ${targetTypeName} rows and attach them` };
5489
+ fields["create"] = {
5490
+ type: listOf(payloadType(tableName, typeName, plan)),
5491
+ description: `Insert these ${targetTypeName} rows and attach them`
5492
+ };
5087
5493
  if (filters) {
5088
5494
  fields["connect"] = {
5089
5495
  type: listOf(filters),
@@ -5142,7 +5548,7 @@ var createNestedWriteTypes = (params) => {
5142
5548
  var mergedOps = (entries) => Object.assign({}, ...entries.map((entry) => entry.ops));
5143
5549
  var asArray = (value) => Array.isArray(value) ? value : value === void 0 ? [] : [value];
5144
5550
  var createNestedWriteRuntime = (params) => {
5145
- const { plans, filterCtx, scopes, contextValues } = params;
5551
+ const { plans, filterCtx, policies, contextValues } = params;
5146
5552
  const conditionOf = (plan, filter, operation, scope) => {
5147
5553
  const condition = extractFilters(
5148
5554
  plan.targetTable,
@@ -5165,6 +5571,48 @@ var createNestedWriteRuntime = (params) => {
5165
5571
  );
5166
5572
  }
5167
5573
  };
5574
+ const selectedTargetKeys = async (executor, plan, filter, operation, scope) => {
5575
+ const rows = await executor.select().from(plan.targetTable).where(conditionOf(plan, filter, operation, scope));
5576
+ return rows.map((row) => row[plan.foreignColPropName]).filter((value) => value !== void 0 && value !== null);
5577
+ };
5578
+ const applyThroughOps = async (executor, plan, key, op, scope) => {
5579
+ const through = plan.through;
5580
+ const unlinkAll = () => executor.delete(through.table).where(eq3(through.sourceCol, key));
5581
+ const link = async (operands, operation) => {
5582
+ const existing = new Set(
5583
+ (await executor.select().from(through.table).where(eq3(through.sourceCol, key))).map(
5584
+ (row) => row[through.targetColPropName]
5585
+ )
5586
+ );
5587
+ const rows = [];
5588
+ for (const filter of operands) {
5589
+ for (const targetKey of await selectedTargetKeys(executor, plan, filter, operation, scope)) {
5590
+ if (!existing.has(targetKey)) {
5591
+ existing.add(targetKey);
5592
+ rows.push({ [through.sourceColPropName]: key, [through.targetColPropName]: targetKey });
5593
+ }
5594
+ }
5595
+ }
5596
+ if (rows.length) {
5597
+ await executor.insert(through.table).values(rows);
5598
+ }
5599
+ };
5600
+ if (op["set"] !== void 0 && op["set"] !== null) {
5601
+ await unlinkAll();
5602
+ await link(asArray(op["set"]), "set");
5603
+ }
5604
+ if (op["disconnect"] !== void 0 && op["disconnect"] !== null) {
5605
+ for (const filter of asArray(op["disconnect"])) {
5606
+ const targetKeys = await selectedTargetKeys(executor, plan, filter, "disconnect", scope);
5607
+ if (targetKeys.length) {
5608
+ await executor.delete(through.table).where(and3(eq3(through.sourceCol, key), inArray3(through.targetCol, targetKeys)));
5609
+ }
5610
+ }
5611
+ }
5612
+ if (op["connect"] !== void 0 && op["connect"] !== null) {
5613
+ await link(asArray(op["connect"]), "connect");
5614
+ }
5615
+ };
5168
5616
  const detachAll = async (executor, plan, key, scope) => executor.update(plan.targetTable).set({ [plan.foreignColPropName]: null }).where(withScope(scope, plan.targetTableName, plan.targetTable, eq3(plan.foreignCol, key)));
5169
5617
  return {
5170
5618
  enabled: (tableName) => !!plans[tableName],
@@ -5205,7 +5653,7 @@ var createNestedWriteRuntime = (params) => {
5205
5653
  if (!tablePlans) {
5206
5654
  return patch;
5207
5655
  }
5208
- const scope = resolveScope(scopes, context, filterCtx);
5656
+ const scope = resolveScope(policies, context, filterCtx);
5209
5657
  for (const [relationName, op] of Object.entries(ops)) {
5210
5658
  const plan = tablePlans[relationName];
5211
5659
  if (!plan || plan.fkSide !== "parent") {
@@ -5245,7 +5693,7 @@ var createNestedWriteRuntime = (params) => {
5245
5693
  if (!tablePlans) {
5246
5694
  return;
5247
5695
  }
5248
- const scope = resolveScope(scopes, context, filterCtx);
5696
+ const scope = resolveScope(policies, context, filterCtx);
5249
5697
  for (const [relationName, op] of Object.entries(ops)) {
5250
5698
  const plan = tablePlans[relationName];
5251
5699
  if (!plan || plan.fkSide !== "child") {
@@ -5261,6 +5709,10 @@ var createNestedWriteRuntime = (params) => {
5261
5709
  `Drizzle-GraphQL Error: cannot write through '${relationName}': the ${tableName} row has no '${plan.localColPropName}' value to attach to.`
5262
5710
  );
5263
5711
  }
5712
+ if (plan.through) {
5713
+ await applyThroughOps(executor, plan, key, op, scope);
5714
+ continue;
5715
+ }
5264
5716
  if (op["set"] !== void 0) {
5265
5717
  await detachAll(executor, plan, key, scope);
5266
5718
  for (const filter of asArray(op["set"])) {
@@ -5337,7 +5789,9 @@ var generateSelectArray2 = (db, tableName, tables, relationMap, orderArgs, filte
5337
5789
  const queryArgs = selectArrayArgs(
5338
5790
  orderArgs,
5339
5791
  filterArgs,
5340
- distinctEnabled ? generateDistinctEnum(table, typeName) : void 0
5792
+ distinctEnabled ? generateDistinctEnum(table, typeName) : void 0,
5793
+ policies?.softDelete,
5794
+ tableName
5341
5795
  );
5342
5796
  return {
5343
5797
  name: fieldName,
@@ -5369,7 +5823,7 @@ var generateSelectArray2 = (db, tableName, tables, relationMap, orderArgs, filte
5369
5823
  nullOrdering: "nulls-largest"
5370
5824
  });
5371
5825
  }
5372
- const { offset, orderBy, where, distinct, after } = args;
5826
+ const { offset, orderBy, where, distinct, after, deleted } = args;
5373
5827
  const selectedColumnsSql = extractSelectedColumnsFromTreeSQLFormat(
5374
5828
  parsedInfo.fieldsByTypeName[typeName],
5375
5829
  table,
@@ -5407,7 +5861,8 @@ var generateSelectArray2 = (db, tableName, tables, relationMap, orderArgs, filte
5407
5861
  scope,
5408
5862
  tableName,
5409
5863
  table,
5410
- where ? extractFilters(table, tableName, where, relationFilterCtx(filterCtx, tableName)) : void 0
5864
+ where ? extractFilters(table, tableName, where, relationFilterCtx(filterCtx, tableName)) : void 0,
5865
+ deleted
5411
5866
  );
5412
5867
  const whereSql = cursorCondition ? and4(baseWhereSql, cursorCondition) : baseWhereSql;
5413
5868
  let distinctKeys;
@@ -5465,7 +5920,7 @@ var generateSelectArray2 = (db, tableName, tables, relationMap, orderArgs, filte
5465
5920
  };
5466
5921
  var generateSelectSingle2 = (db, tableName, tables, relationMap, orderArgs, filterArgs, fieldName, typeName, typeNameMapper, filterCtx, limits, policies) => {
5467
5922
  const queryBase = db.query[tableName];
5468
- const queryArgs = selectSingleArgs(orderArgs, filterArgs);
5923
+ const queryArgs = selectSingleArgs(orderArgs, filterArgs, policies?.softDelete, tableName);
5469
5924
  const table = tables[tableName];
5470
5925
  const pkNames = pgPrimaryKeyPropNames(table);
5471
5926
  return {
@@ -5494,7 +5949,7 @@ var generateSelectSingle2 = (db, tableName, tables, relationMap, orderArgs, filt
5494
5949
  scope
5495
5950
  });
5496
5951
  }
5497
- const { offset, orderBy, where } = args;
5952
+ const { offset, orderBy, where, deleted } = args;
5498
5953
  const selectedColumnsSql = extractSelectedColumnsFromTreeSQLFormat(
5499
5954
  parsedInfo.fieldsByTypeName[typeName],
5500
5955
  table,
@@ -5505,7 +5960,8 @@ var generateSelectSingle2 = (db, tableName, tables, relationMap, orderArgs, filt
5505
5960
  scope,
5506
5961
  tableName,
5507
5962
  table,
5508
- where ? extractFilters(table, tableName, where, relationFilterCtx(filterCtx, tableName)) : void 0
5963
+ where ? extractFilters(table, tableName, where, relationFilterCtx(filterCtx, tableName)) : void 0,
5964
+ deleted
5509
5965
  );
5510
5966
  if (whereSql) {
5511
5967
  q = q.where(whereSql);
@@ -5536,127 +5992,181 @@ var generateInsertArray2 = (db, tableName, table, tables, relationMap, baseType,
5536
5992
  }
5537
5993
  };
5538
5994
  const pkNames = pgPrimaryKeyPropNames(table);
5995
+ const hooks = policies?.onWrite?.(tableName, "insert");
5539
5996
  return {
5540
5997
  name: fieldName,
5541
5998
  resolver: async (_source, args, context, info) => {
5542
5999
  try {
5543
- return await runMutation(db, context, info, txCtx, async (executor) => {
5544
- if (!args.values.length) {
5545
- throw new GraphQLError8("No values were provided!");
5546
- }
5547
- const entries = nested?.enabled(tableName) ? args.values.map((values) => nested.split(tableName, values)) : void 0;
5548
- const nestedEntries = entries?.some((entry) => nested.hasOps(entry.ops)) ? entries : void 0;
5549
- const contextColumns = policies?.contextValues?.(tableName);
5550
- const scope = policies?.scope?.(context);
5551
- const input = nestedEntries ? [] : applyContextValuesAll(
5552
- remapFromGraphQLArrayInput(entries ? entries.map((entry) => entry.columns) : args.values, table),
5553
- contextColumns,
5554
- context
5555
- );
5556
- const parsedInfo = parseResolveInfo3(info, {
5557
- deep: true
5558
- });
5559
- const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
5560
- relationMap,
5561
- tables,
5562
- tableName,
5563
- typeName,
5564
- typeNameMapper,
5565
- table,
5566
- pkNames,
5567
- parsedInfo,
5568
- limits,
5569
- scope
5570
- });
5571
- const returning = nestedEntries ? nested.withJoinColumns(tableName, mergedOps(nestedEntries), { ...columns }, table) : columns;
5572
- const runInsert = async (target, values) => {
5573
- let query = target.insert(table).values(values).returning(returning);
5574
- if (conflictDoNothing) {
5575
- query = query.onConflictDoNothing();
6000
+ return await runMutation(
6001
+ db,
6002
+ context,
6003
+ info,
6004
+ txCtx,
6005
+ async (executor) => {
6006
+ if (!args.values.length) {
6007
+ throw new GraphQLError8("No values were provided!");
5576
6008
  }
5577
- return await query;
5578
- };
5579
- const result = nestedEntries ? await writeWithNestedOps({
5580
- executor,
5581
- runtime: nested,
5582
- tableName,
5583
- entries: nestedEntries,
5584
- remapValues: (values) => applyContextValues(remapFromGraphQLSingleInput(values, table), contextColumns, context),
5585
- write: (tx, values) => runInsert(tx, [values]),
5586
- context
5587
- }) : await runInsert(executor, input);
5588
- const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, result, pkNames, withParams) : result;
5589
- return remapToGraphQLArrayOutput(enriched, tableName, table, relationMap);
5590
- });
5591
- } catch (e) {
5592
- throw toGraphQLError(e);
5593
- }
5594
- },
5595
- args: queryArgs
5596
- };
5597
- };
5598
- var generateInsertSingle2 = (db, tableName, table, tables, relationMap, baseType, fieldName, typeName, typeNameMapper, conflictDoNothing = false, txCtx, nested, limits, policies) => {
5599
- const queryArgs = {
5600
- values: {
6009
+ await runWriteHook(hooks, "before", {
6010
+ table: tableName,
6011
+ operation: "insert",
6012
+ single: false,
6013
+ args,
6014
+ context,
6015
+ info,
6016
+ tx: executor
6017
+ });
6018
+ const entries = nested?.enabled(tableName) ? args.values.map((values) => nested.split(tableName, values)) : void 0;
6019
+ const nestedEntries = entries?.some((entry) => nested.hasOps(entry.ops)) ? entries : void 0;
6020
+ const contextColumns = policies?.contextValues?.(tableName);
6021
+ const scope = policies?.scope?.(context);
6022
+ const input = nestedEntries ? [] : applyContextValuesAll(
6023
+ remapFromGraphQLArrayInput(entries ? entries.map((entry) => entry.columns) : args.values, table),
6024
+ contextColumns,
6025
+ context
6026
+ );
6027
+ const parsedInfo = parseResolveInfo3(info, {
6028
+ deep: true
6029
+ });
6030
+ const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
6031
+ relationMap,
6032
+ tables,
6033
+ tableName,
6034
+ typeName,
6035
+ typeNameMapper,
6036
+ table,
6037
+ pkNames,
6038
+ parsedInfo,
6039
+ limits,
6040
+ scope
6041
+ });
6042
+ const returning = nestedEntries ? nested.withJoinColumns(tableName, mergedOps(nestedEntries), { ...columns }, table) : columns;
6043
+ const runInsert = async (target, values) => {
6044
+ let query = target.insert(table).values(values).returning(returning);
6045
+ if (conflictDoNothing) {
6046
+ query = query.onConflictDoNothing();
6047
+ }
6048
+ return await query;
6049
+ };
6050
+ const result = nestedEntries ? await writeWithNestedOps({
6051
+ executor,
6052
+ runtime: nested,
6053
+ tableName,
6054
+ entries: nestedEntries,
6055
+ remapValues: (values) => applyContextValues(remapFromGraphQLSingleInput(values, table), contextColumns, context),
6056
+ write: (tx, values) => runInsert(tx, [values]),
6057
+ context
6058
+ }) : await runInsert(executor, input);
6059
+ await runWriteHook(hooks, "after", {
6060
+ table: tableName,
6061
+ operation: "insert",
6062
+ single: false,
6063
+ args,
6064
+ rows: result,
6065
+ context,
6066
+ info,
6067
+ tx: executor
6068
+ });
6069
+ const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, result, pkNames, withParams) : result;
6070
+ return remapToGraphQLArrayOutput(enriched, tableName, table, relationMap);
6071
+ },
6072
+ !!hooks
6073
+ );
6074
+ } catch (e) {
6075
+ throw toGraphQLError(e);
6076
+ }
6077
+ },
6078
+ args: queryArgs
6079
+ };
6080
+ };
6081
+ var generateInsertSingle2 = (db, tableName, table, tables, relationMap, baseType, fieldName, typeName, typeNameMapper, conflictDoNothing = false, txCtx, nested, limits, policies) => {
6082
+ const queryArgs = {
6083
+ values: {
5601
6084
  type: new GraphQLNonNull7(baseType)
5602
6085
  }
5603
6086
  };
5604
6087
  const pkNames = pgPrimaryKeyPropNames(table);
6088
+ const hooks = policies?.onWrite?.(tableName, "insert");
5605
6089
  return {
5606
6090
  name: fieldName,
5607
6091
  resolver: async (_source, args, context, info) => {
5608
6092
  try {
5609
- return await runMutation(db, context, info, txCtx, async (executor) => {
5610
- const entry = nested?.enabled(tableName) ? nested.split(tableName, args.values) : void 0;
5611
- const nestedEntry = entry && nested.hasOps(entry.ops) ? entry : void 0;
5612
- const contextColumns = policies?.contextValues?.(tableName);
5613
- const scope = policies?.scope?.(context);
5614
- const input = nestedEntry ? {} : applyContextValues(
5615
- remapFromGraphQLSingleInput(entry ? entry.columns : args.values, table),
5616
- contextColumns,
5617
- context
5618
- );
5619
- const parsedInfo = parseResolveInfo3(info, {
5620
- deep: true
5621
- });
5622
- const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
5623
- relationMap,
5624
- tables,
5625
- tableName,
5626
- typeName,
5627
- typeNameMapper,
5628
- table,
5629
- pkNames,
5630
- parsedInfo,
5631
- limits,
5632
- scope
5633
- });
5634
- const returning = nestedEntry ? nested.withJoinColumns(tableName, nestedEntry.ops, { ...columns }, table) : columns;
5635
- const runInsert = async (target, values) => {
5636
- let query = target.insert(table).values(values).returning(returning);
5637
- if (conflictDoNothing) {
5638
- query = query.onConflictDoNothing();
5639
- }
5640
- return await query;
5641
- };
5642
- const result = nestedEntry ? await writeWithNestedOps({
5643
- executor,
5644
- runtime: nested,
5645
- tableName,
5646
- entries: [nestedEntry],
5647
- remapValues: (values) => applyContextValues(remapFromGraphQLSingleInput(values, table), contextColumns, context),
5648
- write: runInsert,
5649
- context
5650
- }) : await runInsert(executor, input);
5651
- if (!result[0]) {
5652
- if (!conflictDoNothing) {
5653
- throw new GraphQLError8(`${fieldName}: the insert returned no row.`);
6093
+ return await runMutation(
6094
+ db,
6095
+ context,
6096
+ info,
6097
+ txCtx,
6098
+ async (executor) => {
6099
+ await runWriteHook(hooks, "before", {
6100
+ table: tableName,
6101
+ operation: "insert",
6102
+ single: true,
6103
+ args,
6104
+ context,
6105
+ info,
6106
+ tx: executor
6107
+ });
6108
+ const entry = nested?.enabled(tableName) ? nested.split(tableName, args.values) : void 0;
6109
+ const nestedEntry = entry && nested.hasOps(entry.ops) ? entry : void 0;
6110
+ const contextColumns = policies?.contextValues?.(tableName);
6111
+ const scope = policies?.scope?.(context);
6112
+ const input = nestedEntry ? {} : applyContextValues(
6113
+ remapFromGraphQLSingleInput(entry ? entry.columns : args.values, table),
6114
+ contextColumns,
6115
+ context
6116
+ );
6117
+ const parsedInfo = parseResolveInfo3(info, {
6118
+ deep: true
6119
+ });
6120
+ const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
6121
+ relationMap,
6122
+ tables,
6123
+ tableName,
6124
+ typeName,
6125
+ typeNameMapper,
6126
+ table,
6127
+ pkNames,
6128
+ parsedInfo,
6129
+ limits,
6130
+ scope
6131
+ });
6132
+ const returning = nestedEntry ? nested.withJoinColumns(tableName, nestedEntry.ops, { ...columns }, table) : columns;
6133
+ const runInsert = async (target, values) => {
6134
+ let query = target.insert(table).values(values).returning(returning);
6135
+ if (conflictDoNothing) {
6136
+ query = query.onConflictDoNothing();
6137
+ }
6138
+ return await query;
6139
+ };
6140
+ const result = nestedEntry ? await writeWithNestedOps({
6141
+ executor,
6142
+ runtime: nested,
6143
+ tableName,
6144
+ entries: [nestedEntry],
6145
+ remapValues: (values) => applyContextValues(remapFromGraphQLSingleInput(values, table), contextColumns, context),
6146
+ write: runInsert,
6147
+ context
6148
+ }) : await runInsert(executor, input);
6149
+ await runWriteHook(hooks, "after", {
6150
+ table: tableName,
6151
+ operation: "insert",
6152
+ single: true,
6153
+ args,
6154
+ rows: result,
6155
+ context,
6156
+ info,
6157
+ tx: executor
6158
+ });
6159
+ if (!result[0]) {
6160
+ if (!conflictDoNothing) {
6161
+ throw new GraphQLError8(`${fieldName}: the insert returned no row.`);
6162
+ }
6163
+ return void 0;
5654
6164
  }
5655
- return void 0;
5656
- }
5657
- const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, result, pkNames, withParams) : result;
5658
- return remapToGraphQLSingleOutput(enriched[0], tableName, table, relationMap);
5659
- });
6165
+ const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, result, pkNames, withParams) : result;
6166
+ return remapToGraphQLSingleOutput(enriched[0], tableName, table, relationMap);
6167
+ },
6168
+ !!hooks
6169
+ );
5660
6170
  } catch (e) {
5661
6171
  throw toGraphQLError(e);
5662
6172
  }
@@ -5675,69 +6185,96 @@ var generateUpsert2 = (db, tableName, table, tables, relationMap, baseType, onCo
5675
6185
  }
5676
6186
  };
5677
6187
  const pkNames = pgPrimaryKeyPropNames(table);
6188
+ const hooks = policies?.onWrite?.(tableName, "upsert");
5678
6189
  return {
5679
6190
  name: fieldName,
5680
6191
  resolver: async (_source, args, context, info) => {
5681
6192
  try {
5682
- return await runMutation(db, context, info, txCtx, async (executor) => {
5683
- const supplied = single ? [args.values] : args.values;
5684
- if (!supplied.length) {
5685
- throw new GraphQLError8("No values were provided!");
5686
- }
5687
- const entries = nested?.enabled(tableName) ? supplied.map((values) => nested.split(tableName, values)) : void 0;
5688
- const nestedEntries = entries?.some((entry) => nested.hasOps(entry.ops)) ? entries : void 0;
5689
- const contextColumns = policies?.contextValues?.(tableName);
5690
- const scope = policies?.scope?.(context);
5691
- const input = nestedEntries ? [] : applyContextValuesAll(
5692
- remapFromGraphQLArrayInput(entries ? entries.map((entry) => entry.columns) : supplied, table),
5693
- contextColumns,
5694
- context
5695
- );
5696
- const parsedInfo = parseResolveInfo3(info, { deep: true });
5697
- const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
5698
- relationMap,
5699
- tables,
5700
- tableName,
5701
- typeName,
5702
- typeNameMapper,
5703
- table,
5704
- pkNames,
5705
- parsedInfo,
5706
- limits,
5707
- scope
5708
- });
5709
- const returning = nestedEntries ? nested.withJoinColumns(tableName, mergedOps(nestedEntries), { ...columns }, table) : columns;
5710
- const runUpsert = async (target, values) => {
5711
- const plan = resolveConflictPlan({
6193
+ return await runMutation(
6194
+ db,
6195
+ context,
6196
+ info,
6197
+ txCtx,
6198
+ async (executor) => {
6199
+ const supplied = single ? [args.values] : args.values;
6200
+ if (!supplied.length) {
6201
+ throw new GraphQLError8("No values were provided!");
6202
+ }
6203
+ await runWriteHook(hooks, "before", {
6204
+ table: tableName,
6205
+ operation: "upsert",
6206
+ single,
6207
+ args,
6208
+ context,
6209
+ info,
6210
+ tx: executor
6211
+ });
6212
+ const entries = nested?.enabled(tableName) ? supplied.map((values) => nested.split(tableName, values)) : void 0;
6213
+ const nestedEntries = entries?.some((entry) => nested.hasOps(entry.ops)) ? entries : void 0;
6214
+ const contextColumns = policies?.contextValues?.(tableName);
6215
+ const scope = policies?.scope?.(context);
6216
+ const input = nestedEntries ? [] : applyContextValuesAll(
6217
+ remapFromGraphQLArrayInput(entries ? entries.map((entry) => entry.columns) : supplied, table),
6218
+ contextColumns,
6219
+ context
6220
+ );
6221
+ const parsedInfo = parseResolveInfo3(info, { deep: true });
6222
+ const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
6223
+ relationMap,
6224
+ tables,
6225
+ tableName,
6226
+ typeName,
6227
+ typeNameMapper,
5712
6228
  table,
5713
- values,
5714
- onConflict: args.onConflict,
5715
6229
  pkNames,
5716
- uniqueSets,
5717
- excludedRef: excludedColumnRef,
5718
- withTarget: true,
5719
- buildWhere: (where) => extractFilters(table, tableName, where, relationFilterCtx(filterCtx, tableName))
6230
+ parsedInfo,
6231
+ limits,
6232
+ scope
5720
6233
  });
5721
- let query = target.insert(table).values(values).returning(returning);
5722
- const setWhere = withScope(scope, tableName, table, plan.setWhere);
5723
- query = plan.action === "NOTHING" ? query.onConflictDoNothing(plan.target ? { target: plan.target } : void 0) : query.onConflictDoUpdate({ target: plan.target, set: plan.set, setWhere });
5724
- return await query;
5725
- };
5726
- const result = nestedEntries ? await writeWithNestedOps({
5727
- executor,
5728
- runtime: nested,
5729
- tableName,
5730
- entries: nestedEntries,
5731
- remapValues: (values) => applyContextValues(remapFromGraphQLSingleInput(values, table), contextColumns, context),
5732
- write: (tx, values) => runUpsert(tx, [values]),
5733
- context
5734
- }) : await runUpsert(executor, input);
5735
- if (single && !result[0]) {
5736
- return void 0;
5737
- }
5738
- const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, result, pkNames, withParams) : result;
5739
- return single ? remapToGraphQLSingleOutput(enriched[0], tableName, table, relationMap) : remapToGraphQLArrayOutput(enriched, tableName, table, relationMap);
5740
- });
6234
+ const returning = nestedEntries ? nested.withJoinColumns(tableName, mergedOps(nestedEntries), { ...columns }, table) : columns;
6235
+ const runUpsert = async (target, values) => {
6236
+ const plan = resolveConflictPlan({
6237
+ table,
6238
+ values,
6239
+ onConflict: args.onConflict,
6240
+ pkNames,
6241
+ uniqueSets,
6242
+ excludedRef: excludedColumnRef,
6243
+ withTarget: true,
6244
+ buildWhere: (where) => extractFilters(table, tableName, where, relationFilterCtx(filterCtx, tableName))
6245
+ });
6246
+ let query = target.insert(table).values(values).returning(returning);
6247
+ const setWhere = withScope(scope, tableName, table, plan.setWhere);
6248
+ query = plan.action === "NOTHING" ? query.onConflictDoNothing(plan.target ? { target: plan.target } : void 0) : query.onConflictDoUpdate({ target: plan.target, set: plan.set, setWhere });
6249
+ return await query;
6250
+ };
6251
+ const result = nestedEntries ? await writeWithNestedOps({
6252
+ executor,
6253
+ runtime: nested,
6254
+ tableName,
6255
+ entries: nestedEntries,
6256
+ remapValues: (values) => applyContextValues(remapFromGraphQLSingleInput(values, table), contextColumns, context),
6257
+ write: (tx, values) => runUpsert(tx, [values]),
6258
+ context
6259
+ }) : await runUpsert(executor, input);
6260
+ await runWriteHook(hooks, "after", {
6261
+ table: tableName,
6262
+ operation: "upsert",
6263
+ single,
6264
+ args,
6265
+ rows: result,
6266
+ context,
6267
+ info,
6268
+ tx: executor
6269
+ });
6270
+ if (single && !result[0]) {
6271
+ return void 0;
6272
+ }
6273
+ const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, result, pkNames, withParams) : result;
6274
+ return single ? remapToGraphQLSingleOutput(enriched[0], tableName, table, relationMap) : remapToGraphQLArrayOutput(enriched, tableName, table, relationMap);
6275
+ },
6276
+ !!hooks
6277
+ );
5741
6278
  } catch (e) {
5742
6279
  throw toGraphQLError(e);
5743
6280
  }
@@ -5755,75 +6292,102 @@ var generateUpdate2 = (db, tableName, table, tables, relationMap, setArgs, filte
5755
6292
  }
5756
6293
  };
5757
6294
  const pkNames = pgPrimaryKeyPropNames(table);
6295
+ const hooks = policies?.onWrite?.(tableName, "update");
5758
6296
  return {
5759
6297
  name: fieldName,
5760
6298
  resolver: async (_source, args, context, info) => {
5761
6299
  try {
5762
- return await runMutation(db, context, info, txCtx, async (executor) => {
5763
- const { where, set } = args;
5764
- const scope = policies?.scope?.(context);
5765
- const parsedInfo = parseResolveInfo3(info, {
5766
- deep: true
5767
- });
5768
- const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
5769
- relationMap,
5770
- tables,
5771
- tableName,
5772
- typeName,
5773
- typeNameMapper,
5774
- table,
5775
- pkNames,
5776
- parsedInfo,
5777
- limits,
5778
- scope
5779
- });
5780
- const entry = nested?.enabled(tableName) ? nested.split(tableName, set) : void 0;
5781
- const nestedOps = entry && nested.hasOps(entry.ops) ? entry.ops : void 0;
5782
- const input = stripContextValues(
5783
- remapUpdateInput(entry ? entry.columns : set, table, tableName),
5784
- policies?.contextValues?.(tableName)
5785
- );
5786
- if (!Object.keys(input).length && !nestedOps) {
5787
- throw new GraphQLError8("Unable to update with no values specified!");
5788
- }
5789
- const relationCtx = relationFilterCtx(filterCtx, tableName);
5790
- const filters = withScope(
5791
- scope,
5792
- tableName,
5793
- table,
5794
- single || requireWhere ? extractRequiredFilters(table, tableName, where, fieldName, relationCtx) : where ? extractFilters(table, tableName, where, relationCtx) : void 0
5795
- );
5796
- if (single) {
5797
- await assertSingleMatch(executor, table, filters, fieldName);
5798
- }
5799
- const returning = nestedOps ? nested.withJoinColumns(tableName, nestedOps, { ...columns }, table) : columns;
5800
- const runUpdate = async (target, values) => {
5801
- const writes = Object.keys(values).length > 0;
5802
- let query = writes ? target.update(table).set(values) : target.select(returning).from(table);
5803
- if (filters) {
5804
- query = query.where(filters);
6300
+ return await runMutation(
6301
+ db,
6302
+ context,
6303
+ info,
6304
+ txCtx,
6305
+ async (executor) => {
6306
+ const { where, set } = args;
6307
+ const scope = policies?.scope?.(context);
6308
+ await runWriteHook(hooks, "before", {
6309
+ table: tableName,
6310
+ operation: "update",
6311
+ single,
6312
+ args,
6313
+ context,
6314
+ info,
6315
+ tx: executor
6316
+ });
6317
+ const parsedInfo = parseResolveInfo3(info, {
6318
+ deep: true
6319
+ });
6320
+ const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
6321
+ relationMap,
6322
+ tables,
6323
+ tableName,
6324
+ typeName,
6325
+ typeNameMapper,
6326
+ table,
6327
+ pkNames,
6328
+ parsedInfo,
6329
+ limits,
6330
+ scope
6331
+ });
6332
+ const entry = nested?.enabled(tableName) ? nested.split(tableName, set) : void 0;
6333
+ const nestedOps = entry && nested.hasOps(entry.ops) ? entry.ops : void 0;
6334
+ const input = stripContextValues(
6335
+ remapUpdateInput(entry ? entry.columns : set, table, tableName),
6336
+ policies?.contextValues?.(tableName)
6337
+ );
6338
+ if (!Object.keys(input).length && !nestedOps) {
6339
+ throw new GraphQLError8("Unable to update with no values specified!");
5805
6340
  }
5806
- return await (writes ? query.returning(returning) : query);
5807
- };
5808
- const result = nestedOps ? await updateWithNestedOps({
5809
- executor,
5810
- runtime: nested,
5811
- tableName,
5812
- columns: input,
5813
- ops: nestedOps,
5814
- remapValues: (values) => values,
5815
- write: runUpdate,
5816
- context
5817
- }) : await runUpdate(executor, input);
5818
- if (single && result.length > 1) {
5819
- throw new GraphQLError8(`${fieldName}: 'where' matched more than one row!`);
5820
- }
5821
- if (single && !result[0]) {
5822
- return void 0;
5823
- }
5824
- const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, result, pkNames, withParams) : result;
5825
- return single ? remapToGraphQLSingleOutput(enriched[0], tableName, table, relationMap) : remapToGraphQLArrayOutput(enriched, tableName, table, relationMap);
5826
- });
6341
+ const relationCtx = relationFilterCtx(filterCtx, tableName);
6342
+ const filters = withScope(
6343
+ scope,
6344
+ tableName,
6345
+ table,
6346
+ single || requireWhere ? extractRequiredFilters(table, tableName, where, fieldName, relationCtx) : where ? extractFilters(table, tableName, where, relationCtx) : void 0
6347
+ );
6348
+ if (single) {
6349
+ await assertSingleMatch(executor, table, filters, fieldName);
6350
+ }
6351
+ const returning = nestedOps ? nested.withJoinColumns(tableName, nestedOps, { ...columns }, table) : columns;
6352
+ const runUpdate = async (target, values) => {
6353
+ const writes = Object.keys(values).length > 0;
6354
+ let query = writes ? target.update(table).set(values) : target.select(returning).from(table);
6355
+ if (filters) {
6356
+ query = query.where(filters);
6357
+ }
6358
+ return await (writes ? query.returning(returning) : query);
6359
+ };
6360
+ const result = nestedOps ? await updateWithNestedOps({
6361
+ executor,
6362
+ runtime: nested,
6363
+ tableName,
6364
+ columns: input,
6365
+ ops: nestedOps,
6366
+ remapValues: (values) => values,
6367
+ write: runUpdate,
6368
+ context
6369
+ }) : await runUpdate(executor, input);
6370
+ await runWriteHook(hooks, "after", {
6371
+ table: tableName,
6372
+ operation: "update",
6373
+ single,
6374
+ args,
6375
+ rows: result,
6376
+ context,
6377
+ info,
6378
+ tx: executor
6379
+ });
6380
+ if (single && result.length > 1) {
6381
+ throw new GraphQLError8(`${fieldName}: 'where' matched more than one row!`);
6382
+ }
6383
+ if (single && !result[0]) {
6384
+ return void 0;
6385
+ }
6386
+ const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, result, pkNames, withParams) : result;
6387
+ return single ? remapToGraphQLSingleOutput(enriched[0], tableName, table, relationMap) : remapToGraphQLArrayOutput(enriched, tableName, table, relationMap);
6388
+ },
6389
+ !!hooks
6390
+ );
5827
6391
  } catch (e) {
5828
6392
  throw toGraphQLError(e);
5829
6393
  }
@@ -5838,94 +6402,121 @@ var generateUpdateMany2 = (db, tableName, table, tables, relationMap, updateMany
5838
6402
  }
5839
6403
  };
5840
6404
  const pkNames = pgPrimaryKeyPropNames(table);
6405
+ const hooks = policies?.onWrite?.(tableName, "updateMany");
5841
6406
  return {
5842
6407
  name: fieldName,
5843
6408
  resolver: async (_source, args, context, info) => {
5844
6409
  try {
5845
- return await runMutation(db, context, info, txCtx, async (executor) => {
5846
- const { updates } = args;
5847
- if (!updates.length) {
5848
- throw new GraphQLError8("No updates were provided!");
5849
- }
5850
- const scope = policies?.scope?.(context);
5851
- const contextColumns = policies?.contextValues?.(tableName);
5852
- const parsedInfo = parseResolveInfo3(info, {
5853
- deep: true
5854
- });
5855
- const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
5856
- relationMap,
5857
- tables,
5858
- tableName,
5859
- typeName,
5860
- typeNameMapper,
5861
- table,
5862
- pkNames,
5863
- parsedInfo,
5864
- limits,
5865
- scope
5866
- });
5867
- const entries = updates.map(({ where, set }) => {
5868
- const split = nested?.enabled(tableName) ? nested.split(tableName, set) : void 0;
5869
- const ops = split && nested.hasOps(split.ops) ? split.ops : void 0;
5870
- const input = stripContextValues(
5871
- remapUpdateInput(split ? split.columns : set, table, tableName),
5872
- contextColumns
5873
- );
5874
- if (!Object.keys(input).length && !ops) {
5875
- throw new GraphQLError8("Unable to update with no values specified!");
6410
+ return await runMutation(
6411
+ db,
6412
+ context,
6413
+ info,
6414
+ txCtx,
6415
+ async (executor) => {
6416
+ const { updates } = args;
6417
+ if (!updates.length) {
6418
+ throw new GraphQLError8("No updates were provided!");
5876
6419
  }
5877
- return {
5878
- set: input,
5879
- ops,
5880
- filters: withScope(
5881
- scope,
5882
- tableName,
5883
- table,
5884
- where ? extractFilters(table, tableName, where, relationFilterCtx(filterCtx, tableName)) : void 0
5885
- )
5886
- };
5887
- });
5888
- const returning = entries.some((entry) => entry.ops) ? nested.withJoinColumns(
5889
- tableName,
5890
- mergedOps(entries.map((entry) => ({ ops: entry.ops ?? {} }))),
5891
- {
5892
- ...columns
5893
- },
5894
- table
5895
- ) : columns;
5896
- const perEntry = await executor.transaction(async (tx) => {
5897
- const results = [];
5898
- for (const entry of entries) {
5899
- const values = entry.ops ? { ...entry.set, ...await nested.applyParentSide(tx, tableName, entry.ops, context) } : entry.set;
5900
- const writes = Object.keys(values).length > 0;
5901
- let query = writes ? tx.update(table).set(values) : tx.select(returning).from(table);
5902
- if (entry.filters) {
5903
- query = query.where(entry.filters);
6420
+ await runWriteHook(hooks, "before", {
6421
+ table: tableName,
6422
+ operation: "updateMany",
6423
+ single: false,
6424
+ args,
6425
+ context,
6426
+ info,
6427
+ tx: executor
6428
+ });
6429
+ const scope = policies?.scope?.(context);
6430
+ const contextColumns = policies?.contextValues?.(tableName);
6431
+ const parsedInfo = parseResolveInfo3(info, {
6432
+ deep: true
6433
+ });
6434
+ const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
6435
+ relationMap,
6436
+ tables,
6437
+ tableName,
6438
+ typeName,
6439
+ typeNameMapper,
6440
+ table,
6441
+ pkNames,
6442
+ parsedInfo,
6443
+ limits,
6444
+ scope
6445
+ });
6446
+ const entries = updates.map(({ where, set }) => {
6447
+ const split = nested?.enabled(tableName) ? nested.split(tableName, set) : void 0;
6448
+ const ops = split && nested.hasOps(split.ops) ? split.ops : void 0;
6449
+ const input = stripContextValues(
6450
+ remapUpdateInput(split ? split.columns : set, table, tableName),
6451
+ contextColumns
6452
+ );
6453
+ if (!Object.keys(input).length && !ops) {
6454
+ throw new GraphQLError8("Unable to update with no values specified!");
5904
6455
  }
5905
- const rows = await (writes ? query.returning(returning) : query);
5906
- if (entry.ops) {
5907
- await nested.applyChildSide(tx, tableName, entry.ops, rows, context);
6456
+ return {
6457
+ set: input,
6458
+ ops,
6459
+ filters: withScope(
6460
+ scope,
6461
+ tableName,
6462
+ table,
6463
+ where ? extractFilters(table, tableName, where, relationFilterCtx(filterCtx, tableName)) : void 0
6464
+ )
6465
+ };
6466
+ });
6467
+ const returning = entries.some((entry) => entry.ops) ? nested.withJoinColumns(
6468
+ tableName,
6469
+ mergedOps(entries.map((entry) => ({ ops: entry.ops ?? {} }))),
6470
+ {
6471
+ ...columns
6472
+ },
6473
+ table
6474
+ ) : columns;
6475
+ const perEntry = await executor.transaction(async (tx) => {
6476
+ const results = [];
6477
+ for (const entry of entries) {
6478
+ const values = entry.ops ? { ...entry.set, ...await nested.applyParentSide(tx, tableName, entry.ops, context) } : entry.set;
6479
+ const writes = Object.keys(values).length > 0;
6480
+ let query = writes ? tx.update(table).set(values) : tx.select(returning).from(table);
6481
+ if (entry.filters) {
6482
+ query = query.where(entry.filters);
6483
+ }
6484
+ const rows = await (writes ? query.returning(returning) : query);
6485
+ if (entry.ops) {
6486
+ await nested.applyChildSide(tx, tableName, entry.ops, rows, context);
6487
+ }
6488
+ results.push(rows);
5908
6489
  }
5909
- results.push(rows);
5910
- }
5911
- return results;
5912
- });
5913
- const flatRows = perEntry.flat();
5914
- const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, flatRows, pkNames, withParams) : flatRows;
5915
- const output = [];
5916
- let offset = 0;
5917
- for (const rows of perEntry) {
5918
- if (!rows.length) {
5919
- output.push(null);
5920
- continue;
5921
- }
5922
- for (let i = 0; i < rows.length; i++) {
5923
- output.push(remapToGraphQLSingleOutput(enriched[offset + i], tableName, table, relationMap));
6490
+ return results;
6491
+ });
6492
+ const flatRows = perEntry.flat();
6493
+ await runWriteHook(hooks, "after", {
6494
+ table: tableName,
6495
+ operation: "updateMany",
6496
+ single: false,
6497
+ args,
6498
+ rows: flatRows,
6499
+ context,
6500
+ info,
6501
+ tx: executor
6502
+ });
6503
+ const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, flatRows, pkNames, withParams) : flatRows;
6504
+ const output = [];
6505
+ let offset = 0;
6506
+ for (const rows of perEntry) {
6507
+ if (!rows.length) {
6508
+ output.push(null);
6509
+ continue;
6510
+ }
6511
+ for (let i = 0; i < rows.length; i++) {
6512
+ output.push(remapToGraphQLSingleOutput(enriched[offset + i], tableName, table, relationMap));
6513
+ }
6514
+ offset += rows.length;
5924
6515
  }
5925
- offset += rows.length;
5926
- }
5927
- return output;
5928
- });
6516
+ return output;
6517
+ },
6518
+ !!hooks
6519
+ );
5929
6520
  } catch (e) {
5930
6521
  throw toGraphQLError(e);
5931
6522
  }
@@ -5933,7 +6524,10 @@ var generateUpdateMany2 = (db, tableName, table, tables, relationMap, updateMany
5933
6524
  args: queryArgs
5934
6525
  };
5935
6526
  };
5936
- var generateDelete2 = (db, tableName, table, filterArgs, fieldName, typeName, single, requireWhere, filterCtx, selectionCtx, txCtx, policies) => {
6527
+ var generateDelete2 = (db, tableName, table, filterArgs, fieldName, typeName, single, requireWhere, filterCtx, selectionCtx, txCtx, policies, restore = false) => {
6528
+ const softDelete = policies?.softDelete?.(tableName);
6529
+ const operation = restore ? "restore" : "delete";
6530
+ const hooks = policies?.onWrite?.(tableName, operation);
5937
6531
  const queryArgs = {
5938
6532
  where: {
5939
6533
  type: single || requireWhere ? new GraphQLNonNull7(filterArgs) : filterArgs
@@ -5943,41 +6537,68 @@ var generateDelete2 = (db, tableName, table, filterArgs, fieldName, typeName, si
5943
6537
  name: fieldName,
5944
6538
  resolver: async (_source, args, context, info) => {
5945
6539
  try {
5946
- return await runMutation(db, context, info, txCtx, async (executor) => {
5947
- const { where } = args;
5948
- const scope = policies?.scope?.(context);
5949
- const parsedInfo = parseResolveInfo3(info, {
5950
- deep: true
5951
- });
5952
- const columns = extractSelectedColumnsFromTreeSQLFormat(
5953
- parsedInfo.fieldsByTypeName[typeName],
5954
- table,
5955
- selectionCtx
5956
- );
5957
- const relationCtx = relationFilterCtx(filterCtx, tableName);
5958
- const filters = withScope(
5959
- scope,
5960
- tableName,
5961
- table,
5962
- single || requireWhere ? extractRequiredFilters(table, tableName, where, fieldName, relationCtx) : where ? extractFilters(table, tableName, where, relationCtx) : void 0
5963
- );
5964
- if (single) {
5965
- await assertSingleMatch(executor, table, filters, fieldName);
5966
- }
5967
- let query = executor.delete(table);
5968
- if (filters) {
5969
- query = query.where(filters);
5970
- }
5971
- query = query.returning(columns);
5972
- const result = await query;
5973
- if (single && result.length > 1) {
5974
- throw new GraphQLError8(`${fieldName}: 'where' matched more than one row!`);
5975
- }
5976
- if (single) {
5977
- return result[0] ? remapToGraphQLSingleOutput(result[0], tableName, table) : void 0;
5978
- }
5979
- return remapToGraphQLArrayOutput(result, tableName, table);
5980
- });
6540
+ return await runMutation(
6541
+ db,
6542
+ context,
6543
+ info,
6544
+ txCtx,
6545
+ async (executor) => {
6546
+ const { where } = args;
6547
+ const scope = policies?.scope?.(context);
6548
+ await runWriteHook(hooks, "before", {
6549
+ table: tableName,
6550
+ operation,
6551
+ single,
6552
+ args,
6553
+ context,
6554
+ info,
6555
+ tx: executor
6556
+ });
6557
+ const parsedInfo = parseResolveInfo3(info, {
6558
+ deep: true
6559
+ });
6560
+ const columns = extractSelectedColumnsFromTreeSQLFormat(
6561
+ parsedInfo.fieldsByTypeName[typeName],
6562
+ table,
6563
+ selectionCtx
6564
+ );
6565
+ const relationCtx = relationFilterCtx(filterCtx, tableName);
6566
+ const filters = withScope(
6567
+ scope,
6568
+ tableName,
6569
+ table,
6570
+ single || requireWhere ? extractRequiredFilters(table, tableName, where, fieldName, relationCtx) : where ? extractFilters(table, tableName, where, relationCtx) : void 0,
6571
+ restore ? "ONLY" : void 0
6572
+ );
6573
+ if (single) {
6574
+ await assertSingleMatch(executor, table, filters, fieldName);
6575
+ }
6576
+ let query = softDelete ? executor.update(table).set({ [softDelete.columnName]: restore ? softDelete.writeRestored : softDelete.writeDeleted() }) : executor.delete(table);
6577
+ if (filters) {
6578
+ query = query.where(filters);
6579
+ }
6580
+ query = query.returning(columns);
6581
+ const result = await query;
6582
+ await runWriteHook(hooks, "after", {
6583
+ table: tableName,
6584
+ operation,
6585
+ single,
6586
+ args,
6587
+ rows: result,
6588
+ context,
6589
+ info,
6590
+ tx: executor
6591
+ });
6592
+ if (single && result.length > 1) {
6593
+ throw new GraphQLError8(`${fieldName}: 'where' matched more than one row!`);
6594
+ }
6595
+ if (single) {
6596
+ return result[0] ? remapToGraphQLSingleOutput(result[0], tableName, table) : void 0;
6597
+ }
6598
+ return remapToGraphQLArrayOutput(result, tableName, table);
6599
+ },
6600
+ !!hooks
6601
+ );
5981
6602
  } catch (e) {
5982
6603
  throw toGraphQLError(e);
5983
6604
  }
@@ -6018,16 +6639,17 @@ function generateSchemaData2(db, schema, relations, options) {
6018
6639
  attachTargetPrimaryKeys(namedRelations, tables, pgPrimaryKeyPropNames);
6019
6640
  const eagerRelations = pruneNonEagerRelations(namedRelations, shouldEagerLoad);
6020
6641
  const filterCtx = { tables, relationMap: namedRelations };
6021
- const scopes = options.policies?.scope;
6022
- const contextValuesOf = options.policies?.contextValues;
6023
- const policies = bindPolicies(options.policies, filterCtx);
6642
+ const tablePolicies = options.policies;
6643
+ const contextValuesOf = tablePolicies?.contextValues;
6644
+ const softDeleteOf = tablePolicies?.softDelete;
6645
+ const policies = bindPolicies(tablePolicies, filterCtx);
6024
6646
  const resolverFactory = createRelationResolverFactory(
6025
6647
  db,
6026
6648
  tables,
6027
6649
  "nulls-largest",
6028
6650
  filterCtx,
6029
6651
  limits,
6030
- scopes
6652
+ tablePolicies
6031
6653
  );
6032
6654
  const cacheCtx = {
6033
6655
  genericFilterCache: /* @__PURE__ */ new Map(),
@@ -6046,6 +6668,7 @@ function generateSchemaData2(db, schema, relations, options) {
6046
6668
  docs: options.docs ?? {},
6047
6669
  primaryKeyOf: (name) => tables[name] ? pgPrimaryKeyPropNames(tables[name]) : [],
6048
6670
  contextValuesOf,
6671
+ softDeleteOf,
6049
6672
  featureOf
6050
6673
  };
6051
6674
  const nestedPlans = features.nestedWrites ? buildNestedWritePlans(
@@ -6056,8 +6679,13 @@ function generateSchemaData2(db, schema, relations, options) {
6056
6679
  extractRelationJoinColumns
6057
6680
  ) : void 0;
6058
6681
  const nestedTypes = nestedPlans ? createNestedWriteTypes({ plans: nestedPlans, cacheCtx, typeNameMapper, insertPrefix: prefixes.insert }) : void 0;
6059
- const nestedRuntime = nestedPlans ? createNestedWriteRuntime({ plans: nestedPlans, filterCtx, scopes, contextValues: contextValuesOf }) : void 0;
6060
- const relationAggregateFactory = anyTable("relationAggregates") ? createRelationAggregateFactory(db, tables, cacheCtx, typeNameMapper, filterCtx, scopes) : void 0;
6682
+ const nestedRuntime = nestedPlans ? createNestedWriteRuntime({
6683
+ plans: nestedPlans,
6684
+ filterCtx,
6685
+ policies: tablePolicies,
6686
+ contextValues: contextValuesOf
6687
+ }) : void 0;
6688
+ const relationAggregateFactory = anyTable("relationAggregates") ? createRelationAggregateFactory(db, tables, cacheCtx, typeNameMapper, filterCtx, tablePolicies) : void 0;
6061
6689
  const queries = {};
6062
6690
  const mutations = {};
6063
6691
  const mutationTxCtx = createMutationTxCtx(options.transactions);
@@ -6103,8 +6731,11 @@ function generateSchemaData2(db, schema, relations, options) {
6103
6731
  updateCountFieldName,
6104
6732
  deleteFieldName,
6105
6733
  deleteSingleFieldName,
6734
+ restoreFieldName,
6735
+ restoreSingleFieldName,
6106
6736
  deleteCountFieldName
6107
6737
  } = computeResolverFieldNames(tableName, typeNameMapper, prefixes, suffixes);
6738
+ const softDeleteInfo = softDeleteOf?.(tableName);
6108
6739
  const selectArrGenerated = generateSelectArray2(
6109
6740
  db,
6110
6741
  tableName,
@@ -6295,6 +6926,36 @@ function generateSchemaData2(db, schema, relations, options) {
6295
6926
  mutationTxCtx,
6296
6927
  policies
6297
6928
  ) : void 0;
6929
+ const restoreGenerated = softDeleteInfo ? generateDelete2(
6930
+ db,
6931
+ tableName,
6932
+ schema[tableName],
6933
+ tableFilters,
6934
+ restoreFieldName,
6935
+ typeName,
6936
+ false,
6937
+ tableFeatures.requireWhere,
6938
+ filterCtx,
6939
+ { tableName, relationMap: namedRelations, tables },
6940
+ mutationTxCtx,
6941
+ policies,
6942
+ true
6943
+ ) : void 0;
6944
+ const restoreSingleGenerated = softDeleteInfo ? generateDelete2(
6945
+ db,
6946
+ tableName,
6947
+ schema[tableName],
6948
+ tableFilters,
6949
+ restoreSingleFieldName,
6950
+ typeName,
6951
+ true,
6952
+ tableFeatures.requireWhere,
6953
+ filterCtx,
6954
+ { tableName, relationMap: namedRelations, tables },
6955
+ mutationTxCtx,
6956
+ policies,
6957
+ true
6958
+ ) : void 0;
6298
6959
  const updateCountGenerated = tableFeatures.update && tableFeatures.countMutations ? generateWriteCount({
6299
6960
  db,
6300
6961
  tableName,
@@ -6328,7 +6989,7 @@ function generateSchemaData2(db, schema, relations, options) {
6328
6989
  aggregateFieldName,
6329
6990
  tableFilters,
6330
6991
  filterCtx,
6331
- scopes
6992
+ tablePolicies
6332
6993
  ) : void 0;
6333
6994
  const groupByType = tableFeatures.aggregates && tableFeatures.groupBy ? generateGroupByType(schema[tableName], tableName, typeName, cacheCtx) : void 0;
6334
6995
  const groupByEnum = groupByType ? generateGroupByEnum(schema[tableName], tableName, typeName) : void 0;
@@ -6343,7 +7004,7 @@ function generateSchemaData2(db, schema, relations, options) {
6343
7004
  groupByEnum,
6344
7005
  havingInput,
6345
7006
  filterCtx,
6346
- scopes
7007
+ tablePolicies
6347
7008
  ) : void 0;
6348
7009
  queries[selectArrGenerated.name] = {
6349
7010
  type: selectArrOutput,
@@ -6480,6 +7141,26 @@ function generateSchemaData2(db, schema, relations, options) {
6480
7141
  }
6481
7142
  };
6482
7143
  }
7144
+ if (restoreGenerated) {
7145
+ mutations[restoreGenerated.name] = {
7146
+ type: arrTableItemOutput,
7147
+ args: restoreGenerated.args,
7148
+ resolve: restoreGenerated.resolver,
7149
+ extensions: {
7150
+ drizzle: drizzleMeta({ kind: "mutation", operation: "restore", single: false, targetArg: "where" })
7151
+ }
7152
+ };
7153
+ }
7154
+ if (restoreSingleGenerated) {
7155
+ mutations[restoreSingleGenerated.name] = {
7156
+ type: singleTableItemOutput,
7157
+ args: restoreSingleGenerated.args,
7158
+ resolve: restoreSingleGenerated.resolver,
7159
+ extensions: {
7160
+ drizzle: drizzleMeta({ kind: "mutation", operation: "restore", single: true, targetArg: "where" })
7161
+ }
7162
+ };
7163
+ }
6483
7164
  if (updateCountGenerated) {
6484
7165
  mutations[updateCountGenerated.name] = {
6485
7166
  type: new GraphQLNonNull7(GraphQLInt6),
@@ -6563,7 +7244,9 @@ var generateSelectArray3 = (db, tableName, tables, relationMap, orderArgs, filte
6563
7244
  const queryArgs = selectArrayArgs(
6564
7245
  orderArgs,
6565
7246
  filterArgs,
6566
- distinctEnabled ? generateDistinctEnum(table, typeName) : void 0
7247
+ distinctEnabled ? generateDistinctEnum(table, typeName) : void 0,
7248
+ policies?.softDelete,
7249
+ tableName
6567
7250
  );
6568
7251
  return {
6569
7252
  name: fieldName,
@@ -6605,7 +7288,7 @@ var generateSelectSingle3 = (db, tableName, tables, relationMap, orderArgs, filt
6605
7288
  `Drizzle-GraphQL Error: Table ${tableName} not found in drizzle instance. Did you forget to pass schema to drizzle constructor?`
6606
7289
  );
6607
7290
  }
6608
- const queryArgs = selectSingleArgs(orderArgs, filterArgs);
7291
+ const queryArgs = selectSingleArgs(orderArgs, filterArgs, policies?.softDelete, tableName);
6609
7292
  const table = tables[tableName];
6610
7293
  const pkNames = sqlitePrimaryKeyPropNames(table);
6611
7294
  return {
@@ -6646,127 +7329,181 @@ var generateInsertArray3 = (db, tableName, table, tables, relationMap, baseType,
6646
7329
  }
6647
7330
  };
6648
7331
  const pkNames = sqlitePrimaryKeyPropNames(table);
7332
+ const hooks = policies?.onWrite?.(tableName, "insert");
6649
7333
  return {
6650
7334
  name: fieldName,
6651
7335
  resolver: async (_source, args, context, info) => {
6652
7336
  try {
6653
- return await runMutation(db, context, info, txCtx, async (executor) => {
6654
- if (!args.values.length) {
6655
- throw new GraphQLError9("No values were provided!");
6656
- }
6657
- const entries = nested?.enabled(tableName) ? args.values.map((values) => nested.split(tableName, values)) : void 0;
6658
- const nestedEntries = entries?.some((entry) => nested.hasOps(entry.ops)) ? entries : void 0;
6659
- const contextColumns = policies?.contextValues?.(tableName);
6660
- const scope = policies?.scope?.(context);
6661
- const input = nestedEntries ? [] : applyContextValuesAll(
6662
- remapFromGraphQLArrayInput(entries ? entries.map((entry) => entry.columns) : args.values, table),
6663
- contextColumns,
6664
- context
6665
- );
6666
- const parsedInfo = parseResolveInfo4(info, {
6667
- deep: true
6668
- });
6669
- const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
6670
- relationMap,
6671
- tables,
6672
- tableName,
6673
- typeName,
6674
- typeNameMapper,
6675
- table,
6676
- pkNames,
6677
- parsedInfo,
6678
- limits,
6679
- scope
6680
- });
6681
- const returning = nestedEntries ? nested.withJoinColumns(tableName, mergedOps(nestedEntries), { ...columns }, table) : columns;
6682
- const runInsert = async (target, values) => {
6683
- let query = target.insert(table).values(values).returning(returning);
6684
- if (conflictDoNothing) {
6685
- query = query.onConflictDoNothing();
7337
+ return await runMutation(
7338
+ db,
7339
+ context,
7340
+ info,
7341
+ txCtx,
7342
+ async (executor) => {
7343
+ if (!args.values.length) {
7344
+ throw new GraphQLError9("No values were provided!");
6686
7345
  }
6687
- return await query;
6688
- };
6689
- const result = nestedEntries ? await writeWithNestedOps({
6690
- executor,
6691
- runtime: nested,
6692
- tableName,
6693
- entries: nestedEntries,
6694
- remapValues: (values) => applyContextValues(remapFromGraphQLSingleInput(values, table), contextColumns, context),
6695
- write: (tx, values) => runInsert(tx, [values]),
6696
- context
6697
- }) : await runInsert(executor, input);
6698
- const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, result, pkNames, withParams) : result;
6699
- return remapToGraphQLArrayOutput(enriched, tableName, table, relationMap);
6700
- });
7346
+ await runWriteHook(hooks, "before", {
7347
+ table: tableName,
7348
+ operation: "insert",
7349
+ single: false,
7350
+ args,
7351
+ context,
7352
+ info,
7353
+ tx: executor
7354
+ });
7355
+ const entries = nested?.enabled(tableName) ? args.values.map((values) => nested.split(tableName, values)) : void 0;
7356
+ const nestedEntries = entries?.some((entry) => nested.hasOps(entry.ops)) ? entries : void 0;
7357
+ const contextColumns = policies?.contextValues?.(tableName);
7358
+ const scope = policies?.scope?.(context);
7359
+ const input = nestedEntries ? [] : applyContextValuesAll(
7360
+ remapFromGraphQLArrayInput(entries ? entries.map((entry) => entry.columns) : args.values, table),
7361
+ contextColumns,
7362
+ context
7363
+ );
7364
+ const parsedInfo = parseResolveInfo4(info, {
7365
+ deep: true
7366
+ });
7367
+ const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
7368
+ relationMap,
7369
+ tables,
7370
+ tableName,
7371
+ typeName,
7372
+ typeNameMapper,
7373
+ table,
7374
+ pkNames,
7375
+ parsedInfo,
7376
+ limits,
7377
+ scope
7378
+ });
7379
+ const returning = nestedEntries ? nested.withJoinColumns(tableName, mergedOps(nestedEntries), { ...columns }, table) : columns;
7380
+ const runInsert = async (target, values) => {
7381
+ let query = target.insert(table).values(values).returning(returning);
7382
+ if (conflictDoNothing) {
7383
+ query = query.onConflictDoNothing();
7384
+ }
7385
+ return await query;
7386
+ };
7387
+ const result = nestedEntries ? await writeWithNestedOps({
7388
+ executor,
7389
+ runtime: nested,
7390
+ tableName,
7391
+ entries: nestedEntries,
7392
+ remapValues: (values) => applyContextValues(remapFromGraphQLSingleInput(values, table), contextColumns, context),
7393
+ write: (tx, values) => runInsert(tx, [values]),
7394
+ context
7395
+ }) : await runInsert(executor, input);
7396
+ await runWriteHook(hooks, "after", {
7397
+ table: tableName,
7398
+ operation: "insert",
7399
+ single: false,
7400
+ args,
7401
+ rows: result,
7402
+ context,
7403
+ info,
7404
+ tx: executor
7405
+ });
7406
+ const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, result, pkNames, withParams) : result;
7407
+ return remapToGraphQLArrayOutput(enriched, tableName, table, relationMap);
7408
+ },
7409
+ !!hooks
7410
+ );
6701
7411
  } catch (e) {
6702
7412
  throw toGraphQLError(e);
6703
7413
  }
6704
7414
  },
6705
7415
  args: queryArgs
6706
7416
  };
6707
- };
6708
- var generateInsertSingle3 = (db, tableName, table, tables, relationMap, baseType, fieldName, typeName, typeNameMapper, conflictDoNothing = false, txCtx, nested, limits, policies) => {
6709
- const queryArgs = {
6710
- values: {
6711
- type: new GraphQLNonNull8(baseType)
6712
- }
6713
- };
6714
- const pkNames = sqlitePrimaryKeyPropNames(table);
6715
- return {
6716
- name: fieldName,
6717
- resolver: async (_source, args, context, info) => {
6718
- try {
6719
- return await runMutation(db, context, info, txCtx, async (executor) => {
6720
- const entry = nested?.enabled(tableName) ? nested.split(tableName, args.values) : void 0;
6721
- const nestedEntry = entry && nested.hasOps(entry.ops) ? entry : void 0;
6722
- const contextColumns = policies?.contextValues?.(tableName);
6723
- const scope = policies?.scope?.(context);
6724
- const input = nestedEntry ? {} : applyContextValues(
6725
- remapFromGraphQLSingleInput(entry ? entry.columns : args.values, table),
6726
- contextColumns,
6727
- context
6728
- );
6729
- const parsedInfo = parseResolveInfo4(info, {
6730
- deep: true
6731
- });
6732
- const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
6733
- relationMap,
6734
- tables,
6735
- tableName,
6736
- typeName,
6737
- typeNameMapper,
6738
- table,
6739
- pkNames,
6740
- parsedInfo,
6741
- limits,
6742
- scope
6743
- });
6744
- const returning = nestedEntry ? nested.withJoinColumns(tableName, nestedEntry.ops, { ...columns }, table) : columns;
6745
- const runInsert = async (target, values) => {
6746
- let query = target.insert(table).values(values).returning(returning);
6747
- if (conflictDoNothing) {
6748
- query = query.onConflictDoNothing();
6749
- }
6750
- return await query;
6751
- };
6752
- const result = nestedEntry ? await writeWithNestedOps({
6753
- executor,
6754
- runtime: nested,
6755
- tableName,
6756
- entries: [nestedEntry],
6757
- remapValues: (values) => applyContextValues(remapFromGraphQLSingleInput(values, table), contextColumns, context),
6758
- write: runInsert,
6759
- context
6760
- }) : await runInsert(executor, input);
6761
- if (!result[0]) {
6762
- if (!conflictDoNothing) {
6763
- throw new GraphQLError9(`${fieldName}: the insert returned no row.`);
7417
+ };
7418
+ var generateInsertSingle3 = (db, tableName, table, tables, relationMap, baseType, fieldName, typeName, typeNameMapper, conflictDoNothing = false, txCtx, nested, limits, policies) => {
7419
+ const queryArgs = {
7420
+ values: {
7421
+ type: new GraphQLNonNull8(baseType)
7422
+ }
7423
+ };
7424
+ const pkNames = sqlitePrimaryKeyPropNames(table);
7425
+ const hooks = policies?.onWrite?.(tableName, "insert");
7426
+ return {
7427
+ name: fieldName,
7428
+ resolver: async (_source, args, context, info) => {
7429
+ try {
7430
+ return await runMutation(
7431
+ db,
7432
+ context,
7433
+ info,
7434
+ txCtx,
7435
+ async (executor) => {
7436
+ await runWriteHook(hooks, "before", {
7437
+ table: tableName,
7438
+ operation: "insert",
7439
+ single: true,
7440
+ args,
7441
+ context,
7442
+ info,
7443
+ tx: executor
7444
+ });
7445
+ const entry = nested?.enabled(tableName) ? nested.split(tableName, args.values) : void 0;
7446
+ const nestedEntry = entry && nested.hasOps(entry.ops) ? entry : void 0;
7447
+ const contextColumns = policies?.contextValues?.(tableName);
7448
+ const scope = policies?.scope?.(context);
7449
+ const input = nestedEntry ? {} : applyContextValues(
7450
+ remapFromGraphQLSingleInput(entry ? entry.columns : args.values, table),
7451
+ contextColumns,
7452
+ context
7453
+ );
7454
+ const parsedInfo = parseResolveInfo4(info, {
7455
+ deep: true
7456
+ });
7457
+ const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
7458
+ relationMap,
7459
+ tables,
7460
+ tableName,
7461
+ typeName,
7462
+ typeNameMapper,
7463
+ table,
7464
+ pkNames,
7465
+ parsedInfo,
7466
+ limits,
7467
+ scope
7468
+ });
7469
+ const returning = nestedEntry ? nested.withJoinColumns(tableName, nestedEntry.ops, { ...columns }, table) : columns;
7470
+ const runInsert = async (target, values) => {
7471
+ let query = target.insert(table).values(values).returning(returning);
7472
+ if (conflictDoNothing) {
7473
+ query = query.onConflictDoNothing();
7474
+ }
7475
+ return await query;
7476
+ };
7477
+ const result = nestedEntry ? await writeWithNestedOps({
7478
+ executor,
7479
+ runtime: nested,
7480
+ tableName,
7481
+ entries: [nestedEntry],
7482
+ remapValues: (values) => applyContextValues(remapFromGraphQLSingleInput(values, table), contextColumns, context),
7483
+ write: runInsert,
7484
+ context
7485
+ }) : await runInsert(executor, input);
7486
+ await runWriteHook(hooks, "after", {
7487
+ table: tableName,
7488
+ operation: "insert",
7489
+ single: true,
7490
+ args,
7491
+ rows: result,
7492
+ context,
7493
+ info,
7494
+ tx: executor
7495
+ });
7496
+ if (!result[0]) {
7497
+ if (!conflictDoNothing) {
7498
+ throw new GraphQLError9(`${fieldName}: the insert returned no row.`);
7499
+ }
7500
+ return void 0;
6764
7501
  }
6765
- return void 0;
6766
- }
6767
- const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, result, pkNames, withParams) : result;
6768
- return remapToGraphQLSingleOutput(enriched[0], tableName, table, relationMap);
6769
- });
7502
+ const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, result, pkNames, withParams) : result;
7503
+ return remapToGraphQLSingleOutput(enriched[0], tableName, table, relationMap);
7504
+ },
7505
+ !!hooks
7506
+ );
6770
7507
  } catch (e) {
6771
7508
  throw toGraphQLError(e);
6772
7509
  }
@@ -6785,69 +7522,96 @@ var generateUpsert3 = (db, tableName, table, tables, relationMap, baseType, onCo
6785
7522
  }
6786
7523
  };
6787
7524
  const pkNames = sqlitePrimaryKeyPropNames(table);
7525
+ const hooks = policies?.onWrite?.(tableName, "upsert");
6788
7526
  return {
6789
7527
  name: fieldName,
6790
7528
  resolver: async (_source, args, context, info) => {
6791
7529
  try {
6792
- return await runMutation(db, context, info, txCtx, async (executor) => {
6793
- const supplied = single ? [args.values] : args.values;
6794
- if (!supplied.length) {
6795
- throw new GraphQLError9("No values were provided!");
6796
- }
6797
- const entries = nested?.enabled(tableName) ? supplied.map((values) => nested.split(tableName, values)) : void 0;
6798
- const nestedEntries = entries?.some((entry) => nested.hasOps(entry.ops)) ? entries : void 0;
6799
- const contextColumns = policies?.contextValues?.(tableName);
6800
- const scope = policies?.scope?.(context);
6801
- const input = nestedEntries ? [] : applyContextValuesAll(
6802
- remapFromGraphQLArrayInput(entries ? entries.map((entry) => entry.columns) : supplied, table),
6803
- contextColumns,
6804
- context
6805
- );
6806
- const parsedInfo = parseResolveInfo4(info, { deep: true });
6807
- const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
6808
- relationMap,
6809
- tables,
6810
- tableName,
6811
- typeName,
6812
- typeNameMapper,
6813
- table,
6814
- pkNames,
6815
- parsedInfo,
6816
- limits,
6817
- scope
6818
- });
6819
- const returning = nestedEntries ? nested.withJoinColumns(tableName, mergedOps(nestedEntries), { ...columns }, table) : columns;
6820
- const runUpsert = async (target, values) => {
6821
- const plan = resolveConflictPlan({
7530
+ return await runMutation(
7531
+ db,
7532
+ context,
7533
+ info,
7534
+ txCtx,
7535
+ async (executor) => {
7536
+ const supplied = single ? [args.values] : args.values;
7537
+ if (!supplied.length) {
7538
+ throw new GraphQLError9("No values were provided!");
7539
+ }
7540
+ await runWriteHook(hooks, "before", {
7541
+ table: tableName,
7542
+ operation: "upsert",
7543
+ single,
7544
+ args,
7545
+ context,
7546
+ info,
7547
+ tx: executor
7548
+ });
7549
+ const entries = nested?.enabled(tableName) ? supplied.map((values) => nested.split(tableName, values)) : void 0;
7550
+ const nestedEntries = entries?.some((entry) => nested.hasOps(entry.ops)) ? entries : void 0;
7551
+ const contextColumns = policies?.contextValues?.(tableName);
7552
+ const scope = policies?.scope?.(context);
7553
+ const input = nestedEntries ? [] : applyContextValuesAll(
7554
+ remapFromGraphQLArrayInput(entries ? entries.map((entry) => entry.columns) : supplied, table),
7555
+ contextColumns,
7556
+ context
7557
+ );
7558
+ const parsedInfo = parseResolveInfo4(info, { deep: true });
7559
+ const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
7560
+ relationMap,
7561
+ tables,
7562
+ tableName,
7563
+ typeName,
7564
+ typeNameMapper,
6822
7565
  table,
6823
- values,
6824
- onConflict: args.onConflict,
6825
7566
  pkNames,
6826
- uniqueSets,
6827
- excludedRef: excludedColumnRef,
6828
- withTarget: true,
6829
- buildWhere: (where) => extractFilters(table, tableName, where, relationFilterCtx(filterCtx, tableName))
7567
+ parsedInfo,
7568
+ limits,
7569
+ scope
6830
7570
  });
6831
- let query = target.insert(table).values(values).returning(returning);
6832
- const setWhere = withScope(scope, tableName, table, plan.setWhere);
6833
- query = plan.action === "NOTHING" ? query.onConflictDoNothing(plan.target ? { target: plan.target } : void 0) : query.onConflictDoUpdate({ target: plan.target, set: plan.set, setWhere });
6834
- return await query;
6835
- };
6836
- const result = nestedEntries ? await writeWithNestedOps({
6837
- executor,
6838
- runtime: nested,
6839
- tableName,
6840
- entries: nestedEntries,
6841
- remapValues: (values) => applyContextValues(remapFromGraphQLSingleInput(values, table), contextColumns, context),
6842
- write: (tx, values) => runUpsert(tx, [values]),
6843
- context
6844
- }) : await runUpsert(executor, input);
6845
- if (single && !result[0]) {
6846
- return void 0;
6847
- }
6848
- const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, result, pkNames, withParams) : result;
6849
- return single ? remapToGraphQLSingleOutput(enriched[0], tableName, table, relationMap) : remapToGraphQLArrayOutput(enriched, tableName, table, relationMap);
6850
- });
7571
+ const returning = nestedEntries ? nested.withJoinColumns(tableName, mergedOps(nestedEntries), { ...columns }, table) : columns;
7572
+ const runUpsert = async (target, values) => {
7573
+ const plan = resolveConflictPlan({
7574
+ table,
7575
+ values,
7576
+ onConflict: args.onConflict,
7577
+ pkNames,
7578
+ uniqueSets,
7579
+ excludedRef: excludedColumnRef,
7580
+ withTarget: true,
7581
+ buildWhere: (where) => extractFilters(table, tableName, where, relationFilterCtx(filterCtx, tableName))
7582
+ });
7583
+ let query = target.insert(table).values(values).returning(returning);
7584
+ const setWhere = withScope(scope, tableName, table, plan.setWhere);
7585
+ query = plan.action === "NOTHING" ? query.onConflictDoNothing(plan.target ? { target: plan.target } : void 0) : query.onConflictDoUpdate({ target: plan.target, set: plan.set, setWhere });
7586
+ return await query;
7587
+ };
7588
+ const result = nestedEntries ? await writeWithNestedOps({
7589
+ executor,
7590
+ runtime: nested,
7591
+ tableName,
7592
+ entries: nestedEntries,
7593
+ remapValues: (values) => applyContextValues(remapFromGraphQLSingleInput(values, table), contextColumns, context),
7594
+ write: (tx, values) => runUpsert(tx, [values]),
7595
+ context
7596
+ }) : await runUpsert(executor, input);
7597
+ await runWriteHook(hooks, "after", {
7598
+ table: tableName,
7599
+ operation: "upsert",
7600
+ single,
7601
+ args,
7602
+ rows: result,
7603
+ context,
7604
+ info,
7605
+ tx: executor
7606
+ });
7607
+ if (single && !result[0]) {
7608
+ return void 0;
7609
+ }
7610
+ const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, result, pkNames, withParams) : result;
7611
+ return single ? remapToGraphQLSingleOutput(enriched[0], tableName, table, relationMap) : remapToGraphQLArrayOutput(enriched, tableName, table, relationMap);
7612
+ },
7613
+ !!hooks
7614
+ );
6851
7615
  } catch (e) {
6852
7616
  throw toGraphQLError(e);
6853
7617
  }
@@ -6865,75 +7629,102 @@ var generateUpdate3 = (db, tableName, table, tables, relationMap, setArgs, filte
6865
7629
  }
6866
7630
  };
6867
7631
  const pkNames = sqlitePrimaryKeyPropNames(table);
7632
+ const hooks = policies?.onWrite?.(tableName, "update");
6868
7633
  return {
6869
7634
  name: fieldName,
6870
7635
  resolver: async (_source, args, context, info) => {
6871
7636
  try {
6872
- return await runMutation(db, context, info, txCtx, async (executor) => {
6873
- const { where, set } = args;
6874
- const scope = policies?.scope?.(context);
6875
- const parsedInfo = parseResolveInfo4(info, {
6876
- deep: true
6877
- });
6878
- const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
6879
- relationMap,
6880
- tables,
6881
- tableName,
6882
- typeName,
6883
- typeNameMapper,
6884
- table,
6885
- pkNames,
6886
- parsedInfo,
6887
- limits,
6888
- scope
6889
- });
6890
- const entry = nested?.enabled(tableName) ? nested.split(tableName, set) : void 0;
6891
- const nestedOps = entry && nested.hasOps(entry.ops) ? entry.ops : void 0;
6892
- const input = stripContextValues(
6893
- remapUpdateInput(entry ? entry.columns : set, table, tableName),
6894
- policies?.contextValues?.(tableName)
6895
- );
6896
- if (!Object.keys(input).length && !nestedOps) {
6897
- throw new GraphQLError9("Unable to update with no values specified!");
6898
- }
6899
- const relationCtx = relationFilterCtx(filterCtx, tableName);
6900
- const filters = withScope(
6901
- scope,
6902
- tableName,
6903
- table,
6904
- single || requireWhere ? extractRequiredFilters(table, tableName, where, fieldName, relationCtx) : where ? extractFilters(table, tableName, where, relationCtx) : void 0
6905
- );
6906
- if (single) {
6907
- await assertSingleMatch(executor, table, filters, fieldName);
6908
- }
6909
- const returning = nestedOps ? nested.withJoinColumns(tableName, nestedOps, { ...columns }, table) : columns;
6910
- const runUpdate = async (target, values) => {
6911
- const writes = Object.keys(values).length > 0;
6912
- let query = writes ? target.update(table).set(values) : target.select(returning).from(table);
6913
- if (filters) {
6914
- query = query.where(filters);
7637
+ return await runMutation(
7638
+ db,
7639
+ context,
7640
+ info,
7641
+ txCtx,
7642
+ async (executor) => {
7643
+ await runWriteHook(hooks, "before", {
7644
+ table: tableName,
7645
+ operation: "update",
7646
+ single,
7647
+ args,
7648
+ context,
7649
+ info,
7650
+ tx: executor
7651
+ });
7652
+ const { where, set } = args;
7653
+ const scope = policies?.scope?.(context);
7654
+ const parsedInfo = parseResolveInfo4(info, {
7655
+ deep: true
7656
+ });
7657
+ const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
7658
+ relationMap,
7659
+ tables,
7660
+ tableName,
7661
+ typeName,
7662
+ typeNameMapper,
7663
+ table,
7664
+ pkNames,
7665
+ parsedInfo,
7666
+ limits,
7667
+ scope
7668
+ });
7669
+ const entry = nested?.enabled(tableName) ? nested.split(tableName, set) : void 0;
7670
+ const nestedOps = entry && nested.hasOps(entry.ops) ? entry.ops : void 0;
7671
+ const input = stripContextValues(
7672
+ remapUpdateInput(entry ? entry.columns : set, table, tableName),
7673
+ policies?.contextValues?.(tableName)
7674
+ );
7675
+ if (!Object.keys(input).length && !nestedOps) {
7676
+ throw new GraphQLError9("Unable to update with no values specified!");
6915
7677
  }
6916
- return await (writes ? query.returning(returning) : query);
6917
- };
6918
- const result = nestedOps ? await updateWithNestedOps({
6919
- executor,
6920
- runtime: nested,
6921
- tableName,
6922
- columns: input,
6923
- ops: nestedOps,
6924
- remapValues: (values) => values,
6925
- write: runUpdate,
6926
- context
6927
- }) : await runUpdate(executor, input);
6928
- if (single && result.length > 1) {
6929
- throw new GraphQLError9(`${fieldName}: 'where' matched more than one row!`);
6930
- }
6931
- if (single && !result[0]) {
6932
- return void 0;
6933
- }
6934
- const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, result, pkNames, withParams) : result;
6935
- return single ? remapToGraphQLSingleOutput(enriched[0], tableName, table, relationMap) : remapToGraphQLArrayOutput(enriched, tableName, table, relationMap);
6936
- });
7678
+ const relationCtx = relationFilterCtx(filterCtx, tableName);
7679
+ const filters = withScope(
7680
+ scope,
7681
+ tableName,
7682
+ table,
7683
+ single || requireWhere ? extractRequiredFilters(table, tableName, where, fieldName, relationCtx) : where ? extractFilters(table, tableName, where, relationCtx) : void 0
7684
+ );
7685
+ if (single) {
7686
+ await assertSingleMatch(executor, table, filters, fieldName);
7687
+ }
7688
+ const returning = nestedOps ? nested.withJoinColumns(tableName, nestedOps, { ...columns }, table) : columns;
7689
+ const runUpdate = async (target, values) => {
7690
+ const writes = Object.keys(values).length > 0;
7691
+ let query = writes ? target.update(table).set(values) : target.select(returning).from(table);
7692
+ if (filters) {
7693
+ query = query.where(filters);
7694
+ }
7695
+ return await (writes ? query.returning(returning) : query);
7696
+ };
7697
+ const result = nestedOps ? await updateWithNestedOps({
7698
+ executor,
7699
+ runtime: nested,
7700
+ tableName,
7701
+ columns: input,
7702
+ ops: nestedOps,
7703
+ remapValues: (values) => values,
7704
+ write: runUpdate,
7705
+ context
7706
+ }) : await runUpdate(executor, input);
7707
+ await runWriteHook(hooks, "after", {
7708
+ table: tableName,
7709
+ operation: "update",
7710
+ single,
7711
+ args,
7712
+ rows: result,
7713
+ context,
7714
+ info,
7715
+ tx: executor
7716
+ });
7717
+ if (single && result.length > 1) {
7718
+ throw new GraphQLError9(`${fieldName}: 'where' matched more than one row!`);
7719
+ }
7720
+ if (single && !result[0]) {
7721
+ return void 0;
7722
+ }
7723
+ const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, result, pkNames, withParams) : result;
7724
+ return single ? remapToGraphQLSingleOutput(enriched[0], tableName, table, relationMap) : remapToGraphQLArrayOutput(enriched, tableName, table, relationMap);
7725
+ },
7726
+ !!hooks
7727
+ );
6937
7728
  } catch (e) {
6938
7729
  throw toGraphQLError(e);
6939
7730
  }
@@ -6948,122 +7739,149 @@ var generateUpdateMany3 = (db, tableName, table, tables, relationMap, updateMany
6948
7739
  }
6949
7740
  };
6950
7741
  const pkNames = sqlitePrimaryKeyPropNames(table);
7742
+ const hooks = policies?.onWrite?.(tableName, "updateMany");
6951
7743
  return {
6952
7744
  name: fieldName,
6953
7745
  resolver: async (_source, args, context, info) => {
6954
7746
  try {
6955
- return await runMutation(db, context, info, txCtx, async (executor) => {
6956
- const { updates } = args;
6957
- if (!updates.length) {
6958
- throw new GraphQLError9("No updates were provided!");
6959
- }
6960
- const scope = policies?.scope?.(context);
6961
- const contextColumns = policies?.contextValues?.(tableName);
6962
- const parsedInfo = parseResolveInfo4(info, {
6963
- deep: true
6964
- });
6965
- const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
6966
- relationMap,
6967
- tables,
6968
- tableName,
6969
- typeName,
6970
- typeNameMapper,
6971
- table,
6972
- pkNames,
6973
- parsedInfo,
6974
- limits,
6975
- scope
6976
- });
6977
- const entries = updates.map(({ where, set }) => {
6978
- const split = nested?.enabled(tableName) ? nested.split(tableName, set) : void 0;
6979
- const ops = split && nested.hasOps(split.ops) ? split.ops : void 0;
6980
- const input = stripContextValues(
6981
- remapUpdateInput(split ? split.columns : set, table, tableName),
6982
- contextColumns
6983
- );
6984
- if (!Object.keys(input).length && !ops) {
6985
- throw new GraphQLError9("Unable to update with no values specified!");
7747
+ return await runMutation(
7748
+ db,
7749
+ context,
7750
+ info,
7751
+ txCtx,
7752
+ async (executor) => {
7753
+ const { updates } = args;
7754
+ if (!updates.length) {
7755
+ throw new GraphQLError9("No updates were provided!");
6986
7756
  }
6987
- return {
6988
- set: input,
6989
- ops,
6990
- filters: withScope(
6991
- scope,
6992
- tableName,
6993
- table,
6994
- where ? extractFilters(table, tableName, where, relationFilterCtx(filterCtx, tableName)) : void 0
6995
- )
7757
+ await runWriteHook(hooks, "before", {
7758
+ table: tableName,
7759
+ operation: "updateMany",
7760
+ single: false,
7761
+ args,
7762
+ context,
7763
+ info,
7764
+ tx: executor
7765
+ });
7766
+ const scope = policies?.scope?.(context);
7767
+ const contextColumns = policies?.contextValues?.(tableName);
7768
+ const parsedInfo = parseResolveInfo4(info, {
7769
+ deep: true
7770
+ });
7771
+ const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
7772
+ relationMap,
7773
+ tables,
7774
+ tableName,
7775
+ typeName,
7776
+ typeNameMapper,
7777
+ table,
7778
+ pkNames,
7779
+ parsedInfo,
7780
+ limits,
7781
+ scope
7782
+ });
7783
+ const entries = updates.map(({ where, set }) => {
7784
+ const split = nested?.enabled(tableName) ? nested.split(tableName, set) : void 0;
7785
+ const ops = split && nested.hasOps(split.ops) ? split.ops : void 0;
7786
+ const input = stripContextValues(
7787
+ remapUpdateInput(split ? split.columns : set, table, tableName),
7788
+ contextColumns
7789
+ );
7790
+ if (!Object.keys(input).length && !ops) {
7791
+ throw new GraphQLError9("Unable to update with no values specified!");
7792
+ }
7793
+ return {
7794
+ set: input,
7795
+ ops,
7796
+ filters: withScope(
7797
+ scope,
7798
+ tableName,
7799
+ table,
7800
+ where ? extractFilters(table, tableName, where, relationFilterCtx(filterCtx, tableName)) : void 0
7801
+ )
7802
+ };
7803
+ });
7804
+ const anyNested = entries.some((entry) => entry.ops);
7805
+ const returning = anyNested ? nested.withJoinColumns(
7806
+ tableName,
7807
+ mergedOps(entries.map((entry) => ({ ops: entry.ops ?? {} }))),
7808
+ { ...columns },
7809
+ table
7810
+ ) : columns;
7811
+ const runEntry = (tx, entry) => {
7812
+ let query = tx.update(table).set(entry.set);
7813
+ if (entry.filters) {
7814
+ query = query.where(entry.filters);
7815
+ }
7816
+ return query.returning(returning).all();
6996
7817
  };
6997
- });
6998
- const anyNested = entries.some((entry) => entry.ops);
6999
- const returning = anyNested ? nested.withJoinColumns(
7000
- tableName,
7001
- mergedOps(entries.map((entry) => ({ ops: entry.ops ?? {} }))),
7002
- { ...columns },
7003
- table
7004
- ) : columns;
7005
- const runEntry = (tx, entry) => {
7006
- let query = tx.update(table).set(entry.set);
7007
- if (entry.filters) {
7008
- query = query.where(entry.filters);
7009
- }
7010
- return query.returning(returning).all();
7011
- };
7012
- const runNestedEntry = async (tx, entry) => {
7013
- const values = entry.ops ? { ...entry.set, ...await nested.applyParentSide(tx, tableName, entry.ops, context) } : entry.set;
7014
- const writes = Object.keys(values).length > 0;
7015
- let query = writes ? tx.update(table).set(values) : tx.select(returning).from(table);
7016
- if (entry.filters) {
7017
- query = query.where(entry.filters);
7018
- }
7019
- const rows = await (writes ? query.returning(returning) : query);
7020
- if (entry.ops) {
7021
- await nested.applyChildSide(tx, tableName, entry.ops, rows, context);
7022
- }
7023
- return rows;
7024
- };
7025
- const perEntry = await executor.transaction((tx) => {
7026
- if (anyNested) {
7027
- return (async () => {
7028
- const results2 = [];
7029
- for (const entry of entries) {
7030
- results2.push(await runNestedEntry(tx, entry));
7031
- }
7032
- return results2;
7033
- })();
7034
- }
7035
- const first = runEntry(tx, entries[0]);
7036
- if (typeof first?.then === "function") {
7037
- return (async () => {
7038
- const results2 = [await first];
7039
- for (let i = 1; i < entries.length; i++) {
7040
- results2.push(await runEntry(tx, entries[i]));
7041
- }
7042
- return results2;
7043
- })();
7044
- }
7045
- const results = [first];
7046
- for (let i = 1; i < entries.length; i++) {
7047
- results.push(runEntry(tx, entries[i]));
7048
- }
7049
- return results;
7050
- });
7051
- const flatRows = perEntry.flat();
7052
- const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, flatRows, pkNames, withParams) : flatRows;
7053
- const output = [];
7054
- let offset = 0;
7055
- for (const rows of perEntry) {
7056
- if (!rows.length) {
7057
- output.push(null);
7058
- continue;
7059
- }
7060
- for (let i = 0; i < rows.length; i++) {
7061
- output.push(remapToGraphQLSingleOutput(enriched[offset + i], tableName, table, relationMap));
7818
+ const runNestedEntry = async (tx, entry) => {
7819
+ const values = entry.ops ? { ...entry.set, ...await nested.applyParentSide(tx, tableName, entry.ops, context) } : entry.set;
7820
+ const writes = Object.keys(values).length > 0;
7821
+ let query = writes ? tx.update(table).set(values) : tx.select(returning).from(table);
7822
+ if (entry.filters) {
7823
+ query = query.where(entry.filters);
7824
+ }
7825
+ const rows = await (writes ? query.returning(returning) : query);
7826
+ if (entry.ops) {
7827
+ await nested.applyChildSide(tx, tableName, entry.ops, rows, context);
7828
+ }
7829
+ return rows;
7830
+ };
7831
+ const perEntry = await executor.transaction((tx) => {
7832
+ if (anyNested) {
7833
+ return (async () => {
7834
+ const results2 = [];
7835
+ for (const entry of entries) {
7836
+ results2.push(await runNestedEntry(tx, entry));
7837
+ }
7838
+ return results2;
7839
+ })();
7840
+ }
7841
+ const first = runEntry(tx, entries[0]);
7842
+ if (typeof first?.then === "function") {
7843
+ return (async () => {
7844
+ const results2 = [await first];
7845
+ for (let i = 1; i < entries.length; i++) {
7846
+ results2.push(await runEntry(tx, entries[i]));
7847
+ }
7848
+ return results2;
7849
+ })();
7850
+ }
7851
+ const results = [first];
7852
+ for (let i = 1; i < entries.length; i++) {
7853
+ results.push(runEntry(tx, entries[i]));
7854
+ }
7855
+ return results;
7856
+ });
7857
+ const flatRows = perEntry.flat();
7858
+ await runWriteHook(hooks, "after", {
7859
+ table: tableName,
7860
+ operation: "updateMany",
7861
+ single: false,
7862
+ args,
7863
+ rows: flatRows,
7864
+ context,
7865
+ info,
7866
+ tx: executor
7867
+ });
7868
+ const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, flatRows, pkNames, withParams) : flatRows;
7869
+ const output = [];
7870
+ let offset = 0;
7871
+ for (const rows of perEntry) {
7872
+ if (!rows.length) {
7873
+ output.push(null);
7874
+ continue;
7875
+ }
7876
+ for (let i = 0; i < rows.length; i++) {
7877
+ output.push(remapToGraphQLSingleOutput(enriched[offset + i], tableName, table, relationMap));
7878
+ }
7879
+ offset += rows.length;
7062
7880
  }
7063
- offset += rows.length;
7064
- }
7065
- return output;
7066
- });
7881
+ return output;
7882
+ },
7883
+ !!hooks
7884
+ );
7067
7885
  } catch (e) {
7068
7886
  throw toGraphQLError(e);
7069
7887
  }
@@ -7071,51 +7889,81 @@ var generateUpdateMany3 = (db, tableName, table, tables, relationMap, updateMany
7071
7889
  args: queryArgs
7072
7890
  };
7073
7891
  };
7074
- var generateDelete3 = (db, tableName, table, filterArgs, fieldName, typeName, single, requireWhere, filterCtx, selectionCtx, txCtx, policies) => {
7892
+ var generateDelete3 = (db, tableName, table, filterArgs, fieldName, typeName, single, requireWhere, filterCtx, selectionCtx, txCtx, policies, restore = false) => {
7893
+ const softDelete = policies?.softDelete?.(tableName);
7894
+ const operation = restore ? "restore" : "delete";
7075
7895
  const queryArgs = {
7076
7896
  where: {
7077
7897
  type: single || requireWhere ? new GraphQLNonNull8(filterArgs) : filterArgs
7078
7898
  }
7079
7899
  };
7900
+ const hooks = policies?.onWrite?.(tableName, operation);
7080
7901
  return {
7081
7902
  name: fieldName,
7082
7903
  resolver: async (_source, args, context, info) => {
7083
7904
  try {
7084
- return await runMutation(db, context, info, txCtx, async (executor) => {
7085
- const { where } = args;
7086
- const scope = policies?.scope?.(context);
7087
- const parsedInfo = parseResolveInfo4(info, {
7088
- deep: true
7089
- });
7090
- const columns = extractSelectedColumnsFromTreeSQLFormat(
7091
- parsedInfo.fieldsByTypeName[typeName],
7092
- table,
7093
- selectionCtx
7094
- );
7095
- const relationCtx = relationFilterCtx(filterCtx, tableName);
7096
- const filters = withScope(
7097
- scope,
7098
- tableName,
7099
- table,
7100
- single || requireWhere ? extractRequiredFilters(table, tableName, where, fieldName, relationCtx) : where ? extractFilters(table, tableName, where, relationCtx) : void 0
7101
- );
7102
- if (single) {
7103
- await assertSingleMatch(executor, table, filters, fieldName);
7104
- }
7105
- let query = executor.delete(table);
7106
- if (filters) {
7107
- query = query.where(filters);
7108
- }
7109
- query = query.returning(columns);
7110
- const result = await query;
7111
- if (single && result.length > 1) {
7112
- throw new GraphQLError9(`${fieldName}: 'where' matched more than one row!`);
7113
- }
7114
- if (single) {
7115
- return result[0] ? remapToGraphQLSingleOutput(result[0], tableName, table) : void 0;
7116
- }
7117
- return remapToGraphQLArrayOutput(result, tableName, table);
7118
- });
7905
+ return await runMutation(
7906
+ db,
7907
+ context,
7908
+ info,
7909
+ txCtx,
7910
+ async (executor) => {
7911
+ await runWriteHook(hooks, "before", {
7912
+ table: tableName,
7913
+ operation,
7914
+ single,
7915
+ args,
7916
+ context,
7917
+ info,
7918
+ tx: executor
7919
+ });
7920
+ const { where } = args;
7921
+ const scope = policies?.scope?.(context);
7922
+ const parsedInfo = parseResolveInfo4(info, {
7923
+ deep: true
7924
+ });
7925
+ const columns = extractSelectedColumnsFromTreeSQLFormat(
7926
+ parsedInfo.fieldsByTypeName[typeName],
7927
+ table,
7928
+ selectionCtx
7929
+ );
7930
+ const relationCtx = relationFilterCtx(filterCtx, tableName);
7931
+ const filters = withScope(
7932
+ scope,
7933
+ tableName,
7934
+ table,
7935
+ single || requireWhere ? extractRequiredFilters(table, tableName, where, fieldName, relationCtx) : where ? extractFilters(table, tableName, where, relationCtx) : void 0,
7936
+ restore ? "ONLY" : void 0
7937
+ );
7938
+ if (single) {
7939
+ await assertSingleMatch(executor, table, filters, fieldName);
7940
+ }
7941
+ let query = softDelete ? executor.update(table).set({ [softDelete.columnName]: restore ? softDelete.writeRestored : softDelete.writeDeleted() }) : executor.delete(table);
7942
+ if (filters) {
7943
+ query = query.where(filters);
7944
+ }
7945
+ query = query.returning(columns);
7946
+ const result = await query;
7947
+ await runWriteHook(hooks, "after", {
7948
+ table: tableName,
7949
+ operation,
7950
+ single,
7951
+ args,
7952
+ rows: result,
7953
+ context,
7954
+ info,
7955
+ tx: executor
7956
+ });
7957
+ if (single && result.length > 1) {
7958
+ throw new GraphQLError9(`${fieldName}: 'where' matched more than one row!`);
7959
+ }
7960
+ if (single) {
7961
+ return result[0] ? remapToGraphQLSingleOutput(result[0], tableName, table) : void 0;
7962
+ }
7963
+ return remapToGraphQLArrayOutput(result, tableName, table);
7964
+ },
7965
+ !!hooks
7966
+ );
7119
7967
  } catch (e) {
7120
7968
  throw toGraphQLError(e);
7121
7969
  }
@@ -7157,16 +8005,17 @@ var generateSchemaData3 = (db, schema, relations, options) => {
7157
8005
  attachTargetPrimaryKeys(namedRelations, tables, sqlitePrimaryKeyPropNames);
7158
8006
  const eagerRelations = pruneNonEagerRelations(namedRelations, shouldEagerLoad);
7159
8007
  const filterCtx = { tables, relationMap: namedRelations };
7160
- const scopes = options.policies?.scope;
7161
- const contextValuesOf = options.policies?.contextValues;
7162
- const policies = bindPolicies(options.policies, filterCtx);
8008
+ const tablePolicies = options.policies;
8009
+ const contextValuesOf = tablePolicies?.contextValues;
8010
+ const softDeleteOf = tablePolicies?.softDelete;
8011
+ const policies = bindPolicies(tablePolicies, filterCtx);
7163
8012
  const resolverFactory = createRelationResolverFactory(
7164
8013
  db,
7165
8014
  tables,
7166
8015
  "nulls-smallest",
7167
8016
  filterCtx,
7168
8017
  limits,
7169
- scopes
8018
+ tablePolicies
7170
8019
  );
7171
8020
  const cacheCtx = {
7172
8021
  genericFilterCache: /* @__PURE__ */ new Map(),
@@ -7185,6 +8034,7 @@ var generateSchemaData3 = (db, schema, relations, options) => {
7185
8034
  docs: options.docs ?? {},
7186
8035
  primaryKeyOf: (name) => tables[name] ? sqlitePrimaryKeyPropNames(tables[name]) : [],
7187
8036
  contextValuesOf,
8037
+ softDeleteOf,
7188
8038
  featureOf
7189
8039
  };
7190
8040
  if (features.nestedWrites && db.resultKind === "sync") {
@@ -7200,8 +8050,13 @@ var generateSchemaData3 = (db, schema, relations, options) => {
7200
8050
  extractRelationJoinColumns
7201
8051
  ) : void 0;
7202
8052
  const nestedTypes = nestedPlans ? createNestedWriteTypes({ plans: nestedPlans, cacheCtx, typeNameMapper, insertPrefix: prefixes.insert }) : void 0;
7203
- const nestedRuntime = nestedPlans ? createNestedWriteRuntime({ plans: nestedPlans, filterCtx, scopes, contextValues: contextValuesOf }) : void 0;
7204
- const relationAggregateFactory = anyTable("relationAggregates") ? createRelationAggregateFactory(db, tables, cacheCtx, typeNameMapper, filterCtx, scopes) : void 0;
8053
+ const nestedRuntime = nestedPlans ? createNestedWriteRuntime({
8054
+ plans: nestedPlans,
8055
+ filterCtx,
8056
+ policies: tablePolicies,
8057
+ contextValues: contextValuesOf
8058
+ }) : void 0;
8059
+ const relationAggregateFactory = anyTable("relationAggregates") ? createRelationAggregateFactory(db, tables, cacheCtx, typeNameMapper, filterCtx, tablePolicies) : void 0;
7205
8060
  const queries = {};
7206
8061
  const mutations = {};
7207
8062
  if (options.transactions && db.resultKind === "sync") {
@@ -7252,8 +8107,11 @@ var generateSchemaData3 = (db, schema, relations, options) => {
7252
8107
  updateCountFieldName,
7253
8108
  deleteFieldName,
7254
8109
  deleteSingleFieldName,
8110
+ restoreFieldName,
8111
+ restoreSingleFieldName,
7255
8112
  deleteCountFieldName
7256
8113
  } = computeResolverFieldNames(tableName, typeNameMapper, prefixes, suffixes);
8114
+ const softDeleteInfo = softDeleteOf?.(tableName);
7257
8115
  const selectArrGenerated = generateSelectArray3(
7258
8116
  db,
7259
8117
  tableName,
@@ -7444,6 +8302,36 @@ var generateSchemaData3 = (db, schema, relations, options) => {
7444
8302
  mutationTxCtx,
7445
8303
  policies
7446
8304
  ) : void 0;
8305
+ const restoreGenerated = softDeleteInfo ? generateDelete3(
8306
+ db,
8307
+ tableName,
8308
+ schema[tableName],
8309
+ tableFilters,
8310
+ restoreFieldName,
8311
+ typeName,
8312
+ false,
8313
+ tableFeatures.requireWhere,
8314
+ filterCtx,
8315
+ { tableName, relationMap: namedRelations, tables },
8316
+ mutationTxCtx,
8317
+ policies,
8318
+ true
8319
+ ) : void 0;
8320
+ const restoreSingleGenerated = softDeleteInfo ? generateDelete3(
8321
+ db,
8322
+ tableName,
8323
+ schema[tableName],
8324
+ tableFilters,
8325
+ restoreSingleFieldName,
8326
+ typeName,
8327
+ true,
8328
+ tableFeatures.requireWhere,
8329
+ filterCtx,
8330
+ { tableName, relationMap: namedRelations, tables },
8331
+ mutationTxCtx,
8332
+ policies,
8333
+ true
8334
+ ) : void 0;
7447
8335
  const updateCountGenerated = tableFeatures.update && tableFeatures.countMutations ? generateWriteCount({
7448
8336
  db,
7449
8337
  tableName,
@@ -7477,7 +8365,7 @@ var generateSchemaData3 = (db, schema, relations, options) => {
7477
8365
  aggregateFieldName,
7478
8366
  tableFilters,
7479
8367
  filterCtx,
7480
- scopes
8368
+ tablePolicies
7481
8369
  ) : void 0;
7482
8370
  const groupByType = tableFeatures.aggregates && tableFeatures.groupBy ? generateGroupByType(schema[tableName], tableName, typeName, cacheCtx) : void 0;
7483
8371
  const groupByEnum = groupByType ? generateGroupByEnum(schema[tableName], tableName, typeName) : void 0;
@@ -7492,7 +8380,7 @@ var generateSchemaData3 = (db, schema, relations, options) => {
7492
8380
  groupByEnum,
7493
8381
  havingInput,
7494
8382
  filterCtx,
7495
- scopes
8383
+ tablePolicies
7496
8384
  ) : void 0;
7497
8385
  queries[selectArrGenerated.name] = {
7498
8386
  type: selectArrOutput,
@@ -7629,6 +8517,26 @@ var generateSchemaData3 = (db, schema, relations, options) => {
7629
8517
  }
7630
8518
  };
7631
8519
  }
8520
+ if (restoreGenerated) {
8521
+ mutations[restoreGenerated.name] = {
8522
+ type: arrTableItemOutput,
8523
+ args: restoreGenerated.args,
8524
+ resolve: restoreGenerated.resolver,
8525
+ extensions: {
8526
+ drizzle: drizzleMeta({ kind: "mutation", operation: "restore", single: false, targetArg: "where" })
8527
+ }
8528
+ };
8529
+ }
8530
+ if (restoreSingleGenerated) {
8531
+ mutations[restoreSingleGenerated.name] = {
8532
+ type: singleTableItemOutput,
8533
+ args: restoreSingleGenerated.args,
8534
+ resolve: restoreSingleGenerated.resolver,
8535
+ extensions: {
8536
+ drizzle: drizzleMeta({ kind: "mutation", operation: "restore", single: true, targetArg: "where" })
8537
+ }
8538
+ };
8539
+ }
7632
8540
  if (updateCountGenerated) {
7633
8541
  mutations[updateCountGenerated.name] = {
7634
8542
  type: new GraphQLNonNull8(GraphQLInt7),
@@ -7839,7 +8747,8 @@ var buildSchema = (db, config) => {
7839
8747
  insert: config?.prefixes?.insert ?? "create",
7840
8748
  delete: config?.prefixes?.delete ?? "delete",
7841
8749
  update: config?.prefixes?.update ?? "update",
7842
- upsert: config?.prefixes?.upsert ?? "upsert"
8750
+ upsert: config?.prefixes?.upsert ?? "upsert",
8751
+ restore: config?.prefixes?.restore ?? "restore"
7843
8752
  };
7844
8753
  const suffixes = {
7845
8754
  list: config?.suffixes?.list ?? "",
@@ -7917,9 +8826,58 @@ var buildSchema = (db, config) => {
7917
8826
  }
7918
8827
  }
7919
8828
  }
7920
- const policies = scopeConfig || contextValuesConfig ? {
8829
+ const softDeleteConfig = config?.softDelete;
8830
+ const softDeleteInfos = /* @__PURE__ */ new Map();
8831
+ if (softDeleteConfig) {
8832
+ if (typeof softDeleteConfig === "function") {
8833
+ for (const tableName of tableKeys) {
8834
+ const declaration = softDeleteConfig(schema[tableName], tableName);
8835
+ if (declaration) {
8836
+ softDeleteInfos.set(tableName, resolveSoftDeleteInfo(schema[tableName], tableName, declaration));
8837
+ }
8838
+ }
8839
+ } else {
8840
+ const tableNames = new Set(tableKeys);
8841
+ for (const [tableName, declaration] of Object.entries(softDeleteConfig)) {
8842
+ if (!tableNames.has(tableName)) {
8843
+ throw new Error(
8844
+ `Drizzle-GraphQL Error: config.softDelete names '${tableName}', which is not a table in the Drizzle schema.`
8845
+ );
8846
+ }
8847
+ softDeleteInfos.set(tableName, resolveSoftDeleteInfo(schema[tableName], tableName, declaration));
8848
+ }
8849
+ }
8850
+ }
8851
+ const onWriteConfig = config?.onWrite;
8852
+ const writeHooks = /* @__PURE__ */ new Map();
8853
+ let globalWriteHooks;
8854
+ if (onWriteConfig) {
8855
+ const isGlobal = typeof onWriteConfig === "function" || typeof onWriteConfig.before === "function" || typeof onWriteConfig.after === "function";
8856
+ if (isGlobal) {
8857
+ globalWriteHooks = normalizeWriteHooks(onWriteConfig);
8858
+ } else {
8859
+ const tableNames = new Set(tableKeys);
8860
+ for (const [tableName, hooks] of Object.entries(onWriteConfig)) {
8861
+ if (!tableNames.has(tableName)) {
8862
+ throw new Error(
8863
+ `Drizzle-GraphQL Error: config.onWrite names '${tableName}', which is not a table in the Drizzle schema.`
8864
+ );
8865
+ }
8866
+ if (typeof hooks !== "function" && typeof hooks !== "object") {
8867
+ throw new Error(
8868
+ `Drizzle-GraphQL Error: config.onWrite.${tableName} must be a function or an object with 'before' and/or 'after'.`
8869
+ );
8870
+ }
8871
+ writeHooks.set(tableName, normalizeWriteHooks(hooks));
8872
+ }
8873
+ }
8874
+ }
8875
+ const onWrite = globalWriteHooks || writeHooks.size ? (tableName) => globalWriteHooks ?? writeHooks.get(tableName) : void 0;
8876
+ const policies = scopeConfig || contextValuesConfig || softDeleteInfos.size || onWrite ? {
7921
8877
  scope: scopeConfig ? (tableName) => scopeConfig[tableName] : void 0,
7922
- contextValues: contextValuesConfig ? (tableName) => contextValuesConfig[tableName] : void 0
8878
+ contextValues: contextValuesConfig ? (tableName) => contextValuesConfig[tableName] : void 0,
8879
+ softDelete: softDeleteInfos.size ? (tableName) => softDeleteInfos.get(tableName) : void 0,
8880
+ onWrite
7923
8881
  } : void 0;
7924
8882
  const exclude = config?.exclude;
7925
8883
  if (exclude) {