@vantreeseba/drizzle-graphql 12.0.1 → 13.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -165,6 +165,14 @@ const GraphQLDecimalString = new GraphQLScalarType({
165
165
  const uncapitalize = (input) => input?.length ? `${input[0].toLocaleLowerCase()}${input.length > 1 ? input.slice(1, input.length) : ""}` : input;
166
166
  const capitalize = (input) => input?.length ? `${input[0].toLocaleUpperCase()}${input.length > 1 ? input.slice(1, input.length) : ""}` : input;
167
167
  /**
168
+ * `inProgress` / `in-progress` / `in progress` / `InProgress` → `IN_PROGRESS`; `HTTPError` →
169
+ * `HTTP_ERROR`. Words are split at non-alphanumeric runs and at lower→upper and
170
+ * acronym→word boundaries, then upper-cased and joined with `_`.
171
+ *
172
+ * The preset behind `enumValueNameMapper: 'constant'`.
173
+ */
174
+ const constantCase = (input) => input.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/([A-Z])([A-Z][a-z])/g, "$1 $2").split(/[^a-zA-Z0-9]+/).filter(Boolean).map((part) => part.toUpperCase()).join("_");
175
+ /**
168
176
  * The `typeNameMapper` preset behind `typeNameMapper: 'singularize'`.
169
177
  *
170
178
  * Derives the singular and plural forms of a table key with the library's own `pluralize`,
@@ -185,6 +193,8 @@ const singularizeMapper = (tableName) => ({
185
193
  //#endregion
186
194
  //#region src/util/type-converter/index.ts
187
195
  const allowedNameChars = /^[a-zA-Z0-9_]+$/;
196
+ /** A GraphQL `Name` that is also allowed as an enum value — the spec reserves the three literals. */
197
+ const validEnumValueName = (name) => /^[_a-zA-Z][_a-zA-Z0-9]*$/.test(name) && name !== "true" && name !== "false" && name !== "null";
188
198
  /**
189
199
  * The enum type a column was declared with, when it is a *named* one — `pgEnum('status', …)`
190
200
  * shared by however many tables reference it. An inline value list (`text({ enum: [...] })`,
@@ -205,20 +215,35 @@ const pascalize = (input) => input.split(/[^a-zA-Z0-9]+/).filter(Boolean).map((p
205
215
  */
206
216
  const enumValuesKey = (values) => JSON.stringify(values);
207
217
  /**
218
+ * Identity of the GraphQL names those values were given. Kept apart from {@link enumValuesKey}
219
+ * because `enumValueNameMapper` sees the column, so two columns reaching one type can name its
220
+ * values differently — and only one set of names can win.
221
+ */
222
+ const enumValueNamesKey = (names) => JSON.stringify(names);
223
+ /**
208
224
  * Every GraphQL enum name this build has handed out. Two columns that resolve to the same
209
225
  * name share one type — that is how the columns of one `pgEnum` end up on a single type —
210
226
  * so the values are kept alongside it to tell a genuine collision from a re-use.
211
227
  */
212
228
  let enumNamesInUse = /* @__PURE__ */ new Map();
213
229
  let enumNameMapper;
230
+ let enumValueNameMapper;
231
+ /** The value-name presets, by the string a config passes for them. */
232
+ const enumValueNamePresets = { constant: ({ value }) => constantCase(value) };
214
233
  /**
215
234
  * Resets the enum registry for a fresh schema build and installs that build's
216
- * `enumNameMapper`. Called once per `generateSchemaData`, alongside
235
+ * `enumNameMapper` and `enumValueNameMapper`. Called once per `generateSchemaData`, alongside
217
236
  * {@link registerScalarOverrides} — without the reset a second build would reuse the first
218
237
  * build's enum types, and so its naming decisions.
219
238
  */
220
239
  const registerEnumConfig = (config) => {
221
240
  enumNameMapper = config.enumNameMapper;
241
+ const valueMapper = config.enumValueNameMapper;
242
+ if (typeof valueMapper === "string") {
243
+ const preset = enumValueNamePresets[valueMapper];
244
+ if (!preset) throw new Error(`Drizzle-GraphQL Error: Unknown enumValueNameMapper preset '${valueMapper}'. The only preset is 'constant'; pass a function for anything else.`);
245
+ enumValueNameMapper = preset;
246
+ } else enumValueNameMapper = valueMapper;
222
247
  enumNamesInUse = /* @__PURE__ */ new Map();
223
248
  };
224
249
  /**
@@ -247,22 +272,50 @@ const generateEnumCached = (column, columnName, tableName) => {
247
272
  columnName,
248
273
  values
249
274
  }) ?? (declared ? `${pascalize(declared.enumName)}Enum` : `${capitalize(tableName)}${capitalize(columnName)}Enum`);
275
+ const valueNames = values.map((value, index) => {
276
+ const mapped = enumValueNameMapper?.({
277
+ enumName: declared?.enumName,
278
+ schema: declared?.schema,
279
+ tableName,
280
+ columnName,
281
+ values,
282
+ value,
283
+ index,
284
+ typeName: name
285
+ });
286
+ if (mapped === void 0) return allowedNameChars.test(value) ? value : `Option${index}`;
287
+ if (!validEnumValueName(mapped)) throw new Error(`Drizzle-GraphQL Error: config.enumValueNameMapper named value '${value}' of enum '${name}' (on '${tableName}.${columnName}') '${mapped}', which is not a valid GraphQL enum value name. A name must match /^[_a-zA-Z][_a-zA-Z0-9]*$/ and not be true, false or null.`);
288
+ return mapped;
289
+ });
290
+ const seen = /* @__PURE__ */ new Map();
291
+ values.forEach((value, index) => {
292
+ const valueName = valueNames[index];
293
+ const earlier = seen.get(valueName);
294
+ if (earlier !== void 0) throw new Error(`Drizzle-GraphQL Error: Values '${earlier}' and '${value}' of enum '${name}' (on '${tableName}.${columnName}') both map to the GraphQL value name '${valueName}'. Give one of them a different name via config.enumValueNameMapper.`);
295
+ seen.set(valueName, value);
296
+ });
250
297
  const valuesKey = enumValuesKey(values);
298
+ const namesKey = enumValueNamesKey(valueNames);
251
299
  const claimed = enumNamesInUse.get(name);
252
300
  if (claimed) {
253
301
  if (claimed.valuesKey !== valuesKey) throw new Error(`Drizzle-GraphQL Error: Two different enums both map to the GraphQL type name '${name}' (most recently '${tableName}.${columnName}'). Give one of them a different name via config.enumNameMapper.`);
302
+ if (claimed.namesKey !== namesKey) {
303
+ const before = claimed.type.getValues().map((v) => v.name).join(", ");
304
+ throw new Error(`Drizzle-GraphQL Error: Columns sharing the GraphQL enum '${name}' name its values differently ('${tableName}.${columnName}' gives ${valueNames.join(", ")}; an earlier column gave ${before}). config.enumValueNameMapper must name a shared enum's values the same way for every column, or config.enumNameMapper must give the columns separate types.`);
305
+ }
254
306
  return claimed.type;
255
307
  }
256
308
  const gqlEnum = new GraphQLEnumType({
257
309
  name,
258
- values: Object.fromEntries(values.map((e, index) => [allowedNameChars.test(e) ? e : `Option${index}`, {
310
+ values: Object.fromEntries(values.map((e, index) => [valueNames[index], {
259
311
  value: e,
260
312
  description: `Value: ${e}`
261
313
  }]))
262
314
  });
263
315
  enumNamesInUse.set(name, {
264
316
  type: gqlEnum,
265
- valuesKey
317
+ valuesKey,
318
+ namesKey
266
319
  });
267
320
  return gqlEnum;
268
321
  };
@@ -1017,6 +1070,7 @@ const remapToGraphQLArrayOutput = (queryOutput, tableName, table, relationMap) =
1017
1070
  return queryOutput;
1018
1071
  };
1019
1072
  const remapFromGraphQLCore = (value, column, columnName) => {
1073
+ if (value === null) return null;
1020
1074
  if (getColumnScalarOverride(column, true)) {
1021
1075
  if (typeof value === "object" && value !== null && Object.getPrototypeOf(value) === null) return Object.assign({}, value);
1022
1076
  return value;
@@ -4296,17 +4350,15 @@ const computeResolverFieldNames = (tableName, typeNameMapper, prefixes, suffixes
4296
4350
  const upsertPrefix = prefixes.upsert ?? "upsert";
4297
4351
  const upsertArrayFieldName = `${upsertPrefix}${pluralNoun}${suffixes.list}`;
4298
4352
  const upsertSingleFieldName = mapped ? `${upsertPrefix}${capitalize(mapped.singular)}` : `${upsertPrefix}${capitalize(tableName)}${suffixes.single}`;
4299
- const updateBase = `${prefixes.update}${singularNoun}`;
4300
- const deleteBase = `${prefixes.delete}${singularNoun}`;
4301
- const restoreBase = `${prefixes.restore ?? "restore"}${singularNoun}`;
4302
- const updateFieldName = `${updateBase}${suffixes.list}`;
4353
+ const restorePrefix = prefixes.restore ?? "restore";
4354
+ const updateFieldName = `${prefixes.update}${pluralNoun}${suffixes.list}`;
4303
4355
  const updateManyFieldName = `${prefixes.update}${pluralNoun}Many`;
4304
- const deleteFieldName = `${deleteBase}${suffixes.list}`;
4305
- const restoreFieldName = `${restoreBase}${suffixes.list}`;
4306
- const writeSingleSuffix = suffixes.single === suffixes.list ? "Single" : suffixes.single;
4307
- const updateSingleFieldName = `${updateBase}${writeSingleSuffix}`;
4308
- const deleteSingleFieldName = `${deleteBase}${writeSingleSuffix}`;
4309
- const restoreSingleFieldName = `${restoreBase}${writeSingleSuffix}`;
4356
+ const deleteFieldName = `${prefixes.delete}${pluralNoun}${suffixes.list}`;
4357
+ const restoreFieldName = `${restorePrefix}${pluralNoun}${suffixes.list}`;
4358
+ const singleWrite = (prefix) => mapped ? `${prefix}${singularNoun}` : `${prefix}${singularNoun}${suffixes.single}`;
4359
+ const updateSingleFieldName = singleWrite(prefixes.update);
4360
+ const deleteSingleFieldName = singleWrite(prefixes.delete);
4361
+ const restoreSingleFieldName = singleWrite(restorePrefix);
4310
4362
  return {
4311
4363
  typeName,
4312
4364
  listFieldName,
@@ -4806,16 +4858,70 @@ const generateTableTypes = (tableName, tables, relationMap, withReturning, relat
4806
4858
  * that has rows, and the one the common audit/outbox case wants — while `{ before, after }`
4807
4859
  * names them explicitly.
4808
4860
  */
4809
- const normalizeWriteHooks = (hooks) => {
4861
+ const normalizeWriteHooks = (hooks, tableColumns, where) => {
4810
4862
  if (!hooks) return;
4811
4863
  if (typeof hooks === "function") return { after: hooks };
4812
4864
  const resolved = {
4813
4865
  before: hooks.before,
4814
4866
  after: hooks.after
4815
4867
  };
4868
+ if (hooks.columns !== void 0) resolved.columns = resolveHookColumns(hooks.columns, !!hooks.after, tableColumns, where);
4816
4869
  return resolved.before || resolved.after ? resolved : void 0;
4817
4870
  };
4818
4871
  /**
4872
+ * Resolves a hook's `columns` against the table it runs on. `where` names the registration
4873
+ * (`config.onWrite.posts`) and, for a top-level one, the table, so the error says which.
4874
+ */
4875
+ const resolveHookColumns = (spec, hasAfter, tableColumns, where) => {
4876
+ if (!hasAfter) throw new Error(`Drizzle-GraphQL Error: ${where}.columns only applies to an 'after' hook, and none is registered.`);
4877
+ if (spec === "all") return { ...tableColumns };
4878
+ if (!Array.isArray(spec)) throw new Error(`Drizzle-GraphQL Error: ${where}.columns must be an array of column names or 'all'.`);
4879
+ const resolved = {};
4880
+ for (const name of spec) {
4881
+ const column = typeof name === "string" && Object.keys(tableColumns).includes(name) ? tableColumns[name] : void 0;
4882
+ if (!column) throw new Error(`Drizzle-GraphQL Error: ${where}.columns names '${String(name)}', which is not a column of that table.`);
4883
+ resolved[name] = column;
4884
+ }
4885
+ return resolved;
4886
+ };
4887
+ /**
4888
+ * Adds a hook's `columns` to a RETURNING column map, and reports which keys it added — the
4889
+ * ones the client never asked for, which have to come back off before the rows are mapped.
4890
+ * Returns the map untouched, and no keys, when the hook named none or the selection already
4891
+ * covers them.
4892
+ */
4893
+ const withHookColumns = (hooks, columns) => {
4894
+ const hookColumns = hooks?.columns;
4895
+ if (!hookColumns) return {
4896
+ returning: columns,
4897
+ added: void 0
4898
+ };
4899
+ const selected = new Set(Object.keys(columns));
4900
+ const added = Object.keys(hookColumns).filter((key) => !selected.has(key));
4901
+ if (!added.length) return {
4902
+ returning: columns,
4903
+ added: void 0
4904
+ };
4905
+ const returning = { ...columns };
4906
+ for (const key of added) returning[key] = hookColumns[key];
4907
+ return {
4908
+ returning,
4909
+ added
4910
+ };
4911
+ };
4912
+ /**
4913
+ * The rows with the hook-only keys taken off, as fresh objects: the hook was handed the
4914
+ * originals and may still hold them, so they are not mutated under it.
4915
+ */
4916
+ const omitHookColumns = (rows, added) => {
4917
+ if (!added) return rows;
4918
+ return rows.map((row) => {
4919
+ const copy = { ...row };
4920
+ for (const key of added) delete copy[key];
4921
+ return copy;
4922
+ });
4923
+ };
4924
+ /**
4819
4925
  * Invokes one position of a field's hooks. Returns `undefined` rather than a resolved promise
4820
4926
  * when nothing is registered, so the awaiting call site costs nothing on an unhooked build.
4821
4927
  */
@@ -4858,6 +4964,7 @@ const writeResolver = ({ db, tableName, operation, single, fieldName, args: quer
4858
4964
  info,
4859
4965
  tx: executor
4860
4966
  };
4967
+ let added;
4861
4968
  return run({
4862
4969
  executor,
4863
4970
  args,
@@ -4867,7 +4974,13 @@ const writeResolver = ({ db, tableName, operation, single, fieldName, args: quer
4867
4974
  after: (rows) => runWriteHook(hooks, "after", {
4868
4975
  ...payload,
4869
4976
  rows: rows ?? []
4870
- })
4977
+ }),
4978
+ returning: (columns) => {
4979
+ const withHook = withHookColumns(hooks, columns);
4980
+ added = withHook.added;
4981
+ return withHook.returning;
4982
+ },
4983
+ output: (rows) => omitHookColumns(rows, added)
4871
4984
  });
4872
4985
  }, !!hooks);
4873
4986
  } catch (e) {
@@ -5041,19 +5154,18 @@ const resolveBuildConfig = (config, schema) => {
5041
5154
  }
5042
5155
  const onWriteConfig = config?.onWrite;
5043
5156
  const writeHooks = /* @__PURE__ */ new Map();
5044
- let globalWriteHooks;
5045
5157
  if (onWriteConfig) {
5046
- if (typeof onWriteConfig === "function" || typeof onWriteConfig.before === "function" || typeof onWriteConfig.after === "function") globalWriteHooks = normalizeWriteHooks(onWriteConfig);
5158
+ if (typeof onWriteConfig === "function" || typeof onWriteConfig.before === "function" || typeof onWriteConfig.after === "function") for (const tableName of featureTables) writeHooks.set(tableName, normalizeWriteHooks(onWriteConfig, getColumns(schema[tableName]), `config.onWrite (on table '${tableName}')`));
5047
5159
  else {
5048
5160
  const tableNames = new Set(tableKeys);
5049
5161
  for (const [tableName, hooks] of Object.entries(onWriteConfig)) {
5050
5162
  if (!tableNames.has(tableName)) throw new Error(`Drizzle-GraphQL Error: config.onWrite names '${tableName}', which is not a table in the Drizzle schema.`);
5051
5163
  if (typeof hooks !== "function" && typeof hooks !== "object") throw new Error(`Drizzle-GraphQL Error: config.onWrite.${tableName} must be a function or an object with 'before' and/or 'after'.`);
5052
- writeHooks.set(tableName, normalizeWriteHooks(hooks));
5164
+ writeHooks.set(tableName, normalizeWriteHooks(hooks, getColumns(schema[tableName]), `config.onWrite.${tableName}`));
5053
5165
  }
5054
5166
  }
5055
5167
  }
5056
- const onWrite = globalWriteHooks || writeHooks.size ? (tableName) => globalWriteHooks ?? writeHooks.get(tableName) : void 0;
5168
+ const onWrite = writeHooks.size ? (tableName) => writeHooks.get(tableName) : void 0;
5057
5169
  const policies = scopeConfig || contextValuesConfig || softDeleteInfos.size || onWrite || defaultOrderBy ? {
5058
5170
  scope: scopeConfig ? (tableName) => scopeConfig[tableName] : void 0,
5059
5171
  contextValues: contextValuesConfig ? (tableName) => contextValuesConfig[tableName] : void 0,
@@ -5119,6 +5231,7 @@ const resolveBuildConfig = (config, schema) => {
5119
5231
  scalars: config?.scalars,
5120
5232
  mapColumnType: config?.mapColumnType,
5121
5233
  enumNameMapper: config?.enumNameMapper,
5234
+ enumValueNameMapper: config?.enumValueNameMapper,
5122
5235
  transactions,
5123
5236
  limits,
5124
5237
  policies,
@@ -7002,7 +7115,7 @@ const buildWriteResolvers = (primaryKeyPropNames) => {
7002
7115
  txCtx,
7003
7116
  policies,
7004
7117
  args: queryArgs,
7005
- run: async ({ executor, args, context, info, before, after }) => {
7118
+ run: async ({ executor, args, context, info, before, after, returning: withHookReturning, output }) => {
7006
7119
  const supplied = single ? [args.values] : args.values;
7007
7120
  if (!supplied.length) throw drizzleError("No values were provided!", { code: "DRIZZLE_NO_VALUES" });
7008
7121
  await before();
@@ -7024,13 +7137,13 @@ const buildWriteResolvers = (primaryKeyPropNames) => {
7024
7137
  scope,
7025
7138
  defaultOrderBy: policies?.defaultOrderBy
7026
7139
  });
7027
- const returning = nestedEntries ? nested.withJoinColumns(tableName, mergedOps(nestedEntries), { ...columns }, table) : columns;
7140
+ const returning = withHookReturning(nestedEntries ? nested.withJoinColumns(tableName, mergedOps(nestedEntries), { ...columns }, table) : columns);
7028
7141
  const runInsert = async (target, values) => {
7029
7142
  let query = target.insert(table).values(values).returning(returning);
7030
7143
  if (conflictDoNothing) query = query.onConflictDoNothing();
7031
7144
  return await query;
7032
7145
  };
7033
- const result = nestedEntries ? await writeWithNestedOps({
7146
+ const written = nestedEntries ? await writeWithNestedOps({
7034
7147
  executor,
7035
7148
  runtime: nested,
7036
7149
  tableName,
@@ -7039,7 +7152,8 @@ const buildWriteResolvers = (primaryKeyPropNames) => {
7039
7152
  write: (tx, values) => runInsert(tx, [values]),
7040
7153
  context
7041
7154
  }) : await runInsert(executor, input);
7042
- await after(result);
7155
+ await after(written);
7156
+ const result = output(written);
7043
7157
  if (single && !result[0]) {
7044
7158
  if (!conflictDoNothing) throw drizzleError("The insert returned no row.", { code: "DRIZZLE_NO_ROW_RETURNED" });
7045
7159
  return;
@@ -7075,7 +7189,7 @@ const buildWriteResolvers = (primaryKeyPropNames) => {
7075
7189
  txCtx,
7076
7190
  policies,
7077
7191
  args: queryArgs,
7078
- run: async ({ executor, args, context, info, before, after }) => {
7192
+ run: async ({ executor, args, context, info, before, after, returning: withHookReturning, output }) => {
7079
7193
  const supplied = single ? [args.values] : args.values;
7080
7194
  if (!supplied.length) throw drizzleError("No values were provided!", { code: "DRIZZLE_NO_VALUES" });
7081
7195
  await before();
@@ -7097,7 +7211,7 @@ const buildWriteResolvers = (primaryKeyPropNames) => {
7097
7211
  scope,
7098
7212
  defaultOrderBy: policies?.defaultOrderBy
7099
7213
  });
7100
- const returning = nestedEntries ? nested.withJoinColumns(tableName, mergedOps(nestedEntries), { ...columns }, table) : columns;
7214
+ const returning = withHookReturning(nestedEntries ? nested.withJoinColumns(tableName, mergedOps(nestedEntries), { ...columns }, table) : columns);
7101
7215
  const runUpsert = async (target, values) => {
7102
7216
  const plan = resolveConflictPlan({
7103
7217
  table,
@@ -7118,7 +7232,7 @@ const buildWriteResolvers = (primaryKeyPropNames) => {
7118
7232
  });
7119
7233
  return await query;
7120
7234
  };
7121
- const result = nestedEntries ? await writeWithNestedOps({
7235
+ const written = nestedEntries ? await writeWithNestedOps({
7122
7236
  executor,
7123
7237
  runtime: nested,
7124
7238
  tableName,
@@ -7127,7 +7241,8 @@ const buildWriteResolvers = (primaryKeyPropNames) => {
7127
7241
  write: (tx, values) => runUpsert(tx, [values]),
7128
7242
  context
7129
7243
  }) : await runUpsert(executor, input);
7130
- await after(result);
7244
+ await after(written);
7245
+ const result = output(written);
7131
7246
  if (single && !result[0]) return;
7132
7247
  const enriched = await withEagerRelations(executor, tableName, result, pkNames, withParams, hasRelations);
7133
7248
  return single ? remapToGraphQLSingleOutput(enriched[0], tableName, table, relationMap) : remapToGraphQLArrayOutput(enriched, tableName, table, relationMap);
@@ -7151,7 +7266,7 @@ const buildWriteResolvers = (primaryKeyPropNames) => {
7151
7266
  txCtx,
7152
7267
  policies,
7153
7268
  args: queryArgs,
7154
- run: async ({ executor, args, context, info, before, after }) => {
7269
+ run: async ({ executor, args, context, info, before, after, returning: withHookReturning, output }) => {
7155
7270
  const { where, set } = args;
7156
7271
  const scope = policies?.scope?.(context);
7157
7272
  await before();
@@ -7182,14 +7297,14 @@ const buildWriteResolvers = (primaryKeyPropNames) => {
7182
7297
  required: single || requireWhere
7183
7298
  });
7184
7299
  if (single) await assertSingleMatch(executor, table, filters);
7185
- const returning = nestedOps ? nested.withJoinColumns(tableName, nestedOps, { ...columns }, table) : columns;
7300
+ const returning = withHookReturning(nestedOps ? nested.withJoinColumns(tableName, nestedOps, { ...columns }, table) : columns);
7186
7301
  const runUpdate = async (target, values) => {
7187
7302
  const writes = Object.keys(values).length > 0;
7188
7303
  let query = writes ? target.update(table).set(values) : target.select(returning).from(table);
7189
7304
  if (filters) query = query.where(filters);
7190
7305
  return await (writes ? query.returning(returning) : query);
7191
7306
  };
7192
- const result = nestedOps ? await updateWithNestedOps({
7307
+ const written = nestedOps ? await updateWithNestedOps({
7193
7308
  executor,
7194
7309
  runtime: nested,
7195
7310
  tableName,
@@ -7199,7 +7314,8 @@ const buildWriteResolvers = (primaryKeyPropNames) => {
7199
7314
  write: runUpdate,
7200
7315
  context
7201
7316
  }) : await runUpdate(executor, input);
7202
- await after(result);
7317
+ await after(written);
7318
+ const result = output(written);
7203
7319
  if (single && result.length > 1) throw drizzleError("'where' matched more than one row!", { code: "DRIZZLE_MULTI_ROW_MATCH" });
7204
7320
  if (single && !result[0]) return;
7205
7321
  const enriched = await withEagerRelations(executor, tableName, result, pkNames, withParams, hasRelations);
@@ -7239,7 +7355,7 @@ const buildWriteResolvers = (primaryKeyPropNames) => {
7239
7355
  txCtx,
7240
7356
  policies,
7241
7357
  args: queryArgs,
7242
- run: async ({ executor, args, context, info, before, after }) => {
7358
+ run: async ({ executor, args, context, info, before, after, returning: withHookReturning, output }) => {
7243
7359
  const { where } = args;
7244
7360
  const hard = canHardDelete && args.hard === true;
7245
7361
  const scope = policies?.scope?.(context);
@@ -7259,9 +7375,10 @@ const buildWriteResolvers = (primaryKeyPropNames) => {
7259
7375
  if (single) await assertSingleMatch(executor, table, filters);
7260
7376
  let query = softDelete && !hard ? executor.update(table).set({ [softDelete.columnName]: restore ? softDelete.writeRestored : softDelete.writeDeleted() }) : executor.delete(table);
7261
7377
  if (filters) query = query.where(filters);
7262
- query = query.returning(columns);
7263
- const result = await query;
7264
- await after(result);
7378
+ query = query.returning(withHookReturning(columns));
7379
+ const written = await query;
7380
+ await after(written);
7381
+ const result = output(written);
7265
7382
  if (single && result.length > 1) throw drizzleError("'where' matched more than one row!", { code: "DRIZZLE_MULTI_ROW_MATCH" });
7266
7383
  if (single) return result[0] ? remapToGraphQLSingleOutput(result[0], tableName, table) : void 0;
7267
7384
  return remapToGraphQLArrayOutput(result, tableName, table);
@@ -7650,7 +7767,7 @@ const createUpdateManyGenerator = (primaryKeyPropNames, runBatch = runUpdateMany
7650
7767
  txCtx,
7651
7768
  policies,
7652
7769
  args: queryArgs,
7653
- run: async ({ executor, args, context, info, before, after }) => {
7770
+ run: async ({ executor, args, context, info, before, after, returning: withHookReturning, output: withoutHookColumns }) => {
7654
7771
  const { updates } = args;
7655
7772
  if (!updates.length) throw drizzleError("No updates were provided!", { code: "DRIZZLE_NO_VALUES" });
7656
7773
  await before();
@@ -7681,7 +7798,7 @@ const createUpdateManyGenerator = (primaryKeyPropNames, runBatch = runUpdateMany
7681
7798
  };
7682
7799
  });
7683
7800
  const anyNested = entries.some((entry) => entry.ops);
7684
- const returning = anyNested ? nested.withJoinColumns(tableName, mergedOps(entries.map((entry) => ({ ops: entry.ops ?? {} }))), { ...columns }, table) : columns;
7801
+ const returning = withHookReturning(anyNested ? nested.withJoinColumns(tableName, mergedOps(entries.map((entry) => ({ ops: entry.ops ?? {} }))), { ...columns }, table) : columns);
7685
7802
  const perEntry = await runBatch({
7686
7803
  executor,
7687
7804
  entries,
@@ -7692,8 +7809,9 @@ const createUpdateManyGenerator = (primaryKeyPropNames, runBatch = runUpdateMany
7692
7809
  tableName,
7693
7810
  context
7694
7811
  });
7695
- const flatRows = perEntry.flat();
7696
- await after(flatRows);
7812
+ const written = perEntry.flat();
7813
+ await after(written);
7814
+ const flatRows = withoutHookColumns(written);
7697
7815
  const enriched = await withEagerRelations(executor, tableName, flatRows, pkNames, withParams, hasRelations);
7698
7816
  const output = [];
7699
7817
  let offset = 0;