@vantreeseba/drizzle-graphql 13.0.0 → 13.1.1
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/README.md +111 -5
- package/dist/index.cjs +334 -153
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +98 -4
- package/dist/index.d.ts +98 -4
- package/dist/index.js +334 -153
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -189,6 +189,14 @@ const GraphQLDecimalString = new graphql.GraphQLScalarType({
|
|
|
189
189
|
const uncapitalize = (input) => input?.length ? `${input[0].toLocaleLowerCase()}${input.length > 1 ? input.slice(1, input.length) : ""}` : input;
|
|
190
190
|
const capitalize = (input) => input?.length ? `${input[0].toLocaleUpperCase()}${input.length > 1 ? input.slice(1, input.length) : ""}` : input;
|
|
191
191
|
/**
|
|
192
|
+
* `inProgress` / `in-progress` / `in progress` / `InProgress` → `IN_PROGRESS`; `HTTPError` →
|
|
193
|
+
* `HTTP_ERROR`. Words are split at non-alphanumeric runs and at lower→upper and
|
|
194
|
+
* acronym→word boundaries, then upper-cased and joined with `_`.
|
|
195
|
+
*
|
|
196
|
+
* The preset behind `enumValueNameMapper: 'constant'`.
|
|
197
|
+
*/
|
|
198
|
+
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("_");
|
|
199
|
+
/**
|
|
192
200
|
* The `typeNameMapper` preset behind `typeNameMapper: 'singularize'`.
|
|
193
201
|
*
|
|
194
202
|
* Derives the singular and plural forms of a table key with the library's own `pluralize`,
|
|
@@ -209,6 +217,8 @@ const singularizeMapper = (tableName) => ({
|
|
|
209
217
|
//#endregion
|
|
210
218
|
//#region src/util/type-converter/index.ts
|
|
211
219
|
const allowedNameChars = /^[a-zA-Z0-9_]+$/;
|
|
220
|
+
/** A GraphQL `Name` that is also allowed as an enum value — the spec reserves the three literals. */
|
|
221
|
+
const validEnumValueName = (name) => /^[_a-zA-Z][_a-zA-Z0-9]*$/.test(name) && name !== "true" && name !== "false" && name !== "null";
|
|
212
222
|
/**
|
|
213
223
|
* The enum type a column was declared with, when it is a *named* one — `pgEnum('status', …)`
|
|
214
224
|
* shared by however many tables reference it. An inline value list (`text({ enum: [...] })`,
|
|
@@ -229,20 +239,35 @@ const pascalize = (input) => input.split(/[^a-zA-Z0-9]+/).filter(Boolean).map((p
|
|
|
229
239
|
*/
|
|
230
240
|
const enumValuesKey = (values) => JSON.stringify(values);
|
|
231
241
|
/**
|
|
242
|
+
* Identity of the GraphQL names those values were given. Kept apart from {@link enumValuesKey}
|
|
243
|
+
* because `enumValueNameMapper` sees the column, so two columns reaching one type can name its
|
|
244
|
+
* values differently — and only one set of names can win.
|
|
245
|
+
*/
|
|
246
|
+
const enumValueNamesKey = (names) => JSON.stringify(names);
|
|
247
|
+
/**
|
|
232
248
|
* Every GraphQL enum name this build has handed out. Two columns that resolve to the same
|
|
233
249
|
* name share one type — that is how the columns of one `pgEnum` end up on a single type —
|
|
234
250
|
* so the values are kept alongside it to tell a genuine collision from a re-use.
|
|
235
251
|
*/
|
|
236
252
|
let enumNamesInUse = /* @__PURE__ */ new Map();
|
|
237
253
|
let enumNameMapper;
|
|
254
|
+
let enumValueNameMapper;
|
|
255
|
+
/** The value-name presets, by the string a config passes for them. */
|
|
256
|
+
const enumValueNamePresets = { constant: ({ value }) => constantCase(value) };
|
|
238
257
|
/**
|
|
239
258
|
* Resets the enum registry for a fresh schema build and installs that build's
|
|
240
|
-
* `enumNameMapper`. Called once per `generateSchemaData`, alongside
|
|
259
|
+
* `enumNameMapper` and `enumValueNameMapper`. Called once per `generateSchemaData`, alongside
|
|
241
260
|
* {@link registerScalarOverrides} — without the reset a second build would reuse the first
|
|
242
261
|
* build's enum types, and so its naming decisions.
|
|
243
262
|
*/
|
|
244
263
|
const registerEnumConfig = (config) => {
|
|
245
264
|
enumNameMapper = config.enumNameMapper;
|
|
265
|
+
const valueMapper = config.enumValueNameMapper;
|
|
266
|
+
if (typeof valueMapper === "string") {
|
|
267
|
+
const preset = enumValueNamePresets[valueMapper];
|
|
268
|
+
if (!preset) throw new Error(`Drizzle-GraphQL Error: Unknown enumValueNameMapper preset '${valueMapper}'. The only preset is 'constant'; pass a function for anything else.`);
|
|
269
|
+
enumValueNameMapper = preset;
|
|
270
|
+
} else enumValueNameMapper = valueMapper;
|
|
246
271
|
enumNamesInUse = /* @__PURE__ */ new Map();
|
|
247
272
|
};
|
|
248
273
|
/**
|
|
@@ -271,22 +296,50 @@ const generateEnumCached = (column, columnName, tableName) => {
|
|
|
271
296
|
columnName,
|
|
272
297
|
values
|
|
273
298
|
}) ?? (declared ? `${pascalize(declared.enumName)}Enum` : `${capitalize(tableName)}${capitalize(columnName)}Enum`);
|
|
299
|
+
const valueNames = values.map((value, index) => {
|
|
300
|
+
const mapped = enumValueNameMapper?.({
|
|
301
|
+
enumName: declared?.enumName,
|
|
302
|
+
schema: declared?.schema,
|
|
303
|
+
tableName,
|
|
304
|
+
columnName,
|
|
305
|
+
values,
|
|
306
|
+
value,
|
|
307
|
+
index,
|
|
308
|
+
typeName: name
|
|
309
|
+
});
|
|
310
|
+
if (mapped === void 0) return allowedNameChars.test(value) ? value : `Option${index}`;
|
|
311
|
+
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.`);
|
|
312
|
+
return mapped;
|
|
313
|
+
});
|
|
314
|
+
const seen = /* @__PURE__ */ new Map();
|
|
315
|
+
values.forEach((value, index) => {
|
|
316
|
+
const valueName = valueNames[index];
|
|
317
|
+
const earlier = seen.get(valueName);
|
|
318
|
+
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.`);
|
|
319
|
+
seen.set(valueName, value);
|
|
320
|
+
});
|
|
274
321
|
const valuesKey = enumValuesKey(values);
|
|
322
|
+
const namesKey = enumValueNamesKey(valueNames);
|
|
275
323
|
const claimed = enumNamesInUse.get(name);
|
|
276
324
|
if (claimed) {
|
|
277
325
|
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.`);
|
|
326
|
+
if (claimed.namesKey !== namesKey) {
|
|
327
|
+
const before = claimed.type.getValues().map((v) => v.name).join(", ");
|
|
328
|
+
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.`);
|
|
329
|
+
}
|
|
278
330
|
return claimed.type;
|
|
279
331
|
}
|
|
280
332
|
const gqlEnum = new graphql.GraphQLEnumType({
|
|
281
333
|
name,
|
|
282
|
-
values: Object.fromEntries(values.map((e, index) => [
|
|
334
|
+
values: Object.fromEntries(values.map((e, index) => [valueNames[index], {
|
|
283
335
|
value: e,
|
|
284
336
|
description: `Value: ${e}`
|
|
285
337
|
}]))
|
|
286
338
|
});
|
|
287
339
|
enumNamesInUse.set(name, {
|
|
288
340
|
type: gqlEnum,
|
|
289
|
-
valuesKey
|
|
341
|
+
valuesKey,
|
|
342
|
+
namesKey
|
|
290
343
|
});
|
|
291
344
|
return gqlEnum;
|
|
292
345
|
};
|
|
@@ -1041,6 +1094,7 @@ const remapToGraphQLArrayOutput = (queryOutput, tableName, table, relationMap) =
|
|
|
1041
1094
|
return queryOutput;
|
|
1042
1095
|
};
|
|
1043
1096
|
const remapFromGraphQLCore = (value, column, columnName) => {
|
|
1097
|
+
if (value === null) return null;
|
|
1044
1098
|
if (getColumnScalarOverride(column, true)) {
|
|
1045
1099
|
if (typeof value === "object" && value !== null && Object.getPrototypeOf(value) === null) return Object.assign({}, value);
|
|
1046
1100
|
return value;
|
|
@@ -3694,6 +3748,145 @@ const runMutation = async (db, context, info, txCtx, body, forceTx) => {
|
|
|
3694
3748
|
return runOnSharedTx(state, body);
|
|
3695
3749
|
};
|
|
3696
3750
|
//#endregion
|
|
3751
|
+
//#region src/util/builders/common/write-hooks.ts
|
|
3752
|
+
/**
|
|
3753
|
+
* Normalizes the two registration shapes: a bare function is the `after` hook — the position
|
|
3754
|
+
* that has rows, and the one the common audit/outbox case wants — while `{ before, after }`
|
|
3755
|
+
* names them explicitly.
|
|
3756
|
+
*/
|
|
3757
|
+
const normalizeWriteHooks = (hooks, tableColumns, where) => {
|
|
3758
|
+
if (!hooks) return;
|
|
3759
|
+
if (typeof hooks === "function") return { after: hooks };
|
|
3760
|
+
const resolved = {
|
|
3761
|
+
before: hooks.before,
|
|
3762
|
+
after: hooks.after
|
|
3763
|
+
};
|
|
3764
|
+
if (hooks.columns !== void 0) resolved.columns = resolveHookColumns(hooks.columns, !!hooks.after, tableColumns, where);
|
|
3765
|
+
return resolved.before || resolved.after ? resolved : void 0;
|
|
3766
|
+
};
|
|
3767
|
+
/**
|
|
3768
|
+
* Resolves a hook's `columns` against the table it runs on. `where` names the registration
|
|
3769
|
+
* (`config.onWrite.posts`) and, for a top-level one, the table, so the error says which.
|
|
3770
|
+
*/
|
|
3771
|
+
const resolveHookColumns = (spec, hasAfter, tableColumns, where) => {
|
|
3772
|
+
if (!hasAfter) throw new Error(`Drizzle-GraphQL Error: ${where}.columns only applies to an 'after' hook, and none is registered.`);
|
|
3773
|
+
if (spec === "all") return { ...tableColumns };
|
|
3774
|
+
if (!Array.isArray(spec)) throw new Error(`Drizzle-GraphQL Error: ${where}.columns must be an array of column names or 'all'.`);
|
|
3775
|
+
const resolved = {};
|
|
3776
|
+
for (const name of spec) {
|
|
3777
|
+
const column = typeof name === "string" && Object.keys(tableColumns).includes(name) ? tableColumns[name] : void 0;
|
|
3778
|
+
if (!column) throw new Error(`Drizzle-GraphQL Error: ${where}.columns names '${String(name)}', which is not a column of that table.`);
|
|
3779
|
+
resolved[name] = column;
|
|
3780
|
+
}
|
|
3781
|
+
return resolved;
|
|
3782
|
+
};
|
|
3783
|
+
/**
|
|
3784
|
+
* Adds a hook's `columns` to a RETURNING column map, and reports which keys it added — the
|
|
3785
|
+
* ones the client never asked for, which have to come back off before the rows are mapped.
|
|
3786
|
+
* Returns the map untouched, and no keys, when the hook named none or the selection already
|
|
3787
|
+
* covers them.
|
|
3788
|
+
*/
|
|
3789
|
+
const withHookColumns = (hooks, columns) => {
|
|
3790
|
+
const hookColumns = hooks?.columns;
|
|
3791
|
+
if (!hookColumns) return {
|
|
3792
|
+
returning: columns,
|
|
3793
|
+
added: void 0
|
|
3794
|
+
};
|
|
3795
|
+
const selected = new Set(Object.keys(columns));
|
|
3796
|
+
const added = Object.keys(hookColumns).filter((key) => !selected.has(key));
|
|
3797
|
+
if (!added.length) return {
|
|
3798
|
+
returning: columns,
|
|
3799
|
+
added: void 0
|
|
3800
|
+
};
|
|
3801
|
+
const returning = { ...columns };
|
|
3802
|
+
for (const key of added) returning[key] = hookColumns[key];
|
|
3803
|
+
return {
|
|
3804
|
+
returning,
|
|
3805
|
+
added
|
|
3806
|
+
};
|
|
3807
|
+
};
|
|
3808
|
+
/**
|
|
3809
|
+
* The rows with the hook-only keys taken off, as fresh objects: the hook was handed the
|
|
3810
|
+
* originals and may still hold them, so they are not mutated under it.
|
|
3811
|
+
*/
|
|
3812
|
+
const omitHookColumns = (rows, added) => {
|
|
3813
|
+
if (!added) return rows;
|
|
3814
|
+
return rows.map((row) => {
|
|
3815
|
+
const copy = { ...row };
|
|
3816
|
+
for (const key of added) delete copy[key];
|
|
3817
|
+
return copy;
|
|
3818
|
+
});
|
|
3819
|
+
};
|
|
3820
|
+
/**
|
|
3821
|
+
* Invokes one position of a field's hooks. Returns `undefined` rather than a resolved promise
|
|
3822
|
+
* when nothing is registered, so the awaiting call site costs nothing on an unhooked build.
|
|
3823
|
+
*/
|
|
3824
|
+
const runWriteHook = (hooks, position, payload) => {
|
|
3825
|
+
const hook = position === "before" ? hooks?.before : hooks?.after;
|
|
3826
|
+
if (!hook) return;
|
|
3827
|
+
return Promise.resolve(hook({
|
|
3828
|
+
...payload,
|
|
3829
|
+
position,
|
|
3830
|
+
rows: payload.rows ?? []
|
|
3831
|
+
}));
|
|
3832
|
+
};
|
|
3833
|
+
//#endregion
|
|
3834
|
+
//#region src/util/builders/common/write-scaffold.ts
|
|
3835
|
+
/**
|
|
3836
|
+
* Wraps one mutation body in the scaffolding above and returns the field it becomes.
|
|
3837
|
+
*
|
|
3838
|
+
* A field carrying a hook is forced into a transaction even when it would have run straight
|
|
3839
|
+
* on `db`: a hook exists to write atomically with the mutation, and one that ran outside the
|
|
3840
|
+
* transaction could not roll anything back.
|
|
3841
|
+
*/
|
|
3842
|
+
const writeResolver = ({ db, tableName, operation, single, fieldName, args: queryArgs, txCtx, policies, run }) => {
|
|
3843
|
+
const hooks = policies?.onWrite?.(tableName, operation);
|
|
3844
|
+
const errorCtx = {
|
|
3845
|
+
table: tableName,
|
|
3846
|
+
operation,
|
|
3847
|
+
field: fieldName
|
|
3848
|
+
};
|
|
3849
|
+
return {
|
|
3850
|
+
name: fieldName,
|
|
3851
|
+
resolver: async (_source, args, context, info) => {
|
|
3852
|
+
try {
|
|
3853
|
+
return await runMutation(db, context, info, txCtx, (executor) => {
|
|
3854
|
+
const payload = {
|
|
3855
|
+
table: tableName,
|
|
3856
|
+
operation,
|
|
3857
|
+
single,
|
|
3858
|
+
args,
|
|
3859
|
+
context,
|
|
3860
|
+
info,
|
|
3861
|
+
tx: executor
|
|
3862
|
+
};
|
|
3863
|
+
let added;
|
|
3864
|
+
return run({
|
|
3865
|
+
executor,
|
|
3866
|
+
args,
|
|
3867
|
+
context,
|
|
3868
|
+
info,
|
|
3869
|
+
before: () => runWriteHook(hooks, "before", payload),
|
|
3870
|
+
after: (rows) => runWriteHook(hooks, "after", {
|
|
3871
|
+
...payload,
|
|
3872
|
+
rows: rows ?? []
|
|
3873
|
+
}),
|
|
3874
|
+
returning: (columns) => {
|
|
3875
|
+
const withHook = withHookColumns(hooks, columns);
|
|
3876
|
+
added = withHook.added;
|
|
3877
|
+
return withHook.returning;
|
|
3878
|
+
},
|
|
3879
|
+
output: (rows) => omitHookColumns(rows, added)
|
|
3880
|
+
});
|
|
3881
|
+
}, !!hooks);
|
|
3882
|
+
} catch (e) {
|
|
3883
|
+
throw withErrorContext(toGraphQLError(e), errorCtx);
|
|
3884
|
+
}
|
|
3885
|
+
},
|
|
3886
|
+
args: queryArgs
|
|
3887
|
+
};
|
|
3888
|
+
};
|
|
3889
|
+
//#endregion
|
|
3697
3890
|
//#region src/util/builders/common/mutation-helpers.ts
|
|
3698
3891
|
/**
|
|
3699
3892
|
* The `hard` argument on the delete mutations of a soft-deleting table that opted into
|
|
@@ -3837,54 +4030,79 @@ const assertSingleMatch = async (executor, table, filters) => {
|
|
|
3837
4030
|
* The plural `update` and `delete` always `RETURNING *`, which is the wrong shape for
|
|
3838
4031
|
* "archive everything older than a year": the caller pays to ship rows it does not want, and
|
|
3839
4032
|
* on a large match that cost is the whole mutation. These return `Int!` instead — how many
|
|
3840
|
-
* rows the write touched
|
|
4033
|
+
* rows the write touched.
|
|
3841
4034
|
*
|
|
3842
|
-
*
|
|
3843
|
-
*
|
|
3844
|
-
*
|
|
3845
|
-
|
|
3846
|
-
|
|
4035
|
+
* Leaving the payload off changes nothing else: the write obeys every rule of the plural
|
|
4036
|
+
* mutation it mirrors, through the same {@link writeResolver} scaffolding.
|
|
4037
|
+
*
|
|
4038
|
+
* - `scope` (and the soft-delete marker) is ANDed into the `where`, so only rows the plural
|
|
4039
|
+
* write could reach are counted and written.
|
|
4040
|
+
* - On a soft-deleting table the delete is the marking `UPDATE`, not a `DELETE`, and rows that
|
|
4041
|
+
* are already marked are not matched; the update skips marked rows too. `hard: true` exists
|
|
4042
|
+
* exactly where the plural delete takes it.
|
|
4043
|
+
* - Context-derived columns are stripped from the update's `set`, as the plural update does.
|
|
4044
|
+
* - `onWrite` hooks run `before` and `after`, inside a transaction. The `after` hook is handed
|
|
4045
|
+
* rows, so a table with one pays for a `RETURNING` — of the columns the hook names in
|
|
4046
|
+
* `columns`, or of the primary key when it names none (the whole row on a table without a
|
|
4047
|
+
* key) — and the count is the number of rows returned. Every other write, and every write on
|
|
4048
|
+
* MySQL (no `RETURNING`; its hooks see `rows: []` as on every MySQL mutation), skips the
|
|
4049
|
+
* `RETURNING` and reads the count off the driver's result via {@link rowsAffected}.
|
|
4050
|
+
*/
|
|
4051
|
+
const generateWriteCount = ({ db, tableName, table, kind, setArgs, filterArgs, fieldName, requireWhere, filterCtx, txCtx, nested, policies, pkNames = [], returnsRows }) => {
|
|
4052
|
+
const softDelete = kind === "delete" ? policies?.softDelete?.(tableName) : void 0;
|
|
4053
|
+
const canHardDelete = softDelete?.hardDelete === true;
|
|
3847
4054
|
const queryArgs = {
|
|
3848
4055
|
...setArgs ? { set: { type: new graphql.GraphQLNonNull(setArgs) } } : {},
|
|
3849
|
-
where: { type: requireWhere ? new graphql.GraphQLNonNull(filterArgs) : filterArgs }
|
|
4056
|
+
where: { type: requireWhere ? new graphql.GraphQLNonNull(filterArgs) : filterArgs },
|
|
4057
|
+
...canHardDelete ? { hard: hardDeleteArg } : {}
|
|
3850
4058
|
};
|
|
3851
|
-
const
|
|
3852
|
-
|
|
4059
|
+
const readsRows = returnsRows && !!policies?.onWrite?.(tableName, kind)?.after;
|
|
4060
|
+
const allColumns = (0, drizzle_orm.getColumns)(table);
|
|
4061
|
+
const keyColumns = pkNames.length ? Object.fromEntries(pkNames.map((name) => [name, allColumns[name]])) : allColumns;
|
|
4062
|
+
return writeResolver({
|
|
4063
|
+
db,
|
|
4064
|
+
tableName,
|
|
3853
4065
|
operation: kind,
|
|
3854
|
-
|
|
3855
|
-
|
|
3856
|
-
|
|
3857
|
-
|
|
3858
|
-
|
|
3859
|
-
|
|
3860
|
-
|
|
3861
|
-
|
|
3862
|
-
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
|
|
3866
|
-
|
|
3867
|
-
|
|
3868
|
-
|
|
3869
|
-
|
|
3870
|
-
|
|
3871
|
-
|
|
3872
|
-
else {
|
|
3873
|
-
const entry = nested?.enabled(tableName) ? nested.split(tableName, set) : void 0;
|
|
3874
|
-
if (entry && nested.hasOps(entry.ops)) throw drizzleError("This mutation does not support nested writes — use the one that returns the rows instead.", { code: "DRIZZLE_NESTED_WRITES_UNSUPPORTED" });
|
|
3875
|
-
const values = remapUpdateInput(entry ? entry.columns : set, table, tableName);
|
|
3876
|
-
if (!Object.keys(values).length) throw drizzleError("Unable to update with no values specified!", { code: "DRIZZLE_NO_VALUES" });
|
|
3877
|
-
query = executor.update(table).set(values);
|
|
3878
|
-
}
|
|
3879
|
-
if (filters) query = query.where(filters);
|
|
3880
|
-
return rowsAffected(await query);
|
|
3881
|
-
});
|
|
3882
|
-
} catch (e) {
|
|
3883
|
-
throw withErrorContext(toGraphQLError(e), errorCtx);
|
|
4066
|
+
single: false,
|
|
4067
|
+
fieldName,
|
|
4068
|
+
txCtx,
|
|
4069
|
+
policies,
|
|
4070
|
+
args: queryArgs,
|
|
4071
|
+
run: async ({ executor, args, context, before, after, returning }) => {
|
|
4072
|
+
const { where, set } = args;
|
|
4073
|
+
const hard = canHardDelete && args.hard === true;
|
|
4074
|
+
const scope = policies?.scope?.(context);
|
|
4075
|
+
await before();
|
|
4076
|
+
let query;
|
|
4077
|
+
if (kind === "delete") query = softDelete && !hard ? executor.update(table).set({ [softDelete.columnName]: softDelete.writeDeleted() }) : executor.delete(table);
|
|
4078
|
+
else {
|
|
4079
|
+
const entry = nested?.enabled(tableName) ? nested.split(tableName, set) : void 0;
|
|
4080
|
+
if (entry && nested.hasOps(entry.ops)) throw drizzleError("This mutation does not support nested writes — use the one that returns the rows instead.", { code: "DRIZZLE_NESTED_WRITES_UNSUPPORTED" });
|
|
4081
|
+
const values = stripContextValues(remapUpdateInput(entry ? entry.columns : set, table, tableName), policies?.contextValues?.(tableName));
|
|
4082
|
+
if (!Object.keys(values).length) throw drizzleError("Unable to update with no values specified!", { code: "DRIZZLE_NO_VALUES" });
|
|
4083
|
+
query = executor.update(table).set(values);
|
|
3884
4084
|
}
|
|
3885
|
-
|
|
3886
|
-
|
|
3887
|
-
|
|
4085
|
+
const filters = scopedWhere({
|
|
4086
|
+
scope,
|
|
4087
|
+
tableName,
|
|
4088
|
+
table,
|
|
4089
|
+
where,
|
|
4090
|
+
relationCtx: relationFilterCtx(filterCtx, tableName),
|
|
4091
|
+
required: requireWhere,
|
|
4092
|
+
deleted: hard ? "INCLUDE" : void 0
|
|
4093
|
+
});
|
|
4094
|
+
if (filters) query = query.where(filters);
|
|
4095
|
+
if (!readsRows) {
|
|
4096
|
+
const result = await query;
|
|
4097
|
+
await after();
|
|
4098
|
+
return rowsAffected(result);
|
|
4099
|
+
}
|
|
4100
|
+
const named = returning({});
|
|
4101
|
+
const rows = await query.returning(Object.keys(named).length ? named : keyColumns);
|
|
4102
|
+
await after(rows);
|
|
4103
|
+
return rows.length;
|
|
4104
|
+
}
|
|
4105
|
+
});
|
|
3888
4106
|
};
|
|
3889
4107
|
//#endregion
|
|
3890
4108
|
//#region src/util/batch-loader/index.ts
|
|
@@ -4822,84 +5040,6 @@ const generateTableTypes = (tableName, tables, relationMap, withReturning, relat
|
|
|
4822
5040
|
};
|
|
4823
5041
|
};
|
|
4824
5042
|
//#endregion
|
|
4825
|
-
//#region src/util/builders/common/write-hooks.ts
|
|
4826
|
-
/**
|
|
4827
|
-
* Normalizes the two registration shapes: a bare function is the `after` hook — the position
|
|
4828
|
-
* that has rows, and the one the common audit/outbox case wants — while `{ before, after }`
|
|
4829
|
-
* names them explicitly.
|
|
4830
|
-
*/
|
|
4831
|
-
const normalizeWriteHooks = (hooks) => {
|
|
4832
|
-
if (!hooks) return;
|
|
4833
|
-
if (typeof hooks === "function") return { after: hooks };
|
|
4834
|
-
const resolved = {
|
|
4835
|
-
before: hooks.before,
|
|
4836
|
-
after: hooks.after
|
|
4837
|
-
};
|
|
4838
|
-
return resolved.before || resolved.after ? resolved : void 0;
|
|
4839
|
-
};
|
|
4840
|
-
/**
|
|
4841
|
-
* Invokes one position of a field's hooks. Returns `undefined` rather than a resolved promise
|
|
4842
|
-
* when nothing is registered, so the awaiting call site costs nothing on an unhooked build.
|
|
4843
|
-
*/
|
|
4844
|
-
const runWriteHook = (hooks, position, payload) => {
|
|
4845
|
-
const hook = position === "before" ? hooks?.before : hooks?.after;
|
|
4846
|
-
if (!hook) return;
|
|
4847
|
-
return Promise.resolve(hook({
|
|
4848
|
-
...payload,
|
|
4849
|
-
position,
|
|
4850
|
-
rows: payload.rows ?? []
|
|
4851
|
-
}));
|
|
4852
|
-
};
|
|
4853
|
-
//#endregion
|
|
4854
|
-
//#region src/util/builders/common/write-scaffold.ts
|
|
4855
|
-
/**
|
|
4856
|
-
* Wraps one mutation body in the scaffolding above and returns the field it becomes.
|
|
4857
|
-
*
|
|
4858
|
-
* A field carrying a hook is forced into a transaction even when it would have run straight
|
|
4859
|
-
* on `db`: a hook exists to write atomically with the mutation, and one that ran outside the
|
|
4860
|
-
* transaction could not roll anything back.
|
|
4861
|
-
*/
|
|
4862
|
-
const writeResolver = ({ db, tableName, operation, single, fieldName, args: queryArgs, txCtx, policies, run }) => {
|
|
4863
|
-
const hooks = policies?.onWrite?.(tableName, operation);
|
|
4864
|
-
const errorCtx = {
|
|
4865
|
-
table: tableName,
|
|
4866
|
-
operation,
|
|
4867
|
-
field: fieldName
|
|
4868
|
-
};
|
|
4869
|
-
return {
|
|
4870
|
-
name: fieldName,
|
|
4871
|
-
resolver: async (_source, args, context, info) => {
|
|
4872
|
-
try {
|
|
4873
|
-
return await runMutation(db, context, info, txCtx, (executor) => {
|
|
4874
|
-
const payload = {
|
|
4875
|
-
table: tableName,
|
|
4876
|
-
operation,
|
|
4877
|
-
single,
|
|
4878
|
-
args,
|
|
4879
|
-
context,
|
|
4880
|
-
info,
|
|
4881
|
-
tx: executor
|
|
4882
|
-
};
|
|
4883
|
-
return run({
|
|
4884
|
-
executor,
|
|
4885
|
-
args,
|
|
4886
|
-
context,
|
|
4887
|
-
info,
|
|
4888
|
-
before: () => runWriteHook(hooks, "before", payload),
|
|
4889
|
-
after: (rows) => runWriteHook(hooks, "after", {
|
|
4890
|
-
...payload,
|
|
4891
|
-
rows: rows ?? []
|
|
4892
|
-
})
|
|
4893
|
-
});
|
|
4894
|
-
}, !!hooks);
|
|
4895
|
-
} catch (e) {
|
|
4896
|
-
throw withErrorContext(toGraphQLError(e), errorCtx);
|
|
4897
|
-
}
|
|
4898
|
-
},
|
|
4899
|
-
args: queryArgs
|
|
4900
|
-
};
|
|
4901
|
-
};
|
|
4902
|
-
//#endregion
|
|
4903
5043
|
//#region src/util/features.ts
|
|
4904
5044
|
/**
|
|
4905
5045
|
* What a table generates when the config says nothing. Everything is on except the opt-in
|
|
@@ -5063,19 +5203,18 @@ const resolveBuildConfig = (config, schema) => {
|
|
|
5063
5203
|
}
|
|
5064
5204
|
const onWriteConfig = config?.onWrite;
|
|
5065
5205
|
const writeHooks = /* @__PURE__ */ new Map();
|
|
5066
|
-
let globalWriteHooks;
|
|
5067
5206
|
if (onWriteConfig) {
|
|
5068
|
-
if (typeof onWriteConfig === "function" || typeof onWriteConfig.before === "function" || typeof onWriteConfig.after === "function")
|
|
5207
|
+
if (typeof onWriteConfig === "function" || typeof onWriteConfig.before === "function" || typeof onWriteConfig.after === "function") for (const tableName of featureTables) writeHooks.set(tableName, normalizeWriteHooks(onWriteConfig, (0, drizzle_orm.getColumns)(schema[tableName]), `config.onWrite (on table '${tableName}')`));
|
|
5069
5208
|
else {
|
|
5070
5209
|
const tableNames = new Set(tableKeys);
|
|
5071
5210
|
for (const [tableName, hooks] of Object.entries(onWriteConfig)) {
|
|
5072
5211
|
if (!tableNames.has(tableName)) throw new Error(`Drizzle-GraphQL Error: config.onWrite names '${tableName}', which is not a table in the Drizzle schema.`);
|
|
5073
5212
|
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'.`);
|
|
5074
|
-
writeHooks.set(tableName, normalizeWriteHooks(hooks));
|
|
5213
|
+
writeHooks.set(tableName, normalizeWriteHooks(hooks, (0, drizzle_orm.getColumns)(schema[tableName]), `config.onWrite.${tableName}`));
|
|
5075
5214
|
}
|
|
5076
5215
|
}
|
|
5077
5216
|
}
|
|
5078
|
-
const onWrite =
|
|
5217
|
+
const onWrite = writeHooks.size ? (tableName) => writeHooks.get(tableName) : void 0;
|
|
5079
5218
|
const policies = scopeConfig || contextValuesConfig || softDeleteInfos.size || onWrite || defaultOrderBy ? {
|
|
5080
5219
|
scope: scopeConfig ? (tableName) => scopeConfig[tableName] : void 0,
|
|
5081
5220
|
contextValues: contextValuesConfig ? (tableName) => contextValuesConfig[tableName] : void 0,
|
|
@@ -5141,6 +5280,7 @@ const resolveBuildConfig = (config, schema) => {
|
|
|
5141
5280
|
scalars: config?.scalars,
|
|
5142
5281
|
mapColumnType: config?.mapColumnType,
|
|
5143
5282
|
enumNameMapper: config?.enumNameMapper,
|
|
5283
|
+
enumValueNameMapper: config?.enumValueNameMapper,
|
|
5144
5284
|
transactions,
|
|
5145
5285
|
limits,
|
|
5146
5286
|
policies,
|
|
@@ -6901,7 +7041,9 @@ const generateSchemaData$2 = (db, schema, relations, options) => {
|
|
|
6901
7041
|
fieldName: updateCountFieldName,
|
|
6902
7042
|
requireWhere: tableFeatures.requireWhere,
|
|
6903
7043
|
filterCtx,
|
|
6904
|
-
txCtx: mutationTxCtx
|
|
7044
|
+
txCtx: mutationTxCtx,
|
|
7045
|
+
policies,
|
|
7046
|
+
returnsRows: false
|
|
6905
7047
|
}) : void 0;
|
|
6906
7048
|
const deleteCountGenerated = tableFeatures.delete && tableFeatures.countMutations ? generateWriteCount({
|
|
6907
7049
|
db,
|
|
@@ -6912,7 +7054,9 @@ const generateSchemaData$2 = (db, schema, relations, options) => {
|
|
|
6912
7054
|
fieldName: deleteCountFieldName,
|
|
6913
7055
|
requireWhere: tableFeatures.requireWhere,
|
|
6914
7056
|
filterCtx,
|
|
6915
|
-
txCtx: mutationTxCtx
|
|
7057
|
+
txCtx: mutationTxCtx,
|
|
7058
|
+
policies,
|
|
7059
|
+
returnsRows: false
|
|
6916
7060
|
}) : void 0;
|
|
6917
7061
|
const hardDeleteMeta = softDeleteInfo?.hardDelete ? { hardDelete: true } : {};
|
|
6918
7062
|
const withMeta = (generated, meta) => ({
|
|
@@ -6982,12 +7126,25 @@ const generateSchemaData$2 = (db, schema, relations, options) => {
|
|
|
6982
7126
|
{
|
|
6983
7127
|
generated: updateCountGenerated,
|
|
6984
7128
|
type: new graphql.GraphQLNonNull(graphql.GraphQLInt),
|
|
7129
|
+
meta: {
|
|
7130
|
+
operation: "update",
|
|
7131
|
+
single: false,
|
|
7132
|
+
targetArg: "where",
|
|
7133
|
+
count: true
|
|
7134
|
+
},
|
|
6985
7135
|
description: "How many rows the update touched. The rows themselves are not read back, which is the point of this mutation."
|
|
6986
7136
|
},
|
|
6987
7137
|
{
|
|
6988
7138
|
generated: deleteCountGenerated,
|
|
6989
7139
|
type: new graphql.GraphQLNonNull(graphql.GraphQLInt),
|
|
6990
|
-
|
|
7140
|
+
meta: {
|
|
7141
|
+
operation: "delete",
|
|
7142
|
+
single: false,
|
|
7143
|
+
targetArg: "where",
|
|
7144
|
+
count: true,
|
|
7145
|
+
...hardDeleteMeta
|
|
7146
|
+
},
|
|
7147
|
+
description: "How many rows the delete removed — or, on a soft-deleting table, marked deleted. The rows themselves are not read back, which is the point of this mutation."
|
|
6991
7148
|
}
|
|
6992
7149
|
],
|
|
6993
7150
|
onConflictInput,
|
|
@@ -7024,7 +7181,7 @@ const buildWriteResolvers = (primaryKeyPropNames) => {
|
|
|
7024
7181
|
txCtx,
|
|
7025
7182
|
policies,
|
|
7026
7183
|
args: queryArgs,
|
|
7027
|
-
run: async ({ executor, args, context, info, before, after }) => {
|
|
7184
|
+
run: async ({ executor, args, context, info, before, after, returning: withHookReturning, output }) => {
|
|
7028
7185
|
const supplied = single ? [args.values] : args.values;
|
|
7029
7186
|
if (!supplied.length) throw drizzleError("No values were provided!", { code: "DRIZZLE_NO_VALUES" });
|
|
7030
7187
|
await before();
|
|
@@ -7046,13 +7203,13 @@ const buildWriteResolvers = (primaryKeyPropNames) => {
|
|
|
7046
7203
|
scope,
|
|
7047
7204
|
defaultOrderBy: policies?.defaultOrderBy
|
|
7048
7205
|
});
|
|
7049
|
-
const returning = nestedEntries ? nested.withJoinColumns(tableName, mergedOps(nestedEntries), { ...columns }, table) : columns;
|
|
7206
|
+
const returning = withHookReturning(nestedEntries ? nested.withJoinColumns(tableName, mergedOps(nestedEntries), { ...columns }, table) : columns);
|
|
7050
7207
|
const runInsert = async (target, values) => {
|
|
7051
7208
|
let query = target.insert(table).values(values).returning(returning);
|
|
7052
7209
|
if (conflictDoNothing) query = query.onConflictDoNothing();
|
|
7053
7210
|
return await query;
|
|
7054
7211
|
};
|
|
7055
|
-
const
|
|
7212
|
+
const written = nestedEntries ? await writeWithNestedOps({
|
|
7056
7213
|
executor,
|
|
7057
7214
|
runtime: nested,
|
|
7058
7215
|
tableName,
|
|
@@ -7061,7 +7218,8 @@ const buildWriteResolvers = (primaryKeyPropNames) => {
|
|
|
7061
7218
|
write: (tx, values) => runInsert(tx, [values]),
|
|
7062
7219
|
context
|
|
7063
7220
|
}) : await runInsert(executor, input);
|
|
7064
|
-
await after(
|
|
7221
|
+
await after(written);
|
|
7222
|
+
const result = output(written);
|
|
7065
7223
|
if (single && !result[0]) {
|
|
7066
7224
|
if (!conflictDoNothing) throw drizzleError("The insert returned no row.", { code: "DRIZZLE_NO_ROW_RETURNED" });
|
|
7067
7225
|
return;
|
|
@@ -7097,7 +7255,7 @@ const buildWriteResolvers = (primaryKeyPropNames) => {
|
|
|
7097
7255
|
txCtx,
|
|
7098
7256
|
policies,
|
|
7099
7257
|
args: queryArgs,
|
|
7100
|
-
run: async ({ executor, args, context, info, before, after }) => {
|
|
7258
|
+
run: async ({ executor, args, context, info, before, after, returning: withHookReturning, output }) => {
|
|
7101
7259
|
const supplied = single ? [args.values] : args.values;
|
|
7102
7260
|
if (!supplied.length) throw drizzleError("No values were provided!", { code: "DRIZZLE_NO_VALUES" });
|
|
7103
7261
|
await before();
|
|
@@ -7119,7 +7277,7 @@ const buildWriteResolvers = (primaryKeyPropNames) => {
|
|
|
7119
7277
|
scope,
|
|
7120
7278
|
defaultOrderBy: policies?.defaultOrderBy
|
|
7121
7279
|
});
|
|
7122
|
-
const returning = nestedEntries ? nested.withJoinColumns(tableName, mergedOps(nestedEntries), { ...columns }, table) : columns;
|
|
7280
|
+
const returning = withHookReturning(nestedEntries ? nested.withJoinColumns(tableName, mergedOps(nestedEntries), { ...columns }, table) : columns);
|
|
7123
7281
|
const runUpsert = async (target, values) => {
|
|
7124
7282
|
const plan = resolveConflictPlan({
|
|
7125
7283
|
table,
|
|
@@ -7140,7 +7298,7 @@ const buildWriteResolvers = (primaryKeyPropNames) => {
|
|
|
7140
7298
|
});
|
|
7141
7299
|
return await query;
|
|
7142
7300
|
};
|
|
7143
|
-
const
|
|
7301
|
+
const written = nestedEntries ? await writeWithNestedOps({
|
|
7144
7302
|
executor,
|
|
7145
7303
|
runtime: nested,
|
|
7146
7304
|
tableName,
|
|
@@ -7149,7 +7307,8 @@ const buildWriteResolvers = (primaryKeyPropNames) => {
|
|
|
7149
7307
|
write: (tx, values) => runUpsert(tx, [values]),
|
|
7150
7308
|
context
|
|
7151
7309
|
}) : await runUpsert(executor, input);
|
|
7152
|
-
await after(
|
|
7310
|
+
await after(written);
|
|
7311
|
+
const result = output(written);
|
|
7153
7312
|
if (single && !result[0]) return;
|
|
7154
7313
|
const enriched = await withEagerRelations(executor, tableName, result, pkNames, withParams, hasRelations);
|
|
7155
7314
|
return single ? remapToGraphQLSingleOutput(enriched[0], tableName, table, relationMap) : remapToGraphQLArrayOutput(enriched, tableName, table, relationMap);
|
|
@@ -7173,7 +7332,7 @@ const buildWriteResolvers = (primaryKeyPropNames) => {
|
|
|
7173
7332
|
txCtx,
|
|
7174
7333
|
policies,
|
|
7175
7334
|
args: queryArgs,
|
|
7176
|
-
run: async ({ executor, args, context, info, before, after }) => {
|
|
7335
|
+
run: async ({ executor, args, context, info, before, after, returning: withHookReturning, output }) => {
|
|
7177
7336
|
const { where, set } = args;
|
|
7178
7337
|
const scope = policies?.scope?.(context);
|
|
7179
7338
|
await before();
|
|
@@ -7204,14 +7363,14 @@ const buildWriteResolvers = (primaryKeyPropNames) => {
|
|
|
7204
7363
|
required: single || requireWhere
|
|
7205
7364
|
});
|
|
7206
7365
|
if (single) await assertSingleMatch(executor, table, filters);
|
|
7207
|
-
const returning = nestedOps ? nested.withJoinColumns(tableName, nestedOps, { ...columns }, table) : columns;
|
|
7366
|
+
const returning = withHookReturning(nestedOps ? nested.withJoinColumns(tableName, nestedOps, { ...columns }, table) : columns);
|
|
7208
7367
|
const runUpdate = async (target, values) => {
|
|
7209
7368
|
const writes = Object.keys(values).length > 0;
|
|
7210
7369
|
let query = writes ? target.update(table).set(values) : target.select(returning).from(table);
|
|
7211
7370
|
if (filters) query = query.where(filters);
|
|
7212
7371
|
return await (writes ? query.returning(returning) : query);
|
|
7213
7372
|
};
|
|
7214
|
-
const
|
|
7373
|
+
const written = nestedOps ? await updateWithNestedOps({
|
|
7215
7374
|
executor,
|
|
7216
7375
|
runtime: nested,
|
|
7217
7376
|
tableName,
|
|
@@ -7221,7 +7380,8 @@ const buildWriteResolvers = (primaryKeyPropNames) => {
|
|
|
7221
7380
|
write: runUpdate,
|
|
7222
7381
|
context
|
|
7223
7382
|
}) : await runUpdate(executor, input);
|
|
7224
|
-
await after(
|
|
7383
|
+
await after(written);
|
|
7384
|
+
const result = output(written);
|
|
7225
7385
|
if (single && result.length > 1) throw drizzleError("'where' matched more than one row!", { code: "DRIZZLE_MULTI_ROW_MATCH" });
|
|
7226
7386
|
if (single && !result[0]) return;
|
|
7227
7387
|
const enriched = await withEagerRelations(executor, tableName, result, pkNames, withParams, hasRelations);
|
|
@@ -7261,7 +7421,7 @@ const buildWriteResolvers = (primaryKeyPropNames) => {
|
|
|
7261
7421
|
txCtx,
|
|
7262
7422
|
policies,
|
|
7263
7423
|
args: queryArgs,
|
|
7264
|
-
run: async ({ executor, args, context, info, before, after }) => {
|
|
7424
|
+
run: async ({ executor, args, context, info, before, after, returning: withHookReturning, output }) => {
|
|
7265
7425
|
const { where } = args;
|
|
7266
7426
|
const hard = canHardDelete && args.hard === true;
|
|
7267
7427
|
const scope = policies?.scope?.(context);
|
|
@@ -7281,9 +7441,10 @@ const buildWriteResolvers = (primaryKeyPropNames) => {
|
|
|
7281
7441
|
if (single) await assertSingleMatch(executor, table, filters);
|
|
7282
7442
|
let query = softDelete && !hard ? executor.update(table).set({ [softDelete.columnName]: restore ? softDelete.writeRestored : softDelete.writeDeleted() }) : executor.delete(table);
|
|
7283
7443
|
if (filters) query = query.where(filters);
|
|
7284
|
-
query = query.returning(columns);
|
|
7285
|
-
const
|
|
7286
|
-
await after(
|
|
7444
|
+
query = query.returning(withHookReturning(columns));
|
|
7445
|
+
const written = await query;
|
|
7446
|
+
await after(written);
|
|
7447
|
+
const result = output(written);
|
|
7287
7448
|
if (single && result.length > 1) throw drizzleError("'where' matched more than one row!", { code: "DRIZZLE_MULTI_ROW_MATCH" });
|
|
7288
7449
|
if (single) return result[0] ? remapToGraphQLSingleOutput(result[0], tableName, table) : void 0;
|
|
7289
7450
|
return remapToGraphQLArrayOutput(result, tableName, table);
|
|
@@ -7484,7 +7645,10 @@ const createSchemaDataGenerator = (adapter) => {
|
|
|
7484
7645
|
requireWhere: tableFeatures.requireWhere,
|
|
7485
7646
|
filterCtx,
|
|
7486
7647
|
txCtx: mutationTxCtx,
|
|
7487
|
-
nested: nestedRuntime
|
|
7648
|
+
nested: nestedRuntime,
|
|
7649
|
+
policies,
|
|
7650
|
+
pkNames: primaryKeyPropNames(schema[tableName]),
|
|
7651
|
+
returnsRows: true
|
|
7488
7652
|
}) : void 0;
|
|
7489
7653
|
const deleteCountGenerated = tableFeatures.delete && tableFeatures.countMutations ? generateWriteCount({
|
|
7490
7654
|
db,
|
|
@@ -7495,7 +7659,10 @@ const createSchemaDataGenerator = (adapter) => {
|
|
|
7495
7659
|
fieldName: deleteCountFieldName,
|
|
7496
7660
|
requireWhere: tableFeatures.requireWhere,
|
|
7497
7661
|
filterCtx,
|
|
7498
|
-
txCtx: mutationTxCtx
|
|
7662
|
+
txCtx: mutationTxCtx,
|
|
7663
|
+
policies,
|
|
7664
|
+
pkNames: primaryKeyPropNames(schema[tableName]),
|
|
7665
|
+
returnsRows: true
|
|
7499
7666
|
}) : void 0;
|
|
7500
7667
|
const hardDeleteMeta = softDeleteInfo?.hardDelete ? { hardDelete: true } : {};
|
|
7501
7668
|
return {
|
|
@@ -7605,12 +7772,25 @@ const createSchemaDataGenerator = (adapter) => {
|
|
|
7605
7772
|
{
|
|
7606
7773
|
generated: updateCountGenerated,
|
|
7607
7774
|
type: new graphql.GraphQLNonNull(graphql.GraphQLInt),
|
|
7775
|
+
meta: {
|
|
7776
|
+
operation: "update",
|
|
7777
|
+
single: false,
|
|
7778
|
+
targetArg: "where",
|
|
7779
|
+
count: true
|
|
7780
|
+
},
|
|
7608
7781
|
description: "How many rows the update touched. The rows themselves are not read back, which is the point of this mutation."
|
|
7609
7782
|
},
|
|
7610
7783
|
{
|
|
7611
7784
|
generated: deleteCountGenerated,
|
|
7612
7785
|
type: new graphql.GraphQLNonNull(graphql.GraphQLInt),
|
|
7613
|
-
|
|
7786
|
+
meta: {
|
|
7787
|
+
operation: "delete",
|
|
7788
|
+
single: false,
|
|
7789
|
+
targetArg: "where",
|
|
7790
|
+
count: true,
|
|
7791
|
+
...hardDeleteMeta
|
|
7792
|
+
},
|
|
7793
|
+
description: "How many rows the delete removed — or, on a soft-deleting table, marked deleted. The rows themselves are not read back, which is the point of this mutation."
|
|
7614
7794
|
}
|
|
7615
7795
|
],
|
|
7616
7796
|
onConflictInput,
|
|
@@ -7672,7 +7852,7 @@ const createUpdateManyGenerator = (primaryKeyPropNames, runBatch = runUpdateMany
|
|
|
7672
7852
|
txCtx,
|
|
7673
7853
|
policies,
|
|
7674
7854
|
args: queryArgs,
|
|
7675
|
-
run: async ({ executor, args, context, info, before, after }) => {
|
|
7855
|
+
run: async ({ executor, args, context, info, before, after, returning: withHookReturning, output: withoutHookColumns }) => {
|
|
7676
7856
|
const { updates } = args;
|
|
7677
7857
|
if (!updates.length) throw drizzleError("No updates were provided!", { code: "DRIZZLE_NO_VALUES" });
|
|
7678
7858
|
await before();
|
|
@@ -7703,7 +7883,7 @@ const createUpdateManyGenerator = (primaryKeyPropNames, runBatch = runUpdateMany
|
|
|
7703
7883
|
};
|
|
7704
7884
|
});
|
|
7705
7885
|
const anyNested = entries.some((entry) => entry.ops);
|
|
7706
|
-
const returning = anyNested ? nested.withJoinColumns(tableName, mergedOps(entries.map((entry) => ({ ops: entry.ops ?? {} }))), { ...columns }, table) : columns;
|
|
7886
|
+
const returning = withHookReturning(anyNested ? nested.withJoinColumns(tableName, mergedOps(entries.map((entry) => ({ ops: entry.ops ?? {} }))), { ...columns }, table) : columns);
|
|
7707
7887
|
const perEntry = await runBatch({
|
|
7708
7888
|
executor,
|
|
7709
7889
|
entries,
|
|
@@ -7714,8 +7894,9 @@ const createUpdateManyGenerator = (primaryKeyPropNames, runBatch = runUpdateMany
|
|
|
7714
7894
|
tableName,
|
|
7715
7895
|
context
|
|
7716
7896
|
});
|
|
7717
|
-
const
|
|
7718
|
-
await after(
|
|
7897
|
+
const written = perEntry.flat();
|
|
7898
|
+
await after(written);
|
|
7899
|
+
const flatRows = withoutHookColumns(written);
|
|
7719
7900
|
const enriched = await withEagerRelations(executor, tableName, flatRows, pkNames, withParams, hasRelations);
|
|
7720
7901
|
const output = [];
|
|
7721
7902
|
let offset = 0;
|