@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
@@ -1,11 +1,12 @@
1
- /* eslint-disable prefer-destructuring */
2
- /* eslint-disable no-underscore-dangle */
3
1
  import { createContextCache, SchemaTypes } from '@pothos/core';
4
- import { getDelegateFromModel, getFindUniqueForRef, getRefFromModel } from './refs';
5
2
  import { PrismaDelegate } from './types';
6
- import { mergeIncludes } from './util';
7
-
8
- const loaderCache = new WeakMap<object, (model: object) => ModelLoader>();
3
+ import { getDelegateFromModel } from './util/datamodel';
4
+ import {
5
+ mergeSelection,
6
+ selectionCompatible,
7
+ SelectionState,
8
+ selectionToQuery,
9
+ } from './util/selections';
9
10
 
10
11
  export class ModelLoader {
11
12
  model: object;
@@ -14,7 +15,7 @@ export class ModelLoader {
14
15
 
15
16
  staged = new Set<{
16
17
  promise: Promise<Record<string, unknown>>;
17
- include: Record<string, unknown>;
18
+ state: SelectionState;
18
19
  }>();
19
20
 
20
21
  constructor(
@@ -27,105 +28,31 @@ export class ModelLoader {
27
28
  this.findUnique = findUnique;
28
29
  }
29
30
 
30
- static forModel<Types extends SchemaTypes>(
31
+ static forRef<Types extends SchemaTypes>(
31
32
  modelName: string,
33
+ findUnique: (args: unknown, ctx: {}) => unknown,
32
34
  builder: PothosSchemaTypes.SchemaBuilder<Types>,
33
35
  ) {
34
36
  const delegate = getDelegateFromModel(builder.options.prisma.client, modelName);
35
37
 
36
- if (!loaderCache.has(delegate)) {
37
- const ref = getRefFromModel(modelName, builder);
38
-
39
- const findUnique = getFindUniqueForRef(ref, builder);
40
- loaderCache.set(
41
- delegate,
42
- createContextCache((model) => new ModelLoader(model, delegate, findUnique!)),
43
- );
44
- }
45
-
46
- return loaderCache.get(delegate)!;
38
+ return createContextCache((model) => new ModelLoader(model, delegate, findUnique));
47
39
  }
48
40
 
49
- async loadCount(relation: string, context: {}): Promise<number> {
50
- let promise;
51
- const entry = [...this.staged][0];
52
-
53
- if (entry) {
54
- if (!entry.include._count) {
55
- entry.include._count = { select: {} };
56
- }
57
-
58
- (entry.include._count as { select: Record<string, boolean> }).select[relation] = true;
59
- promise = entry.promise;
60
- } else {
61
- promise = this.initLoad(relation, null, context, true);
62
- }
63
-
64
- const result = await promise;
41
+ async loadSelection(selection: SelectionState, context: object) {
42
+ const query = selectionToQuery(selection);
65
43
 
66
- return (result._count as Record<string, number>)[relation];
67
- }
68
-
69
- async loadSelf(include: unknown, context: {}) {
70
44
  for (const entry of this.staged) {
71
- const merged = mergeIncludes(entry.include, include as Record<string, unknown>);
72
-
73
- if (merged) {
74
- entry.include = merged as Record<string, unknown>;
45
+ if (selectionCompatible(entry.state, query)) {
46
+ mergeSelection(entry.state, query);
75
47
 
76
48
  return entry.promise;
77
49
  }
78
50
  }
79
51
 
80
- return this.initLoad(null, include, context);
52
+ return this.initLoad(selection, context);
81
53
  }
82
54
 
83
- async loadRelation(relation: string, include: unknown, context: {}) {
84
- let promise;
85
- for (const entry of this.staged) {
86
- if (entry.include[relation] === undefined) {
87
- promise = entry.promise;
88
- entry.include[relation] = include;
89
-
90
- break;
91
- }
92
-
93
- const merged = mergeIncludes(
94
- entry.include[relation] as Record<string, unknown>,
95
- include as Record<string, unknown>,
96
- );
97
-
98
- if (merged) {
99
- entry.include[relation] = merged;
100
- break;
101
- }
102
- }
103
-
104
- if (!promise) {
105
- promise = this.initLoad(relation, include, context);
106
- }
107
-
108
- const result = await promise;
109
-
110
- return result[relation];
111
- }
112
-
113
- async initLoad(relation: string | null, includeArg: unknown, context: {}, count = false) {
114
- const include: Record<string, unknown> =
115
- (relation &&
116
- (count
117
- ? {
118
- _count: {
119
- select: {
120
- [relation]: true,
121
- },
122
- },
123
- }
124
- : {
125
- [relation]: includeArg,
126
- })) ||
127
- {};
128
-
55
+ async initLoad(state: SelectionState, context: {}) {
129
56
  const promise = new Promise<Record<string, unknown>>((resolve, reject) => {
130
57
  setTimeout(() => {
131
58
  this.staged.delete(entry);
@@ -133,8 +60,8 @@ export class ModelLoader {
133
60
  resolve(
134
61
  this.delegate.findUnique({
135
62
  rejectOnNotFound: true,
63
+ ...selectionToQuery(state),
136
64
  where: { ...(this.findUnique(this.model, context) as {}) },
137
- include: Object.keys(include).length > 0 ? include : undefined,
138
65
  } as never) as Promise<Record<string, unknown>>,
139
66
  );
140
67
  }, 0);
@@ -142,7 +69,7 @@ export class ModelLoader {
142
69
 
143
70
  const entry = {
144
71
  promise,
145
- include,
72
+ state,
146
73
  };
147
74
 
148
75
  this.staged.add(entry);
@@ -1,39 +1,66 @@
1
1
  /* eslint-disable no-underscore-dangle */
2
2
  import { GraphQLResolveInfo } from 'graphql';
3
3
  import {
4
+ CompatibleTypes,
5
+ FieldKind,
4
6
  FieldRef,
5
7
  InputFieldMap,
6
8
  MaybePromise,
7
9
  NormalizeArgs,
8
- ObjectFieldBuilder,
9
10
  ObjectRef,
10
11
  PluginName,
12
+ RootFieldBuilder,
11
13
  SchemaTypes,
14
+ TypeParam,
12
15
  } from '@pothos/core';
13
- import { prismaCursorConnectionQuery, wrapConnectionResult } from './cursors';
14
- import { getLoaderMapping, setLoaderMappings } from './loader-map';
15
- import { ModelLoader } from './model-loader';
16
16
  import { PrismaObjectRef } from './object-ref';
17
- import { getDelegateFromModel, getFindUniqueForRef, getRefFromModel, getRelation } from './refs';
18
17
  import {
19
- PrismaDelegate,
20
18
  PrismaModelTypes,
21
19
  RelatedConnectionOptions,
22
20
  RelatedFieldOptions,
23
21
  RelationCountOptions,
24
22
  ShapeFromConnection,
23
+ VariantFieldOptions,
25
24
  } from './types';
26
- import { queryFromInfo, SELF_RELATION } from './util';
27
- import { VariantFieldOptions } from '.';
25
+ import { prismaCursorConnectionQuery, wrapConnectionResult } from './util/cursors';
26
+ import {
27
+ getCursorFormatter,
28
+ getCursorParser,
29
+ getRefFromModel,
30
+ getRelation,
31
+ } from './util/datamodel';
32
+ import { FieldMap } from './util/relation-map';
33
+
34
+ // Workaround for FieldKind not being extended on Builder classes
35
+ const RootBuilder: {
36
+ // eslint-disable-next-line @typescript-eslint/prefer-function-type
37
+ new <Types extends SchemaTypes, Shape, Kind extends FieldKind>(
38
+ name: string,
39
+ builder: PothosSchemaTypes.SchemaBuilder<Types>,
40
+ kind: FieldKind,
41
+ graphqlKind: PothosSchemaTypes.PothosKindToGraphQLType[FieldKind],
42
+ ): PothosSchemaTypes.RootFieldBuilder<Types, Shape, Kind>;
43
+ } = RootFieldBuilder as never;
28
44
 
29
45
  export class PrismaObjectFieldBuilder<
30
46
  Types extends SchemaTypes,
31
47
  Model extends PrismaModelTypes,
32
48
  NeedsResolve extends boolean,
33
49
  Shape extends object = Model['Shape'],
34
- > extends ObjectFieldBuilder<Types, Shape> {
35
- delegate: PrismaDelegate;
50
+ > extends RootBuilder<Types, Shape, 'PrismaObject'> {
36
51
  model: string;
52
+ prismaFieldMap: FieldMap;
53
+
54
+ exposeBoolean = this.createExpose('Boolean');
55
+ exposeFloat = this.createExpose('Float');
56
+ exposeInt = this.createExpose('Int');
57
+ exposeID = this.createExpose('ID');
58
+ exposeString = this.createExpose('String');
59
+ exposeBooleanList = this.createExpose(['Boolean']);
60
+ exposeFloatList = this.createExpose(['Float']);
61
+ exposeIntList = this.createExpose(['Int']);
62
+ exposeIDList = this.createExpose(['ID']);
63
+ exposeStringList = this.createExpose(['String']);
37
64
 
38
65
  relatedConnection: 'relay' extends PluginName
39
66
  ? <
@@ -90,22 +117,40 @@ export class PrismaObjectFieldBuilder<
90
117
  edgeOptions = {},
91
118
  ) {
92
119
  const relationField = getRelation(this.model, this.builder, name);
93
- const parentRef = getRefFromModel(this.model, this.builder);
94
120
  const ref = options.type ?? getRefFromModel(relationField.type, this.builder);
95
- const findUnique = getFindUniqueForRef(parentRef, this.builder);
96
- const loaderCache = ModelLoader.forModel(this.model, this.builder);
97
121
  let typeName: string | undefined;
98
122
 
123
+ const formatCursor = getCursorFormatter(relationField.type, this.builder, cursor);
124
+ const parseCursor = getCursorParser(relationField.type, this.builder, cursor);
125
+
99
126
  const getQuery = (args: PothosSchemaTypes.DefaultConnectionArguments, ctx: {}) => ({
100
127
  ...((typeof query === 'function' ? query(args, ctx) : query) as {}),
101
128
  ...prismaCursorConnectionQuery({
102
- column: cursor,
129
+ parseCursor,
103
130
  maxSize,
104
131
  defaultSize,
105
132
  args,
106
133
  }),
107
134
  });
108
135
 
136
+ const relationSelect = (
137
+ args: object,
138
+ context: object,
139
+ nestedQuery: (query: unknown) => unknown,
140
+ ) => ({
141
+ select: {
142
+ [name]: nestedQuery({
143
+ ...((typeof query === 'function' ? query(args, context) : query) as {}),
144
+ ...prismaCursorConnectionQuery({
145
+ parseCursor,
146
+ maxSize,
147
+ defaultSize,
148
+ args,
149
+ }),
150
+ }),
151
+ },
152
+ });
153
+
109
154
  const fieldRef = (
110
155
  this as unknown as {
111
156
  connection: (...args: unknown[]) => FieldRef<unknown>;
@@ -115,52 +160,43 @@ export class PrismaObjectFieldBuilder<
115
160
  ...options,
116
161
  extensions: {
117
162
  ...extensions,
118
- pothosPrismaQuery: getQuery,
119
- pothosPrismaRelation: name,
163
+ pothosPrismaLoaded: (value: Record<string, unknown>) => value[name] !== undefined,
164
+ pothosPrismaFallback:
165
+ resolve &&
166
+ ((
167
+ q: { take: number },
168
+ parent: unknown,
169
+ args: PothosSchemaTypes.DefaultConnectionArguments,
170
+ context: {},
171
+ info: GraphQLResolveInfo,
172
+ ) =>
173
+ Promise.resolve(
174
+ resolve(
175
+ {
176
+ ...q,
177
+ ...(typeof query === 'function' ? query(args, context) : query),
178
+ } as never,
179
+ parent,
180
+ args,
181
+ context,
182
+ info,
183
+ ),
184
+ ).then((result) => wrapConnectionResult(result, args, q.take, formatCursor))),
120
185
  },
121
186
  type: ref,
122
- resolve: async (
123
- parent: object,
187
+ select: relationSelect as never,
188
+ resolve: (
189
+ parent: unknown,
124
190
  args: PothosSchemaTypes.DefaultConnectionArguments,
125
191
  context: {},
126
- info: GraphQLResolveInfo,
127
192
  ) => {
128
193
  const connectionQuery = getQuery(args, context);
129
- const getResult = () => {
130
- const mapping = getLoaderMapping(context, info.path);
131
- const loadedValue = (parent as Record<string, unknown>)[name];
132
-
133
- if (
134
- // if we attempted to load the relation, and its missing it will be null
135
- // undefined means that the query was not constructed in a way that requested the relation
136
- loadedValue !== undefined &&
137
- mapping
138
- ) {
139
- if (loadedValue !== null && loadedValue !== undefined) {
140
- setLoaderMappings(context, info.path, mapping);
141
- }
142
-
143
- return loadedValue as {}[];
144
- }
145
-
146
- if (!resolve && !findUnique) {
147
- throw new Error(`Missing findUnique for Prisma type ${this.model}`);
148
- }
149
-
150
- const mergedQuery = { ...queryFromInfo(context, info), ...connectionQuery };
151
-
152
- if (resolve) {
153
- return resolve(mergedQuery, parent, args, context, info);
154
- }
155
-
156
- return loaderCache(parent).loadRelation(name, mergedQuery, context) as Promise<{}[]>;
157
- };
158
194
 
159
195
  return wrapConnectionResult(
160
- await getResult(),
196
+ (parent as Record<string, never>)[name],
161
197
  args,
162
198
  connectionQuery.take,
163
- cursor,
199
+ formatCursor,
164
200
  (parent as { _count?: Record<string, number> })._count?.[name],
165
201
  );
166
202
  },
@@ -171,17 +207,9 @@ export class PrismaObjectFieldBuilder<
171
207
  ? (t: PothosSchemaTypes.ObjectFieldBuilder<SchemaTypes, { totalCount?: number }>) => ({
172
208
  totalCount: t.int({
173
209
  extensions: {
174
- pothosPrismaRelationCountForParent: name,
175
- },
176
- resolve: (parent, args, context) => {
177
- const loadedValue = parent.totalCount;
178
-
179
- if (loadedValue !== undefined) {
180
- return loadedValue;
181
- }
182
-
183
- return loaderCache(parent).loadCount(name, context);
210
+ pothosPrismaParentSelect: { _count: { select: { [name]: true } } },
184
211
  },
212
+ resolve: (parent, args, context) => parent.totalCount,
185
213
  }),
186
214
  ...(connectionOptions as { fields?: (t: unknown) => {} }).fields?.(t),
187
215
  })
@@ -205,11 +233,16 @@ export class PrismaObjectFieldBuilder<
205
233
  return fieldRef;
206
234
  } as never;
207
235
 
208
- constructor(name: string, builder: PothosSchemaTypes.SchemaBuilder<Types>, model: string) {
209
- super(name, builder);
236
+ constructor(
237
+ name: string,
238
+ builder: PothosSchemaTypes.SchemaBuilder<Types>,
239
+ model: string,
240
+ fieldMap: FieldMap,
241
+ ) {
242
+ super(name, builder, 'PrismaObject', 'Object');
210
243
 
211
244
  this.model = model;
212
- this.delegate = getDelegateFromModel(builder.options.prisma.client, model);
245
+ this.prismaFieldMap = fieldMap;
213
246
  }
214
247
 
215
248
  relation<
@@ -236,54 +269,35 @@ export class PrismaObjectFieldBuilder<
236
269
  ): FieldRef<Model['Relations'][Field]['Shape'], 'Object'> {
237
270
  const [name, options = {} as never] = allArgs;
238
271
  const relationField = getRelation(this.model, this.builder, name);
239
- const parentRef = getRefFromModel(this.model, this.builder);
240
272
  const ref = options.type ?? getRefFromModel(relationField.type, this.builder);
241
- const findUnique = getFindUniqueForRef(parentRef, this.builder);
242
- const loaderCache = ModelLoader.forModel(this.model, this.builder);
243
273
 
244
- const { query = {}, resolve, ...rest } = options;
274
+ const { query = {}, resolve, extensions, ...rest } = options;
275
+
276
+ const relationSelect = (
277
+ args: object,
278
+ context: object,
279
+ nestedQuery: (query: unknown) => unknown,
280
+ ) => ({ select: { [name]: nestedQuery(query) } });
245
281
 
246
282
  return this.field({
247
283
  ...rest,
248
284
  type: relationField.isList ? [ref] : ref,
249
285
  extensions: {
250
- ...options.extensions,
251
- pothosPrismaQuery: query,
252
- pothosPrismaRelation: name,
253
- },
254
- resolve: (parent, args, context, info) => {
255
- const mapping = getLoaderMapping(context, info.path);
256
-
257
- const loadedValue = (parent as Record<string, unknown>)[name];
258
-
259
- if (
260
- // if we attempted to load the relation, and its missing it will be null
261
- // undefined means that the query was not constructed in a way that requested the relation
262
- loadedValue !== undefined &&
263
- mapping
264
- ) {
265
- if (loadedValue !== null && loadedValue !== undefined) {
266
- setLoaderMappings(context, info.path, mapping);
267
- }
268
-
269
- return loadedValue as never;
270
- }
271
-
272
- const queryOptions = {
273
- ...((typeof query === 'function' ? query(args, context) : query) as {}),
274
- ...queryFromInfo(context, info),
275
- };
276
-
277
- if (resolve) {
278
- return resolve(queryOptions, parent, args as never, context, info) as never;
279
- }
280
-
281
- if (!findUnique) {
282
- throw new Error(`Missing findUnique for Prisma type ${this.model}`);
283
- }
284
-
285
- return loaderCache(parent).loadRelation(name, queryOptions, context) as never;
286
+ ...extensions,
287
+ pothosPrismaLoaded: (value: Record<string, unknown>) => value[name] !== undefined,
288
+ pothosPrismaFallback:
289
+ resolve &&
290
+ ((q: {}, parent: Shape, args: {}, context: {}, info: GraphQLResolveInfo) =>
291
+ resolve(
292
+ { ...q, ...(typeof query === 'function' ? query(args, context) : query) } as never,
293
+ parent,
294
+ args as never,
295
+ context,
296
+ info,
297
+ )),
286
298
  },
299
+ select: relationSelect as never,
300
+ resolve: (parent) => (parent as Record<string, never>)[name],
287
301
  }) as FieldRef<Model['Relations'][Field]['Shape'], 'Object'>;
288
302
  }
289
303
 
@@ -293,37 +307,22 @@ export class PrismaObjectFieldBuilder<
293
307
  >
294
308
  ): FieldRef<number, 'Object'> {
295
309
  const [name, options = {} as never] = allArgs;
296
- const parentRef = getRefFromModel(this.model, this.builder);
297
- const findUnique = getFindUniqueForRef(parentRef, this.builder);
298
- const loaderCache = ModelLoader.forModel(this.model, this.builder);
299
310
 
300
311
  const { resolve, ...rest } = options;
301
312
 
313
+ const countSelect = {
314
+ _count: {
315
+ select: { [name]: true },
316
+ },
317
+ };
318
+
302
319
  return this.field({
303
320
  ...rest,
304
321
  type: 'Int',
305
322
  nullable: false,
306
- extensions: {
307
- ...options.extensions,
308
- pothosPrismaRelationCount: name,
309
- },
310
- resolve: (parent, args, context, info) => {
311
- const loadedValue = (parent as { _count: Record<string, unknown> })._count?.[name];
312
-
313
- if (loadedValue !== undefined) {
314
- return loadedValue as never;
315
- }
316
-
317
- if (resolve) {
318
- return resolve(parent, args, context, info) as never;
319
- }
320
-
321
- if (!findUnique) {
322
- throw new Error(`Missing findUnique for Prisma type ${this.model}`);
323
- }
324
-
325
- return loaderCache(parent).loadCount(name, context) as never;
326
- },
323
+ select: countSelect as never,
324
+ resolve: (parent, args, context, info) =>
325
+ (parent as unknown as { _count: Record<string, never> })._count?.[name],
327
326
  }) as FieldRef<number, 'Object'>;
328
327
  }
329
328
 
@@ -342,34 +341,87 @@ export class PrismaObjectFieldBuilder<
342
341
  const [variant, options = {} as never] = allArgs;
343
342
  const ref: PrismaObjectRef<PrismaModelTypes> =
344
343
  typeof variant === 'string' ? getRefFromModel(variant, this.builder) : variant;
345
- const parentRef = getRefFromModel(this.model, this.builder);
346
- const findUnique = getFindUniqueForRef(parentRef, this.builder);
347
- const loaderCache = ModelLoader.forModel(this.model, this.builder);
344
+
345
+ const selfSelect = (args: object, context: object, nestedQuery: (query: unknown) => unknown) =>
346
+ nestedQuery({});
348
347
 
349
348
  return this.field({
350
349
  ...options,
351
350
  type: ref,
352
- extensions: {
353
- ...options.extensions,
354
- pothosPrismaRelation: SELF_RELATION,
355
- },
356
- resolve: (parent, args, context, info) => {
357
- const mapping = getLoaderMapping(context, info.path);
351
+ select: selfSelect as never,
352
+ resolve: (parent, args, context, info) => parent,
353
+ }) as FieldRef<Model['Shape'], 'Object'>;
354
+ }
358
355
 
359
- if (mapping) {
360
- setLoaderMappings(context, info.path, mapping);
356
+ expose<
357
+ Type extends TypeParam<Types>,
358
+ Nullable extends boolean,
359
+ ResolveReturnShape,
360
+ Name extends CompatibleTypes<Types, Model['Shape'], Type, Nullable>,
361
+ >(
362
+ ...args: NormalizeArgs<
363
+ [
364
+ name: Name,
365
+ options?: Omit<
366
+ PothosSchemaTypes.ObjectFieldOptions<
367
+ Types,
368
+ Shape,
369
+ Type,
370
+ Nullable,
371
+ {},
372
+ ResolveReturnShape
373
+ >,
374
+ 'resolve' | 'select'
375
+ >,
376
+ ]
377
+ >
378
+ ) {
379
+ const [name, options = {} as never] = args;
361
380
 
362
- return parent as never;
363
- }
381
+ const typeConfig = this.builder.configStore.getTypeConfig(this.typename, 'Object');
382
+ const usingSelect = !!typeConfig.extensions?.pothosPrismaSelect;
364
383
 
365
- const queryOptions = queryFromInfo(context, info);
384
+ return this.exposeField(name as never, {
385
+ ...options,
386
+ extensions: {
387
+ ...options.extensions,
388
+ pothosPrismaVariant: name,
389
+ pothosPrismaSelect: usingSelect && {
390
+ [name]: true,
391
+ },
392
+ },
393
+ });
394
+ }
366
395
 
367
- if (!findUnique) {
368
- throw new Error(`Missing findUnique for Prisma type ${this.model}`);
369
- }
396
+ private createExpose<Type extends TypeParam<Types>>(type: Type) {
397
+ return <
398
+ Nullable extends boolean,
399
+ ResolveReturnShape,
400
+ Name extends CompatibleTypes<Types, Model['Shape'], Type, Nullable>,
401
+ >(
402
+ ...args: NormalizeArgs<
403
+ [
404
+ name: Name,
405
+ options?: Omit<
406
+ PothosSchemaTypes.ObjectFieldOptions<
407
+ Types,
408
+ Shape,
409
+ Type,
410
+ Nullable,
411
+ {},
412
+ ResolveReturnShape
413
+ >,
414
+ 'resolve' | 'type' | 'select'
415
+ >,
416
+ ]
417
+ >
418
+ ) => {
419
+ const [name, options = {} as never] = args;
370
420
 
371
- return loaderCache(parent).loadSelf(queryOptions, context) as never;
372
- },
373
- }) as FieldRef<Model['Shape'], 'Object'>;
421
+ return this.expose(name as never, {
422
+ ...options,
423
+ type,
424
+ });
425
+ };
374
426
  }
375
427
  }