@pothos/plugin-prisma 0.17.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 (168) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +403 -308
  3. package/esm/field-builder.js +12 -14
  4. package/esm/field-builder.js.map +1 -1
  5. package/esm/generator.js +4 -0
  6. package/esm/generator.js.map +1 -1
  7. package/esm/global-types.d.ts +23 -5
  8. package/esm/global-types.d.ts.map +1 -1
  9. package/esm/index.d.ts +4 -1
  10. package/esm/index.d.ts.map +1 -1
  11. package/esm/index.js +46 -1
  12. package/esm/index.js.map +1 -1
  13. package/esm/model-loader.d.ts +5 -6
  14. package/esm/model-loader.d.ts.map +1 -1
  15. package/esm/model-loader.js +12 -69
  16. package/esm/model-loader.js.map +1 -1
  17. package/esm/prisma-field-builder.d.ts +25 -6
  18. package/esm/prisma-field-builder.d.ts.map +1 -1
  19. package/esm/prisma-field-builder.js +86 -117
  20. package/esm/prisma-field-builder.js.map +1 -1
  21. package/esm/schema-builder.js +15 -4
  22. package/esm/schema-builder.js.map +1 -1
  23. package/esm/types.d.ts +73 -77
  24. package/esm/types.d.ts.map +1 -1
  25. package/esm/types.js +1 -0
  26. package/esm/types.js.map +1 -1
  27. package/esm/{cursors.d.ts → util/cursors.d.ts} +10 -8
  28. package/esm/util/cursors.d.ts.map +1 -0
  29. package/esm/{cursors.js → util/cursors.js} +34 -13
  30. package/esm/util/cursors.js.map +1 -0
  31. package/{lib/refs.d.ts → esm/util/datamodel.d.ts} +9 -8
  32. package/esm/util/datamodel.d.ts.map +1 -0
  33. package/esm/{refs.js → util/datamodel.js} +27 -30
  34. package/esm/util/datamodel.js.map +1 -0
  35. package/esm/util/deep-equal.d.ts +2 -0
  36. package/esm/util/deep-equal.d.ts.map +1 -0
  37. package/esm/util/deep-equal.js +39 -0
  38. package/esm/util/deep-equal.js.map +1 -0
  39. package/esm/util/loader-map.d.ts +6 -0
  40. package/esm/util/loader-map.d.ts.map +1 -0
  41. package/esm/{loader-map.js → util/loader-map.js} +10 -12
  42. package/esm/util/loader-map.js.map +1 -0
  43. package/esm/util/map-query.d.ts +6 -0
  44. package/esm/util/map-query.d.ts.map +1 -0
  45. package/esm/util/map-query.js +169 -0
  46. package/esm/util/map-query.js.map +1 -0
  47. package/esm/util/relation-map.d.ts +9 -0
  48. package/esm/util/relation-map.d.ts.map +1 -0
  49. package/esm/util/relation-map.js +20 -0
  50. package/esm/util/relation-map.js.map +1 -0
  51. package/esm/util/selections.d.ts +20 -0
  52. package/esm/util/selections.d.ts.map +1 -0
  53. package/esm/util/selections.js +139 -0
  54. package/esm/util/selections.js.map +1 -0
  55. package/lib/field-builder.js +16 -18
  56. package/lib/field-builder.js.map +1 -1
  57. package/lib/generator.js +4 -0
  58. package/lib/generator.js.map +1 -1
  59. package/lib/global-types.d.ts +23 -5
  60. package/lib/global-types.d.ts.map +1 -1
  61. package/lib/index.d.ts +4 -1
  62. package/lib/index.d.ts.map +1 -1
  63. package/lib/index.js +45 -0
  64. package/lib/index.js.map +1 -1
  65. package/lib/model-loader.d.ts +5 -6
  66. package/lib/model-loader.d.ts.map +1 -1
  67. package/lib/model-loader.js +13 -70
  68. package/lib/model-loader.js.map +1 -1
  69. package/lib/prisma-field-builder.d.ts +25 -6
  70. package/lib/prisma-field-builder.d.ts.map +1 -1
  71. package/lib/prisma-field-builder.js +90 -121
  72. package/lib/prisma-field-builder.js.map +1 -1
  73. package/lib/schema-builder.js +18 -7
  74. package/lib/schema-builder.js.map +1 -1
  75. package/lib/types.d.ts +73 -77
  76. package/lib/types.d.ts.map +1 -1
  77. package/lib/types.js +2 -0
  78. package/lib/types.js.map +1 -1
  79. package/lib/{cursors.d.ts → util/cursors.d.ts} +10 -8
  80. package/lib/util/cursors.d.ts.map +1 -0
  81. package/lib/{cursors.js → util/cursors.js} +39 -14
  82. package/lib/util/cursors.js.map +1 -0
  83. package/{esm/refs.d.ts → lib/util/datamodel.d.ts} +9 -8
  84. package/lib/util/datamodel.d.ts.map +1 -0
  85. package/lib/{refs.js → util/datamodel.js} +32 -35
  86. package/lib/util/datamodel.js.map +1 -0
  87. package/lib/util/deep-equal.d.ts +2 -0
  88. package/lib/util/deep-equal.d.ts.map +1 -0
  89. package/lib/util/deep-equal.js +43 -0
  90. package/lib/util/deep-equal.js.map +1 -0
  91. package/lib/util/loader-map.d.ts +6 -0
  92. package/lib/util/loader-map.d.ts.map +1 -0
  93. package/lib/{loader-map.js → util/loader-map.js} +10 -12
  94. package/lib/util/loader-map.js.map +1 -0
  95. package/lib/util/map-query.d.ts +6 -0
  96. package/lib/util/map-query.d.ts.map +1 -0
  97. package/lib/util/map-query.js +175 -0
  98. package/lib/util/map-query.js.map +1 -0
  99. package/lib/util/relation-map.d.ts +9 -0
  100. package/lib/util/relation-map.d.ts.map +1 -0
  101. package/lib/util/relation-map.js +24 -0
  102. package/lib/util/relation-map.js.map +1 -0
  103. package/lib/util/selections.d.ts +20 -0
  104. package/lib/util/selections.d.ts.map +1 -0
  105. package/lib/util/selections.js +148 -0
  106. package/lib/util/selections.js.map +1 -0
  107. package/package.json +7 -7
  108. package/src/field-builder.ts +12 -12
  109. package/src/generator.ts +18 -0
  110. package/src/global-types.ts +59 -12
  111. package/src/index.ts +75 -1
  112. package/src/model-loader.ts +19 -92
  113. package/src/prisma-field-builder.ts +199 -147
  114. package/src/schema-builder.ts +29 -7
  115. package/src/types.ts +138 -102
  116. package/src/{cursors.ts → util/cursors.ts} +45 -20
  117. package/src/{refs.ts → util/datamodel.ts} +36 -40
  118. package/src/util/deep-equal.ts +51 -0
  119. package/src/{loader-map.ts → util/loader-map.ts} +13 -13
  120. package/src/util/map-query.ts +327 -0
  121. package/src/util/relation-map.ts +36 -0
  122. package/src/util/selections.ts +192 -0
  123. package/.turbo/turbo-build.log +0 -17
  124. package/.turbo/turbo-test.log +0 -18
  125. package/.turbo/turbo-type.log +0 -5
  126. package/babel.config.js +0 -3
  127. package/esm/cursors.d.ts.map +0 -1
  128. package/esm/cursors.js.map +0 -1
  129. package/esm/loader-map.d.ts +0 -6
  130. package/esm/loader-map.d.ts.map +0 -1
  131. package/esm/loader-map.js.map +0 -1
  132. package/esm/refs.d.ts.map +0 -1
  133. package/esm/refs.js.map +0 -1
  134. package/esm/util/index.d.ts +0 -5
  135. package/esm/util/index.d.ts.map +0 -1
  136. package/esm/util/index.js +0 -16
  137. package/esm/util/index.js.map +0 -1
  138. package/esm/util/map-includes.d.ts +0 -6
  139. package/esm/util/map-includes.d.ts.map +0 -1
  140. package/esm/util/map-includes.js +0 -184
  141. package/esm/util/map-includes.js.map +0 -1
  142. package/esm/util/merge-includes.d.ts +0 -3
  143. package/esm/util/merge-includes.d.ts.map +0 -1
  144. package/esm/util/merge-includes.js +0 -91
  145. package/esm/util/merge-includes.js.map +0 -1
  146. package/lib/cursors.d.ts.map +0 -1
  147. package/lib/cursors.js.map +0 -1
  148. package/lib/loader-map.d.ts +0 -6
  149. package/lib/loader-map.d.ts.map +0 -1
  150. package/lib/loader-map.js.map +0 -1
  151. package/lib/refs.d.ts.map +0 -1
  152. package/lib/refs.js.map +0 -1
  153. package/lib/util/index.d.ts +0 -5
  154. package/lib/util/index.d.ts.map +0 -1
  155. package/lib/util/index.js +0 -30
  156. package/lib/util/index.js.map +0 -1
  157. package/lib/util/map-includes.d.ts +0 -6
  158. package/lib/util/map-includes.d.ts.map +0 -1
  159. package/lib/util/map-includes.js +0 -189
  160. package/lib/util/map-includes.js.map +0 -1
  161. package/lib/util/merge-includes.d.ts +0 -3
  162. package/lib/util/merge-includes.d.ts.map +0 -1
  163. package/lib/util/merge-includes.js +0 -96
  164. package/lib/util/merge-includes.js.map +0 -1
  165. package/src/util/index.ts +0 -26
  166. package/src/util/map-includes.ts +0 -328
  167. package/src/util/merge-includes.ts +0 -121
  168. package/tsconfig.tsbuildinfo +0 -1
@@ -0,0 +1,51 @@
1
+ /* eslint-disable no-continue */
2
+ export function deepEqual(left: unknown, right: unknown, ignore?: Set<string>) {
3
+ if (left === right) {
4
+ return true;
5
+ }
6
+
7
+ if (left && right && typeof left === 'object' && typeof right === 'object') {
8
+ if (Array.isArray(left)) {
9
+ if (!Array.isArray(right)) {
10
+ return false;
11
+ }
12
+
13
+ const { length } = left;
14
+
15
+ if (right.length !== length) {
16
+ return false;
17
+ }
18
+
19
+ for (let i = 0; i < length; i += 1) {
20
+ if (!deepEqual(left[i], right[i])) {
21
+ return false;
22
+ }
23
+ }
24
+
25
+ return true;
26
+ }
27
+
28
+ const keys = Object.keys(left);
29
+ const keyLength = keys.length;
30
+
31
+ if (keyLength !== Object.keys(right).length) {
32
+ return false;
33
+ }
34
+
35
+ for (const key of keys) {
36
+ if (ignore?.has(key)) {
37
+ continue;
38
+ }
39
+
40
+ if (
41
+ !deepEqual((left as Record<string, unknown>)[key], (right as Record<string, unknown>)[key])
42
+ ) {
43
+ return false;
44
+ }
45
+ }
46
+
47
+ return true;
48
+ }
49
+
50
+ return false;
51
+ }
@@ -1,10 +1,11 @@
1
- import { GraphQLResolveInfo } from 'graphql';
1
+ import { GraphQLNamedType, GraphQLResolveInfo } from 'graphql';
2
2
  import { createContextCache } from '@pothos/core';
3
- import { LoaderMappings } from './types';
3
+ import { LoaderMappings } from '../types';
4
+ import { getIndirectType } from './map-query';
4
5
 
5
6
  const cache = createContextCache((ctx) => new Map<string, LoaderMappings>());
6
7
 
7
- export function cacheKey(path: GraphQLResolveInfo['path'], subPath: string[]) {
8
+ export function cacheKey(type: string, path: GraphQLResolveInfo['path'], subPath: string[]) {
8
9
  let key = '';
9
10
  let current: GraphQLResolveInfo['path'] | undefined = path;
10
11
 
@@ -19,30 +20,29 @@ export function cacheKey(path: GraphQLResolveInfo['path'], subPath: string[]) {
19
20
  key = `${key}.${entry}`;
20
21
  }
21
22
 
22
- return key;
23
+ return `${type}@${key}`;
23
24
  }
24
25
 
25
26
  export function setLoaderMappings(
26
27
  ctx: object,
27
- path: GraphQLResolveInfo['path'],
28
+ info: GraphQLResolveInfo,
28
29
  value: LoaderMappings,
30
+ type: GraphQLNamedType,
29
31
  ) {
30
32
  Object.keys(value).forEach((field) => {
31
33
  const map = cache(ctx);
32
34
 
33
- for (const mapping of value[field]) {
34
- const selectionName = mapping.alias ?? mapping.field;
35
- const subPath = [...mapping.indirectPath, selectionName];
36
- const key = cacheKey(path, subPath);
35
+ const mapping = value[field];
36
+ const subPath = [...mapping.indirectPath, field];
37
+ const key = cacheKey(getIndirectType(type, info).name, info.path, subPath);
37
38
 
38
- map.set(key, mapping.mappings);
39
- }
39
+ map.set(key, mapping.mappings);
40
40
  });
41
41
  }
42
42
 
43
- export function getLoaderMapping(ctx: object, path: GraphQLResolveInfo['path']) {
43
+ export function getLoaderMapping(ctx: object, path: GraphQLResolveInfo['path'], type: string) {
44
44
  const map = cache(ctx);
45
- const key = cacheKey(path, []);
45
+ const key = cacheKey(type, path, []);
46
46
 
47
47
  return map.get(key) ?? null;
48
48
  }
@@ -0,0 +1,327 @@
1
+ /* eslint-disable no-param-reassign */
2
+ /* eslint-disable no-continue */
3
+ import {
4
+ FieldNode,
5
+ FragmentDefinitionNode,
6
+ getNamedType,
7
+ GraphQLNamedType,
8
+ GraphQLObjectType,
9
+ GraphQLResolveInfo,
10
+ InlineFragmentNode,
11
+ isObjectType,
12
+ Kind,
13
+ SelectionSetNode,
14
+ } from 'graphql';
15
+ import { getArgumentValues } from 'graphql/execution/values';
16
+ import { setLoaderMappings } from './loader-map';
17
+ import { FieldMap } from './relation-map';
18
+ import {
19
+ createState,
20
+ mergeSelection,
21
+ selectionCompatible,
22
+ SelectionState,
23
+ selectionToQuery,
24
+ } from './selections';
25
+
26
+ import { FieldSelection, IncludeMap, IndirectInclude, LoaderMappings, SelectionMap } from '..';
27
+
28
+ function addTypeSelectionsForField(
29
+ type: GraphQLNamedType,
30
+ context: object,
31
+ info: GraphQLResolveInfo,
32
+ state: SelectionState,
33
+ selection: FieldNode,
34
+ indirectPath: string[],
35
+ ) {
36
+ if (selection.name.value.startsWith('__')) {
37
+ return;
38
+ }
39
+
40
+ const {
41
+ pothosPrismaInclude,
42
+ pothosPrismaSelect,
43
+ pothosPrismaIndirectInclude,
44
+ pothosPrismaModel,
45
+ } = (type.extensions ?? {}) as {
46
+ pothosPrismaModel?: string;
47
+ pothosPrismaInclude?: IncludeMap;
48
+ pothosPrismaSelect?: IncludeMap;
49
+ pothosPrismaIndirectInclude?: IndirectInclude;
50
+ };
51
+
52
+ if (pothosPrismaIndirectInclude) {
53
+ resolveIndirectInclude(
54
+ type,
55
+ info,
56
+ selection,
57
+ pothosPrismaIndirectInclude.path,
58
+ indirectPath,
59
+ (resolvedType, field, path) => {
60
+ addTypeSelectionsForField(resolvedType, context, info, state, field, path);
61
+ },
62
+ );
63
+ }
64
+
65
+ if (!isObjectType(type)) {
66
+ return;
67
+ }
68
+
69
+ if (pothosPrismaModel && !pothosPrismaSelect) {
70
+ state.mode = 'include';
71
+ }
72
+
73
+ if (pothosPrismaInclude || pothosPrismaSelect) {
74
+ mergeSelection(state, {
75
+ select: pothosPrismaSelect ? { ...pothosPrismaSelect } : undefined,
76
+ include: pothosPrismaInclude ? { ...pothosPrismaInclude } : undefined,
77
+ });
78
+ }
79
+
80
+ if (selection.selectionSet) {
81
+ addNestedSelections(type, context, info, state, selection.selectionSet, indirectPath);
82
+ }
83
+ }
84
+
85
+ function resolveIndirectInclude(
86
+ type: GraphQLNamedType,
87
+ info: GraphQLResolveInfo,
88
+ selection: FieldNode | FragmentDefinitionNode | InlineFragmentNode,
89
+ includePath: IndirectInclude['path'],
90
+ path: string[],
91
+ resolve: (type: GraphQLNamedType, field: FieldNode, path: string[]) => void,
92
+ ) {
93
+ const [include, ...rest] = includePath;
94
+ if (!selection.selectionSet || !include) {
95
+ return;
96
+ }
97
+
98
+ for (const sel of selection.selectionSet.selections) {
99
+ switch (sel.kind) {
100
+ case Kind.FIELD:
101
+ if (sel.name.value === include.name && isObjectType(type)) {
102
+ const returnType = getNamedType(type.getFields()[sel.name.value].type);
103
+
104
+ if (rest.length === 0) {
105
+ resolve(returnType, sel, [...path, sel.alias?.value ?? sel.name.value]);
106
+ } else {
107
+ resolveIndirectInclude(
108
+ returnType,
109
+ info,
110
+ sel,
111
+ rest,
112
+ [...path, sel.alias?.value ?? sel.name.value],
113
+ resolve,
114
+ );
115
+ }
116
+ }
117
+ continue;
118
+ case Kind.FRAGMENT_SPREAD:
119
+ if (info.fragments[sel.name.value].typeCondition.name.value === include.type) {
120
+ resolveIndirectInclude(
121
+ info.schema.getType(include.type)!,
122
+ info,
123
+ info.fragments[sel.name.value],
124
+ includePath,
125
+ path,
126
+ resolve,
127
+ );
128
+ }
129
+
130
+ continue;
131
+
132
+ case Kind.INLINE_FRAGMENT:
133
+ if (!sel.typeCondition || sel.typeCondition.name.value === include.type) {
134
+ resolveIndirectInclude(
135
+ sel.typeCondition ? info.schema.getType(sel.typeCondition.name.value)! : type,
136
+ info,
137
+ sel,
138
+ includePath,
139
+ path,
140
+ resolve,
141
+ );
142
+ }
143
+
144
+ continue;
145
+
146
+ default:
147
+ throw new Error(`Unsupported selection kind ${(selection as { kind: string }).kind}`);
148
+ }
149
+ }
150
+ }
151
+
152
+ function addNestedSelections(
153
+ type: GraphQLObjectType,
154
+ context: object,
155
+ info: GraphQLResolveInfo,
156
+ state: SelectionState,
157
+ selections: SelectionSetNode,
158
+ indirectPath: string[],
159
+ ) {
160
+ for (const selection of selections.selections) {
161
+ switch (selection.kind) {
162
+ case Kind.FIELD:
163
+ addFieldSelection(type, context, info, state, selection, indirectPath);
164
+
165
+ continue;
166
+ case Kind.FRAGMENT_SPREAD:
167
+ if (info.fragments[selection.name.value].typeCondition.name.value !== type.name) {
168
+ continue;
169
+ }
170
+
171
+ addNestedSelections(
172
+ type,
173
+ context,
174
+ info,
175
+ state,
176
+ info.fragments[selection.name.value].selectionSet,
177
+ indirectPath,
178
+ );
179
+
180
+ continue;
181
+
182
+ case Kind.INLINE_FRAGMENT:
183
+ if (selection.typeCondition && selection.typeCondition.name.value !== type.name) {
184
+ continue;
185
+ }
186
+
187
+ addNestedSelections(type, context, info, state, selection.selectionSet, indirectPath);
188
+
189
+ continue;
190
+
191
+ default:
192
+ throw new Error(`Unsupported selection kind ${(selection as { kind: string }).kind}`);
193
+ }
194
+ }
195
+ }
196
+
197
+ function addFieldSelection(
198
+ type: GraphQLObjectType,
199
+ context: object,
200
+ info: GraphQLResolveInfo,
201
+ state: SelectionState,
202
+ selection: FieldNode,
203
+ indirectPath: string[],
204
+ ) {
205
+ if (selection.name.value.startsWith('__')) {
206
+ return;
207
+ }
208
+
209
+ const field = type.getFields()[selection.name.value];
210
+
211
+ if (!field) {
212
+ throw new Error(`Unknown field ${selection.name.value} on ${type.name}`);
213
+ }
214
+
215
+ const fieldSelect = field.extensions?.pothosPrismaSelect as FieldSelection | undefined;
216
+
217
+ let fieldSelectionMap: SelectionMap;
218
+
219
+ const fieldParentSelect = field.extensions?.pothosPrismaParentSelect as
220
+ | Record<string, SelectionMap | boolean>
221
+ | undefined;
222
+ let mappings: LoaderMappings = {};
223
+
224
+ if (typeof fieldSelect === 'function') {
225
+ const args = getArgumentValues(field, selection, info.variableValues) as Record<
226
+ string,
227
+ unknown
228
+ >;
229
+
230
+ fieldSelectionMap = fieldSelect(args, context, (rawQuery) => {
231
+ const returnType = getNamedType(field.type);
232
+ const query = typeof rawQuery === 'function' ? rawQuery(args, context) : rawQuery;
233
+
234
+ const fieldState = createStateForType(returnType, info, state);
235
+
236
+ if (typeof query === 'object' && Object.keys(query).length > 0) {
237
+ mergeSelection(fieldState, { select: {}, ...query });
238
+ }
239
+
240
+ addTypeSelectionsForField(returnType, context, info, fieldState, selection, []);
241
+
242
+ // eslint-disable-next-line prefer-destructuring
243
+ mappings = fieldState.mappings;
244
+
245
+ return selectionToQuery(fieldState);
246
+ });
247
+ } else {
248
+ fieldSelectionMap = { select: fieldSelect };
249
+ }
250
+
251
+ if (fieldSelect && selectionCompatible(state, fieldSelectionMap, true)) {
252
+ mergeSelection(state, fieldSelectionMap);
253
+ state.mappings[selection.alias?.value ?? selection.name.value] = {
254
+ field: selection.name.value,
255
+ mappings,
256
+ indirectPath,
257
+ };
258
+ } else if (
259
+ fieldParentSelect &&
260
+ state.parent &&
261
+ selectionCompatible(state.parent, { select: fieldParentSelect }, true)
262
+ ) {
263
+ mergeSelection(state.parent, { select: fieldParentSelect });
264
+ state.mappings[selection.alias?.value ?? selection.name.value] = {
265
+ field: selection.name.value,
266
+ mappings,
267
+ indirectPath,
268
+ };
269
+ }
270
+ }
271
+
272
+ export function queryFromInfo(context: object, info: GraphQLResolveInfo, typeName?: string): {} {
273
+ const type = typeName ? info.schema.getTypeMap()[typeName] : getNamedType(info.returnType);
274
+ const state = createStateForType(type, info);
275
+
276
+ addTypeSelectionsForField(type, context, info, state, info.fieldNodes[0], []);
277
+
278
+ setLoaderMappings(context, info, state.mappings, type);
279
+
280
+ return selectionToQuery(state);
281
+ }
282
+
283
+ export function selectionStateFromInfo(
284
+ context: object,
285
+ info: GraphQLResolveInfo,
286
+ typeName?: string,
287
+ ) {
288
+ const type = typeName ? info.schema.getTypeMap()[typeName] : info.parentType;
289
+
290
+ const state = createStateForType(type, info);
291
+
292
+ if (!isObjectType(type)) {
293
+ throw new Error('Prisma plugin can only resolve includes for object types');
294
+ }
295
+
296
+ addFieldSelection(type, context, info, state, info.fieldNodes[0], []);
297
+
298
+ return state;
299
+ }
300
+
301
+ function createStateForType(
302
+ type: GraphQLNamedType,
303
+ info: GraphQLResolveInfo,
304
+ parent?: SelectionState,
305
+ ) {
306
+ const targetType = getIndirectType(type, info);
307
+
308
+ const fieldMap = targetType.extensions.pothosPrismaFieldMap as FieldMap;
309
+
310
+ return createState(
311
+ fieldMap,
312
+ targetType.extensions.pothosPrismaSelect ? 'select' : 'include',
313
+ parent,
314
+ );
315
+ }
316
+
317
+ export function getIndirectType(type: GraphQLNamedType, info: GraphQLResolveInfo) {
318
+ let targetType = type;
319
+
320
+ while (targetType.extensions.pothosPrismaIndirectInclude) {
321
+ targetType = info.schema.getType(
322
+ (targetType.extensions.pothosPrismaIndirectInclude as IndirectInclude).getType(),
323
+ )!;
324
+ }
325
+
326
+ return targetType;
327
+ }
@@ -0,0 +1,36 @@
1
+ import { createContextCache } from '@pothos/core';
2
+ import { DMMF } from '@prisma/generator-helper';
3
+
4
+ export interface FieldMap {
5
+ model: string;
6
+ relations: Map<string, FieldMap>;
7
+ }
8
+
9
+ export type RelationMap = Map<string, FieldMap>;
10
+
11
+ export const getRelationMap = createContextCache((client: object) =>
12
+ createRelationMap(
13
+ // eslint-disable-next-line no-underscore-dangle
14
+ (client as unknown as { _dmmf: { datamodel: DMMF.Datamodel } })._dmmf.datamodel,
15
+ ),
16
+ );
17
+
18
+ export function createRelationMap({ models }: DMMF.Datamodel) {
19
+ const relationMap: RelationMap = new Map();
20
+
21
+ models.forEach((model) => {
22
+ relationMap.set(model.name, { model: model.name, relations: new Map() });
23
+ });
24
+
25
+ models.forEach((model) => {
26
+ const map = relationMap.get(model.name)!.relations;
27
+
28
+ model.fields.forEach((field) => {
29
+ if (field.kind === 'object' && relationMap.has(field.type)) {
30
+ map.set(field.name, relationMap.get(field.type)!);
31
+ }
32
+ });
33
+ });
34
+
35
+ return relationMap;
36
+ }
@@ -0,0 +1,192 @@
1
+ /* eslint-disable no-param-reassign */
2
+ /* eslint-disable no-underscore-dangle */
3
+ import { deepEqual } from './deep-equal';
4
+ import { FieldMap } from './relation-map';
5
+
6
+ import { LoaderMappings, SelectionMap } from '..';
7
+
8
+ export type SelectionMode = 'select' | 'include';
9
+
10
+ export interface SelectionState {
11
+ fieldMap: FieldMap;
12
+ query: object;
13
+ mode: SelectionMode;
14
+ fields: Set<string>;
15
+ counts: Set<string>;
16
+ relations: Map<string, SelectionState>;
17
+ mappings: LoaderMappings;
18
+ parent?: SelectionState;
19
+ }
20
+
21
+ export function selectionCompatible(
22
+ state: SelectionState,
23
+ selectionMap: SelectionMap | boolean,
24
+ ignoreQuery = false,
25
+ ): boolean {
26
+ if (typeof selectionMap === 'boolean') {
27
+ return ignoreQuery || !selectionMap || Object.keys(state.query).length === 0;
28
+ }
29
+
30
+ const { select, include, ...query } = selectionMap;
31
+
32
+ if (select && Object.keys(select).some((key) => compare(key, select[key]))) {
33
+ return false;
34
+ }
35
+
36
+ if (include && Object.keys(include).some((key) => compare(key, include[key]))) {
37
+ return false;
38
+ }
39
+
40
+ return ignoreQuery || deepEqual(state.query, query);
41
+
42
+ function compare(key: string, value: SelectionMap | boolean) {
43
+ return (
44
+ value &&
45
+ state.fieldMap.relations.has(key) &&
46
+ state.relations.has(key) &&
47
+ !selectionCompatible(state.relations.get(key)!, value)
48
+ );
49
+ }
50
+ }
51
+
52
+ export function stateCompatible(
53
+ state: SelectionState,
54
+ newState: SelectionState,
55
+ ignoreQuery = false,
56
+ ): boolean {
57
+ for (const [name, relationState] of newState.relations) {
58
+ if (state.relations.has(name) && !stateCompatible(state.relations.get(name)!, relationState)) {
59
+ return false;
60
+ }
61
+ }
62
+
63
+ return ignoreQuery || deepEqual(state.query, newState.query);
64
+ }
65
+
66
+ export function mergeState(state: SelectionState, newState: SelectionState) {
67
+ for (const [name, relationState] of newState.relations) {
68
+ if (state.relations.has(name)) {
69
+ mergeState(state.relations.get(name)!, relationState);
70
+ }
71
+ }
72
+
73
+ if (newState.mode === 'include') {
74
+ state.mode = 'include';
75
+ } else {
76
+ for (const name of newState.fields) {
77
+ state.fields.add(name);
78
+ }
79
+ }
80
+ }
81
+
82
+ export function createState(
83
+ fieldMap: FieldMap,
84
+ mode: SelectionMode,
85
+ parent?: SelectionState,
86
+ ): SelectionState {
87
+ return {
88
+ parent,
89
+ mode,
90
+ fieldMap,
91
+ query: {},
92
+ fields: new Set(),
93
+ counts: new Set(),
94
+ relations: new Map(),
95
+ mappings: {},
96
+ };
97
+ }
98
+
99
+ export function mergeSelection(state: SelectionState, { select, include, ...query }: SelectionMap) {
100
+ if (state.mode === 'select' && !select) {
101
+ state.mode = 'include';
102
+ }
103
+
104
+ if (include) {
105
+ Object.keys(include).forEach((key) => {
106
+ merge(key, include[key]);
107
+ });
108
+ }
109
+
110
+ if (select) {
111
+ Object.keys(select).forEach((key) => {
112
+ merge(key, select[key]);
113
+ });
114
+ }
115
+
116
+ if (Object.keys(query).length > 0) {
117
+ state.query = query;
118
+ }
119
+
120
+ function merge(key: string, value: SelectionMap | boolean) {
121
+ if (!value) {
122
+ return;
123
+ }
124
+
125
+ if (key === '_count') {
126
+ const counts = (value as { select?: {} }).select ?? {};
127
+ Object.keys(counts).forEach((count) => {
128
+ state.counts.add(count);
129
+ });
130
+
131
+ return;
132
+ }
133
+
134
+ const selection = value === true ? { include: {} } : value;
135
+ const childMap = state.fieldMap.relations.get(key);
136
+
137
+ if (childMap) {
138
+ if (state.relations.has(key)) {
139
+ mergeSelection(state.relations.get(key)!, selection);
140
+ } else {
141
+ const relatedState = createState(childMap, 'select');
142
+ mergeSelection(relatedState, selection);
143
+ state.relations.set(key, relatedState);
144
+ }
145
+ } else {
146
+ state.fields.add(key);
147
+ }
148
+ }
149
+ }
150
+
151
+ export function selectionToQuery(state: SelectionState): SelectionMap {
152
+ const nestedIncludes: Record<string, SelectionMap | boolean> = {};
153
+ const counts: Record<string, boolean> = {};
154
+
155
+ let hasSelection = false;
156
+
157
+ state.relations.forEach((sel, relation) => {
158
+ hasSelection = true;
159
+ const nested = selectionToQuery(sel);
160
+ nestedIncludes[relation] = Object.keys(nested).length > 0 ? nested : true;
161
+ });
162
+
163
+ if (state.counts.size > 0) {
164
+ hasSelection = true;
165
+ for (const count of state.counts) {
166
+ counts[count] = true;
167
+ }
168
+
169
+ nestedIncludes._count = {
170
+ select: counts,
171
+ };
172
+ }
173
+
174
+ if (state.mode === 'select') {
175
+ state.fields.forEach((field) => {
176
+ hasSelection = true;
177
+ nestedIncludes[field] = true;
178
+ });
179
+
180
+ return {
181
+ ...(state.query as SelectionMap),
182
+ select: nestedIncludes,
183
+ };
184
+ }
185
+
186
+ return hasSelection
187
+ ? {
188
+ ...state.query,
189
+ include: nestedIncludes,
190
+ }
191
+ : (state.query as SelectionMap);
192
+ }
@@ -1,17 +0,0 @@
1
- @pothos/plugin-prisma:build: cache hit, replaying output 9c93fd074a680c48
2
- @pothos/plugin-prisma:build:
3
- @pothos/plugin-prisma:build: > @pothos/plugin-prisma@0.17.1 build /home/runner/work/pothos/pothos/packages/plugin-prisma
4
- @pothos/plugin-prisma:build: > pnpm build:cjs && pnpm build:esm
5
- @pothos/plugin-prisma:build:
6
- @pothos/plugin-prisma:build:
7
- @pothos/plugin-prisma:build: > @pothos/plugin-prisma@0.17.1 build:cjs /home/runner/work/pothos/pothos/packages/plugin-prisma
8
- @pothos/plugin-prisma:build: > tsc --module commonjs --outDir lib
9
- @pothos/plugin-prisma:build:
10
- @pothos/plugin-prisma:build:
11
- @pothos/plugin-prisma:build: > @pothos/plugin-prisma@0.17.1 build:esm /home/runner/work/pothos/pothos/packages/plugin-prisma
12
- @pothos/plugin-prisma:build: > tsc --module es2020 --outDir esm && pnpm esm:extensions
13
- @pothos/plugin-prisma:build:
14
- @pothos/plugin-prisma:build:
15
- @pothos/plugin-prisma:build: > @pothos/plugin-prisma@0.17.1 esm:extensions /home/runner/work/pothos/pothos/packages/plugin-prisma
16
- @pothos/plugin-prisma:build: > ts-node --compiler-options "{\"module\":\"commonjs\"}" ../../.config/esm-transformer.ts
17
- @pothos/plugin-prisma:build:
@@ -1,18 +0,0 @@
1
- @pothos/plugin-prisma:test: cache hit, replaying output 1f0f9df2409eabae
2
- @pothos/plugin-prisma:test:
3
- @pothos/plugin-prisma:test: > @pothos/plugin-prisma@0.17.1 test /home/runner/work/pothos/pothos/packages/plugin-prisma
4
- @pothos/plugin-prisma:test: > pnpm jest --runInBand
5
- @pothos/plugin-prisma:test:
6
- @pothos/plugin-prisma:test: PASS tests/connections.test.ts (27.557 s)
7
- @pothos/plugin-prisma:test: PASS tests/index.test.ts
8
- @pothos/plugin-prisma:test: PASS tests/counts.test.ts
9
- @pothos/plugin-prisma:test: PASS tests/nodes.test.ts
10
- @pothos/plugin-prisma:test: PASS tests/relation-query.test.ts
11
- @pothos/plugin-prisma:test: PASS tests/variants.test.ts
12
- @pothos/plugin-prisma:test: PASS tests/type-include.test.ts
13
- @pothos/plugin-prisma:test:
14
- @pothos/plugin-prisma:test: Test Suites: 7 passed, 7 total
15
- @pothos/plugin-prisma:test: Tests: 36 passed, 36 total
16
- @pothos/plugin-prisma:test: Snapshots: 71 passed, 71 total
17
- @pothos/plugin-prisma:test: Time: 50.169 s
18
- @pothos/plugin-prisma:test: Ran all test suites.
@@ -1,5 +0,0 @@
1
- @pothos/plugin-prisma:type: cache hit, replaying output ebb014e08a51cd14
2
- @pothos/plugin-prisma:type:
3
- @pothos/plugin-prisma:type: > @pothos/plugin-prisma@0.17.1 type /home/runner/work/pothos/pothos/packages/plugin-prisma
4
- @pothos/plugin-prisma:type: > tsc --noEmit && tsc --project tests/tsconfig.json
5
- @pothos/plugin-prisma:type: