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