@vantreeseba/drizzle-graphql 7.9.1 → 8.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.d.ts CHANGED
@@ -1,11 +1,222 @@
1
- import { Column, Table, Relation, SQL, One, Many } from 'drizzle-orm';
1
+ import { Column, Table, Relation, SQL, TablesRelationalConfig, One, Many } from 'drizzle-orm';
2
2
  import { MySqlDatabase } from 'drizzle-orm/mysql-core';
3
3
  import { PgAsyncDatabase } from 'drizzle-orm/pg-core';
4
4
  import { BaseSQLiteDatabase } from 'drizzle-orm/sqlite-core';
5
- import { GraphQLScalarType, GraphQLOutputType, GraphQLInputType, GraphQLFieldResolver, GraphQLResolveInfo, GraphQLSchema, GraphQLInputObjectType, GraphQLObjectType, GraphQLNonNull, GraphQLList, GraphQLEnumType } from 'graphql';
5
+ import { ASTNode, GraphQLError, GraphQLScalarType, GraphQLOutputType, GraphQLInputType, GraphQLFieldResolver, GraphQLResolveInfo, GraphQLSchema, GraphQLInputObjectType, GraphQLObjectType, GraphQLNonNull, GraphQLList, GraphQLEnumType } from 'graphql';
6
6
  import { ResolveTree } from 'graphql-parse-resolve-info';
7
7
  export { GraphQLDate, GraphQLDateTime, GraphQLJSON, GraphQLUUID } from 'graphql-scalars';
8
8
 
9
+ /**
10
+ * The identity a generated field publishes about itself, under `extensions.drizzle`.
11
+ *
12
+ * Every field this library generates carries one, so a wrapper — `graphql-middleware`,
13
+ * envelop, `graphql-shield`, an audit log, a cache invalidator — can ask the schema what a
14
+ * field is instead of reverse-engineering its name. Names are configurable (`prefixes`,
15
+ * `suffixes`, `typeNameMapper`), so name parsing is ambiguous by construction; this is not.
16
+ *
17
+ * This shape is stable API: fields are only ever added to it, never removed or repurposed.
18
+ */
19
+ /** What family of field this is. */
20
+ type DrizzleFieldKind = 'query' | 'mutation' | 'relation' | 'aggregate' | 'type';
21
+ /**
22
+ * What the field does. Arity is carried separately by `single`, so `select` covers both
23
+ * the list query and its `…Single` sibling rather than being spelled twice.
24
+ */
25
+ type DrizzleFieldOperation = 'select' | 'insert' | 'update' | 'updateMany' | 'upsert' | 'delete' | 'restore' | 'aggregate' | 'groupBy' | 'relation' | 'relationAggregate';
26
+ /** The argument holding the rows a field is about, when it has one. */
27
+ type DrizzleTargetArg = 'where' | 'values' | 'updates';
28
+ /** `extensions.drizzle` on a generated field. */
29
+ type DrizzleFieldExtension = {
30
+ /** The Drizzle schema key of the table the field reads or writes. */
31
+ table: string;
32
+ kind: Exclude<DrizzleFieldKind, 'type'>;
33
+ operation: DrizzleFieldOperation;
34
+ /** Whether the field resolves to one row (or one aggregate) rather than a list. */
35
+ single: boolean;
36
+ /**
37
+ * Which argument names the rows: `where` for select/update/delete/aggregate, `values`
38
+ * for insert and upsert, `updates` for the batch update's per-entry `{ where, set }`.
39
+ * Absent when the field takes no such argument.
40
+ */
41
+ targetArg?: DrizzleTargetArg;
42
+ /**
43
+ * Primary-key property names of `table`, in schema order. Empty when the table declares
44
+ * no primary key. Carried here so {@link identifyRows} works from the field alone.
45
+ */
46
+ primaryKey: readonly string[];
47
+ /**
48
+ * For `operation: 'delete'` on a soft-deleting table that opted into `hardDelete`: the
49
+ * field takes a `hard` argument that issues a real `DELETE` rather than writing the marker.
50
+ * Absent everywhere else, so a wrapper can gate purging without parsing arguments.
51
+ */
52
+ hardDelete?: boolean;
53
+ /** For `kind: 'relation'` and `operation: 'relationAggregate'`: the relation's name. */
54
+ relation?: string;
55
+ /** For `kind: 'relation'` and `operation: 'relationAggregate'`: the table it hangs off. */
56
+ parentTable?: string;
57
+ };
58
+ /** `extensions.drizzle` on a generated object type. */
59
+ type DrizzleTypeExtension = {
60
+ /** The Drizzle schema key this type was generated from. */
61
+ table: string;
62
+ kind: 'type';
63
+ /** Primary-key property names of `table`, in schema order. Empty when there is none. */
64
+ primaryKey: readonly string[];
65
+ };
66
+ type DrizzleExtension = DrizzleFieldExtension | DrizzleTypeExtension;
67
+ /** Anything carrying GraphQL `extensions` — a field config, a field, or a named type. */
68
+ type WithExtensions = {
69
+ extensions?: {
70
+ drizzle?: unknown;
71
+ } | null;
72
+ } | null | undefined;
73
+ /**
74
+ * Reads the `drizzle` extension off a field or type, typed. Returns `undefined` for
75
+ * anything this library did not generate, which is the check a wrapper wants before it
76
+ * treats a field as one of ours.
77
+ */
78
+ declare const drizzleExtension: (target: WithExtensions) => DrizzleExtension | undefined;
79
+ /** Narrows to the field form — object types carry `kind: 'type'` and no operation. */
80
+ declare const isDrizzleFieldExtension: (value: DrizzleExtension | undefined) => value is DrizzleFieldExtension;
81
+ /** The rows a field's arguments name. */
82
+ type IdentifiedRows = {
83
+ /** The Drizzle schema key of the table being read or written. */
84
+ table: string;
85
+ /** Primary-key property names, in schema order. */
86
+ primaryKey: readonly string[];
87
+ /** One entry per identified row: the primary-key values, keyed by property name. */
88
+ rows: Record<string, unknown>[];
89
+ /**
90
+ * Whether `rows` is exactly the set the operation affects.
91
+ *
92
+ * `false` means the arguments do not pin the rows down — an insert whose key the
93
+ * database generates, a `where` that filters on something other than the primary key,
94
+ * a plural update with no filter at all, or a table with no primary key. `rows` is then
95
+ * a best-effort subset-free superset: every entry in it is a row the key values name,
96
+ * but other filters may narrow the real set further, and it may be empty.
97
+ */
98
+ complete: boolean;
99
+ };
100
+ /**
101
+ * The rows a generated field's arguments are about, as primary-key values.
102
+ *
103
+ * The row identity of a mutation lives in a different argument for every operation —
104
+ * `where` for update and delete, `values` for insert and upsert, a per-entry `where` inside
105
+ * `updates` for the batch update — and each of those has both a one-row and a many-row
106
+ * spelling. This reads whichever one applies:
107
+ *
108
+ * ```ts
109
+ * const field = info.parentType.getFields()[info.fieldName];
110
+ * const target = identifyRows(field, args);
111
+ * if (target?.complete) await invalidate(target.table, target.rows);
112
+ * ```
113
+ *
114
+ * Returns `undefined` for a field this library did not generate, or an object type. Check
115
+ * `complete` before treating `rows` as the affected set — see {@link IdentifiedRows}.
116
+ */
117
+ declare const identifyRows: (field: WithExtensions, args: Record<string, unknown> | undefined) => IdentifiedRows | undefined;
118
+
119
+ /**
120
+ * The classification of an error drizzle-graphql raised itself, published as
121
+ * `extensions.code`. A consumer maps these onto its own vocabulary; the prose is written for
122
+ * a client to read and may be reworded in any release, so it is not something to match on.
123
+ */
124
+ type DrizzleErrorCode =
125
+ /** A `Single` write's `where` matched more than one row, so nothing was written. */
126
+ 'DRIZZLE_MULTI_ROW_MATCH'
127
+ /** A write that refuses to run unbounded was given no `where`, or one matching every row. */
128
+ | 'DRIZZLE_WHERE_REQUIRED'
129
+ /** The requested `limit` is above the maximum the field's limit policy allows. */
130
+ | 'DRIZZLE_LIMIT_EXCEEDED'
131
+ /** A write's `values` / `set` carried nothing to write. */
132
+ | 'DRIZZLE_NO_VALUES'
133
+ /** An insert reported no row back, so there is nothing to return or attach. */
134
+ | 'DRIZZLE_NO_ROW_RETURNED'
135
+ /** The driver reported no row count for a returnless write. */
136
+ | 'DRIZZLE_ROW_COUNT_UNAVAILABLE'
137
+ /** A nested write was passed to a field that cannot run one. */
138
+ | 'DRIZZLE_NESTED_WRITES_UNSUPPORTED'
139
+ /** A nested `create` / `connect` / `disconnect` / `set` operand was not usable. */
140
+ | 'DRIZZLE_NESTED_WRITE_INVALID'
141
+ /** A `groupBy` named no column, or one that cannot be grouped by. */
142
+ | 'DRIZZLE_INVALID_GROUP_BY'
143
+ /** An `after` cursor did not decode, or was issued for a different ordering. */
144
+ | 'DRIZZLE_INVALID_CURSOR'
145
+ /** A `where` key was not a filter of the table, or a relation was used as one. */
146
+ | 'DRIZZLE_INVALID_FILTER'
147
+ /** A column's update operations input held none, or more than one. */
148
+ | 'DRIZZLE_INVALID_UPDATE_OPERATION'
149
+ /** A configured `scope` hook returned something that is not a predicate. */
150
+ | 'DRIZZLE_INVALID_SCOPE'
151
+ /** An `orderBy` named something that cannot be ordered by in this query. */
152
+ | 'DRIZZLE_INVALID_ORDER_BY'
153
+ /** `distinct` was asked for where it cannot be applied. */
154
+ | 'DRIZZLE_INVALID_DISTINCT'
155
+ /** An `onConflict` named a target or update column the upsert cannot use. */
156
+ | 'DRIZZLE_INVALID_ON_CONFLICT'
157
+ /** A written value could not be converted to what its column stores. */
158
+ | 'DRIZZLE_INVALID_INPUT_VALUE'
159
+ /** A write's input named a key that is not a column of the table. */
160
+ | 'DRIZZLE_UNKNOWN_COLUMN'
161
+ /** A stored value could not be represented by the scalar that transports it. */
162
+ | 'DRIZZLE_UNREPRESENTABLE_VALUE'
163
+ /** The request's shared mutation transaction was rolled back, so this field never ran. */
164
+ | 'DRIZZLE_TRANSACTION_ABORTED';
165
+ /**
166
+ * What an error was about, published as `extensions.drizzle` beside the code — the runtime
167
+ * half of the `extensions.drizzle` markers a generated field carries.
168
+ *
169
+ * `field` is the **generated** field name, which is why it is data rather than prose: a build
170
+ * that republishes these fields under other names (`RenameRootFields`, a stitched gateway, a
171
+ * hand-written façade) would otherwise be telling a client about a field it has never heard
172
+ * of. The name the client asked for is already in the error's `path`.
173
+ */
174
+ type DrizzleErrorContext = {
175
+ /** The Drizzle schema key of the table the field reads or writes. */
176
+ table?: string;
177
+ operation?: DrizzleFieldOperation;
178
+ /** The generated field's own name, pre-rename. */
179
+ field?: string;
180
+ /** For a relation field: the relation's name. */
181
+ relation?: string;
182
+ };
183
+ /**
184
+ * A `GraphQLError` carrying its context as data. The message says what went wrong, in prose
185
+ * meant for a client; `extensions.code` classifies it and `extensions.drizzle` says which
186
+ * table, operation and generated field it came from.
187
+ */
188
+ declare const drizzleError: (message: string, { code, nodes, ...context }: DrizzleErrorContext & {
189
+ code: DrizzleErrorCode;
190
+ /** The AST the error is about, where the throw site has one — it becomes the `locations`. */
191
+ nodes?: ASTNode | readonly ASTNode[];
192
+ }) => GraphQLError;
193
+ /**
194
+ * Default for `config.onError`: keeps drizzle-graphql's own errors, which are written for
195
+ * the client, and replaces driver/database errors with a generic message. Their text names
196
+ * tables, columns, constraints and offending values, none of which belongs in a response.
197
+ * The original is preserved on `originalError` for server-side logging.
198
+ */
199
+ declare const defaultErrorMapper: (error: unknown) => unknown;
200
+
201
+ /** {@link BuildSchemaConfig.limits} resolved for one table. `undefined` means no policy. */
202
+ type ResolvedLimitPolicy = {
203
+ defaultLimit: number | undefined;
204
+ maxLimit: number | undefined;
205
+ clampToMax: boolean;
206
+ };
207
+ /**
208
+ * Looks up the limit policy for a table by its Drizzle schema key. Built once per schema by
209
+ * `buildSchema`; `undefined` for a table the caller left unbounded.
210
+ */
211
+ type LimitPolicyFor = (tableName: string) => ResolvedLimitPolicy | undefined;
212
+ /**
213
+ * {@link BuildSchemaConfig.defaults} resolved for one table: the `orderBy` a read falls back
214
+ * to when the request passes none, already in the shape the generated argument has.
215
+ * `undefined` means the table declares no default and keeps whatever order the database
216
+ * returns.
217
+ */
218
+ type DefaultOrderByFor = (tableName: string) => Record<string, any> | undefined;
219
+
9
220
  /**
10
221
  * A scalar override for one column. A bare scalar applies to both directions; the object form
11
222
  * lets output (object type, aggregate min/max) and input (create/update inputs, filter operands)
@@ -171,6 +382,12 @@ type FilterColumnOperatorsCore<TColumn extends Column, TColType = GetColumnGqlDa
171
382
  isEmpty: boolean;
172
383
  isNull: boolean;
173
384
  isNotNull: boolean;
385
+ /**
386
+ * String columns: compares every operator in this object case-insensitively
387
+ * (`lower(column)` against `lower(operand)`). Applies to the operators beside it only —
388
+ * a nested `AND`/`OR`/`NOT` branch sets its own.
389
+ */
390
+ insensitive: boolean;
174
391
  }>;
175
392
  type FiltersCore<TTable extends Table> = Partial<{
176
393
  [Column in keyof TTable['_']['columns']]: FilterColumnOperatorsCore<TTable['_']['columns'][Column]>;
@@ -198,133 +415,55 @@ type OrderByArgs<TTable extends Table> = {
198
415
  };
199
416
  };
200
417
 
201
- /** Optional mapper from table key to singular/plural name pair. Return undefined to use default naming for a table. */
202
- type TypeNameMapper = (tableName: string) => {
203
- singular: string;
204
- plural: string;
205
- } | undefined;
206
418
  /**
207
- * Extracts the join column info from a drizzle-orm v1 Relation object.
208
- * Returns the JS property name of the local column on the parent table and the
209
- * Column object for the foreign column on the target table, or undefined if the
210
- * relation internals are not accessible.
211
- */
212
- declare const extractRelationJoinColumns: (relEntry: TableNamedRelations, parentTable: Table, targetTable: Table) => {
213
- localColPropName: string;
214
- foreignCol: Column;
215
- foreignColPropName: string;
216
- } | undefined;
217
- type RelationResolverFactory = (params: {
218
- tableName: string;
219
- relationName: string;
220
- relEntry: TableNamedRelations;
221
- isOne: boolean;
222
- }) => GraphQLFieldResolver<any, any> | undefined;
223
- /**
224
- * Key on the GraphQL context object under which a caller can place a Drizzle transaction
225
- * (or any other executor: a pooled connection, a logging proxy). Every generated resolver
226
- * reads it at resolve time and runs its statements there instead of on the database the
227
- * schema was built from, which is what lets several mutations in one request share a
228
- * transaction and lets a query see that transaction's uncommitted rows:
229
- *
230
- * ```ts
231
- * await db.transaction(async (tx) => {
232
- * await graphql({ schema, source, contextValue: { [drizzleExecutorKey]: tx } });
233
- * });
234
- * ```
419
+ * What a generated type is, so a name mapper can answer for one kind without matching on the
420
+ * spelling of the default name.
235
421
  *
236
- * Registered with `Symbol.for` so the ESM and CJS builds of this package agree on it when
237
- * both end up loaded in one process.
422
+ * - `object` — a table's row type, the one `typeNameMapper` already names
423
+ * - `filter` / `listRelationFilter` — `<Type>Filters` and the `some`/`none`/`every` wrapper
424
+ * - `orderBy` — `<Type>OrderBy`
425
+ * - `createInput` / `updateInput` / `updateManyInput` / `nestedWriteInput` / `fieldUpdateInput`
426
+ * — the write inputs
427
+ * - `aggregate` — `<Type>Aggregate` and its per-operation children (`operation` names which)
428
+ * - `having` / `groupBy` / `groupKeys` — the group-by family
429
+ * - `onConflict` — the upsert's conflict input
430
+ * - `uniqueKey` — a compound unique constraint's key input
431
+ * - `columnEnum` — an enum naming a table's columns (`distinct`, conflict target, …)
432
+ * - `columnFilter` — a shared per-scalar filter (`StringFilter`, `IntFilter`, …), not per table
433
+ * - `shared` — everything else the build creates once (`OrderNulls`, `DeletedFilter`, …)
238
434
  */
239
- declare const drizzleExecutorKey: unique symbol;
240
- /** The write a hook is attached to. `restore` only exists for a soft-deleting table. */
241
- type WriteOperation = 'insert' | 'update' | 'updateMany' | 'upsert' | 'delete' | 'restore';
242
- /** What a write hook is handed. See {@link BuildSchemaConfig.onWrite}. */
243
- type WriteHookPayload = {
244
- /** Drizzle schema key of the table being written. */
245
- table: string;
246
- operation: WriteOperation;
247
- /** `'before'` runs ahead of the statement, `'after'` once it has returned. */
248
- position: 'before' | 'after';
249
- /** Whether the field writes a single row (`createUsersSingle`) or a list. */
250
- single: boolean;
251
- /** The field's GraphQL arguments, exactly as the resolver received them. */
252
- args: any;
435
+ type GeneratedTypeKind = 'object' | 'filter' | 'listRelationFilter' | 'orderBy' | 'createInput' | 'updateInput' | 'updateManyInput' | 'nestedWriteInput' | 'fieldUpdateInput' | 'aggregate' | 'having' | 'groupBy' | 'groupKeys' | 'onConflict' | 'uniqueKey' | 'columnEnum' | 'columnFilter' | 'shared';
436
+ /** What a name mapper is told about the type it is being asked to name. */
437
+ interface GeneratedTypeInfo {
438
+ /** Which kind of generated type this is. */
439
+ kind: GeneratedTypeKind;
440
+ /** The name the library would use, before any prefix or suffix. */
441
+ defaultName: string;
442
+ /** The table key the type belongs to, where it belongs to one. */
443
+ table?: string;
253
444
  /**
254
- * The rows the write produced, as the database returned them — before the output mapper,
255
- * so values are database-shaped. Empty at the `before` position, and on MySQL, whose
256
- * mutations return no rows.
445
+ * The narrower thing the type is for, where the kind alone does not say: the aggregate
446
+ * operation (`sum`, `avg`, …), the relation a nested-write input is for, the scalar a
447
+ * column filter covers, the columns of a unique key.
257
448
  */
258
- rows: any[];
259
- context: any;
260
- info: any;
261
- /** The executor the mutation itself ran on. Throwing from the hook rolls it back. */
262
- tx: any;
263
- };
264
- type WriteHook = (payload: WriteHookPayload) => void | Promise<void>;
265
- type WriteHookPositions = {
266
- before?: WriteHook;
267
- after?: WriteHook;
268
- };
269
- type WriteHooks = WriteHook | WriteHookPositions;
270
- /** Resolved, per-field hooks: `undefined` when the table registered none. */
271
- type ResolvedWriteHooks = {
272
- before?: WriteHook;
273
- after?: WriteHook;
274
- };
275
- /** Build-time lookup of a table's hooks, installed on {@link TablePolicies}. */
276
- type WriteHookFor = (tableName: string, operation: WriteOperation) => ResolvedWriteHooks | undefined;
277
- /**
278
- * Creates a RelationResolverFactory that generates field-level resolvers for each relation.
279
- * Each resolver:
280
- * 1. Returns pre-fetched data if the parent resolver already included it (eager path, zero cost).
281
- * 2. When limit/offset args are present, falls back to a direct per-item query.
282
- * 3. Otherwise batches all sibling resolver calls within the same GraphQL execution tick
283
- * into a single IN-clause query, eliminating N+1 database round-trips.
284
- *
285
- * `nullOrdering` is the dialect's native `ORDER BY` placement for `NULL` — the keyset
286
- * predicate behind a relation's `after` argument has to match it, or rows with a `NULL` in an
287
- * ordered column would be skipped instead of paged through. It has no sensible default, so
288
- * each dialect builder states it: `'nulls-largest'` for PostgreSQL, `'nulls-smallest'` for
289
- * MySQL and SQLite.
290
- */
291
- declare const createRelationResolverFactory: (db: any, tables: Record<string, Table>, nullOrdering: NullOrdering, filterCtx?: RelationFilterBase, limits?: LimitPolicyFor, policies?: TablePolicies) => RelationResolverFactory;
292
- /** {@link BuildSchemaConfig.limits} resolved for one table. `undefined` means no policy. */
293
- type ResolvedLimitPolicy = {
294
- defaultLimit: number | undefined;
295
- maxLimit: number | undefined;
296
- clampToMax: boolean;
297
- };
298
- /**
299
- * Looks up the limit policy for a table by its Drizzle schema key. Built once per schema by
300
- * `buildSchema`; `undefined` for a table the caller left unbounded.
301
- */
302
- type LimitPolicyFor = (tableName: string) => ResolvedLimitPolicy | undefined;
449
+ operation?: string;
450
+ }
303
451
  /**
304
- * {@link BuildSchemaConfig.defaults} resolved for one table: the `orderBy` a read falls back
305
- * to when the request passes none, already in the shape the generated argument has.
306
- * `undefined` means the table declares no default and keeps whatever order the database
307
- * returns.
452
+ * Renames a generated type. Return `undefined` — or the same name — to keep the default,
453
+ * which is what an unlisted kind should do; anything returned is used verbatim, without the
454
+ * build's prefix or suffix.
308
455
  */
309
- type DefaultOrderByFor = (tableName: string) => Record<string, any> | undefined;
310
- /**
311
- * Compiles an `orderBy` argument into ORDER BY expressions, highest priority first.
312
- *
313
- * A key naming one of the table's columns orders by that column. A key naming a to-one
314
- * relation takes the target table's own OrderBy input and orders by the related row's
315
- * column(s), compiled as a correlated scalar subquery — reusing the aliased-join machinery
316
- * the relation filters are built on — so it works identically on all three dialects and
317
- * inside the relational query builder's aliased CTEs. Priorities share one global space
318
- * across relation boundaries. Each entry may also carry `nulls: first | last`
319
- * (see {@link orderExpressions} for how MySQL emulates it).
320
- *
321
- * An entry may instead set `matchFilterOrder`, which sorts by the column's position in the
322
- * `inArray` list the same request's `where` gives it — `whereArgs` is that `where`.
323
- *
324
- * `ctx` supplies the tables and relation map that relation ordering needs; callers whose
325
- * inputs cannot contain relation keys may omit it.
326
- */
327
- declare const extractOrderBy: <TTable extends Table, TArgs extends OrderByArgs<any> = OrderByArgs<TTable>>(table: TTable, orderArgs: TArgs, ctx?: RelationFilterContext, whereArgs?: Record<string, any>) => SQL[];
456
+ type DerivedTypeNameMapper = (info: GeneratedTypeInfo) => string | undefined;
457
+
458
+ /** Optional mapper from table key to singular/plural name pair. Return undefined to use default naming for a table. */
459
+ type TypeNameMapper = (tableName: string) => {
460
+ singular: string;
461
+ plural: string;
462
+ } | undefined;
463
+
464
+ /** A table's unique-key `where` fields: field name → the constraint's member property names. */
465
+ type UniqueKeyMap = Record<string, readonly string[]>;
466
+
328
467
  /**
329
468
  * Everything `extractFilters` needs to turn a relation key in a `where` argument into a
330
469
  * correlated subquery. Omitted by callers that don't generate relation filters, in which case
@@ -344,13 +483,58 @@ interface RelationFilterContext {
344
483
  aliases?: {
345
484
  n: number;
346
485
  };
486
+ /**
487
+ * The compound-unique-key `where` fields of each table, keyed by table schema key. Only
488
+ * populated when `features.uniqueKeyFilters` generated them — a key field the build did not
489
+ * generate is an unknown filter key like any other.
490
+ */
491
+ uniqueKeys?: Record<string, UniqueKeyMap>;
347
492
  }
348
493
  /**
349
494
  * The build-scoped half of {@link RelationFilterContext}. Created once per generated schema and
350
495
  * handed to every resolver, which adds the table it is filtering.
351
496
  */
352
- type RelationFilterBase = Pick<RelationFilterContext, 'tables' | 'relationMap'>;
497
+ type RelationFilterBase = Pick<RelationFilterContext, 'tables' | 'relationMap' | 'uniqueKeys'>;
353
498
  declare const extractFilters: <TTable extends Table>(table: TTable, tableName: string, filters: Filters<TTable>, relationCtx?: RelationFilterContext) => SQL | undefined;
499
+
500
+ /** The write a hook is attached to. `restore` only exists for a soft-deleting table. */
501
+ type WriteOperation = 'insert' | 'update' | 'updateMany' | 'upsert' | 'delete' | 'restore';
502
+ /** What a write hook is handed. See {@link BuildSchemaConfig.onWrite}. */
503
+ type WriteHookPayload = {
504
+ /** Drizzle schema key of the table being written. */
505
+ table: string;
506
+ operation: WriteOperation;
507
+ /** `'before'` runs ahead of the statement, `'after'` once it has returned. */
508
+ position: 'before' | 'after';
509
+ /** Whether the field writes a single row (`createUsersSingle`) or a list. */
510
+ single: boolean;
511
+ /** The field's GraphQL arguments, exactly as the resolver received them. */
512
+ args: any;
513
+ /**
514
+ * The rows the write produced, as the database returned them — before the output mapper,
515
+ * so values are database-shaped. Empty at the `before` position, and on MySQL, whose
516
+ * mutations return no rows.
517
+ */
518
+ rows: any[];
519
+ context: any;
520
+ info: any;
521
+ /** The executor the mutation itself ran on. Throwing from the hook rolls it back. */
522
+ tx: any;
523
+ };
524
+ type WriteHook = (payload: WriteHookPayload) => void | Promise<void>;
525
+ type WriteHookPositions = {
526
+ before?: WriteHook;
527
+ after?: WriteHook;
528
+ };
529
+ type WriteHooks = WriteHook | WriteHookPositions;
530
+ /** Resolved, per-field hooks: `undefined` when the table registered none. */
531
+ type ResolvedWriteHooks = {
532
+ before?: WriteHook;
533
+ after?: WriteHook;
534
+ };
535
+ /** Build-time lookup of a table's hooks, installed on {@link TablePolicies}. */
536
+ type WriteHookFor = (tableName: string, operation: WriteOperation) => ResolvedWriteHooks | undefined;
537
+
354
538
  /**
355
539
  * A table's row-level scope: the predicate every generated read, update and delete on that
356
540
  * table is narrowed by. See `BuildSchemaConfig.scope`.
@@ -368,10 +552,11 @@ type ScopeFor = (tableName: string) => ScopeHook | undefined;
368
552
  /**
369
553
  * One table's soft-delete convention, resolved against the real column at build time.
370
554
  *
371
- * `nullable` picks the shape of the predicate. A nullable column marks a row deleted by
372
- * holding a value at all (`deletedAt IS NOT NULL`), which is the common timestamp form; a
373
- * non-nullable one marks it by holding `marker` (`isDeleted = true`), so the column has to
374
- * have a constant that means "deleted" and another that means "not deleted".
555
+ * `marker` picks the shape of the predicate. When the config names a constant that means
556
+ * deleted, the predicate compares against it (`isDeleted = true`); otherwise the column marks
557
+ * a row deleted by holding a value at all (`deletedAt IS NOT NULL`), the common timestamp
558
+ * form. A NOT NULL column has no "absent" state, so it must have a marker; a nullable one
559
+ * takes the NULL-means-alive reading only when no marker was configured.
375
560
  */
376
561
  type SoftDeleteInfo = {
377
562
  /** Property name of the column on the drizzle table — also the key on an aliased proxy. */
@@ -382,8 +567,22 @@ type SoftDeleteInfo = {
382
567
  writeDeleted: () => any;
383
568
  /** Written by the restore mutation. */
384
569
  writeRestored: any;
385
- /** Non-nullable form only: the constant that means "this row is deleted". */
570
+ /**
571
+ * The constant that means "this row is deleted", when the config named one. Required on a
572
+ * NOT NULL column; optional on a nullable one, which otherwise reads NULL as alive.
573
+ */
386
574
  marker?: any;
575
+ /**
576
+ * Which reads hide marked rows by default — `'all'` (the table's own fields and every
577
+ * relation pointing at it) or `'root'` (its own fields only). See
578
+ * {@link relationDeletedDefault}.
579
+ */
580
+ scope: 'root' | 'all';
581
+ /**
582
+ * Whether the delete mutations take a `hard` argument that issues a real `DELETE`. Off
583
+ * unless the table opts in, so the generated schema says which tables can be purged.
584
+ */
585
+ hardDelete: boolean;
387
586
  };
388
587
  /** Build-time lookup: the soft-delete convention of a table, if it declares one. */
389
588
  type SoftDeleteFor = (tableName: string) => SoftDeleteInfo | undefined;
@@ -409,6 +608,26 @@ type TablePolicies = {
409
608
  /** See `BuildSchemaConfig.defaults`. */
410
609
  defaultOrderBy?: DefaultOrderByFor;
411
610
  };
611
+
612
+ /**
613
+ * Compiles an `orderBy` argument into ORDER BY expressions, highest priority first.
614
+ *
615
+ * A key naming one of the table's columns orders by that column. A key naming a to-one
616
+ * relation takes the target table's own OrderBy input and orders by the related row's
617
+ * column(s), compiled as a correlated scalar subquery — reusing the aliased-join machinery
618
+ * the relation filters are built on — so it works identically on all three dialects and
619
+ * inside the relational query builder's aliased CTEs. Priorities share one global space
620
+ * across relation boundaries. Each entry may also carry `nulls: first | last`
621
+ * (see {@link orderExpressions} for how MySQL emulates it).
622
+ *
623
+ * An entry may instead set `matchFilterOrder`, which sorts by the column's position in the
624
+ * `inArray` list the same request's `where` gives it — `whereArgs` is that `where`.
625
+ *
626
+ * `ctx` supplies the tables and relation map that relation ordering needs; callers whose
627
+ * inputs cannot contain relation keys may omit it.
628
+ */
629
+ declare const extractOrderBy: <TTable extends Table, TArgs extends OrderByArgs<any> = OrderByArgs<TTable>>(table: TTable, orderArgs: TArgs, ctx?: RelationFilterContext, whereArgs?: Record<string, any>) => SQL[];
630
+
412
631
  /**
413
632
  * Where a dialect's default sort places NULLs relative to non-NULL values:
414
633
  * - `nulls-largest` — PostgreSQL: NULLs sort as the largest values (last in ASC, first in DESC).
@@ -417,13 +636,58 @@ type TablePolicies = {
417
636
  * The keyset predicate has to agree with the dialect's ORDER BY, so each builder passes its own.
418
637
  */
419
638
  type NullOrdering = 'nulls-largest' | 'nulls-smallest';
639
+
420
640
  /**
421
- * Default for `config.onError`: keeps drizzle-graphql's own errors, which are written for
422
- * the client, and replaces driver/database errors with a generic message. Their text names
423
- * tables, columns, constraints and offending values, none of which belongs in a response.
424
- * The original is preserved on `originalError` for server-side logging.
641
+ * Key on the GraphQL context object under which a caller can place a Drizzle transaction
642
+ * (or any other executor: a pooled connection, a logging proxy). Every generated resolver
643
+ * reads it at resolve time and runs its statements there instead of on the database the
644
+ * schema was built from, which is what lets several mutations in one request share a
645
+ * transaction and lets a query see that transaction's uncommitted rows:
646
+ *
647
+ * ```ts
648
+ * await db.transaction(async (tx) => {
649
+ * await graphql({ schema, source, contextValue: { [drizzleExecutorKey]: tx } });
650
+ * });
651
+ * ```
652
+ *
653
+ * Registered with `Symbol.for` so the ESM and CJS builds of this package agree on it when
654
+ * both end up loaded in one process.
425
655
  */
426
- declare const defaultErrorMapper: (error: unknown) => unknown;
656
+ declare const drizzleExecutorKey: unique symbol;
657
+
658
+ /**
659
+ * Extracts the join column info from a drizzle-orm v1 Relation object.
660
+ * Returns the JS property name of the local column on the parent table and the
661
+ * Column object for the foreign column on the target table, or undefined if the
662
+ * relation internals are not accessible.
663
+ */
664
+ declare const extractRelationJoinColumns: (relEntry: TableNamedRelations, parentTable: Table, targetTable: Table) => {
665
+ localColPropName: string;
666
+ foreignCol: Column;
667
+ foreignColPropName: string;
668
+ } | undefined;
669
+ type RelationResolverFactory = (params: {
670
+ tableName: string;
671
+ relationName: string;
672
+ relEntry: TableNamedRelations;
673
+ isOne: boolean;
674
+ }) => GraphQLFieldResolver<any, any> | undefined;
675
+
676
+ /**
677
+ * Creates a RelationResolverFactory that generates field-level resolvers for each relation.
678
+ * Each resolver:
679
+ * 1. Returns pre-fetched data if the parent resolver already included it (eager path, zero cost).
680
+ * 2. When limit/offset args are present, falls back to a direct per-item query.
681
+ * 3. Otherwise batches all sibling resolver calls within the same GraphQL execution tick
682
+ * into a single IN-clause query, eliminating N+1 database round-trips.
683
+ *
684
+ * `nullOrdering` is the dialect's native `ORDER BY` placement for `NULL` — the keyset
685
+ * predicate behind a relation's `after` argument has to match it, or rows with a `NULL` in an
686
+ * ordered column would be skipped instead of paged through. It has no sensible default, so
687
+ * each dialect builder states it: `'nulls-largest'` for PostgreSQL, `'nulls-smallest'` for
688
+ * MySQL and SQLite.
689
+ */
690
+ declare const createRelationResolverFactory: (db: any, tables: Record<string, Table>, nullOrdering: NullOrdering, filterCtx?: RelationFilterBase, limits?: LimitPolicyFor, policies?: TablePolicies) => RelationResolverFactory;
427
691
 
428
692
  type Relations<TTable extends string = string, TConfig extends Record<string, Relation> = Record<string, Relation>> = {
429
693
  table: {
@@ -435,7 +699,19 @@ type Relations<TTable extends string = string, TConfig extends Record<string, Re
435
699
  };
436
700
 
437
701
  type AnyDrizzleDB<TSchema extends Record<string, any>> = PgAsyncDatabase<any, TSchema> | BaseSQLiteDatabase<any, any, TSchema> | MySqlDatabase<any, any, TSchema>;
438
- type ExtractTables<TSchema extends Record<string, Table | unknown>> = {
702
+ /**
703
+ * The tables a database handle knows about, keyed by their schema key.
704
+ *
705
+ * Two shapes reach this type, because drizzle-orm v1 changed what a database's generic slot
706
+ * holds. SQLite and MySQL handles still carry the schema module itself, so the tables are
707
+ * picked out of it directly. PostgreSQL handles carry only the relations config built by
708
+ * `buildRelations` — a `Record<string, { table; name; relations }>` — so the tables are read
709
+ * off its `table` members instead. Without the second branch every PG-derived type collapses
710
+ * to `never`.
711
+ */
712
+ type ExtractTables<TSchema extends Record<string, Table | unknown>> = TSchema extends TablesRelationalConfig ? {
713
+ [K in keyof TSchema]: TSchema[K]['table'] extends infer TTable extends Table ? TTable : never;
714
+ } : {
439
715
  [K in keyof TSchema as TSchema[K] extends Table ? K : never]: TSchema[K] extends Table ? TSchema[K] : never;
440
716
  };
441
717
  type ExtractRelations<TSchema extends Record<string, Table | unknown>> = {
@@ -550,7 +826,7 @@ type DeleteResolver<TTable extends Table, IsReturnless extends boolean> = (sourc
550
826
  type DeleteSingleResolver<TTable extends Table, IsReturnless extends boolean> = (source: any, args: DeleteSingleArgs<TTable>, context: any, info: GraphQLResolveInfo) => Promise<IsReturnless extends false ? GetRemappedTableDataType<TTable> | undefined : MutationReturnlessResult>;
551
827
  type QueriesCore<TSchemaTables extends Record<string, Table>, TSchemaRelations extends Record<string, Relations>, TInputs extends Record<string, GraphQLInputObjectType>, TOutputs extends Record<string, GraphQLObjectType>> = {
552
828
  [TName in keyof TSchemaTables as TName extends string ? `${Uncapitalize<TName>}` : never]: TName extends string ? {
553
- type: GraphQLNonNull<GraphQLList<GraphQLNonNull<TOutputs[`${Capitalize<TName>}SelectItem`]>>>;
829
+ type: GraphQLNonNull<GraphQLList<GraphQLNonNull<TOutputs[Capitalize<TName>]>>>;
554
830
  args: {
555
831
  offset: {
556
832
  type: GraphQLScalarType<number, number>;
@@ -572,7 +848,7 @@ type QueriesCore<TSchemaTables extends Record<string, Table>, TSchemaRelations e
572
848
  } : never;
573
849
  } & {
574
850
  [TName in keyof TSchemaTables as TName extends string ? `${Uncapitalize<TName>}Single` : never]: TName extends string ? {
575
- type: TOutputs[`${Capitalize<TName>}SelectItem`];
851
+ type: TOutputs[Capitalize<TName>];
576
852
  args: {
577
853
  offset: {
578
854
  type: GraphQLScalarType<number, number>;
@@ -599,30 +875,30 @@ type QueriesCore<TSchemaTables extends Record<string, Table>, TSchemaRelations e
599
875
  };
600
876
  type MutationsCore<TSchemaTables extends Record<string, Table>, TInputs extends Record<string, GraphQLInputObjectType>, TOutputs extends Record<string, GraphQLObjectType>, IsReturnless extends boolean> = {
601
877
  [TName in keyof TSchemaTables as TName extends string ? `create${Capitalize<TName>}` : never]: TName extends string ? {
602
- type: IsReturnless extends true ? TOutputs['MutationReturn'] extends GraphQLObjectType ? TOutputs['MutationReturn'] : never : GraphQLNonNull<GraphQLList<GraphQLNonNull<TOutputs[`${Capitalize<TName>}Item`]>>>;
878
+ type: IsReturnless extends true ? TOutputs['MutationReturn'] extends GraphQLObjectType ? TOutputs['MutationReturn'] : never : GraphQLNonNull<GraphQLList<GraphQLNonNull<TOutputs[Capitalize<TName>]>>>;
603
879
  args: {
604
880
  values: {
605
- type: GraphQLNonNull<GraphQLList<GraphQLNonNull<TInputs[`${Capitalize<TName>}InsertInput`]>>>;
881
+ type: GraphQLNonNull<GraphQLList<GraphQLNonNull<TInputs[`Create${Capitalize<TName>}Input`]>>>;
606
882
  };
607
883
  };
608
884
  resolve: InsertArrResolver<TSchemaTables[TName], IsReturnless>;
609
885
  } : never;
610
886
  } & {
611
887
  [TName in keyof TSchemaTables as TName extends string ? `create${Capitalize<TName>}Single` : never]: TName extends string ? {
612
- type: IsReturnless extends true ? TOutputs['MutationReturn'] extends GraphQLObjectType ? TOutputs['MutationReturn'] : never : TOutputs[`${Capitalize<TName>}Item`];
888
+ type: IsReturnless extends true ? TOutputs['MutationReturn'] extends GraphQLObjectType ? TOutputs['MutationReturn'] : never : TOutputs[Capitalize<TName>];
613
889
  args: {
614
890
  values: {
615
- type: GraphQLNonNull<TInputs[`${Capitalize<TName>}InsertInput`]>;
891
+ type: GraphQLNonNull<TInputs[`Create${Capitalize<TName>}Input`]>;
616
892
  };
617
893
  };
618
894
  resolve: InsertResolver<TSchemaTables[TName], IsReturnless>;
619
895
  } : never;
620
896
  } & {
621
897
  [TName in keyof TSchemaTables as TName extends string ? `upsert${Capitalize<TName>}` : never]?: TName extends string ? {
622
- type: IsReturnless extends true ? TOutputs['MutationReturn'] extends GraphQLObjectType ? TOutputs['MutationReturn'] : never : GraphQLNonNull<GraphQLList<GraphQLNonNull<TOutputs[`${Capitalize<TName>}Item`]>>>;
898
+ type: IsReturnless extends true ? TOutputs['MutationReturn'] extends GraphQLObjectType ? TOutputs['MutationReturn'] : never : GraphQLNonNull<GraphQLList<GraphQLNonNull<TOutputs[Capitalize<TName>]>>>;
623
899
  args: {
624
900
  values: {
625
- type: GraphQLNonNull<GraphQLList<GraphQLNonNull<TInputs[`${Capitalize<TName>}InsertInput`]>>>;
901
+ type: GraphQLNonNull<GraphQLList<GraphQLNonNull<TInputs[`Create${Capitalize<TName>}Input`]>>>;
626
902
  };
627
903
  onConflict: {
628
904
  type: GraphQLInputObjectType;
@@ -632,10 +908,10 @@ type MutationsCore<TSchemaTables extends Record<string, Table>, TInputs extends
632
908
  } : never;
633
909
  } & {
634
910
  [TName in keyof TSchemaTables as TName extends string ? `upsert${Capitalize<TName>}Single` : never]?: TName extends string ? {
635
- type: IsReturnless extends true ? TOutputs['MutationReturn'] extends GraphQLObjectType ? TOutputs['MutationReturn'] : never : TOutputs[`${Capitalize<TName>}Item`];
911
+ type: IsReturnless extends true ? TOutputs['MutationReturn'] extends GraphQLObjectType ? TOutputs['MutationReturn'] : never : TOutputs[Capitalize<TName>];
636
912
  args: {
637
913
  values: {
638
- type: GraphQLNonNull<TInputs[`${Capitalize<TName>}InsertInput`]>;
914
+ type: GraphQLNonNull<TInputs[`Create${Capitalize<TName>}Input`]>;
639
915
  };
640
916
  onConflict: {
641
917
  type: GraphQLInputObjectType;
@@ -645,10 +921,10 @@ type MutationsCore<TSchemaTables extends Record<string, Table>, TInputs extends
645
921
  } : never;
646
922
  } & {
647
923
  [TName in keyof TSchemaTables as TName extends string ? `update${Capitalize<TName>}` : never]: TName extends string ? {
648
- type: IsReturnless extends true ? TOutputs['MutationReturn'] extends GraphQLObjectType ? TOutputs['MutationReturn'] : never : GraphQLNonNull<GraphQLList<GraphQLNonNull<TOutputs[`${Capitalize<TName>}Item`]>>>;
924
+ type: IsReturnless extends true ? TOutputs['MutationReturn'] extends GraphQLObjectType ? TOutputs['MutationReturn'] : never : GraphQLNonNull<GraphQLList<GraphQLNonNull<TOutputs[Capitalize<TName>]>>>;
649
925
  args: {
650
926
  set: {
651
- type: GraphQLNonNull<TInputs[`${Capitalize<TName>}UpdateInput`]>;
927
+ type: GraphQLNonNull<TInputs[`Update${Capitalize<TName>}Input`]>;
652
928
  };
653
929
  where: {
654
930
  type: TInputs[`${Capitalize<TName>}Filters`] extends GraphQLInputObjectType ? TInputs[`${Capitalize<TName>}Filters`] : never;
@@ -658,20 +934,20 @@ type MutationsCore<TSchemaTables extends Record<string, Table>, TInputs extends
658
934
  } : never;
659
935
  } & {
660
936
  [TName in keyof TSchemaTables as TName extends string ? `update${Capitalize<TName>}Many` : never]: TName extends string ? {
661
- type: IsReturnless extends true ? TOutputs['MutationReturn'] extends GraphQLObjectType ? TOutputs['MutationReturn'] : never : GraphQLNonNull<GraphQLList<TOutputs[`${Capitalize<TName>}Item`]>>;
937
+ type: IsReturnless extends true ? TOutputs['MutationReturn'] extends GraphQLObjectType ? TOutputs['MutationReturn'] : never : GraphQLNonNull<GraphQLList<TOutputs[Capitalize<TName>]>>;
662
938
  args: {
663
939
  updates: {
664
- type: GraphQLNonNull<GraphQLList<GraphQLNonNull<TInputs[`${Capitalize<TName>}UpdateManyInput`]>>>;
940
+ type: GraphQLNonNull<GraphQLList<GraphQLNonNull<TInputs[`Update${Capitalize<TName>}ManyInput`]>>>;
665
941
  };
666
942
  };
667
943
  resolve: UpdateManyResolver<TSchemaTables[TName], IsReturnless>;
668
944
  } : never;
669
945
  } & {
670
946
  [TName in keyof TSchemaTables as TName extends string ? `update${Capitalize<TName>}Single` : never]: TName extends string ? {
671
- type: IsReturnless extends true ? TOutputs['MutationReturn'] extends GraphQLObjectType ? TOutputs['MutationReturn'] : never : TOutputs[`${Capitalize<TName>}Item`];
947
+ type: IsReturnless extends true ? TOutputs['MutationReturn'] extends GraphQLObjectType ? TOutputs['MutationReturn'] : never : TOutputs[Capitalize<TName>];
672
948
  args: {
673
949
  set: {
674
- type: GraphQLNonNull<TInputs[`${Capitalize<TName>}UpdateInput`]>;
950
+ type: GraphQLNonNull<TInputs[`Update${Capitalize<TName>}Input`]>;
675
951
  };
676
952
  where: {
677
953
  type: GraphQLNonNull<TInputs[`${Capitalize<TName>}Filters`] extends GraphQLInputObjectType ? TInputs[`${Capitalize<TName>}Filters`] : never>;
@@ -681,7 +957,7 @@ type MutationsCore<TSchemaTables extends Record<string, Table>, TInputs extends
681
957
  } : never;
682
958
  } & {
683
959
  [TName in keyof TSchemaTables as TName extends string ? `delete${Capitalize<TName>}` : never]: TName extends string ? {
684
- type: IsReturnless extends true ? TOutputs['MutationReturn'] extends GraphQLObjectType ? TOutputs['MutationReturn'] : never : GraphQLNonNull<GraphQLList<GraphQLNonNull<TOutputs[`${Capitalize<TName>}Item`]>>>;
960
+ type: IsReturnless extends true ? TOutputs['MutationReturn'] extends GraphQLObjectType ? TOutputs['MutationReturn'] : never : GraphQLNonNull<GraphQLList<GraphQLNonNull<TOutputs[Capitalize<TName>]>>>;
685
961
  args: {
686
962
  where: {
687
963
  type: TInputs[`${Capitalize<TName>}Filters`] extends GraphQLInputObjectType ? TInputs[`${Capitalize<TName>}Filters`] : never;
@@ -691,7 +967,7 @@ type MutationsCore<TSchemaTables extends Record<string, Table>, TInputs extends
691
967
  } : never;
692
968
  } & {
693
969
  [TName in keyof TSchemaTables as TName extends string ? `delete${Capitalize<TName>}Single` : never]: TName extends string ? {
694
- type: IsReturnless extends true ? TOutputs['MutationReturn'] extends GraphQLObjectType ? TOutputs['MutationReturn'] : never : TOutputs[`${Capitalize<TName>}Item`];
970
+ type: IsReturnless extends true ? TOutputs['MutationReturn'] extends GraphQLObjectType ? TOutputs['MutationReturn'] : never : TOutputs[Capitalize<TName>];
695
971
  args: {
696
972
  where: {
697
973
  type: GraphQLNonNull<TInputs[`${Capitalize<TName>}Filters`] extends GraphQLInputObjectType ? TInputs[`${Capitalize<TName>}Filters`] : never>;
@@ -700,25 +976,42 @@ type MutationsCore<TSchemaTables extends Record<string, Table>, TInputs extends
700
976
  resolve: DeleteSingleResolver<TSchemaTables[TName], IsReturnless>;
701
977
  } : never;
702
978
  };
979
+ /**
980
+ * Keys of `entities.inputs`, which is keyed by generated GraphQL input type name.
981
+ *
982
+ * The write inputs are named from the configured `prefixes`, so these keys describe a build
983
+ * that uses the defaults (`insert: 'create'`, `update: 'update'`); a build that overrides a
984
+ * prefix produces correspondingly different keys. Inputs that only exist when a feature is
985
+ * enabled — `${Table}OnConflict` for upsert, `${Table}Having` for group-by — are not listed,
986
+ * since whether they were generated is a runtime decision.
987
+ */
703
988
  type GeneratedInputs<TSchema extends Record<string, Table>> = {
704
- [TName in keyof TSchema as TName extends string ? `${Capitalize<TName>}InsertInput` : never]: GraphQLInputObjectType;
989
+ [TName in keyof TSchema as TName extends string ? `Create${Capitalize<TName>}Input` : never]: GraphQLInputObjectType;
705
990
  } & {
706
- [TName in keyof TSchema as TName extends string ? `${Capitalize<TName>}UpdateInput` : never]: GraphQLInputObjectType;
991
+ [TName in keyof TSchema as TName extends string ? `Update${Capitalize<TName>}Input` : never]: GraphQLInputObjectType;
707
992
  } & {
708
- [TName in keyof TSchema as TName extends string ? `${Capitalize<TName>}UpdateManyInput` : never]: GraphQLInputObjectType;
993
+ [TName in keyof TSchema as TName extends string ? `Update${Capitalize<TName>}ManyInput` : never]: GraphQLInputObjectType;
709
994
  } & {
710
995
  [TName in keyof TSchema as TName extends string ? `${Capitalize<TName>}OrderBy` : never]: GraphQLInputObjectType;
711
996
  } & {
712
997
  [TName in keyof TSchema as TName extends string ? `${Capitalize<TName>}Filters` : never]: GraphQLInputObjectType;
713
998
  };
999
+ /**
1000
+ * Keys of `entities.types`, which is keyed by generated GraphQL type name.
1001
+ *
1002
+ * This build emits ONE object type per table (`Users`) used for both selects and mutation
1003
+ * returns — upstream drizzle-graphql emitted two (`UsersSelectItem` / `UsersItem`), and this
1004
+ * type kept those names long after the generator stopped producing them. MySQL still has a
1005
+ * shared `MutationReturn` instead of per-table return types, because its writes return no rows.
1006
+ */
714
1007
  type GeneratedOutputs<TSchema extends Record<string, Table>, IsReturnless extends boolean> = {
715
- [TName in keyof TSchema as TName extends string ? `${Capitalize<TName>}SelectItem` : never]: GraphQLObjectType;
1008
+ [TName in keyof TSchema as TName extends string ? Capitalize<TName> : never]: GraphQLObjectType;
716
1009
  } & {
717
1010
  [TName in keyof TSchema as TName extends string ? `${Capitalize<TName>}Aggregate` : never]: GraphQLObjectType;
718
1011
  } & (IsReturnless extends true ? {
719
1012
  MutationReturn: GraphQLObjectType;
720
1013
  } : {
721
- [TName in keyof TSchema as TName extends string ? `${Capitalize<TName>}Item` : never]: GraphQLObjectType;
1014
+ [TName in keyof TSchema as TName extends string ? Capitalize<TName> : never]: GraphQLObjectType;
722
1015
  });
723
1016
  type GeneratedEntities<TDatabase extends AnyDrizzleDB<TSchema>, TSchema extends Record<string, unknown> = TDatabase extends AnyDrizzleDB<infer ISchema> ? ISchema : never, TSchemaTables extends ExtractTables<TSchema> = ExtractTables<TSchema>, TSchemaRelations extends ExtractRelations<TSchema> = ExtractRelations<TSchema>, TInputs extends GeneratedInputs<TSchemaTables> = GeneratedInputs<TSchemaTables>, TOutputs extends GeneratedOutputs<TSchemaTables, TDatabase extends MySqlDatabase<any, any, any, any> ? true : false> = GeneratedOutputs<TSchemaTables, TDatabase extends MySqlDatabase<any, any, any, any> ? true : false>> = {
724
1017
  queries: QueriesCore<TSchemaTables, TSchemaRelations, TInputs, TOutputs>;
@@ -750,7 +1043,8 @@ type GeneratedData<TDatabase extends AnyDrizzleDB<any>> = {
750
1043
  type FeatureSwitch = boolean | ((tableName: string) => boolean);
751
1044
  /**
752
1045
  * Per-feature switches for what `buildSchema` generates. Every flag defaults to `true` except
753
- * `upsert`, `nestedWrites`, `fieldUpdateOperations`, `countMutations` and `requireWhere`.
1046
+ * `upsert`, `nestedWrites`, `fieldUpdateOperations`, `countMutations`, `requireWhere` and
1047
+ * `uniqueKeyFilters`.
754
1048
  * Each one takes a boolean or a per-table predicate, apart from `nestedWrites`, which is
755
1049
  * build-wide.
756
1050
  * See {@link BuildSchemaConfig.features}.
@@ -885,6 +1179,29 @@ type SchemaFeatures = {
885
1179
  * @default false
886
1180
  */
887
1181
  requireWhere?: FeatureSwitch;
1182
+ /**
1183
+ * One `where` field per compound unique constraint, so a lookup by a natural key names the
1184
+ * key instead of restating its columns:
1185
+ *
1186
+ * ```graphql
1187
+ * stockSingle(where: { itemId_locationId: { itemId: "i1", locationId: "l1" } }) { quantity }
1188
+ * ```
1189
+ *
1190
+ * The field is named after its member columns and every member is non-null, so a half-
1191
+ * supplied key fails query validation rather than becoming a broader filter that returns
1192
+ * whichever row happens to come first. It compiles to the same equalities the columns would
1193
+ * have — the guarantee is in the input type.
1194
+ *
1195
+ * Single-column constraints get no field (`eq` on the column already says it), and a
1196
+ * constraint whose field name is taken by a column or a relation is skipped. The fields live
1197
+ * on `<Type>Filters`, so they are accepted wherever a filter is: the `Single` reads and
1198
+ * writes they are meant for, but also list queries, relation filters and `onConflict.where`.
1199
+ *
1200
+ * Off unless asked for: it adds an input type per compound constraint.
1201
+ *
1202
+ * @default false
1203
+ */
1204
+ uniqueKeyFilters?: FeatureSwitch;
888
1205
  };
889
1206
  /**
890
1207
  * Per-field cost hints for `graphql-query-complexity` (or any estimator that reads
@@ -995,6 +1312,33 @@ type SoftDeleteColumn = string | {
995
1312
  * for a NOT NULL boolean; required for any other NOT NULL column.
996
1313
  */
997
1314
  restoredValue?: any;
1315
+ /**
1316
+ * Which generated reads hide marked rows by default.
1317
+ *
1318
+ * - `'all'` (the default) — the table's own query fields and every relation field that
1319
+ * points at it. A *required* to-one relation is the one exception: hiding the row
1320
+ * there can only produce "Cannot return null for non-nullable field", never a usable
1321
+ * result, so it reads `INCLUDE` unless the request says otherwise.
1322
+ * - `'root'` — only the table's own query fields. Relation fields read marked rows,
1323
+ * which is what "retired, not erased" usually means: keep the row off the pickers and
1324
+ * the lists, keep rendering it on the historical rows that reference it.
1325
+ *
1326
+ * The `deleted` argument is generated either way, so either default can be overridden
1327
+ * per request.
1328
+ */
1329
+ scope?: 'root' | 'all';
1330
+ /**
1331
+ * Whether the table's delete mutations accept `hard: Boolean = false`, which issues a
1332
+ * real `DELETE` instead of writing the marker — emptying the trash, reclaiming a unique
1333
+ * key, clearing a table. Opt-in per table, and the argument is only generated when it is
1334
+ * on, so the schema itself says which tables can be purged.
1335
+ *
1336
+ * A hard delete reads at `INCLUDE`: it reaches already-marked rows, which is the case it
1337
+ * mostly exists for. A `scope` still confines it, exactly as it confines every other write.
1338
+ *
1339
+ * @default false
1340
+ */
1341
+ hardDelete?: boolean;
998
1342
  };
999
1343
 
1000
1344
  /**
@@ -1466,6 +1810,58 @@ type BuildSchemaConfig = {
1466
1810
  singular: string;
1467
1811
  plural: string;
1468
1812
  } | undefined);
1813
+ /**
1814
+ * Renames the types generated *around* a table — the filter, the write inputs, the
1815
+ * order-by, the aggregate family, the group-by types, the conflict input, the shared scalar
1816
+ * filters. `typeNameMapper` names a table's object type and its root fields; those derived
1817
+ * names follow from it by a fixed rule, and this is the hook that overrides the rule.
1818
+ *
1819
+ * Asked once per generated type, with the kind, the name the library would use, and the
1820
+ * table it belongs to where it belongs to one. Return `undefined` — or the same name — to
1821
+ * keep the default, so a mapper that answers for one kind leaves everything else alone:
1822
+ *
1823
+ * ```ts
1824
+ * buildSchema(db, {
1825
+ * derivedTypeNameMapper: ({ kind, table, operation, defaultName }) =>
1826
+ * kind === 'aggregate' && operation ? `${table}Aggregate${operation}` : undefined,
1827
+ * });
1828
+ * ```
1829
+ *
1830
+ * A returned name is used verbatim — {@link BuildSchemaConfig.typeNamePrefix} and
1831
+ * {@link BuildSchemaConfig.typeNameSuffix} do not wrap it, since the more specific of the
1832
+ * two knobs wins.
1833
+ *
1834
+ * The point of it is stitching: a schema merged with a second generator over the same
1835
+ * tables collides on the derived names constantly, because both derive them from the model
1836
+ * name by the same obvious rule. Renaming them here means the collision never exists, rather
1837
+ * than being papered over by a `RenameTypes` transform on the way out.
1838
+ */
1839
+ derivedTypeNameMapper?: DerivedTypeNameMapper;
1840
+ /**
1841
+ * Prepended to every generated type name — object types, filters, write inputs, the
1842
+ * aggregate family, the shared scalar filters, everything — that
1843
+ * {@link BuildSchemaConfig.derivedTypeNameMapper} did not answer for. The one-line way to
1844
+ * make a whole subschema collision-free before stitching it:
1845
+ *
1846
+ * ```ts
1847
+ * buildSchema(db, { typeNamePrefix: 'Db' });
1848
+ * // type DbUsers, input DbUsersFilters, input DbStringFilter, enum DbOrderNulls, …
1849
+ * ```
1850
+ *
1851
+ * Custom scalars, database enum types and `PgGeometryObject` keep their own names: their
1852
+ * definition does not vary between builds, so a gateway merges them rather than colliding
1853
+ * on them. Field and argument names are untouched too — only type names move, so a client's
1854
+ * queries are unaffected apart from the names it spells in variable declarations.
1855
+ *
1856
+ * Must be a valid GraphQL name start (`/^[_A-Za-z][_0-9A-Za-z]*$/`); anything else throws.
1857
+ */
1858
+ typeNamePrefix?: string;
1859
+ /**
1860
+ * Appended to every generated type name that {@link BuildSchemaConfig.derivedTypeNameMapper}
1861
+ * did not answer for. The counterpart of {@link BuildSchemaConfig.typeNamePrefix}; both may
1862
+ * be given at once.
1863
+ */
1864
+ typeNameSuffix?: string;
1469
1865
  /**
1470
1866
  * Controls whether a relation is eagerly pre-fetched via Drizzle's `with:` clause
1471
1867
  * when its parent is loaded through a generated query or mutation.
@@ -1682,110 +2078,6 @@ declare const singularizeMapper: (tableName: string) => {
1682
2078
  plural: string;
1683
2079
  };
1684
2080
 
1685
- /**
1686
- * The identity a generated field publishes about itself, under `extensions.drizzle`.
1687
- *
1688
- * Every field this library generates carries one, so a wrapper — `graphql-middleware`,
1689
- * envelop, `graphql-shield`, an audit log, a cache invalidator — can ask the schema what a
1690
- * field is instead of reverse-engineering its name. Names are configurable (`prefixes`,
1691
- * `suffixes`, `typeNameMapper`), so name parsing is ambiguous by construction; this is not.
1692
- *
1693
- * This shape is stable API: fields are only ever added to it, never removed or repurposed.
1694
- */
1695
- /** What family of field this is. */
1696
- type DrizzleFieldKind = 'query' | 'mutation' | 'relation' | 'aggregate' | 'type';
1697
- /**
1698
- * What the field does. Arity is carried separately by `single`, so `select` covers both
1699
- * the list query and its `…Single` sibling rather than being spelled twice.
1700
- */
1701
- type DrizzleFieldOperation = 'select' | 'insert' | 'update' | 'updateMany' | 'upsert' | 'delete' | 'restore' | 'aggregate' | 'groupBy' | 'relation' | 'relationAggregate';
1702
- /** The argument holding the rows a field is about, when it has one. */
1703
- type DrizzleTargetArg = 'where' | 'values' | 'updates';
1704
- /** `extensions.drizzle` on a generated field. */
1705
- type DrizzleFieldExtension = {
1706
- /** The Drizzle schema key of the table the field reads or writes. */
1707
- table: string;
1708
- kind: Exclude<DrizzleFieldKind, 'type'>;
1709
- operation: DrizzleFieldOperation;
1710
- /** Whether the field resolves to one row (or one aggregate) rather than a list. */
1711
- single: boolean;
1712
- /**
1713
- * Which argument names the rows: `where` for select/update/delete/aggregate, `values`
1714
- * for insert and upsert, `updates` for the batch update's per-entry `{ where, set }`.
1715
- * Absent when the field takes no such argument.
1716
- */
1717
- targetArg?: DrizzleTargetArg;
1718
- /**
1719
- * Primary-key property names of `table`, in schema order. Empty when the table declares
1720
- * no primary key. Carried here so {@link identifyRows} works from the field alone.
1721
- */
1722
- primaryKey: readonly string[];
1723
- /** For `kind: 'relation'` and `operation: 'relationAggregate'`: the relation's name. */
1724
- relation?: string;
1725
- /** For `kind: 'relation'` and `operation: 'relationAggregate'`: the table it hangs off. */
1726
- parentTable?: string;
1727
- };
1728
- /** `extensions.drizzle` on a generated object type. */
1729
- type DrizzleTypeExtension = {
1730
- /** The Drizzle schema key this type was generated from. */
1731
- table: string;
1732
- kind: 'type';
1733
- /** Primary-key property names of `table`, in schema order. Empty when there is none. */
1734
- primaryKey: readonly string[];
1735
- };
1736
- type DrizzleExtension = DrizzleFieldExtension | DrizzleTypeExtension;
1737
- /** Anything carrying GraphQL `extensions` — a field config, a field, or a named type. */
1738
- type WithExtensions = {
1739
- extensions?: {
1740
- drizzle?: unknown;
1741
- } | null;
1742
- } | null | undefined;
1743
- /**
1744
- * Reads the `drizzle` extension off a field or type, typed. Returns `undefined` for
1745
- * anything this library did not generate, which is the check a wrapper wants before it
1746
- * treats a field as one of ours.
1747
- */
1748
- declare const drizzleExtension: (target: WithExtensions) => DrizzleExtension | undefined;
1749
- /** Narrows to the field form — object types carry `kind: 'type'` and no operation. */
1750
- declare const isDrizzleFieldExtension: (value: DrizzleExtension | undefined) => value is DrizzleFieldExtension;
1751
- /** The rows a field's arguments name. */
1752
- type IdentifiedRows = {
1753
- /** The Drizzle schema key of the table being read or written. */
1754
- table: string;
1755
- /** Primary-key property names, in schema order. */
1756
- primaryKey: readonly string[];
1757
- /** One entry per identified row: the primary-key values, keyed by property name. */
1758
- rows: Record<string, unknown>[];
1759
- /**
1760
- * Whether `rows` is exactly the set the operation affects.
1761
- *
1762
- * `false` means the arguments do not pin the rows down — an insert whose key the
1763
- * database generates, a `where` that filters on something other than the primary key,
1764
- * a plural update with no filter at all, or a table with no primary key. `rows` is then
1765
- * a best-effort subset-free superset: every entry in it is a row the key values name,
1766
- * but other filters may narrow the real set further, and it may be empty.
1767
- */
1768
- complete: boolean;
1769
- };
1770
- /**
1771
- * The rows a generated field's arguments are about, as primary-key values.
1772
- *
1773
- * The row identity of a mutation lives in a different argument for every operation —
1774
- * `where` for update and delete, `values` for insert and upsert, a per-entry `where` inside
1775
- * `updates` for the batch update — and each of those has both a one-row and a many-row
1776
- * spelling. This reads whichever one applies:
1777
- *
1778
- * ```ts
1779
- * const field = info.parentType.getFields()[info.fieldName];
1780
- * const target = identifyRows(field, args);
1781
- * if (target?.complete) await invalidate(target.table, target.rows);
1782
- * ```
1783
- *
1784
- * Returns `undefined` for a field this library did not generate, or an object type. Check
1785
- * `complete` before treating `rows` as the affected set — see {@link IdentifiedRows}.
1786
- */
1787
- declare const identifyRows: (field: WithExtensions, args: Record<string, unknown> | undefined) => IdentifiedRows | undefined;
1788
-
1789
2081
  /**
1790
2082
  * A 64-bit integer. Always transported as a decimal string, in both directions, so no value
1791
2083
  * is ever silently rounded by JSON's double-precision numbers. `graphql-scalars`' own
@@ -1813,6 +2105,14 @@ type SelectionOptions = {
1813
2105
  * pass the same mapper here or nothing will match.
1814
2106
  */
1815
2107
  typeNameMapper?: TypeNameMapper;
2108
+ /**
2109
+ * The `derivedTypeNameMapper` / `typeNamePrefix` / `typeNameSuffix` the schema was built
2110
+ * with, when any were configured. Same reason as `typeNameMapper`: the translation matches
2111
+ * selections against the names the build actually published.
2112
+ */
2113
+ derivedTypeNameMapper?: DerivedTypeNameMapper;
2114
+ typeNamePrefix?: string;
2115
+ typeNameSuffix?: string;
1816
2116
  };
1817
2117
  /**
1818
2118
  * Translates a GraphQL selection set into the relational query builder's `with` tree — the
@@ -1872,4 +2172,4 @@ declare const resolveSelection: (info: GraphQLResolveInfo | ResolveTree, options
1872
2172
 
1873
2173
  declare const buildSchema: <TDbClient extends AnyDrizzleDB<any>>(db: TDbClient, config?: BuildSchemaConfig) => GeneratedData<TDbClient>;
1874
2174
 
1875
- export { type AggregateResolver, type AnyDrizzleDB, type BuildSchemaConfig, type ColumnDeprecator, type ColumnDescriber, type ColumnDocInfo, type ColumnTypeMapper, type ColumnTypeMapperInfo, type ComplexityConfig, type ContextValuesConfig, type DefaultOrderByEntry, type DefaultsConfig, type DeleteResolver, type DeleteSingleResolver, type DrizzleExtension, type DrizzleFieldExtension, type DrizzleFieldKind, type DrizzleFieldOperation, type DrizzleTargetArg, type DrizzleTypeExtension, type EnumNameInfo, type EnumNameMapper, type ExtractRelations, type ExtractTableByName, type ExtractTableRelations, type ExtractTables, type FeatureSwitch, type GeneratedData, type GeneratedEntities, type GeneratedInputs, type GeneratedOutputs, GraphQLBigIntString, GraphQLDecimalString, type IdentifiedRows, type InsertArrResolver, type InsertResolver, type LimitsConfig, type MutationReturnlessResult, type MutationsCore, type OnWriteConfig, type QueriesCore, type RelationDescriber, type RelationResolverFactory, type ResolveSelectionOptions, type RowScope, type RowScopeFilter, type ScalarOverride, type ScalarOverridesConfig, type SchemaDocs, type SchemaExclusions, type SchemaFeatures, type ScopeConfig, type SelectResolver, type SelectSingleResolver, type SelectionOptions, type SoftDeleteColumn, type SoftDeleteConfig, type TableDefaults, type TableDescriber, type TableLimitPolicy, type TableNamedRelations, type UpdateManyArgs, type UpdateManyEntry, type UpdateManyResolver, type UpdateResolver, type UpdateSingleResolver, type UpsertArgs, type UpsertArrResolver, type UpsertConflictArgs, type UpsertResolver, type WriteHook, type WriteHookPayload, type WriteHookPositions, type WriteHooks, type WriteOperation, buildSchema, createRelationResolverFactory, defaultErrorMapper, drizzleExecutorKey, drizzleExtension, extractFilters, extractOrderBy, extractRelationJoinColumns, identifyRows, isDrizzleFieldExtension, resolveSelection, selectionToWith, singularizeMapper };
2175
+ export { type AggregateResolver, type AnyDrizzleDB, type BuildSchemaConfig, type ColumnDeprecator, type ColumnDescriber, type ColumnDocInfo, type ColumnTypeMapper, type ColumnTypeMapperInfo, type ComplexityConfig, type ContextValuesConfig, type DefaultOrderByEntry, type DefaultsConfig, type DeleteResolver, type DeleteSingleResolver, type DrizzleErrorCode, type DrizzleErrorContext, type DrizzleExtension, type DrizzleFieldExtension, type DrizzleFieldKind, type DrizzleFieldOperation, type DrizzleTargetArg, type DrizzleTypeExtension, type EnumNameInfo, type EnumNameMapper, type ExtractRelations, type ExtractTableByName, type ExtractTableRelations, type ExtractTables, type FeatureSwitch, type GeneratedData, type GeneratedEntities, type GeneratedInputs, type GeneratedOutputs, GraphQLBigIntString, GraphQLDecimalString, type IdentifiedRows, type InsertArrResolver, type InsertResolver, type LimitsConfig, type MutationReturnlessResult, type MutationsCore, type OnWriteConfig, type QueriesCore, type RelationDescriber, type RelationResolverFactory, type ResolveSelectionOptions, type RowScope, type RowScopeFilter, type ScalarOverride, type ScalarOverridesConfig, type SchemaDocs, type SchemaExclusions, type SchemaFeatures, type ScopeConfig, type SelectResolver, type SelectSingleResolver, type SelectionOptions, type SoftDeleteColumn, type SoftDeleteConfig, type TableDefaults, type TableDescriber, type TableLimitPolicy, type TableNamedRelations, type UpdateManyArgs, type UpdateManyEntry, type UpdateManyResolver, type UpdateResolver, type UpdateSingleResolver, type UpsertArgs, type UpsertArrResolver, type UpsertConflictArgs, type UpsertResolver, type WriteHook, type WriteHookPayload, type WriteHookPositions, type WriteHooks, type WriteOperation, buildSchema, createRelationResolverFactory, defaultErrorMapper, drizzleError, drizzleExecutorKey, drizzleExtension, extractFilters, extractOrderBy, extractRelationJoinColumns, identifyRows, isDrizzleFieldExtension, resolveSelection, selectionToWith, singularizeMapper };