@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.
- package/CHANGELOG.md +345 -0
- package/README.md +784 -221
- package/dts/connection-helpers.d.ts +18 -8
- package/dts/connection-helpers.d.ts.map +1 -1
- package/dts/drizzle-field-builder.d.ts +4 -11
- package/dts/drizzle-field-builder.d.ts.map +1 -1
- package/dts/global-types.d.ts.map +1 -1
- package/dts/index.d.ts.map +1 -1
- package/dts/model-loader.d.ts +18 -17
- package/dts/model-loader.d.ts.map +1 -1
- package/dts/types.d.ts +80 -20
- package/dts/types.d.ts.map +1 -1
- package/dts/utils/adapter.d.ts +39 -0
- package/dts/utils/adapter.d.ts.map +1 -0
- package/dts/utils/await-selections.d.ts +12 -0
- package/dts/utils/await-selections.d.ts.map +1 -0
- package/dts/utils/config.d.ts +23 -3
- package/dts/utils/config.d.ts.map +1 -1
- package/dts/utils/cursors.d.ts +6 -6
- package/dts/utils/cursors.d.ts.map +1 -1
- package/dts/utils/map-query.d.ts +22 -21
- package/dts/utils/map-query.d.ts.map +1 -1
- package/dts/utils/path-info.d.ts +13 -0
- package/dts/utils/path-info.d.ts.map +1 -0
- package/dts/utils/relation-filter.d.ts +48 -0
- package/dts/utils/relation-filter.d.ts.map +1 -0
- package/dts/utils/selections.d.ts +2 -21
- package/dts/utils/selections.d.ts.map +1 -1
- package/esm/connection-helpers.d.ts +18 -8
- package/esm/connection-helpers.d.ts.map +1 -1
- package/esm/connection-helpers.js +52 -19
- package/esm/connection-helpers.js.map +1 -1
- package/esm/drizzle-field-builder.d.ts +4 -11
- package/esm/drizzle-field-builder.d.ts.map +1 -1
- package/esm/drizzle-field-builder.js +138 -96
- package/esm/drizzle-field-builder.js.map +1 -1
- package/esm/field-builder.js +61 -47
- package/esm/field-builder.js.map +1 -1
- package/esm/global-types.d.ts.map +1 -1
- package/esm/index.d.ts.map +1 -1
- package/esm/index.js +37 -61
- package/esm/index.js.map +1 -1
- package/esm/model-loader.d.ts +18 -17
- package/esm/model-loader.d.ts.map +1 -1
- package/esm/model-loader.js +76 -45
- package/esm/model-loader.js.map +1 -1
- package/esm/schema-builder.js +40 -149
- package/esm/schema-builder.js.map +1 -1
- package/esm/types.d.ts +80 -20
- package/esm/types.d.ts.map +1 -1
- package/esm/types.js.map +1 -1
- package/esm/utils/adapter.d.ts +39 -0
- package/esm/utils/adapter.d.ts.map +1 -0
- package/esm/utils/adapter.js +137 -0
- package/esm/utils/adapter.js.map +1 -0
- package/esm/utils/await-selections.d.ts +12 -0
- package/esm/utils/await-selections.d.ts.map +1 -0
- package/esm/utils/await-selections.js +19 -0
- package/esm/utils/await-selections.js.map +1 -0
- package/esm/utils/config.d.ts +23 -3
- package/esm/utils/config.d.ts.map +1 -1
- package/esm/utils/config.js +1 -4
- package/esm/utils/config.js.map +1 -1
- package/esm/utils/cursors.d.ts +6 -6
- package/esm/utils/cursors.d.ts.map +1 -1
- package/esm/utils/cursors.js +55 -90
- package/esm/utils/cursors.js.map +1 -1
- package/esm/utils/map-query.d.ts +22 -21
- package/esm/utils/map-query.d.ts.map +1 -1
- package/esm/utils/map-query.js +40 -354
- package/esm/utils/map-query.js.map +1 -1
- package/esm/utils/path-info.d.ts +13 -0
- package/esm/utils/path-info.d.ts.map +1 -0
- package/esm/utils/path-info.js +37 -0
- package/esm/utils/path-info.js.map +1 -0
- package/esm/utils/relation-filter.d.ts +48 -0
- package/esm/utils/relation-filter.d.ts.map +1 -0
- package/esm/utils/relation-filter.js +92 -0
- package/esm/utils/relation-filter.js.map +1 -0
- package/esm/utils/selections.d.ts +2 -21
- package/esm/utils/selections.d.ts.map +1 -1
- package/esm/utils/selections.js +1 -133
- package/esm/utils/selections.js.map +1 -1
- package/lib/connection-helpers.js +51 -18
- package/lib/connection-helpers.js.map +1 -1
- package/lib/drizzle-field-builder.js +136 -94
- package/lib/drizzle-field-builder.js.map +1 -1
- package/lib/field-builder.js +58 -44
- package/lib/field-builder.js.map +1 -1
- package/lib/index.js +36 -60
- package/lib/index.js.map +1 -1
- package/lib/model-loader.js +76 -45
- package/lib/model-loader.js.map +1 -1
- package/lib/schema-builder.js +40 -149
- package/lib/schema-builder.js.map +1 -1
- package/lib/types.js.map +1 -1
- package/lib/utils/adapter.js +147 -0
- package/lib/utils/adapter.js.map +1 -0
- package/lib/utils/await-selections.js +20 -0
- package/lib/utils/await-selections.js.map +1 -0
- package/lib/utils/config.js +1 -4
- package/lib/utils/config.js.map +1 -1
- package/lib/utils/cursors.js +47 -88
- package/lib/utils/cursors.js.map +1 -1
- package/lib/utils/map-query.js +28 -359
- package/lib/utils/map-query.js.map +1 -1
- package/lib/utils/path-info.js +39 -0
- package/lib/utils/path-info.js.map +1 -0
- package/lib/utils/relation-filter.js +91 -0
- package/lib/utils/relation-filter.js.map +1 -0
- package/lib/utils/selections.js +3 -158
- package/lib/utils/selections.js.map +1 -1
- package/package.json +8 -6
- package/src/connection-helpers.ts +167 -53
- package/src/drizzle-field-builder.ts +296 -176
- package/src/field-builder.ts +110 -77
- package/src/global-types.ts +0 -32
- package/src/index.ts +42 -68
- package/src/model-loader.ts +110 -71
- package/src/schema-builder.ts +76 -169
- package/src/types.ts +175 -44
- package/src/utils/adapter.ts +197 -0
- package/src/utils/await-selections.ts +31 -0
- package/src/utils/config.ts +1 -5
- package/src/utils/cursors.ts +53 -81
- package/src/utils/map-query.ts +51 -734
- package/src/utils/path-info.ts +40 -0
- package/src/utils/relation-filter.ts +185 -0
- package/src/utils/selections.ts +2 -207
- package/dts/utils/deep-equal.d.ts +0 -2
- package/dts/utils/deep-equal.d.ts.map +0 -1
- package/dts/utils/drizzle-graphql.d.ts +0 -2
- package/dts/utils/drizzle-graphql.d.ts.map +0 -1
- package/dts/utils/loader-map.d.ts +0 -11
- package/dts/utils/loader-map.d.ts.map +0 -1
- package/dts/utils/usage.d.ts +0 -5
- package/dts/utils/usage.d.ts.map +0 -1
- package/esm/utils/deep-equal.d.ts +0 -2
- package/esm/utils/deep-equal.d.ts.map +0 -1
- package/esm/utils/deep-equal.js +0 -45
- package/esm/utils/deep-equal.js.map +0 -1
- package/esm/utils/drizzle-graphql.d.ts +0 -2
- package/esm/utils/drizzle-graphql.d.ts.map +0 -1
- package/esm/utils/drizzle-graphql.js +0 -232
- package/esm/utils/drizzle-graphql.js.map +0 -1
- package/esm/utils/loader-map.d.ts +0 -11
- package/esm/utils/loader-map.d.ts.map +0 -1
- package/esm/utils/loader-map.js +0 -35
- package/esm/utils/loader-map.js.map +0 -1
- package/esm/utils/usage.d.ts +0 -5
- package/esm/utils/usage.d.ts.map +0 -1
- package/esm/utils/usage.js +0 -59
- package/esm/utils/usage.js.map +0 -1
- package/lib/utils/deep-equal.js +0 -55
- package/lib/utils/deep-equal.js.map +0 -1
- package/lib/utils/drizzle-graphql.js +0 -236
- package/lib/utils/drizzle-graphql.js.map +0 -1
- package/lib/utils/loader-map.js +0 -56
- package/lib/utils/loader-map.js.map +0 -1
- package/lib/utils/usage.js +0 -83
- package/lib/utils/usage.js.map +0 -1
- package/src/utils/deep-equal.ts +0 -57
- package/src/utils/drizzle-graphql.ts +0 -274
- package/src/utils/loader-map.ts +0 -49
- package/src/utils/usage.ts +0 -62
package/src/utils/map-query.ts
CHANGED
|
@@ -1,504 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type
|
|
3
|
-
import {
|
|
4
|
-
|
|
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
|
|
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
|
-
// The type to plan a fragment's selections against, or null when the fragment
|
|
234
|
-
// cannot apply to `type`. A fragment on an interface the object implements is
|
|
235
|
-
// planned against the object, whose field map already carries the interface
|
|
236
|
-
// fields and their `select` extensions.
|
|
237
|
-
function typeForFragment(
|
|
238
|
-
type: GraphQLInterfaceType | GraphQLObjectType,
|
|
239
|
-
condition: GraphQLNamedType,
|
|
240
|
-
): GraphQLInterfaceType | GraphQLObjectType | null {
|
|
241
|
-
if (isObjectType(type)) {
|
|
242
|
-
if (condition.name === type.name) {
|
|
243
|
-
return type;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
if (
|
|
247
|
-
isInterfaceType(condition) &&
|
|
248
|
-
type.getInterfaces().some((iface) => iface.name === condition.name)
|
|
249
|
-
) {
|
|
250
|
-
return type;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
return null;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
if (
|
|
257
|
-
(isObjectType(condition) || isInterfaceType(condition)) &&
|
|
258
|
-
condition.extensions?.pothosDrizzleModel === type.extensions.pothosDrizzleModel
|
|
259
|
-
) {
|
|
260
|
-
return condition;
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
return null;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
function addNestedSelections(
|
|
267
|
-
config: PothosDrizzleSchemaConfig,
|
|
268
|
-
type: GraphQLInterfaceType | GraphQLObjectType,
|
|
269
|
-
context: object,
|
|
270
|
-
info: GraphQLResolveInfo,
|
|
271
|
-
state: SelectionState,
|
|
272
|
-
selections: SelectionSetNode,
|
|
273
|
-
indirectPath: string[],
|
|
274
|
-
segments: FieldPathInfo[] = [],
|
|
275
|
-
) {
|
|
276
|
-
let parentType: GraphQLInterfaceType | GraphQLObjectType | null = type;
|
|
277
|
-
for (const selection of selections.selections) {
|
|
278
|
-
switch (selection.kind) {
|
|
279
|
-
case Kind.FIELD:
|
|
280
|
-
addFieldSelection(config, type, context, info, state, selection, indirectPath, segments);
|
|
281
|
-
|
|
282
|
-
continue;
|
|
283
|
-
case Kind.FRAGMENT_SPREAD:
|
|
284
|
-
if (state.skipDeferredFragments && isDeferredFragment(selection, info)) {
|
|
285
|
-
continue;
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
parentType = typeForFragment(
|
|
289
|
-
type,
|
|
290
|
-
info.schema.getType(info.fragments[selection.name.value].typeCondition.name.value)!,
|
|
291
|
-
);
|
|
292
|
-
if (!parentType) {
|
|
293
|
-
continue;
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
addNestedSelections(
|
|
297
|
-
config,
|
|
298
|
-
parentType,
|
|
299
|
-
context,
|
|
300
|
-
info,
|
|
301
|
-
state,
|
|
302
|
-
info.fragments[selection.name.value].selectionSet,
|
|
303
|
-
indirectPath,
|
|
304
|
-
segments,
|
|
305
|
-
);
|
|
306
|
-
|
|
307
|
-
continue;
|
|
308
|
-
|
|
309
|
-
case Kind.INLINE_FRAGMENT:
|
|
310
|
-
if (state.skipDeferredFragments && isDeferredFragment(selection, info)) {
|
|
311
|
-
continue;
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
parentType = selection.typeCondition
|
|
315
|
-
? typeForFragment(type, info.schema.getType(selection.typeCondition.name.value)!)
|
|
316
|
-
: type;
|
|
317
|
-
if (!parentType) {
|
|
318
|
-
continue;
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
addNestedSelections(
|
|
322
|
-
config,
|
|
323
|
-
parentType,
|
|
324
|
-
context,
|
|
325
|
-
info,
|
|
326
|
-
state,
|
|
327
|
-
selection.selectionSet,
|
|
328
|
-
indirectPath,
|
|
329
|
-
segments,
|
|
330
|
-
);
|
|
331
|
-
|
|
332
|
-
continue;
|
|
333
|
-
|
|
334
|
-
default:
|
|
335
|
-
throw new PothosValidationError(
|
|
336
|
-
`Unsupported selection kind ${(selection as { kind: string }).kind}`,
|
|
337
|
-
);
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
function addFieldSelection(
|
|
343
|
-
config: PothosDrizzleSchemaConfig,
|
|
344
|
-
type: GraphQLInterfaceType | GraphQLObjectType,
|
|
345
|
-
context: object,
|
|
346
|
-
info: GraphQLResolveInfo,
|
|
347
|
-
state: SelectionState,
|
|
348
|
-
selection: FieldNode,
|
|
349
|
-
indirectPath: string[],
|
|
350
|
-
segments: FieldPathInfo[] = [],
|
|
351
|
-
) {
|
|
352
|
-
if (selection.name.value.startsWith('__') || fieldSkipped(info, selection)) {
|
|
353
|
-
return;
|
|
354
|
-
}
|
|
355
|
-
const field = type.getFields()[selection.name.value];
|
|
356
|
-
if (!field) {
|
|
357
|
-
throw new PothosValidationError(`Unknown field ${selection.name.value} on ${type.name}`);
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
const allSegments = [
|
|
361
|
-
...segments,
|
|
362
|
-
{
|
|
363
|
-
field: selection.name.value,
|
|
364
|
-
alias: selection.alias?.value ?? selection.name.value,
|
|
365
|
-
parentType: type.name,
|
|
366
|
-
isList: isListType(field.type) || isListType(getNamedType(field.type)),
|
|
367
|
-
},
|
|
368
|
-
];
|
|
369
|
-
|
|
370
|
-
const fieldSelect = field.extensions?.pothosDrizzleSelect as DrizzleFieldSelection | undefined;
|
|
371
|
-
|
|
372
|
-
let fieldSelectionMap: DBQueryConfig<'one'> | undefined;
|
|
373
|
-
let mappings: LoaderMappings = {};
|
|
374
|
-
if (typeof fieldSelect === 'function') {
|
|
375
|
-
const pathInfo: PathInfo = {
|
|
376
|
-
path: allSegments.map((s) => `${s.parentType}.${s.field}`),
|
|
377
|
-
segments: allSegments,
|
|
378
|
-
};
|
|
379
|
-
|
|
380
|
-
const args = getMappedArgumentValues(field, selection, context, info) as Record<
|
|
381
|
-
string,
|
|
382
|
-
unknown
|
|
383
|
-
>;
|
|
384
|
-
fieldSelectionMap = fieldSelect(
|
|
385
|
-
args,
|
|
386
|
-
context,
|
|
387
|
-
(rawQuery, indirectInclude, expectedType) => {
|
|
388
|
-
const returnType = getNamedType(field.type);
|
|
389
|
-
const query = typeof rawQuery === 'function' ? rawQuery(args, context) : rawQuery;
|
|
390
|
-
const normalizedIndirectInclude = Array.isArray(indirectInclude)
|
|
391
|
-
? normalizeInclude(
|
|
392
|
-
indirectInclude,
|
|
393
|
-
getIndirectType(returnType, info),
|
|
394
|
-
expectedType ? getNamedType(info.schema.getType(expectedType)) : undefined,
|
|
395
|
-
)
|
|
396
|
-
: indirectInclude;
|
|
397
|
-
const fieldState = createStateForSelection(
|
|
398
|
-
config,
|
|
399
|
-
info,
|
|
400
|
-
normalizedIndirectInclude
|
|
401
|
-
? info.schema.getType(normalizedIndirectInclude.getType())!
|
|
402
|
-
: returnType,
|
|
403
|
-
state,
|
|
404
|
-
);
|
|
405
|
-
|
|
406
|
-
if (typeof query === 'object' && Object.keys(query).length > 0) {
|
|
407
|
-
mergeSelection(config, fieldState, { columns: {}, ...query });
|
|
408
|
-
}
|
|
409
|
-
if (
|
|
410
|
-
(!!normalizedIndirectInclude?.path && normalizedIndirectInclude.path.length > 0) ||
|
|
411
|
-
(!!normalizedIndirectInclude?.paths && normalizedIndirectInclude.paths.length > 0)
|
|
412
|
-
) {
|
|
413
|
-
resolveIndirectIncludePaths(
|
|
414
|
-
returnType,
|
|
415
|
-
info,
|
|
416
|
-
selection,
|
|
417
|
-
(returnType.extensions?.pothosIndirectInclude as { path: [] })?.path ?? [],
|
|
418
|
-
normalizedIndirectInclude.paths ?? [normalizedIndirectInclude.path!],
|
|
419
|
-
[],
|
|
420
|
-
(resolvedType, resolvedField, path, deferred) => {
|
|
421
|
-
addTypeSelectionsForField(
|
|
422
|
-
config,
|
|
423
|
-
resolvedType,
|
|
424
|
-
context,
|
|
425
|
-
info,
|
|
426
|
-
fieldState,
|
|
427
|
-
resolvedField,
|
|
428
|
-
path,
|
|
429
|
-
deferred,
|
|
430
|
-
allSegments,
|
|
431
|
-
);
|
|
432
|
-
},
|
|
433
|
-
);
|
|
434
|
-
}
|
|
435
|
-
addTypeSelectionsForField(
|
|
436
|
-
config,
|
|
437
|
-
returnType,
|
|
438
|
-
context,
|
|
439
|
-
info,
|
|
440
|
-
fieldState,
|
|
441
|
-
selection,
|
|
442
|
-
[],
|
|
443
|
-
undefined,
|
|
444
|
-
allSegments,
|
|
445
|
-
);
|
|
446
|
-
mappings = fieldState.mappings;
|
|
447
|
-
return selectionToQuery(config, fieldState);
|
|
448
|
-
},
|
|
449
|
-
(path) => {
|
|
450
|
-
const returnType = getNamedType(field.type);
|
|
451
|
-
let node: FieldNode | null = null;
|
|
452
|
-
resolveIndirectInclude(
|
|
453
|
-
returnType,
|
|
454
|
-
info,
|
|
455
|
-
selection,
|
|
456
|
-
path.map((name) => ({
|
|
457
|
-
name,
|
|
458
|
-
})),
|
|
459
|
-
[],
|
|
460
|
-
(_, resolvedField) => {
|
|
461
|
-
node = resolvedField;
|
|
462
|
-
},
|
|
463
|
-
);
|
|
464
|
-
return node;
|
|
465
|
-
},
|
|
466
|
-
pathInfo,
|
|
467
|
-
);
|
|
468
|
-
} else {
|
|
469
|
-
fieldSelectionMap = fieldSelect!;
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
if (fieldSelect && selectionCompatible(state, fieldSelectionMap, true)) {
|
|
473
|
-
mergeSelection(config, state, fieldSelectionMap);
|
|
474
|
-
|
|
475
|
-
state.mappings = mergeMappings(state.mappings, {
|
|
476
|
-
[selection.alias?.value ?? selection.name.value]: {
|
|
477
|
-
field: selection.name.value,
|
|
478
|
-
type: type.name,
|
|
479
|
-
mappings,
|
|
480
|
-
indirectPath,
|
|
481
|
-
},
|
|
482
|
-
});
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
function mergeMappings(existing: LoaderMappings, incoming: LoaderMappings): LoaderMappings {
|
|
487
|
-
const result: LoaderMappings = { ...existing };
|
|
488
|
-
|
|
489
|
-
for (const [key, value] of Object.entries(incoming)) {
|
|
490
|
-
if (result[key]) {
|
|
491
|
-
result[key] = {
|
|
492
|
-
...result[key],
|
|
493
|
-
mappings: mergeMappings(result[key].mappings, value.mappings),
|
|
494
|
-
};
|
|
495
|
-
} else {
|
|
496
|
-
result[key] = value;
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
return result;
|
|
501
|
-
}
|
|
6
|
+
import type { SelectionMap } from './selections.js';
|
|
502
7
|
|
|
503
8
|
export interface QueryFromInfoOptions<T extends SelectionMap> {
|
|
504
9
|
config: PothosDrizzleSchemaConfig;
|
|
@@ -506,248 +11,60 @@ export interface QueryFromInfoOptions<T extends SelectionMap> {
|
|
|
506
11
|
info: GraphQLResolveInfo;
|
|
507
12
|
typeName?: string;
|
|
508
13
|
select?: T;
|
|
509
|
-
path?:
|
|
510
|
-
paths?:
|
|
511
|
-
withUsageCheck?: boolean;
|
|
14
|
+
path?: PathSegment[];
|
|
15
|
+
paths?: PathSegment[][];
|
|
512
16
|
}
|
|
513
17
|
|
|
514
18
|
export function queryFromInfo<T extends SelectionMap>({
|
|
515
|
-
withUsageCheck,
|
|
516
|
-
...options
|
|
517
|
-
}: QueryFromInfoOptions<T>): T {
|
|
518
|
-
const state = stateFromInfo(options);
|
|
519
|
-
|
|
520
|
-
setLoaderMappings(options.context, options.info, state.mappings);
|
|
521
|
-
|
|
522
|
-
const query = selectionToQuery(options.config, state) as T;
|
|
523
|
-
|
|
524
|
-
return withUsageCheck ? wrapWithUsageCheck(query) : query;
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
export function stateFromInfo<T extends SelectionMap>({
|
|
528
19
|
config,
|
|
529
|
-
context,
|
|
530
|
-
info,
|
|
531
|
-
typeName,
|
|
532
20
|
select,
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
columns: {},
|
|
541
|
-
...select,
|
|
542
|
-
}
|
|
543
|
-
: undefined;
|
|
544
|
-
|
|
545
|
-
let state: SelectionState | undefined;
|
|
546
|
-
|
|
547
|
-
if (path.length > 0 || paths.length > 0) {
|
|
548
|
-
const { pothosIndirectInclude } = (returnType.extensions ?? {}) as {
|
|
549
|
-
pothosIndirectInclude?: IndirectInclude;
|
|
550
|
-
};
|
|
551
|
-
|
|
552
|
-
resolveIndirectInclude(
|
|
553
|
-
returnType,
|
|
554
|
-
info,
|
|
555
|
-
info.fieldNodes[0],
|
|
556
|
-
pothosIndirectInclude?.path ?? [],
|
|
557
|
-
[],
|
|
558
|
-
(indirectType, indirectField, subPath, deferred) => {
|
|
559
|
-
resolveIndirectIncludePaths(
|
|
560
|
-
indirectType,
|
|
561
|
-
info,
|
|
562
|
-
indirectField,
|
|
563
|
-
[],
|
|
564
|
-
paths.length > 0
|
|
565
|
-
? paths.map((p) => p.map((n) => (typeof n === 'string' ? { name: n } : n)))
|
|
566
|
-
: [path.map((n) => (typeof n === 'string' ? { name: n } : n))],
|
|
567
|
-
subPath,
|
|
568
|
-
(resolvedType, resolvedField, nested, deferred) => {
|
|
569
|
-
state = createStateForSelection(
|
|
570
|
-
config,
|
|
571
|
-
info,
|
|
572
|
-
resolvedType,
|
|
573
|
-
undefined,
|
|
574
|
-
initialSelection,
|
|
575
|
-
);
|
|
576
|
-
|
|
577
|
-
addTypeSelectionsForField(
|
|
578
|
-
config,
|
|
579
|
-
typeName ? type : resolvedType,
|
|
580
|
-
context,
|
|
581
|
-
info,
|
|
582
|
-
state,
|
|
583
|
-
resolvedField,
|
|
584
|
-
nested,
|
|
585
|
-
deferred,
|
|
586
|
-
);
|
|
587
|
-
},
|
|
588
|
-
deferred,
|
|
589
|
-
);
|
|
590
|
-
},
|
|
591
|
-
);
|
|
592
|
-
} else {
|
|
593
|
-
state = createStateForSelection(config, info, type, undefined, initialSelection);
|
|
594
|
-
|
|
595
|
-
// Create initial segment for the root query field
|
|
596
|
-
const rootFieldNode = info.fieldNodes[0];
|
|
597
|
-
const rootField = info.parentType.getFields()[rootFieldNode.name.value];
|
|
598
|
-
const initialSegments: FieldPathInfo[] = rootField
|
|
599
|
-
? [
|
|
600
|
-
{
|
|
601
|
-
field: rootFieldNode.name.value,
|
|
602
|
-
alias: rootFieldNode.alias?.value ?? rootFieldNode.name.value,
|
|
603
|
-
parentType: info.parentType.name,
|
|
604
|
-
isList: isListType(rootField.type) || isListType(getNamedType(rootField.type)),
|
|
605
|
-
},
|
|
606
|
-
]
|
|
607
|
-
: [];
|
|
608
|
-
|
|
609
|
-
addTypeSelectionsForField(
|
|
610
|
-
config,
|
|
611
|
-
type,
|
|
612
|
-
context,
|
|
613
|
-
info,
|
|
614
|
-
state,
|
|
615
|
-
info.fieldNodes[0],
|
|
616
|
-
[],
|
|
617
|
-
undefined,
|
|
618
|
-
initialSegments,
|
|
619
|
-
);
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
if (!state) {
|
|
623
|
-
state = createStateForSelection(config, info, type, undefined, initialSelection);
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
return state;
|
|
627
|
-
}
|
|
628
|
-
|
|
629
|
-
export function selectionStateFromInfo(
|
|
630
|
-
config: PothosDrizzleSchemaConfig,
|
|
631
|
-
context: object,
|
|
632
|
-
info: GraphQLResolveInfo,
|
|
633
|
-
typeName?: string,
|
|
634
|
-
) {
|
|
635
|
-
const type = typeName ? info.schema.getTypeMap()[typeName] : info.parentType;
|
|
636
|
-
|
|
637
|
-
const state = createStateForSelection(config, info, type);
|
|
638
|
-
|
|
639
|
-
if (!(isObjectType(type) || isInterfaceType(type))) {
|
|
640
|
-
throw new PothosValidationError(
|
|
641
|
-
'Drizzle plugin can only resolve selections for object and interface types',
|
|
642
|
-
);
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
addFieldSelection(config, type, context, info, state, info.fieldNodes[0], []);
|
|
646
|
-
|
|
647
|
-
return state;
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
function createStateForSelection(
|
|
651
|
-
config: PothosDrizzleSchemaConfig,
|
|
652
|
-
info: GraphQLResolveInfo,
|
|
653
|
-
type: GraphQLNamedType,
|
|
654
|
-
parent?: SelectionState,
|
|
655
|
-
initialSelections?: SelectionMap,
|
|
656
|
-
) {
|
|
657
|
-
const targetType = getIndirectType(type, info);
|
|
658
|
-
const { pothosDrizzleTable } = (targetType.extensions ?? {}) as {
|
|
659
|
-
pothosDrizzleTable?: TableRelationalConfig;
|
|
660
|
-
};
|
|
661
|
-
|
|
662
|
-
if (!pothosDrizzleTable) {
|
|
663
|
-
throw new PothosValidationError(`Expected ${targetType.name} to have a table config`);
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
const state = createState(
|
|
667
|
-
pothosDrizzleTable,
|
|
668
|
-
parent?.skipDeferredFragments ?? config.skipDeferredFragments,
|
|
669
|
-
parent,
|
|
670
|
-
);
|
|
671
|
-
|
|
672
|
-
if (initialSelections) {
|
|
673
|
-
mergeSelection(config, state, initialSelections);
|
|
674
|
-
}
|
|
675
|
-
|
|
676
|
-
return state;
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
export function getIndirectType(type: GraphQLNamedType, info: GraphQLResolveInfo) {
|
|
680
|
-
let targetType = type;
|
|
681
|
-
|
|
682
|
-
while (targetType.extensions?.pothosIndirectInclude) {
|
|
683
|
-
targetType = info.schema.getType(
|
|
684
|
-
(targetType.extensions.pothosIndirectInclude as IndirectInclude).getType(),
|
|
685
|
-
)!;
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
return targetType;
|
|
689
|
-
}
|
|
690
|
-
|
|
691
|
-
export function normalizeInclude(
|
|
692
|
-
path: string[],
|
|
693
|
-
type: GraphQLNamedType,
|
|
694
|
-
expectedType?: GraphQLNamedType,
|
|
695
|
-
): IndirectInclude {
|
|
696
|
-
let currentType = path.length > 0 ? type : (expectedType ?? type);
|
|
697
|
-
|
|
698
|
-
const normalized: { name: string; type: string }[] = [];
|
|
699
|
-
|
|
700
|
-
if (!(isObjectType(currentType) || isInterfaceType(currentType))) {
|
|
701
|
-
throw new PothosValidationError(`Expected ${currentType} to be an Object type`);
|
|
702
|
-
}
|
|
703
|
-
|
|
704
|
-
for (const fieldName of path) {
|
|
705
|
-
const field: GraphQLField<unknown, unknown> = currentType.getFields()[fieldName];
|
|
706
|
-
|
|
707
|
-
if (!field) {
|
|
708
|
-
throw new PothosValidationError(`Expected ${currentType} to have a field ${fieldName}`);
|
|
709
|
-
}
|
|
710
|
-
|
|
711
|
-
currentType = getNamedType(field.type);
|
|
712
|
-
|
|
713
|
-
if (!(isObjectType(currentType) || isInterfaceType(currentType))) {
|
|
714
|
-
throw new PothosValidationError(`Expected ${currentType} to be an Object or Interface type`);
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
normalized.push({ name: fieldName, type: currentType.name });
|
|
718
|
-
}
|
|
719
|
-
|
|
720
|
-
return {
|
|
721
|
-
getType: () =>
|
|
722
|
-
expectedType?.name ??
|
|
723
|
-
(normalized.length > 0 ? normalized[normalized.length - 1].type : type.name),
|
|
724
|
-
path: normalized,
|
|
725
|
-
};
|
|
726
|
-
}
|
|
727
|
-
|
|
728
|
-
function fieldSkipped(info: GraphQLResolveInfo, selection: FieldNode) {
|
|
729
|
-
const skip = getDirectiveValues(GraphQLSkipDirective, selection, info.variableValues);
|
|
730
|
-
if (skip?.if === true) {
|
|
731
|
-
return true;
|
|
732
|
-
}
|
|
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
|
+
});
|
|
733
28
|
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
return
|
|
29
|
+
if (!plan) {
|
|
30
|
+
// Nothing is planned under the paths: the caller gets its own selection back untouched.
|
|
31
|
+
return (select ?? {}) as T;
|
|
737
32
|
}
|
|
738
33
|
|
|
739
|
-
return
|
|
34
|
+
return (
|
|
35
|
+
isThenable(plan) ? plan.then((settled) => (settled as DrizzlePlan).query()) : plan.query()
|
|
36
|
+
) as T;
|
|
740
37
|
}
|
|
741
38
|
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
return
|
|
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;
|
|
753
70
|
}
|