@pothos/plugin-drizzle 0.18.0 → 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 (167) hide show
  1. package/CHANGELOG.md +351 -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 +81 -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 +81 -20
  50. package/esm/types.d.ts.map +1 -1
  51. package/esm/types.js +1 -0
  52. package/esm/types.js.map +1 -1
  53. package/esm/utils/adapter.d.ts +39 -0
  54. package/esm/utils/adapter.d.ts.map +1 -0
  55. package/esm/utils/adapter.js +137 -0
  56. package/esm/utils/adapter.js.map +1 -0
  57. package/esm/utils/await-selections.d.ts +12 -0
  58. package/esm/utils/await-selections.d.ts.map +1 -0
  59. package/esm/utils/await-selections.js +19 -0
  60. package/esm/utils/await-selections.js.map +1 -0
  61. package/esm/utils/config.d.ts +23 -3
  62. package/esm/utils/config.d.ts.map +1 -1
  63. package/esm/utils/config.js +1 -4
  64. package/esm/utils/config.js.map +1 -1
  65. package/esm/utils/cursors.d.ts +6 -6
  66. package/esm/utils/cursors.d.ts.map +1 -1
  67. package/esm/utils/cursors.js +55 -90
  68. package/esm/utils/cursors.js.map +1 -1
  69. package/esm/utils/map-query.d.ts +22 -21
  70. package/esm/utils/map-query.d.ts.map +1 -1
  71. package/esm/utils/map-query.js +40 -336
  72. package/esm/utils/map-query.js.map +1 -1
  73. package/esm/utils/path-info.d.ts +13 -0
  74. package/esm/utils/path-info.d.ts.map +1 -0
  75. package/esm/utils/path-info.js +37 -0
  76. package/esm/utils/path-info.js.map +1 -0
  77. package/esm/utils/relation-filter.d.ts +48 -0
  78. package/esm/utils/relation-filter.d.ts.map +1 -0
  79. package/esm/utils/relation-filter.js +92 -0
  80. package/esm/utils/relation-filter.js.map +1 -0
  81. package/esm/utils/selections.d.ts +2 -21
  82. package/esm/utils/selections.d.ts.map +1 -1
  83. package/esm/utils/selections.js +1 -133
  84. package/esm/utils/selections.js.map +1 -1
  85. package/lib/connection-helpers.js +51 -18
  86. package/lib/connection-helpers.js.map +1 -1
  87. package/lib/drizzle-field-builder.js +136 -94
  88. package/lib/drizzle-field-builder.js.map +1 -1
  89. package/lib/field-builder.js +58 -44
  90. package/lib/field-builder.js.map +1 -1
  91. package/lib/index.js +36 -60
  92. package/lib/index.js.map +1 -1
  93. package/lib/model-loader.js +76 -45
  94. package/lib/model-loader.js.map +1 -1
  95. package/lib/schema-builder.js +40 -149
  96. package/lib/schema-builder.js.map +1 -1
  97. package/lib/types.js +4 -0
  98. package/lib/types.js.map +1 -1
  99. package/lib/utils/adapter.js +147 -0
  100. package/lib/utils/adapter.js.map +1 -0
  101. package/lib/utils/await-selections.js +20 -0
  102. package/lib/utils/await-selections.js.map +1 -0
  103. package/lib/utils/config.js +1 -4
  104. package/lib/utils/config.js.map +1 -1
  105. package/lib/utils/cursors.js +47 -88
  106. package/lib/utils/cursors.js.map +1 -1
  107. package/lib/utils/map-query.js +28 -341
  108. package/lib/utils/map-query.js.map +1 -1
  109. package/lib/utils/path-info.js +39 -0
  110. package/lib/utils/path-info.js.map +1 -0
  111. package/lib/utils/relation-filter.js +91 -0
  112. package/lib/utils/relation-filter.js.map +1 -0
  113. package/lib/utils/selections.js +3 -158
  114. package/lib/utils/selections.js.map +1 -1
  115. package/package.json +8 -6
  116. package/src/connection-helpers.ts +167 -53
  117. package/src/drizzle-field-builder.ts +296 -176
  118. package/src/field-builder.ts +110 -77
  119. package/src/global-types.ts +0 -32
  120. package/src/index.ts +42 -68
  121. package/src/model-loader.ts +110 -71
  122. package/src/schema-builder.ts +76 -169
  123. package/src/types.ts +176 -44
  124. package/src/utils/adapter.ts +197 -0
  125. package/src/utils/await-selections.ts +31 -0
  126. package/src/utils/config.ts +1 -5
  127. package/src/utils/cursors.ts +53 -81
  128. package/src/utils/map-query.ts +51 -708
  129. package/src/utils/path-info.ts +40 -0
  130. package/src/utils/relation-filter.ts +185 -0
  131. package/src/utils/selections.ts +2 -207
  132. package/dts/utils/deep-equal.d.ts +0 -2
  133. package/dts/utils/deep-equal.d.ts.map +0 -1
  134. package/dts/utils/drizzle-graphql.d.ts +0 -2
  135. package/dts/utils/drizzle-graphql.d.ts.map +0 -1
  136. package/dts/utils/loader-map.d.ts +0 -11
  137. package/dts/utils/loader-map.d.ts.map +0 -1
  138. package/dts/utils/usage.d.ts +0 -5
  139. package/dts/utils/usage.d.ts.map +0 -1
  140. package/esm/utils/deep-equal.d.ts +0 -2
  141. package/esm/utils/deep-equal.d.ts.map +0 -1
  142. package/esm/utils/deep-equal.js +0 -45
  143. package/esm/utils/deep-equal.js.map +0 -1
  144. package/esm/utils/drizzle-graphql.d.ts +0 -2
  145. package/esm/utils/drizzle-graphql.d.ts.map +0 -1
  146. package/esm/utils/drizzle-graphql.js +0 -232
  147. package/esm/utils/drizzle-graphql.js.map +0 -1
  148. package/esm/utils/loader-map.d.ts +0 -11
  149. package/esm/utils/loader-map.d.ts.map +0 -1
  150. package/esm/utils/loader-map.js +0 -35
  151. package/esm/utils/loader-map.js.map +0 -1
  152. package/esm/utils/usage.d.ts +0 -5
  153. package/esm/utils/usage.d.ts.map +0 -1
  154. package/esm/utils/usage.js +0 -59
  155. package/esm/utils/usage.js.map +0 -1
  156. package/lib/utils/deep-equal.js +0 -55
  157. package/lib/utils/deep-equal.js.map +0 -1
  158. package/lib/utils/drizzle-graphql.js +0 -236
  159. package/lib/utils/drizzle-graphql.js.map +0 -1
  160. package/lib/utils/loader-map.js +0 -56
  161. package/lib/utils/loader-map.js.map +0 -1
  162. package/lib/utils/usage.js +0 -83
  163. package/lib/utils/usage.js.map +0 -1
  164. package/src/utils/deep-equal.ts +0 -57
  165. package/src/utils/drizzle-graphql.ts +0 -274
  166. package/src/utils/loader-map.ts +0 -49
  167. package/src/utils/usage.ts +0 -62
@@ -1,478 +1,9 @@
1
- import { getMappedArgumentValues, PothosValidationError } from '@pothos/core';
2
- import type { DBQueryConfig, TableRelationalConfig } from 'drizzle-orm';
3
- import {
4
- type FieldNode,
5
- type FragmentDefinitionNode,
6
- type FragmentSpreadNode,
7
- type GraphQLField,
8
- GraphQLIncludeDirective,
9
- type GraphQLInterfaceType,
10
- type GraphQLNamedType,
11
- type GraphQLObjectType,
12
- type GraphQLResolveInfo,
13
- GraphQLSkipDirective,
14
- getDirectiveValues,
15
- getNamedType,
16
- type InlineFragmentNode,
17
- isInterfaceType,
18
- isListType,
19
- isObjectType,
20
- Kind,
21
- type SelectionSetNode,
22
- } from 'graphql';
23
- import type { DrizzleFieldSelection, FieldPathInfo, PathInfo } from '../types.js';
1
+ import { isThenable } from '@pothos/core';
2
+ import { type PathSegment, Plan } from '@pothos/selection-mapper';
3
+ import type { GraphQLResolveInfo } from 'graphql';
4
+ import { type DrizzlePlan, drizzleAdapter } from './adapter.js';
24
5
  import type { PothosDrizzleSchemaConfig } from './config.js';
25
- import { type LoaderMappings, setLoaderMappings } from './loader-map.js';
26
- import {
27
- createState,
28
- mergeSelection,
29
- type SelectionMap,
30
- type SelectionState,
31
- selectionCompatible,
32
- selectionToQuery,
33
- } from './selections.js';
34
- import { wrapWithUsageCheck } from './usage.js';
35
-
36
- export interface IndirectInclude {
37
- getType: () => string;
38
- path?: { type?: string; name: string }[];
39
- paths?: { type?: string; name: string }[][];
40
- }
41
-
42
- function addTypeSelectionsForField(
43
- config: PothosDrizzleSchemaConfig,
44
- type: GraphQLNamedType,
45
- context: object,
46
- info: GraphQLResolveInfo,
47
- state: SelectionState,
48
- selection: FieldNode,
49
- indirectPath: string[],
50
- deferred?: boolean,
51
- segments: FieldPathInfo[] = [],
52
- ) {
53
- if (selection.name.value.startsWith('__')) {
54
- return;
55
- }
56
-
57
- const { pothosDrizzleSelect, pothosIndirectInclude } = (type.extensions ?? {}) as {
58
- pothosIndirectInclude?: IndirectInclude;
59
- pothosDrizzleSelect?: boolean | DBQueryConfig<'one'>;
60
- };
61
-
62
- if (
63
- (!!pothosIndirectInclude?.path && pothosIndirectInclude.path.length > 0) ||
64
- (!!pothosIndirectInclude?.paths && pothosIndirectInclude.paths.length === 0)
65
- ) {
66
- resolveIndirectIncludePaths(
67
- type,
68
- info,
69
- selection,
70
- [],
71
- pothosIndirectInclude.paths ?? [pothosIndirectInclude.path!],
72
- indirectPath,
73
- (resolvedType, field, path) => {
74
- addTypeSelectionsForField(
75
- config,
76
- resolvedType,
77
- context,
78
- info,
79
- state,
80
- field,
81
- path,
82
- deferred,
83
- segments,
84
- );
85
- },
86
- deferred,
87
- );
88
- } else if (pothosIndirectInclude) {
89
- addTypeSelectionsForField(
90
- config,
91
- info.schema.getType(pothosIndirectInclude.getType())!,
92
- context,
93
- info,
94
- state,
95
- selection,
96
- indirectPath,
97
- deferred,
98
- segments,
99
- );
100
- return;
101
- }
102
-
103
- if (!(isObjectType(type) || isInterfaceType(type))) {
104
- return;
105
- }
106
-
107
- if (pothosDrizzleSelect) {
108
- mergeSelection(config, state, pothosDrizzleSelect === true ? true : { ...pothosDrizzleSelect });
109
- }
110
-
111
- if (selection.selectionSet && (!deferred || !state.skipDeferredFragments)) {
112
- addNestedSelections(
113
- config,
114
- type,
115
- context,
116
- info,
117
- state,
118
- selection.selectionSet,
119
- indirectPath,
120
- segments,
121
- );
122
- }
123
- }
124
-
125
- function resolveIndirectIncludePaths(
126
- type: GraphQLNamedType,
127
- info: GraphQLResolveInfo,
128
- selection: FieldNode | FragmentDefinitionNode | InlineFragmentNode,
129
- pathPrefix: { type?: string; name: string }[],
130
- includePaths: { type?: string; name: string }[][],
131
- path: string[],
132
- resolve: (type: GraphQLNamedType, field: FieldNode, path: string[], deferred: boolean) => void,
133
- deferred?: boolean,
134
- ) {
135
- for (const includePath of includePaths) {
136
- if (pathPrefix.length > 0) {
137
- resolveIndirectInclude(
138
- type,
139
- info,
140
- selection,
141
- [...pathPrefix, ...includePath],
142
- path,
143
- resolve,
144
- deferred,
145
- );
146
- } else {
147
- resolveIndirectInclude(type, info, selection, includePath, path, resolve, deferred);
148
- }
149
- }
150
- }
151
-
152
- function resolveIndirectInclude(
153
- type: GraphQLNamedType,
154
- info: GraphQLResolveInfo,
155
- selection: FieldNode | FragmentDefinitionNode | InlineFragmentNode,
156
- includePath: { type?: string; name: string }[],
157
- path: string[],
158
- resolve: (type: GraphQLNamedType, field: FieldNode, path: string[], deferred: boolean) => void,
159
- deferred = false,
160
- ) {
161
- if (includePath.length === 0) {
162
- resolve(type, selection as FieldNode, path, deferred);
163
- return;
164
- }
165
-
166
- const [include, ...rest] = includePath;
167
- if (!selection.selectionSet || !include) {
168
- return;
169
- }
170
-
171
- for (const sel of selection.selectionSet.selections) {
172
- switch (sel.kind) {
173
- case Kind.FIELD:
174
- if (
175
- !fieldSkipped(info, sel) &&
176
- sel.name.value === include.name &&
177
- (isObjectType(type) || isInterfaceType(type))
178
- ) {
179
- const returnType = getNamedType(type.getFields()[sel.name.value].type);
180
-
181
- resolveIndirectInclude(
182
- returnType,
183
- info,
184
- sel,
185
- rest,
186
- [...path, sel.alias?.value ?? sel.name.value],
187
- resolve,
188
- deferred,
189
- );
190
- }
191
- continue;
192
- case Kind.FRAGMENT_SPREAD:
193
- if (
194
- !include.type ||
195
- info.fragments[sel.name.value].typeCondition.name.value === include.type
196
- ) {
197
- resolveIndirectInclude(
198
- include.type ? info.schema.getType(include.type)! : type,
199
- info,
200
- info.fragments[sel.name.value],
201
- includePath,
202
- path,
203
- resolve,
204
- deferred || isDeferredFragment(sel, info),
205
- );
206
- }
207
-
208
- continue;
209
-
210
- case Kind.INLINE_FRAGMENT:
211
- if (!sel.typeCondition || !include.type || sel.typeCondition.name.value === include.type) {
212
- resolveIndirectInclude(
213
- sel.typeCondition ? info.schema.getType(sel.typeCondition.name.value)! : type,
214
- info,
215
- sel,
216
- includePath,
217
- path,
218
- resolve,
219
- deferred || isDeferredFragment(sel, info),
220
- );
221
- }
222
-
223
- continue;
224
-
225
- default:
226
- throw new PothosValidationError(
227
- `Unsupported selection kind ${(selection as { kind: string }).kind}`,
228
- );
229
- }
230
- }
231
- }
232
-
233
- function addNestedSelections(
234
- config: PothosDrizzleSchemaConfig,
235
- type: GraphQLInterfaceType | GraphQLObjectType,
236
- context: object,
237
- info: GraphQLResolveInfo,
238
- state: SelectionState,
239
- selections: SelectionSetNode,
240
- indirectPath: string[],
241
- segments: FieldPathInfo[] = [],
242
- ) {
243
- let parentType = type;
244
- for (const selection of selections.selections) {
245
- switch (selection.kind) {
246
- case Kind.FIELD:
247
- addFieldSelection(config, type, context, info, state, selection, indirectPath, segments);
248
-
249
- continue;
250
- case Kind.FRAGMENT_SPREAD:
251
- if (state.skipDeferredFragments && isDeferredFragment(selection, info)) {
252
- continue;
253
- }
254
-
255
- parentType = info.schema.getType(
256
- info.fragments[selection.name.value].typeCondition.name.value,
257
- )! as GraphQLObjectType;
258
- if (
259
- isObjectType(type)
260
- ? parentType.name !== type.name
261
- : parentType.extensions?.pothosDrizzleModel !== type.extensions.pothosDrizzleModel
262
- ) {
263
- continue;
264
- }
265
-
266
- addNestedSelections(
267
- config,
268
- parentType,
269
- context,
270
- info,
271
- state,
272
- info.fragments[selection.name.value].selectionSet,
273
- indirectPath,
274
- segments,
275
- );
276
-
277
- continue;
278
-
279
- case Kind.INLINE_FRAGMENT:
280
- if (state.skipDeferredFragments && isDeferredFragment(selection, info)) {
281
- continue;
282
- }
283
-
284
- parentType = selection.typeCondition
285
- ? (info.schema.getType(selection.typeCondition.name.value) as GraphQLObjectType)
286
- : type;
287
- if (
288
- isObjectType(type)
289
- ? parentType.name !== type.name
290
- : parentType.extensions?.pothosDrizzleModel !== type.extensions.pothosDrizzleModel
291
- ) {
292
- continue;
293
- }
294
-
295
- addNestedSelections(
296
- config,
297
- parentType,
298
- context,
299
- info,
300
- state,
301
- selection.selectionSet,
302
- indirectPath,
303
- segments,
304
- );
305
-
306
- continue;
307
-
308
- default:
309
- throw new PothosValidationError(
310
- `Unsupported selection kind ${(selection as { kind: string }).kind}`,
311
- );
312
- }
313
- }
314
- }
315
-
316
- function addFieldSelection(
317
- config: PothosDrizzleSchemaConfig,
318
- type: GraphQLInterfaceType | GraphQLObjectType,
319
- context: object,
320
- info: GraphQLResolveInfo,
321
- state: SelectionState,
322
- selection: FieldNode,
323
- indirectPath: string[],
324
- segments: FieldPathInfo[] = [],
325
- ) {
326
- if (selection.name.value.startsWith('__') || fieldSkipped(info, selection)) {
327
- return;
328
- }
329
- const field = type.getFields()[selection.name.value];
330
- if (!field) {
331
- throw new PothosValidationError(`Unknown field ${selection.name.value} on ${type.name}`);
332
- }
333
-
334
- const allSegments = [
335
- ...segments,
336
- {
337
- field: selection.name.value,
338
- alias: selection.alias?.value ?? selection.name.value,
339
- parentType: type.name,
340
- isList: isListType(field.type) || isListType(getNamedType(field.type)),
341
- },
342
- ];
343
-
344
- const fieldSelect = field.extensions?.pothosDrizzleSelect as DrizzleFieldSelection | undefined;
345
-
346
- let fieldSelectionMap: DBQueryConfig<'one'> | undefined;
347
- let mappings: LoaderMappings = {};
348
- if (typeof fieldSelect === 'function') {
349
- const pathInfo: PathInfo = {
350
- path: allSegments.map((s) => `${s.parentType}.${s.field}`),
351
- segments: allSegments,
352
- };
353
-
354
- const args = getMappedArgumentValues(field, selection, context, info) as Record<
355
- string,
356
- unknown
357
- >;
358
- fieldSelectionMap = fieldSelect(
359
- args,
360
- context,
361
- (rawQuery, indirectInclude, expectedType) => {
362
- const returnType = getNamedType(field.type);
363
- const query = typeof rawQuery === 'function' ? rawQuery(args, context) : rawQuery;
364
- const normalizedIndirectInclude = Array.isArray(indirectInclude)
365
- ? normalizeInclude(
366
- indirectInclude,
367
- getIndirectType(returnType, info),
368
- expectedType ? getNamedType(info.schema.getType(expectedType)) : undefined,
369
- )
370
- : indirectInclude;
371
- const fieldState = createStateForSelection(
372
- config,
373
- info,
374
- normalizedIndirectInclude
375
- ? info.schema.getType(normalizedIndirectInclude.getType())!
376
- : returnType,
377
- state,
378
- );
379
-
380
- if (typeof query === 'object' && Object.keys(query).length > 0) {
381
- mergeSelection(config, fieldState, { columns: {}, ...query });
382
- }
383
- if (
384
- (!!normalizedIndirectInclude?.path && normalizedIndirectInclude.path.length > 0) ||
385
- (!!normalizedIndirectInclude?.paths && normalizedIndirectInclude.paths.length > 0)
386
- ) {
387
- resolveIndirectIncludePaths(
388
- returnType,
389
- info,
390
- selection,
391
- (returnType.extensions?.pothosIndirectInclude as { path: [] })?.path ?? [],
392
- normalizedIndirectInclude.paths ?? [normalizedIndirectInclude.path!],
393
- [],
394
- (resolvedType, resolvedField, path, deferred) => {
395
- addTypeSelectionsForField(
396
- config,
397
- resolvedType,
398
- context,
399
- info,
400
- fieldState,
401
- resolvedField,
402
- path,
403
- deferred,
404
- allSegments,
405
- );
406
- },
407
- );
408
- }
409
- addTypeSelectionsForField(
410
- config,
411
- returnType,
412
- context,
413
- info,
414
- fieldState,
415
- selection,
416
- [],
417
- undefined,
418
- allSegments,
419
- );
420
- mappings = fieldState.mappings;
421
- return selectionToQuery(config, fieldState);
422
- },
423
- (path) => {
424
- const returnType = getNamedType(field.type);
425
- let node: FieldNode | null = null;
426
- resolveIndirectInclude(
427
- returnType,
428
- info,
429
- selection,
430
- path.map((name) => ({
431
- name,
432
- })),
433
- [],
434
- (_, resolvedField) => {
435
- node = resolvedField;
436
- },
437
- );
438
- return node;
439
- },
440
- pathInfo,
441
- );
442
- } else {
443
- fieldSelectionMap = fieldSelect!;
444
- }
445
-
446
- if (fieldSelect && selectionCompatible(state, fieldSelectionMap, true)) {
447
- mergeSelection(config, state, fieldSelectionMap);
448
-
449
- state.mappings = mergeMappings(state.mappings, {
450
- [selection.alias?.value ?? selection.name.value]: {
451
- field: selection.name.value,
452
- type: type.name,
453
- mappings,
454
- indirectPath,
455
- },
456
- });
457
- }
458
- }
459
-
460
- function mergeMappings(existing: LoaderMappings, incoming: LoaderMappings): LoaderMappings {
461
- const result: LoaderMappings = { ...existing };
462
-
463
- for (const [key, value] of Object.entries(incoming)) {
464
- if (result[key]) {
465
- result[key] = {
466
- ...result[key],
467
- mappings: mergeMappings(result[key].mappings, value.mappings),
468
- };
469
- } else {
470
- result[key] = value;
471
- }
472
- }
473
-
474
- return result;
475
- }
6
+ import type { SelectionMap } from './selections.js';
476
7
 
477
8
  export interface QueryFromInfoOptions<T extends SelectionMap> {
478
9
  config: PothosDrizzleSchemaConfig;
@@ -480,248 +11,60 @@ export interface QueryFromInfoOptions<T extends SelectionMap> {
480
11
  info: GraphQLResolveInfo;
481
12
  typeName?: string;
482
13
  select?: T;
483
- path?: string[];
484
- paths?: string[][];
485
- withUsageCheck?: boolean;
14
+ path?: PathSegment[];
15
+ paths?: PathSegment[][];
486
16
  }
487
17
 
488
18
  export function queryFromInfo<T extends SelectionMap>({
489
- withUsageCheck,
490
- ...options
491
- }: QueryFromInfoOptions<T>): T {
492
- const state = stateFromInfo(options);
493
-
494
- setLoaderMappings(options.context, options.info, state.mappings);
495
-
496
- const query = selectionToQuery(options.config, state) as T;
497
-
498
- return withUsageCheck ? wrapWithUsageCheck(query) : query;
499
- }
500
-
501
- export function stateFromInfo<T extends SelectionMap>({
502
19
  config,
503
- context,
504
- info,
505
- typeName,
506
20
  select,
507
- path = [],
508
- paths = [],
509
- }: QueryFromInfoOptions<T>) {
510
- const returnType = getNamedType(info.returnType);
511
- const type = typeName ? info.schema.getTypeMap()[typeName] : returnType;
512
- const initialSelection = select
513
- ? {
514
- columns: {},
515
- ...select,
516
- }
517
- : undefined;
518
-
519
- let state: SelectionState | undefined;
520
-
521
- if (path.length > 0 || paths.length > 0) {
522
- const { pothosIndirectInclude } = (returnType.extensions ?? {}) as {
523
- pothosIndirectInclude?: IndirectInclude;
524
- };
525
-
526
- resolveIndirectInclude(
527
- returnType,
528
- info,
529
- info.fieldNodes[0],
530
- pothosIndirectInclude?.path ?? [],
531
- [],
532
- (indirectType, indirectField, subPath, deferred) => {
533
- resolveIndirectIncludePaths(
534
- indirectType,
535
- info,
536
- indirectField,
537
- [],
538
- paths.length > 0
539
- ? paths.map((p) => p.map((n) => (typeof n === 'string' ? { name: n } : n)))
540
- : [path.map((n) => (typeof n === 'string' ? { name: n } : n))],
541
- subPath,
542
- (resolvedType, resolvedField, nested, deferred) => {
543
- state = createStateForSelection(
544
- config,
545
- info,
546
- resolvedType,
547
- undefined,
548
- initialSelection,
549
- );
550
-
551
- addTypeSelectionsForField(
552
- config,
553
- typeName ? type : resolvedType,
554
- context,
555
- info,
556
- state,
557
- resolvedField,
558
- nested,
559
- deferred,
560
- );
561
- },
562
- deferred,
563
- );
564
- },
565
- );
566
- } else {
567
- state = createStateForSelection(config, info, type, undefined, initialSelection);
568
-
569
- // Create initial segment for the root query field
570
- const rootFieldNode = info.fieldNodes[0];
571
- const rootField = info.parentType.getFields()[rootFieldNode.name.value];
572
- const initialSegments: FieldPathInfo[] = rootField
573
- ? [
574
- {
575
- field: rootFieldNode.name.value,
576
- alias: rootFieldNode.alias?.value ?? rootFieldNode.name.value,
577
- parentType: info.parentType.name,
578
- isList: isListType(rootField.type) || isListType(getNamedType(rootField.type)),
579
- },
580
- ]
581
- : [];
582
-
583
- addTypeSelectionsForField(
584
- config,
585
- type,
586
- context,
587
- info,
588
- state,
589
- info.fieldNodes[0],
590
- [],
591
- undefined,
592
- initialSegments,
593
- );
594
- }
595
-
596
- if (!state) {
597
- state = createStateForSelection(config, info, type, undefined, initialSelection);
598
- }
599
-
600
- return state;
601
- }
602
-
603
- export function selectionStateFromInfo(
604
- config: PothosDrizzleSchemaConfig,
605
- context: object,
606
- info: GraphQLResolveInfo,
607
- typeName?: string,
608
- ) {
609
- const type = typeName ? info.schema.getTypeMap()[typeName] : info.parentType;
610
-
611
- const state = createStateForSelection(config, info, type);
612
-
613
- if (!(isObjectType(type) || isInterfaceType(type))) {
614
- throw new PothosValidationError(
615
- 'Drizzle plugin can only resolve selections for object and interface types',
616
- );
617
- }
618
-
619
- addFieldSelection(config, type, context, info, state, info.fieldNodes[0], []);
620
-
621
- return state;
622
- }
623
-
624
- function createStateForSelection(
625
- config: PothosDrizzleSchemaConfig,
626
- info: GraphQLResolveInfo,
627
- type: GraphQLNamedType,
628
- parent?: SelectionState,
629
- initialSelections?: SelectionMap,
630
- ) {
631
- const targetType = getIndirectType(type, info);
632
- const { pothosDrizzleTable } = (targetType.extensions ?? {}) as {
633
- pothosDrizzleTable?: TableRelationalConfig;
634
- };
635
-
636
- if (!pothosDrizzleTable) {
637
- throw new PothosValidationError(`Expected ${targetType.name} to have a table config`);
638
- }
639
-
640
- const state = createState(
641
- pothosDrizzleTable,
642
- parent?.skipDeferredFragments ?? config.skipDeferredFragments,
643
- parent,
644
- );
645
-
646
- if (initialSelections) {
647
- mergeSelection(config, state, initialSelections);
648
- }
649
-
650
- return state;
651
- }
652
-
653
- export function getIndirectType(type: GraphQLNamedType, info: GraphQLResolveInfo) {
654
- let targetType = type;
655
-
656
- while (targetType.extensions?.pothosIndirectInclude) {
657
- targetType = info.schema.getType(
658
- (targetType.extensions.pothosIndirectInclude as IndirectInclude).getType(),
659
- )!;
660
- }
661
-
662
- return targetType;
663
- }
664
-
665
- export function normalizeInclude(
666
- path: string[],
667
- type: GraphQLNamedType,
668
- expectedType?: GraphQLNamedType,
669
- ): IndirectInclude {
670
- let currentType = path.length > 0 ? type : (expectedType ?? type);
671
-
672
- const normalized: { name: string; type: string }[] = [];
673
-
674
- if (!(isObjectType(currentType) || isInterfaceType(currentType))) {
675
- throw new PothosValidationError(`Expected ${currentType} to be an Object type`);
676
- }
677
-
678
- for (const fieldName of path) {
679
- const field: GraphQLField<unknown, unknown> = currentType.getFields()[fieldName];
680
-
681
- if (!field) {
682
- throw new PothosValidationError(`Expected ${currentType} to have a field ${fieldName}`);
683
- }
684
-
685
- currentType = getNamedType(field.type);
686
-
687
- if (!(isObjectType(currentType) || isInterfaceType(currentType))) {
688
- throw new PothosValidationError(`Expected ${currentType} to be an Object or Interface type`);
689
- }
690
-
691
- normalized.push({ name: fieldName, type: currentType.name });
692
- }
693
-
694
- return {
695
- getType: () =>
696
- expectedType?.name ??
697
- (normalized.length > 0 ? normalized[normalized.length - 1].type : type.name),
698
- path: normalized,
699
- };
700
- }
701
-
702
- function fieldSkipped(info: GraphQLResolveInfo, selection: FieldNode) {
703
- const skip = getDirectiveValues(GraphQLSkipDirective, selection, info.variableValues);
704
- if (skip?.if === true) {
705
- return true;
706
- }
21
+ ...options
22
+ }: QueryFromInfoOptions<T>): T {
23
+ const plan = Plan.fromInfo(drizzleAdapter(config), {
24
+ ...options,
25
+ // A `select` without `columns` merges as "no columns yet", not "every column".
26
+ initial: select ? { columns: {}, ...select } : undefined,
27
+ });
707
28
 
708
- const include = getDirectiveValues(GraphQLIncludeDirective, selection, info.variableValues);
709
- if (include?.if === false) {
710
- return true;
29
+ if (!plan) {
30
+ // Nothing is planned under the paths: the caller gets its own selection back untouched.
31
+ return (select ?? {}) as T;
711
32
  }
712
33
 
713
- return false;
34
+ return (
35
+ isThenable(plan) ? plan.then((settled) => (settled as DrizzlePlan).query()) : plan.query()
36
+ ) as T;
714
37
  }
715
38
 
716
- function isDeferredFragment(
717
- node: FragmentSpreadNode | InlineFragmentNode,
718
- info: GraphQLResolveInfo,
719
- ) {
720
- const deferDirective = info.schema.getDirective('defer');
721
- if (!deferDirective) {
722
- return false;
723
- }
724
-
725
- const defer = getDirectiveValues(deferDirective, node, info.variableValues);
726
- return !!defer && defer.if !== false;
39
+ /**
40
+ * The plan for the field `info` resolves (or `typeName`), nothing recorded: a promise while a
41
+ * select beneath the field is async, undefined when `paths` are given and nothing is selected
42
+ * under them. The model loader's, and the first step of a `drizzleField` resolve, whose
43
+ * merges are recorded so the resolver's `query()` selection can come first.
44
+ */
45
+ export function planFromInfo({
46
+ config,
47
+ ...options
48
+ }: Omit<QueryFromInfoOptions<SelectionMap>, 'select'>): DrizzlePlan | undefined {
49
+ return Plan.fromInfo(drizzleAdapter(config), options);
50
+ }
51
+
52
+ /**
53
+ * The query for a plan `planFromInfo` returned, settled: the caller's `select` first, the walked
54
+ * plan after it, the mappings recorded, and the query serialized. This is how the `query()`
55
+ * builder handed to a `drizzleField` or `drizzleConnection` resolver stays synchronous once a
56
+ * selection beneath the field is async, with the precedence of `queryFromInfo`: a relation the
57
+ * document also plans with other arguments loads on its own.
58
+ */
59
+ export function queryFromPlan<T extends SelectionMap>(
60
+ plan: DrizzlePlan | undefined,
61
+ select?: T,
62
+ ): T {
63
+ if (!plan) {
64
+ // Nothing is selected under the paths: the caller gets its own selection back.
65
+ return (select ?? {}) as T;
66
+ }
67
+
68
+ // A `select` without `columns` merges as "no columns yet", not "every column".
69
+ return plan.query(select ? { columns: {}, ...select } : undefined) as T;
727
70
  }