@pothos/plugin-drizzle 0.18.1 → 0.19.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.
Files changed (165) hide show
  1. package/CHANGELOG.md +357 -0
  2. package/README.md +1199 -563
  3. package/dts/connection-helpers.d.ts +18 -8
  4. package/dts/connection-helpers.d.ts.map +1 -1
  5. package/dts/drizzle-field-builder.d.ts +4 -11
  6. package/dts/drizzle-field-builder.d.ts.map +1 -1
  7. package/dts/global-types.d.ts.map +1 -1
  8. package/dts/index.d.ts.map +1 -1
  9. package/dts/model-loader.d.ts +18 -17
  10. package/dts/model-loader.d.ts.map +1 -1
  11. package/dts/types.d.ts +82 -22
  12. package/dts/types.d.ts.map +1 -1
  13. package/dts/utils/adapter.d.ts +39 -0
  14. package/dts/utils/adapter.d.ts.map +1 -0
  15. package/dts/utils/await-selections.d.ts +12 -0
  16. package/dts/utils/await-selections.d.ts.map +1 -0
  17. package/dts/utils/config.d.ts +23 -3
  18. package/dts/utils/config.d.ts.map +1 -1
  19. package/dts/utils/cursors.d.ts +6 -6
  20. package/dts/utils/cursors.d.ts.map +1 -1
  21. package/dts/utils/map-query.d.ts +22 -21
  22. package/dts/utils/map-query.d.ts.map +1 -1
  23. package/dts/utils/path-info.d.ts +13 -0
  24. package/dts/utils/path-info.d.ts.map +1 -0
  25. package/dts/utils/relation-filter.d.ts +48 -0
  26. package/dts/utils/relation-filter.d.ts.map +1 -0
  27. package/dts/utils/selections.d.ts +2 -21
  28. package/dts/utils/selections.d.ts.map +1 -1
  29. package/esm/connection-helpers.d.ts +18 -8
  30. package/esm/connection-helpers.d.ts.map +1 -1
  31. package/esm/connection-helpers.js +52 -19
  32. package/esm/connection-helpers.js.map +1 -1
  33. package/esm/drizzle-field-builder.d.ts +4 -11
  34. package/esm/drizzle-field-builder.d.ts.map +1 -1
  35. package/esm/drizzle-field-builder.js +138 -96
  36. package/esm/drizzle-field-builder.js.map +1 -1
  37. package/esm/field-builder.js +61 -47
  38. package/esm/field-builder.js.map +1 -1
  39. package/esm/global-types.d.ts.map +1 -1
  40. package/esm/index.d.ts.map +1 -1
  41. package/esm/index.js +37 -61
  42. package/esm/index.js.map +1 -1
  43. package/esm/model-loader.d.ts +18 -17
  44. package/esm/model-loader.d.ts.map +1 -1
  45. package/esm/model-loader.js +76 -45
  46. package/esm/model-loader.js.map +1 -1
  47. package/esm/schema-builder.js +40 -149
  48. package/esm/schema-builder.js.map +1 -1
  49. package/esm/types.d.ts +82 -22
  50. package/esm/types.d.ts.map +1 -1
  51. package/esm/types.js.map +1 -1
  52. package/esm/utils/adapter.d.ts +39 -0
  53. package/esm/utils/adapter.d.ts.map +1 -0
  54. package/esm/utils/adapter.js +137 -0
  55. package/esm/utils/adapter.js.map +1 -0
  56. package/esm/utils/await-selections.d.ts +12 -0
  57. package/esm/utils/await-selections.d.ts.map +1 -0
  58. package/esm/utils/await-selections.js +19 -0
  59. package/esm/utils/await-selections.js.map +1 -0
  60. package/esm/utils/config.d.ts +23 -3
  61. package/esm/utils/config.d.ts.map +1 -1
  62. package/esm/utils/config.js +1 -4
  63. package/esm/utils/config.js.map +1 -1
  64. package/esm/utils/cursors.d.ts +6 -6
  65. package/esm/utils/cursors.d.ts.map +1 -1
  66. package/esm/utils/cursors.js +75 -92
  67. package/esm/utils/cursors.js.map +1 -1
  68. package/esm/utils/map-query.d.ts +22 -21
  69. package/esm/utils/map-query.d.ts.map +1 -1
  70. package/esm/utils/map-query.js +40 -354
  71. package/esm/utils/map-query.js.map +1 -1
  72. package/esm/utils/path-info.d.ts +13 -0
  73. package/esm/utils/path-info.d.ts.map +1 -0
  74. package/esm/utils/path-info.js +37 -0
  75. package/esm/utils/path-info.js.map +1 -0
  76. package/esm/utils/relation-filter.d.ts +48 -0
  77. package/esm/utils/relation-filter.d.ts.map +1 -0
  78. package/esm/utils/relation-filter.js +92 -0
  79. package/esm/utils/relation-filter.js.map +1 -0
  80. package/esm/utils/selections.d.ts +2 -21
  81. package/esm/utils/selections.d.ts.map +1 -1
  82. package/esm/utils/selections.js +1 -133
  83. package/esm/utils/selections.js.map +1 -1
  84. package/lib/connection-helpers.js +51 -18
  85. package/lib/connection-helpers.js.map +1 -1
  86. package/lib/drizzle-field-builder.js +136 -94
  87. package/lib/drizzle-field-builder.js.map +1 -1
  88. package/lib/field-builder.js +58 -44
  89. package/lib/field-builder.js.map +1 -1
  90. package/lib/index.js +36 -60
  91. package/lib/index.js.map +1 -1
  92. package/lib/model-loader.js +76 -45
  93. package/lib/model-loader.js.map +1 -1
  94. package/lib/schema-builder.js +40 -149
  95. package/lib/schema-builder.js.map +1 -1
  96. package/lib/types.js.map +1 -1
  97. package/lib/utils/adapter.js +147 -0
  98. package/lib/utils/adapter.js.map +1 -0
  99. package/lib/utils/await-selections.js +20 -0
  100. package/lib/utils/await-selections.js.map +1 -0
  101. package/lib/utils/config.js +1 -4
  102. package/lib/utils/config.js.map +1 -1
  103. package/lib/utils/cursors.js +67 -90
  104. package/lib/utils/cursors.js.map +1 -1
  105. package/lib/utils/map-query.js +28 -359
  106. package/lib/utils/map-query.js.map +1 -1
  107. package/lib/utils/path-info.js +39 -0
  108. package/lib/utils/path-info.js.map +1 -0
  109. package/lib/utils/relation-filter.js +91 -0
  110. package/lib/utils/relation-filter.js.map +1 -0
  111. package/lib/utils/selections.js +3 -158
  112. package/lib/utils/selections.js.map +1 -1
  113. package/package.json +10 -8
  114. package/src/connection-helpers.ts +168 -54
  115. package/src/drizzle-field-builder.ts +296 -176
  116. package/src/field-builder.ts +110 -77
  117. package/src/global-types.ts +0 -32
  118. package/src/index.ts +42 -68
  119. package/src/model-loader.ts +110 -71
  120. package/src/schema-builder.ts +76 -169
  121. package/src/types.ts +178 -46
  122. package/src/utils/adapter.ts +197 -0
  123. package/src/utils/await-selections.ts +31 -0
  124. package/src/utils/config.ts +1 -5
  125. package/src/utils/cursors.ts +84 -83
  126. package/src/utils/map-query.ts +51 -734
  127. package/src/utils/path-info.ts +40 -0
  128. package/src/utils/relation-filter.ts +185 -0
  129. package/src/utils/selections.ts +2 -207
  130. package/dts/utils/deep-equal.d.ts +0 -2
  131. package/dts/utils/deep-equal.d.ts.map +0 -1
  132. package/dts/utils/drizzle-graphql.d.ts +0 -2
  133. package/dts/utils/drizzle-graphql.d.ts.map +0 -1
  134. package/dts/utils/loader-map.d.ts +0 -11
  135. package/dts/utils/loader-map.d.ts.map +0 -1
  136. package/dts/utils/usage.d.ts +0 -5
  137. package/dts/utils/usage.d.ts.map +0 -1
  138. package/esm/utils/deep-equal.d.ts +0 -2
  139. package/esm/utils/deep-equal.d.ts.map +0 -1
  140. package/esm/utils/deep-equal.js +0 -45
  141. package/esm/utils/deep-equal.js.map +0 -1
  142. package/esm/utils/drizzle-graphql.d.ts +0 -2
  143. package/esm/utils/drizzle-graphql.d.ts.map +0 -1
  144. package/esm/utils/drizzle-graphql.js +0 -232
  145. package/esm/utils/drizzle-graphql.js.map +0 -1
  146. package/esm/utils/loader-map.d.ts +0 -11
  147. package/esm/utils/loader-map.d.ts.map +0 -1
  148. package/esm/utils/loader-map.js +0 -35
  149. package/esm/utils/loader-map.js.map +0 -1
  150. package/esm/utils/usage.d.ts +0 -5
  151. package/esm/utils/usage.d.ts.map +0 -1
  152. package/esm/utils/usage.js +0 -59
  153. package/esm/utils/usage.js.map +0 -1
  154. package/lib/utils/deep-equal.js +0 -55
  155. package/lib/utils/deep-equal.js.map +0 -1
  156. package/lib/utils/drizzle-graphql.js +0 -236
  157. package/lib/utils/drizzle-graphql.js.map +0 -1
  158. package/lib/utils/loader-map.js +0 -56
  159. package/lib/utils/loader-map.js.map +0 -1
  160. package/lib/utils/usage.js +0 -83
  161. package/lib/utils/usage.js.map +0 -1
  162. package/src/utils/deep-equal.ts +0 -57
  163. package/src/utils/drizzle-graphql.ts +0 -274
  164. package/src/utils/loader-map.ts +0 -49
  165. package/src/utils/usage.ts +0 -62
package/CHANGELOG.md CHANGED
@@ -1,5 +1,362 @@
1
1
  # @pothos/plugin-drizzle
2
2
 
3
+ ## 0.19.1
4
+
5
+ ### Patch Changes
6
+
7
+ - fc8e577: Fix excessive type instantiation when constructing a schema builder with many Drizzle tables, while retaining checks for required client methods.
8
+ - b1b147d: Preserve bigint and Date values in compound node IDs, which previously threw or came back as a string
9
+
10
+ Parse numeric node IDs with `Number` rather than `parseInt`, so a non-integer ID no longer truncates
11
+
12
+ - eb771cc: Pass the drizzle table, rather than its relational config, to connection helper `orderBy` callbacks
13
+ - @pothos/selection-mapper@0.1.0
14
+
15
+ ## 0.19.0
16
+
17
+ ### Minor Changes
18
+
19
+ - 0c7565a: Selections can be built by async callbacks. New `AsyncSelections` flag in the schema types, off by
20
+ default.
21
+ - `AsyncSelections: true` widens what the selection callbacks accept: a field's `select` function,
22
+ a relation `query` callback, a `relationCount` / `relatedCount` `where` callback, and the
23
+ `select` / `query` callbacks of `prismaConnectionHelpers` and `drizzleConnectionHelpers` may all
24
+ return promises. Without the flag they are typed as synchronous and an async callback is a type
25
+ error, which is what the prisma plugin needed: it accepted an async `select` and silently
26
+ dropped the selection it resolved to.
27
+ - The plugins still build one query. Callbacks start in the same tick, and what they return is
28
+ merged after every synchronous selection, in document order. Async argument mappers (such as the
29
+ validation plugin's) are awaited before the field's `select` runs. Synchronous selection planning
30
+ remains synchronous and does not create promises solely to support async callbacks.
31
+ - Inside an async `select`, `await` the result of `nestedSelection` before adding it to the
32
+ selection. A `select` that returns while a nested selection it started is still pending throws,
33
+ naming the field.
34
+ - Everything the plugins generate settles its plan before your resolver runs, so a schema built
35
+ only from `t.relation`, `t.relationCount` / `t.relatedCount`, `t.prismaField` /
36
+ `t.drizzleField`, `t.prismaConnection` / `t.drizzleConnection` and `t.relatedConnection` can
37
+ turn the flag on and change nothing else. The `query()` builder handed to a `drizzleField` or
38
+ `drizzleConnection` resolver never returns a promise for the same reason.
39
+ - The places where your own code asks for a query or a selection are the ones to audit:
40
+ `queryFromInfo` (prisma), a connection helper's `getQuery` (both plugins), `nestedSelection`,
41
+ and `mergeNestedSelection`. `queryFromInfo` and `getQuery` build the query synchronously and
42
+ take an `awaitSelections` option; pass `awaitSelections: true` to get a `MaybePromise` and
43
+ `await` it. A call that did not ask for a promise throws, naming the option and what the call
44
+ was building — `queryFromInfo` the field, `getQuery` the connection's model or table, which is
45
+ as much as it is given — rather than returning one where the declared type said there was none:
46
+ a promise spread into a prisma or drizzle call is one unusable key and no type error. Two things
47
+ make the query async. One is a selection beneath the field, which is a property of the document
48
+ rather than of the call site. The other is the helper's own `select` or `query` callback: an
49
+ async one makes `getQuery` throw for a fully synchronous document, since it is the helper's own
50
+ callback that has not settled. The return type follows the option's literal type, so the default
51
+ keeps the plain query; a non-literal `boolean` gets the `MaybePromise`, since the call cannot
52
+ rely on the synchronous type either.
53
+ - A selection passed to drizzle's `query()` comes first, as it does for `queryFromInfo`: a
54
+ relation the document also plans with other arguments loads on its own.
55
+
56
+ - 1358f71: Cursors and node ids round trip for the column types a key holds — numbers, strings, bigints,
57
+ dates, booleans, decimals and byte arrays — and compound cursors are encoded with per-part type tags so
58
+ every value in one keeps its type, including booleans in Prisma compound unique keys.
59
+
60
+ A compound cursor was a plain JSON array of the raw values in the prisma plugin. That throws
61
+ outright on a bigint, so a connection whose cursor is a `@@id` or `@@unique` containing a `BigInt`
62
+ column failed on every edge of every page; it wrote a `DateTime` out as an ISO string that came
63
+ back as a string, so the millisecond a row was created on was gone by the time the value reached
64
+ the query; and it wrote a `Bytes` column out as the JSON form of a buffer. Every part now carries
65
+ its own type tag. The drizzle plugin already tagged each part; what changes there is that a
66
+ compound cursor can hold bytes, a decimal and a JSON value there too.
67
+
68
+ A null cursor value now round trips in both plugins. The prisma plugin threw "Unsupported cursor
69
+ type object" while building the edge of a single value cursor, so a connection whose cursor names a
70
+ nullable column failed the whole page rather than only a request that paged from that row; `Json`
71
+ and `Bytes` values could not be written there either. The drizzle plugin wrote the string `null`,
72
+ which is not a value the cursor format can hold, so the cursor came back as "Invalid cursor" the
73
+ moment a client paged from that edge. Both now write a null as a tagged chunk and read it back as
74
+ `null`.
75
+
76
+ Cursors issued before this release still parse, in both plugins, to the same values they always
77
+ did. That legacy handling is deprecated and is removed in the next major. The `GPC:` and `DC:`
78
+ prefixes are unchanged, so only compound cursors are written differently; a single value cursor is
79
+ byte for byte what it was, apart from the null that neither plugin could write usefully. A single
80
+ value string cursor also no longer loses everything after the first newline it contains.
81
+
82
+ Other values that did not survive a round trip:
83
+ - A cursor value on a float, real or `Decimal` column, in both plugins. It was read back with
84
+ `parseInt`, which truncates `1.75` to `1` and reads a large number's `1e+21` as `1`, so the page
85
+ resumed from the wrong row.
86
+ - A prisma node id built from a `DateTime` field. It was serialized with `String(date)`, whose
87
+ format carries no milliseconds, so `findUnique` looked for a row a fraction of a second before
88
+ the real one and the node resolved as not found. Ids already handed out in the old format still
89
+ parse. A drizzle node id built from a date column had the mirror problem on the way back: `new
90
+ Date` was given the epoch milliseconds the serializer writes as a string, which is not a format
91
+ it parses, so every such id resolved as an Invalid Date.
92
+ - A prisma node id built from a `Bytes` field. The base64 case was spelled `Byte`, which is not
93
+ what the datamodel calls the type, so the value was serialized as `String(bytes)` (the bytes
94
+ separated by commas) and parsed back as that string.
95
+ - A drizzle node id built by `builder.drizzleNode` with an `id.column` that is not the table's
96
+ primary key. The id was parsed back into a record keyed by the database column name, while the
97
+ serializer wrote, and the model loader reads, the typescript name; the loaded rows were then
98
+ matched back to the ids they were loaded for by primary key, which such a record does not carry.
99
+ Such a node now loads.
100
+ - A prisma compound node id (a `@@id` or `@@unique` used as `id.field`). It passed the field's
101
+ `kind` (always `scalar`) where the type was wanted, so a `Json` or `Bytes` part was written as
102
+ `String(value)` and could not be read back. Each part is now serialized by its scalar type, as
103
+ the parser already read them.
104
+
105
+ Two paging fixes that go with them:
106
+ - A connection asked for `last: 0` pages backwards in both plugins, as any other `last` does. `0`
107
+ was read as "no `last` given", so the query took rows forwards and `pageInfo` came back as
108
+ though the request had been a forward page: `hasNextPage` true and `hasPreviousPage` false on a
109
+ non-empty connection.
110
+ - A prisma compound cursor carrying the wrong number of values is rejected with a validation error
111
+ naming both counts, rather than building a query with a missing key (or silently dropping an
112
+ extra one) for prisma to reject. The drizzle plugin already checked this.
113
+
114
+ The tagging itself lives in `@pothos/core` (`encodeCursorChunk`, `encodeCursorTuple` and
115
+ `decodeCursorChunk`, alongside `encodeBase64Bytes` / `decodeBase64Bytes`) rather than being written
116
+ out once per plugin.
117
+
118
+ - 1358f71: `t.relatedCount` and a `t.relatedConnection`'s `totalCount` now count the rows the connection pages
119
+ over. The parent-to-child SQL is built by drizzle's `relationToSQL` instead of being reassembled
120
+ from the relation's source and target columns, so the plugin and the relational query builder agree
121
+ on what "related" means.
122
+ - New builder option `drizzle.filterConnectionTotalCount` (default `true`): `totalCount` on a
123
+ `t.relatedConnection` applies the `where` returned by the field's `query`, so it counts the same
124
+ rows the connection paginates. Previously it reported the unfiltered count. Set the option to
125
+ `false` to keep counting every related row regardless of the filter, matching the prisma plugin's
126
+ option.
127
+ - A count over a many-to-many relation (one defined with `.through(...)`) counts related rows. The
128
+ junction table was left out, which compared the parent's key to the target's key instead: the
129
+ count came back as the number of target rows that happen to share an id with the parent, with no
130
+ error. The same correction applies to the filter `t.relatedField` hands to its `select` callback.
131
+ - A count honours a `where` declared on the relation itself, and the `where` a relation without
132
+ `from`/`to` inherits from the relation it reverses. Both were dropped, so the count included rows
133
+ the connection does not page over.
134
+ - The count reads the relation's source column off the parent by its typescript name, as every
135
+ other read of a row does. A table declaring `postId: integer('id')` carries it as `postId`, so
136
+ the count bound `undefined` and the driver rejected the query with "undefined cannot be passed as
137
+ argument to the database".
138
+ - `totalCount` and the query that pages the relation are now the same predicate: the count selects
139
+ from what the page query selects from and repeats its `where` clause, minus the limit, the
140
+ ordering and the keyset clauses.
141
+ - A count over a many-to-many relation joins the junction table by its index. `t.relatedCount`
142
+ counts a related row once however many junction rows reach it; a `t.relatedConnection`'s
143
+ `totalCount` counts rows the way the page query returns them, so the two report different
144
+ totals for a relation whose junction holds duplicate rows.
145
+ - A `drizzleConnection` declared `nullable: false` no longer queries rows for a document that
146
+ selects only `totalCount`. The check that recognises a count-only selection read the field's
147
+ return type without unwrapping it, so a non-null connection never matched and always loaded a
148
+ page of rows it then discarded. Nullable connections were already unaffected.
149
+
150
+ - 1358f71: When a field's data is missing from the parent row it falls back to the model loader, which
151
+ reloads the row. That fallback now loads the right row, and loads more of it.
152
+ - The loaded row also carries the parent type's type-level `select`/`include` (prisma) or
153
+ type-level columns, relations and extras (drizzle), since that row replaces the parent the
154
+ resolver sees. Fallback queries therefore load more than before: the parent type's own columns
155
+ and its type-level relations. The field's own selection always wins — a type-level relation
156
+ whose arguments conflict with it is left out of that query — and when a prisma type-level
157
+ `_count` has several counts and one conflicts with the count a field selects for itself, the
158
+ loader keeps the other counts instead of dropping `_count` altogether.
159
+ - Drizzle: `t.relation`, `t.relatedCount` and `t.relatedConnection` fall back to the loader when
160
+ their data is missing from the parent row — most often because the row was returned by a
161
+ resolver that fetched it itself. Previously the loader was reached only when the field had no
162
+ loader mapping at all, and a row without the relation on it resolved as `undefined` or as an
163
+ empty page. `t.relatedConnection` decides what it has by reading its selection at resolve time
164
+ the same way the planner does, through fragments, `@skip`/`@include`, and a wrapping type, so a
165
+ row that carries the relation rows but not the count reloads rather than reporting `totalCount`
166
+ as `undefined`.
167
+ - Prisma: a model load that failed before its query was sent hung the request instead of returning
168
+ an error. The loader batches the rows it reloads and sends one query per row on the next tick. A
169
+ throw while building any of those queries, most reachably `findUnique: null`, which throws
170
+ `Missing findUnique for <type>`, abandoned every row it had not reached yet, so those fields
171
+ never resolved and never errored and the request never finished. Every row in the batch now
172
+ rejects with that error, matching what `@pothos/plugin-drizzle` already did.
173
+ - Prisma: the per-row fallback plan is cached. When a relation or related connection has a
174
+ user-supplied `resolve` and the planned query did not load it, the field's resolver plans the
175
+ field for itself; that planning ran again for every row of the list its parent came from. It is
176
+ now cached per `Type@path` per request, the way `ModelLoader` already caches its own entry point
177
+ and under the same key: the plan is settled once and played per call, so each row still gets a
178
+ query of its own and still records the same mappings under the same keys. On a document with 161
179
+ such resolutions the planning cost falls from about 405µs to about 68µs per request, with the
180
+ queries issued and the response unchanged.
181
+
182
+ - 220a1b1: The selection API is now typed to match what it actually does.
183
+
184
+ Two changes can stop existing code from compiling. In the first case that code already failed at
185
+ runtime:
186
+ - `t.relationCount` (prisma) and `t.relatedCount` (drizzle) accept only list relations. Calling
187
+ either on a to-one relation built a count query prisma or drizzle rejected.
188
+ - `LoaderMappings` (prisma) is a deprecated alias of the shared `Mappings` type. The record it
189
+ names is internal to the plugin, and the plugin no longer builds the `{ field, type, mappings,
190
+ indirectPath }` entries the old declaration described, so code that reads those keys off it no
191
+ longer compiles. Nothing in the plugin's API hands one to a schema.
192
+
193
+ New and widened types:
194
+ - `queryFromInfo` (prisma) is typed by what was passed: `{ include }` for a given `include`, and
195
+ `{ select }` keeping a given select's own type, so the selection round trips. Spread the result
196
+ into a prisma call and read the selected columns and relations back off the rows, with types. A
197
+ call with neither now returns `{ select?, include? }`, whichever of the two the type's mode
198
+ produced, rather than a `select` key that a type in include mode does not return.
199
+ - `nestedSelection` returns the relation query for the field's model or table (prisma:
200
+ `PrismaRelationQuery<Model>`; drizzle: the table's `DBQueryConfig`, a `many` config for a list
201
+ field), keeping the keys it was given as given, so a `select`/`columns` in them still narrows
202
+ the parent shape. Its argument is typed by that query when the field's type names a model, so
203
+ literals such as `select: { title: true }` are kept, and it accepts a callback or a promise as
204
+ the runtime always did. It used to return its argument's type.
205
+ - One `PathSegment` type (`string | { name: string; type?: string }`) for `queryFromInfo` paths
206
+ and `nestedSelection` paths, exported from `@pothos/selection-mapper` and re-exported by both
207
+ plugins, together with `IndirectPathSegment` (its `{ name, type? }` form) and `IndirectInclude`,
208
+ whose `path`/`paths` still take only `IndirectPathSegment` objects. A `{ name, type }` segment
209
+ given to `nestedSelection` now pins the implementation the field is found under at runtime, as
210
+ it already did for `queryFromInfo`.
211
+ - The `query` a `t.relation` fallback `resolve` receives (prisma) — the relation's own arguments
212
+ (`where`, `orderBy`, `take`, ...) alongside the planned `select`/`include` — has a name:
213
+ `QueryFromRelation`.
214
+ - `t.relatedField` (drizzle) accepts the ordinary field options (`deprecationReason`,
215
+ `extensions`, `authScopes`, ...), and forwards every one of them to the field it builds, merging
216
+ `extensions` rather than dropping them; `t.relatedCount`, which is built on it, gets the same.
217
+ Its `resolve` may be async and receives the resolve `info`. Exported as
218
+ `RelatedSelectionFieldOptions`.
219
+ - A query whose `select` key is optional but names columns (`{ select?: { email: true } }`)
220
+ narrows the row to those columns, rather than to no columns at all as `ShapeFromSelection` did
221
+ before: the columns are on the row whether or not the `select` is applied, since a row loaded
222
+ without a `select` carries every column.
223
+
224
+ The docs for both plugins now cover how a field gets its data, and when it costs a query of its
225
+ own. Also newly documented: prisma `findUnique: null`, `onNull`, `nestedSelection`'s typed
226
+ segments and third argument, `skipDeferredFragments`,
227
+ `prismaFieldWithInput`/`drizzleFieldWithInput`, drizzle
228
+ `drizzleInterface`/`drizzleInterfaceField(s)`, `t.variant(Ref, { select })`, connection size
229
+ options, the drizzle fallback loader, and the error two variants raise when their selections
230
+ conflict.
231
+
232
+ - 220a1b1: The prisma and drizzle plugins now plan their queries with one shared selection planner, the new
233
+ package `@pothos/selection-mapper`. Each plugin keeps its own adapter for its query format; the
234
+ walk from a GraphQL selection to a query is the same code in both.
235
+
236
+ The plugins' public API is unchanged: only internal helpers moved (each plugin's `map-query`,
237
+ `selections`, `loader-map` and `deep-equal`), and the loader mapping keys (internal) now carry the
238
+ full field path. Behaviour that differed between the two plugins for no reason now agrees. In
239
+ particular, prisma walks fragments the way drizzle did: fields under a fragment on an interface the
240
+ type implements, including interfaces not backed by a model, are planned for the type instead of
241
+ falling back to a query per row.
242
+
243
+ The package is an implementation detail of the two plugins. Its exports exist for them and change
244
+ with them: it is not a supported public API, and its versions carry no compatibility promise of
245
+ their own.
246
+
247
+ - 1358f71: Entering another type of the same model through a fragment (a variant under a model interface or
248
+ union) now merges that type's type-level selection, so resolvers relying on it find their data in
249
+ the same query. This can change the shape of queries a schema with variants issues, and it adds one
250
+ validation error.
251
+ - A variant without a type-level `select` switches the query to include mode (prisma) or every
252
+ column (drizzle).
253
+ - If the two types' type-level selections disagree on a relation's arguments, a validation error
254
+ names both types and the relation; move the relation arguments to a field-level `select` on one
255
+ of the types. The error is raised whichever route brought the two types together: a fragment
256
+ entering a variant, or two indirect-include path matches landing on different types of the one
257
+ model. Relation arguments are compared structurally, so equal arguments written
258
+ separately are fine. In drizzle, two types defining the same `extras` key with different
259
+ functions are rejected the same way, naming the extra: an extra shared across variants must be
260
+ the same function reference.
261
+ - The type-level selections of every variant entered at a selection set are merged before the
262
+ fields there are planned, so the result no longer depends on document order: a field-level
263
+ `select` whose relation arguments conflict with a variant's type-level selection falls back to
264
+ its own query whichever comes first, and only two type-level selections can conflict.
265
+ - A walk on an object type is a walk on rows of that type: the field's own type, the `typeName`
266
+ given to `queryFromInfo`, the type pinned by `nestedSelection`, or a node load. A fragment on
267
+ another object type of the same model cannot apply to those rows, so it is not entered; it is
268
+ entered while walking an interface, where rows may resolve to it. Prisma previously entered it
269
+ whenever the field's declared type was abstract, planning a sibling variant's selection for
270
+ rows that could never be it.
271
+ - A fragment that does not apply to the type still lets a nested fragment narrow back to it.
272
+ Drizzle previously stopped at the outer fragment and discarded what was nested inside it.
273
+
274
+ ### Patch Changes
275
+
276
+ - 220a1b1: Drizzle plugin fixes.
277
+ - `t.relatedConnection` calls its `query` callback with the same `pathInfo` while resolving as
278
+ while planning, so a query that branches on `pathInfo` pages the rows it selected.
279
+ - `t.relatedConnection` no longer accepts a `resolve` option. It was silently discarded, so the
280
+ resolver never ran.
281
+ - `t.relation` evaluates a `query` callback once, with `pathInfo`. Previously it was called a
282
+ second time without `pathInfo`.
283
+ - `drizzleConnectionHelpers(builder, table)` can now be called without the options argument.
284
+ Previously it threw.
285
+ - `builder.drizzleInterfaceField(s)` given a table name resolves it to the interface registered for
286
+ that table, as the docs describe, instead of the object type.
287
+ - `pathInfo.path` handed to relation `query` callbacks now starts with the root field for queries
288
+ planned through `path`/`paths`, as it already did for queries planned directly for a field.
289
+ - `pathInfo.segments[].isList` is `true` for non-null list fields (`[Post!]!`). Previously only
290
+ nullable lists were reported as lists.
291
+ - The error for a `drizzleObject` and a `drizzleInterface` it implements being built on different
292
+ tables names drizzle rather than prisma. It read "must be based on the same prisma model as any
293
+ DrizzleInterfaces they extend"; it now says "the same drizzle table".
294
+ - Custom client adapters must now provide `select()` and its SQL builder chain, plus
295
+ `query.<table>.findMany()`, alongside relation metadata and `$count()`. Forward SQL construction
296
+ synchronously; do not wrap `select` in a promise or Effect. Full Drizzle clients already
297
+ provide these methods.
298
+ - Requires Drizzle `1.0.0-rc.2` or newer for the relation SQL API used by counts and predicates.
299
+ - Custom connection fields retain their rows when `totalCount` is selected.
300
+ - Connection helper `resolve()` returns a maybe-promise for async-enabled schemas; await it
301
+ before inspecting or spreading connection fields.
302
+ - Nested query callbacks passed to `nestedSelection` now receive `PathInfo` as their third
303
+ argument, after the field arguments and context.
304
+
305
+ - 1358f71: Fix how selections are found through fragments, paths and wrapper types, in both plugins. Each of
306
+ these left a field to load its own data per row, or planned the wrong selection for it.
307
+ - A path segment selected behind a fragment that narrows an interface or union
308
+ (`... on Impl { field }`), or a fragment on an interface that overlaps the list type, is now
309
+ found without needing to pin the type on the segment. Previously the field was skipped and the
310
+ plugin fell back to a separate query per row. Drizzle crashed on a fragment spread that narrows
311
+ an interface.
312
+ - When the same field is selected in several places (multiple fragments, several `paths`, or the
313
+ same response key under several fragments), every selection is merged into one query. Previously
314
+ `queryFromInfo` kept only the last one, and a field loaded through the fallback planned only its
315
+ first occurrence.
316
+ - A relation selected under both `nodes` and `edges { node }` of a connection is answered from the
317
+ loaded rows in both places. The loader mappings used to be keyed by field alias alone, so the
318
+ second path fell back to a query of its own.
319
+ - A `nestedSelection` path into a field selected through a fragment on the same type
320
+ (`... on Type { field }` or `...Fragment`) now merges the nested selection instead of producing
321
+ an empty selection.
322
+ - Fragment spreads and inline fragments left out by `@skip` / `@include` are no longer planned, so
323
+ they neither enter a variant nor add fields to the query.
324
+ - `queryFromInfo`'s `path` and `paths` accept `{ name, type }` segments alongside strings, in the
325
+ types and the docs as well as at runtime. A segment `type` pins the fragment type condition the
326
+ field must be found under, which is useful when several implementations share a field name. The
327
+ pin is applied wherever the field is looked for, not only where a fragment was crossed to reach
328
+ it: a field of the same name selected directly on a level that is not the pinned type is no
329
+ longer taken as the segment's, so the match no longer carries that level's return type instead
330
+ of the pinned type's. A segment `type` that does not exist in the schema is a validation error,
331
+ rather than a `TypeError` (prisma) or a path that silently matches nothing (drizzle).
332
+ - When several implementations share a field name, matches whose field returns a different model or
333
+ table than the one the query is being built for are ignored, so their selections are never merged
334
+ into the wrong query. Variants of the target model are walked with their own fields.
335
+ - A type carrying `pothosIndirectInclude` with `paths` only contributes its own selection when it
336
+ is backed by the model being queried. A plain wrapper, or a wrapper backed by another model, no
337
+ longer merges its selection into the target model's query.
338
+ - `queryFromInfo` with `path`/`paths` that select nothing now returns the `select`/`include`
339
+ (prisma) or `select` (drizzle) it was given, or `{}`, instead of an empty `select` (which prisma
340
+ rejects) or a query that dropped the caller's selection.
341
+ - The selection lookup handed to a field's `select` callback (the fourth argument) now looks
342
+ through wrappers on the field's return type and returns the first match. A `relatedConnection`
343
+ with `totalCount: true` wrapped by `@pothos/plugin-errors` now selects the count, and its
344
+ totalCount-only handling agrees with what was planned.
345
+ - A field `select` callback returning `false`, `null` or `undefined` is treated as selecting
346
+ nothing: the field is not answered from the parent row and loads its own data when resolved.
347
+ `false` used to switch the query to include mode and still record the field as loaded, so it
348
+ resolved from a row that held nothing for it; `null` and `undefined` threw.
349
+ - Relation arguments compare the same way whichever of two selections was planned first. The
350
+ structural equality both plugins use decided whether a value was an array, or a boxed value such
351
+ as a `Date`, by looking only at its left-hand argument: `{}` and `[]` compared equal one way
352
+ round and unequal the other, and so did `{}` and a `Date`. That helper answers whether two
353
+ selections of one relation ask for the same arguments, so an answer that depended on argument
354
+ order could let an incompatible selection merge and replace the arguments already planned, or
355
+ refuse a compatible one and issue a separate query for the field. Both guards now read both
356
+ sides. `orderBy` is the argument where this is easiest to hit, since prisma accepts an object and
357
+ an array there for the same meaning.
358
+ - @pothos/selection-mapper@0.1.0
359
+
3
360
  ## 0.18.1
4
361
 
5
362
  ### Patch Changes