@pothos/plugin-prisma 0.0.0-preview-20220211212258

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 (155) hide show
  1. package/.turbo/turbo-build.log +17 -0
  2. package/.turbo/turbo-test.log +18 -0
  3. package/.turbo/turbo-type.log +5 -0
  4. package/CHANGELOG.md +355 -0
  5. package/LICENSE +6 -0
  6. package/README.md +808 -0
  7. package/babel.config.js +3 -0
  8. package/bin/generator.js +2 -0
  9. package/esm/cursors.d.ts +55 -0
  10. package/esm/cursors.d.ts.map +1 -0
  11. package/esm/cursors.js +106 -0
  12. package/esm/cursors.js.map +1 -0
  13. package/esm/field-builder.d.ts +2 -0
  14. package/esm/field-builder.d.ts.map +1 -0
  15. package/esm/field-builder.js +53 -0
  16. package/esm/field-builder.js.map +1 -0
  17. package/esm/generator.d.ts +2 -0
  18. package/esm/generator.d.ts.map +1 -0
  19. package/esm/generator.js +83 -0
  20. package/esm/generator.js.map +1 -0
  21. package/esm/global-types.d.ts +52 -0
  22. package/esm/global-types.d.ts.map +1 -0
  23. package/esm/global-types.js +2 -0
  24. package/esm/global-types.js.map +1 -0
  25. package/esm/index.d.ts +11 -0
  26. package/esm/index.d.ts.map +1 -0
  27. package/esm/index.js +14 -0
  28. package/esm/index.js.map +1 -0
  29. package/esm/loader-map.d.ts +6 -0
  30. package/esm/loader-map.d.ts.map +1 -0
  31. package/esm/loader-map.js +35 -0
  32. package/esm/loader-map.js.map +1 -0
  33. package/esm/model-loader.d.ts +18 -0
  34. package/esm/model-loader.d.ts.map +1 -0
  35. package/esm/model-loader.js +101 -0
  36. package/esm/model-loader.js.map +1 -0
  37. package/esm/node-ref.d.ts +12 -0
  38. package/esm/node-ref.d.ts.map +1 -0
  39. package/esm/node-ref.js +19 -0
  40. package/esm/node-ref.js.map +1 -0
  41. package/esm/object-ref.d.ts +7 -0
  42. package/esm/object-ref.d.ts.map +1 -0
  43. package/esm/object-ref.js +5 -0
  44. package/esm/object-ref.js.map +1 -0
  45. package/esm/package.json +3 -0
  46. package/esm/prisma-field-builder.d.ts +28 -0
  47. package/esm/prisma-field-builder.d.ts.map +1 -0
  48. package/esm/prisma-field-builder.js +204 -0
  49. package/esm/prisma-field-builder.js.map +1 -0
  50. package/esm/refs.d.ts +15 -0
  51. package/esm/refs.d.ts.map +1 -0
  52. package/esm/refs.js +64 -0
  53. package/esm/refs.js.map +1 -0
  54. package/esm/schema-builder.d.ts +2 -0
  55. package/esm/schema-builder.d.ts.map +1 -0
  56. package/esm/schema-builder.js +65 -0
  57. package/esm/schema-builder.js.map +1 -0
  58. package/esm/types.d.ts +177 -0
  59. package/esm/types.d.ts.map +1 -0
  60. package/esm/types.js +2 -0
  61. package/esm/types.js.map +1 -0
  62. package/esm/util/index.d.ts +5 -0
  63. package/esm/util/index.d.ts.map +1 -0
  64. package/esm/util/index.js +16 -0
  65. package/esm/util/index.js.map +1 -0
  66. package/esm/util/map-includes.d.ts +6 -0
  67. package/esm/util/map-includes.d.ts.map +1 -0
  68. package/esm/util/map-includes.js +184 -0
  69. package/esm/util/map-includes.js.map +1 -0
  70. package/esm/util/merge-includes.d.ts +3 -0
  71. package/esm/util/merge-includes.d.ts.map +1 -0
  72. package/esm/util/merge-includes.js +91 -0
  73. package/esm/util/merge-includes.js.map +1 -0
  74. package/lib/cursors.d.ts +55 -0
  75. package/lib/cursors.d.ts.map +1 -0
  76. package/lib/cursors.js +112 -0
  77. package/lib/cursors.js.map +1 -0
  78. package/lib/field-builder.d.ts +2 -0
  79. package/lib/field-builder.d.ts.map +1 -0
  80. package/lib/field-builder.js +65 -0
  81. package/lib/field-builder.js.map +1 -0
  82. package/lib/generator.d.ts +2 -0
  83. package/lib/generator.d.ts.map +1 -0
  84. package/lib/generator.js +104 -0
  85. package/lib/generator.js.map +1 -0
  86. package/lib/global-types.d.ts +52 -0
  87. package/lib/global-types.d.ts.map +1 -0
  88. package/lib/global-types.js +3 -0
  89. package/lib/global-types.js.map +1 -0
  90. package/lib/index.d.ts +11 -0
  91. package/lib/index.d.ts.map +1 -0
  92. package/lib/index.js +40 -0
  93. package/lib/index.js.map +1 -0
  94. package/lib/loader-map.d.ts +6 -0
  95. package/lib/loader-map.d.ts.map +1 -0
  96. package/lib/loader-map.js +41 -0
  97. package/lib/loader-map.js.map +1 -0
  98. package/lib/model-loader.d.ts +18 -0
  99. package/lib/model-loader.d.ts.map +1 -0
  100. package/lib/model-loader.js +105 -0
  101. package/lib/model-loader.js.map +1 -0
  102. package/lib/node-ref.d.ts +12 -0
  103. package/lib/node-ref.d.ts.map +1 -0
  104. package/lib/node-ref.js +22 -0
  105. package/lib/node-ref.js.map +1 -0
  106. package/lib/object-ref.d.ts +7 -0
  107. package/lib/object-ref.d.ts.map +1 -0
  108. package/lib/object-ref.js +9 -0
  109. package/lib/object-ref.js.map +1 -0
  110. package/lib/prisma-field-builder.d.ts +28 -0
  111. package/lib/prisma-field-builder.d.ts.map +1 -0
  112. package/lib/prisma-field-builder.js +208 -0
  113. package/lib/prisma-field-builder.js.map +1 -0
  114. package/lib/refs.d.ts +15 -0
  115. package/lib/refs.d.ts.map +1 -0
  116. package/lib/refs.js +73 -0
  117. package/lib/refs.js.map +1 -0
  118. package/lib/schema-builder.d.ts +2 -0
  119. package/lib/schema-builder.d.ts.map +1 -0
  120. package/lib/schema-builder.js +89 -0
  121. package/lib/schema-builder.js.map +1 -0
  122. package/lib/types.d.ts +177 -0
  123. package/lib/types.d.ts.map +1 -0
  124. package/lib/types.js +4 -0
  125. package/lib/types.js.map +1 -0
  126. package/lib/util/index.d.ts +5 -0
  127. package/lib/util/index.d.ts.map +1 -0
  128. package/lib/util/index.js +30 -0
  129. package/lib/util/index.js.map +1 -0
  130. package/lib/util/map-includes.d.ts +6 -0
  131. package/lib/util/map-includes.d.ts.map +1 -0
  132. package/lib/util/map-includes.js +189 -0
  133. package/lib/util/map-includes.js.map +1 -0
  134. package/lib/util/merge-includes.d.ts +3 -0
  135. package/lib/util/merge-includes.d.ts.map +1 -0
  136. package/lib/util/merge-includes.js +96 -0
  137. package/lib/util/merge-includes.js.map +1 -0
  138. package/package.json +71 -0
  139. package/src/cursors.ts +159 -0
  140. package/src/field-builder.ts +117 -0
  141. package/src/generator.ts +191 -0
  142. package/src/global-types.ts +196 -0
  143. package/src/index.ts +18 -0
  144. package/src/loader-map.ts +48 -0
  145. package/src/model-loader.ts +152 -0
  146. package/src/node-ref.ts +34 -0
  147. package/src/object-ref.ts +8 -0
  148. package/src/prisma-field-builder.ts +375 -0
  149. package/src/refs.ts +112 -0
  150. package/src/schema-builder.ts +121 -0
  151. package/src/types.ts +502 -0
  152. package/src/util/index.ts +26 -0
  153. package/src/util/map-includes.ts +329 -0
  154. package/src/util/merge-includes.ts +121 -0
  155. package/tsconfig.json +21 -0
@@ -0,0 +1,191 @@
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
+
49
+ ts.factory.createTypeLiteralNode([
50
+ ts.factory.createPropertySignature(
51
+ [],
52
+ 'Name',
53
+ undefined,
54
+ ts.factory.createLiteralTypeNode(ts.factory.createStringLiteral(model.name)),
55
+ ),
56
+ ts.factory.createPropertySignature(
57
+ [],
58
+ 'Shape',
59
+ undefined,
60
+ ts.factory.createTypeReferenceNode(model.name),
61
+ ),
62
+ ts.factory.createPropertySignature(
63
+ [],
64
+ 'Include',
65
+ undefined,
66
+ relations.length > 0
67
+ ? ts.factory.createTypeReferenceNode(`Prisma.${model.name}Include`)
68
+ : ts.factory.createTypeReferenceNode('never'),
69
+ ),
70
+ ts.factory.createPropertySignature(
71
+ [],
72
+ 'Where',
73
+ undefined,
74
+ ts.factory.createTypeReferenceNode(`Prisma.${model.name}WhereUniqueInput`),
75
+ ),
76
+ ts.factory.createPropertySignature(
77
+ [],
78
+ 'Fields',
79
+ undefined,
80
+ relations.length > 0
81
+ ? ts.factory.createUnionTypeNode(
82
+ relations.map((field) =>
83
+ ts.factory.createLiteralTypeNode(ts.factory.createStringLiteral(field.name)),
84
+ ),
85
+ )
86
+ : ts.factory.createTypeReferenceNode('never'),
87
+ ),
88
+ ts.factory.createPropertySignature(
89
+ [],
90
+ 'ListRelations',
91
+ undefined,
92
+ listRelations.length > 0
93
+ ? ts.factory.createUnionTypeNode(
94
+ listRelations.map((field) =>
95
+ ts.factory.createLiteralTypeNode(ts.factory.createStringLiteral(field.name)),
96
+ ),
97
+ )
98
+ : ts.factory.createTypeReferenceNode('never'),
99
+ ),
100
+ ts.factory.createPropertySignature(
101
+ [],
102
+ 'Relations',
103
+ undefined,
104
+ ts.factory.createTypeLiteralNode(
105
+ relations.map((field) => {
106
+ const typeName = field.type;
107
+
108
+ return ts.factory.createPropertySignature(
109
+ [],
110
+ field.name,
111
+ undefined,
112
+ ts.factory.createTypeLiteralNode([
113
+ ts.factory.createPropertySignature(
114
+ [],
115
+ 'Shape',
116
+ undefined,
117
+ field.isList
118
+ ? ts.factory.createArrayTypeNode(
119
+ ts.factory.createTypeReferenceNode(typeName),
120
+ )
121
+ : field.isRequired
122
+ ? ts.factory.createTypeReferenceNode(typeName)
123
+ : ts.factory.createUnionTypeNode([
124
+ ts.factory.createTypeReferenceNode(typeName),
125
+ ts.factory.createLiteralTypeNode(ts.factory.createNull()),
126
+ ]),
127
+ ),
128
+ ts.factory.createPropertySignature(
129
+ [],
130
+ 'Types',
131
+ undefined,
132
+ ts.factory.createIndexedAccessTypeNode(
133
+ ts.factory.createTypeReferenceNode('PrismaTypes'),
134
+ ts.factory.createLiteralTypeNode(ts.factory.createStringLiteral(typeName)),
135
+ ),
136
+ ),
137
+ ]),
138
+ );
139
+ }),
140
+ ),
141
+ ),
142
+ ]),
143
+ );
144
+ });
145
+
146
+ const prismaTypes = ts.factory.createInterfaceDeclaration(
147
+ [],
148
+ [
149
+ ts.factory.createModifier(SyntaxKind.ExportKeyword),
150
+ ts.factory.createModifier(SyntaxKind.DefaultKeyword),
151
+ ],
152
+ 'PrismaTypes',
153
+ [],
154
+ [],
155
+ modelTypes,
156
+ );
157
+ const printer = ts.createPrinter({});
158
+
159
+ const sourcefile = ts.createSourceFile(
160
+ options.generator.output!.value,
161
+ '',
162
+ ScriptTarget.ESNext,
163
+ false,
164
+ ScriptKind.TS,
165
+ );
166
+
167
+ const nodes = ts.factory.createNodeArray([importStatement, prismaTypes]);
168
+
169
+ const result = printer.printList(ListFormat.SourceFileStatements, nodes, sourcefile);
170
+
171
+ await new Promise<void>((resolve, reject) => {
172
+ mkdir(dirname(sourcefile.fileName), { recursive: true }, (err) => {
173
+ if (err) {
174
+ reject(err);
175
+ } else {
176
+ resolve();
177
+ }
178
+ });
179
+ });
180
+
181
+ return new Promise<void>((resolve, reject) => {
182
+ writeFile(sourcefile.fileName, result, (err) => {
183
+ if (err) {
184
+ reject(err);
185
+ } else {
186
+ resolve();
187
+ }
188
+ });
189
+ });
190
+ },
191
+ });
@@ -0,0 +1,196 @@
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
+ OutputType,
11
+ PluginName,
12
+ SchemaTypes,
13
+ ShapeFromTypeParam,
14
+ } from '@pothos/core';
15
+ import PrismaNodeRef from './node-ref';
16
+ import { prismaModelKey, PrismaObjectRef } from './object-ref';
17
+ import {
18
+ PrismaConnectionFieldOptions,
19
+ PrismaFieldOptions,
20
+ PrismaModelTypes,
21
+ PrismaNodeOptions,
22
+ PrismaObjectTypeOptions,
23
+ ShapeWithInclude,
24
+ } from './types';
25
+ import { PrismaPlugin, ShapeFromConnection } 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
+ ) => PrismaObjectRef<Model, 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<Model, 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
86
+ | PrismaObjectRef<PrismaModelTypes>
87
+ | keyof Types['PrismaTypes']
88
+ | [keyof Types['PrismaTypes']]
89
+ | [PrismaObjectRef<PrismaModelTypes>],
90
+ Nullable extends FieldNullability<Type>,
91
+ ResolveReturnShape,
92
+ Type extends TypeParam extends [unknown]
93
+ ? [ObjectRef<Model['Shape']>]
94
+ : ObjectRef<Model['Shape']>,
95
+ Model extends PrismaModelTypes = PrismaModelTypes &
96
+ (TypeParam extends [keyof Types['PrismaTypes']]
97
+ ? Types['PrismaTypes'][TypeParam[0]]
98
+ : TypeParam extends [PrismaObjectRef<PrismaModelTypes>]
99
+ ? TypeParam[0][typeof prismaModelKey]
100
+ : TypeParam extends PrismaObjectRef<PrismaModelTypes>
101
+ ? TypeParam[typeof prismaModelKey]
102
+ : TypeParam extends keyof Types['PrismaTypes']
103
+ ? Types['PrismaTypes'][TypeParam]
104
+ : never),
105
+ >(
106
+ options: PrismaFieldOptions<
107
+ Types,
108
+ ParentShape,
109
+ TypeParam,
110
+ Model,
111
+ Type,
112
+ Args,
113
+ Nullable,
114
+ ResolveReturnShape,
115
+ Kind
116
+ >,
117
+ ) => FieldRef<ShapeFromTypeParam<Types, Type, Nullable>>;
118
+
119
+ prismaConnection: 'relay' extends PluginName
120
+ ? <
121
+ Type extends PrismaObjectRef<PrismaModelTypes> | keyof Types['PrismaTypes'],
122
+ Nullable extends boolean,
123
+ ResolveReturnShape,
124
+ Args extends InputFieldMap = {},
125
+ Model extends PrismaModelTypes = Type extends PrismaObjectRef<infer T>
126
+ ? T
127
+ : PrismaModelTypes & Types['PrismaTypes'][Type & keyof Types['PrismaTypes']],
128
+ >(
129
+ ...args: NormalizeArgs<
130
+ [
131
+ options: PrismaConnectionFieldOptions<
132
+ Types,
133
+ ParentShape,
134
+ Type,
135
+ Model,
136
+ ObjectRef<Model['Shape']>,
137
+ Nullable,
138
+ Args,
139
+ ResolveReturnShape,
140
+ Kind
141
+ >,
142
+ connectionOptions?: ConnectionObjectOptions<
143
+ Types,
144
+ ObjectRef<Model['Shape']>,
145
+ false,
146
+ false,
147
+ ResolveReturnShape
148
+ >,
149
+ edgeOptions?: ConnectionEdgeObjectOptions<
150
+ Types,
151
+ ObjectRef<Model['Shape']>,
152
+ false,
153
+ ResolveReturnShape
154
+ >,
155
+ ]
156
+ >
157
+ ) => FieldRef<ShapeFromConnection<ConnectionShapeHelper<Types, Model['Shape'], Nullable>>>
158
+ : '@pothos/plugin-relay is required to use this method';
159
+ }
160
+
161
+ export interface ConnectionFieldOptions<
162
+ Types extends SchemaTypes,
163
+ ParentShape,
164
+ Type extends OutputType<Types>,
165
+ Nullable extends boolean,
166
+ EdgeNullability extends FieldNullability<[unknown]>,
167
+ NodeNullability extends boolean,
168
+ Args extends InputFieldMap,
169
+ ResolveReturnShape,
170
+ > {}
171
+
172
+ export interface ConnectionObjectOptions<
173
+ Types extends SchemaTypes,
174
+ Type extends OutputType<Types>,
175
+ EdgeNullability extends FieldNullability<[unknown]>,
176
+ NodeNullability extends boolean,
177
+ Resolved,
178
+ > {}
179
+
180
+ export interface ConnectionEdgeObjectOptions<
181
+ Types extends SchemaTypes,
182
+ Type extends OutputType<Types>,
183
+ NodeNullability extends boolean,
184
+ Resolved,
185
+ > {}
186
+
187
+ export interface DefaultConnectionArguments {
188
+ first?: number | null | undefined;
189
+ last?: number | null | undefined;
190
+ before?: string | null | undefined;
191
+ after?: string | null | undefined;
192
+ }
193
+
194
+ export interface ConnectionShapeHelper<Types extends SchemaTypes, T, Nullable> {}
195
+ }
196
+ }
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,152 @@
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 loadSelf(include: unknown, context: {}) {
70
+ for (const entry of this.staged) {
71
+ const merged = mergeIncludes(entry.include, include as Record<string, unknown>);
72
+
73
+ if (merged) {
74
+ entry.include = merged as Record<string, unknown>;
75
+
76
+ return entry.promise;
77
+ }
78
+ }
79
+
80
+ return this.initLoad(null, include, context);
81
+ }
82
+
83
+ async loadRelation(relation: string, include: unknown, context: {}) {
84
+ let promise;
85
+ for (const entry of this.staged) {
86
+ if (entry.include[relation] === undefined) {
87
+ promise = entry.promise;
88
+ entry.include[relation] = include;
89
+
90
+ break;
91
+ }
92
+
93
+ const merged = mergeIncludes(
94
+ entry.include[relation] as Record<string, unknown>,
95
+ include as Record<string, unknown>,
96
+ );
97
+
98
+ if (merged) {
99
+ entry.include[relation] = merged;
100
+ break;
101
+ }
102
+ }
103
+
104
+ if (!promise) {
105
+ promise = this.initLoad(relation, include, context);
106
+ }
107
+
108
+ const result = await promise;
109
+
110
+ return result[relation];
111
+ }
112
+
113
+ async initLoad(relation: string | null, includeArg: unknown, context: {}, count = false) {
114
+ const include: Record<string, unknown> =
115
+ (relation &&
116
+ (count
117
+ ? {
118
+ _count: {
119
+ select: {
120
+ [relation]: true,
121
+ },
122
+ },
123
+ }
124
+ : {
125
+ [relation]: includeArg,
126
+ })) ||
127
+ {};
128
+
129
+ const promise = new Promise<Record<string, unknown>>((resolve, reject) => {
130
+ setTimeout(() => {
131
+ this.staged.delete(entry);
132
+
133
+ resolve(
134
+ this.delegate.findUnique({
135
+ rejectOnNotFound: true,
136
+ where: { ...(this.findUnique(this.model, context) as {}) },
137
+ include: Object.keys(include).length > 0 ? include : undefined,
138
+ } as never) as Promise<Record<string, unknown>>,
139
+ );
140
+ }, 0);
141
+ });
142
+
143
+ const entry = {
144
+ promise,
145
+ include,
146
+ };
147
+
148
+ this.staged.add(entry);
149
+
150
+ return promise;
151
+ }
152
+ }
@@ -0,0 +1,34 @@
1
+ import { abstractReturnShapeKey, brandWithType, typeBrandKey } from '@pothos/core';
2
+ import { PrismaObjectRef } from './object-ref';
3
+ import { WithBrand } from './types';
4
+ import { PrismaModelTypes } from '.';
5
+
6
+ export default class PrismaNodeRef<Model extends PrismaModelTypes, T> extends PrismaObjectRef<
7
+ Model,
8
+ T
9
+ > {
10
+ [abstractReturnShapeKey]: WithBrand<T>;
11
+
12
+ addBrand<V extends T | T[]>(
13
+ value: V,
14
+ ): V extends T[] ? { [K in keyof V]: WithBrand<V[K]> } : WithBrand<V> {
15
+ if (Array.isArray(value)) {
16
+ value.forEach((val) => void brandWithType(val, this.name as never));
17
+
18
+ return value as never;
19
+ }
20
+
21
+ brandWithType(value, this.name as never);
22
+
23
+ return value as never;
24
+ }
25
+
26
+ hasBrand(value: unknown) {
27
+ return (
28
+ typeof value === 'object' &&
29
+ value !== null &&
30
+ typeBrandKey in value &&
31
+ (value as { [typeBrandKey]?: unknown })[typeBrandKey] === this.name
32
+ );
33
+ }
34
+ }
@@ -0,0 +1,8 @@
1
+ import { ObjectRef } from '@pothos/core';
2
+ import { PrismaModelTypes } from '.';
3
+
4
+ export const prismaModelKey = Symbol.for('Pothos.prismaModelKey');
5
+
6
+ export class PrismaObjectRef<Model extends PrismaModelTypes, T = {}> extends ObjectRef<T> {
7
+ [prismaModelKey]: Model;
8
+ }