@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
@@ -10,21 +10,22 @@ import SchemaBuilder, {
10
10
  TypeParam,
11
11
  } from '@pothos/core';
12
12
  import { PrismaObjectFieldBuilder } from './field-builder';
13
+ import { ModelLoader } from './model-loader';
13
14
  import PrismaNodeRef from './node-ref';
14
- import { getDelegateFromModel, getRefFromModel, setFindUniqueForRef } from './refs';
15
- import { ModelTypes, PrismaDelegate, PrismaNodeOptions } from './types';
16
- import { queryFromInfo } from './util';
15
+ import { PrismaModelTypes, PrismaNodeOptions } from './types';
16
+ import { getDelegateFromModel, getRefFromModel } from './util/datamodel';
17
+ import { queryFromInfo } from './util/map-query';
18
+ import { getRelationMap } from './util/relation-map';
17
19
 
18
20
  const schemaBuilderProto = SchemaBuilder.prototype as PothosSchemaTypes.SchemaBuilder<SchemaTypes>;
19
21
 
20
22
  schemaBuilderProto.prismaObject = function prismaObject(type, { fields, findUnique, ...options }) {
21
23
  const ref = options.variant ? this.objectRef(options.variant) : getRefFromModel(type, this);
22
24
  const name = options.variant ?? options.name ?? type;
25
+ const fieldMap = getRelationMap(this.options.prisma.client).get(type)!;
23
26
 
24
27
  ref.name = name;
25
28
 
26
- setFindUniqueForRef(ref, this, findUnique);
27
-
28
29
  this.objectType(ref, {
29
30
  ...(options as {} as PothosSchemaTypes.ObjectFieldOptions<
30
31
  SchemaTypes,
@@ -37,9 +38,30 @@ schemaBuilderProto.prismaObject = function prismaObject(type, { fields, findUniq
37
38
  extensions: {
38
39
  ...options.extensions,
39
40
  pothosPrismaInclude: options.include,
41
+ pothosPrismaModel: type,
42
+ pothosPrismaFieldMap: fieldMap,
43
+ pothosPrismaSelect: options.select,
44
+ pothosPrismaLoader: ModelLoader.forRef(
45
+ type,
46
+ (findUnique as never) ||
47
+ (() => {
48
+ throw new Error(`Missing findUnique for ${ref.name}`);
49
+ }),
50
+ this,
51
+ ),
40
52
  },
41
53
  name,
42
- fields: fields ? () => fields(new PrismaObjectFieldBuilder(name, this, type)) : undefined,
54
+ fields: fields
55
+ ? () =>
56
+ fields(
57
+ new PrismaObjectFieldBuilder(
58
+ name,
59
+ this,
60
+ type,
61
+ getRelationMap(this.options.prisma.client).get(type)!,
62
+ ),
63
+ )
64
+ : undefined,
43
65
  });
44
66
 
45
67
  return ref as never;
@@ -55,7 +77,7 @@ schemaBuilderProto.prismaNode = function prismaNode(
55
77
  name,
56
78
  variant,
57
79
  ...options
58
- }: PrismaNodeOptions<SchemaTypes, ModelTypes<PrismaDelegate>, [], never, {}>,
80
+ }: PrismaNodeOptions<SchemaTypes, PrismaModelTypes, [], never, {}, {}>,
59
81
  ) {
60
82
  const interfaceRef = this.nodeInterfaceRef?.();
61
83
 
package/src/types.ts CHANGED
@@ -10,6 +10,7 @@ import {
10
10
  InterfaceParam,
11
11
  ListResolveValue,
12
12
  MaybePromise,
13
+ Normalize,
13
14
  ObjectRef,
14
15
  OutputShape,
15
16
  OutputType,
@@ -26,52 +27,17 @@ export interface PrismaDelegate {
26
27
  findUnique: (...args: any[]) => Promise<unknown>;
27
28
  }
28
29
 
29
- type RelationKeys<T> = {
30
- [K in keyof T]: T[K] extends (args: {}) => {
31
- then: (cb: (result: unknown) => unknown) => unknown;
32
- }
33
- ? K
34
- : never;
35
- }[keyof T];
36
-
37
- export type ModelTypes<Model extends {}> = Model extends {
38
- findUnique: (
39
- options: infer UniqueOptions & {
40
- where?: infer Where | null | undefined;
41
- select?: infer Select | null | undefined;
42
- } & (
43
- | {
44
- include?: infer Include | null | undefined;
45
- }
46
- | {}
47
- ),
48
- ) => infer Chain & {
49
- then: (cb: (result: infer Shape | null) => unknown) => unknown;
50
- };
51
- }
52
- ? PrismaModelTypes & {
53
- Shape: Shape;
54
- Include: unknown extends Include ? never : Include;
55
- Where: Where;
56
- Fields: keyof Select;
57
- ListRelation: ListRelationFields<Include> & string;
58
- Relations: {
59
- [RelationName in RelationKeys<Chain>]: Chain[RelationName] extends (args: {}) => {
60
- then: (cb: (result: infer Relation) => unknown) => unknown;
61
- }
62
- ? { Shape: Relation; Types: PrismaModelTypes }
63
- : never;
64
- };
65
- }
66
- : never;
30
+ export const prismaModelName = Symbol.for('Pothos.prismaModelName');
67
31
 
68
32
  export interface PrismaModelTypes {
69
33
  Name: string;
70
34
  Shape: {};
71
35
  Include: unknown;
36
+ Select: unknown;
72
37
  Where: {};
73
38
  Fields: string;
74
39
  ListRelations: string;
40
+ RelationName: string;
75
41
  Relations: Record<
76
42
  string,
77
43
  {
@@ -81,52 +47,86 @@ export interface PrismaModelTypes {
81
47
  >;
82
48
  }
83
49
 
84
- export type ListRelationFields<T> = {
85
- [K in keyof T]: T[K] extends infer Option
86
- ? Option extends { orderBy?: unknown }
87
- ? K
50
+ type ExtractModel<Types extends SchemaTypes, ParentShape> = ParentShape extends {
51
+ [prismaModelName]?: infer Name;
52
+ }
53
+ ? Types['PrismaTypes'][Name & keyof Types['PrismaTypes']] extends infer Model
54
+ ? Model extends PrismaModelTypes
55
+ ? Model
88
56
  : never
89
- : never;
90
- }[keyof T];
57
+ : never
58
+ : never;
91
59
 
92
- export type PrismaObjectFieldsShape<
60
+ export type PrismaObjectFieldOptions<
61
+ Types extends SchemaTypes,
62
+ ParentShape,
63
+ Type extends TypeParam<Types>,
64
+ Nullable extends FieldNullability<Type>,
65
+ Args extends InputFieldMap,
66
+ Select,
67
+ ResolveReturnShape,
68
+ > = PothosSchemaTypes.ObjectFieldOptions<
69
+ Types,
70
+ unknown extends Select
71
+ ? ParentShape
72
+ : ParentShape & ShapeFromSelection<ExtractModel<Types, ParentShape>, { select: Select }>,
73
+ Type,
74
+ Nullable,
75
+ Args,
76
+ ResolveReturnShape
77
+ > & {
78
+ select?: ExtractModel<Types, ParentShape>['Select'] & Select;
79
+ };
80
+
81
+ type PrismaObjectFieldsShape<
93
82
  Types extends SchemaTypes,
94
83
  Model extends PrismaModelTypes,
95
84
  NeedsResolve extends boolean,
96
85
  Shape extends object,
97
- > = (t: PrismaObjectFieldBuilder<Types, Model, NeedsResolve, Shape>) => FieldMap;
98
-
99
- export type ShapeFromInclude<Model extends PrismaModelTypes, Include> = {} extends Include
100
- ? Model['Shape']
101
- : {
102
- [K in keyof Include &
103
- keyof Model['Relations']]: Model['Relations'][K]['Shape'] extends infer RelationShape
104
- ? RelationShape extends (infer ItemShape)[]
105
- ? (ItemShape &
106
- (Include[K] extends { include?: infer NestedInclude & object }
107
- ? ShapeFromInclude<Model['Relations'][K]['Types'], NestedInclude>
108
- : {}))[]
109
- : RelationShape &
110
- (
111
- | (Include[K] extends { include?: infer NestedInclude & object }
112
- ? ShapeFromInclude<Model['Relations'][K]['Types'], NestedInclude>
113
- : {})
114
- | null
115
- )
116
- : never;
117
- };
86
+ Select,
87
+ > = Model['Select'] extends Select
88
+ ? (t: PrismaObjectFieldBuilder<Types, Model, NeedsResolve, Shape>) => FieldMap
89
+ : (t: PrismaSelectionFieldBuilder<Types, Model, Shape>) => FieldMap;
118
90
 
119
- export type ShapeWithInclude<
91
+ type PrismaSelectionFieldBuilder<
92
+ Types extends SchemaTypes,
120
93
  Model extends PrismaModelTypes,
121
- Include extends Model['Include'],
122
- > = Model['Shape'] & ([Include] extends [never] ? {} : ShapeFromInclude<Model, Include>);
94
+ Shape extends object,
95
+ > = PrismaObjectFieldBuilder<Types, Model, false, Shape>;
96
+
97
+ interface BaseSelection {
98
+ include?: unknown;
99
+ select?: unknown;
100
+ }
101
+
102
+ export type SelectedKeys<T> = { [K in keyof T]: T[K] extends false ? never : K }[keyof T];
103
+
104
+ export type ShapeFromSelection<Model extends PrismaModelTypes, Selection> = Normalize<
105
+ Selection extends BaseSelection
106
+ ? unknown extends Selection['select']
107
+ ? Model['Shape'] & RelationShapeFromInclude<Model, Selection['include']>
108
+ : Pick<Model['Shape'], SelectedKeys<Selection['select']>> &
109
+ RelationShapeFromInclude<Model, Selection['select']>
110
+ : Model['Shape']
111
+ >;
112
+
113
+ type RelationShapeFromInclude<Model extends PrismaModelTypes, Include> = Normalize<{
114
+ [K in SelectedKeys<Include> as K extends Model['RelationName']
115
+ ? K
116
+ : never]: K extends keyof Model['Relations']
117
+ ? Model['Relations'][K]['Shape'] extends unknown[]
118
+ ? ShapeFromSelection<Model['Relations'][K]['Types'], Include[K]>[]
119
+ : ShapeFromSelection<Model['Relations'][K]['Types'], Include[K]>
120
+ : unknown;
121
+ }>;
123
122
 
124
123
  export type PrismaObjectTypeOptions<
125
124
  Types extends SchemaTypes,
126
125
  Model extends PrismaModelTypes,
127
126
  Interfaces extends InterfaceParam<Types>[],
128
127
  FindUnique,
129
- Include extends Model['Include'],
128
+ Include,
129
+ Select,
130
130
  Shape extends object,
131
131
  > = NameOrVariant &
132
132
  Omit<
@@ -134,13 +134,28 @@ export type PrismaObjectTypeOptions<
134
134
  | PothosSchemaTypes.ObjectTypeWithInterfaceOptions<Types, Shape, Interfaces>,
135
135
  'fields'
136
136
  > & {
137
- include?: Include;
138
- fields?: PrismaObjectFieldsShape<Types, Model, FindUnique extends null ? true : false, Shape>;
139
- findUnique: FindUnique &
140
- (((parent: Shape, context: Types['Context']) => Model['Where']) | null);
141
- };
137
+ fields?: PrismaObjectFieldsShape<
138
+ Types,
139
+ Model,
140
+ FindUnique extends null ? true : false,
141
+ Shape & (FindUnique extends null ? {} : { [prismaModelName]?: Model['Name'] }),
142
+ Select
143
+ >;
144
+ } & (
145
+ | {
146
+ include?: Include & Model['Include'];
147
+ select?: never;
148
+ findUnique: FindUnique &
149
+ (((parent: Shape, context: Types['Context']) => Model['Where']) | null);
150
+ }
151
+ | {
152
+ select: Model['Select'] & Select;
153
+ include?: never;
154
+ findUnique: (parent: Shape, context: Types['Context']) => Model['Where'];
155
+ }
156
+ );
142
157
 
143
- export type NameOrVariant =
158
+ type NameOrVariant =
144
159
  | {
145
160
  name?: never;
146
161
  variant?: string;
@@ -154,7 +169,8 @@ export type PrismaNodeOptions<
154
169
  Types extends SchemaTypes,
155
170
  Model extends PrismaModelTypes,
156
171
  Interfaces extends InterfaceParam<Types>[],
157
- Include extends Model['Include'],
172
+ Include,
173
+ Select,
158
174
  Shape extends object,
159
175
  > = NameOrVariant &
160
176
  Omit<
@@ -162,7 +178,6 @@ export type PrismaNodeOptions<
162
178
  | PothosSchemaTypes.ObjectTypeWithInterfaceOptions<Types, Shape, Interfaces>,
163
179
  'fields' | 'isTypeOf'
164
180
  > & {
165
- include?: Include;
166
181
  id: Omit<
167
182
  FieldOptionsFromKind<
168
183
  Types,
@@ -178,11 +193,26 @@ export type PrismaNodeOptions<
178
193
  > & {
179
194
  resolve: (parent: Shape, context: Types['Context']) => MaybePromise<OutputShape<Types, 'ID'>>;
180
195
  };
181
- fields?: PrismaObjectFieldsShape<Types, Model, false, Shape>;
196
+ fields?: PrismaObjectFieldsShape<
197
+ Types,
198
+ Model,
199
+ false,
200
+ Shape & { [prismaModelName]?: Model['Name'] },
201
+ Select
202
+ >;
182
203
  findUnique: (id: string, context: Types['Context']) => Model['Where'];
183
- };
204
+ } & (
205
+ | {
206
+ include?: Include & Model['Include'];
207
+ select?: never;
208
+ }
209
+ | {
210
+ select: Model['Select'] & Select;
211
+ include?: never;
212
+ }
213
+ );
184
214
 
185
- export type QueryForField<
215
+ type QueryForField<
186
216
  Types extends SchemaTypes,
187
217
  Args extends InputFieldMap,
188
218
  Include,
@@ -195,18 +225,19 @@ export type QueryForField<
195
225
  ) => Omit<Include, 'include' | 'select'>)
196
226
  : never;
197
227
 
198
- export type IncludeFromRelation<
228
+ type QueryFromRelation<
199
229
  Model extends PrismaModelTypes,
200
230
  Field extends keyof Model['Include'],
201
231
  > = Model['Include'][Field] extends infer Include
202
232
  ? Include extends {
203
- include?: infer T;
233
+ include?: infer I;
234
+ select?: infer S;
204
235
  }
205
- ? NonNullable<T>
236
+ ? { include?: NonNullable<I>; select?: NonNullable<S> }
206
237
  : never
207
238
  : never;
208
239
 
209
- export type CursorFromRelation<
240
+ type CursorFromRelation<
210
241
  Model extends PrismaModelTypes,
211
242
  Field extends Model['ListRelations'],
212
243
  > = Field extends keyof Model['Include']
@@ -217,7 +248,7 @@ export type CursorFromRelation<
217
248
  : never
218
249
  : never;
219
250
 
220
- export type RefForRelation<
251
+ type RefForRelation<
221
252
  Model extends PrismaModelTypes,
222
253
  Field extends keyof Model['Relations'],
223
254
  > = Model['Relations'][Field] extends unknown[]
@@ -247,7 +278,7 @@ export type RelatedFieldOptions<
247
278
  (NeedsResolve extends false
248
279
  ? {
249
280
  resolve?: (
250
- query: { include?: IncludeFromRelation<Model, Field & keyof Model['Include']> },
281
+ query: QueryFromRelation<Model, Field & keyof Model['Include']>,
251
282
  parent: Shape,
252
283
  args: InputShapeFromFields<Args>,
253
284
  context: Types['Context'],
@@ -258,7 +289,7 @@ export type RelatedFieldOptions<
258
289
  }
259
290
  : {
260
291
  resolve: (
261
- query: { include?: IncludeFromRelation<Model, Field & keyof Model['Include']> },
292
+ query: QueryFromRelation<Model, Field & keyof Model['Include']>,
262
293
  parent: Shape,
263
294
  args: InputShapeFromFields<Args>,
264
295
  context: Types['Context'],
@@ -472,31 +503,36 @@ export type RelatedConnectionOptions<
472
503
 
473
504
  export type WithBrand<T> = T & { [typeBrandKey]: string };
474
505
 
475
- export type IncludeMap = Record<string, Record<string, unknown> | boolean>;
506
+ export type IncludeMap = Record<string, SelectionMap | boolean>;
476
507
 
477
- export interface IncludeCounts {
478
- current: Record<string, boolean>;
479
- parent: Record<string, boolean>;
508
+ export interface SelectionMap {
509
+ select?: Record<string, SelectionMap | boolean>;
510
+ include?: Record<string, SelectionMap | boolean>;
511
+ where?: {};
480
512
  }
481
513
 
514
+ export type FieldSelection =
515
+ | Record<string, SelectionMap | boolean>
516
+ | ((
517
+ args: object,
518
+ context: object,
519
+ query: (
520
+ selection: SelectionMap | boolean | ((args: object, context: object) => SelectionMap),
521
+ ) => SelectionMap | boolean,
522
+ ) => SelectionMap);
523
+
482
524
  export type LoaderMappings = Record<
483
525
  string,
484
526
  {
485
527
  field: string;
486
- alias?: string;
487
528
  mappings: LoaderMappings;
488
529
  indirectPath: string[];
489
- }[]
530
+ }
490
531
  >;
491
532
 
492
- export interface SubFieldInclude {
493
- type?: string;
494
- name: string;
495
- }
496
-
497
- export interface IndirectLoadMap {
498
- subFields: SubFieldInclude[];
499
- path: string[];
533
+ export interface IndirectInclude {
534
+ getType: () => string;
535
+ path: { type?: string; name: string }[];
500
536
  }
501
537
 
502
538
  export type ShapeFromConnection<T> = T extends { shape: unknown } ? T['shape'] : never;
@@ -4,22 +4,34 @@ import { MaybePromise } from '@pothos/core';
4
4
  const DEFAULT_MAX_SIZE = 100;
5
5
  const DEFAULT_SIZE = 20;
6
6
 
7
- function formatCursor(value: unknown): string {
7
+ export function formatCursorChunk(value: unknown) {
8
8
  if (value instanceof Date) {
9
- return Buffer.from(`GPC:D:${String(Number(value))}`).toString('base64');
9
+ return `D:${String(Number(value))}`;
10
10
  }
11
11
 
12
12
  switch (typeof value) {
13
13
  case 'number':
14
- return Buffer.from(`GPC:N:${value}`).toString('base64');
14
+ return `N:${value}`;
15
15
  case 'string':
16
- return Buffer.from(`GPC:S:${value}`).toString('base64');
16
+ return `S:${value}`;
17
17
  default:
18
18
  throw new TypeError(`Unsupported cursor type ${typeof value}`);
19
19
  }
20
20
  }
21
21
 
22
- function parseCursor(cursor: unknown) {
22
+ export function formatCursor(fields: string | string[]) {
23
+ return (value: Record<string, unknown>) => {
24
+ if (typeof fields === 'string') {
25
+ return Buffer.from(`GPC:${formatCursorChunk(value[fields])}`).toString('base64');
26
+ }
27
+
28
+ return Buffer.from(`GPC:J:${JSON.stringify(fields.map((name) => value[name]))}`).toString(
29
+ 'base64',
30
+ );
31
+ };
32
+ }
33
+
34
+ export function parseRawCursor(cursor: unknown) {
23
35
  if (typeof cursor !== 'string') {
24
36
  throw new TypeError('Cursor must be a string');
25
37
  }
@@ -35,6 +47,8 @@ function parseCursor(cursor: unknown) {
35
47
  return Number.parseInt(value, 10);
36
48
  case 'D':
37
49
  return new Date(Number.parseInt(value, 10));
50
+ case 'J':
51
+ return JSON.parse(value) as unknown;
38
52
  default:
39
53
  throw new TypeError(`Invalid cursor type ${type}`);
40
54
  }
@@ -43,11 +57,29 @@ function parseCursor(cursor: unknown) {
43
57
  }
44
58
  }
45
59
 
46
- interface PrismaCursorConnectionQueryOptions {
60
+ export function parseCompositeCursor(fields: string[]) {
61
+ return (cursor: string) => {
62
+ const parsed = parseRawCursor(cursor) as unknown[];
63
+
64
+ if (!Array.isArray(parsed)) {
65
+ throw new TypeError(`Expected compound cursor to contain an array, but got ${parsed}`);
66
+ }
67
+
68
+ const record: Record<string, unknown> = {};
69
+
70
+ fields.forEach((field, i) => {
71
+ record[field] = parsed[i];
72
+ });
73
+
74
+ return record;
75
+ };
76
+ }
77
+
78
+ export interface PrismaCursorConnectionQueryOptions {
47
79
  args: PothosSchemaTypes.DefaultConnectionArguments;
48
80
  defaultSize?: number;
49
81
  maxSize?: number;
50
- column: string;
82
+ parseCursor: (cursor: string) => Record<string, unknown>;
51
83
  }
52
84
 
53
85
  interface ResolvePrismaCursorConnectionOptions extends PrismaCursorConnectionQueryOptions {
@@ -59,7 +91,7 @@ export function prismaCursorConnectionQuery({
59
91
  args: { before, after, first, last },
60
92
  maxSize = DEFAULT_MAX_SIZE,
61
93
  defaultSize = DEFAULT_SIZE,
62
- column,
94
+ parseCursor,
63
95
  }: PrismaCursorConnectionQueryOptions) {
64
96
  if (first != null && first < 0) {
65
97
  throw new TypeError('Argument "first" must be a non-negative integer');
@@ -96,9 +128,7 @@ export function prismaCursorConnectionQuery({
96
128
  return cursor == null
97
129
  ? { take, skip: 0 }
98
130
  : {
99
- cursor: {
100
- [column]: parseCursor(cursor),
101
- },
131
+ cursor: parseCursor(cursor),
102
132
  take,
103
133
  skip: 1,
104
134
  };
@@ -108,7 +138,7 @@ export function wrapConnectionResult<T extends {}>(
108
138
  results: T[],
109
139
  args: PothosSchemaTypes.DefaultConnectionArguments,
110
140
  take: number,
111
- column: string,
141
+ cursor: (node: T) => string,
112
142
  totalCount?: number,
113
143
  ) {
114
144
  const gotFullResults = results.length === Math.abs(take);
@@ -122,7 +152,7 @@ export function wrapConnectionResult<T extends {}>(
122
152
  value == null
123
153
  ? null
124
154
  : {
125
- cursor: formatCursor((value as Record<string, string>)[column]),
155
+ cursor: cursor(value),
126
156
  node: value,
127
157
  },
128
158
  );
@@ -141,6 +171,7 @@ export function wrapConnectionResult<T extends {}>(
141
171
 
142
172
  export async function resolvePrismaCursorConnection<T extends {}>(
143
173
  options: ResolvePrismaCursorConnectionOptions,
174
+ cursor: (node: T) => string,
144
175
  resolve: (query: { include?: {}; cursor?: {}; take: number; skip: number }) => MaybePromise<T[]>,
145
176
  ) {
146
177
  const query = prismaCursorConnectionQuery(options);
@@ -149,11 +180,5 @@ export async function resolvePrismaCursorConnection<T extends {}>(
149
180
  ...query,
150
181
  });
151
182
 
152
- return wrapConnectionResult(
153
- results,
154
- options.args,
155
- query.take,
156
- options.column,
157
- options.totalCount,
158
- );
183
+ return wrapConnectionResult(results, options.args, query.take, cursor, options.totalCount);
159
184
  }
@@ -1,8 +1,8 @@
1
1
  import { ObjectRef, SchemaTypes } from '@pothos/core';
2
- import { Prisma } from '../tests/client';
3
- import { PrismaObjectRef } from './object-ref';
4
- import { PrismaDelegate } from './types';
5
- import { PrismaModelTypes } from '.';
2
+ import { Prisma } from '../../tests/client';
3
+ import { PrismaObjectRef } from '../object-ref';
4
+ import { PrismaDelegate, PrismaModelTypes } from '../types';
5
+ import { formatCursor, parseCompositeCursor, parseRawCursor } from './cursors';
6
6
 
7
7
  export const refMap = new WeakMap<object, Map<string, PrismaObjectRef<PrismaModelTypes>>>();
8
8
  export const findUniqueMap = new WeakMap<
@@ -31,40 +31,29 @@ export function getRefFromModel<Types extends SchemaTypes>(
31
31
  return cache.get(name)!;
32
32
  }
33
33
 
34
- export function getFindUniqueForRef<Types extends SchemaTypes>(
35
- ref: ObjectRef<unknown>,
34
+ export function getRelation<Types extends SchemaTypes>(
35
+ name: string,
36
36
  builder: PothosSchemaTypes.SchemaBuilder<Types>,
37
+ relation: string,
37
38
  ) {
38
- if (!findUniqueMap.has(builder)) {
39
- findUniqueMap.set(builder, new Map());
40
- }
41
- const cache = findUniqueMap.get(builder)!;
39
+ const modelData = getModel(name, builder);
42
40
 
43
- if (!cache.has(ref)) {
44
- return null;
45
- }
41
+ const fieldData = modelData.fields.find((field) => field.name === relation);
46
42
 
47
- return cache.get(ref)! as (args: unknown, context: Types['Context']) => unknown;
48
- }
43
+ if (!fieldData) {
44
+ throw new Error(`Field '${relation}' not found in model '${name}'`);
45
+ }
49
46
 
50
- export function setFindUniqueForRef<Types extends SchemaTypes>(
51
- ref: ObjectRef<unknown>,
52
- builder: PothosSchemaTypes.SchemaBuilder<Types>,
53
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
54
- findUnique: ((args: any, context: Types['Context']) => unknown) | null,
55
- ) {
56
- if (!findUniqueMap.has(builder)) {
57
- findUniqueMap.set(builder, new Map());
47
+ if (fieldData.kind !== 'object') {
48
+ throw new Error(`Field ${relation} of model '${name}' is not a relation (${fieldData.kind})`);
58
49
  }
59
- const cache = findUniqueMap.get(builder)!;
60
50
 
61
- cache.set(ref, findUnique);
51
+ return fieldData;
62
52
  }
63
53
 
64
- export function getRelation<Types extends SchemaTypes>(
54
+ export function getModel<Types extends SchemaTypes>(
65
55
  name: string,
66
56
  builder: PothosSchemaTypes.SchemaBuilder<Types>,
67
- relation: string,
68
57
  ) {
69
58
  const { client } = builder.options.prisma;
70
59
  // eslint-disable-next-line no-underscore-dangle
@@ -76,27 +65,34 @@ export function getRelation<Types extends SchemaTypes>(
76
65
  throw new Error(`Model '${name}' not found in DMMF`);
77
66
  }
78
67
 
79
- const fieldData = modelData.fields.find((field) => field.name === relation);
80
-
81
- if (!fieldData) {
82
- throw new Error(`Field '${relation}' not found in model '${name}'`);
83
- }
68
+ return modelData;
69
+ }
84
70
 
85
- if (fieldData.kind !== 'object') {
86
- throw new Error(`Field ${relation} of model '${name}' is not a relation (${fieldData.kind})`);
87
- }
71
+ export function getCursorFormatter<Types extends SchemaTypes>(
72
+ name: string,
73
+ builder: PothosSchemaTypes.SchemaBuilder<Types>,
74
+ cursor: string,
75
+ ) {
76
+ const modelData = getModel(name, builder);
77
+ const primaryKey = modelData.primaryKey?.name ?? modelData.primaryKey?.fields.join('_');
88
78
 
89
- return fieldData;
79
+ return formatCursor(cursor === primaryKey ? modelData.primaryKey!.fields : cursor);
90
80
  }
91
81
 
92
- export function getRelatedDelegate<Types extends SchemaTypes>(
82
+ export function getCursorParser<Types extends SchemaTypes>(
93
83
  name: string,
94
84
  builder: PothosSchemaTypes.SchemaBuilder<Types>,
95
- relation: string,
85
+ cursor: string,
96
86
  ) {
97
- const fieldData = getRelation(name, builder, relation);
87
+ const modelData = getModel(name, builder);
88
+ const primaryKey = modelData.primaryKey?.name ?? modelData.primaryKey?.fields.join('_');
89
+
90
+ const parser =
91
+ cursor === primaryKey ? parseCompositeCursor(modelData.primaryKey!.fields) : parseRawCursor;
98
92
 
99
- return fieldData.type;
93
+ return (rawCursor: string) => ({
94
+ [cursor]: parser(rawCursor),
95
+ });
100
96
  }
101
97
 
102
98
  export function getDelegateFromModel(client: Record<string, unknown>, model: string) {