@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.cjs CHANGED
@@ -1095,7 +1095,7 @@ var runOnSharedTx = async (state, body) => {
1095
1095
  throw e;
1096
1096
  }
1097
1097
  };
1098
- var runMutation = async (db, context, info, txCtx, body) => {
1098
+ var runMutation = async (db, context, info, txCtx, body, forceTx) => {
1099
1099
  const contextIsObject = context !== null && (typeof context === "object" || typeof context === "function");
1100
1100
  const stateKey = contextIsObject ? context : info.operation;
1101
1101
  let state = txCtx?.states.get(stateKey);
@@ -1104,7 +1104,7 @@ var runMutation = async (db, context, info, txCtx, body) => {
1104
1104
  return body(context[drizzleExecutorKey]);
1105
1105
  }
1106
1106
  if (!txCtx || info.operation.operation !== "mutation") {
1107
- return body(db);
1107
+ return forceTx ? db.transaction((tx) => body(tx)) : body(db);
1108
1108
  }
1109
1109
  const keys = /* @__PURE__ */ new Set();
1110
1110
  const allKnown = collectRootMutationKeys(
@@ -1119,10 +1119,28 @@ var runMutation = async (db, context, info, txCtx, body) => {
1119
1119
  txCtx.states.set(stateKey, state);
1120
1120
  }
1121
1121
  if (state.kind === "passthrough") {
1122
- return body(resolveExecutor(db, context));
1122
+ const executor = resolveExecutor(db, context);
1123
+ return forceTx && executor === db ? db.transaction((tx) => body(tx)) : body(executor);
1123
1124
  }
1124
1125
  return runOnSharedTx(state, body);
1125
1126
  };
1127
+ var normalizeWriteHooks = (hooks) => {
1128
+ if (!hooks) {
1129
+ return void 0;
1130
+ }
1131
+ if (typeof hooks === "function") {
1132
+ return { after: hooks };
1133
+ }
1134
+ const resolved = { before: hooks.before, after: hooks.after };
1135
+ return resolved.before || resolved.after ? resolved : void 0;
1136
+ };
1137
+ var runWriteHook = (hooks, position, payload) => {
1138
+ const hook = position === "before" ? hooks?.before : hooks?.after;
1139
+ if (!hook) {
1140
+ return void 0;
1141
+ }
1142
+ return Promise.resolve(hook({ ...payload, position, rows: payload.rows ?? [] }));
1143
+ };
1126
1144
  var batchedPaginatedRelationQuery = async (db, targetTable, foreignCol, whereCondition, orderByArg, limit, offset, pkNames, orderCtx, whereArgs) => {
1127
1145
  const cols = (0, import_drizzle_orm4.getColumns)(targetTable);
1128
1146
  const orderExprs = [
@@ -1144,7 +1162,7 @@ var batchedPaginatedRelationQuery = async (db, targetTable, foreignCol, whereCon
1144
1162
  }
1145
1163
  return rows;
1146
1164
  };
1147
- var createRelationResolverFactory = (db, tables, nullOrdering, filterCtx, limits, scopes) => ({ tableName, relationName, relEntry, isOne }) => {
1165
+ var createRelationResolverFactory = (db, tables, nullOrdering, filterCtx, limits, policies) => ({ tableName, relationName, relEntry, isOne }) => {
1148
1166
  const parentTable = tables[tableName];
1149
1167
  const targetTableName = relEntry.targetTableName;
1150
1168
  const targetTable = tables[targetTableName];
@@ -1166,7 +1184,14 @@ var createRelationResolverFactory = (db, tables, nullOrdering, filterCtx, limits
1166
1184
  if (localValue == null) {
1167
1185
  return isOne ? null : [];
1168
1186
  }
1169
- const { where: whereArg, orderBy: orderByArg, limit: requestedLimit, offset, after } = args ?? {};
1187
+ const {
1188
+ where: whereArg,
1189
+ orderBy: orderByArg,
1190
+ limit: requestedLimit,
1191
+ offset,
1192
+ after,
1193
+ deleted
1194
+ } = args ?? {};
1170
1195
  const limit = applyLimitPolicy(requestedLimit, limitPolicy, `${tableName}.${relationName}`);
1171
1196
  const distinct = (args ?? {}).distinct?.length ? (args ?? {}).distinct : void 0;
1172
1197
  const cursorSelected = !isOne && !!info && selectsCursorField(info, targetTable);
@@ -1197,6 +1222,7 @@ var createRelationResolverFactory = (db, tables, nullOrdering, filterCtx, limits
1197
1222
  orderBy: orderByArg ?? null,
1198
1223
  limit: limit ?? null,
1199
1224
  offset: offset ?? null,
1225
+ deleted: deleted ?? null,
1200
1226
  after: after ?? null,
1201
1227
  distinct: distinct ?? null,
1202
1228
  cursor: cursorEntries ? 1 : 0
@@ -1205,7 +1231,7 @@ var createRelationResolverFactory = (db, tables, nullOrdering, filterCtx, limits
1205
1231
  const loader = getOrCreateLoader(context, loaderKey, async (parentIds) => {
1206
1232
  const executor = resolveExecutor(db, context);
1207
1233
  const uniqueIds = [...new Set(parentIds)];
1208
- const scope = resolveScope(scopes, context, filterCtx);
1234
+ const scope = resolveScope(policies, context, filterCtx);
1209
1235
  let whereCondition = withScope(
1210
1236
  scope,
1211
1237
  targetTableName,
@@ -1214,7 +1240,8 @@ var createRelationResolverFactory = (db, tables, nullOrdering, filterCtx, limits
1214
1240
  (0, import_drizzle_orm4.inArray)(foreignCol, uniqueIds),
1215
1241
  whereArg ? extractFilters(targetTable, targetTableName, whereArg, relationFilterCtx(filterCtx, targetTableName)) : void 0,
1216
1242
  cursorValues ? buildCursorCondition(targetTable, cursorEntries, cursorValues, nullOrdering) : void 0
1217
- )
1243
+ ),
1244
+ deleted
1218
1245
  );
1219
1246
  if (distinct) {
1220
1247
  const keys = await selectDistinctKeys({
@@ -2019,7 +2046,8 @@ var generateSelectFields = (tables, tableName, relationMap, fromTableName, fromR
2019
2046
  {
2020
2047
  type: isRequired ? new import_graphql5.GraphQLNonNull(relType) : relType,
2021
2048
  args: {
2022
- where: { type: relSelectData.filters }
2049
+ where: { type: relSelectData.filters },
2050
+ ...deletedArg(cacheCtx.softDeleteOf, targetTableName)
2023
2051
  },
2024
2052
  resolve,
2025
2053
  ...relationDocs,
@@ -2038,6 +2066,7 @@ var generateSelectFields = (tables, tableName, relationMap, fromTableName, fromR
2038
2066
  orderBy: { type: relSelectData.order },
2039
2067
  offset: { type: import_graphql5.GraphQLInt },
2040
2068
  limit: { type: import_graphql5.GraphQLInt },
2069
+ ...deletedArg(cacheCtx.softDeleteOf, targetTableName),
2041
2070
  after: {
2042
2071
  type: import_graphql5.GraphQLString,
2043
2072
  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)."
@@ -2065,7 +2094,8 @@ var generateSelectFields = (tables, tableName, relationMap, fromTableName, fromR
2065
2094
  {
2066
2095
  type: new import_graphql5.GraphQLNonNull(relationAggregate.type),
2067
2096
  args: {
2068
- where: { type: relSelectData.filters }
2097
+ where: { type: relSelectData.filters },
2098
+ ...deletedArg(cacheCtx.softDeleteOf, targetTableName)
2069
2099
  },
2070
2100
  resolve: relationAggregate.resolve,
2071
2101
  extensions: {
@@ -2121,7 +2151,10 @@ var generateTableTypes = (tableName, tables, relationMap, withReturning, relatio
2121
2151
  const columns = visibleColumns(table);
2122
2152
  const columnEntries = Object.entries(columns);
2123
2153
  const contextColumns = cacheCtx.contextValuesOf?.(tableName);
2124
- const writableEntries = contextColumns ? columnEntries.filter(([columnName]) => !(columnName in contextColumns)) : columnEntries;
2154
+ const markerColumn = cacheCtx.softDeleteOf?.(tableName)?.columnName;
2155
+ const writableEntries = contextColumns || markerColumn ? columnEntries.filter(
2156
+ ([columnName]) => !(contextColumns && columnName in contextColumns) && columnName !== markerColumn
2157
+ ) : columnEntries;
2125
2158
  const relaxedColumns = nestedWrites?.relaxedColumns(tableName);
2126
2159
  const insertFields = Object.fromEntries(
2127
2160
  writableEntries.map(([columnName, column]) => {
@@ -2663,40 +2696,104 @@ var extractFilters = (table, tableName, filters, relationCtx) => {
2663
2696
  }
2664
2697
  return variants.length ? variants.length > 1 ? (0, import_drizzle_orm4.and)(...variants) : variants[0] : void 0;
2665
2698
  };
2666
- var resolveScope = (scopes, context, filterCtx) => {
2667
- if (!scopes) {
2699
+ var deletedFilterEnum = new import_graphql5.GraphQLEnumType({
2700
+ name: "DeletedFilter",
2701
+ description: "Which rows a read over a soft-deleting table returns.",
2702
+ values: {
2703
+ EXCLUDE: { value: "EXCLUDE", description: "Only rows that are not marked deleted. The default." },
2704
+ INCLUDE: { value: "INCLUDE", description: "Marked and unmarked rows alike." },
2705
+ ONLY: { value: "ONLY", description: "Only rows that are marked deleted \u2014 a trash view." }
2706
+ }
2707
+ });
2708
+ var resolveSoftDeleteInfo = (table, tableName, declaration) => {
2709
+ const config = typeof declaration === "string" ? { column: declaration } : declaration;
2710
+ const columnName = config?.column;
2711
+ if (typeof columnName !== "string" || !columnName) {
2712
+ throw new Error(
2713
+ `Drizzle-GraphQL Error: config.softDelete.${tableName} must be a column name or an object with a 'column' property.`
2714
+ );
2715
+ }
2716
+ const column = (0, import_drizzle_orm4.getColumns)(table)[columnName];
2717
+ if (!column) {
2718
+ throw new Error(
2719
+ `Drizzle-GraphQL Error: config.softDelete names '${tableName}.${columnName}', which is not a column of that table.`
2720
+ );
2721
+ }
2722
+ const nullable = !column.notNull;
2723
+ const { type: baseType, constraint } = (0, import_drizzle_orm4.extractExtendedColumnType)(column);
2724
+ const hasDeleted = "deletedValue" in config && config.deletedValue !== void 0;
2725
+ 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;
2726
+ let marker;
2727
+ if (!nullable) {
2728
+ if (hasDeleted && typeof config.deletedValue !== "function") {
2729
+ marker = config.deletedValue;
2730
+ } else if (!hasDeleted && baseType === "boolean") {
2731
+ marker = true;
2732
+ } else {
2733
+ throw new Error(
2734
+ `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.`
2735
+ );
2736
+ }
2737
+ }
2738
+ const hasRestored = "restoredValue" in config;
2739
+ const writeRestored = hasRestored ? config.restoredValue : nullable ? null : baseType === "boolean" ? false : void 0;
2740
+ if (!nullable && writeRestored === void 0) {
2741
+ throw new Error(
2742
+ `Drizzle-GraphQL Error: config.softDelete.${tableName} marks a NOT NULL column ('${columnName}'), so 'restoredValue' must say what restoring writes back.`
2743
+ );
2744
+ }
2745
+ return { columnName, column, nullable, writeDeleted, writeRestored, marker };
2746
+ };
2747
+ var softDeletePredicate = (info, table, mode) => {
2748
+ if (mode === "INCLUDE") {
2749
+ return void 0;
2750
+ }
2751
+ const column = table?.[info.columnName] ?? info.column;
2752
+ if (info.nullable) {
2753
+ return mode === "ONLY" ? (0, import_drizzle_orm4.isNotNull)(column) : (0, import_drizzle_orm4.isNull)(column);
2754
+ }
2755
+ return mode === "ONLY" ? (0, import_drizzle_orm4.eq)(column, info.marker) : (0, import_drizzle_orm4.ne)(column, info.marker);
2756
+ };
2757
+ var resolveScope = (policies, context, filterCtx) => {
2758
+ const scopes = policies?.scope;
2759
+ const softDelete = policies?.softDelete;
2760
+ if (!scopes && !softDelete) {
2668
2761
  return void 0;
2669
2762
  }
2670
2763
  return {
2671
- has: (tableName) => !!scopes(tableName),
2672
- on: (tableName, table) => {
2673
- const hook = scopes(tableName);
2764
+ has: (tableName, mode) => !!scopes?.(tableName) || !!softDelete?.(tableName) && (mode ?? "EXCLUDE") !== "INCLUDE",
2765
+ on: (tableName, table, mode) => {
2766
+ const marked = softDelete?.(tableName);
2767
+ const deleted = marked ? softDeletePredicate(marked, table, mode ?? "EXCLUDE") : void 0;
2768
+ const hook = scopes?.(tableName);
2674
2769
  if (!hook) {
2675
- return void 0;
2770
+ return deleted;
2676
2771
  }
2677
2772
  const predicate = hook(context, table);
2678
2773
  if (predicate === void 0 || predicate === null) {
2679
- return void 0;
2774
+ return deleted;
2680
2775
  }
2681
2776
  if ((0, import_drizzle_orm4.is)(predicate, import_drizzle_orm4.SQL)) {
2682
- return predicate;
2777
+ return (0, import_drizzle_orm4.and)(deleted, predicate);
2683
2778
  }
2684
2779
  if (typeof predicate !== "object") {
2685
2780
  throw new import_graphql5.GraphQLError(
2686
2781
  `Drizzle-GraphQL Error: the scope for '${tableName}' returned a ${typeof predicate}. A scope returns a filter object, a Drizzle SQL expression, or undefined.`
2687
2782
  );
2688
2783
  }
2689
- return extractFilters(table, tableName, predicate, relationFilterCtx(filterCtx, tableName));
2784
+ return (0, import_drizzle_orm4.and)(deleted, extractFilters(table, tableName, predicate, relationFilterCtx(filterCtx, tableName)));
2690
2785
  }
2691
2786
  };
2692
2787
  };
2693
- var withScope = (scope, tableName, table, condition) => {
2694
- const predicate = scope?.on(tableName, table);
2788
+ var withScope = (scope, tableName, table, condition, mode) => {
2789
+ const predicate = scope?.on(tableName, table, mode);
2695
2790
  return predicate ? (0, import_drizzle_orm4.and)(condition, predicate) : condition;
2696
2791
  };
2697
- var bindPolicies = (policies, filterCtx) => policies?.scope || policies?.contextValues ? {
2698
- scope: policies.scope ? (context) => resolveScope(policies.scope, context, filterCtx) : void 0,
2699
- contextValues: policies.contextValues
2792
+ var bindPolicies = (policies, filterCtx) => policies?.scope || policies?.contextValues || policies?.softDelete || policies?.onWrite ? {
2793
+ scope: policies.scope || policies.softDelete ? (context) => resolveScope(policies, context, filterCtx) : void 0,
2794
+ contextValues: policies.contextValues,
2795
+ softDelete: policies.softDelete,
2796
+ onWrite: policies.onWrite
2700
2797
  } : void 0;
2701
2798
  var applyContextValues = (values, hooks, context) => {
2702
2799
  if (!hooks) {
@@ -2771,12 +2868,14 @@ var extractRelationsParamsInner = (relationMap, tables, tableName, typeName, ori
2771
2868
  const offset = relationArgs?.offset ?? void 0;
2772
2869
  const limit = (0, import_drizzle_orm4.is)(relEntry.relation, import_drizzle_orm4.One) ? relationArgs?.limit ?? void 0 : applyLimitPolicy(relationArgs?.limit, limits?.(targetTableName), `${tableName}.${relName}`);
2773
2870
  const relWhere = relationArgs?.where;
2774
- thisRecord.where = relWhere || scope?.has(targetTableName) ? {
2871
+ const relDeleted = relationArgs?.deleted;
2872
+ thisRecord.where = relWhere || scope?.has(targetTableName, relDeleted) ? {
2775
2873
  RAW: (aliasedTable2) => withScope(
2776
2874
  scope,
2777
2875
  targetTableName,
2778
2876
  aliasedTable2,
2779
- relWhere ? extractFilters(aliasedTable2, relName, relWhere, relationFilterCtx(filterCtx, targetTableName)) : void 0
2877
+ relWhere ? extractFilters(aliasedTable2, relName, relWhere, relationFilterCtx(filterCtx, targetTableName)) : void 0,
2878
+ relDeleted
2780
2879
  )
2781
2880
  } : void 0;
2782
2881
  const hasPagination = offset != null || limit != null;
@@ -3302,9 +3401,11 @@ var computeResolverFieldNames = (tableName, typeNameMapper, prefixes, suffixes)
3302
3401
  const updateFieldName = `${prefixes.update}${mapped ? capitalize(mapped.singular) : capitalize(tableName)}`;
3303
3402
  const updateManyFieldName = `${prefixes.update}${mapped ? capitalize(mapped.plural) : capitalize(tableName)}Many`;
3304
3403
  const deleteFieldName = `${prefixes.delete}${mapped ? capitalize(mapped.singular) : capitalize(tableName)}`;
3404
+ const restoreFieldName = `${prefixes.restore ?? "restore"}${mapped ? capitalize(mapped.singular) : capitalize(tableName)}`;
3305
3405
  const writeSingleSuffix = suffixes.single === "" ? "Single" : suffixes.single;
3306
3406
  const updateSingleFieldName = `${updateFieldName}${writeSingleSuffix}`;
3307
3407
  const deleteSingleFieldName = `${deleteFieldName}${writeSingleSuffix}`;
3408
+ const restoreSingleFieldName = `${restoreFieldName}${writeSingleSuffix}`;
3308
3409
  const pluralNoun = mapped ? capitalize(mapped.plural) : capitalize(tableName);
3309
3410
  const updateCountFieldName = `${prefixes.update}${pluralNoun}Count`;
3310
3411
  const deleteCountFieldName = `${prefixes.delete}${pluralNoun}Count`;
@@ -3324,6 +3425,8 @@ var computeResolverFieldNames = (tableName, typeNameMapper, prefixes, suffixes)
3324
3425
  updateCountFieldName,
3325
3426
  deleteFieldName,
3326
3427
  deleteSingleFieldName,
3428
+ restoreFieldName,
3429
+ restoreSingleFieldName,
3327
3430
  deleteCountFieldName
3328
3431
  };
3329
3432
  };
@@ -3428,7 +3531,13 @@ var generateWriteCount = ({
3428
3531
  args: queryArgs
3429
3532
  };
3430
3533
  };
3431
- var selectArrayArgs = (orderArgs, filterArgs, distinctEnum) => ({
3534
+ var deletedArg = (softDelete, tableName) => softDelete?.(tableName) ? {
3535
+ deleted: {
3536
+ type: deletedFilterEnum,
3537
+ description: "Whether rows marked deleted are returned. Defaults to EXCLUDE."
3538
+ }
3539
+ } : {};
3540
+ var selectArrayArgs = (orderArgs, filterArgs, distinctEnum, softDelete, tableName) => ({
3432
3541
  offset: { type: import_graphql5.GraphQLInt },
3433
3542
  limit: { type: import_graphql5.GraphQLInt },
3434
3543
  orderBy: { type: orderArgs },
@@ -3437,12 +3546,14 @@ var selectArrayArgs = (orderArgs, filterArgs, distinctEnum) => ({
3437
3546
  type: import_graphql5.GraphQLString,
3438
3547
  description: "Keyset pagination: only return rows strictly after this cursor (a row's `cursor` field from a previous page, under the same orderBy)."
3439
3548
  },
3440
- ...distinctEnum ? { distinct: { type: new import_graphql5.GraphQLList(new import_graphql5.GraphQLNonNull(distinctEnum)) } } : {}
3549
+ ...distinctEnum ? { distinct: { type: new import_graphql5.GraphQLList(new import_graphql5.GraphQLNonNull(distinctEnum)) } } : {},
3550
+ ...deletedArg(softDelete, tableName)
3441
3551
  });
3442
- var selectSingleArgs = (orderArgs, filterArgs) => ({
3552
+ var selectSingleArgs = (orderArgs, filterArgs, softDelete, tableName) => ({
3443
3553
  offset: { type: import_graphql5.GraphQLInt },
3444
3554
  orderBy: { type: orderArgs },
3445
- where: { type: filterArgs }
3555
+ where: { type: filterArgs },
3556
+ ...deletedArg(softDelete, tableName)
3446
3557
  });
3447
3558
  var runRelationalSelect = async (opts) => {
3448
3559
  const {
@@ -3461,7 +3572,8 @@ var runRelationalSelect = async (opts) => {
3461
3572
  filterCtx,
3462
3573
  pkNames,
3463
3574
  after,
3464
- scope
3575
+ scope,
3576
+ deleted
3465
3577
  } = opts;
3466
3578
  const distinct = opts.distinct?.length ? opts.distinct : void 0;
3467
3579
  const cursorSelected = !single && isCursorFieldSelected(parsedInfo.fieldsByTypeName[typeName], table);
@@ -3497,7 +3609,8 @@ var runRelationalSelect = async (opts) => {
3497
3609
  scope,
3498
3610
  tableName,
3499
3611
  table,
3500
- where ? extractFilters(table, tableName, where, relationFilterCtx(filterCtx, tableName)) : void 0
3612
+ where ? extractFilters(table, tableName, where, relationFilterCtx(filterCtx, tableName)) : void 0,
3613
+ deleted
3501
3614
  ),
3502
3615
  orderBy,
3503
3616
  limit: single ? 1 : opts.limit,
@@ -3528,7 +3641,7 @@ var runRelationalSelect = async (opts) => {
3528
3641
  where: distinctKeys ? (
3529
3642
  // The distinct pass already ran inside the scope, so the keys it picked are in it.
3530
3643
  { RAW: (aliasedTable2) => primaryKeyRestriction(aliasedTable2, pkNames, distinctKeys) }
3531
- ) : where || cursorValues || scope?.has(tableName) ? {
3644
+ ) : where || cursorValues || scope?.has(tableName, deleted) ? {
3532
3645
  RAW: (aliasedTable2) => withScope(
3533
3646
  scope,
3534
3647
  tableName,
@@ -3541,7 +3654,8 @@ var runRelationalSelect = async (opts) => {
3541
3654
  cursorValues,
3542
3655
  opts.nullOrdering ?? "nulls-smallest"
3543
3656
  ) : void 0
3544
- )
3657
+ ),
3658
+ deleted
3545
3659
  )
3546
3660
  } : void 0,
3547
3661
  with: relationMap[tableName] ? extractRelationsParams(
@@ -3858,10 +3972,11 @@ var assembleAggregateRow = (row, request, target) => {
3858
3972
  }
3859
3973
  return result;
3860
3974
  };
3861
- var generateAggregate = (db, tableName, table, typeName, fieldName, filterArgs, filterCtx, scopes) => {
3975
+ var generateAggregate = (db, tableName, table, typeName, fieldName, filterArgs, filterCtx, policies) => {
3862
3976
  const target = aggregateTarget(table, tableName, typeName);
3863
3977
  const queryArgs = {
3864
- where: { type: filterArgs }
3978
+ where: { type: filterArgs },
3979
+ ...deletedArg(policies?.softDelete, tableName)
3865
3980
  };
3866
3981
  return {
3867
3982
  name: fieldName,
@@ -3873,10 +3988,11 @@ var generateAggregate = (db, tableName, table, typeName, fieldName, filterArgs,
3873
3988
  }
3874
3989
  let query = resolveExecutor(db, context).select(request.selection).from(table);
3875
3990
  const where = withScope(
3876
- resolveScope(scopes, context, filterCtx),
3991
+ resolveScope(policies, context, filterCtx),
3877
3992
  tableName,
3878
3993
  table,
3879
- args.where ? extractFilters(table, tableName, args.where, relationFilterCtx(filterCtx, tableName)) : void 0
3994
+ args.where ? extractFilters(table, tableName, args.where, relationFilterCtx(filterCtx, tableName)) : void 0,
3995
+ args.deleted
3880
3996
  );
3881
3997
  if (where) {
3882
3998
  query = query.where(where);
@@ -3891,7 +4007,7 @@ var generateAggregate = (db, tableName, table, typeName, fieldName, filterArgs,
3891
4007
  };
3892
4008
  };
3893
4009
  var GROUP_KEY = "__dgql_group_key";
3894
- var createRelationAggregateFactory = (db, tables, cacheCtx, typeNameMapper, filterCtx, scopes) => {
4010
+ var createRelationAggregateFactory = (db, tables, cacheCtx, typeNameMapper, filterCtx, policies) => {
3895
4011
  return ({ tableName, relationName, relEntry }) => {
3896
4012
  const parentTable = tables[tableName];
3897
4013
  const targetTableName = relEntry.targetTableName;
@@ -3918,8 +4034,10 @@ var createRelationAggregateFactory = (db, tables, cacheCtx, typeNameMapper, filt
3918
4034
  return assembleAggregateRow({}, request, target);
3919
4035
  }
3920
4036
  const whereArg = args?.where;
4037
+ const deleted = args?.deleted;
3921
4038
  const argsKey = JSON.stringify({
3922
4039
  where: whereArg ?? null,
4040
+ deleted: deleted ?? null,
3923
4041
  selection: Object.keys(request.selection).sort()
3924
4042
  });
3925
4043
  const loaderKey = `${tableName}::${relationName}::aggregate::${argsKey}`;
@@ -3927,13 +4045,14 @@ var createRelationAggregateFactory = (db, tables, cacheCtx, typeNameMapper, filt
3927
4045
  const executor = resolveExecutor(db, context);
3928
4046
  const uniqueIds = [...new Set(parentIds)];
3929
4047
  const whereCondition = withScope(
3930
- resolveScope(scopes, context, filterCtx),
4048
+ resolveScope(policies, context, filterCtx),
3931
4049
  targetTableName,
3932
4050
  targetTable,
3933
4051
  (0, import_drizzle_orm5.and)(
3934
4052
  (0, import_drizzle_orm5.inArray)(foreignCol, uniqueIds),
3935
4053
  whereArg ? extractFilters(targetTable, targetTableName, whereArg, relationFilterCtx(filterCtx, targetTableName)) : void 0
3936
- )
4054
+ ),
4055
+ deleted
3937
4056
  );
3938
4057
  const rows = await executor.select({ [GROUP_KEY]: foreignCol, ...request.selection }).from(targetTable).where(whereCondition).groupBy(foreignCol);
3939
4058
  const byKey = new Map(rows.map((row) => [row[GROUP_KEY], row]));
@@ -4066,7 +4185,7 @@ var buildHavingCondition = (having, columns) => {
4066
4185
  }
4067
4186
  return conditions.length ? (0, import_drizzle_orm5.and)(...conditions) : void 0;
4068
4187
  };
4069
- var generateGroupBy = (db, tableName, table, typeName, fieldName, filterArgs, groupByEnum, havingInput, filterCtx, scopes) => {
4188
+ var generateGroupBy = (db, tableName, table, typeName, fieldName, filterArgs, groupByEnum, havingInput, filterCtx, policies) => {
4070
4189
  const target = aggregateTarget(table, tableName, typeName);
4071
4190
  const groupable = groupableColumns(table, tableName);
4072
4191
  const columns = (0, import_drizzle_orm5.getColumns)(table);
@@ -4077,7 +4196,8 @@ var generateGroupBy = (db, tableName, table, typeName, fieldName, filterArgs, gr
4077
4196
  description: "Columns to group by. One result row per distinct combination of their values."
4078
4197
  },
4079
4198
  where: { type: filterArgs, description: "Filters the rows before they are grouped." },
4080
- having: { type: havingInput, description: "Filters the groups after they are aggregated." }
4199
+ having: { type: havingInput, description: "Filters the groups after they are aggregated." },
4200
+ ...deletedArg(policies?.softDelete, tableName)
4081
4201
  };
4082
4202
  return {
4083
4203
  name: fieldName,
@@ -4101,10 +4221,11 @@ var generateGroupBy = (db, tableName, table, typeName, fieldName, filterArgs, gr
4101
4221
  };
4102
4222
  let query = resolveExecutor(db, context).select(selection).from(table);
4103
4223
  const where = withScope(
4104
- resolveScope(scopes, context, filterCtx),
4224
+ resolveScope(policies, context, filterCtx),
4105
4225
  tableName,
4106
4226
  table,
4107
- args.where ? extractFilters(table, tableName, args.where, relationFilterCtx(filterCtx, tableName)) : void 0
4227
+ args.where ? extractFilters(table, tableName, args.where, relationFilterCtx(filterCtx, tableName)) : void 0,
4228
+ args.deleted
4108
4229
  );
4109
4230
  if (where) {
4110
4231
  query = query.where(where);
@@ -4146,7 +4267,9 @@ var generateSelectArray = (db, tableName, tables, relationMap, orderArgs, filter
4146
4267
  const queryArgs = selectArrayArgs(
4147
4268
  orderArgs,
4148
4269
  filterArgs,
4149
- distinctEnabled ? generateDistinctEnum(table, typeName) : void 0
4270
+ distinctEnabled ? generateDistinctEnum(table, typeName) : void 0,
4271
+ policies?.softDelete,
4272
+ tableName
4150
4273
  );
4151
4274
  return {
4152
4275
  name: fieldName,
@@ -4188,7 +4311,7 @@ var generateSelectSingle = (db, tableName, tables, relationMap, orderArgs, filte
4188
4311
  `Drizzle-GraphQL Error: Table ${tableName} not found in drizzle instance. Did you forget to pass schema to drizzle constructor?`
4189
4312
  );
4190
4313
  }
4191
- const queryArgs = selectSingleArgs(orderArgs, filterArgs);
4314
+ const queryArgs = selectSingleArgs(orderArgs, filterArgs, policies?.softDelete, tableName);
4192
4315
  const table = tables[tableName];
4193
4316
  const pkNames = mysqlPrimaryKeyPropNames(table);
4194
4317
  return {
@@ -4227,22 +4350,49 @@ var generateInsertArray = (db, tableName, table, baseType, fieldName, txCtx, pol
4227
4350
  type: new import_graphql7.GraphQLNonNull(new import_graphql7.GraphQLList(new import_graphql7.GraphQLNonNull(baseType)))
4228
4351
  }
4229
4352
  };
4353
+ const hooks = policies?.onWrite?.(tableName, "insert");
4230
4354
  return {
4231
4355
  name: fieldName,
4232
4356
  resolver: async (_source, args, context, info) => {
4233
4357
  try {
4234
- return await runMutation(db, context, info, txCtx, async (executor) => {
4235
- const input = applyContextValuesAll(
4236
- remapFromGraphQLArrayInput(args.values, table),
4237
- policies?.contextValues?.(tableName),
4238
- context
4239
- );
4240
- if (!input.length) {
4241
- throw new import_graphql7.GraphQLError("No values were provided!");
4242
- }
4243
- await executor.insert(table).values(input);
4244
- return { isSuccess: true };
4245
- });
4358
+ return await runMutation(
4359
+ db,
4360
+ context,
4361
+ info,
4362
+ txCtx,
4363
+ async (executor) => {
4364
+ const input = applyContextValuesAll(
4365
+ remapFromGraphQLArrayInput(args.values, table),
4366
+ policies?.contextValues?.(tableName),
4367
+ context
4368
+ );
4369
+ if (!input.length) {
4370
+ throw new import_graphql7.GraphQLError("No values were provided!");
4371
+ }
4372
+ await runWriteHook(hooks, "before", {
4373
+ table: tableName,
4374
+ operation: "insert",
4375
+ single: false,
4376
+ args,
4377
+ context,
4378
+ info,
4379
+ tx: executor
4380
+ });
4381
+ await executor.insert(table).values(input);
4382
+ await runWriteHook(hooks, "after", {
4383
+ table: tableName,
4384
+ operation: "insert",
4385
+ single: false,
4386
+ args,
4387
+ rows: [],
4388
+ context,
4389
+ info,
4390
+ tx: executor
4391
+ });
4392
+ return { isSuccess: true };
4393
+ },
4394
+ !!hooks
4395
+ );
4246
4396
  } catch (e) {
4247
4397
  throw toGraphQLError(e);
4248
4398
  }
@@ -4256,19 +4406,46 @@ var generateInsertSingle = (db, tableName, table, baseType, fieldName, txCtx, po
4256
4406
  type: new import_graphql7.GraphQLNonNull(baseType)
4257
4407
  }
4258
4408
  };
4409
+ const hooks = policies?.onWrite?.(tableName, "insert");
4259
4410
  return {
4260
4411
  name: fieldName,
4261
4412
  resolver: async (_source, args, context, info) => {
4262
4413
  try {
4263
- return await runMutation(db, context, info, txCtx, async (executor) => {
4264
- const input = applyContextValues(
4265
- remapFromGraphQLSingleInput(args.values, table),
4266
- policies?.contextValues?.(tableName),
4267
- context
4268
- );
4269
- await executor.insert(table).values(input);
4270
- return { isSuccess: true };
4271
- });
4414
+ return await runMutation(
4415
+ db,
4416
+ context,
4417
+ info,
4418
+ txCtx,
4419
+ async (executor) => {
4420
+ await runWriteHook(hooks, "before", {
4421
+ table: tableName,
4422
+ operation: "insert",
4423
+ single: true,
4424
+ args,
4425
+ context,
4426
+ info,
4427
+ tx: executor
4428
+ });
4429
+ const input = applyContextValues(
4430
+ remapFromGraphQLSingleInput(args.values, table),
4431
+ policies?.contextValues?.(tableName),
4432
+ context
4433
+ );
4434
+ await executor.insert(table).values(input);
4435
+ await runWriteHook(hooks, "after", {
4436
+ table: tableName,
4437
+ operation: "insert",
4438
+ single: true,
4439
+ args,
4440
+ rows: [],
4441
+ context,
4442
+ info,
4443
+ tx: executor
4444
+ });
4445
+ return { isSuccess: true };
4446
+ },
4447
+ !!hooks
4448
+ );
4272
4449
  } catch (e) {
4273
4450
  throw toGraphQLError(e);
4274
4451
  }
@@ -4287,35 +4464,62 @@ var generateUpsert = (db, tableName, table, baseType, onConflictType, fieldName,
4287
4464
  }
4288
4465
  };
4289
4466
  const pkNames = mysqlPrimaryKeyPropNames(table);
4467
+ const hooks = policies?.onWrite?.(tableName, "upsert");
4290
4468
  return {
4291
4469
  name: fieldName,
4292
4470
  resolver: async (_source, args, context, info) => {
4293
4471
  try {
4294
- return await runMutation(db, context, info, txCtx, async (executor) => {
4295
- const input = applyContextValuesAll(
4296
- single ? [remapFromGraphQLSingleInput(args.values, table)] : remapFromGraphQLArrayInput(args.values, table),
4297
- policies?.contextValues?.(tableName),
4298
- context
4299
- );
4300
- if (!input.length) {
4301
- throw new import_graphql7.GraphQLError("No values were provided!");
4302
- }
4303
- const plan = resolveConflictPlan({
4304
- table,
4305
- values: input,
4306
- onConflict: args.onConflict,
4307
- pkNames,
4308
- uniqueSets: [],
4309
- excludedRef: mysqlValuesColumnRef,
4310
- withTarget: false
4311
- });
4312
- if (plan.action === "NOTHING") {
4313
- await executor.insert(table).ignore().values(input);
4314
- } else {
4315
- await executor.insert(table).values(input).onDuplicateKeyUpdate({ set: plan.set });
4316
- }
4317
- return { isSuccess: true };
4318
- });
4472
+ return await runMutation(
4473
+ db,
4474
+ context,
4475
+ info,
4476
+ txCtx,
4477
+ async (executor) => {
4478
+ const input = applyContextValuesAll(
4479
+ single ? [remapFromGraphQLSingleInput(args.values, table)] : remapFromGraphQLArrayInput(args.values, table),
4480
+ policies?.contextValues?.(tableName),
4481
+ context
4482
+ );
4483
+ if (!input.length) {
4484
+ throw new import_graphql7.GraphQLError("No values were provided!");
4485
+ }
4486
+ await runWriteHook(hooks, "before", {
4487
+ table: tableName,
4488
+ operation: "upsert",
4489
+ single,
4490
+ args,
4491
+ context,
4492
+ info,
4493
+ tx: executor
4494
+ });
4495
+ const plan = resolveConflictPlan({
4496
+ table,
4497
+ values: input,
4498
+ onConflict: args.onConflict,
4499
+ pkNames,
4500
+ uniqueSets: [],
4501
+ excludedRef: mysqlValuesColumnRef,
4502
+ withTarget: false
4503
+ });
4504
+ if (plan.action === "NOTHING") {
4505
+ await executor.insert(table).ignore().values(input);
4506
+ } else {
4507
+ await executor.insert(table).values(input).onDuplicateKeyUpdate({ set: plan.set });
4508
+ }
4509
+ await runWriteHook(hooks, "after", {
4510
+ table: tableName,
4511
+ operation: "upsert",
4512
+ single,
4513
+ args,
4514
+ rows: [],
4515
+ context,
4516
+ info,
4517
+ tx: executor
4518
+ });
4519
+ return { isSuccess: true };
4520
+ },
4521
+ !!hooks
4522
+ );
4319
4523
  } catch (e) {
4320
4524
  throw toGraphQLError(e);
4321
4525
  }
@@ -4332,37 +4536,64 @@ var generateUpdate = (db, tableName, table, setArgs, filterArgs, fieldName, sing
4332
4536
  type: single || requireWhere ? new import_graphql7.GraphQLNonNull(filterArgs) : filterArgs
4333
4537
  }
4334
4538
  };
4539
+ const hooks = policies?.onWrite?.(tableName, "update");
4335
4540
  return {
4336
4541
  name: fieldName,
4337
4542
  resolver: async (_source, args, context, info) => {
4338
4543
  try {
4339
- return await runMutation(db, context, info, txCtx, async (executor) => {
4340
- const { where, set } = args;
4341
- const scope = policies?.scope?.(context);
4342
- const input = stripContextValues(
4343
- remapUpdateInput(set, table, tableName),
4344
- policies?.contextValues?.(tableName)
4345
- );
4346
- if (!Object.keys(input).length) {
4347
- throw new import_graphql7.GraphQLError("Unable to update with no values specified!");
4348
- }
4349
- const relationCtx = relationFilterCtx(filterCtx, tableName);
4350
- const filters = withScope(
4351
- scope,
4352
- tableName,
4353
- table,
4354
- single || requireWhere ? extractRequiredFilters(table, tableName, where, fieldName, relationCtx) : where ? extractFilters(table, tableName, where, relationCtx) : void 0
4355
- );
4356
- if (single) {
4357
- await assertSingleMatch(executor, table, filters, fieldName);
4358
- }
4359
- let query = executor.update(table).set(input);
4360
- if (filters) {
4361
- query = query.where(filters);
4362
- }
4363
- await query;
4364
- return { isSuccess: true };
4365
- });
4544
+ return await runMutation(
4545
+ db,
4546
+ context,
4547
+ info,
4548
+ txCtx,
4549
+ async (executor) => {
4550
+ const { where, set } = args;
4551
+ const scope = policies?.scope?.(context);
4552
+ const input = stripContextValues(
4553
+ remapUpdateInput(set, table, tableName),
4554
+ policies?.contextValues?.(tableName)
4555
+ );
4556
+ if (!Object.keys(input).length) {
4557
+ throw new import_graphql7.GraphQLError("Unable to update with no values specified!");
4558
+ }
4559
+ await runWriteHook(hooks, "before", {
4560
+ table: tableName,
4561
+ operation: "update",
4562
+ single,
4563
+ args,
4564
+ context,
4565
+ info,
4566
+ tx: executor
4567
+ });
4568
+ const relationCtx = relationFilterCtx(filterCtx, tableName);
4569
+ const filters = withScope(
4570
+ scope,
4571
+ tableName,
4572
+ table,
4573
+ single || requireWhere ? extractRequiredFilters(table, tableName, where, fieldName, relationCtx) : where ? extractFilters(table, tableName, where, relationCtx) : void 0
4574
+ );
4575
+ if (single) {
4576
+ await assertSingleMatch(executor, table, filters, fieldName);
4577
+ }
4578
+ let query = executor.update(table).set(input);
4579
+ if (filters) {
4580
+ query = query.where(filters);
4581
+ }
4582
+ await query;
4583
+ await runWriteHook(hooks, "after", {
4584
+ table: tableName,
4585
+ operation: "update",
4586
+ single,
4587
+ args,
4588
+ rows: [],
4589
+ context,
4590
+ info,
4591
+ tx: executor
4592
+ });
4593
+ return { isSuccess: true };
4594
+ },
4595
+ !!hooks
4596
+ );
4366
4597
  } catch (e) {
4367
4598
  throw toGraphQLError(e);
4368
4599
  }
@@ -4376,43 +4607,70 @@ var generateUpdateMany = (db, tableName, table, updateManyInput, fieldName, filt
4376
4607
  type: new import_graphql7.GraphQLNonNull(new import_graphql7.GraphQLList(new import_graphql7.GraphQLNonNull(updateManyInput)))
4377
4608
  }
4378
4609
  };
4610
+ const hooks = policies?.onWrite?.(tableName, "updateMany");
4379
4611
  return {
4380
4612
  name: fieldName,
4381
4613
  resolver: async (_source, args, context, info) => {
4382
4614
  try {
4383
- return await runMutation(db, context, info, txCtx, async (executor) => {
4384
- const { updates } = args;
4385
- if (!updates.length) {
4386
- throw new import_graphql7.GraphQLError("No updates were provided!");
4387
- }
4388
- const scope = policies?.scope?.(context);
4389
- const contextColumns = policies?.contextValues?.(tableName);
4390
- const entries = updates.map(({ where, set }) => {
4391
- const input = stripContextValues(remapUpdateInput(set, table, tableName), contextColumns);
4392
- if (!Object.keys(input).length) {
4393
- throw new import_graphql7.GraphQLError("Unable to update with no values specified!");
4615
+ return await runMutation(
4616
+ db,
4617
+ context,
4618
+ info,
4619
+ txCtx,
4620
+ async (executor) => {
4621
+ const { updates } = args;
4622
+ if (!updates.length) {
4623
+ throw new import_graphql7.GraphQLError("No updates were provided!");
4394
4624
  }
4395
- return {
4396
- set: input,
4397
- filters: withScope(
4398
- scope,
4399
- tableName,
4400
- table,
4401
- where ? extractFilters(table, tableName, where, relationFilterCtx(filterCtx, tableName)) : void 0
4402
- )
4403
- };
4404
- });
4405
- await executor.transaction(async (tx) => {
4406
- for (const entry of entries) {
4407
- let query = tx.update(table).set(entry.set);
4408
- if (entry.filters) {
4409
- query = query.where(entry.filters);
4625
+ await runWriteHook(hooks, "before", {
4626
+ table: tableName,
4627
+ operation: "updateMany",
4628
+ single: false,
4629
+ args,
4630
+ context,
4631
+ info,
4632
+ tx: executor
4633
+ });
4634
+ const scope = policies?.scope?.(context);
4635
+ const contextColumns = policies?.contextValues?.(tableName);
4636
+ const entries = updates.map(({ where, set }) => {
4637
+ const input = stripContextValues(remapUpdateInput(set, table, tableName), contextColumns);
4638
+ if (!Object.keys(input).length) {
4639
+ throw new import_graphql7.GraphQLError("Unable to update with no values specified!");
4410
4640
  }
4411
- await query;
4412
- }
4413
- });
4414
- return { isSuccess: true };
4415
- });
4641
+ return {
4642
+ set: input,
4643
+ filters: withScope(
4644
+ scope,
4645
+ tableName,
4646
+ table,
4647
+ where ? extractFilters(table, tableName, where, relationFilterCtx(filterCtx, tableName)) : void 0
4648
+ )
4649
+ };
4650
+ });
4651
+ await executor.transaction(async (tx) => {
4652
+ for (const entry of entries) {
4653
+ let query = tx.update(table).set(entry.set);
4654
+ if (entry.filters) {
4655
+ query = query.where(entry.filters);
4656
+ }
4657
+ await query;
4658
+ }
4659
+ });
4660
+ await runWriteHook(hooks, "after", {
4661
+ table: tableName,
4662
+ operation: "updateMany",
4663
+ single: false,
4664
+ args,
4665
+ rows: [],
4666
+ context,
4667
+ info,
4668
+ tx: executor
4669
+ });
4670
+ return { isSuccess: true };
4671
+ },
4672
+ !!hooks
4673
+ );
4416
4674
  } catch (e) {
4417
4675
  throw toGraphQLError(e);
4418
4676
  }
@@ -4420,36 +4678,66 @@ var generateUpdateMany = (db, tableName, table, updateManyInput, fieldName, filt
4420
4678
  args: queryArgs
4421
4679
  };
4422
4680
  };
4423
- var generateDelete = (db, tableName, table, filterArgs, fieldName, single, requireWhere, filterCtx, txCtx, policies) => {
4681
+ var generateDelete = (db, tableName, table, filterArgs, fieldName, single, requireWhere, filterCtx, txCtx, policies, restore = false) => {
4682
+ const softDelete = policies?.softDelete?.(tableName);
4683
+ const operation = restore ? "restore" : "delete";
4424
4684
  const queryArgs = {
4425
4685
  where: {
4426
4686
  type: single || requireWhere ? new import_graphql7.GraphQLNonNull(filterArgs) : filterArgs
4427
4687
  }
4428
4688
  };
4689
+ const hooks = policies?.onWrite?.(tableName, operation);
4429
4690
  return {
4430
4691
  name: fieldName,
4431
4692
  resolver: async (_source, args, context, info) => {
4432
4693
  try {
4433
- return await runMutation(db, context, info, txCtx, async (executor) => {
4434
- const { where } = args;
4435
- const scope = policies?.scope?.(context);
4436
- const relationCtx = relationFilterCtx(filterCtx, tableName);
4437
- const filters = withScope(
4438
- scope,
4439
- tableName,
4440
- table,
4441
- single || requireWhere ? extractRequiredFilters(table, tableName, where, fieldName, relationCtx) : where ? extractFilters(table, tableName, where, relationCtx) : void 0
4442
- );
4443
- if (single) {
4444
- await assertSingleMatch(executor, table, filters, fieldName);
4445
- }
4446
- let query = executor.delete(table);
4447
- if (filters) {
4448
- query = query.where(filters);
4449
- }
4450
- await query;
4451
- return { isSuccess: true };
4452
- });
4694
+ return await runMutation(
4695
+ db,
4696
+ context,
4697
+ info,
4698
+ txCtx,
4699
+ async (executor) => {
4700
+ await runWriteHook(hooks, "before", {
4701
+ table: tableName,
4702
+ operation,
4703
+ single,
4704
+ args,
4705
+ context,
4706
+ info,
4707
+ tx: executor
4708
+ });
4709
+ const { where } = args;
4710
+ const scope = policies?.scope?.(context);
4711
+ const relationCtx = relationFilterCtx(filterCtx, tableName);
4712
+ const filters = withScope(
4713
+ scope,
4714
+ tableName,
4715
+ table,
4716
+ single || requireWhere ? extractRequiredFilters(table, tableName, where, fieldName, relationCtx) : where ? extractFilters(table, tableName, where, relationCtx) : void 0,
4717
+ restore ? "ONLY" : void 0
4718
+ );
4719
+ if (single) {
4720
+ await assertSingleMatch(executor, table, filters, fieldName);
4721
+ }
4722
+ let query = softDelete ? executor.update(table).set({ [softDelete.columnName]: restore ? softDelete.writeRestored : softDelete.writeDeleted() }) : executor.delete(table);
4723
+ if (filters) {
4724
+ query = query.where(filters);
4725
+ }
4726
+ await query;
4727
+ await runWriteHook(hooks, "after", {
4728
+ table: tableName,
4729
+ operation,
4730
+ single,
4731
+ args,
4732
+ rows: [],
4733
+ context,
4734
+ info,
4735
+ tx: executor
4736
+ });
4737
+ return { isSuccess: true };
4738
+ },
4739
+ !!hooks
4740
+ );
4453
4741
  } catch (e) {
4454
4742
  throw toGraphQLError(e);
4455
4743
  }
@@ -4487,16 +4775,17 @@ var generateSchemaData = (db, schema, relations, options) => {
4487
4775
  attachTargetPrimaryKeys(namedRelations, tables, mysqlPrimaryKeyPropNames);
4488
4776
  const eagerRelations = pruneNonEagerRelations(namedRelations, shouldEagerLoad);
4489
4777
  const filterCtx = { tables, relationMap: namedRelations };
4490
- const scopes = options.policies?.scope;
4491
- const contextValuesOf = options.policies?.contextValues;
4492
- const policies = bindPolicies(options.policies, filterCtx);
4778
+ const tablePolicies = options.policies;
4779
+ const contextValuesOf = tablePolicies?.contextValues;
4780
+ const softDeleteOf = tablePolicies?.softDelete;
4781
+ const policies = bindPolicies(tablePolicies, filterCtx);
4493
4782
  const resolverFactory = createRelationResolverFactory(
4494
4783
  db,
4495
4784
  tables,
4496
4785
  "nulls-smallest",
4497
4786
  filterCtx,
4498
4787
  limits,
4499
- scopes
4788
+ tablePolicies
4500
4789
  );
4501
4790
  const cacheCtx = {
4502
4791
  genericFilterCache: /* @__PURE__ */ new Map(),
@@ -4515,9 +4804,10 @@ var generateSchemaData = (db, schema, relations, options) => {
4515
4804
  docs: options.docs ?? {},
4516
4805
  primaryKeyOf: (name) => tables[name] ? mysqlPrimaryKeyPropNames(tables[name]) : [],
4517
4806
  contextValuesOf,
4807
+ softDeleteOf,
4518
4808
  featureOf
4519
4809
  };
4520
- const relationAggregateFactory = anyTable("relationAggregates") ? createRelationAggregateFactory(db, tables, cacheCtx, typeNameMapper, filterCtx, scopes) : void 0;
4810
+ const relationAggregateFactory = anyTable("relationAggregates") ? createRelationAggregateFactory(db, tables, cacheCtx, typeNameMapper, filterCtx, tablePolicies) : void 0;
4521
4811
  const queries = {};
4522
4812
  const mutations = {};
4523
4813
  const mutationTxCtx = createMutationTxCtx(options.transactions);
@@ -4573,8 +4863,11 @@ var generateSchemaData = (db, schema, relations, options) => {
4573
4863
  updateCountFieldName,
4574
4864
  deleteFieldName,
4575
4865
  deleteSingleFieldName,
4866
+ restoreFieldName,
4867
+ restoreSingleFieldName,
4576
4868
  deleteCountFieldName
4577
4869
  } = computeResolverFieldNames(tableName, typeNameMapper, prefixes, suffixes);
4870
+ const softDeleteInfo = softDeleteOf?.(tableName);
4578
4871
  const selectArrGenerated = generateSelectArray(
4579
4872
  db,
4580
4873
  tableName,
@@ -4712,6 +5005,32 @@ var generateSchemaData = (db, schema, relations, options) => {
4712
5005
  mutationTxCtx,
4713
5006
  policies
4714
5007
  ) : void 0;
5008
+ const restoreGenerated = softDeleteInfo ? generateDelete(
5009
+ db,
5010
+ tableName,
5011
+ schema[tableName],
5012
+ tableFilters,
5013
+ restoreFieldName,
5014
+ false,
5015
+ tableFeatures.requireWhere,
5016
+ filterCtx,
5017
+ mutationTxCtx,
5018
+ policies,
5019
+ true
5020
+ ) : void 0;
5021
+ const restoreSingleGenerated = softDeleteInfo ? generateDelete(
5022
+ db,
5023
+ tableName,
5024
+ schema[tableName],
5025
+ tableFilters,
5026
+ restoreSingleFieldName,
5027
+ true,
5028
+ tableFeatures.requireWhere,
5029
+ filterCtx,
5030
+ mutationTxCtx,
5031
+ policies,
5032
+ true
5033
+ ) : void 0;
4715
5034
  const updateCountGenerated = tableFeatures.update && tableFeatures.countMutations ? generateWriteCount({
4716
5035
  db,
4717
5036
  tableName,
@@ -4744,7 +5063,7 @@ var generateSchemaData = (db, schema, relations, options) => {
4744
5063
  aggregateFieldName,
4745
5064
  tableFilters,
4746
5065
  filterCtx,
4747
- scopes
5066
+ tablePolicies
4748
5067
  ) : void 0;
4749
5068
  const groupByType = tableFeatures.aggregates && tableFeatures.groupBy ? generateGroupByType(schema[tableName], tableName, typeName, cacheCtx) : void 0;
4750
5069
  const groupByEnum = groupByType ? generateGroupByEnum(schema[tableName], tableName, typeName) : void 0;
@@ -4759,7 +5078,7 @@ var generateSchemaData = (db, schema, relations, options) => {
4759
5078
  groupByEnum,
4760
5079
  havingInput,
4761
5080
  filterCtx,
4762
- scopes
5081
+ tablePolicies
4763
5082
  ) : void 0;
4764
5083
  queries[selectArrGenerated.name] = {
4765
5084
  type: selectArrOutput,
@@ -4809,7 +5128,9 @@ var generateSchemaData = (db, schema, relations, options) => {
4809
5128
  [updateManyGenerated, { operation: "updateMany", single: false, targetArg: "updates" }],
4810
5129
  [updateSingleGenerated, { operation: "update", single: true, targetArg: "where" }],
4811
5130
  [deleteGenerated, { operation: "delete", single: false, targetArg: "where" }],
4812
- [deleteSingleGenerated, { operation: "delete", single: true, targetArg: "where" }]
5131
+ [deleteSingleGenerated, { operation: "delete", single: true, targetArg: "where" }],
5132
+ [restoreGenerated, { operation: "restore", single: false, targetArg: "where" }],
5133
+ [restoreSingleGenerated, { operation: "restore", single: true, targetArg: "where" }]
4813
5134
  ];
4814
5135
  for (const [generated, meta] of generatedMutations) {
4815
5136
  if (generated) {
@@ -4890,6 +5211,64 @@ var import_graphql_parse_resolve_info3 = require("graphql-parse-resolve-info");
4890
5211
  var import_drizzle_orm7 = require("drizzle-orm");
4891
5212
  var import_graphql8 = require("graphql");
4892
5213
  var isOptionalOnInsert = (column) => !column.notNull || !!column.hasDefault || !!column.defaultFn;
5214
+ var propNameOf = (table, column) => {
5215
+ if (!column) {
5216
+ return void 0;
5217
+ }
5218
+ for (const [propName, candidate] of Object.entries((0, import_drizzle_orm7.getColumns)(table))) {
5219
+ if (candidate === column) {
5220
+ return propName;
5221
+ }
5222
+ }
5223
+ return void 0;
5224
+ };
5225
+ var throughColumn = (entry) => entry && (0, import_drizzle_orm7.is)(entry, import_drizzle_orm7.Column) ? entry : entry?._?.column;
5226
+ var buildThroughPlan = (relationName, relation, table, targetTableName, targetTable) => {
5227
+ if ((0, import_drizzle_orm7.is)(relation, import_drizzle_orm7.One)) {
5228
+ return void 0;
5229
+ }
5230
+ const junction = relation.throughTable;
5231
+ const sourceEntries = relation.through?.source;
5232
+ const targetEntries = relation.through?.target;
5233
+ if (!junction || sourceEntries?.length !== 1 || targetEntries?.length !== 1) {
5234
+ return void 0;
5235
+ }
5236
+ if (relation.sourceColumns?.length !== 1 || relation.targetColumns?.length !== 1) {
5237
+ return void 0;
5238
+ }
5239
+ const localCol = relation.sourceColumns[0];
5240
+ const foreignCol = relation.targetColumns[0];
5241
+ const localColPropName = propNameOf(table, localCol);
5242
+ const foreignColPropName = propNameOf(targetTable, foreignCol);
5243
+ const sourceColPropName = propNameOf(junction, throughColumn(sourceEntries[0]));
5244
+ const targetColPropName = propNameOf(junction, throughColumn(targetEntries[0]));
5245
+ if (!localColPropName || !foreignColPropName || !sourceColPropName || !targetColPropName) {
5246
+ return void 0;
5247
+ }
5248
+ const junctionColumns = (0, import_drizzle_orm7.getColumns)(junction);
5249
+ const sourceCol = junctionColumns[sourceColPropName];
5250
+ const targetCol = junctionColumns[targetColPropName];
5251
+ const unsuppliable = Object.entries(junctionColumns).some(
5252
+ ([propName, column]) => propName !== sourceColPropName && propName !== targetColPropName && !isOptionalOnInsert(column)
5253
+ );
5254
+ if (unsuppliable) {
5255
+ return void 0;
5256
+ }
5257
+ return {
5258
+ relationName,
5259
+ targetTableName,
5260
+ targetTable,
5261
+ localColPropName,
5262
+ foreignColPropName,
5263
+ foreignCol,
5264
+ localCol,
5265
+ isOne: false,
5266
+ // The link is written after the parent row exists, exactly like a child-side foreign key.
5267
+ fkSide: "child",
5268
+ canDetach: true,
5269
+ through: { table: junction, sourceColPropName, targetColPropName, sourceCol, targetCol }
5270
+ };
5271
+ };
4893
5272
  var buildNestedWritePlans = (tables, relationMap, resolveUniqueSets, resolvePrimaryKeys, extractJoinColumns) => {
4894
5273
  const uniqueCache = /* @__PURE__ */ new Map();
4895
5274
  const singleColumnUniques = (tableName, table) => {
@@ -4919,14 +5298,18 @@ var buildNestedWritePlans = (tables, relationMap, resolveUniqueSets, resolvePrim
4919
5298
  const tablePlans = {};
4920
5299
  for (const [relationName, relEntry] of Object.entries(relations)) {
4921
5300
  const relation = relEntry.relation;
4922
- if (relation.through) {
4923
- continue;
4924
- }
4925
5301
  const targetTableName = relEntry.targetTableName;
4926
5302
  const targetTable = tables[targetTableName];
4927
5303
  if (!targetTable) {
4928
5304
  continue;
4929
5305
  }
5306
+ if (relation.through) {
5307
+ const throughPlan = buildThroughPlan(relationName, relation, table, targetTableName, targetTable);
5308
+ if (throughPlan) {
5309
+ tablePlans[relationName] = throughPlan;
5310
+ }
5311
+ continue;
5312
+ }
4930
5313
  const joinColumns = extractJoinColumns(relEntry, table, targetTable);
4931
5314
  if (!joinColumns) {
4932
5315
  continue;
@@ -5011,11 +5394,31 @@ var createNestedWriteTypes = (params) => {
5011
5394
  return cached;
5012
5395
  }
5013
5396
  const filters = targetFilters(plan);
5014
- const payload = payloadType(tableName, typeName, plan);
5015
5397
  const targetTypeName = resolveTypeName(plan.targetTableName, typeNameMapper);
5016
5398
  const fields = {};
5017
- if (plan.isOne) {
5018
- fields["create"] = { type: payload, description: `Insert a ${targetTypeName} row and attach it` };
5399
+ if (plan.through) {
5400
+ if (!filters) {
5401
+ return void 0;
5402
+ }
5403
+ fields["connect"] = {
5404
+ type: listOf(filters),
5405
+ description: `Link every existing ${targetTypeName} row these match. Already-linked rows are left alone.`
5406
+ };
5407
+ if (forUpdate) {
5408
+ fields["disconnect"] = {
5409
+ type: listOf(filters),
5410
+ description: `Unlink every linked ${targetTypeName} row these match, leaving the rows in place`
5411
+ };
5412
+ fields["set"] = {
5413
+ type: listOf(filters),
5414
+ description: `Replace the whole set: unlink everything linked, then link every ${targetTypeName} row these match. \`set: []\` clears the relation. Applied before \`disconnect\` and \`connect\`.`
5415
+ };
5416
+ }
5417
+ } else if (plan.isOne) {
5418
+ fields["create"] = {
5419
+ type: payloadType(tableName, typeName, plan),
5420
+ description: `Insert a ${targetTypeName} row and attach it`
5421
+ };
5019
5422
  if (filters) {
5020
5423
  fields["connect"] = {
5021
5424
  type: filters,
@@ -5029,7 +5432,10 @@ var createNestedWriteTypes = (params) => {
5029
5432
  };
5030
5433
  }
5031
5434
  } else {
5032
- fields["create"] = { type: listOf(payload), description: `Insert these ${targetTypeName} rows and attach them` };
5435
+ fields["create"] = {
5436
+ type: listOf(payloadType(tableName, typeName, plan)),
5437
+ description: `Insert these ${targetTypeName} rows and attach them`
5438
+ };
5033
5439
  if (filters) {
5034
5440
  fields["connect"] = {
5035
5441
  type: listOf(filters),
@@ -5088,7 +5494,7 @@ var createNestedWriteTypes = (params) => {
5088
5494
  var mergedOps = (entries) => Object.assign({}, ...entries.map((entry) => entry.ops));
5089
5495
  var asArray = (value) => Array.isArray(value) ? value : value === void 0 ? [] : [value];
5090
5496
  var createNestedWriteRuntime = (params) => {
5091
- const { plans, filterCtx, scopes, contextValues } = params;
5497
+ const { plans, filterCtx, policies, contextValues } = params;
5092
5498
  const conditionOf = (plan, filter, operation, scope) => {
5093
5499
  const condition = extractFilters(
5094
5500
  plan.targetTable,
@@ -5111,6 +5517,48 @@ var createNestedWriteRuntime = (params) => {
5111
5517
  );
5112
5518
  }
5113
5519
  };
5520
+ const selectedTargetKeys = async (executor, plan, filter, operation, scope) => {
5521
+ const rows = await executor.select().from(plan.targetTable).where(conditionOf(plan, filter, operation, scope));
5522
+ return rows.map((row) => row[plan.foreignColPropName]).filter((value) => value !== void 0 && value !== null);
5523
+ };
5524
+ const applyThroughOps = async (executor, plan, key, op, scope) => {
5525
+ const through = plan.through;
5526
+ const unlinkAll = () => executor.delete(through.table).where((0, import_drizzle_orm7.eq)(through.sourceCol, key));
5527
+ const link = async (operands, operation) => {
5528
+ const existing = new Set(
5529
+ (await executor.select().from(through.table).where((0, import_drizzle_orm7.eq)(through.sourceCol, key))).map(
5530
+ (row) => row[through.targetColPropName]
5531
+ )
5532
+ );
5533
+ const rows = [];
5534
+ for (const filter of operands) {
5535
+ for (const targetKey of await selectedTargetKeys(executor, plan, filter, operation, scope)) {
5536
+ if (!existing.has(targetKey)) {
5537
+ existing.add(targetKey);
5538
+ rows.push({ [through.sourceColPropName]: key, [through.targetColPropName]: targetKey });
5539
+ }
5540
+ }
5541
+ }
5542
+ if (rows.length) {
5543
+ await executor.insert(through.table).values(rows);
5544
+ }
5545
+ };
5546
+ if (op["set"] !== void 0 && op["set"] !== null) {
5547
+ await unlinkAll();
5548
+ await link(asArray(op["set"]), "set");
5549
+ }
5550
+ if (op["disconnect"] !== void 0 && op["disconnect"] !== null) {
5551
+ for (const filter of asArray(op["disconnect"])) {
5552
+ const targetKeys = await selectedTargetKeys(executor, plan, filter, "disconnect", scope);
5553
+ if (targetKeys.length) {
5554
+ await executor.delete(through.table).where((0, import_drizzle_orm7.and)((0, import_drizzle_orm7.eq)(through.sourceCol, key), (0, import_drizzle_orm7.inArray)(through.targetCol, targetKeys)));
5555
+ }
5556
+ }
5557
+ }
5558
+ if (op["connect"] !== void 0 && op["connect"] !== null) {
5559
+ await link(asArray(op["connect"]), "connect");
5560
+ }
5561
+ };
5114
5562
  const detachAll = async (executor, plan, key, scope) => executor.update(plan.targetTable).set({ [plan.foreignColPropName]: null }).where(withScope(scope, plan.targetTableName, plan.targetTable, (0, import_drizzle_orm7.eq)(plan.foreignCol, key)));
5115
5563
  return {
5116
5564
  enabled: (tableName) => !!plans[tableName],
@@ -5151,7 +5599,7 @@ var createNestedWriteRuntime = (params) => {
5151
5599
  if (!tablePlans) {
5152
5600
  return patch;
5153
5601
  }
5154
- const scope = resolveScope(scopes, context, filterCtx);
5602
+ const scope = resolveScope(policies, context, filterCtx);
5155
5603
  for (const [relationName, op] of Object.entries(ops)) {
5156
5604
  const plan = tablePlans[relationName];
5157
5605
  if (!plan || plan.fkSide !== "parent") {
@@ -5191,7 +5639,7 @@ var createNestedWriteRuntime = (params) => {
5191
5639
  if (!tablePlans) {
5192
5640
  return;
5193
5641
  }
5194
- const scope = resolveScope(scopes, context, filterCtx);
5642
+ const scope = resolveScope(policies, context, filterCtx);
5195
5643
  for (const [relationName, op] of Object.entries(ops)) {
5196
5644
  const plan = tablePlans[relationName];
5197
5645
  if (!plan || plan.fkSide !== "child") {
@@ -5207,6 +5655,10 @@ var createNestedWriteRuntime = (params) => {
5207
5655
  `Drizzle-GraphQL Error: cannot write through '${relationName}': the ${tableName} row has no '${plan.localColPropName}' value to attach to.`
5208
5656
  );
5209
5657
  }
5658
+ if (plan.through) {
5659
+ await applyThroughOps(executor, plan, key, op, scope);
5660
+ continue;
5661
+ }
5210
5662
  if (op["set"] !== void 0) {
5211
5663
  await detachAll(executor, plan, key, scope);
5212
5664
  for (const filter of asArray(op["set"])) {
@@ -5283,7 +5735,9 @@ var generateSelectArray2 = (db, tableName, tables, relationMap, orderArgs, filte
5283
5735
  const queryArgs = selectArrayArgs(
5284
5736
  orderArgs,
5285
5737
  filterArgs,
5286
- distinctEnabled ? generateDistinctEnum(table, typeName) : void 0
5738
+ distinctEnabled ? generateDistinctEnum(table, typeName) : void 0,
5739
+ policies?.softDelete,
5740
+ tableName
5287
5741
  );
5288
5742
  return {
5289
5743
  name: fieldName,
@@ -5315,7 +5769,7 @@ var generateSelectArray2 = (db, tableName, tables, relationMap, orderArgs, filte
5315
5769
  nullOrdering: "nulls-largest"
5316
5770
  });
5317
5771
  }
5318
- const { offset, orderBy, where, distinct, after } = args;
5772
+ const { offset, orderBy, where, distinct, after, deleted } = args;
5319
5773
  const selectedColumnsSql = extractSelectedColumnsFromTreeSQLFormat(
5320
5774
  parsedInfo.fieldsByTypeName[typeName],
5321
5775
  table,
@@ -5353,7 +5807,8 @@ var generateSelectArray2 = (db, tableName, tables, relationMap, orderArgs, filte
5353
5807
  scope,
5354
5808
  tableName,
5355
5809
  table,
5356
- where ? extractFilters(table, tableName, where, relationFilterCtx(filterCtx, tableName)) : void 0
5810
+ where ? extractFilters(table, tableName, where, relationFilterCtx(filterCtx, tableName)) : void 0,
5811
+ deleted
5357
5812
  );
5358
5813
  const whereSql = cursorCondition ? (0, import_drizzle_orm8.and)(baseWhereSql, cursorCondition) : baseWhereSql;
5359
5814
  let distinctKeys;
@@ -5411,7 +5866,7 @@ var generateSelectArray2 = (db, tableName, tables, relationMap, orderArgs, filte
5411
5866
  };
5412
5867
  var generateSelectSingle2 = (db, tableName, tables, relationMap, orderArgs, filterArgs, fieldName, typeName, typeNameMapper, filterCtx, limits, policies) => {
5413
5868
  const queryBase = db.query[tableName];
5414
- const queryArgs = selectSingleArgs(orderArgs, filterArgs);
5869
+ const queryArgs = selectSingleArgs(orderArgs, filterArgs, policies?.softDelete, tableName);
5415
5870
  const table = tables[tableName];
5416
5871
  const pkNames = pgPrimaryKeyPropNames(table);
5417
5872
  return {
@@ -5440,7 +5895,7 @@ var generateSelectSingle2 = (db, tableName, tables, relationMap, orderArgs, filt
5440
5895
  scope
5441
5896
  });
5442
5897
  }
5443
- const { offset, orderBy, where } = args;
5898
+ const { offset, orderBy, where, deleted } = args;
5444
5899
  const selectedColumnsSql = extractSelectedColumnsFromTreeSQLFormat(
5445
5900
  parsedInfo.fieldsByTypeName[typeName],
5446
5901
  table,
@@ -5451,7 +5906,8 @@ var generateSelectSingle2 = (db, tableName, tables, relationMap, orderArgs, filt
5451
5906
  scope,
5452
5907
  tableName,
5453
5908
  table,
5454
- where ? extractFilters(table, tableName, where, relationFilterCtx(filterCtx, tableName)) : void 0
5909
+ where ? extractFilters(table, tableName, where, relationFilterCtx(filterCtx, tableName)) : void 0,
5910
+ deleted
5455
5911
  );
5456
5912
  if (whereSql) {
5457
5913
  q = q.where(whereSql);
@@ -5482,127 +5938,181 @@ var generateInsertArray2 = (db, tableName, table, tables, relationMap, baseType,
5482
5938
  }
5483
5939
  };
5484
5940
  const pkNames = pgPrimaryKeyPropNames(table);
5941
+ const hooks = policies?.onWrite?.(tableName, "insert");
5485
5942
  return {
5486
5943
  name: fieldName,
5487
5944
  resolver: async (_source, args, context, info) => {
5488
5945
  try {
5489
- return await runMutation(db, context, info, txCtx, async (executor) => {
5490
- if (!args.values.length) {
5491
- throw new import_graphql9.GraphQLError("No values were provided!");
5492
- }
5493
- const entries = nested?.enabled(tableName) ? args.values.map((values) => nested.split(tableName, values)) : void 0;
5494
- const nestedEntries = entries?.some((entry) => nested.hasOps(entry.ops)) ? entries : void 0;
5495
- const contextColumns = policies?.contextValues?.(tableName);
5496
- const scope = policies?.scope?.(context);
5497
- const input = nestedEntries ? [] : applyContextValuesAll(
5498
- remapFromGraphQLArrayInput(entries ? entries.map((entry) => entry.columns) : args.values, table),
5499
- contextColumns,
5500
- context
5501
- );
5502
- const parsedInfo = (0, import_graphql_parse_resolve_info3.parseResolveInfo)(info, {
5503
- deep: true
5504
- });
5505
- const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
5506
- relationMap,
5507
- tables,
5508
- tableName,
5509
- typeName,
5510
- typeNameMapper,
5511
- table,
5512
- pkNames,
5513
- parsedInfo,
5514
- limits,
5515
- scope
5516
- });
5517
- const returning = nestedEntries ? nested.withJoinColumns(tableName, mergedOps(nestedEntries), { ...columns }, table) : columns;
5518
- const runInsert = async (target, values) => {
5519
- let query = target.insert(table).values(values).returning(returning);
5520
- if (conflictDoNothing) {
5521
- query = query.onConflictDoNothing();
5946
+ return await runMutation(
5947
+ db,
5948
+ context,
5949
+ info,
5950
+ txCtx,
5951
+ async (executor) => {
5952
+ if (!args.values.length) {
5953
+ throw new import_graphql9.GraphQLError("No values were provided!");
5522
5954
  }
5523
- return await query;
5524
- };
5525
- const result = nestedEntries ? await writeWithNestedOps({
5526
- executor,
5527
- runtime: nested,
5528
- tableName,
5529
- entries: nestedEntries,
5530
- remapValues: (values) => applyContextValues(remapFromGraphQLSingleInput(values, table), contextColumns, context),
5531
- write: (tx, values) => runInsert(tx, [values]),
5532
- context
5533
- }) : await runInsert(executor, input);
5534
- const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, result, pkNames, withParams) : result;
5535
- return remapToGraphQLArrayOutput(enriched, tableName, table, relationMap);
5536
- });
5537
- } catch (e) {
5538
- throw toGraphQLError(e);
5539
- }
5540
- },
5541
- args: queryArgs
5542
- };
5543
- };
5544
- var generateInsertSingle2 = (db, tableName, table, tables, relationMap, baseType, fieldName, typeName, typeNameMapper, conflictDoNothing = false, txCtx, nested, limits, policies) => {
5545
- const queryArgs = {
5546
- values: {
5955
+ await runWriteHook(hooks, "before", {
5956
+ table: tableName,
5957
+ operation: "insert",
5958
+ single: false,
5959
+ args,
5960
+ context,
5961
+ info,
5962
+ tx: executor
5963
+ });
5964
+ const entries = nested?.enabled(tableName) ? args.values.map((values) => nested.split(tableName, values)) : void 0;
5965
+ const nestedEntries = entries?.some((entry) => nested.hasOps(entry.ops)) ? entries : void 0;
5966
+ const contextColumns = policies?.contextValues?.(tableName);
5967
+ const scope = policies?.scope?.(context);
5968
+ const input = nestedEntries ? [] : applyContextValuesAll(
5969
+ remapFromGraphQLArrayInput(entries ? entries.map((entry) => entry.columns) : args.values, table),
5970
+ contextColumns,
5971
+ context
5972
+ );
5973
+ const parsedInfo = (0, import_graphql_parse_resolve_info3.parseResolveInfo)(info, {
5974
+ deep: true
5975
+ });
5976
+ const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
5977
+ relationMap,
5978
+ tables,
5979
+ tableName,
5980
+ typeName,
5981
+ typeNameMapper,
5982
+ table,
5983
+ pkNames,
5984
+ parsedInfo,
5985
+ limits,
5986
+ scope
5987
+ });
5988
+ const returning = nestedEntries ? nested.withJoinColumns(tableName, mergedOps(nestedEntries), { ...columns }, table) : columns;
5989
+ const runInsert = async (target, values) => {
5990
+ let query = target.insert(table).values(values).returning(returning);
5991
+ if (conflictDoNothing) {
5992
+ query = query.onConflictDoNothing();
5993
+ }
5994
+ return await query;
5995
+ };
5996
+ const result = nestedEntries ? await writeWithNestedOps({
5997
+ executor,
5998
+ runtime: nested,
5999
+ tableName,
6000
+ entries: nestedEntries,
6001
+ remapValues: (values) => applyContextValues(remapFromGraphQLSingleInput(values, table), contextColumns, context),
6002
+ write: (tx, values) => runInsert(tx, [values]),
6003
+ context
6004
+ }) : await runInsert(executor, input);
6005
+ await runWriteHook(hooks, "after", {
6006
+ table: tableName,
6007
+ operation: "insert",
6008
+ single: false,
6009
+ args,
6010
+ rows: result,
6011
+ context,
6012
+ info,
6013
+ tx: executor
6014
+ });
6015
+ const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, result, pkNames, withParams) : result;
6016
+ return remapToGraphQLArrayOutput(enriched, tableName, table, relationMap);
6017
+ },
6018
+ !!hooks
6019
+ );
6020
+ } catch (e) {
6021
+ throw toGraphQLError(e);
6022
+ }
6023
+ },
6024
+ args: queryArgs
6025
+ };
6026
+ };
6027
+ var generateInsertSingle2 = (db, tableName, table, tables, relationMap, baseType, fieldName, typeName, typeNameMapper, conflictDoNothing = false, txCtx, nested, limits, policies) => {
6028
+ const queryArgs = {
6029
+ values: {
5547
6030
  type: new import_graphql9.GraphQLNonNull(baseType)
5548
6031
  }
5549
6032
  };
5550
6033
  const pkNames = pgPrimaryKeyPropNames(table);
6034
+ const hooks = policies?.onWrite?.(tableName, "insert");
5551
6035
  return {
5552
6036
  name: fieldName,
5553
6037
  resolver: async (_source, args, context, info) => {
5554
6038
  try {
5555
- return await runMutation(db, context, info, txCtx, async (executor) => {
5556
- const entry = nested?.enabled(tableName) ? nested.split(tableName, args.values) : void 0;
5557
- const nestedEntry = entry && nested.hasOps(entry.ops) ? entry : void 0;
5558
- const contextColumns = policies?.contextValues?.(tableName);
5559
- const scope = policies?.scope?.(context);
5560
- const input = nestedEntry ? {} : applyContextValues(
5561
- remapFromGraphQLSingleInput(entry ? entry.columns : args.values, table),
5562
- contextColumns,
5563
- context
5564
- );
5565
- const parsedInfo = (0, import_graphql_parse_resolve_info3.parseResolveInfo)(info, {
5566
- deep: true
5567
- });
5568
- const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
5569
- relationMap,
5570
- tables,
5571
- tableName,
5572
- typeName,
5573
- typeNameMapper,
5574
- table,
5575
- pkNames,
5576
- parsedInfo,
5577
- limits,
5578
- scope
5579
- });
5580
- const returning = nestedEntry ? nested.withJoinColumns(tableName, nestedEntry.ops, { ...columns }, table) : columns;
5581
- const runInsert = async (target, values) => {
5582
- let query = target.insert(table).values(values).returning(returning);
5583
- if (conflictDoNothing) {
5584
- query = query.onConflictDoNothing();
5585
- }
5586
- return await query;
5587
- };
5588
- const result = nestedEntry ? await writeWithNestedOps({
5589
- executor,
5590
- runtime: nested,
5591
- tableName,
5592
- entries: [nestedEntry],
5593
- remapValues: (values) => applyContextValues(remapFromGraphQLSingleInput(values, table), contextColumns, context),
5594
- write: runInsert,
5595
- context
5596
- }) : await runInsert(executor, input);
5597
- if (!result[0]) {
5598
- if (!conflictDoNothing) {
5599
- throw new import_graphql9.GraphQLError(`${fieldName}: the insert returned no row.`);
6039
+ return await runMutation(
6040
+ db,
6041
+ context,
6042
+ info,
6043
+ txCtx,
6044
+ async (executor) => {
6045
+ await runWriteHook(hooks, "before", {
6046
+ table: tableName,
6047
+ operation: "insert",
6048
+ single: true,
6049
+ args,
6050
+ context,
6051
+ info,
6052
+ tx: executor
6053
+ });
6054
+ const entry = nested?.enabled(tableName) ? nested.split(tableName, args.values) : void 0;
6055
+ const nestedEntry = entry && nested.hasOps(entry.ops) ? entry : void 0;
6056
+ const contextColumns = policies?.contextValues?.(tableName);
6057
+ const scope = policies?.scope?.(context);
6058
+ const input = nestedEntry ? {} : applyContextValues(
6059
+ remapFromGraphQLSingleInput(entry ? entry.columns : args.values, table),
6060
+ contextColumns,
6061
+ context
6062
+ );
6063
+ const parsedInfo = (0, import_graphql_parse_resolve_info3.parseResolveInfo)(info, {
6064
+ deep: true
6065
+ });
6066
+ const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
6067
+ relationMap,
6068
+ tables,
6069
+ tableName,
6070
+ typeName,
6071
+ typeNameMapper,
6072
+ table,
6073
+ pkNames,
6074
+ parsedInfo,
6075
+ limits,
6076
+ scope
6077
+ });
6078
+ const returning = nestedEntry ? nested.withJoinColumns(tableName, nestedEntry.ops, { ...columns }, table) : columns;
6079
+ const runInsert = async (target, values) => {
6080
+ let query = target.insert(table).values(values).returning(returning);
6081
+ if (conflictDoNothing) {
6082
+ query = query.onConflictDoNothing();
6083
+ }
6084
+ return await query;
6085
+ };
6086
+ const result = nestedEntry ? await writeWithNestedOps({
6087
+ executor,
6088
+ runtime: nested,
6089
+ tableName,
6090
+ entries: [nestedEntry],
6091
+ remapValues: (values) => applyContextValues(remapFromGraphQLSingleInput(values, table), contextColumns, context),
6092
+ write: runInsert,
6093
+ context
6094
+ }) : await runInsert(executor, input);
6095
+ await runWriteHook(hooks, "after", {
6096
+ table: tableName,
6097
+ operation: "insert",
6098
+ single: true,
6099
+ args,
6100
+ rows: result,
6101
+ context,
6102
+ info,
6103
+ tx: executor
6104
+ });
6105
+ if (!result[0]) {
6106
+ if (!conflictDoNothing) {
6107
+ throw new import_graphql9.GraphQLError(`${fieldName}: the insert returned no row.`);
6108
+ }
6109
+ return void 0;
5600
6110
  }
5601
- return void 0;
5602
- }
5603
- const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, result, pkNames, withParams) : result;
5604
- return remapToGraphQLSingleOutput(enriched[0], tableName, table, relationMap);
5605
- });
6111
+ const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, result, pkNames, withParams) : result;
6112
+ return remapToGraphQLSingleOutput(enriched[0], tableName, table, relationMap);
6113
+ },
6114
+ !!hooks
6115
+ );
5606
6116
  } catch (e) {
5607
6117
  throw toGraphQLError(e);
5608
6118
  }
@@ -5621,69 +6131,96 @@ var generateUpsert2 = (db, tableName, table, tables, relationMap, baseType, onCo
5621
6131
  }
5622
6132
  };
5623
6133
  const pkNames = pgPrimaryKeyPropNames(table);
6134
+ const hooks = policies?.onWrite?.(tableName, "upsert");
5624
6135
  return {
5625
6136
  name: fieldName,
5626
6137
  resolver: async (_source, args, context, info) => {
5627
6138
  try {
5628
- return await runMutation(db, context, info, txCtx, async (executor) => {
5629
- const supplied = single ? [args.values] : args.values;
5630
- if (!supplied.length) {
5631
- throw new import_graphql9.GraphQLError("No values were provided!");
5632
- }
5633
- const entries = nested?.enabled(tableName) ? supplied.map((values) => nested.split(tableName, values)) : void 0;
5634
- const nestedEntries = entries?.some((entry) => nested.hasOps(entry.ops)) ? entries : void 0;
5635
- const contextColumns = policies?.contextValues?.(tableName);
5636
- const scope = policies?.scope?.(context);
5637
- const input = nestedEntries ? [] : applyContextValuesAll(
5638
- remapFromGraphQLArrayInput(entries ? entries.map((entry) => entry.columns) : supplied, table),
5639
- contextColumns,
5640
- context
5641
- );
5642
- const parsedInfo = (0, import_graphql_parse_resolve_info3.parseResolveInfo)(info, { deep: true });
5643
- const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
5644
- relationMap,
5645
- tables,
5646
- tableName,
5647
- typeName,
5648
- typeNameMapper,
5649
- table,
5650
- pkNames,
5651
- parsedInfo,
5652
- limits,
5653
- scope
5654
- });
5655
- const returning = nestedEntries ? nested.withJoinColumns(tableName, mergedOps(nestedEntries), { ...columns }, table) : columns;
5656
- const runUpsert = async (target, values) => {
5657
- const plan = resolveConflictPlan({
6139
+ return await runMutation(
6140
+ db,
6141
+ context,
6142
+ info,
6143
+ txCtx,
6144
+ async (executor) => {
6145
+ const supplied = single ? [args.values] : args.values;
6146
+ if (!supplied.length) {
6147
+ throw new import_graphql9.GraphQLError("No values were provided!");
6148
+ }
6149
+ await runWriteHook(hooks, "before", {
6150
+ table: tableName,
6151
+ operation: "upsert",
6152
+ single,
6153
+ args,
6154
+ context,
6155
+ info,
6156
+ tx: executor
6157
+ });
6158
+ const entries = nested?.enabled(tableName) ? supplied.map((values) => nested.split(tableName, values)) : void 0;
6159
+ const nestedEntries = entries?.some((entry) => nested.hasOps(entry.ops)) ? entries : void 0;
6160
+ const contextColumns = policies?.contextValues?.(tableName);
6161
+ const scope = policies?.scope?.(context);
6162
+ const input = nestedEntries ? [] : applyContextValuesAll(
6163
+ remapFromGraphQLArrayInput(entries ? entries.map((entry) => entry.columns) : supplied, table),
6164
+ contextColumns,
6165
+ context
6166
+ );
6167
+ const parsedInfo = (0, import_graphql_parse_resolve_info3.parseResolveInfo)(info, { deep: true });
6168
+ const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
6169
+ relationMap,
6170
+ tables,
6171
+ tableName,
6172
+ typeName,
6173
+ typeNameMapper,
5658
6174
  table,
5659
- values,
5660
- onConflict: args.onConflict,
5661
6175
  pkNames,
5662
- uniqueSets,
5663
- excludedRef: excludedColumnRef,
5664
- withTarget: true,
5665
- buildWhere: (where) => extractFilters(table, tableName, where, relationFilterCtx(filterCtx, tableName))
6176
+ parsedInfo,
6177
+ limits,
6178
+ scope
5666
6179
  });
5667
- let query = target.insert(table).values(values).returning(returning);
5668
- const setWhere = withScope(scope, tableName, table, plan.setWhere);
5669
- query = plan.action === "NOTHING" ? query.onConflictDoNothing(plan.target ? { target: plan.target } : void 0) : query.onConflictDoUpdate({ target: plan.target, set: plan.set, setWhere });
5670
- return await query;
5671
- };
5672
- const result = nestedEntries ? await writeWithNestedOps({
5673
- executor,
5674
- runtime: nested,
5675
- tableName,
5676
- entries: nestedEntries,
5677
- remapValues: (values) => applyContextValues(remapFromGraphQLSingleInput(values, table), contextColumns, context),
5678
- write: (tx, values) => runUpsert(tx, [values]),
5679
- context
5680
- }) : await runUpsert(executor, input);
5681
- if (single && !result[0]) {
5682
- return void 0;
5683
- }
5684
- const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, result, pkNames, withParams) : result;
5685
- return single ? remapToGraphQLSingleOutput(enriched[0], tableName, table, relationMap) : remapToGraphQLArrayOutput(enriched, tableName, table, relationMap);
5686
- });
6180
+ const returning = nestedEntries ? nested.withJoinColumns(tableName, mergedOps(nestedEntries), { ...columns }, table) : columns;
6181
+ const runUpsert = async (target, values) => {
6182
+ const plan = resolveConflictPlan({
6183
+ table,
6184
+ values,
6185
+ onConflict: args.onConflict,
6186
+ pkNames,
6187
+ uniqueSets,
6188
+ excludedRef: excludedColumnRef,
6189
+ withTarget: true,
6190
+ buildWhere: (where) => extractFilters(table, tableName, where, relationFilterCtx(filterCtx, tableName))
6191
+ });
6192
+ let query = target.insert(table).values(values).returning(returning);
6193
+ const setWhere = withScope(scope, tableName, table, plan.setWhere);
6194
+ query = plan.action === "NOTHING" ? query.onConflictDoNothing(plan.target ? { target: plan.target } : void 0) : query.onConflictDoUpdate({ target: plan.target, set: plan.set, setWhere });
6195
+ return await query;
6196
+ };
6197
+ const result = nestedEntries ? await writeWithNestedOps({
6198
+ executor,
6199
+ runtime: nested,
6200
+ tableName,
6201
+ entries: nestedEntries,
6202
+ remapValues: (values) => applyContextValues(remapFromGraphQLSingleInput(values, table), contextColumns, context),
6203
+ write: (tx, values) => runUpsert(tx, [values]),
6204
+ context
6205
+ }) : await runUpsert(executor, input);
6206
+ await runWriteHook(hooks, "after", {
6207
+ table: tableName,
6208
+ operation: "upsert",
6209
+ single,
6210
+ args,
6211
+ rows: result,
6212
+ context,
6213
+ info,
6214
+ tx: executor
6215
+ });
6216
+ if (single && !result[0]) {
6217
+ return void 0;
6218
+ }
6219
+ const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, result, pkNames, withParams) : result;
6220
+ return single ? remapToGraphQLSingleOutput(enriched[0], tableName, table, relationMap) : remapToGraphQLArrayOutput(enriched, tableName, table, relationMap);
6221
+ },
6222
+ !!hooks
6223
+ );
5687
6224
  } catch (e) {
5688
6225
  throw toGraphQLError(e);
5689
6226
  }
@@ -5701,75 +6238,102 @@ var generateUpdate2 = (db, tableName, table, tables, relationMap, setArgs, filte
5701
6238
  }
5702
6239
  };
5703
6240
  const pkNames = pgPrimaryKeyPropNames(table);
6241
+ const hooks = policies?.onWrite?.(tableName, "update");
5704
6242
  return {
5705
6243
  name: fieldName,
5706
6244
  resolver: async (_source, args, context, info) => {
5707
6245
  try {
5708
- return await runMutation(db, context, info, txCtx, async (executor) => {
5709
- const { where, set } = args;
5710
- const scope = policies?.scope?.(context);
5711
- const parsedInfo = (0, import_graphql_parse_resolve_info3.parseResolveInfo)(info, {
5712
- deep: true
5713
- });
5714
- const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
5715
- relationMap,
5716
- tables,
5717
- tableName,
5718
- typeName,
5719
- typeNameMapper,
5720
- table,
5721
- pkNames,
5722
- parsedInfo,
5723
- limits,
5724
- scope
5725
- });
5726
- const entry = nested?.enabled(tableName) ? nested.split(tableName, set) : void 0;
5727
- const nestedOps = entry && nested.hasOps(entry.ops) ? entry.ops : void 0;
5728
- const input = stripContextValues(
5729
- remapUpdateInput(entry ? entry.columns : set, table, tableName),
5730
- policies?.contextValues?.(tableName)
5731
- );
5732
- if (!Object.keys(input).length && !nestedOps) {
5733
- throw new import_graphql9.GraphQLError("Unable to update with no values specified!");
5734
- }
5735
- const relationCtx = relationFilterCtx(filterCtx, tableName);
5736
- const filters = withScope(
5737
- scope,
5738
- tableName,
5739
- table,
5740
- single || requireWhere ? extractRequiredFilters(table, tableName, where, fieldName, relationCtx) : where ? extractFilters(table, tableName, where, relationCtx) : void 0
5741
- );
5742
- if (single) {
5743
- await assertSingleMatch(executor, table, filters, fieldName);
5744
- }
5745
- const returning = nestedOps ? nested.withJoinColumns(tableName, nestedOps, { ...columns }, table) : columns;
5746
- const runUpdate = async (target, values) => {
5747
- const writes = Object.keys(values).length > 0;
5748
- let query = writes ? target.update(table).set(values) : target.select(returning).from(table);
5749
- if (filters) {
5750
- query = query.where(filters);
6246
+ return await runMutation(
6247
+ db,
6248
+ context,
6249
+ info,
6250
+ txCtx,
6251
+ async (executor) => {
6252
+ const { where, set } = args;
6253
+ const scope = policies?.scope?.(context);
6254
+ await runWriteHook(hooks, "before", {
6255
+ table: tableName,
6256
+ operation: "update",
6257
+ single,
6258
+ args,
6259
+ context,
6260
+ info,
6261
+ tx: executor
6262
+ });
6263
+ const parsedInfo = (0, import_graphql_parse_resolve_info3.parseResolveInfo)(info, {
6264
+ deep: true
6265
+ });
6266
+ const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
6267
+ relationMap,
6268
+ tables,
6269
+ tableName,
6270
+ typeName,
6271
+ typeNameMapper,
6272
+ table,
6273
+ pkNames,
6274
+ parsedInfo,
6275
+ limits,
6276
+ scope
6277
+ });
6278
+ const entry = nested?.enabled(tableName) ? nested.split(tableName, set) : void 0;
6279
+ const nestedOps = entry && nested.hasOps(entry.ops) ? entry.ops : void 0;
6280
+ const input = stripContextValues(
6281
+ remapUpdateInput(entry ? entry.columns : set, table, tableName),
6282
+ policies?.contextValues?.(tableName)
6283
+ );
6284
+ if (!Object.keys(input).length && !nestedOps) {
6285
+ throw new import_graphql9.GraphQLError("Unable to update with no values specified!");
5751
6286
  }
5752
- return await (writes ? query.returning(returning) : query);
5753
- };
5754
- const result = nestedOps ? await updateWithNestedOps({
5755
- executor,
5756
- runtime: nested,
5757
- tableName,
5758
- columns: input,
5759
- ops: nestedOps,
5760
- remapValues: (values) => values,
5761
- write: runUpdate,
5762
- context
5763
- }) : await runUpdate(executor, input);
5764
- if (single && result.length > 1) {
5765
- throw new import_graphql9.GraphQLError(`${fieldName}: 'where' matched more than one row!`);
5766
- }
5767
- if (single && !result[0]) {
5768
- return void 0;
5769
- }
5770
- const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, result, pkNames, withParams) : result;
5771
- return single ? remapToGraphQLSingleOutput(enriched[0], tableName, table, relationMap) : remapToGraphQLArrayOutput(enriched, tableName, table, relationMap);
5772
- });
6287
+ const relationCtx = relationFilterCtx(filterCtx, tableName);
6288
+ const filters = withScope(
6289
+ scope,
6290
+ tableName,
6291
+ table,
6292
+ single || requireWhere ? extractRequiredFilters(table, tableName, where, fieldName, relationCtx) : where ? extractFilters(table, tableName, where, relationCtx) : void 0
6293
+ );
6294
+ if (single) {
6295
+ await assertSingleMatch(executor, table, filters, fieldName);
6296
+ }
6297
+ const returning = nestedOps ? nested.withJoinColumns(tableName, nestedOps, { ...columns }, table) : columns;
6298
+ const runUpdate = async (target, values) => {
6299
+ const writes = Object.keys(values).length > 0;
6300
+ let query = writes ? target.update(table).set(values) : target.select(returning).from(table);
6301
+ if (filters) {
6302
+ query = query.where(filters);
6303
+ }
6304
+ return await (writes ? query.returning(returning) : query);
6305
+ };
6306
+ const result = nestedOps ? await updateWithNestedOps({
6307
+ executor,
6308
+ runtime: nested,
6309
+ tableName,
6310
+ columns: input,
6311
+ ops: nestedOps,
6312
+ remapValues: (values) => values,
6313
+ write: runUpdate,
6314
+ context
6315
+ }) : await runUpdate(executor, input);
6316
+ await runWriteHook(hooks, "after", {
6317
+ table: tableName,
6318
+ operation: "update",
6319
+ single,
6320
+ args,
6321
+ rows: result,
6322
+ context,
6323
+ info,
6324
+ tx: executor
6325
+ });
6326
+ if (single && result.length > 1) {
6327
+ throw new import_graphql9.GraphQLError(`${fieldName}: 'where' matched more than one row!`);
6328
+ }
6329
+ if (single && !result[0]) {
6330
+ return void 0;
6331
+ }
6332
+ const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, result, pkNames, withParams) : result;
6333
+ return single ? remapToGraphQLSingleOutput(enriched[0], tableName, table, relationMap) : remapToGraphQLArrayOutput(enriched, tableName, table, relationMap);
6334
+ },
6335
+ !!hooks
6336
+ );
5773
6337
  } catch (e) {
5774
6338
  throw toGraphQLError(e);
5775
6339
  }
@@ -5784,94 +6348,121 @@ var generateUpdateMany2 = (db, tableName, table, tables, relationMap, updateMany
5784
6348
  }
5785
6349
  };
5786
6350
  const pkNames = pgPrimaryKeyPropNames(table);
6351
+ const hooks = policies?.onWrite?.(tableName, "updateMany");
5787
6352
  return {
5788
6353
  name: fieldName,
5789
6354
  resolver: async (_source, args, context, info) => {
5790
6355
  try {
5791
- return await runMutation(db, context, info, txCtx, async (executor) => {
5792
- const { updates } = args;
5793
- if (!updates.length) {
5794
- throw new import_graphql9.GraphQLError("No updates were provided!");
5795
- }
5796
- const scope = policies?.scope?.(context);
5797
- const contextColumns = policies?.contextValues?.(tableName);
5798
- const parsedInfo = (0, import_graphql_parse_resolve_info3.parseResolveInfo)(info, {
5799
- deep: true
5800
- });
5801
- const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
5802
- relationMap,
5803
- tables,
5804
- tableName,
5805
- typeName,
5806
- typeNameMapper,
5807
- table,
5808
- pkNames,
5809
- parsedInfo,
5810
- limits,
5811
- scope
5812
- });
5813
- const entries = updates.map(({ where, set }) => {
5814
- const split = nested?.enabled(tableName) ? nested.split(tableName, set) : void 0;
5815
- const ops = split && nested.hasOps(split.ops) ? split.ops : void 0;
5816
- const input = stripContextValues(
5817
- remapUpdateInput(split ? split.columns : set, table, tableName),
5818
- contextColumns
5819
- );
5820
- if (!Object.keys(input).length && !ops) {
5821
- throw new import_graphql9.GraphQLError("Unable to update with no values specified!");
6356
+ return await runMutation(
6357
+ db,
6358
+ context,
6359
+ info,
6360
+ txCtx,
6361
+ async (executor) => {
6362
+ const { updates } = args;
6363
+ if (!updates.length) {
6364
+ throw new import_graphql9.GraphQLError("No updates were provided!");
5822
6365
  }
5823
- return {
5824
- set: input,
5825
- ops,
5826
- filters: withScope(
5827
- scope,
5828
- tableName,
5829
- table,
5830
- where ? extractFilters(table, tableName, where, relationFilterCtx(filterCtx, tableName)) : void 0
5831
- )
5832
- };
5833
- });
5834
- const returning = entries.some((entry) => entry.ops) ? nested.withJoinColumns(
5835
- tableName,
5836
- mergedOps(entries.map((entry) => ({ ops: entry.ops ?? {} }))),
5837
- {
5838
- ...columns
5839
- },
5840
- table
5841
- ) : columns;
5842
- const perEntry = await executor.transaction(async (tx) => {
5843
- const results = [];
5844
- for (const entry of entries) {
5845
- const values = entry.ops ? { ...entry.set, ...await nested.applyParentSide(tx, tableName, entry.ops, context) } : entry.set;
5846
- const writes = Object.keys(values).length > 0;
5847
- let query = writes ? tx.update(table).set(values) : tx.select(returning).from(table);
5848
- if (entry.filters) {
5849
- query = query.where(entry.filters);
6366
+ await runWriteHook(hooks, "before", {
6367
+ table: tableName,
6368
+ operation: "updateMany",
6369
+ single: false,
6370
+ args,
6371
+ context,
6372
+ info,
6373
+ tx: executor
6374
+ });
6375
+ const scope = policies?.scope?.(context);
6376
+ const contextColumns = policies?.contextValues?.(tableName);
6377
+ const parsedInfo = (0, import_graphql_parse_resolve_info3.parseResolveInfo)(info, {
6378
+ deep: true
6379
+ });
6380
+ const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
6381
+ relationMap,
6382
+ tables,
6383
+ tableName,
6384
+ typeName,
6385
+ typeNameMapper,
6386
+ table,
6387
+ pkNames,
6388
+ parsedInfo,
6389
+ limits,
6390
+ scope
6391
+ });
6392
+ const entries = updates.map(({ where, set }) => {
6393
+ const split = nested?.enabled(tableName) ? nested.split(tableName, set) : void 0;
6394
+ const ops = split && nested.hasOps(split.ops) ? split.ops : void 0;
6395
+ const input = stripContextValues(
6396
+ remapUpdateInput(split ? split.columns : set, table, tableName),
6397
+ contextColumns
6398
+ );
6399
+ if (!Object.keys(input).length && !ops) {
6400
+ throw new import_graphql9.GraphQLError("Unable to update with no values specified!");
5850
6401
  }
5851
- const rows = await (writes ? query.returning(returning) : query);
5852
- if (entry.ops) {
5853
- await nested.applyChildSide(tx, tableName, entry.ops, rows, context);
6402
+ return {
6403
+ set: input,
6404
+ ops,
6405
+ filters: withScope(
6406
+ scope,
6407
+ tableName,
6408
+ table,
6409
+ where ? extractFilters(table, tableName, where, relationFilterCtx(filterCtx, tableName)) : void 0
6410
+ )
6411
+ };
6412
+ });
6413
+ const returning = entries.some((entry) => entry.ops) ? nested.withJoinColumns(
6414
+ tableName,
6415
+ mergedOps(entries.map((entry) => ({ ops: entry.ops ?? {} }))),
6416
+ {
6417
+ ...columns
6418
+ },
6419
+ table
6420
+ ) : columns;
6421
+ const perEntry = await executor.transaction(async (tx) => {
6422
+ const results = [];
6423
+ for (const entry of entries) {
6424
+ const values = entry.ops ? { ...entry.set, ...await nested.applyParentSide(tx, tableName, entry.ops, context) } : entry.set;
6425
+ const writes = Object.keys(values).length > 0;
6426
+ let query = writes ? tx.update(table).set(values) : tx.select(returning).from(table);
6427
+ if (entry.filters) {
6428
+ query = query.where(entry.filters);
6429
+ }
6430
+ const rows = await (writes ? query.returning(returning) : query);
6431
+ if (entry.ops) {
6432
+ await nested.applyChildSide(tx, tableName, entry.ops, rows, context);
6433
+ }
6434
+ results.push(rows);
5854
6435
  }
5855
- results.push(rows);
5856
- }
5857
- return results;
5858
- });
5859
- const flatRows = perEntry.flat();
5860
- const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, flatRows, pkNames, withParams) : flatRows;
5861
- const output = [];
5862
- let offset = 0;
5863
- for (const rows of perEntry) {
5864
- if (!rows.length) {
5865
- output.push(null);
5866
- continue;
5867
- }
5868
- for (let i = 0; i < rows.length; i++) {
5869
- output.push(remapToGraphQLSingleOutput(enriched[offset + i], tableName, table, relationMap));
6436
+ return results;
6437
+ });
6438
+ const flatRows = perEntry.flat();
6439
+ await runWriteHook(hooks, "after", {
6440
+ table: tableName,
6441
+ operation: "updateMany",
6442
+ single: false,
6443
+ args,
6444
+ rows: flatRows,
6445
+ context,
6446
+ info,
6447
+ tx: executor
6448
+ });
6449
+ const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, flatRows, pkNames, withParams) : flatRows;
6450
+ const output = [];
6451
+ let offset = 0;
6452
+ for (const rows of perEntry) {
6453
+ if (!rows.length) {
6454
+ output.push(null);
6455
+ continue;
6456
+ }
6457
+ for (let i = 0; i < rows.length; i++) {
6458
+ output.push(remapToGraphQLSingleOutput(enriched[offset + i], tableName, table, relationMap));
6459
+ }
6460
+ offset += rows.length;
5870
6461
  }
5871
- offset += rows.length;
5872
- }
5873
- return output;
5874
- });
6462
+ return output;
6463
+ },
6464
+ !!hooks
6465
+ );
5875
6466
  } catch (e) {
5876
6467
  throw toGraphQLError(e);
5877
6468
  }
@@ -5879,7 +6470,10 @@ var generateUpdateMany2 = (db, tableName, table, tables, relationMap, updateMany
5879
6470
  args: queryArgs
5880
6471
  };
5881
6472
  };
5882
- var generateDelete2 = (db, tableName, table, filterArgs, fieldName, typeName, single, requireWhere, filterCtx, selectionCtx, txCtx, policies) => {
6473
+ var generateDelete2 = (db, tableName, table, filterArgs, fieldName, typeName, single, requireWhere, filterCtx, selectionCtx, txCtx, policies, restore = false) => {
6474
+ const softDelete = policies?.softDelete?.(tableName);
6475
+ const operation = restore ? "restore" : "delete";
6476
+ const hooks = policies?.onWrite?.(tableName, operation);
5883
6477
  const queryArgs = {
5884
6478
  where: {
5885
6479
  type: single || requireWhere ? new import_graphql9.GraphQLNonNull(filterArgs) : filterArgs
@@ -5889,41 +6483,68 @@ var generateDelete2 = (db, tableName, table, filterArgs, fieldName, typeName, si
5889
6483
  name: fieldName,
5890
6484
  resolver: async (_source, args, context, info) => {
5891
6485
  try {
5892
- return await runMutation(db, context, info, txCtx, async (executor) => {
5893
- const { where } = args;
5894
- const scope = policies?.scope?.(context);
5895
- const parsedInfo = (0, import_graphql_parse_resolve_info3.parseResolveInfo)(info, {
5896
- deep: true
5897
- });
5898
- const columns = extractSelectedColumnsFromTreeSQLFormat(
5899
- parsedInfo.fieldsByTypeName[typeName],
5900
- table,
5901
- selectionCtx
5902
- );
5903
- const relationCtx = relationFilterCtx(filterCtx, tableName);
5904
- const filters = withScope(
5905
- scope,
5906
- tableName,
5907
- table,
5908
- single || requireWhere ? extractRequiredFilters(table, tableName, where, fieldName, relationCtx) : where ? extractFilters(table, tableName, where, relationCtx) : void 0
5909
- );
5910
- if (single) {
5911
- await assertSingleMatch(executor, table, filters, fieldName);
5912
- }
5913
- let query = executor.delete(table);
5914
- if (filters) {
5915
- query = query.where(filters);
5916
- }
5917
- query = query.returning(columns);
5918
- const result = await query;
5919
- if (single && result.length > 1) {
5920
- throw new import_graphql9.GraphQLError(`${fieldName}: 'where' matched more than one row!`);
5921
- }
5922
- if (single) {
5923
- return result[0] ? remapToGraphQLSingleOutput(result[0], tableName, table) : void 0;
5924
- }
5925
- return remapToGraphQLArrayOutput(result, tableName, table);
5926
- });
6486
+ return await runMutation(
6487
+ db,
6488
+ context,
6489
+ info,
6490
+ txCtx,
6491
+ async (executor) => {
6492
+ const { where } = args;
6493
+ const scope = policies?.scope?.(context);
6494
+ await runWriteHook(hooks, "before", {
6495
+ table: tableName,
6496
+ operation,
6497
+ single,
6498
+ args,
6499
+ context,
6500
+ info,
6501
+ tx: executor
6502
+ });
6503
+ const parsedInfo = (0, import_graphql_parse_resolve_info3.parseResolveInfo)(info, {
6504
+ deep: true
6505
+ });
6506
+ const columns = extractSelectedColumnsFromTreeSQLFormat(
6507
+ parsedInfo.fieldsByTypeName[typeName],
6508
+ table,
6509
+ selectionCtx
6510
+ );
6511
+ const relationCtx = relationFilterCtx(filterCtx, tableName);
6512
+ const filters = withScope(
6513
+ scope,
6514
+ tableName,
6515
+ table,
6516
+ single || requireWhere ? extractRequiredFilters(table, tableName, where, fieldName, relationCtx) : where ? extractFilters(table, tableName, where, relationCtx) : void 0,
6517
+ restore ? "ONLY" : void 0
6518
+ );
6519
+ if (single) {
6520
+ await assertSingleMatch(executor, table, filters, fieldName);
6521
+ }
6522
+ let query = softDelete ? executor.update(table).set({ [softDelete.columnName]: restore ? softDelete.writeRestored : softDelete.writeDeleted() }) : executor.delete(table);
6523
+ if (filters) {
6524
+ query = query.where(filters);
6525
+ }
6526
+ query = query.returning(columns);
6527
+ const result = await query;
6528
+ await runWriteHook(hooks, "after", {
6529
+ table: tableName,
6530
+ operation,
6531
+ single,
6532
+ args,
6533
+ rows: result,
6534
+ context,
6535
+ info,
6536
+ tx: executor
6537
+ });
6538
+ if (single && result.length > 1) {
6539
+ throw new import_graphql9.GraphQLError(`${fieldName}: 'where' matched more than one row!`);
6540
+ }
6541
+ if (single) {
6542
+ return result[0] ? remapToGraphQLSingleOutput(result[0], tableName, table) : void 0;
6543
+ }
6544
+ return remapToGraphQLArrayOutput(result, tableName, table);
6545
+ },
6546
+ !!hooks
6547
+ );
5927
6548
  } catch (e) {
5928
6549
  throw toGraphQLError(e);
5929
6550
  }
@@ -5964,16 +6585,17 @@ function generateSchemaData2(db, schema, relations, options) {
5964
6585
  attachTargetPrimaryKeys(namedRelations, tables, pgPrimaryKeyPropNames);
5965
6586
  const eagerRelations = pruneNonEagerRelations(namedRelations, shouldEagerLoad);
5966
6587
  const filterCtx = { tables, relationMap: namedRelations };
5967
- const scopes = options.policies?.scope;
5968
- const contextValuesOf = options.policies?.contextValues;
5969
- const policies = bindPolicies(options.policies, filterCtx);
6588
+ const tablePolicies = options.policies;
6589
+ const contextValuesOf = tablePolicies?.contextValues;
6590
+ const softDeleteOf = tablePolicies?.softDelete;
6591
+ const policies = bindPolicies(tablePolicies, filterCtx);
5970
6592
  const resolverFactory = createRelationResolverFactory(
5971
6593
  db,
5972
6594
  tables,
5973
6595
  "nulls-largest",
5974
6596
  filterCtx,
5975
6597
  limits,
5976
- scopes
6598
+ tablePolicies
5977
6599
  );
5978
6600
  const cacheCtx = {
5979
6601
  genericFilterCache: /* @__PURE__ */ new Map(),
@@ -5992,6 +6614,7 @@ function generateSchemaData2(db, schema, relations, options) {
5992
6614
  docs: options.docs ?? {},
5993
6615
  primaryKeyOf: (name) => tables[name] ? pgPrimaryKeyPropNames(tables[name]) : [],
5994
6616
  contextValuesOf,
6617
+ softDeleteOf,
5995
6618
  featureOf
5996
6619
  };
5997
6620
  const nestedPlans = features.nestedWrites ? buildNestedWritePlans(
@@ -6002,8 +6625,13 @@ function generateSchemaData2(db, schema, relations, options) {
6002
6625
  extractRelationJoinColumns
6003
6626
  ) : void 0;
6004
6627
  const nestedTypes = nestedPlans ? createNestedWriteTypes({ plans: nestedPlans, cacheCtx, typeNameMapper, insertPrefix: prefixes.insert }) : void 0;
6005
- const nestedRuntime = nestedPlans ? createNestedWriteRuntime({ plans: nestedPlans, filterCtx, scopes, contextValues: contextValuesOf }) : void 0;
6006
- const relationAggregateFactory = anyTable("relationAggregates") ? createRelationAggregateFactory(db, tables, cacheCtx, typeNameMapper, filterCtx, scopes) : void 0;
6628
+ const nestedRuntime = nestedPlans ? createNestedWriteRuntime({
6629
+ plans: nestedPlans,
6630
+ filterCtx,
6631
+ policies: tablePolicies,
6632
+ contextValues: contextValuesOf
6633
+ }) : void 0;
6634
+ const relationAggregateFactory = anyTable("relationAggregates") ? createRelationAggregateFactory(db, tables, cacheCtx, typeNameMapper, filterCtx, tablePolicies) : void 0;
6007
6635
  const queries = {};
6008
6636
  const mutations = {};
6009
6637
  const mutationTxCtx = createMutationTxCtx(options.transactions);
@@ -6049,8 +6677,11 @@ function generateSchemaData2(db, schema, relations, options) {
6049
6677
  updateCountFieldName,
6050
6678
  deleteFieldName,
6051
6679
  deleteSingleFieldName,
6680
+ restoreFieldName,
6681
+ restoreSingleFieldName,
6052
6682
  deleteCountFieldName
6053
6683
  } = computeResolverFieldNames(tableName, typeNameMapper, prefixes, suffixes);
6684
+ const softDeleteInfo = softDeleteOf?.(tableName);
6054
6685
  const selectArrGenerated = generateSelectArray2(
6055
6686
  db,
6056
6687
  tableName,
@@ -6241,6 +6872,36 @@ function generateSchemaData2(db, schema, relations, options) {
6241
6872
  mutationTxCtx,
6242
6873
  policies
6243
6874
  ) : void 0;
6875
+ const restoreGenerated = softDeleteInfo ? generateDelete2(
6876
+ db,
6877
+ tableName,
6878
+ schema[tableName],
6879
+ tableFilters,
6880
+ restoreFieldName,
6881
+ typeName,
6882
+ false,
6883
+ tableFeatures.requireWhere,
6884
+ filterCtx,
6885
+ { tableName, relationMap: namedRelations, tables },
6886
+ mutationTxCtx,
6887
+ policies,
6888
+ true
6889
+ ) : void 0;
6890
+ const restoreSingleGenerated = softDeleteInfo ? generateDelete2(
6891
+ db,
6892
+ tableName,
6893
+ schema[tableName],
6894
+ tableFilters,
6895
+ restoreSingleFieldName,
6896
+ typeName,
6897
+ true,
6898
+ tableFeatures.requireWhere,
6899
+ filterCtx,
6900
+ { tableName, relationMap: namedRelations, tables },
6901
+ mutationTxCtx,
6902
+ policies,
6903
+ true
6904
+ ) : void 0;
6244
6905
  const updateCountGenerated = tableFeatures.update && tableFeatures.countMutations ? generateWriteCount({
6245
6906
  db,
6246
6907
  tableName,
@@ -6274,7 +6935,7 @@ function generateSchemaData2(db, schema, relations, options) {
6274
6935
  aggregateFieldName,
6275
6936
  tableFilters,
6276
6937
  filterCtx,
6277
- scopes
6938
+ tablePolicies
6278
6939
  ) : void 0;
6279
6940
  const groupByType = tableFeatures.aggregates && tableFeatures.groupBy ? generateGroupByType(schema[tableName], tableName, typeName, cacheCtx) : void 0;
6280
6941
  const groupByEnum = groupByType ? generateGroupByEnum(schema[tableName], tableName, typeName) : void 0;
@@ -6289,7 +6950,7 @@ function generateSchemaData2(db, schema, relations, options) {
6289
6950
  groupByEnum,
6290
6951
  havingInput,
6291
6952
  filterCtx,
6292
- scopes
6953
+ tablePolicies
6293
6954
  ) : void 0;
6294
6955
  queries[selectArrGenerated.name] = {
6295
6956
  type: selectArrOutput,
@@ -6426,6 +7087,26 @@ function generateSchemaData2(db, schema, relations, options) {
6426
7087
  }
6427
7088
  };
6428
7089
  }
7090
+ if (restoreGenerated) {
7091
+ mutations[restoreGenerated.name] = {
7092
+ type: arrTableItemOutput,
7093
+ args: restoreGenerated.args,
7094
+ resolve: restoreGenerated.resolver,
7095
+ extensions: {
7096
+ drizzle: drizzleMeta({ kind: "mutation", operation: "restore", single: false, targetArg: "where" })
7097
+ }
7098
+ };
7099
+ }
7100
+ if (restoreSingleGenerated) {
7101
+ mutations[restoreSingleGenerated.name] = {
7102
+ type: singleTableItemOutput,
7103
+ args: restoreSingleGenerated.args,
7104
+ resolve: restoreSingleGenerated.resolver,
7105
+ extensions: {
7106
+ drizzle: drizzleMeta({ kind: "mutation", operation: "restore", single: true, targetArg: "where" })
7107
+ }
7108
+ };
7109
+ }
6429
7110
  if (updateCountGenerated) {
6430
7111
  mutations[updateCountGenerated.name] = {
6431
7112
  type: new import_graphql9.GraphQLNonNull(import_graphql9.GraphQLInt),
@@ -6504,7 +7185,9 @@ var generateSelectArray3 = (db, tableName, tables, relationMap, orderArgs, filte
6504
7185
  const queryArgs = selectArrayArgs(
6505
7186
  orderArgs,
6506
7187
  filterArgs,
6507
- distinctEnabled ? generateDistinctEnum(table, typeName) : void 0
7188
+ distinctEnabled ? generateDistinctEnum(table, typeName) : void 0,
7189
+ policies?.softDelete,
7190
+ tableName
6508
7191
  );
6509
7192
  return {
6510
7193
  name: fieldName,
@@ -6546,7 +7229,7 @@ var generateSelectSingle3 = (db, tableName, tables, relationMap, orderArgs, filt
6546
7229
  `Drizzle-GraphQL Error: Table ${tableName} not found in drizzle instance. Did you forget to pass schema to drizzle constructor?`
6547
7230
  );
6548
7231
  }
6549
- const queryArgs = selectSingleArgs(orderArgs, filterArgs);
7232
+ const queryArgs = selectSingleArgs(orderArgs, filterArgs, policies?.softDelete, tableName);
6550
7233
  const table = tables[tableName];
6551
7234
  const pkNames = sqlitePrimaryKeyPropNames(table);
6552
7235
  return {
@@ -6587,127 +7270,181 @@ var generateInsertArray3 = (db, tableName, table, tables, relationMap, baseType,
6587
7270
  }
6588
7271
  };
6589
7272
  const pkNames = sqlitePrimaryKeyPropNames(table);
7273
+ const hooks = policies?.onWrite?.(tableName, "insert");
6590
7274
  return {
6591
7275
  name: fieldName,
6592
7276
  resolver: async (_source, args, context, info) => {
6593
7277
  try {
6594
- return await runMutation(db, context, info, txCtx, async (executor) => {
6595
- if (!args.values.length) {
6596
- throw new import_graphql10.GraphQLError("No values were provided!");
6597
- }
6598
- const entries = nested?.enabled(tableName) ? args.values.map((values) => nested.split(tableName, values)) : void 0;
6599
- const nestedEntries = entries?.some((entry) => nested.hasOps(entry.ops)) ? entries : void 0;
6600
- const contextColumns = policies?.contextValues?.(tableName);
6601
- const scope = policies?.scope?.(context);
6602
- const input = nestedEntries ? [] : applyContextValuesAll(
6603
- remapFromGraphQLArrayInput(entries ? entries.map((entry) => entry.columns) : args.values, table),
6604
- contextColumns,
6605
- context
6606
- );
6607
- const parsedInfo = (0, import_graphql_parse_resolve_info4.parseResolveInfo)(info, {
6608
- deep: true
6609
- });
6610
- const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
6611
- relationMap,
6612
- tables,
6613
- tableName,
6614
- typeName,
6615
- typeNameMapper,
6616
- table,
6617
- pkNames,
6618
- parsedInfo,
6619
- limits,
6620
- scope
6621
- });
6622
- const returning = nestedEntries ? nested.withJoinColumns(tableName, mergedOps(nestedEntries), { ...columns }, table) : columns;
6623
- const runInsert = async (target, values) => {
6624
- let query = target.insert(table).values(values).returning(returning);
6625
- if (conflictDoNothing) {
6626
- query = query.onConflictDoNothing();
7278
+ return await runMutation(
7279
+ db,
7280
+ context,
7281
+ info,
7282
+ txCtx,
7283
+ async (executor) => {
7284
+ if (!args.values.length) {
7285
+ throw new import_graphql10.GraphQLError("No values were provided!");
6627
7286
  }
6628
- return await query;
6629
- };
6630
- const result = nestedEntries ? await writeWithNestedOps({
6631
- executor,
6632
- runtime: nested,
6633
- tableName,
6634
- entries: nestedEntries,
6635
- remapValues: (values) => applyContextValues(remapFromGraphQLSingleInput(values, table), contextColumns, context),
6636
- write: (tx, values) => runInsert(tx, [values]),
6637
- context
6638
- }) : await runInsert(executor, input);
6639
- const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, result, pkNames, withParams) : result;
6640
- return remapToGraphQLArrayOutput(enriched, tableName, table, relationMap);
6641
- });
7287
+ await runWriteHook(hooks, "before", {
7288
+ table: tableName,
7289
+ operation: "insert",
7290
+ single: false,
7291
+ args,
7292
+ context,
7293
+ info,
7294
+ tx: executor
7295
+ });
7296
+ const entries = nested?.enabled(tableName) ? args.values.map((values) => nested.split(tableName, values)) : void 0;
7297
+ const nestedEntries = entries?.some((entry) => nested.hasOps(entry.ops)) ? entries : void 0;
7298
+ const contextColumns = policies?.contextValues?.(tableName);
7299
+ const scope = policies?.scope?.(context);
7300
+ const input = nestedEntries ? [] : applyContextValuesAll(
7301
+ remapFromGraphQLArrayInput(entries ? entries.map((entry) => entry.columns) : args.values, table),
7302
+ contextColumns,
7303
+ context
7304
+ );
7305
+ const parsedInfo = (0, import_graphql_parse_resolve_info4.parseResolveInfo)(info, {
7306
+ deep: true
7307
+ });
7308
+ const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
7309
+ relationMap,
7310
+ tables,
7311
+ tableName,
7312
+ typeName,
7313
+ typeNameMapper,
7314
+ table,
7315
+ pkNames,
7316
+ parsedInfo,
7317
+ limits,
7318
+ scope
7319
+ });
7320
+ const returning = nestedEntries ? nested.withJoinColumns(tableName, mergedOps(nestedEntries), { ...columns }, table) : columns;
7321
+ const runInsert = async (target, values) => {
7322
+ let query = target.insert(table).values(values).returning(returning);
7323
+ if (conflictDoNothing) {
7324
+ query = query.onConflictDoNothing();
7325
+ }
7326
+ return await query;
7327
+ };
7328
+ const result = nestedEntries ? await writeWithNestedOps({
7329
+ executor,
7330
+ runtime: nested,
7331
+ tableName,
7332
+ entries: nestedEntries,
7333
+ remapValues: (values) => applyContextValues(remapFromGraphQLSingleInput(values, table), contextColumns, context),
7334
+ write: (tx, values) => runInsert(tx, [values]),
7335
+ context
7336
+ }) : await runInsert(executor, input);
7337
+ await runWriteHook(hooks, "after", {
7338
+ table: tableName,
7339
+ operation: "insert",
7340
+ single: false,
7341
+ args,
7342
+ rows: result,
7343
+ context,
7344
+ info,
7345
+ tx: executor
7346
+ });
7347
+ const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, result, pkNames, withParams) : result;
7348
+ return remapToGraphQLArrayOutput(enriched, tableName, table, relationMap);
7349
+ },
7350
+ !!hooks
7351
+ );
6642
7352
  } catch (e) {
6643
7353
  throw toGraphQLError(e);
6644
7354
  }
6645
7355
  },
6646
7356
  args: queryArgs
6647
7357
  };
6648
- };
6649
- var generateInsertSingle3 = (db, tableName, table, tables, relationMap, baseType, fieldName, typeName, typeNameMapper, conflictDoNothing = false, txCtx, nested, limits, policies) => {
6650
- const queryArgs = {
6651
- values: {
6652
- type: new import_graphql10.GraphQLNonNull(baseType)
6653
- }
6654
- };
6655
- const pkNames = sqlitePrimaryKeyPropNames(table);
6656
- return {
6657
- name: fieldName,
6658
- resolver: async (_source, args, context, info) => {
6659
- try {
6660
- return await runMutation(db, context, info, txCtx, async (executor) => {
6661
- const entry = nested?.enabled(tableName) ? nested.split(tableName, args.values) : void 0;
6662
- const nestedEntry = entry && nested.hasOps(entry.ops) ? entry : void 0;
6663
- const contextColumns = policies?.contextValues?.(tableName);
6664
- const scope = policies?.scope?.(context);
6665
- const input = nestedEntry ? {} : applyContextValues(
6666
- remapFromGraphQLSingleInput(entry ? entry.columns : args.values, table),
6667
- contextColumns,
6668
- context
6669
- );
6670
- const parsedInfo = (0, import_graphql_parse_resolve_info4.parseResolveInfo)(info, {
6671
- deep: true
6672
- });
6673
- const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
6674
- relationMap,
6675
- tables,
6676
- tableName,
6677
- typeName,
6678
- typeNameMapper,
6679
- table,
6680
- pkNames,
6681
- parsedInfo,
6682
- limits,
6683
- scope
6684
- });
6685
- const returning = nestedEntry ? nested.withJoinColumns(tableName, nestedEntry.ops, { ...columns }, table) : columns;
6686
- const runInsert = async (target, values) => {
6687
- let query = target.insert(table).values(values).returning(returning);
6688
- if (conflictDoNothing) {
6689
- query = query.onConflictDoNothing();
6690
- }
6691
- return await query;
6692
- };
6693
- const result = nestedEntry ? await writeWithNestedOps({
6694
- executor,
6695
- runtime: nested,
6696
- tableName,
6697
- entries: [nestedEntry],
6698
- remapValues: (values) => applyContextValues(remapFromGraphQLSingleInput(values, table), contextColumns, context),
6699
- write: runInsert,
6700
- context
6701
- }) : await runInsert(executor, input);
6702
- if (!result[0]) {
6703
- if (!conflictDoNothing) {
6704
- throw new import_graphql10.GraphQLError(`${fieldName}: the insert returned no row.`);
7358
+ };
7359
+ var generateInsertSingle3 = (db, tableName, table, tables, relationMap, baseType, fieldName, typeName, typeNameMapper, conflictDoNothing = false, txCtx, nested, limits, policies) => {
7360
+ const queryArgs = {
7361
+ values: {
7362
+ type: new import_graphql10.GraphQLNonNull(baseType)
7363
+ }
7364
+ };
7365
+ const pkNames = sqlitePrimaryKeyPropNames(table);
7366
+ const hooks = policies?.onWrite?.(tableName, "insert");
7367
+ return {
7368
+ name: fieldName,
7369
+ resolver: async (_source, args, context, info) => {
7370
+ try {
7371
+ return await runMutation(
7372
+ db,
7373
+ context,
7374
+ info,
7375
+ txCtx,
7376
+ async (executor) => {
7377
+ await runWriteHook(hooks, "before", {
7378
+ table: tableName,
7379
+ operation: "insert",
7380
+ single: true,
7381
+ args,
7382
+ context,
7383
+ info,
7384
+ tx: executor
7385
+ });
7386
+ const entry = nested?.enabled(tableName) ? nested.split(tableName, args.values) : void 0;
7387
+ const nestedEntry = entry && nested.hasOps(entry.ops) ? entry : void 0;
7388
+ const contextColumns = policies?.contextValues?.(tableName);
7389
+ const scope = policies?.scope?.(context);
7390
+ const input = nestedEntry ? {} : applyContextValues(
7391
+ remapFromGraphQLSingleInput(entry ? entry.columns : args.values, table),
7392
+ contextColumns,
7393
+ context
7394
+ );
7395
+ const parsedInfo = (0, import_graphql_parse_resolve_info4.parseResolveInfo)(info, {
7396
+ deep: true
7397
+ });
7398
+ const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
7399
+ relationMap,
7400
+ tables,
7401
+ tableName,
7402
+ typeName,
7403
+ typeNameMapper,
7404
+ table,
7405
+ pkNames,
7406
+ parsedInfo,
7407
+ limits,
7408
+ scope
7409
+ });
7410
+ const returning = nestedEntry ? nested.withJoinColumns(tableName, nestedEntry.ops, { ...columns }, table) : columns;
7411
+ const runInsert = async (target, values) => {
7412
+ let query = target.insert(table).values(values).returning(returning);
7413
+ if (conflictDoNothing) {
7414
+ query = query.onConflictDoNothing();
7415
+ }
7416
+ return await query;
7417
+ };
7418
+ const result = nestedEntry ? await writeWithNestedOps({
7419
+ executor,
7420
+ runtime: nested,
7421
+ tableName,
7422
+ entries: [nestedEntry],
7423
+ remapValues: (values) => applyContextValues(remapFromGraphQLSingleInput(values, table), contextColumns, context),
7424
+ write: runInsert,
7425
+ context
7426
+ }) : await runInsert(executor, input);
7427
+ await runWriteHook(hooks, "after", {
7428
+ table: tableName,
7429
+ operation: "insert",
7430
+ single: true,
7431
+ args,
7432
+ rows: result,
7433
+ context,
7434
+ info,
7435
+ tx: executor
7436
+ });
7437
+ if (!result[0]) {
7438
+ if (!conflictDoNothing) {
7439
+ throw new import_graphql10.GraphQLError(`${fieldName}: the insert returned no row.`);
7440
+ }
7441
+ return void 0;
6705
7442
  }
6706
- return void 0;
6707
- }
6708
- const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, result, pkNames, withParams) : result;
6709
- return remapToGraphQLSingleOutput(enriched[0], tableName, table, relationMap);
6710
- });
7443
+ const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, result, pkNames, withParams) : result;
7444
+ return remapToGraphQLSingleOutput(enriched[0], tableName, table, relationMap);
7445
+ },
7446
+ !!hooks
7447
+ );
6711
7448
  } catch (e) {
6712
7449
  throw toGraphQLError(e);
6713
7450
  }
@@ -6726,69 +7463,96 @@ var generateUpsert3 = (db, tableName, table, tables, relationMap, baseType, onCo
6726
7463
  }
6727
7464
  };
6728
7465
  const pkNames = sqlitePrimaryKeyPropNames(table);
7466
+ const hooks = policies?.onWrite?.(tableName, "upsert");
6729
7467
  return {
6730
7468
  name: fieldName,
6731
7469
  resolver: async (_source, args, context, info) => {
6732
7470
  try {
6733
- return await runMutation(db, context, info, txCtx, async (executor) => {
6734
- const supplied = single ? [args.values] : args.values;
6735
- if (!supplied.length) {
6736
- throw new import_graphql10.GraphQLError("No values were provided!");
6737
- }
6738
- const entries = nested?.enabled(tableName) ? supplied.map((values) => nested.split(tableName, values)) : void 0;
6739
- const nestedEntries = entries?.some((entry) => nested.hasOps(entry.ops)) ? entries : void 0;
6740
- const contextColumns = policies?.contextValues?.(tableName);
6741
- const scope = policies?.scope?.(context);
6742
- const input = nestedEntries ? [] : applyContextValuesAll(
6743
- remapFromGraphQLArrayInput(entries ? entries.map((entry) => entry.columns) : supplied, table),
6744
- contextColumns,
6745
- context
6746
- );
6747
- const parsedInfo = (0, import_graphql_parse_resolve_info4.parseResolveInfo)(info, { deep: true });
6748
- const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
6749
- relationMap,
6750
- tables,
6751
- tableName,
6752
- typeName,
6753
- typeNameMapper,
6754
- table,
6755
- pkNames,
6756
- parsedInfo,
6757
- limits,
6758
- scope
6759
- });
6760
- const returning = nestedEntries ? nested.withJoinColumns(tableName, mergedOps(nestedEntries), { ...columns }, table) : columns;
6761
- const runUpsert = async (target, values) => {
6762
- const plan = resolveConflictPlan({
7471
+ return await runMutation(
7472
+ db,
7473
+ context,
7474
+ info,
7475
+ txCtx,
7476
+ async (executor) => {
7477
+ const supplied = single ? [args.values] : args.values;
7478
+ if (!supplied.length) {
7479
+ throw new import_graphql10.GraphQLError("No values were provided!");
7480
+ }
7481
+ await runWriteHook(hooks, "before", {
7482
+ table: tableName,
7483
+ operation: "upsert",
7484
+ single,
7485
+ args,
7486
+ context,
7487
+ info,
7488
+ tx: executor
7489
+ });
7490
+ const entries = nested?.enabled(tableName) ? supplied.map((values) => nested.split(tableName, values)) : void 0;
7491
+ const nestedEntries = entries?.some((entry) => nested.hasOps(entry.ops)) ? entries : void 0;
7492
+ const contextColumns = policies?.contextValues?.(tableName);
7493
+ const scope = policies?.scope?.(context);
7494
+ const input = nestedEntries ? [] : applyContextValuesAll(
7495
+ remapFromGraphQLArrayInput(entries ? entries.map((entry) => entry.columns) : supplied, table),
7496
+ contextColumns,
7497
+ context
7498
+ );
7499
+ const parsedInfo = (0, import_graphql_parse_resolve_info4.parseResolveInfo)(info, { deep: true });
7500
+ const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
7501
+ relationMap,
7502
+ tables,
7503
+ tableName,
7504
+ typeName,
7505
+ typeNameMapper,
6763
7506
  table,
6764
- values,
6765
- onConflict: args.onConflict,
6766
7507
  pkNames,
6767
- uniqueSets,
6768
- excludedRef: excludedColumnRef,
6769
- withTarget: true,
6770
- buildWhere: (where) => extractFilters(table, tableName, where, relationFilterCtx(filterCtx, tableName))
7508
+ parsedInfo,
7509
+ limits,
7510
+ scope
6771
7511
  });
6772
- let query = target.insert(table).values(values).returning(returning);
6773
- const setWhere = withScope(scope, tableName, table, plan.setWhere);
6774
- query = plan.action === "NOTHING" ? query.onConflictDoNothing(plan.target ? { target: plan.target } : void 0) : query.onConflictDoUpdate({ target: plan.target, set: plan.set, setWhere });
6775
- return await query;
6776
- };
6777
- const result = nestedEntries ? await writeWithNestedOps({
6778
- executor,
6779
- runtime: nested,
6780
- tableName,
6781
- entries: nestedEntries,
6782
- remapValues: (values) => applyContextValues(remapFromGraphQLSingleInput(values, table), contextColumns, context),
6783
- write: (tx, values) => runUpsert(tx, [values]),
6784
- context
6785
- }) : await runUpsert(executor, input);
6786
- if (single && !result[0]) {
6787
- return void 0;
6788
- }
6789
- const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, result, pkNames, withParams) : result;
6790
- return single ? remapToGraphQLSingleOutput(enriched[0], tableName, table, relationMap) : remapToGraphQLArrayOutput(enriched, tableName, table, relationMap);
6791
- });
7512
+ const returning = nestedEntries ? nested.withJoinColumns(tableName, mergedOps(nestedEntries), { ...columns }, table) : columns;
7513
+ const runUpsert = async (target, values) => {
7514
+ const plan = resolveConflictPlan({
7515
+ table,
7516
+ values,
7517
+ onConflict: args.onConflict,
7518
+ pkNames,
7519
+ uniqueSets,
7520
+ excludedRef: excludedColumnRef,
7521
+ withTarget: true,
7522
+ buildWhere: (where) => extractFilters(table, tableName, where, relationFilterCtx(filterCtx, tableName))
7523
+ });
7524
+ let query = target.insert(table).values(values).returning(returning);
7525
+ const setWhere = withScope(scope, tableName, table, plan.setWhere);
7526
+ query = plan.action === "NOTHING" ? query.onConflictDoNothing(plan.target ? { target: plan.target } : void 0) : query.onConflictDoUpdate({ target: plan.target, set: plan.set, setWhere });
7527
+ return await query;
7528
+ };
7529
+ const result = nestedEntries ? await writeWithNestedOps({
7530
+ executor,
7531
+ runtime: nested,
7532
+ tableName,
7533
+ entries: nestedEntries,
7534
+ remapValues: (values) => applyContextValues(remapFromGraphQLSingleInput(values, table), contextColumns, context),
7535
+ write: (tx, values) => runUpsert(tx, [values]),
7536
+ context
7537
+ }) : await runUpsert(executor, input);
7538
+ await runWriteHook(hooks, "after", {
7539
+ table: tableName,
7540
+ operation: "upsert",
7541
+ single,
7542
+ args,
7543
+ rows: result,
7544
+ context,
7545
+ info,
7546
+ tx: executor
7547
+ });
7548
+ if (single && !result[0]) {
7549
+ return void 0;
7550
+ }
7551
+ const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, result, pkNames, withParams) : result;
7552
+ return single ? remapToGraphQLSingleOutput(enriched[0], tableName, table, relationMap) : remapToGraphQLArrayOutput(enriched, tableName, table, relationMap);
7553
+ },
7554
+ !!hooks
7555
+ );
6792
7556
  } catch (e) {
6793
7557
  throw toGraphQLError(e);
6794
7558
  }
@@ -6806,75 +7570,102 @@ var generateUpdate3 = (db, tableName, table, tables, relationMap, setArgs, filte
6806
7570
  }
6807
7571
  };
6808
7572
  const pkNames = sqlitePrimaryKeyPropNames(table);
7573
+ const hooks = policies?.onWrite?.(tableName, "update");
6809
7574
  return {
6810
7575
  name: fieldName,
6811
7576
  resolver: async (_source, args, context, info) => {
6812
7577
  try {
6813
- return await runMutation(db, context, info, txCtx, async (executor) => {
6814
- const { where, set } = args;
6815
- const scope = policies?.scope?.(context);
6816
- const parsedInfo = (0, import_graphql_parse_resolve_info4.parseResolveInfo)(info, {
6817
- deep: true
6818
- });
6819
- const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
6820
- relationMap,
6821
- tables,
6822
- tableName,
6823
- typeName,
6824
- typeNameMapper,
6825
- table,
6826
- pkNames,
6827
- parsedInfo,
6828
- limits,
6829
- scope
6830
- });
6831
- const entry = nested?.enabled(tableName) ? nested.split(tableName, set) : void 0;
6832
- const nestedOps = entry && nested.hasOps(entry.ops) ? entry.ops : void 0;
6833
- const input = stripContextValues(
6834
- remapUpdateInput(entry ? entry.columns : set, table, tableName),
6835
- policies?.contextValues?.(tableName)
6836
- );
6837
- if (!Object.keys(input).length && !nestedOps) {
6838
- throw new import_graphql10.GraphQLError("Unable to update with no values specified!");
6839
- }
6840
- const relationCtx = relationFilterCtx(filterCtx, tableName);
6841
- const filters = withScope(
6842
- scope,
6843
- tableName,
6844
- table,
6845
- single || requireWhere ? extractRequiredFilters(table, tableName, where, fieldName, relationCtx) : where ? extractFilters(table, tableName, where, relationCtx) : void 0
6846
- );
6847
- if (single) {
6848
- await assertSingleMatch(executor, table, filters, fieldName);
6849
- }
6850
- const returning = nestedOps ? nested.withJoinColumns(tableName, nestedOps, { ...columns }, table) : columns;
6851
- const runUpdate = async (target, values) => {
6852
- const writes = Object.keys(values).length > 0;
6853
- let query = writes ? target.update(table).set(values) : target.select(returning).from(table);
6854
- if (filters) {
6855
- query = query.where(filters);
7578
+ return await runMutation(
7579
+ db,
7580
+ context,
7581
+ info,
7582
+ txCtx,
7583
+ async (executor) => {
7584
+ await runWriteHook(hooks, "before", {
7585
+ table: tableName,
7586
+ operation: "update",
7587
+ single,
7588
+ args,
7589
+ context,
7590
+ info,
7591
+ tx: executor
7592
+ });
7593
+ const { where, set } = args;
7594
+ const scope = policies?.scope?.(context);
7595
+ const parsedInfo = (0, import_graphql_parse_resolve_info4.parseResolveInfo)(info, {
7596
+ deep: true
7597
+ });
7598
+ const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
7599
+ relationMap,
7600
+ tables,
7601
+ tableName,
7602
+ typeName,
7603
+ typeNameMapper,
7604
+ table,
7605
+ pkNames,
7606
+ parsedInfo,
7607
+ limits,
7608
+ scope
7609
+ });
7610
+ const entry = nested?.enabled(tableName) ? nested.split(tableName, set) : void 0;
7611
+ const nestedOps = entry && nested.hasOps(entry.ops) ? entry.ops : void 0;
7612
+ const input = stripContextValues(
7613
+ remapUpdateInput(entry ? entry.columns : set, table, tableName),
7614
+ policies?.contextValues?.(tableName)
7615
+ );
7616
+ if (!Object.keys(input).length && !nestedOps) {
7617
+ throw new import_graphql10.GraphQLError("Unable to update with no values specified!");
6856
7618
  }
6857
- return await (writes ? query.returning(returning) : query);
6858
- };
6859
- const result = nestedOps ? await updateWithNestedOps({
6860
- executor,
6861
- runtime: nested,
6862
- tableName,
6863
- columns: input,
6864
- ops: nestedOps,
6865
- remapValues: (values) => values,
6866
- write: runUpdate,
6867
- context
6868
- }) : await runUpdate(executor, input);
6869
- if (single && result.length > 1) {
6870
- throw new import_graphql10.GraphQLError(`${fieldName}: 'where' matched more than one row!`);
6871
- }
6872
- if (single && !result[0]) {
6873
- return void 0;
6874
- }
6875
- const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, result, pkNames, withParams) : result;
6876
- return single ? remapToGraphQLSingleOutput(enriched[0], tableName, table, relationMap) : remapToGraphQLArrayOutput(enriched, tableName, table, relationMap);
6877
- });
7619
+ const relationCtx = relationFilterCtx(filterCtx, tableName);
7620
+ const filters = withScope(
7621
+ scope,
7622
+ tableName,
7623
+ table,
7624
+ single || requireWhere ? extractRequiredFilters(table, tableName, where, fieldName, relationCtx) : where ? extractFilters(table, tableName, where, relationCtx) : void 0
7625
+ );
7626
+ if (single) {
7627
+ await assertSingleMatch(executor, table, filters, fieldName);
7628
+ }
7629
+ const returning = nestedOps ? nested.withJoinColumns(tableName, nestedOps, { ...columns }, table) : columns;
7630
+ const runUpdate = async (target, values) => {
7631
+ const writes = Object.keys(values).length > 0;
7632
+ let query = writes ? target.update(table).set(values) : target.select(returning).from(table);
7633
+ if (filters) {
7634
+ query = query.where(filters);
7635
+ }
7636
+ return await (writes ? query.returning(returning) : query);
7637
+ };
7638
+ const result = nestedOps ? await updateWithNestedOps({
7639
+ executor,
7640
+ runtime: nested,
7641
+ tableName,
7642
+ columns: input,
7643
+ ops: nestedOps,
7644
+ remapValues: (values) => values,
7645
+ write: runUpdate,
7646
+ context
7647
+ }) : await runUpdate(executor, input);
7648
+ await runWriteHook(hooks, "after", {
7649
+ table: tableName,
7650
+ operation: "update",
7651
+ single,
7652
+ args,
7653
+ rows: result,
7654
+ context,
7655
+ info,
7656
+ tx: executor
7657
+ });
7658
+ if (single && result.length > 1) {
7659
+ throw new import_graphql10.GraphQLError(`${fieldName}: 'where' matched more than one row!`);
7660
+ }
7661
+ if (single && !result[0]) {
7662
+ return void 0;
7663
+ }
7664
+ const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, result, pkNames, withParams) : result;
7665
+ return single ? remapToGraphQLSingleOutput(enriched[0], tableName, table, relationMap) : remapToGraphQLArrayOutput(enriched, tableName, table, relationMap);
7666
+ },
7667
+ !!hooks
7668
+ );
6878
7669
  } catch (e) {
6879
7670
  throw toGraphQLError(e);
6880
7671
  }
@@ -6889,122 +7680,149 @@ var generateUpdateMany3 = (db, tableName, table, tables, relationMap, updateMany
6889
7680
  }
6890
7681
  };
6891
7682
  const pkNames = sqlitePrimaryKeyPropNames(table);
7683
+ const hooks = policies?.onWrite?.(tableName, "updateMany");
6892
7684
  return {
6893
7685
  name: fieldName,
6894
7686
  resolver: async (_source, args, context, info) => {
6895
7687
  try {
6896
- return await runMutation(db, context, info, txCtx, async (executor) => {
6897
- const { updates } = args;
6898
- if (!updates.length) {
6899
- throw new import_graphql10.GraphQLError("No updates were provided!");
6900
- }
6901
- const scope = policies?.scope?.(context);
6902
- const contextColumns = policies?.contextValues?.(tableName);
6903
- const parsedInfo = (0, import_graphql_parse_resolve_info4.parseResolveInfo)(info, {
6904
- deep: true
6905
- });
6906
- const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
6907
- relationMap,
6908
- tables,
6909
- tableName,
6910
- typeName,
6911
- typeNameMapper,
6912
- table,
6913
- pkNames,
6914
- parsedInfo,
6915
- limits,
6916
- scope
6917
- });
6918
- const entries = updates.map(({ where, set }) => {
6919
- const split = nested?.enabled(tableName) ? nested.split(tableName, set) : void 0;
6920
- const ops = split && nested.hasOps(split.ops) ? split.ops : void 0;
6921
- const input = stripContextValues(
6922
- remapUpdateInput(split ? split.columns : set, table, tableName),
6923
- contextColumns
6924
- );
6925
- if (!Object.keys(input).length && !ops) {
6926
- throw new import_graphql10.GraphQLError("Unable to update with no values specified!");
7688
+ return await runMutation(
7689
+ db,
7690
+ context,
7691
+ info,
7692
+ txCtx,
7693
+ async (executor) => {
7694
+ const { updates } = args;
7695
+ if (!updates.length) {
7696
+ throw new import_graphql10.GraphQLError("No updates were provided!");
6927
7697
  }
6928
- return {
6929
- set: input,
6930
- ops,
6931
- filters: withScope(
6932
- scope,
6933
- tableName,
6934
- table,
6935
- where ? extractFilters(table, tableName, where, relationFilterCtx(filterCtx, tableName)) : void 0
6936
- )
7698
+ await runWriteHook(hooks, "before", {
7699
+ table: tableName,
7700
+ operation: "updateMany",
7701
+ single: false,
7702
+ args,
7703
+ context,
7704
+ info,
7705
+ tx: executor
7706
+ });
7707
+ const scope = policies?.scope?.(context);
7708
+ const contextColumns = policies?.contextValues?.(tableName);
7709
+ const parsedInfo = (0, import_graphql_parse_resolve_info4.parseResolveInfo)(info, {
7710
+ deep: true
7711
+ });
7712
+ const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
7713
+ relationMap,
7714
+ tables,
7715
+ tableName,
7716
+ typeName,
7717
+ typeNameMapper,
7718
+ table,
7719
+ pkNames,
7720
+ parsedInfo,
7721
+ limits,
7722
+ scope
7723
+ });
7724
+ const entries = updates.map(({ where, set }) => {
7725
+ const split = nested?.enabled(tableName) ? nested.split(tableName, set) : void 0;
7726
+ const ops = split && nested.hasOps(split.ops) ? split.ops : void 0;
7727
+ const input = stripContextValues(
7728
+ remapUpdateInput(split ? split.columns : set, table, tableName),
7729
+ contextColumns
7730
+ );
7731
+ if (!Object.keys(input).length && !ops) {
7732
+ throw new import_graphql10.GraphQLError("Unable to update with no values specified!");
7733
+ }
7734
+ return {
7735
+ set: input,
7736
+ ops,
7737
+ filters: withScope(
7738
+ scope,
7739
+ tableName,
7740
+ table,
7741
+ where ? extractFilters(table, tableName, where, relationFilterCtx(filterCtx, tableName)) : void 0
7742
+ )
7743
+ };
7744
+ });
7745
+ const anyNested = entries.some((entry) => entry.ops);
7746
+ const returning = anyNested ? nested.withJoinColumns(
7747
+ tableName,
7748
+ mergedOps(entries.map((entry) => ({ ops: entry.ops ?? {} }))),
7749
+ { ...columns },
7750
+ table
7751
+ ) : columns;
7752
+ const runEntry = (tx, entry) => {
7753
+ let query = tx.update(table).set(entry.set);
7754
+ if (entry.filters) {
7755
+ query = query.where(entry.filters);
7756
+ }
7757
+ return query.returning(returning).all();
6937
7758
  };
6938
- });
6939
- const anyNested = entries.some((entry) => entry.ops);
6940
- const returning = anyNested ? nested.withJoinColumns(
6941
- tableName,
6942
- mergedOps(entries.map((entry) => ({ ops: entry.ops ?? {} }))),
6943
- { ...columns },
6944
- table
6945
- ) : columns;
6946
- const runEntry = (tx, entry) => {
6947
- let query = tx.update(table).set(entry.set);
6948
- if (entry.filters) {
6949
- query = query.where(entry.filters);
6950
- }
6951
- return query.returning(returning).all();
6952
- };
6953
- const runNestedEntry = async (tx, entry) => {
6954
- const values = entry.ops ? { ...entry.set, ...await nested.applyParentSide(tx, tableName, entry.ops, context) } : entry.set;
6955
- const writes = Object.keys(values).length > 0;
6956
- let query = writes ? tx.update(table).set(values) : tx.select(returning).from(table);
6957
- if (entry.filters) {
6958
- query = query.where(entry.filters);
6959
- }
6960
- const rows = await (writes ? query.returning(returning) : query);
6961
- if (entry.ops) {
6962
- await nested.applyChildSide(tx, tableName, entry.ops, rows, context);
6963
- }
6964
- return rows;
6965
- };
6966
- const perEntry = await executor.transaction((tx) => {
6967
- if (anyNested) {
6968
- return (async () => {
6969
- const results2 = [];
6970
- for (const entry of entries) {
6971
- results2.push(await runNestedEntry(tx, entry));
6972
- }
6973
- return results2;
6974
- })();
6975
- }
6976
- const first = runEntry(tx, entries[0]);
6977
- if (typeof first?.then === "function") {
6978
- return (async () => {
6979
- const results2 = [await first];
6980
- for (let i = 1; i < entries.length; i++) {
6981
- results2.push(await runEntry(tx, entries[i]));
6982
- }
6983
- return results2;
6984
- })();
6985
- }
6986
- const results = [first];
6987
- for (let i = 1; i < entries.length; i++) {
6988
- results.push(runEntry(tx, entries[i]));
6989
- }
6990
- return results;
6991
- });
6992
- const flatRows = perEntry.flat();
6993
- const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, flatRows, pkNames, withParams) : flatRows;
6994
- const output = [];
6995
- let offset = 0;
6996
- for (const rows of perEntry) {
6997
- if (!rows.length) {
6998
- output.push(null);
6999
- continue;
7000
- }
7001
- for (let i = 0; i < rows.length; i++) {
7002
- output.push(remapToGraphQLSingleOutput(enriched[offset + i], tableName, table, relationMap));
7759
+ const runNestedEntry = async (tx, entry) => {
7760
+ const values = entry.ops ? { ...entry.set, ...await nested.applyParentSide(tx, tableName, entry.ops, context) } : entry.set;
7761
+ const writes = Object.keys(values).length > 0;
7762
+ let query = writes ? tx.update(table).set(values) : tx.select(returning).from(table);
7763
+ if (entry.filters) {
7764
+ query = query.where(entry.filters);
7765
+ }
7766
+ const rows = await (writes ? query.returning(returning) : query);
7767
+ if (entry.ops) {
7768
+ await nested.applyChildSide(tx, tableName, entry.ops, rows, context);
7769
+ }
7770
+ return rows;
7771
+ };
7772
+ const perEntry = await executor.transaction((tx) => {
7773
+ if (anyNested) {
7774
+ return (async () => {
7775
+ const results2 = [];
7776
+ for (const entry of entries) {
7777
+ results2.push(await runNestedEntry(tx, entry));
7778
+ }
7779
+ return results2;
7780
+ })();
7781
+ }
7782
+ const first = runEntry(tx, entries[0]);
7783
+ if (typeof first?.then === "function") {
7784
+ return (async () => {
7785
+ const results2 = [await first];
7786
+ for (let i = 1; i < entries.length; i++) {
7787
+ results2.push(await runEntry(tx, entries[i]));
7788
+ }
7789
+ return results2;
7790
+ })();
7791
+ }
7792
+ const results = [first];
7793
+ for (let i = 1; i < entries.length; i++) {
7794
+ results.push(runEntry(tx, entries[i]));
7795
+ }
7796
+ return results;
7797
+ });
7798
+ const flatRows = perEntry.flat();
7799
+ await runWriteHook(hooks, "after", {
7800
+ table: tableName,
7801
+ operation: "updateMany",
7802
+ single: false,
7803
+ args,
7804
+ rows: flatRows,
7805
+ context,
7806
+ info,
7807
+ tx: executor
7808
+ });
7809
+ const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, flatRows, pkNames, withParams) : flatRows;
7810
+ const output = [];
7811
+ let offset = 0;
7812
+ for (const rows of perEntry) {
7813
+ if (!rows.length) {
7814
+ output.push(null);
7815
+ continue;
7816
+ }
7817
+ for (let i = 0; i < rows.length; i++) {
7818
+ output.push(remapToGraphQLSingleOutput(enriched[offset + i], tableName, table, relationMap));
7819
+ }
7820
+ offset += rows.length;
7003
7821
  }
7004
- offset += rows.length;
7005
- }
7006
- return output;
7007
- });
7822
+ return output;
7823
+ },
7824
+ !!hooks
7825
+ );
7008
7826
  } catch (e) {
7009
7827
  throw toGraphQLError(e);
7010
7828
  }
@@ -7012,51 +7830,81 @@ var generateUpdateMany3 = (db, tableName, table, tables, relationMap, updateMany
7012
7830
  args: queryArgs
7013
7831
  };
7014
7832
  };
7015
- var generateDelete3 = (db, tableName, table, filterArgs, fieldName, typeName, single, requireWhere, filterCtx, selectionCtx, txCtx, policies) => {
7833
+ var generateDelete3 = (db, tableName, table, filterArgs, fieldName, typeName, single, requireWhere, filterCtx, selectionCtx, txCtx, policies, restore = false) => {
7834
+ const softDelete = policies?.softDelete?.(tableName);
7835
+ const operation = restore ? "restore" : "delete";
7016
7836
  const queryArgs = {
7017
7837
  where: {
7018
7838
  type: single || requireWhere ? new import_graphql10.GraphQLNonNull(filterArgs) : filterArgs
7019
7839
  }
7020
7840
  };
7841
+ const hooks = policies?.onWrite?.(tableName, operation);
7021
7842
  return {
7022
7843
  name: fieldName,
7023
7844
  resolver: async (_source, args, context, info) => {
7024
7845
  try {
7025
- return await runMutation(db, context, info, txCtx, async (executor) => {
7026
- const { where } = args;
7027
- const scope = policies?.scope?.(context);
7028
- const parsedInfo = (0, import_graphql_parse_resolve_info4.parseResolveInfo)(info, {
7029
- deep: true
7030
- });
7031
- const columns = extractSelectedColumnsFromTreeSQLFormat(
7032
- parsedInfo.fieldsByTypeName[typeName],
7033
- table,
7034
- selectionCtx
7035
- );
7036
- const relationCtx = relationFilterCtx(filterCtx, tableName);
7037
- const filters = withScope(
7038
- scope,
7039
- tableName,
7040
- table,
7041
- single || requireWhere ? extractRequiredFilters(table, tableName, where, fieldName, relationCtx) : where ? extractFilters(table, tableName, where, relationCtx) : void 0
7042
- );
7043
- if (single) {
7044
- await assertSingleMatch(executor, table, filters, fieldName);
7045
- }
7046
- let query = executor.delete(table);
7047
- if (filters) {
7048
- query = query.where(filters);
7049
- }
7050
- query = query.returning(columns);
7051
- const result = await query;
7052
- if (single && result.length > 1) {
7053
- throw new import_graphql10.GraphQLError(`${fieldName}: 'where' matched more than one row!`);
7054
- }
7055
- if (single) {
7056
- return result[0] ? remapToGraphQLSingleOutput(result[0], tableName, table) : void 0;
7057
- }
7058
- return remapToGraphQLArrayOutput(result, tableName, table);
7059
- });
7846
+ return await runMutation(
7847
+ db,
7848
+ context,
7849
+ info,
7850
+ txCtx,
7851
+ async (executor) => {
7852
+ await runWriteHook(hooks, "before", {
7853
+ table: tableName,
7854
+ operation,
7855
+ single,
7856
+ args,
7857
+ context,
7858
+ info,
7859
+ tx: executor
7860
+ });
7861
+ const { where } = args;
7862
+ const scope = policies?.scope?.(context);
7863
+ const parsedInfo = (0, import_graphql_parse_resolve_info4.parseResolveInfo)(info, {
7864
+ deep: true
7865
+ });
7866
+ const columns = extractSelectedColumnsFromTreeSQLFormat(
7867
+ parsedInfo.fieldsByTypeName[typeName],
7868
+ table,
7869
+ selectionCtx
7870
+ );
7871
+ const relationCtx = relationFilterCtx(filterCtx, tableName);
7872
+ const filters = withScope(
7873
+ scope,
7874
+ tableName,
7875
+ table,
7876
+ single || requireWhere ? extractRequiredFilters(table, tableName, where, fieldName, relationCtx) : where ? extractFilters(table, tableName, where, relationCtx) : void 0,
7877
+ restore ? "ONLY" : void 0
7878
+ );
7879
+ if (single) {
7880
+ await assertSingleMatch(executor, table, filters, fieldName);
7881
+ }
7882
+ let query = softDelete ? executor.update(table).set({ [softDelete.columnName]: restore ? softDelete.writeRestored : softDelete.writeDeleted() }) : executor.delete(table);
7883
+ if (filters) {
7884
+ query = query.where(filters);
7885
+ }
7886
+ query = query.returning(columns);
7887
+ const result = await query;
7888
+ await runWriteHook(hooks, "after", {
7889
+ table: tableName,
7890
+ operation,
7891
+ single,
7892
+ args,
7893
+ rows: result,
7894
+ context,
7895
+ info,
7896
+ tx: executor
7897
+ });
7898
+ if (single && result.length > 1) {
7899
+ throw new import_graphql10.GraphQLError(`${fieldName}: 'where' matched more than one row!`);
7900
+ }
7901
+ if (single) {
7902
+ return result[0] ? remapToGraphQLSingleOutput(result[0], tableName, table) : void 0;
7903
+ }
7904
+ return remapToGraphQLArrayOutput(result, tableName, table);
7905
+ },
7906
+ !!hooks
7907
+ );
7060
7908
  } catch (e) {
7061
7909
  throw toGraphQLError(e);
7062
7910
  }
@@ -7098,16 +7946,17 @@ var generateSchemaData3 = (db, schema, relations, options) => {
7098
7946
  attachTargetPrimaryKeys(namedRelations, tables, sqlitePrimaryKeyPropNames);
7099
7947
  const eagerRelations = pruneNonEagerRelations(namedRelations, shouldEagerLoad);
7100
7948
  const filterCtx = { tables, relationMap: namedRelations };
7101
- const scopes = options.policies?.scope;
7102
- const contextValuesOf = options.policies?.contextValues;
7103
- const policies = bindPolicies(options.policies, filterCtx);
7949
+ const tablePolicies = options.policies;
7950
+ const contextValuesOf = tablePolicies?.contextValues;
7951
+ const softDeleteOf = tablePolicies?.softDelete;
7952
+ const policies = bindPolicies(tablePolicies, filterCtx);
7104
7953
  const resolverFactory = createRelationResolverFactory(
7105
7954
  db,
7106
7955
  tables,
7107
7956
  "nulls-smallest",
7108
7957
  filterCtx,
7109
7958
  limits,
7110
- scopes
7959
+ tablePolicies
7111
7960
  );
7112
7961
  const cacheCtx = {
7113
7962
  genericFilterCache: /* @__PURE__ */ new Map(),
@@ -7126,6 +7975,7 @@ var generateSchemaData3 = (db, schema, relations, options) => {
7126
7975
  docs: options.docs ?? {},
7127
7976
  primaryKeyOf: (name) => tables[name] ? sqlitePrimaryKeyPropNames(tables[name]) : [],
7128
7977
  contextValuesOf,
7978
+ softDeleteOf,
7129
7979
  featureOf
7130
7980
  };
7131
7981
  if (features.nestedWrites && db.resultKind === "sync") {
@@ -7141,8 +7991,13 @@ var generateSchemaData3 = (db, schema, relations, options) => {
7141
7991
  extractRelationJoinColumns
7142
7992
  ) : void 0;
7143
7993
  const nestedTypes = nestedPlans ? createNestedWriteTypes({ plans: nestedPlans, cacheCtx, typeNameMapper, insertPrefix: prefixes.insert }) : void 0;
7144
- const nestedRuntime = nestedPlans ? createNestedWriteRuntime({ plans: nestedPlans, filterCtx, scopes, contextValues: contextValuesOf }) : void 0;
7145
- const relationAggregateFactory = anyTable("relationAggregates") ? createRelationAggregateFactory(db, tables, cacheCtx, typeNameMapper, filterCtx, scopes) : void 0;
7994
+ const nestedRuntime = nestedPlans ? createNestedWriteRuntime({
7995
+ plans: nestedPlans,
7996
+ filterCtx,
7997
+ policies: tablePolicies,
7998
+ contextValues: contextValuesOf
7999
+ }) : void 0;
8000
+ const relationAggregateFactory = anyTable("relationAggregates") ? createRelationAggregateFactory(db, tables, cacheCtx, typeNameMapper, filterCtx, tablePolicies) : void 0;
7146
8001
  const queries = {};
7147
8002
  const mutations = {};
7148
8003
  if (options.transactions && db.resultKind === "sync") {
@@ -7193,8 +8048,11 @@ var generateSchemaData3 = (db, schema, relations, options) => {
7193
8048
  updateCountFieldName,
7194
8049
  deleteFieldName,
7195
8050
  deleteSingleFieldName,
8051
+ restoreFieldName,
8052
+ restoreSingleFieldName,
7196
8053
  deleteCountFieldName
7197
8054
  } = computeResolverFieldNames(tableName, typeNameMapper, prefixes, suffixes);
8055
+ const softDeleteInfo = softDeleteOf?.(tableName);
7198
8056
  const selectArrGenerated = generateSelectArray3(
7199
8057
  db,
7200
8058
  tableName,
@@ -7385,6 +8243,36 @@ var generateSchemaData3 = (db, schema, relations, options) => {
7385
8243
  mutationTxCtx,
7386
8244
  policies
7387
8245
  ) : void 0;
8246
+ const restoreGenerated = softDeleteInfo ? generateDelete3(
8247
+ db,
8248
+ tableName,
8249
+ schema[tableName],
8250
+ tableFilters,
8251
+ restoreFieldName,
8252
+ typeName,
8253
+ false,
8254
+ tableFeatures.requireWhere,
8255
+ filterCtx,
8256
+ { tableName, relationMap: namedRelations, tables },
8257
+ mutationTxCtx,
8258
+ policies,
8259
+ true
8260
+ ) : void 0;
8261
+ const restoreSingleGenerated = softDeleteInfo ? generateDelete3(
8262
+ db,
8263
+ tableName,
8264
+ schema[tableName],
8265
+ tableFilters,
8266
+ restoreSingleFieldName,
8267
+ typeName,
8268
+ true,
8269
+ tableFeatures.requireWhere,
8270
+ filterCtx,
8271
+ { tableName, relationMap: namedRelations, tables },
8272
+ mutationTxCtx,
8273
+ policies,
8274
+ true
8275
+ ) : void 0;
7388
8276
  const updateCountGenerated = tableFeatures.update && tableFeatures.countMutations ? generateWriteCount({
7389
8277
  db,
7390
8278
  tableName,
@@ -7418,7 +8306,7 @@ var generateSchemaData3 = (db, schema, relations, options) => {
7418
8306
  aggregateFieldName,
7419
8307
  tableFilters,
7420
8308
  filterCtx,
7421
- scopes
8309
+ tablePolicies
7422
8310
  ) : void 0;
7423
8311
  const groupByType = tableFeatures.aggregates && tableFeatures.groupBy ? generateGroupByType(schema[tableName], tableName, typeName, cacheCtx) : void 0;
7424
8312
  const groupByEnum = groupByType ? generateGroupByEnum(schema[tableName], tableName, typeName) : void 0;
@@ -7433,7 +8321,7 @@ var generateSchemaData3 = (db, schema, relations, options) => {
7433
8321
  groupByEnum,
7434
8322
  havingInput,
7435
8323
  filterCtx,
7436
- scopes
8324
+ tablePolicies
7437
8325
  ) : void 0;
7438
8326
  queries[selectArrGenerated.name] = {
7439
8327
  type: selectArrOutput,
@@ -7570,6 +8458,26 @@ var generateSchemaData3 = (db, schema, relations, options) => {
7570
8458
  }
7571
8459
  };
7572
8460
  }
8461
+ if (restoreGenerated) {
8462
+ mutations[restoreGenerated.name] = {
8463
+ type: arrTableItemOutput,
8464
+ args: restoreGenerated.args,
8465
+ resolve: restoreGenerated.resolver,
8466
+ extensions: {
8467
+ drizzle: drizzleMeta({ kind: "mutation", operation: "restore", single: false, targetArg: "where" })
8468
+ }
8469
+ };
8470
+ }
8471
+ if (restoreSingleGenerated) {
8472
+ mutations[restoreSingleGenerated.name] = {
8473
+ type: singleTableItemOutput,
8474
+ args: restoreSingleGenerated.args,
8475
+ resolve: restoreSingleGenerated.resolver,
8476
+ extensions: {
8477
+ drizzle: drizzleMeta({ kind: "mutation", operation: "restore", single: true, targetArg: "where" })
8478
+ }
8479
+ };
8480
+ }
7573
8481
  if (updateCountGenerated) {
7574
8482
  mutations[updateCountGenerated.name] = {
7575
8483
  type: new import_graphql10.GraphQLNonNull(import_graphql10.GraphQLInt),
@@ -7780,7 +8688,8 @@ var buildSchema = (db, config) => {
7780
8688
  insert: config?.prefixes?.insert ?? "create",
7781
8689
  delete: config?.prefixes?.delete ?? "delete",
7782
8690
  update: config?.prefixes?.update ?? "update",
7783
- upsert: config?.prefixes?.upsert ?? "upsert"
8691
+ upsert: config?.prefixes?.upsert ?? "upsert",
8692
+ restore: config?.prefixes?.restore ?? "restore"
7784
8693
  };
7785
8694
  const suffixes = {
7786
8695
  list: config?.suffixes?.list ?? "",
@@ -7858,9 +8767,58 @@ var buildSchema = (db, config) => {
7858
8767
  }
7859
8768
  }
7860
8769
  }
7861
- const policies = scopeConfig || contextValuesConfig ? {
8770
+ const softDeleteConfig = config?.softDelete;
8771
+ const softDeleteInfos = /* @__PURE__ */ new Map();
8772
+ if (softDeleteConfig) {
8773
+ if (typeof softDeleteConfig === "function") {
8774
+ for (const tableName of tableKeys) {
8775
+ const declaration = softDeleteConfig(schema[tableName], tableName);
8776
+ if (declaration) {
8777
+ softDeleteInfos.set(tableName, resolveSoftDeleteInfo(schema[tableName], tableName, declaration));
8778
+ }
8779
+ }
8780
+ } else {
8781
+ const tableNames = new Set(tableKeys);
8782
+ for (const [tableName, declaration] of Object.entries(softDeleteConfig)) {
8783
+ if (!tableNames.has(tableName)) {
8784
+ throw new Error(
8785
+ `Drizzle-GraphQL Error: config.softDelete names '${tableName}', which is not a table in the Drizzle schema.`
8786
+ );
8787
+ }
8788
+ softDeleteInfos.set(tableName, resolveSoftDeleteInfo(schema[tableName], tableName, declaration));
8789
+ }
8790
+ }
8791
+ }
8792
+ const onWriteConfig = config?.onWrite;
8793
+ const writeHooks = /* @__PURE__ */ new Map();
8794
+ let globalWriteHooks;
8795
+ if (onWriteConfig) {
8796
+ const isGlobal = typeof onWriteConfig === "function" || typeof onWriteConfig.before === "function" || typeof onWriteConfig.after === "function";
8797
+ if (isGlobal) {
8798
+ globalWriteHooks = normalizeWriteHooks(onWriteConfig);
8799
+ } else {
8800
+ const tableNames = new Set(tableKeys);
8801
+ for (const [tableName, hooks] of Object.entries(onWriteConfig)) {
8802
+ if (!tableNames.has(tableName)) {
8803
+ throw new Error(
8804
+ `Drizzle-GraphQL Error: config.onWrite names '${tableName}', which is not a table in the Drizzle schema.`
8805
+ );
8806
+ }
8807
+ if (typeof hooks !== "function" && typeof hooks !== "object") {
8808
+ throw new Error(
8809
+ `Drizzle-GraphQL Error: config.onWrite.${tableName} must be a function or an object with 'before' and/or 'after'.`
8810
+ );
8811
+ }
8812
+ writeHooks.set(tableName, normalizeWriteHooks(hooks));
8813
+ }
8814
+ }
8815
+ }
8816
+ const onWrite = globalWriteHooks || writeHooks.size ? (tableName) => globalWriteHooks ?? writeHooks.get(tableName) : void 0;
8817
+ const policies = scopeConfig || contextValuesConfig || softDeleteInfos.size || onWrite ? {
7862
8818
  scope: scopeConfig ? (tableName) => scopeConfig[tableName] : void 0,
7863
- contextValues: contextValuesConfig ? (tableName) => contextValuesConfig[tableName] : void 0
8819
+ contextValues: contextValuesConfig ? (tableName) => contextValuesConfig[tableName] : void 0,
8820
+ softDelete: softDeleteInfos.size ? (tableName) => softDeleteInfos.get(tableName) : void 0,
8821
+ onWrite
7864
8822
  } : void 0;
7865
8823
  const exclude = config?.exclude;
7866
8824
  if (exclude) {