@pothos/plugin-drizzle 0.18.1 → 0.19.1

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 (165) hide show
  1. package/CHANGELOG.md +357 -0
  2. package/README.md +1199 -563
  3. package/dts/connection-helpers.d.ts +18 -8
  4. package/dts/connection-helpers.d.ts.map +1 -1
  5. package/dts/drizzle-field-builder.d.ts +4 -11
  6. package/dts/drizzle-field-builder.d.ts.map +1 -1
  7. package/dts/global-types.d.ts.map +1 -1
  8. package/dts/index.d.ts.map +1 -1
  9. package/dts/model-loader.d.ts +18 -17
  10. package/dts/model-loader.d.ts.map +1 -1
  11. package/dts/types.d.ts +82 -22
  12. package/dts/types.d.ts.map +1 -1
  13. package/dts/utils/adapter.d.ts +39 -0
  14. package/dts/utils/adapter.d.ts.map +1 -0
  15. package/dts/utils/await-selections.d.ts +12 -0
  16. package/dts/utils/await-selections.d.ts.map +1 -0
  17. package/dts/utils/config.d.ts +23 -3
  18. package/dts/utils/config.d.ts.map +1 -1
  19. package/dts/utils/cursors.d.ts +6 -6
  20. package/dts/utils/cursors.d.ts.map +1 -1
  21. package/dts/utils/map-query.d.ts +22 -21
  22. package/dts/utils/map-query.d.ts.map +1 -1
  23. package/dts/utils/path-info.d.ts +13 -0
  24. package/dts/utils/path-info.d.ts.map +1 -0
  25. package/dts/utils/relation-filter.d.ts +48 -0
  26. package/dts/utils/relation-filter.d.ts.map +1 -0
  27. package/dts/utils/selections.d.ts +2 -21
  28. package/dts/utils/selections.d.ts.map +1 -1
  29. package/esm/connection-helpers.d.ts +18 -8
  30. package/esm/connection-helpers.d.ts.map +1 -1
  31. package/esm/connection-helpers.js +52 -19
  32. package/esm/connection-helpers.js.map +1 -1
  33. package/esm/drizzle-field-builder.d.ts +4 -11
  34. package/esm/drizzle-field-builder.d.ts.map +1 -1
  35. package/esm/drizzle-field-builder.js +138 -96
  36. package/esm/drizzle-field-builder.js.map +1 -1
  37. package/esm/field-builder.js +61 -47
  38. package/esm/field-builder.js.map +1 -1
  39. package/esm/global-types.d.ts.map +1 -1
  40. package/esm/index.d.ts.map +1 -1
  41. package/esm/index.js +37 -61
  42. package/esm/index.js.map +1 -1
  43. package/esm/model-loader.d.ts +18 -17
  44. package/esm/model-loader.d.ts.map +1 -1
  45. package/esm/model-loader.js +76 -45
  46. package/esm/model-loader.js.map +1 -1
  47. package/esm/schema-builder.js +40 -149
  48. package/esm/schema-builder.js.map +1 -1
  49. package/esm/types.d.ts +82 -22
  50. package/esm/types.d.ts.map +1 -1
  51. package/esm/types.js.map +1 -1
  52. package/esm/utils/adapter.d.ts +39 -0
  53. package/esm/utils/adapter.d.ts.map +1 -0
  54. package/esm/utils/adapter.js +137 -0
  55. package/esm/utils/adapter.js.map +1 -0
  56. package/esm/utils/await-selections.d.ts +12 -0
  57. package/esm/utils/await-selections.d.ts.map +1 -0
  58. package/esm/utils/await-selections.js +19 -0
  59. package/esm/utils/await-selections.js.map +1 -0
  60. package/esm/utils/config.d.ts +23 -3
  61. package/esm/utils/config.d.ts.map +1 -1
  62. package/esm/utils/config.js +1 -4
  63. package/esm/utils/config.js.map +1 -1
  64. package/esm/utils/cursors.d.ts +6 -6
  65. package/esm/utils/cursors.d.ts.map +1 -1
  66. package/esm/utils/cursors.js +75 -92
  67. package/esm/utils/cursors.js.map +1 -1
  68. package/esm/utils/map-query.d.ts +22 -21
  69. package/esm/utils/map-query.d.ts.map +1 -1
  70. package/esm/utils/map-query.js +40 -354
  71. package/esm/utils/map-query.js.map +1 -1
  72. package/esm/utils/path-info.d.ts +13 -0
  73. package/esm/utils/path-info.d.ts.map +1 -0
  74. package/esm/utils/path-info.js +37 -0
  75. package/esm/utils/path-info.js.map +1 -0
  76. package/esm/utils/relation-filter.d.ts +48 -0
  77. package/esm/utils/relation-filter.d.ts.map +1 -0
  78. package/esm/utils/relation-filter.js +92 -0
  79. package/esm/utils/relation-filter.js.map +1 -0
  80. package/esm/utils/selections.d.ts +2 -21
  81. package/esm/utils/selections.d.ts.map +1 -1
  82. package/esm/utils/selections.js +1 -133
  83. package/esm/utils/selections.js.map +1 -1
  84. package/lib/connection-helpers.js +51 -18
  85. package/lib/connection-helpers.js.map +1 -1
  86. package/lib/drizzle-field-builder.js +136 -94
  87. package/lib/drizzle-field-builder.js.map +1 -1
  88. package/lib/field-builder.js +58 -44
  89. package/lib/field-builder.js.map +1 -1
  90. package/lib/index.js +36 -60
  91. package/lib/index.js.map +1 -1
  92. package/lib/model-loader.js +76 -45
  93. package/lib/model-loader.js.map +1 -1
  94. package/lib/schema-builder.js +40 -149
  95. package/lib/schema-builder.js.map +1 -1
  96. package/lib/types.js.map +1 -1
  97. package/lib/utils/adapter.js +147 -0
  98. package/lib/utils/adapter.js.map +1 -0
  99. package/lib/utils/await-selections.js +20 -0
  100. package/lib/utils/await-selections.js.map +1 -0
  101. package/lib/utils/config.js +1 -4
  102. package/lib/utils/config.js.map +1 -1
  103. package/lib/utils/cursors.js +67 -90
  104. package/lib/utils/cursors.js.map +1 -1
  105. package/lib/utils/map-query.js +28 -359
  106. package/lib/utils/map-query.js.map +1 -1
  107. package/lib/utils/path-info.js +39 -0
  108. package/lib/utils/path-info.js.map +1 -0
  109. package/lib/utils/relation-filter.js +91 -0
  110. package/lib/utils/relation-filter.js.map +1 -0
  111. package/lib/utils/selections.js +3 -158
  112. package/lib/utils/selections.js.map +1 -1
  113. package/package.json +10 -8
  114. package/src/connection-helpers.ts +168 -54
  115. package/src/drizzle-field-builder.ts +296 -176
  116. package/src/field-builder.ts +110 -77
  117. package/src/global-types.ts +0 -32
  118. package/src/index.ts +42 -68
  119. package/src/model-loader.ts +110 -71
  120. package/src/schema-builder.ts +76 -169
  121. package/src/types.ts +178 -46
  122. package/src/utils/adapter.ts +197 -0
  123. package/src/utils/await-selections.ts +31 -0
  124. package/src/utils/config.ts +1 -5
  125. package/src/utils/cursors.ts +84 -83
  126. package/src/utils/map-query.ts +51 -734
  127. package/src/utils/path-info.ts +40 -0
  128. package/src/utils/relation-filter.ts +185 -0
  129. package/src/utils/selections.ts +2 -207
  130. package/dts/utils/deep-equal.d.ts +0 -2
  131. package/dts/utils/deep-equal.d.ts.map +0 -1
  132. package/dts/utils/drizzle-graphql.d.ts +0 -2
  133. package/dts/utils/drizzle-graphql.d.ts.map +0 -1
  134. package/dts/utils/loader-map.d.ts +0 -11
  135. package/dts/utils/loader-map.d.ts.map +0 -1
  136. package/dts/utils/usage.d.ts +0 -5
  137. package/dts/utils/usage.d.ts.map +0 -1
  138. package/esm/utils/deep-equal.d.ts +0 -2
  139. package/esm/utils/deep-equal.d.ts.map +0 -1
  140. package/esm/utils/deep-equal.js +0 -45
  141. package/esm/utils/deep-equal.js.map +0 -1
  142. package/esm/utils/drizzle-graphql.d.ts +0 -2
  143. package/esm/utils/drizzle-graphql.d.ts.map +0 -1
  144. package/esm/utils/drizzle-graphql.js +0 -232
  145. package/esm/utils/drizzle-graphql.js.map +0 -1
  146. package/esm/utils/loader-map.d.ts +0 -11
  147. package/esm/utils/loader-map.d.ts.map +0 -1
  148. package/esm/utils/loader-map.js +0 -35
  149. package/esm/utils/loader-map.js.map +0 -1
  150. package/esm/utils/usage.d.ts +0 -5
  151. package/esm/utils/usage.d.ts.map +0 -1
  152. package/esm/utils/usage.js +0 -59
  153. package/esm/utils/usage.js.map +0 -1
  154. package/lib/utils/deep-equal.js +0 -55
  155. package/lib/utils/deep-equal.js.map +0 -1
  156. package/lib/utils/drizzle-graphql.js +0 -236
  157. package/lib/utils/drizzle-graphql.js.map +0 -1
  158. package/lib/utils/loader-map.js +0 -56
  159. package/lib/utils/loader-map.js.map +0 -1
  160. package/lib/utils/usage.js +0 -83
  161. package/lib/utils/usage.js.map +0 -1
  162. package/src/utils/deep-equal.ts +0 -57
  163. package/src/utils/drizzle-graphql.ts +0 -274
  164. package/src/utils/loader-map.ts +0 -49
  165. package/src/utils/usage.ts +0 -62
@@ -1,13 +1,20 @@
1
1
  import type { FieldRef, MaybePromise } from '@pothos/core';
2
- import { type FieldKind, ObjectRef, RootFieldBuilder, type SchemaTypes } from '@pothos/core';
2
+ import {
3
+ type FieldKind,
4
+ isThenable,
5
+ ObjectRef,
6
+ RootFieldBuilder,
7
+ type SchemaTypes,
8
+ } from '@pothos/core';
3
9
  import type { TableRelationalConfig } from 'drizzle-orm';
4
10
  import type { GraphQLResolveInfo } from 'graphql';
5
- import { isInterfaceType, isObjectType, Kind } from 'graphql';
11
+ import { getNamedType, isInterfaceType, isObjectType, Kind } from 'graphql';
6
12
  import type { DrizzleRef } from './interface-ref.js';
7
13
  import type { DrizzleConnectionFieldOptions } from './types.js';
14
+ import type { DrizzlePlan } from './utils/adapter.js';
8
15
  import { getSchemaConfig } from './utils/config.js';
9
16
  import { resolveDrizzleCursorConnection } from './utils/cursors.js';
10
- import { queryFromInfo } from './utils/map-query.js';
17
+ import { planFromInfo, queryFromPlan } from './utils/map-query.js';
11
18
  import { getRefFromModel } from './utils/refs.js';
12
19
  import type { SelectionMap } from './utils/selections.js';
13
20
 
@@ -17,34 +24,56 @@ const fieldBuilderProto = RootFieldBuilder.prototype as PothosSchemaTypes.RootFi
17
24
  FieldKind
18
25
  >;
19
26
 
20
- fieldBuilderProto.drizzleField = function drizzleField({ type, resolve, ...options }) {
27
+ type DrizzleFieldType =
28
+ | ObjectRef<SchemaTypes, unknown>
29
+ | [ObjectRef<SchemaTypes, unknown> | string];
30
+
31
+ /** The `type` of a `drizzleField`, with a table name resolved to its ref, list-ness kept. */
32
+ function refForType(
33
+ builder: PothosSchemaTypes.SchemaBuilder<SchemaTypes>,
34
+ type: DrizzleFieldType | string,
35
+ ) {
21
36
  const modelOrRef = Array.isArray(type) ? type[0] : type;
22
37
  const typeRef =
23
38
  typeof modelOrRef === 'string'
24
- ? getRefFromModel(modelOrRef, this.builder)
39
+ ? getRefFromModel(modelOrRef, builder)
25
40
  : (modelOrRef as ObjectRef<SchemaTypes, unknown>);
26
- const typeParam = Array.isArray(type)
27
- ? ([typeRef] as [ObjectRef<SchemaTypes, unknown>])
28
- : typeRef;
41
+
42
+ return Array.isArray(type) ? ([typeRef] as [ObjectRef<SchemaTypes, unknown>]) : typeRef;
43
+ }
44
+
45
+ /**
46
+ * The resolver of a `drizzleField`: the plan for the field, then the user's resolver with a query
47
+ * builder over it. A promise while a selection beneath the field is async, so the resolver runs
48
+ * once the plan has settled and the builder it is handed never returns one. Built once per field,
49
+ * so a synchronous plan allocates nothing but the builder itself.
50
+ */
51
+ function planResolver(
52
+ builder: PothosSchemaTypes.SchemaBuilder<SchemaTypes>,
53
+ resolve: (...args: unknown[]) => unknown,
54
+ ) {
55
+ const run = (
56
+ plan: DrizzlePlan | undefined,
57
+ parent: unknown,
58
+ args: unknown,
59
+ context: {},
60
+ info: GraphQLResolveInfo,
61
+ ) => resolve((select?: SelectionMap) => queryFromPlan(plan, select), parent, args, context, info);
62
+
63
+ return (parent: unknown, args: unknown, context: {}, info: GraphQLResolveInfo) => {
64
+ const plan = planFromInfo({ config: getSchemaConfig(builder), context, info });
65
+
66
+ return isThenable(plan)
67
+ ? plan.then((settled) => run(settled as DrizzlePlan | undefined, parent, args, context, info))
68
+ : run(plan, parent, args, context, info);
69
+ };
70
+ }
71
+
72
+ fieldBuilderProto.drizzleField = function drizzleField({ type, resolve, ...options }) {
29
73
  return this.field({
30
74
  ...(options as {}),
31
- type: typeParam,
32
- resolve: (parent: unknown, args: unknown, context: {}, info: GraphQLResolveInfo) => {
33
- return resolve(
34
- (select) =>
35
- queryFromInfo({
36
- config: getSchemaConfig(this.builder),
37
- context,
38
- select,
39
- info,
40
- // withUsageCheck: !!this.builder.options.drizzle?.onUnusedQuery,
41
- }) as never,
42
- parent,
43
- args as never,
44
- context,
45
- info,
46
- ) as never;
47
- },
75
+ type: refForType(this.builder, type as DrizzleFieldType),
76
+ resolve: planResolver(this.builder, resolve as never),
48
77
  }) as never;
49
78
  };
50
79
 
@@ -54,38 +83,14 @@ fieldBuilderProto.drizzleFieldWithInput = function drizzleFieldWithInput(
54
83
  type,
55
84
  resolve,
56
85
  ...options
57
- }: { type: ObjectRef<SchemaTypes, unknown> | [string]; resolve: (...args: unknown[]) => unknown },
86
+ }: { type: DrizzleFieldType; resolve: (...args: unknown[]) => unknown },
58
87
  ) {
59
- const modelOrRef = Array.isArray(type) ? type[0] : type;
60
- const typeRef =
61
- typeof modelOrRef === 'string'
62
- ? getRefFromModel(modelOrRef, this.builder)
63
- : (modelOrRef as ObjectRef<SchemaTypes, unknown>);
64
- const typeParam = Array.isArray(type)
65
- ? ([typeRef] as [ObjectRef<SchemaTypes, unknown>])
66
- : typeRef;
67
-
68
88
  return (
69
89
  this as typeof fieldBuilderProto & { fieldWithInput: typeof fieldBuilderProto.field }
70
90
  ).fieldWithInput({
71
91
  ...(options as {}),
72
- type: typeParam,
73
- resolve: (parent: unknown, args: unknown, context: {}, info: GraphQLResolveInfo) => {
74
- return resolve(
75
- (select: SelectionMap) =>
76
- queryFromInfo({
77
- config: getSchemaConfig(this.builder),
78
- context,
79
- select,
80
- info,
81
- // withUsageCheck: !!this.builder.options.drizzle?.onUnusedQuery,
82
- }),
83
- parent,
84
- args as never,
85
- context,
86
- info,
87
- ) as never;
88
- },
92
+ type: refForType(this.builder, type),
93
+ resolve: planResolver(this.builder, resolve),
89
94
  }) as never;
90
95
  } as never;
91
96
 
@@ -121,6 +126,38 @@ fieldBuilderProto.drizzleConnection = function drizzleConnection<
121
126
  const ref = typeof type === 'string' ? getRefFromModel(type, this.builder) : type;
122
127
  const typeName = this.builder.configStore.getTypeConfig(ref).name;
123
128
  const tableName = typeof type === 'string' ? type : (ref as DrizzleRef<SchemaTypes>).tableName;
129
+
130
+ // Built once per field: resolving with a synchronous plan allocates nothing beyond the
131
+ // callback `resolveDrizzleCursorConnection` was already handed.
132
+ const resolveConnection = (
133
+ plan: DrizzlePlan | undefined,
134
+ parent: unknown,
135
+ args: PothosSchemaTypes.DefaultConnectionArguments,
136
+ context: {},
137
+ info: GraphQLResolveInfo,
138
+ totalCountOnly: boolean,
139
+ ) =>
140
+ resolveDrizzleCursorConnection(
141
+ tableName,
142
+ plan,
143
+ getSchemaConfig(this.builder),
144
+ {
145
+ ctx: context,
146
+ maxSize,
147
+ defaultSize,
148
+ args,
149
+ totalCount: totalCount && (() => totalCount(parent, args as never, context, info)),
150
+ },
151
+ (q) => {
152
+ if (totalCountOnly) {
153
+ return [];
154
+ }
155
+
156
+ return resolve(q as never, parent, args as never, context, info) as never;
157
+ },
158
+ parent,
159
+ );
160
+
124
161
  const fieldRef = (
125
162
  this as typeof fieldBuilderProto & {
126
163
  connection: (...args: unknown[]) => FieldRef<SchemaTypes, unknown>;
@@ -135,7 +172,7 @@ fieldBuilderProto.drizzleConnection = function drizzleConnection<
135
172
  context: {},
136
173
  info: GraphQLResolveInfo,
137
174
  ) => {
138
- const returnType = info.returnType;
175
+ const returnType = getNamedType(info.returnType);
139
176
  const fields =
140
177
  isObjectType(returnType) || isInterfaceType(returnType) ? returnType.getFields() : {};
141
178
 
@@ -150,32 +187,28 @@ fieldBuilderProto.drizzleConnection = function drizzleConnection<
150
187
  ),
151
188
  );
152
189
 
153
- return resolveDrizzleCursorConnection(
154
- tableName,
190
+ // Planned before the resolver runs, so the builder it is handed is synchronous even when
191
+ // a selection beneath the connection is async.
192
+ const plan = planFromInfo({
193
+ config: getSchemaConfig(this.builder),
194
+ context,
155
195
  info,
196
+ paths: [['nodes'], ['edges', 'node']],
156
197
  typeName,
157
- getSchemaConfig(this.builder),
158
- {
159
- ctx: context,
160
- maxSize,
161
- defaultSize,
162
- args,
163
- totalCount: totalCount && (() => totalCount(parent, args as never, context, info)),
164
- },
165
- (q) => {
166
- if (totalCountOnly) {
167
- return [];
168
- }
169
-
170
- // return checkIfQueryIsUsed(
171
- // this.builder,
172
- // query,
173
- // info,
174
- return resolve(q as never, parent, args as never, context, info) as never;
175
- // );
176
- },
177
- parent,
178
- );
198
+ });
199
+
200
+ return isThenable(plan)
201
+ ? plan.then((settled) =>
202
+ resolveConnection(
203
+ settled as DrizzlePlan | undefined,
204
+ parent,
205
+ args,
206
+ context,
207
+ info,
208
+ totalCountOnly,
209
+ ),
210
+ )
211
+ : resolveConnection(plan, parent, args, context, info, totalCountOnly);
179
212
  },
180
213
  },
181
214
  connectionOptions instanceof ObjectRef
@@ -184,38 +184,6 @@ declare global {
184
184
  type: Type,
185
185
  fields: (t: DrizzleObjectFieldBuilder<Types, TableConfig, Shape>) => FieldMap,
186
186
  ) => void;
187
-
188
- // drizzleGraphQLOrderBy: 'addGraphQL' extends PluginName
189
- // ? <Table extends keyof Types['DrizzleRelations']>(
190
- // table: Table,
191
- // type: GraphQLInputObjectType,
192
- // ...args: NormalizeArgs<[options: AddGraphQLInputTypeOptions<Types, {}>]>
193
- // ) => InputObjectRef<Types, SQL | SQL[]>
194
- // : '@pothos/plugin-add-graphql is required to use this method';
195
-
196
- // drizzleGraphQLFilters: 'addGraphQL' extends PluginName
197
- // ? <Table extends keyof Types['DrizzleRelations']>(
198
- // table: Table,
199
- // type: GraphQLInputObjectType,
200
- // ...args: NormalizeArgs<[options: AddGraphQLInputTypeOptions<Types, {}>]>
201
- // ) => InputObjectRef<Types, SQL>
202
- // : '@pothos/plugin-add-graphql is required to use this method';
203
-
204
- // drizzleGraphQLInsert: 'addGraphQL' extends PluginName
205
- // ? <Table extends keyof Types['DrizzleRelations']>(
206
- // table: Table,
207
- // type: GraphQLInputObjectType,
208
- // ...args: NormalizeArgs<[options: AddGraphQLInputTypeOptions<Types, {}>]>
209
- // ) => InputObjectRef<Types, Record<string, unknown>>
210
- // : '@pothos/plugin-add-graphql is required to use this method';
211
-
212
- // drizzleGraphQLUpdate: 'addGraphQL' extends PluginName
213
- // ? <Table extends keyof Types['DrizzleRelations']>(
214
- // table: Table,
215
- // type: GraphQLInputObjectType,
216
- // ...args: NormalizeArgs<[options: AddGraphQLInputTypeOptions<Types, {}>]>
217
- // ) => InputObjectRef<Types, Record<string, unknown>>
218
- // : '@pothos/plugin-add-graphql is required to use this method';
219
187
  }
220
188
 
221
189
  export interface PothosKindToGraphQLType {
package/src/index.ts CHANGED
@@ -3,14 +3,17 @@ import './field-builder.js';
3
3
  import './schema-builder.js';
4
4
  import SchemaBuilder, {
5
5
  BasePlugin,
6
+ completeValue,
6
7
  type PothosOutputFieldConfig,
7
8
  PothosSchemaError,
8
9
  type PothosTypeConfig,
9
10
  type SchemaTypes,
10
11
  } from '@pothos/core';
11
- import type { GraphQLFieldResolver } from 'graphql';
12
+ import { getLoaderMapping, setFieldMapping } from '@pothos/selection-mapper';
13
+ import type { TableRelationalConfig } from 'drizzle-orm';
14
+ import type { GraphQLFieldResolver, GraphQLResolveInfo } from 'graphql';
12
15
  import type { ModelLoader } from './model-loader.js';
13
- import { getLoaderMapping, setLoaderMappings } from './utils/loader-map.js';
16
+ import type { PathInfo } from './types.js';
14
17
 
15
18
  export { drizzleConnectionHelpers } from './connection-helpers.js';
16
19
  export { DrizzleObjectFieldBuilder } from './drizzle-field-builder.js';
@@ -23,29 +26,31 @@ const pluginName = 'drizzle';
23
26
  export default pluginName;
24
27
 
25
28
  export class PothosDrizzlePlugin<Types extends SchemaTypes> extends BasePlugin<Types> {
26
- // private mappingCache = new Map<
27
- // string,
28
- // InputTypeFieldsMapping<Types, DrizzleGraphQLInputExtensions>
29
- // >();
30
29
  override onTypeConfig(typeConfig: PothosTypeConfig): PothosTypeConfig {
31
30
  if (typeConfig.kind !== 'Object' && typeConfig.kind !== 'Interface') {
32
31
  return typeConfig;
33
32
  }
34
33
 
35
34
  let model = typeConfig.extensions?.pothosDrizzleModel as string | undefined;
35
+ let table = typeConfig.extensions?.pothosDrizzleTable as TableRelationalConfig | undefined;
36
36
 
37
37
  for (const iface of typeConfig.interfaces) {
38
- const interfaceModel = this.buildCache.getTypeConfig(iface, 'Interface').extensions
39
- ?.pothosDrizzleModel as string | undefined;
38
+ const interfaceConfig = this.buildCache.getTypeConfig(iface, 'Interface');
39
+ const interfaceModel = interfaceConfig.extensions?.pothosDrizzleModel as string | undefined;
40
40
 
41
41
  if (interfaceModel) {
42
42
  if (model && model !== interfaceModel) {
43
43
  throw new PothosSchemaError(
44
- `DrizzleObjects must be based on the same prisma model as any DrizzleInterfaces they extend. ${typeConfig.name} uses ${model} and ${iface.name} uses ${interfaceModel}`,
44
+ `DrizzleObjects must be based on the same drizzle table as any DrizzleInterfaces they extend. ${typeConfig.name} uses ${model} and ${iface.name} uses ${interfaceModel}`,
45
45
  );
46
46
  }
47
47
 
48
48
  model = interfaceModel;
49
+ // A plain object type implementing a drizzle interface is walked with the interface's
50
+ // table, so fragments on it plan the relations it inherits.
51
+ table ??= interfaceConfig.extensions?.pothosDrizzleTable as
52
+ | TableRelationalConfig
53
+ | undefined;
49
54
  }
50
55
  }
51
56
 
@@ -54,6 +59,7 @@ export class PothosDrizzlePlugin<Types extends SchemaTypes> extends BasePlugin<T
54
59
  extensions: {
55
60
  ...typeConfig.extensions,
56
61
  pothosDrizzleModel: model,
62
+ pothosDrizzleTable: table,
57
63
  },
58
64
  };
59
65
  }
@@ -61,49 +67,6 @@ export class PothosDrizzlePlugin<Types extends SchemaTypes> extends BasePlugin<T
61
67
  override onOutputFieldConfig(
62
68
  fieldConfig: PothosOutputFieldConfig<Types>,
63
69
  ): PothosOutputFieldConfig<Types> | null {
64
- // const argMappings = mapInputFields<Types, DrizzleGraphQLInputExtensions>(
65
- // fieldConfig.args,
66
- // this.buildCache,
67
- // (inputField) => {
68
- // if (inputField.type.kind === 'InputObject') {
69
- // const config = this.buildCache.getTypeConfig(inputField.type.ref);
70
-
71
- // return (config.extensions?.drizzleGraphQL as DrizzleGraphQLInputExtensions) ?? null;
72
- // }
73
-
74
- // return null;
75
- // },
76
- // this.mappingCache,
77
- // );
78
-
79
- // const argMapper = argMappings
80
- // ? createInputValueMapper(argMappings, (input, mappings) => {
81
- // if (!mappings.value) {
82
- // return input;
83
- // }
84
-
85
- // const { table, tableConfig, inputType } = mappings.value;
86
-
87
- // switch (inputType) {
88
- // case 'orderBy':
89
- // return extractOrderBy(tableConfig, input as never);
90
- // case 'filters':
91
- // return extractFilters(tableConfig, table, input as never);
92
- // case 'insert':
93
- // return remapFromGraphQLSingleInput(input as never, tableConfig);
94
- // case 'update':
95
- // return remapFromGraphQLSingleInput(input as never, tableConfig);
96
-
97
- // default:
98
- // throw new Error(`Unknown drizzle input type: ${inputType}`);
99
- // }
100
- // })
101
- // : null;
102
-
103
- // const argMappers: typeof fieldConfig.argMappers = argMapper
104
- // ? [...(fieldConfig.argMappers ?? []), (args) => argMapper(args, undefined)]
105
- // : fieldConfig.argMappers;
106
-
107
70
  if (
108
71
  fieldConfig.kind === 'DrizzleObject' &&
109
72
  fieldConfig.pothosOptions.select &&
@@ -112,7 +75,6 @@ export class PothosDrizzlePlugin<Types extends SchemaTypes> extends BasePlugin<T
112
75
  const { select } = fieldConfig.pothosOptions;
113
76
  return {
114
77
  ...fieldConfig,
115
- // argMappers,
116
78
  extensions: {
117
79
  ...fieldConfig.extensions,
118
80
  pothosDrizzleSelect:
@@ -122,21 +84,18 @@ export class PothosDrizzlePlugin<Types extends SchemaTypes> extends BasePlugin<T
122
84
  ctx: Types['Context'],
123
85
  nestedQuery: (query: unknown, path?: string[]) => never,
124
86
  _resolveSelection: unknown,
125
- pathInfo: import('./types').PathInfo,
87
+ pathInfo: PathInfo,
126
88
  ) => {
127
- // Attach PathInfo properties to nestedQuery for t.field select callbacks
128
- const nestedQueryWithPath = Object.assign(nestedQuery, {
129
- path: pathInfo.path,
130
- segments: pathInfo.segments,
131
- });
132
- return {
133
- columns: {},
134
- ...(select as (args: unknown, ctx: unknown, nestedQuery: unknown) => {})(
89
+ // A field's select also reads its metadata from the nested selection function.
90
+ const nestedQueryWithPath = Object.assign(nestedQuery, pathInfo);
91
+ return completeValue(
92
+ (select as (args: unknown, ctx: unknown, nestedQuery: unknown) => {} | null)(
135
93
  args,
136
94
  ctx,
137
95
  nestedQueryWithPath,
138
96
  ),
139
- };
97
+ wrapSelect,
98
+ );
140
99
  }
141
100
  : {
142
101
  columns: {},
@@ -148,7 +107,6 @@ export class PothosDrizzlePlugin<Types extends SchemaTypes> extends BasePlugin<T
148
107
 
149
108
  return {
150
109
  ...fieldConfig,
151
- // argMappers,
152
110
  };
153
111
  }
154
112
 
@@ -161,6 +119,12 @@ export class PothosDrizzlePlugin<Types extends SchemaTypes> extends BasePlugin<T
161
119
  }
162
120
 
163
121
  const parentConfig = this.buildCache.getTypeConfig(fieldConfig.parentType);
122
+ // A field can declare how to tell whether its data is already on the parent row. When the
123
+ // row was not loaded from the planned query (a resolver returned a row it fetched itself),
124
+ // the mapping alone is not proof, and the field falls back to the model loader.
125
+ const loadedCheck = fieldConfig.extensions?.pothosDrizzleLoaded as
126
+ | ((value: unknown, info: GraphQLResolveInfo, context: object) => boolean)
127
+ | undefined;
164
128
 
165
129
  const parentTypes = new Set([fieldConfig.parentType]);
166
130
 
@@ -182,19 +146,24 @@ export class PothosDrizzlePlugin<Types extends SchemaTypes> extends BasePlugin<T
182
146
  }
183
147
 
184
148
  return (parent, args, context, info) => {
185
- let mapping = getLoaderMapping(context, info.path, info.parentType.name);
149
+ // Asked of the row: a mapping recorded for this row wins over the plan's, which answers
150
+ // for the rows the planned query loaded.
151
+ let mapping = getLoaderMapping(context, info.path, info.parentType.name, parent);
186
152
 
187
153
  if (!mapping) {
188
154
  for (const parentType of parentTypes) {
189
- mapping = getLoaderMapping(context, info.path, parentType);
155
+ mapping = getLoaderMapping(context, info.path, parentType, parent);
190
156
  if (mapping) {
191
157
  break;
192
158
  }
193
159
  }
194
160
  }
195
161
 
196
- if (mapping) {
197
- setLoaderMappings(context, info, mapping);
162
+ if ((!loadedCheck || loadedCheck(parent, info, context)) && mapping) {
163
+ // Recorded under the field's own parent type as well, so its resolver finds the pathInfo
164
+ // it was planned with, whichever same-model type the plan was made for. Against the row
165
+ // it resolves with: a sibling row of the same list may have been loaded by another plan.
166
+ setFieldMapping(context, info, mapping, parent);
198
167
 
199
168
  return resolver(parent, args, context, info);
200
169
  }
@@ -206,4 +175,9 @@ export class PothosDrizzlePlugin<Types extends SchemaTypes> extends BasePlugin<T
206
175
  }
207
176
  }
208
177
 
178
+ /** A falsy selection means the field selects nothing from the parent row. */
179
+ function wrapSelect(selected: {} | null) {
180
+ return selected ? { columns: {}, ...selected } : null;
181
+ }
182
+
209
183
  SchemaBuilder.registerPlugin(pluginName, PothosDrizzlePlugin, {});