@pothos/plugin-prisma 0.17.2 → 3.0.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 (163) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +403 -308
  3. package/esm/field-builder.js +11 -13
  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 +18 -78
  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 +88 -119
  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 +20 -18
  56. package/lib/field-builder.js.map +1 -1
  57. package/lib/generator.js +9 -1
  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 +50 -1
  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 +19 -79
  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 +92 -123
  72. package/lib/prisma-field-builder.js.map +1 -1
  73. package/lib/schema-builder.js +23 -8
  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 +8 -8
  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 +27 -106
  113. package/src/prisma-field-builder.ts +199 -147
  114. package/src/schema-builder.ts +29 -7
  115. package/src/types.ts +149 -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/esm/cursors.d.ts.map +0 -1
  124. package/esm/cursors.js.map +0 -1
  125. package/esm/loader-map.d.ts +0 -6
  126. package/esm/loader-map.d.ts.map +0 -1
  127. package/esm/loader-map.js.map +0 -1
  128. package/esm/refs.d.ts.map +0 -1
  129. package/esm/refs.js.map +0 -1
  130. package/esm/util/index.d.ts +0 -5
  131. package/esm/util/index.d.ts.map +0 -1
  132. package/esm/util/index.js +0 -16
  133. package/esm/util/index.js.map +0 -1
  134. package/esm/util/map-includes.d.ts +0 -6
  135. package/esm/util/map-includes.d.ts.map +0 -1
  136. package/esm/util/map-includes.js +0 -184
  137. package/esm/util/map-includes.js.map +0 -1
  138. package/esm/util/merge-includes.d.ts +0 -3
  139. package/esm/util/merge-includes.d.ts.map +0 -1
  140. package/esm/util/merge-includes.js +0 -91
  141. package/esm/util/merge-includes.js.map +0 -1
  142. package/lib/cursors.d.ts.map +0 -1
  143. package/lib/cursors.js.map +0 -1
  144. package/lib/loader-map.d.ts +0 -6
  145. package/lib/loader-map.d.ts.map +0 -1
  146. package/lib/loader-map.js.map +0 -1
  147. package/lib/refs.d.ts.map +0 -1
  148. package/lib/refs.js.map +0 -1
  149. package/lib/util/index.d.ts +0 -5
  150. package/lib/util/index.d.ts.map +0 -1
  151. package/lib/util/index.js +0 -30
  152. package/lib/util/index.js.map +0 -1
  153. package/lib/util/map-includes.d.ts +0 -6
  154. package/lib/util/map-includes.d.ts.map +0 -1
  155. package/lib/util/map-includes.js +0 -189
  156. package/lib/util/map-includes.js.map +0 -1
  157. package/lib/util/merge-includes.d.ts +0 -3
  158. package/lib/util/merge-includes.d.ts.map +0 -1
  159. package/lib/util/merge-includes.js +0 -96
  160. package/lib/util/merge-includes.js.map +0 -1
  161. package/src/util/index.ts +0 -26
  162. package/src/util/map-includes.ts +0 -328
  163. package/src/util/merge-includes.ts +0 -121
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,97 @@ 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 &
73
+ ShapeFromSelection<
74
+ ExtractModel<Types, ParentShape>,
75
+ { select: Select extends (...args: any[]) => infer S ? S : Select }
76
+ >,
77
+ Type,
78
+ Nullable,
79
+ Args,
80
+ ResolveReturnShape
81
+ > & {
82
+ select?: Select &
83
+ (
84
+ | ExtractModel<Types, ParentShape>['Select']
85
+ | ((
86
+ args: InputShapeFromFields<Args>,
87
+ ctx: Types['Context'],
88
+ ) => ExtractModel<Types, ParentShape>['Select'])
89
+ );
90
+ };
91
+
92
+ type PrismaObjectFieldsShape<
93
93
  Types extends SchemaTypes,
94
94
  Model extends PrismaModelTypes,
95
95
  NeedsResolve extends boolean,
96
96
  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
- };
97
+ Select,
98
+ > = Model['Select'] extends Select
99
+ ? (t: PrismaObjectFieldBuilder<Types, Model, NeedsResolve, Shape>) => FieldMap
100
+ : (t: PrismaSelectionFieldBuilder<Types, Model, Shape>) => FieldMap;
118
101
 
119
- export type ShapeWithInclude<
102
+ type PrismaSelectionFieldBuilder<
103
+ Types extends SchemaTypes,
120
104
  Model extends PrismaModelTypes,
121
- Include extends Model['Include'],
122
- > = Model['Shape'] & ([Include] extends [never] ? {} : ShapeFromInclude<Model, Include>);
105
+ Shape extends object,
106
+ > = PrismaObjectFieldBuilder<Types, Model, false, Shape>;
107
+
108
+ interface BaseSelection {
109
+ include?: unknown;
110
+ select?: unknown;
111
+ }
112
+
113
+ export type SelectedKeys<T> = { [K in keyof T]: T[K] extends false ? never : K }[keyof T];
114
+
115
+ export type ShapeFromSelection<Model extends PrismaModelTypes, Selection> = Normalize<
116
+ Selection extends BaseSelection
117
+ ? unknown extends Selection['select']
118
+ ? Model['Shape'] & RelationShapeFromInclude<Model, Selection['include']>
119
+ : Pick<Model['Shape'], SelectedKeys<Selection['select']>> &
120
+ RelationShapeFromInclude<Model, Selection['select']>
121
+ : Model['Shape']
122
+ >;
123
+
124
+ type RelationShapeFromInclude<Model extends PrismaModelTypes, Include> = Normalize<{
125
+ [K in SelectedKeys<Include> as K extends Model['RelationName']
126
+ ? K
127
+ : never]: K extends keyof Model['Relations']
128
+ ? Model['Relations'][K]['Shape'] extends unknown[]
129
+ ? ShapeFromSelection<Model['Relations'][K]['Types'], Include[K]>[]
130
+ : ShapeFromSelection<Model['Relations'][K]['Types'], Include[K]>
131
+ : unknown;
132
+ }>;
123
133
 
124
134
  export type PrismaObjectTypeOptions<
125
135
  Types extends SchemaTypes,
126
136
  Model extends PrismaModelTypes,
127
137
  Interfaces extends InterfaceParam<Types>[],
128
138
  FindUnique,
129
- Include extends Model['Include'],
139
+ Include,
140
+ Select,
130
141
  Shape extends object,
131
142
  > = NameOrVariant &
132
143
  Omit<
@@ -134,13 +145,28 @@ export type PrismaObjectTypeOptions<
134
145
  | PothosSchemaTypes.ObjectTypeWithInterfaceOptions<Types, Shape, Interfaces>,
135
146
  'fields'
136
147
  > & {
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
- };
148
+ fields?: PrismaObjectFieldsShape<
149
+ Types,
150
+ Model,
151
+ FindUnique extends null ? true : false,
152
+ Shape & (FindUnique extends null ? {} : { [prismaModelName]?: Model['Name'] }),
153
+ Select
154
+ >;
155
+ } & (
156
+ | {
157
+ include?: Include & Model['Include'];
158
+ select?: never;
159
+ findUnique: FindUnique &
160
+ (((parent: Shape, context: Types['Context']) => Model['Where']) | null);
161
+ }
162
+ | {
163
+ select: Model['Select'] & Select;
164
+ include?: never;
165
+ findUnique: (parent: Shape, context: Types['Context']) => Model['Where'];
166
+ }
167
+ );
142
168
 
143
- export type NameOrVariant =
169
+ type NameOrVariant =
144
170
  | {
145
171
  name?: never;
146
172
  variant?: string;
@@ -154,7 +180,8 @@ export type PrismaNodeOptions<
154
180
  Types extends SchemaTypes,
155
181
  Model extends PrismaModelTypes,
156
182
  Interfaces extends InterfaceParam<Types>[],
157
- Include extends Model['Include'],
183
+ Include,
184
+ Select,
158
185
  Shape extends object,
159
186
  > = NameOrVariant &
160
187
  Omit<
@@ -162,7 +189,6 @@ export type PrismaNodeOptions<
162
189
  | PothosSchemaTypes.ObjectTypeWithInterfaceOptions<Types, Shape, Interfaces>,
163
190
  'fields' | 'isTypeOf'
164
191
  > & {
165
- include?: Include;
166
192
  id: Omit<
167
193
  FieldOptionsFromKind<
168
194
  Types,
@@ -178,11 +204,26 @@ export type PrismaNodeOptions<
178
204
  > & {
179
205
  resolve: (parent: Shape, context: Types['Context']) => MaybePromise<OutputShape<Types, 'ID'>>;
180
206
  };
181
- fields?: PrismaObjectFieldsShape<Types, Model, false, Shape>;
207
+ fields?: PrismaObjectFieldsShape<
208
+ Types,
209
+ Model,
210
+ false,
211
+ Shape & { [prismaModelName]?: Model['Name'] },
212
+ Select
213
+ >;
182
214
  findUnique: (id: string, context: Types['Context']) => Model['Where'];
183
- };
215
+ } & (
216
+ | {
217
+ include?: Include & Model['Include'];
218
+ select?: never;
219
+ }
220
+ | {
221
+ select: Model['Select'] & Select;
222
+ include?: never;
223
+ }
224
+ );
184
225
 
185
- export type QueryForField<
226
+ type QueryForField<
186
227
  Types extends SchemaTypes,
187
228
  Args extends InputFieldMap,
188
229
  Include,
@@ -195,18 +236,19 @@ export type QueryForField<
195
236
  ) => Omit<Include, 'include' | 'select'>)
196
237
  : never;
197
238
 
198
- export type IncludeFromRelation<
239
+ type QueryFromRelation<
199
240
  Model extends PrismaModelTypes,
200
241
  Field extends keyof Model['Include'],
201
242
  > = Model['Include'][Field] extends infer Include
202
243
  ? Include extends {
203
- include?: infer T;
244
+ include?: infer I;
245
+ select?: infer S;
204
246
  }
205
- ? NonNullable<T>
247
+ ? { include?: NonNullable<I>; select?: NonNullable<S> }
206
248
  : never
207
249
  : never;
208
250
 
209
- export type CursorFromRelation<
251
+ type CursorFromRelation<
210
252
  Model extends PrismaModelTypes,
211
253
  Field extends Model['ListRelations'],
212
254
  > = Field extends keyof Model['Include']
@@ -217,7 +259,7 @@ export type CursorFromRelation<
217
259
  : never
218
260
  : never;
219
261
 
220
- export type RefForRelation<
262
+ type RefForRelation<
221
263
  Model extends PrismaModelTypes,
222
264
  Field extends keyof Model['Relations'],
223
265
  > = Model['Relations'][Field] extends unknown[]
@@ -247,7 +289,7 @@ export type RelatedFieldOptions<
247
289
  (NeedsResolve extends false
248
290
  ? {
249
291
  resolve?: (
250
- query: { include?: IncludeFromRelation<Model, Field & keyof Model['Include']> },
292
+ query: QueryFromRelation<Model, Field & keyof Model['Include']>,
251
293
  parent: Shape,
252
294
  args: InputShapeFromFields<Args>,
253
295
  context: Types['Context'],
@@ -258,7 +300,7 @@ export type RelatedFieldOptions<
258
300
  }
259
301
  : {
260
302
  resolve: (
261
- query: { include?: IncludeFromRelation<Model, Field & keyof Model['Include']> },
303
+ query: QueryFromRelation<Model, Field & keyof Model['Include']>,
262
304
  parent: Shape,
263
305
  args: InputShapeFromFields<Args>,
264
306
  context: Types['Context'],
@@ -472,31 +514,36 @@ export type RelatedConnectionOptions<
472
514
 
473
515
  export type WithBrand<T> = T & { [typeBrandKey]: string };
474
516
 
475
- export type IncludeMap = Record<string, Record<string, unknown> | boolean>;
517
+ export type IncludeMap = Record<string, SelectionMap | boolean>;
476
518
 
477
- export interface IncludeCounts {
478
- current: Record<string, boolean>;
479
- parent: Record<string, boolean>;
519
+ export interface SelectionMap {
520
+ select?: Record<string, SelectionMap | boolean>;
521
+ include?: Record<string, SelectionMap | boolean>;
522
+ where?: {};
480
523
  }
481
524
 
525
+ export type FieldSelection =
526
+ | Record<string, SelectionMap | boolean>
527
+ | ((
528
+ args: object,
529
+ context: object,
530
+ query: (
531
+ selection: SelectionMap | boolean | ((args: object, context: object) => SelectionMap),
532
+ ) => SelectionMap | boolean,
533
+ ) => SelectionMap);
534
+
482
535
  export type LoaderMappings = Record<
483
536
  string,
484
537
  {
485
538
  field: string;
486
- alias?: string;
487
539
  mappings: LoaderMappings;
488
540
  indirectPath: string[];
489
- }[]
541
+ }
490
542
  >;
491
543
 
492
- export interface SubFieldInclude {
493
- type?: string;
494
- name: string;
495
- }
496
-
497
- export interface IndirectLoadMap {
498
- subFields: SubFieldInclude[];
499
- path: string[];
544
+ export interface IndirectInclude {
545
+ getType: () => string;
546
+ path: { type?: string; name: string }[];
500
547
  }
501
548
 
502
549
  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) {
@@ -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
+ }