@pothos/plugin-prisma 3.12.1 → 3.13.2

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 (82) hide show
  1. package/CHANGELOG.md +27 -2
  2. package/README.md +10 -16
  3. package/dts/field-builder.d.ts.map +1 -1
  4. package/dts/global-types.d.ts +7 -0
  5. package/dts/global-types.d.ts.map +1 -1
  6. package/dts/index.d.ts +4 -2
  7. package/dts/index.d.ts.map +1 -1
  8. package/dts/model-loader.d.ts +1 -0
  9. package/dts/model-loader.d.ts.map +1 -1
  10. package/dts/prisma-field-builder.d.ts +9 -0
  11. package/dts/prisma-field-builder.d.ts.map +1 -1
  12. package/dts/types.d.ts +3 -2
  13. package/dts/types.d.ts.map +1 -1
  14. package/dts/util/cursors.d.ts +1 -1
  15. package/dts/util/cursors.d.ts.map +1 -1
  16. package/dts/util/datamodel.d.ts.map +1 -1
  17. package/dts/util/map-query.d.ts +2 -1
  18. package/dts/util/map-query.d.ts.map +1 -1
  19. package/esm/field-builder.js +5 -1
  20. package/esm/field-builder.js.map +1 -1
  21. package/esm/global-types.js.map +1 -1
  22. package/esm/index.js +2 -1
  23. package/esm/index.js.map +1 -1
  24. package/esm/model-loader.js +30 -0
  25. package/esm/model-loader.js.map +1 -1
  26. package/esm/prisma-field-builder.js +53 -26
  27. package/esm/prisma-field-builder.js.map +1 -1
  28. package/esm/schema-builder.js +7 -2
  29. package/esm/schema-builder.js.map +1 -1
  30. package/esm/types.js.map +1 -1
  31. package/esm/util/cursors.js.map +1 -1
  32. package/esm/util/datamodel.js +18 -2
  33. package/esm/util/datamodel.js.map +1 -1
  34. package/esm/util/map-query.js +4 -1
  35. package/esm/util/map-query.js.map +1 -1
  36. package/lib/field-builder.js +25 -59
  37. package/lib/field-builder.js.map +1 -1
  38. package/lib/generator.js +20 -17
  39. package/lib/generator.js.map +1 -1
  40. package/lib/index.js +42 -36
  41. package/lib/index.js.map +1 -1
  42. package/lib/model-loader.js +46 -13
  43. package/lib/model-loader.js.map +1 -1
  44. package/lib/node-ref.js +8 -6
  45. package/lib/node-ref.js.map +1 -1
  46. package/lib/object-ref.js +11 -4
  47. package/lib/object-ref.js.map +1 -1
  48. package/lib/prisma-field-builder.js +71 -41
  49. package/lib/prisma-field-builder.js.map +1 -1
  50. package/lib/schema-builder.js +37 -29
  51. package/lib/schema-builder.js.map +1 -1
  52. package/lib/types.js +4 -2
  53. package/lib/types.js.map +1 -1
  54. package/lib/util/cursors.js +22 -14
  55. package/lib/util/cursors.js.map +1 -1
  56. package/lib/util/datamodel.js +38 -16
  57. package/lib/util/datamodel.js.map +1 -1
  58. package/lib/util/deep-equal.js +4 -1
  59. package/lib/util/deep-equal.js.map +1 -1
  60. package/lib/util/get-client.js +12 -4
  61. package/lib/util/get-client.js.map +1 -1
  62. package/lib/util/loader-map.js +13 -5
  63. package/lib/util/loader-map.js.map +1 -1
  64. package/lib/util/map-query.js +39 -28
  65. package/lib/util/map-query.js.map +1 -1
  66. package/lib/util/relation-map.js +12 -5
  67. package/lib/util/relation-map.js.map +1 -1
  68. package/lib/util/selections.js +17 -9
  69. package/lib/util/selections.js.map +1 -1
  70. package/package.json +7 -7
  71. package/src/field-builder.ts +3 -1
  72. package/src/global-types.ts +16 -0
  73. package/src/index.ts +17 -1
  74. package/src/model-loader.ts +36 -0
  75. package/src/prisma-field-builder.ts +110 -22
  76. package/src/schema-builder.ts +11 -5
  77. package/src/types.ts +6 -3
  78. package/src/util/cursors.ts +1 -1
  79. package/src/util/datamodel.ts +21 -3
  80. package/src/util/map-query.ts +10 -1
  81. package/tsconfig.type.json +3 -3
  82. package/tsconfig.type.tsbuildinfo +1 -1
@@ -144,6 +144,35 @@ export class ModelLoader {
144
144
  return result;
145
145
  }
146
146
 
147
+ static getCursorSelection<Types extends SchemaTypes>(
148
+ ref: ObjectRef<unknown>,
149
+ modelName: string,
150
+ cursor: string,
151
+ builder: PothosSchemaTypes.SchemaBuilder<Types>,
152
+ ): Record<string, boolean> {
153
+ const model = getModel(modelName, builder);
154
+ const field = model.fields.find((field) => field.name === cursor);
155
+
156
+ if (field) {
157
+ return { [field.name]: true };
158
+ }
159
+ const index = [model.primaryKey, ...model.uniqueIndexes]
160
+ .filter(Boolean)
161
+ .find((idx) => (idx!.name ?? idx!.fields.join('_')) === cursor);
162
+
163
+ if (!index) {
164
+ throw new Error(`Can't find "${cursor}" field or index for ${ref.name}`);
165
+ }
166
+
167
+ const selection: Record<string, boolean> = {};
168
+
169
+ for (const column of index.fields) {
170
+ selection[column] = true;
171
+ }
172
+
173
+ return selection;
174
+ }
175
+
147
176
  static getFindUniqueForField<Types extends SchemaTypes>(
148
177
  ref: ObjectRef<unknown>,
149
178
  modelName: string,
@@ -206,6 +235,13 @@ export class ModelLoader {
206
235
  this.modelName,
207
236
  );
208
237
 
238
+ if (delegate.findUniqueOrThrow) {
239
+ return delegate.findUniqueOrThrow({
240
+ ...selectionToQuery(state),
241
+ where: { ...(this.findUnique(this.model, context) as {}) },
242
+ } as never) as Promise<Record<string, unknown>>;
243
+ }
244
+
209
245
  return delegate.findUnique({
210
246
  rejectOnNotFound: true,
211
247
  ...selectionToQuery(state),
@@ -14,12 +14,14 @@ import {
14
14
  SchemaTypes,
15
15
  TypeParam,
16
16
  } from '@pothos/core';
17
+ import { ModelLoader } from './model-loader';
17
18
  import { PrismaObjectRef } from './object-ref';
18
19
  import {
19
20
  PrismaModelTypes,
20
21
  RelatedConnectionOptions,
21
22
  RelatedFieldOptions,
22
23
  RelationCountOptions,
24
+ SelectionMap,
23
25
  ShapeFromConnection,
24
26
  VariantFieldOptions,
25
27
  } from './types';
@@ -43,6 +45,27 @@ const RootBuilder: {
43
45
  ): PothosSchemaTypes.RootFieldBuilder<Types, Shape, Kind>;
44
46
  } = RootFieldBuilder as never;
45
47
 
48
+ type ContextForAuth<
49
+ Types extends SchemaTypes,
50
+ Scopes extends {} = {},
51
+ > = PothosSchemaTypes.ScopeAuthContextForAuth<Types, Scopes> extends {
52
+ Context: infer T;
53
+ }
54
+ ? T extends object
55
+ ? T
56
+ : object
57
+ : object;
58
+
59
+ type FieldAuthScopes<
60
+ Types extends SchemaTypes,
61
+ Parent,
62
+ Args extends {} = {},
63
+ > = PothosSchemaTypes.ScopeAuthFieldAuthScopes<Types, Parent, Args> extends {
64
+ Scopes: infer T;
65
+ }
66
+ ? T
67
+ : never;
68
+
46
69
  export class PrismaObjectFieldBuilder<
47
70
  Types extends SchemaTypes,
48
71
  Model extends PrismaModelTypes,
@@ -63,6 +86,17 @@ export class PrismaObjectFieldBuilder<
63
86
  exposeIDList = this.createExpose(['ID']);
64
87
  exposeStringList = this.createExpose(['String']);
65
88
 
89
+ withAuth: 'scopeAuth' extends PluginName
90
+ ? <Scopes extends FieldAuthScopes<Types, Shape, Record<string, unknown>>>(
91
+ scopes: Scopes,
92
+ ) => PothosSchemaTypes.PrismaObjectFieldBuilder<
93
+ Omit<Types, 'Context'> & { Context: ContextForAuth<Types, Scopes> },
94
+ Model,
95
+ NeedsResolve,
96
+ Shape
97
+ >
98
+ : '@pothos/plugin-scope-auth is required to use this method' = (() => {}) as never;
99
+
66
100
  relatedConnection: 'relay' extends PluginName
67
101
  ? <
68
102
  Field extends Model['ListRelations'],
@@ -133,35 +167,53 @@ export class PrismaObjectFieldBuilder<
133
167
  args,
134
168
  }),
135
169
  });
170
+ const cursorSelection = ModelLoader.getCursorSelection(
171
+ ref,
172
+ relationField.type,
173
+ cursor,
174
+ this.builder,
175
+ );
136
176
 
137
177
  const relationSelect = (
138
178
  args: object,
139
179
  context: object,
140
180
  nestedQuery: (query: unknown, path: unknown) => unknown,
141
- ) => ({
142
- select: {
143
- [name]: nestedQuery(
144
- {
145
- ...((typeof query === 'function' ? query(args, context) : query) as {}),
146
- ...prismaCursorConnectionQuery({
147
- parseCursor,
148
- maxSize,
149
- defaultSize,
150
- args,
151
- }),
181
+ ) => {
182
+ const nested = nestedQuery(
183
+ {
184
+ ...((typeof query === 'function' ? query(args, context) : query) as {}),
185
+ ...prismaCursorConnectionQuery({
186
+ parseCursor,
187
+ maxSize,
188
+ defaultSize,
189
+ args,
190
+ }),
191
+ },
192
+ {
193
+ getType: () => {
194
+ if (!typeName) {
195
+ typeName = this.builder.configStore.getTypeConfig(ref).name;
196
+ }
197
+ return typeName;
152
198
  },
153
- {
154
- getType: () => {
155
- if (!typeName) {
156
- typeName = this.builder.configStore.getTypeConfig(ref).name;
199
+ path: [{ name: 'edges' }, { name: 'node' }],
200
+ },
201
+ ) as SelectionMap;
202
+
203
+ return {
204
+ select: {
205
+ [name]: nested?.select
206
+ ? {
207
+ ...nested,
208
+ select: {
209
+ ...cursorSelection,
210
+ ...nested.select,
211
+ },
157
212
  }
158
- return typeName;
159
- },
160
- path: [{ name: 'edges' }, { name: 'node' }],
161
- },
162
- ),
163
- },
164
- });
213
+ : nested,
214
+ },
215
+ };
216
+ };
165
217
 
166
218
  const fieldRef = (
167
219
  this as unknown as {
@@ -448,3 +500,39 @@ export class PrismaObjectFieldBuilder<
448
500
  };
449
501
  }
450
502
  }
503
+
504
+ const prismaFieldBuilderProto = PrismaObjectFieldBuilder.prototype as {} as Omit<
505
+ PrismaObjectFieldBuilder<SchemaTypes, PrismaModelTypes, false, {}>,
506
+ 'withAuth'
507
+ > & {
508
+ withAuth: (scopes: {}) => unknown;
509
+ };
510
+
511
+ prismaFieldBuilderProto.withAuth = function withAuth(scopes) {
512
+ return addScopes(
513
+ scopes,
514
+ new PrismaObjectFieldBuilder(
515
+ this.typename,
516
+ this.builder,
517
+ this.model,
518
+ this.prismaFieldMap,
519
+ ) as never,
520
+ );
521
+ };
522
+
523
+ function addScopes(
524
+ scopes: unknown,
525
+ builder: { createField: (options: Record<string, unknown>) => unknown },
526
+ ) {
527
+ const originalCreateField = builder.createField;
528
+
529
+ // eslint-disable-next-line no-param-reassign
530
+ builder.createField = function createField(options) {
531
+ return originalCreateField.call(this, {
532
+ authScopes: scopes,
533
+ ...options,
534
+ });
535
+ };
536
+
537
+ return builder as never;
538
+ }
@@ -97,11 +97,17 @@ schemaBuilderProto.prismaNode = function prismaNode(
97
97
  ) => {
98
98
  const query = queryFromInfo(context, info, typeName);
99
99
  const delegate = getDelegateFromModel(getClient(this, context), type);
100
- const record = await delegate.findUnique({
101
- ...query,
102
- rejectOnNotFound: true,
103
- where: rawFindUnique ? rawFindUnique(id, context) : { [fieldName]: idParser!(id) },
104
- } as never);
100
+
101
+ const record = await (delegate.findUniqueOrThrow
102
+ ? delegate.findUniqueOrThrow({
103
+ ...query,
104
+ where: rawFindUnique ? rawFindUnique(id, context) : { [fieldName]: idParser!(id) },
105
+ } as never)
106
+ : delegate.findUnique({
107
+ ...query,
108
+ rejectOnNotFound: true,
109
+ where: rawFindUnique ? rawFindUnique(id, context) : { [fieldName]: idParser!(id) },
110
+ } as never));
105
111
 
106
112
  brandWithType(record, typeName as OutputType<SchemaTypes>);
107
113
 
package/src/types.ts CHANGED
@@ -24,6 +24,7 @@ import { PrismaObjectFieldBuilder } from './field-builder';
24
24
  import { PrismaObjectRef } from './object-ref';
25
25
 
26
26
  export interface PrismaDelegate {
27
+ findUniqueOrThrow?: (...args: any[]) => Promise<unknown>;
27
28
  findUnique: (...args: any[]) => Promise<unknown>;
28
29
  }
29
30
 
@@ -473,7 +474,8 @@ export type PrismaConnectionFieldOptions<
473
474
  ParentShape,
474
475
  Param,
475
476
  Nullable,
476
- Args & InputFieldsFromShape<PothosSchemaTypes.DefaultConnectionArguments>,
477
+ (InputFieldMap extends Args ? {} : Args) &
478
+ InputFieldsFromShape<PothosSchemaTypes.DefaultConnectionArguments>,
477
479
  Kind,
478
480
  ParentShape,
479
481
  ResolveReturnShape
@@ -517,10 +519,11 @@ export type RelatedConnectionOptions<
517
519
  Model['Shape'],
518
520
  ObjectRef<unknown>,
519
521
  Nullable,
520
- Args,
522
+ (InputFieldMap extends Args ? {} : Args) &
523
+ InputFieldsFromShape<PothosSchemaTypes.DefaultConnectionArguments>,
521
524
  unknown
522
525
  >,
523
- 'resolve' | 'type'
526
+ 'resolve' | 'type' | 'args'
524
527
  > &
525
528
  Omit<
526
529
  PothosSchemaTypes.ConnectionFieldOptions<
@@ -174,7 +174,7 @@ export function serializeID(id: unknown, dataType: string) {
174
174
  }
175
175
 
176
176
  export function parseCompositeCursor(fields: string[]) {
177
- return (cursor: string) => {
177
+ return (cursor: unknown) => {
178
178
  const parsed = parseRawCursor(cursor) as unknown[];
179
179
 
180
180
  if (!Array.isArray(parsed)) {
@@ -72,8 +72,15 @@ export function getCursorFormatter<Types extends SchemaTypes>(
72
72
  ) {
73
73
  const modelData = getModel(name, builder);
74
74
  const primaryKey = modelData.primaryKey?.name ?? modelData.primaryKey?.fields.join('_');
75
+ if (primaryKey === cursor) {
76
+ return formatCursor(modelData.primaryKey!.fields);
77
+ }
78
+
79
+ const uniqueIndex = modelData.uniqueIndexes.find(
80
+ (idx) => (idx.name ?? idx.fields.join('_')) === cursor,
81
+ );
75
82
 
76
- return formatCursor(cursor === primaryKey ? modelData.primaryKey!.fields : cursor);
83
+ return formatCursor(uniqueIndex?.fields ?? cursor);
77
84
  }
78
85
 
79
86
  export function getCursorParser<Types extends SchemaTypes>(
@@ -84,8 +91,19 @@ export function getCursorParser<Types extends SchemaTypes>(
84
91
  const modelData = getModel(name, builder);
85
92
  const primaryKey = modelData.primaryKey?.name ?? modelData.primaryKey?.fields.join('_');
86
93
 
87
- const parser =
88
- cursor === primaryKey ? parseCompositeCursor(modelData.primaryKey!.fields) : parseRawCursor;
94
+ let parser = parseRawCursor;
95
+
96
+ if (primaryKey === cursor) {
97
+ parser = parseCompositeCursor(modelData.primaryKey!.fields);
98
+ } else {
99
+ const uniqueIndex = modelData.uniqueIndexes.find(
100
+ (idx) => (idx.name ?? idx.fields.join('_')) === cursor,
101
+ );
102
+
103
+ if (uniqueIndex) {
104
+ parser = parseCompositeCursor(uniqueIndex.fields);
105
+ }
106
+ }
89
107
 
90
108
  return (rawCursor: string) => ({
91
109
  [cursor]: parser(rawCursor),
@@ -306,10 +306,19 @@ function addFieldSelection(
306
306
  }
307
307
  }
308
308
 
309
- export function queryFromInfo(context: object, info: GraphQLResolveInfo, typeName?: string): {} {
309
+ export function queryFromInfo(
310
+ context: object,
311
+ info: GraphQLResolveInfo,
312
+ typeName?: string,
313
+ initialSelection?: SelectionMap,
314
+ ): {} {
310
315
  const type = typeName ? info.schema.getTypeMap()[typeName] : getNamedType(info.returnType);
311
316
  const state = createStateForType(type, info);
312
317
 
318
+ if (initialSelection) {
319
+ mergeSelection(state, initialSelection);
320
+ }
321
+
313
322
  addTypeSelectionsForField(type, context, info, state, info.fieldNodes[0], []);
314
323
 
315
324
  setLoaderMappings(context, info, state.mappings);
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "compilerOptions": {
3
- "skipLibCheck": true,
3
+ "skipLibCheck": false
4
4
  },
5
5
  "extends": "../../tsconfig.options.json",
6
6
  "include": [
7
7
  "src/**/*",
8
- "tests/**/*",
8
+ "tests/**/*"
9
9
  ]
10
- }
10
+ }