@pothos/plugin-prisma 0.15.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 (147) hide show
  1. package/.turbo/turbo-build.log +17 -0
  2. package/.turbo/turbo-test.log +17 -0
  3. package/CHANGELOG.md +312 -0
  4. package/LICENSE +6 -0
  5. package/README.md +808 -0
  6. package/babel.config.js +3 -0
  7. package/bin/generator.js +2 -0
  8. package/esm/cursors.d.ts +55 -0
  9. package/esm/cursors.d.ts.map +1 -0
  10. package/esm/cursors.js +106 -0
  11. package/esm/cursors.js.map +1 -0
  12. package/esm/field-builder.d.ts +2 -0
  13. package/esm/field-builder.d.ts.map +1 -0
  14. package/esm/field-builder.js +51 -0
  15. package/esm/field-builder.js.map +1 -0
  16. package/esm/generator.d.ts +2 -0
  17. package/esm/generator.d.ts.map +1 -0
  18. package/esm/generator.js +82 -0
  19. package/esm/generator.js.map +1 -0
  20. package/esm/global-types.d.ts +52 -0
  21. package/esm/global-types.d.ts.map +1 -0
  22. package/esm/global-types.js +2 -0
  23. package/esm/global-types.js.map +1 -0
  24. package/esm/index.d.ts +11 -0
  25. package/esm/index.d.ts.map +1 -0
  26. package/esm/index.js +14 -0
  27. package/esm/index.js.map +1 -0
  28. package/esm/loader-map.d.ts +6 -0
  29. package/esm/loader-map.d.ts.map +1 -0
  30. package/esm/loader-map.js +35 -0
  31. package/esm/loader-map.js.map +1 -0
  32. package/esm/model-loader.d.ts +17 -0
  33. package/esm/model-loader.d.ts.map +1 -0
  34. package/esm/model-loader.js +89 -0
  35. package/esm/model-loader.js.map +1 -0
  36. package/esm/node-ref.d.ts +10 -0
  37. package/esm/node-ref.d.ts.map +1 -0
  38. package/esm/node-ref.js +18 -0
  39. package/esm/node-ref.js.map +1 -0
  40. package/esm/package.json +3 -0
  41. package/esm/prisma-field-builder.d.ts +22 -0
  42. package/esm/prisma-field-builder.d.ts.map +1 -0
  43. package/esm/prisma-field-builder.js +178 -0
  44. package/esm/prisma-field-builder.js.map +1 -0
  45. package/esm/refs.d.ts +13 -0
  46. package/esm/refs.d.ts.map +1 -0
  47. package/esm/refs.js +63 -0
  48. package/esm/refs.js.map +1 -0
  49. package/esm/schema-builder.d.ts +2 -0
  50. package/esm/schema-builder.d.ts.map +1 -0
  51. package/esm/schema-builder.js +69 -0
  52. package/esm/schema-builder.js.map +1 -0
  53. package/esm/types.d.ts +164 -0
  54. package/esm/types.d.ts.map +1 -0
  55. package/esm/types.js +2 -0
  56. package/esm/types.js.map +1 -0
  57. package/esm/util/index.d.ts +5 -0
  58. package/esm/util/index.d.ts.map +1 -0
  59. package/esm/util/index.js +16 -0
  60. package/esm/util/index.js.map +1 -0
  61. package/esm/util/map-includes.d.ts +5 -0
  62. package/esm/util/map-includes.d.ts.map +1 -0
  63. package/esm/util/map-includes.js +168 -0
  64. package/esm/util/map-includes.js.map +1 -0
  65. package/esm/util/merge-includes.d.ts +3 -0
  66. package/esm/util/merge-includes.d.ts.map +1 -0
  67. package/esm/util/merge-includes.js +91 -0
  68. package/esm/util/merge-includes.js.map +1 -0
  69. package/generated.ts +54 -0
  70. package/lib/cursors.d.ts +55 -0
  71. package/lib/cursors.d.ts.map +1 -0
  72. package/lib/cursors.js +112 -0
  73. package/lib/cursors.js.map +1 -0
  74. package/lib/field-builder.d.ts +2 -0
  75. package/lib/field-builder.d.ts.map +1 -0
  76. package/lib/field-builder.js +63 -0
  77. package/lib/field-builder.js.map +1 -0
  78. package/lib/generator.d.ts +2 -0
  79. package/lib/generator.d.ts.map +1 -0
  80. package/lib/generator.js +103 -0
  81. package/lib/generator.js.map +1 -0
  82. package/lib/global-types.d.ts +52 -0
  83. package/lib/global-types.d.ts.map +1 -0
  84. package/lib/global-types.js +3 -0
  85. package/lib/global-types.js.map +1 -0
  86. package/lib/index.d.ts +11 -0
  87. package/lib/index.d.ts.map +1 -0
  88. package/lib/index.js +40 -0
  89. package/lib/index.js.map +1 -0
  90. package/lib/loader-map.d.ts +6 -0
  91. package/lib/loader-map.d.ts.map +1 -0
  92. package/lib/loader-map.js +41 -0
  93. package/lib/loader-map.js.map +1 -0
  94. package/lib/model-loader.d.ts +17 -0
  95. package/lib/model-loader.d.ts.map +1 -0
  96. package/lib/model-loader.js +93 -0
  97. package/lib/model-loader.js.map +1 -0
  98. package/lib/node-ref.d.ts +10 -0
  99. package/lib/node-ref.d.ts.map +1 -0
  100. package/lib/node-ref.js +21 -0
  101. package/lib/node-ref.js.map +1 -0
  102. package/lib/prisma-field-builder.d.ts +22 -0
  103. package/lib/prisma-field-builder.d.ts.map +1 -0
  104. package/lib/prisma-field-builder.js +182 -0
  105. package/lib/prisma-field-builder.js.map +1 -0
  106. package/lib/refs.d.ts +13 -0
  107. package/lib/refs.d.ts.map +1 -0
  108. package/lib/refs.js +72 -0
  109. package/lib/refs.js.map +1 -0
  110. package/lib/schema-builder.d.ts +2 -0
  111. package/lib/schema-builder.d.ts.map +1 -0
  112. package/lib/schema-builder.js +93 -0
  113. package/lib/schema-builder.js.map +1 -0
  114. package/lib/types.d.ts +164 -0
  115. package/lib/types.d.ts.map +1 -0
  116. package/lib/types.js +4 -0
  117. package/lib/types.js.map +1 -0
  118. package/lib/util/index.d.ts +5 -0
  119. package/lib/util/index.d.ts.map +1 -0
  120. package/lib/util/index.js +30 -0
  121. package/lib/util/index.js.map +1 -0
  122. package/lib/util/map-includes.d.ts +5 -0
  123. package/lib/util/map-includes.d.ts.map +1 -0
  124. package/lib/util/map-includes.js +173 -0
  125. package/lib/util/map-includes.js.map +1 -0
  126. package/lib/util/merge-includes.d.ts +3 -0
  127. package/lib/util/merge-includes.d.ts.map +1 -0
  128. package/lib/util/merge-includes.js +96 -0
  129. package/lib/util/merge-includes.js.map +1 -0
  130. package/package.json +70 -0
  131. package/src/cursors.ts +159 -0
  132. package/src/field-builder.ts +115 -0
  133. package/src/generator.ts +184 -0
  134. package/src/global-types.ts +180 -0
  135. package/src/index.ts +18 -0
  136. package/src/loader-map.ts +48 -0
  137. package/src/model-loader.ts +135 -0
  138. package/src/node-ref.ts +29 -0
  139. package/src/prisma-field-builder.ts +324 -0
  140. package/src/refs.ts +110 -0
  141. package/src/schema-builder.ts +124 -0
  142. package/src/types.ts +470 -0
  143. package/src/util/index.ts +26 -0
  144. package/src/util/map-includes.ts +298 -0
  145. package/src/util/merge-includes.ts +121 -0
  146. package/tsconfig.json +21 -0
  147. package/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1,115 @@
1
+ import { GraphQLResolveInfo } from 'graphql';
2
+ import {
3
+ FieldKind,
4
+ FieldRef,
5
+ InputFieldMap,
6
+ ObjectRef,
7
+ RootFieldBuilder,
8
+ SchemaTypes,
9
+ TypeParam,
10
+ } from '@pothos/core';
11
+ import { resolvePrismaCursorConnection } from './cursors';
12
+ import { getRefFromModel } from './refs';
13
+ import { PrismaConnectionFieldOptions, PrismaModelTypes } from './types';
14
+ import { queryFromInfo } from './util';
15
+
16
+ export * from './prisma-field-builder';
17
+
18
+ const fieldBuilderProto = RootFieldBuilder.prototype as PothosSchemaTypes.RootFieldBuilder<
19
+ SchemaTypes,
20
+ unknown,
21
+ FieldKind
22
+ >;
23
+
24
+ fieldBuilderProto.prismaField = function prismaField({ type, resolve, ...options }) {
25
+ const model: string = Array.isArray(type) ? type[0] : type;
26
+ const typeRef = getRefFromModel(model, this.builder);
27
+ const typeParam: TypeParam<SchemaTypes> = Array.isArray(type) ? [typeRef] : typeRef;
28
+
29
+ return this.field({
30
+ ...options,
31
+ type: typeParam,
32
+ resolve: (parent: unknown, args: unknown, ctx: {}, info: GraphQLResolveInfo) => {
33
+ const query = queryFromInfo(ctx, info);
34
+
35
+ return resolve(query, parent, args as never, ctx, info) as never;
36
+ },
37
+ }) as never;
38
+ };
39
+
40
+ fieldBuilderProto.prismaConnection = function prismaConnection<
41
+ Type extends keyof SchemaTypes['PrismaTypes'],
42
+ Nullable extends boolean,
43
+ ResolveReturnShape,
44
+ Args extends InputFieldMap,
45
+ Model extends PrismaModelTypes,
46
+ >(
47
+ this: typeof fieldBuilderProto,
48
+ {
49
+ type,
50
+ cursor,
51
+ maxSize,
52
+ defaultSize,
53
+ resolve,
54
+ ...options
55
+ }: PrismaConnectionFieldOptions<
56
+ SchemaTypes,
57
+ unknown,
58
+ Type,
59
+ Model,
60
+ ObjectRef<{}>,
61
+ Nullable,
62
+ Args,
63
+ ResolveReturnShape,
64
+ FieldKind
65
+ >,
66
+ connectionOptions: {},
67
+ edgeOptions: {},
68
+ ) {
69
+ const ref = getRefFromModel(type, this.builder);
70
+ let typeName: string | undefined;
71
+
72
+ const fieldRef = (
73
+ this as typeof fieldBuilderProto & { connection: (...args: unknown[]) => FieldRef<unknown> }
74
+ ).connection(
75
+ {
76
+ ...options,
77
+ type: ref,
78
+ resolve: (
79
+ parent: unknown,
80
+ args: PothosSchemaTypes.DefaultConnectionArguments,
81
+ ctx: {},
82
+ info: GraphQLResolveInfo,
83
+ ) =>
84
+ resolvePrismaCursorConnection(
85
+ {
86
+ query: queryFromInfo(ctx, info),
87
+ column: cursor,
88
+ maxSize,
89
+ defaultSize,
90
+ args,
91
+ },
92
+ (query) => resolve(query as never, parent, args as never, ctx, info),
93
+ ),
94
+ },
95
+ {
96
+ ...connectionOptions,
97
+ extensions: {
98
+ ...(connectionOptions as Record<string, {}> | undefined)?.extensions,
99
+ pothosPrismaIndirectInclude: {
100
+ getType: () => {
101
+ if (!typeName) {
102
+ typeName = this.builder.configStore.getTypeConfig(ref).name;
103
+ }
104
+
105
+ return typeName;
106
+ },
107
+ path: [{ name: 'edges' }, { name: 'node' }],
108
+ },
109
+ },
110
+ },
111
+ edgeOptions,
112
+ );
113
+
114
+ return fieldRef;
115
+ } as never;
@@ -0,0 +1,184 @@
1
+ /* eslint-disable no-nested-ternary */
2
+ import { mkdir, writeFile } from 'fs';
3
+ import { dirname } from 'path';
4
+ import ts, { ListFormat, ScriptKind, ScriptTarget, SyntaxKind } from 'typescript';
5
+ import { generatorHandler } from '@prisma/generator-helper';
6
+
7
+ generatorHandler({
8
+ onManifest: () => ({
9
+ prettyName: 'Pothos integration',
10
+ defaultOutput: 'node_modules/@pothos/plugin-prisma/generated.ts',
11
+ requiresGenerators: ['prisma-client-js'],
12
+ }),
13
+ onGenerate: async (options) => {
14
+ const config = options.generator.config as { clientOutput?: string };
15
+ const prismaLocation =
16
+ config.clientOutput ??
17
+ options.otherGenerators.find((gen) => gen.provider.value === 'prisma-client-js')!.output!
18
+ .value;
19
+
20
+ const importStatement = ts.factory.createImportDeclaration(
21
+ [],
22
+ [],
23
+ ts.factory.createImportClause(
24
+ true,
25
+ undefined,
26
+ ts.factory.createNamedImports([
27
+ ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier('Prisma')),
28
+ ...options.dmmf.datamodel.models.map((model) =>
29
+ ts.factory.createImportSpecifier(
30
+ false,
31
+ undefined,
32
+ ts.factory.createIdentifier(model.name),
33
+ ),
34
+ ),
35
+ ]),
36
+ ),
37
+ ts.factory.createStringLiteral(prismaLocation),
38
+ );
39
+
40
+ const modelTypes = options.dmmf.datamodel.models.map((model) => {
41
+ const relations = model.fields.filter((field) => !!field.relationName);
42
+ const listRelations = model.fields.filter((field) => !!field.relationName && field.isList);
43
+
44
+ return ts.factory.createPropertySignature(
45
+ [],
46
+ model.name,
47
+ undefined,
48
+ ts.factory.createTypeLiteralNode([
49
+ ts.factory.createPropertySignature(
50
+ [],
51
+ 'Shape',
52
+ undefined,
53
+ ts.factory.createTypeReferenceNode(model.name),
54
+ ),
55
+ ts.factory.createPropertySignature(
56
+ [],
57
+ 'Include',
58
+ undefined,
59
+ relations.length > 0
60
+ ? ts.factory.createTypeReferenceNode(`Prisma.${model.name}Include`)
61
+ : ts.factory.createTypeReferenceNode('never'),
62
+ ),
63
+ ts.factory.createPropertySignature(
64
+ [],
65
+ 'Where',
66
+ undefined,
67
+ ts.factory.createTypeReferenceNode(`Prisma.${model.name}WhereUniqueInput`),
68
+ ),
69
+ ts.factory.createPropertySignature(
70
+ [],
71
+ 'Fields',
72
+ undefined,
73
+ relations.length > 0
74
+ ? ts.factory.createUnionTypeNode(
75
+ relations.map((field) =>
76
+ ts.factory.createLiteralTypeNode(ts.factory.createStringLiteral(field.name)),
77
+ ),
78
+ )
79
+ : ts.factory.createTypeReferenceNode('never'),
80
+ ),
81
+ ts.factory.createPropertySignature(
82
+ [],
83
+ 'ListRelations',
84
+ undefined,
85
+ listRelations.length > 0
86
+ ? ts.factory.createUnionTypeNode(
87
+ listRelations.map((field) =>
88
+ ts.factory.createLiteralTypeNode(ts.factory.createStringLiteral(field.name)),
89
+ ),
90
+ )
91
+ : ts.factory.createTypeReferenceNode('never'),
92
+ ),
93
+ ts.factory.createPropertySignature(
94
+ [],
95
+ 'Relations',
96
+ undefined,
97
+ ts.factory.createTypeLiteralNode(
98
+ relations.map((field) => {
99
+ const typeName = typeof field.type === 'string' ? field.type : field.type.name;
100
+
101
+ return ts.factory.createPropertySignature(
102
+ [],
103
+ field.name,
104
+ undefined,
105
+ ts.factory.createTypeLiteralNode([
106
+ ts.factory.createPropertySignature(
107
+ [],
108
+ 'Shape',
109
+ undefined,
110
+ field.isList
111
+ ? ts.factory.createArrayTypeNode(
112
+ ts.factory.createTypeReferenceNode(typeName),
113
+ )
114
+ : field.isRequired
115
+ ? ts.factory.createTypeReferenceNode(typeName)
116
+ : ts.factory.createUnionTypeNode([
117
+ ts.factory.createTypeReferenceNode(typeName),
118
+ ts.factory.createLiteralTypeNode(ts.factory.createNull()),
119
+ ]),
120
+ ),
121
+ ts.factory.createPropertySignature(
122
+ [],
123
+ 'Types',
124
+ undefined,
125
+ ts.factory.createIndexedAccessTypeNode(
126
+ ts.factory.createTypeReferenceNode('PrismaTypes'),
127
+ ts.factory.createLiteralTypeNode(ts.factory.createStringLiteral(typeName)),
128
+ ),
129
+ ),
130
+ ]),
131
+ );
132
+ }),
133
+ ),
134
+ ),
135
+ ]),
136
+ );
137
+ });
138
+
139
+ const prismaTypes = ts.factory.createInterfaceDeclaration(
140
+ [],
141
+ [
142
+ ts.factory.createModifier(SyntaxKind.ExportKeyword),
143
+ ts.factory.createModifier(SyntaxKind.DefaultKeyword),
144
+ ],
145
+ 'PrismaTypes',
146
+ [],
147
+ [],
148
+ modelTypes,
149
+ );
150
+ const printer = ts.createPrinter({});
151
+
152
+ const sourcefile = ts.createSourceFile(
153
+ options.generator.output!.value,
154
+ '',
155
+ ScriptTarget.ESNext,
156
+ false,
157
+ ScriptKind.TS,
158
+ );
159
+
160
+ const nodes = ts.factory.createNodeArray([importStatement, prismaTypes]);
161
+
162
+ const result = printer.printList(ListFormat.SourceFileStatements, nodes, sourcefile);
163
+
164
+ await new Promise<void>((resolve, reject) => {
165
+ mkdir(dirname(sourcefile.fileName), { recursive: true }, (err) => {
166
+ if (err) {
167
+ reject(err);
168
+ } else {
169
+ resolve();
170
+ }
171
+ });
172
+ });
173
+
174
+ return new Promise<void>((resolve, reject) => {
175
+ writeFile(sourcefile.fileName, result, (err) => {
176
+ if (err) {
177
+ reject(err);
178
+ } else {
179
+ resolve();
180
+ }
181
+ });
182
+ });
183
+ },
184
+ });
@@ -0,0 +1,180 @@
1
+ /* eslint-disable @typescript-eslint/no-unused-vars */
2
+ /* eslint-disable @typescript-eslint/no-empty-interface */
3
+ import {
4
+ FieldKind,
5
+ FieldNullability,
6
+ FieldRef,
7
+ InputFieldMap,
8
+ InterfaceParam,
9
+ NormalizeArgs,
10
+ ObjectRef,
11
+ OutputType,
12
+ PluginName,
13
+ SchemaTypes,
14
+ ShapeFromTypeParam,
15
+ } from '@pothos/core';
16
+ import PrismaNodeRef from './node-ref';
17
+ import {
18
+ PrismaConnectionFieldOptions,
19
+ PrismaFieldOptions,
20
+ PrismaModelTypes,
21
+ PrismaNodeOptions,
22
+ PrismaObjectTypeOptions,
23
+ ShapeWithInclude,
24
+ } from './types';
25
+ import { PrismaPlugin } from '.';
26
+
27
+ declare global {
28
+ export namespace PothosSchemaTypes {
29
+ export interface Plugins<Types extends SchemaTypes> {
30
+ prisma: PrismaPlugin<Types>;
31
+ }
32
+
33
+ export interface SchemaBuilderOptions<Types extends SchemaTypes> {
34
+ prisma: {
35
+ client: {
36
+ $connect: () => Promise<void>;
37
+ };
38
+ };
39
+ }
40
+
41
+ export interface UserSchemaTypes {
42
+ PrismaTypes: {};
43
+ }
44
+
45
+ export interface ExtendDefaultTypes<PartialTypes extends Partial<UserSchemaTypes>> {
46
+ PrismaTypes: undefined extends PartialTypes['PrismaTypes']
47
+ ? {}
48
+ : PartialTypes['PrismaTypes'] & {};
49
+ }
50
+
51
+ export interface SchemaBuilder<Types extends SchemaTypes> {
52
+ prismaObject: <
53
+ Name extends keyof Types['PrismaTypes'],
54
+ Interfaces extends InterfaceParam<Types>[],
55
+ FindUnique,
56
+ Model extends PrismaModelTypes & Types['PrismaTypes'][Name],
57
+ Include extends Model['Include'] = {},
58
+ Shape extends object = ShapeWithInclude<Model, Include>,
59
+ >(
60
+ name: Name,
61
+ options: PrismaObjectTypeOptions<Types, Model, Interfaces, FindUnique, Include, Shape>,
62
+ ) => ObjectRef<Shape>;
63
+
64
+ prismaNode: 'relay' extends PluginName
65
+ ? <
66
+ Name extends keyof Types['PrismaTypes'],
67
+ Interfaces extends InterfaceParam<Types>[],
68
+ Model extends PrismaModelTypes & Types['PrismaTypes'][Name],
69
+ Include extends Model['Include'] = {},
70
+ Shape extends object = ShapeWithInclude<Model, Include>,
71
+ >(
72
+ name: Name,
73
+ options: PrismaNodeOptions<Types, Model, Interfaces, Include, Shape>,
74
+ ) => PrismaNodeRef<Shape>
75
+ : '@pothos/plugin-relay is required to use this method';
76
+ }
77
+
78
+ export interface RootFieldBuilder<
79
+ Types extends SchemaTypes,
80
+ ParentShape,
81
+ Kind extends FieldKind = FieldKind,
82
+ > {
83
+ prismaField: <
84
+ Args extends InputFieldMap,
85
+ TypeParam extends keyof Types['PrismaTypes'] | [keyof Types['PrismaTypes']],
86
+ Nullable extends FieldNullability<Type>,
87
+ ResolveReturnShape,
88
+ Type extends TypeParam extends [keyof Types['PrismaTypes']]
89
+ ? [ObjectRef<Model['Shape']>]
90
+ : ObjectRef<Model['Shape']>,
91
+ Model extends PrismaModelTypes = PrismaModelTypes &
92
+ (TypeParam extends [keyof Types['PrismaTypes']]
93
+ ? Types['PrismaTypes'][TypeParam[0]]
94
+ : TypeParam extends keyof Types['PrismaTypes']
95
+ ? Types['PrismaTypes'][TypeParam]
96
+ : never),
97
+ >(
98
+ options: PrismaFieldOptions<
99
+ Types,
100
+ ParentShape,
101
+ TypeParam,
102
+ Model,
103
+ Type,
104
+ Args,
105
+ Nullable,
106
+ ResolveReturnShape,
107
+ Kind
108
+ >,
109
+ ) => FieldRef<ShapeFromTypeParam<Types, Type, Nullable>>;
110
+
111
+ prismaConnection: 'relay' extends PluginName
112
+ ? <
113
+ Type extends keyof Types['PrismaTypes'],
114
+ Nullable extends boolean,
115
+ ResolveReturnShape,
116
+ Args extends InputFieldMap = {},
117
+ Model extends PrismaModelTypes = PrismaModelTypes & Types['PrismaTypes'][Type],
118
+ >(
119
+ ...args: NormalizeArgs<
120
+ [
121
+ options: PrismaConnectionFieldOptions<
122
+ Types,
123
+ ParentShape,
124
+ Type,
125
+ Model,
126
+ ObjectRef<Model['Shape']>,
127
+ Nullable,
128
+ Args,
129
+ ResolveReturnShape,
130
+ Kind
131
+ >,
132
+ connectionOptions?: ConnectionObjectOptions<
133
+ Types,
134
+ ObjectRef<Model['Shape']>,
135
+ ResolveReturnShape
136
+ >,
137
+ edgeOptions?: ConnectionEdgeObjectOptions<
138
+ Types,
139
+ ObjectRef<Model['Shape']>,
140
+ ResolveReturnShape
141
+ >,
142
+ ]
143
+ >
144
+ ) => FieldRef<ConnectionShapeHelper<Types, Model['Shape'], Nullable>['shape']>
145
+ : '@pothos/plugin-relay is required to use this method';
146
+ }
147
+
148
+ export interface ConnectionFieldOptions<
149
+ Types extends SchemaTypes,
150
+ ParentShape,
151
+ Type extends OutputType<Types>,
152
+ Nullable extends boolean,
153
+ Args extends InputFieldMap,
154
+ ResolveReturnShape,
155
+ > {}
156
+
157
+ export interface ConnectionObjectOptions<
158
+ Types extends SchemaTypes,
159
+ Type extends OutputType<Types>,
160
+ Resolved,
161
+ > {}
162
+
163
+ export interface ConnectionEdgeObjectOptions<
164
+ Types extends SchemaTypes,
165
+ Type extends OutputType<Types>,
166
+ Resolved,
167
+ > {}
168
+
169
+ export interface DefaultConnectionArguments {
170
+ first?: number | null | undefined;
171
+ last?: number | null | undefined;
172
+ before?: string | null | undefined;
173
+ after?: string | null | undefined;
174
+ }
175
+
176
+ export interface ConnectionShapeHelper<Types extends SchemaTypes, T, Nullable> {
177
+ shape: unknown;
178
+ }
179
+ }
180
+ }
package/src/index.ts ADDED
@@ -0,0 +1,18 @@
1
+ import './global-types';
2
+ import './field-builder';
3
+ import './schema-builder';
4
+ import SchemaBuilder, { BasePlugin, BuildCache, SchemaTypes } from '@pothos/core';
5
+
6
+ export * from './types';
7
+
8
+ const pluginName = 'prisma' as const;
9
+
10
+ export default pluginName;
11
+
12
+ export class PrismaPlugin<Types extends SchemaTypes> extends BasePlugin<Types> {
13
+ constructor(cache: BuildCache<Types>) {
14
+ super(cache, pluginName);
15
+ }
16
+ }
17
+
18
+ SchemaBuilder.registerPlugin(pluginName, PrismaPlugin);
@@ -0,0 +1,48 @@
1
+ import { GraphQLResolveInfo } from 'graphql';
2
+ import { createContextCache } from '@pothos/core';
3
+ import { LoaderMappings } from './types';
4
+
5
+ const cache = createContextCache((ctx) => new Map<string, LoaderMappings>());
6
+
7
+ export function cacheKey(path: GraphQLResolveInfo['path'], subPath: string[]) {
8
+ let key = '';
9
+ let current: GraphQLResolveInfo['path'] | undefined = path;
10
+
11
+ while (current) {
12
+ if (typeof current.key === 'string') {
13
+ key = key ? `${current.key}.${key}` : current.key;
14
+ }
15
+ current = current.prev;
16
+ }
17
+
18
+ for (const entry of subPath) {
19
+ key = `${key}.${entry}`;
20
+ }
21
+
22
+ return key;
23
+ }
24
+
25
+ export function setLoaderMappings(
26
+ ctx: object,
27
+ path: GraphQLResolveInfo['path'],
28
+ value: LoaderMappings,
29
+ ) {
30
+ Object.keys(value).forEach((field) => {
31
+ const map = cache(ctx);
32
+
33
+ for (const mapping of value[field]) {
34
+ const selectionName = mapping.alias ?? mapping.field;
35
+ const subPath = [...mapping.indirectPath, selectionName];
36
+ const key = cacheKey(path, subPath);
37
+
38
+ map.set(key, mapping.mappings);
39
+ }
40
+ });
41
+ }
42
+
43
+ export function getLoaderMapping(ctx: object, path: GraphQLResolveInfo['path']) {
44
+ const map = cache(ctx);
45
+ const key = cacheKey(path, []);
46
+
47
+ return map.get(key) ?? null;
48
+ }
@@ -0,0 +1,135 @@
1
+ /* eslint-disable prefer-destructuring */
2
+ /* eslint-disable no-underscore-dangle */
3
+ import { createContextCache, SchemaTypes } from '@pothos/core';
4
+ import { getDelegateFromModel, getFindUniqueForRef, getRefFromModel } from './refs';
5
+ import { PrismaDelegate } from './types';
6
+ import { mergeIncludes } from './util';
7
+
8
+ const loaderCache = new WeakMap<object, (model: object) => ModelLoader>();
9
+
10
+ export class ModelLoader {
11
+ model: object;
12
+ delegate: PrismaDelegate;
13
+ findUnique: (args: unknown, ctx: {}) => unknown;
14
+
15
+ staged = new Set<{
16
+ promise: Promise<Record<string, unknown>>;
17
+ include: Record<string, unknown>;
18
+ }>();
19
+
20
+ constructor(
21
+ model: object,
22
+ delegate: PrismaDelegate,
23
+ findUnique: (args: unknown, ctx: {}) => unknown,
24
+ ) {
25
+ this.model = model;
26
+ this.delegate = delegate;
27
+ this.findUnique = findUnique;
28
+ }
29
+
30
+ static forModel<Types extends SchemaTypes>(
31
+ modelName: string,
32
+ builder: PothosSchemaTypes.SchemaBuilder<Types>,
33
+ ) {
34
+ const delegate = getDelegateFromModel(builder.options.prisma.client, modelName);
35
+
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)!;
47
+ }
48
+
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;
65
+
66
+ return (result._count as Record<string, number>)[relation];
67
+ }
68
+
69
+ async loadRelation(relation: string, include: unknown, context: {}) {
70
+ let promise;
71
+ for (const entry of this.staged) {
72
+ if (entry.include[relation] === undefined) {
73
+ promise = entry.promise;
74
+ entry.include[relation] = include;
75
+
76
+ break;
77
+ }
78
+
79
+ const merged = mergeIncludes(
80
+ entry.include[relation] as Record<string, unknown>,
81
+ include as Record<string, unknown>,
82
+ );
83
+
84
+ if (merged) {
85
+ entry.include[relation] = merged;
86
+ break;
87
+ }
88
+ }
89
+
90
+ if (!promise) {
91
+ promise = this.initLoad(relation, include, context);
92
+ }
93
+
94
+ const result = await promise;
95
+
96
+ return result[relation];
97
+ }
98
+
99
+ async initLoad(relation: string, includeArg: unknown, context: {}, count = false) {
100
+ const include: Record<string, unknown> = count
101
+ ? {
102
+ _count: {
103
+ select: {
104
+ [relation]: true,
105
+ },
106
+ },
107
+ }
108
+ : {
109
+ [relation]: includeArg,
110
+ };
111
+
112
+ const promise = new Promise<Record<string, unknown>>((resolve, reject) => {
113
+ setTimeout(() => {
114
+ this.staged.delete(entry);
115
+
116
+ resolve(
117
+ this.delegate.findUnique({
118
+ rejectOnNotFound: true,
119
+ where: { ...(this.findUnique(this.model, context) as {}) },
120
+ include,
121
+ } as never) as Promise<Record<string, unknown>>,
122
+ );
123
+ }, 0);
124
+ });
125
+
126
+ const entry = {
127
+ promise,
128
+ include,
129
+ };
130
+
131
+ this.staged.add(entry);
132
+
133
+ return promise;
134
+ }
135
+ }
@@ -0,0 +1,29 @@
1
+ import { abstractReturnShapeKey, brandWithType, ObjectRef, typeBrandKey } from '@pothos/core';
2
+ import { WithBrand } from './types';
3
+
4
+ export default class PrismaNodeRef<T> extends ObjectRef<T, T> {
5
+ [abstractReturnShapeKey]: WithBrand<T>;
6
+
7
+ addBrand<V extends T | T[]>(
8
+ value: V,
9
+ ): V extends T[] ? { [K in keyof V]: WithBrand<V[K]> } : WithBrand<V> {
10
+ if (Array.isArray(value)) {
11
+ value.forEach((val) => void brandWithType(val, this.name as never));
12
+
13
+ return value as never;
14
+ }
15
+
16
+ brandWithType(value, this.name as never);
17
+
18
+ return value as never;
19
+ }
20
+
21
+ hasBrand(value: unknown) {
22
+ return (
23
+ typeof value === 'object' &&
24
+ value !== null &&
25
+ typeBrandKey in value &&
26
+ (value as { [typeBrandKey]?: unknown })[typeBrandKey] === this.name
27
+ );
28
+ }
29
+ }