@pothos/plugin-drizzle 0.18.0 → 0.19.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 (167) hide show
  1. package/CHANGELOG.md +351 -0
  2. package/README.md +784 -221
  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 +81 -20
  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 +81 -20
  50. package/esm/types.d.ts.map +1 -1
  51. package/esm/types.js +1 -0
  52. package/esm/types.js.map +1 -1
  53. package/esm/utils/adapter.d.ts +39 -0
  54. package/esm/utils/adapter.d.ts.map +1 -0
  55. package/esm/utils/adapter.js +137 -0
  56. package/esm/utils/adapter.js.map +1 -0
  57. package/esm/utils/await-selections.d.ts +12 -0
  58. package/esm/utils/await-selections.d.ts.map +1 -0
  59. package/esm/utils/await-selections.js +19 -0
  60. package/esm/utils/await-selections.js.map +1 -0
  61. package/esm/utils/config.d.ts +23 -3
  62. package/esm/utils/config.d.ts.map +1 -1
  63. package/esm/utils/config.js +1 -4
  64. package/esm/utils/config.js.map +1 -1
  65. package/esm/utils/cursors.d.ts +6 -6
  66. package/esm/utils/cursors.d.ts.map +1 -1
  67. package/esm/utils/cursors.js +55 -90
  68. package/esm/utils/cursors.js.map +1 -1
  69. package/esm/utils/map-query.d.ts +22 -21
  70. package/esm/utils/map-query.d.ts.map +1 -1
  71. package/esm/utils/map-query.js +40 -336
  72. package/esm/utils/map-query.js.map +1 -1
  73. package/esm/utils/path-info.d.ts +13 -0
  74. package/esm/utils/path-info.d.ts.map +1 -0
  75. package/esm/utils/path-info.js +37 -0
  76. package/esm/utils/path-info.js.map +1 -0
  77. package/esm/utils/relation-filter.d.ts +48 -0
  78. package/esm/utils/relation-filter.d.ts.map +1 -0
  79. package/esm/utils/relation-filter.js +92 -0
  80. package/esm/utils/relation-filter.js.map +1 -0
  81. package/esm/utils/selections.d.ts +2 -21
  82. package/esm/utils/selections.d.ts.map +1 -1
  83. package/esm/utils/selections.js +1 -133
  84. package/esm/utils/selections.js.map +1 -1
  85. package/lib/connection-helpers.js +51 -18
  86. package/lib/connection-helpers.js.map +1 -1
  87. package/lib/drizzle-field-builder.js +136 -94
  88. package/lib/drizzle-field-builder.js.map +1 -1
  89. package/lib/field-builder.js +58 -44
  90. package/lib/field-builder.js.map +1 -1
  91. package/lib/index.js +36 -60
  92. package/lib/index.js.map +1 -1
  93. package/lib/model-loader.js +76 -45
  94. package/lib/model-loader.js.map +1 -1
  95. package/lib/schema-builder.js +40 -149
  96. package/lib/schema-builder.js.map +1 -1
  97. package/lib/types.js +4 -0
  98. package/lib/types.js.map +1 -1
  99. package/lib/utils/adapter.js +147 -0
  100. package/lib/utils/adapter.js.map +1 -0
  101. package/lib/utils/await-selections.js +20 -0
  102. package/lib/utils/await-selections.js.map +1 -0
  103. package/lib/utils/config.js +1 -4
  104. package/lib/utils/config.js.map +1 -1
  105. package/lib/utils/cursors.js +47 -88
  106. package/lib/utils/cursors.js.map +1 -1
  107. package/lib/utils/map-query.js +28 -341
  108. package/lib/utils/map-query.js.map +1 -1
  109. package/lib/utils/path-info.js +39 -0
  110. package/lib/utils/path-info.js.map +1 -0
  111. package/lib/utils/relation-filter.js +91 -0
  112. package/lib/utils/relation-filter.js.map +1 -0
  113. package/lib/utils/selections.js +3 -158
  114. package/lib/utils/selections.js.map +1 -1
  115. package/package.json +8 -6
  116. package/src/connection-helpers.ts +167 -53
  117. package/src/drizzle-field-builder.ts +296 -176
  118. package/src/field-builder.ts +110 -77
  119. package/src/global-types.ts +0 -32
  120. package/src/index.ts +42 -68
  121. package/src/model-loader.ts +110 -71
  122. package/src/schema-builder.ts +76 -169
  123. package/src/types.ts +176 -44
  124. package/src/utils/adapter.ts +197 -0
  125. package/src/utils/await-selections.ts +31 -0
  126. package/src/utils/config.ts +1 -5
  127. package/src/utils/cursors.ts +53 -81
  128. package/src/utils/map-query.ts +51 -708
  129. package/src/utils/path-info.ts +40 -0
  130. package/src/utils/relation-filter.ts +185 -0
  131. package/src/utils/selections.ts +2 -207
  132. package/dts/utils/deep-equal.d.ts +0 -2
  133. package/dts/utils/deep-equal.d.ts.map +0 -1
  134. package/dts/utils/drizzle-graphql.d.ts +0 -2
  135. package/dts/utils/drizzle-graphql.d.ts.map +0 -1
  136. package/dts/utils/loader-map.d.ts +0 -11
  137. package/dts/utils/loader-map.d.ts.map +0 -1
  138. package/dts/utils/usage.d.ts +0 -5
  139. package/dts/utils/usage.d.ts.map +0 -1
  140. package/esm/utils/deep-equal.d.ts +0 -2
  141. package/esm/utils/deep-equal.d.ts.map +0 -1
  142. package/esm/utils/deep-equal.js +0 -45
  143. package/esm/utils/deep-equal.js.map +0 -1
  144. package/esm/utils/drizzle-graphql.d.ts +0 -2
  145. package/esm/utils/drizzle-graphql.d.ts.map +0 -1
  146. package/esm/utils/drizzle-graphql.js +0 -232
  147. package/esm/utils/drizzle-graphql.js.map +0 -1
  148. package/esm/utils/loader-map.d.ts +0 -11
  149. package/esm/utils/loader-map.d.ts.map +0 -1
  150. package/esm/utils/loader-map.js +0 -35
  151. package/esm/utils/loader-map.js.map +0 -1
  152. package/esm/utils/usage.d.ts +0 -5
  153. package/esm/utils/usage.d.ts.map +0 -1
  154. package/esm/utils/usage.js +0 -59
  155. package/esm/utils/usage.js.map +0 -1
  156. package/lib/utils/deep-equal.js +0 -55
  157. package/lib/utils/deep-equal.js.map +0 -1
  158. package/lib/utils/drizzle-graphql.js +0 -236
  159. package/lib/utils/drizzle-graphql.js.map +0 -1
  160. package/lib/utils/loader-map.js +0 -56
  161. package/lib/utils/loader-map.js.map +0 -1
  162. package/lib/utils/usage.js +0 -83
  163. package/lib/utils/usage.js.map +0 -1
  164. package/src/utils/deep-equal.ts +0 -57
  165. package/src/utils/drizzle-graphql.ts +0 -274
  166. package/src/utils/loader-map.ts +0 -49
  167. package/src/utils/usage.ts +0 -62
@@ -1,4 +1,5 @@
1
- import { createContextCache, type SchemaTypes } from '@pothos/core';
1
+ import { createContextCache, isThenable, type MaybePromise, type SchemaTypes } from '@pothos/core';
2
+ import { cacheKey, Plan, setLoaderMappings, setRowFieldMapping } from '@pothos/selection-mapper';
2
3
  import {
3
4
  type AnyTable,
4
5
  type Column,
@@ -9,22 +10,22 @@ import {
9
10
  type TableRelationalConfig,
10
11
  } from 'drizzle-orm';
11
12
  import type { GraphQLResolveInfo } from 'graphql';
12
- import { getClient, getSchemaConfig, type PothosDrizzleSchemaConfig } from './utils/config.js';
13
- import { cacheKey, setLoaderMappings } from './utils/loader-map.js';
14
- import { selectionStateFromInfo, stateFromInfo } from './utils/map-query.js';
15
13
  import {
16
- mergeSelection,
17
- type SelectionMap,
18
- type SelectionState,
19
- selectionCompatible,
20
- selectionToQuery,
21
- } from './utils/selections.js';
14
+ type DrizzleAdapter,
15
+ type DrizzleNode,
16
+ type DrizzlePlan,
17
+ type DrizzlePlayedPlan,
18
+ drizzleAdapter,
19
+ } from './utils/adapter.js';
20
+ import { getClient, getSchemaConfig, type PothosDrizzleSchemaConfig } from './utils/config.js';
21
+ import { planFromInfo } from './utils/map-query.js';
22
22
 
23
23
  interface ResolvablePromise<T> {
24
24
  promise: Promise<T>;
25
25
  resolve: (value: T) => void;
26
26
  reject: (err: unknown) => void;
27
27
  }
28
+
28
29
  export class ModelLoader {
29
30
  context: object;
30
31
 
@@ -32,14 +33,22 @@ export class ModelLoader {
32
33
 
33
34
  modelName: string;
34
35
 
35
- queryCache = new Map<string, { selection: SelectionState; query: SelectionMap }>();
36
+ // One plan per `Type@path`, a promise while a select beneath the field is async. A parent-row
37
+ // plan is played where it is made, since it is never played behind another selection; a field's
38
+ // plan is played per load, so no two loads share a node.
39
+ rowCache = new Map<string, MaybePromise<DrizzlePlayedPlan>>();
36
40
 
41
+ planCache = new Map<string, MaybePromise<DrizzlePlan>>();
42
+
43
+ // Each batch owns the node it accumulates into, so nothing a cached plan or play holds is
44
+ // changed by a row joining the batch.
37
45
  staged = new Set<{
38
- state: SelectionState;
46
+ root: DrizzleNode;
39
47
  models: Map<object, ResolvablePromise<Record<string, unknown> | null>>;
40
48
  }>();
41
49
 
42
50
  config: PothosDrizzleSchemaConfig;
51
+ adapter: DrizzleAdapter;
43
52
  table: TableRelationalConfig;
44
53
  columns: Column[];
45
54
  primaryKey: Column[];
@@ -55,6 +64,7 @@ export class ModelLoader {
55
64
  this.builder = builder;
56
65
  this.modelName = modelName;
57
66
  this.config = getSchemaConfig(builder);
67
+ this.adapter = drizzleAdapter(this.config);
58
68
  this.table = this.config.relations[modelName];
59
69
  this.primaryKey = this.config.getPrimaryKey(modelName);
60
70
  this.columns = columns ?? this.primaryKey;
@@ -93,68 +103,88 @@ export class ModelLoader {
93
103
 
94
104
  getSelection(info: GraphQLResolveInfo) {
95
105
  const key = cacheKey(info.parentType.name, info.path);
96
- if (!this.queryCache.has(key)) {
97
- const selection = selectionStateFromInfo(this.config, this.context, info);
98
- this.queryCache.set(key, {
99
- selection,
100
- query: selectionToQuery(this.config, selection),
101
- });
106
+ if (!this.rowCache.has(key)) {
107
+ this.rowCache.set(key, Plan.forParentRow(drizzleAdapter(this.config), this.context, info));
102
108
  }
103
109
 
104
- return this.queryCache.get(key)!;
110
+ return this.rowCache.get(key)!;
105
111
  }
106
112
 
107
113
  getSelectionForField(info: GraphQLResolveInfo, typeName: string) {
108
114
  const key = cacheKey(typeName, info.path);
109
- if (!this.queryCache.has(key)) {
110
- const selection = stateFromInfo({
111
- config: this.config,
112
- context: this.context,
113
- info,
114
- typeName,
115
- });
116
-
117
- this.queryCache.set(key, {
118
- selection,
119
- query: selectionToQuery(this.config, selection),
120
- });
115
+ if (!this.planCache.has(key)) {
116
+ this.planCache.set(
117
+ key,
118
+ // Walked without paths, so there is always a plan.
119
+ planFromInfo({ config: this.config, context: this.context, info, typeName })!,
120
+ );
121
121
  }
122
122
 
123
- return this.queryCache.get(key)!;
123
+ return this.planCache.get(key)!;
124
124
  }
125
125
 
126
- async loadSelection(info: GraphQLResolveInfo, model: object) {
127
- const { selection, query } = this.getSelection(info);
128
-
129
- const result = await this.stageQuery(selection, query, model);
130
-
131
- if (result) {
132
- const mappings = selection.mappings[info.path.key];
126
+ /**
127
+ * `model` reloaded with the selection of the field `info` resolves. A synchronous selection
128
+ * stages synchronously, so every row resolved in a tick joins the same batch; only a selection
129
+ * with an async select beneath the field waits for it.
130
+ */
131
+ loadSelection(info: GraphQLResolveInfo, model: object): Promise<Record<string, unknown> | null> {
132
+ const selection = this.getSelection(info);
133
+
134
+ return isThenable(selection)
135
+ ? selection.then((settled) => this.loadWith(settled, info, model))
136
+ : this.loadWith(selection, info, model);
137
+ }
133
138
 
134
- if (mappings) {
135
- setLoaderMappings(this.context, info, mappings.mappings);
139
+ private loadWith(played: DrizzlePlayedPlan, info: GraphQLResolveInfo, model: object) {
140
+ return this.stageQuery(played, model).then((result) => {
141
+ if (result) {
142
+ const mapping = played.mappings[`${info.parentType.name}@${info.path.key}`];
143
+
144
+ if (mapping) {
145
+ // Recorded for the field itself too, so its resolver finds the pathInfo it was planned
146
+ // with, along with the mappings of the fields beneath it. Against `result` alone: this
147
+ // load fetched one row, so the field's own mapping is not the whole field's to claim.
148
+ // A sibling row that falls back on a later tick would otherwise read it as proof of
149
+ // having been loaded and resolve against a row that never carried the data.
150
+ setRowFieldMapping(this.context, info, mapping, result);
151
+ }
136
152
  }
137
- }
138
153
 
139
- return result;
154
+ return result;
155
+ });
140
156
  }
141
157
 
142
- async loadSelectionForField(info: GraphQLResolveInfo, model: object, returnType: string) {
143
- const { selection, query } = this.getSelectionForField(info, returnType);
144
-
145
- const result = await this.stageQuery(selection, query, model);
158
+ /** A node loaded by id with the selection beneath the field `info` resolves, as `returnType`. */
159
+ loadSelectionForField(
160
+ info: GraphQLResolveInfo,
161
+ model: object,
162
+ returnType: string,
163
+ ): Promise<Record<string, unknown> | null> {
164
+ const selection = this.getSelectionForField(info, returnType);
165
+
166
+ return isThenable(selection)
167
+ ? selection.then((settled) => this.loadFieldWith(settled.play(), info, model))
168
+ : this.loadFieldWith(selection.play(), info, model);
169
+ }
146
170
 
147
- if (result) {
148
- setLoaderMappings(this.context, info, selection.mappings);
149
- }
171
+ private loadFieldWith(played: DrizzlePlayedPlan, info: GraphQLResolveInfo, model: object) {
172
+ return this.stageQuery(played, model).then((result) => {
173
+ if (result) {
174
+ // Relay nodes of different types share this field path. Each node plan contributes
175
+ // its type-qualified mappings, as a queryFromInfo plan would.
176
+ setLoaderMappings(this.context, info, played.mappings);
177
+ }
150
178
 
151
- return result;
179
+ return result;
180
+ });
152
181
  }
153
182
 
154
- stageQuery(selection: SelectionState, query: SelectionMap, model: object) {
183
+ stageQuery(played: DrizzlePlayedPlan, model: object) {
155
184
  for (const entry of this.staged) {
156
- if (selectionCompatible(entry.state, query)) {
157
- mergeSelection(this.config, entry.state, query);
185
+ // Node to node: the batch takes the field's play whole, never through a query.
186
+ if (this.adapter.canMergeNode(entry.root, played.root)) {
187
+ this.adapter.mergeNode(entry.root, played.root);
158
188
 
159
189
  if (!entry.models.has(model)) {
160
190
  entry.models.set(model, createResolvablePromise<Record<string, unknown> | null>());
@@ -164,13 +194,17 @@ export class ModelLoader {
164
194
  }
165
195
  }
166
196
 
167
- return this.initLoad(selection, model);
197
+ return this.initLoad(played, model);
168
198
  }
169
199
 
170
- initLoad(selection: SelectionState, model: object) {
200
+ initLoad(played: DrizzlePlayedPlan, model: object) {
171
201
  const promise = createResolvablePromise<Record<string, unknown> | null>();
202
+ const root = this.adapter.createNode(played.root.model);
203
+
204
+ this.adapter.mergeNode(root, played.root);
205
+
172
206
  const entry = {
173
- state: selection,
207
+ root,
174
208
  models: new Map([[model, promise]]),
175
209
  };
176
210
  this.staged.add(entry);
@@ -181,15 +215,10 @@ export class ModelLoader {
181
215
  nextTick.promise
182
216
  .then(() => {
183
217
  this.staged.delete(entry);
184
- const api = (
185
- client.query as Record<
186
- string,
187
- { findMany: (...args: unknown[]) => Promise<Record<string, unknown>[]> }
188
- >
189
- )[this.modelName];
218
+ const api = client.query[this.modelName];
190
219
 
191
220
  const query = api.findMany({
192
- ...selectionToQuery(this.config, selection),
221
+ ...this.adapter.toQuery(entry.root),
193
222
  where: {
194
223
  RAW: (table: AnyTable<{}>) =>
195
224
  inArray(
@@ -197,21 +226,31 @@ export class ModelLoader {
197
226
  [...entry.models.keys()].map((model) => this.sqlForModel(model)),
198
227
  ),
199
228
  },
200
- });
229
+ } as never);
201
230
 
202
231
  query.then(
203
232
  (results) => {
204
233
  for (const [model, promise] of entry.models.entries()) {
234
+ // Matched on the columns the batch was keyed by, which are the columns a row of
235
+ // `entry.models` carries: a node loaded by a column other than the primary key
236
+ // has only that one.
205
237
  const result = results.find((row) =>
206
- this.primaryKey.every(
207
- (key) =>
208
- row[this.config.columnToTsName(key) as keyof typeof row] ===
209
- (model as Record<string, unknown>)[this.config.columnToTsName(key)],
210
- ),
238
+ this.columns.every((key) => {
239
+ const name = this.config.columnToTsName(key);
240
+ const actual = row[name as keyof typeof row];
241
+ const expected = (model as Record<string, unknown>)[name];
242
+
243
+ return (
244
+ actual === expected ||
245
+ (actual instanceof Date &&
246
+ expected instanceof Date &&
247
+ actual.getTime() === expected.getTime())
248
+ );
249
+ }),
211
250
  );
212
251
 
213
252
  if (result) {
214
- promise.resolve(result ?? null);
253
+ promise.resolve(result);
215
254
  } else {
216
255
  promise.reject(
217
256
  new Error(`Model ${this.modelName}(${this.sqlForModel(model)}) not found`),
@@ -8,7 +8,7 @@ import SchemaBuilder, {
8
8
  PothosError,
9
9
  type SchemaTypes,
10
10
  } from '@pothos/core';
11
- import type { Column } from 'drizzle-orm';
11
+ import type { Column, TableRelationalConfig } from 'drizzle-orm';
12
12
  import type { GraphQLResolveInfo } from 'graphql';
13
13
  import { DrizzleObjectFieldBuilder } from './drizzle-field-builder.js';
14
14
  import { DrizzleInterfaceRef } from './interface-ref.js';
@@ -22,61 +22,70 @@ import { getRefFromModel } from './utils/refs.js';
22
22
 
23
23
  const schemaBuilderProto = SchemaBuilder.prototype as PothosSchemaTypes.SchemaBuilder<SchemaTypes>;
24
24
 
25
- schemaBuilderProto.drizzleObject = function drizzleObject(table, { select, fields, ...options }) {
26
- const name = options.variant ?? options.name ?? table;
27
-
28
- const ref = options.variant
29
- ? new DrizzleObjectRef(options.variant, table)
30
- : (getRefFromModel(table, this, 'object') as ObjectRef<SchemaTypes, unknown>);
31
-
32
- ref.name = name;
33
-
34
- this.objectType(ref, {
35
- ...(options as {}),
36
- extensions: {
37
- ...options.extensions,
38
- pothosDrizzleModel: table,
39
- pothosDrizzleTable: getSchemaConfig(this).relations[table],
40
- pothosDrizzleSelect:
41
- typeof select === 'object' ? { columns: {}, ...select } : (select ?? true),
42
- pothosDrizzleLoader: ModelLoader.forModel(table, this),
43
- },
44
- name,
45
- fields: fields ? () => fields(new DrizzleObjectFieldBuilder(ref.name, this, table)) : undefined,
46
- });
47
-
48
- return ref as never;
49
- };
50
-
51
- schemaBuilderProto.drizzleInterface = function drizzleInterface(
52
- table,
53
- { select, fields, ...options },
25
+ /**
26
+ * The body of `drizzleObject` and `drizzleInterface`. The two build the same ref, extensions and
27
+ * field builder; only the ref class and which of `objectType`/`interfaceType` registers it differ.
28
+ */
29
+ function defineDrizzleType(
30
+ builder: PothosSchemaTypes.SchemaBuilder<SchemaTypes>,
31
+ table: string,
32
+ {
33
+ select,
34
+ fields,
35
+ ...options
36
+ }: {
37
+ select?: {} | boolean;
38
+ fields?: (t: DrizzleObjectFieldBuilder<SchemaTypes, TableRelationalConfig, {}>) => {};
39
+ variant?: string;
40
+ name?: string;
41
+ extensions?: {};
42
+ },
43
+ kind: 'Interface' | 'Object',
54
44
  ) {
45
+ const isInterface = kind === 'Interface';
55
46
  const name = options.variant ?? options.name ?? table;
56
-
57
47
  const ref = options.variant
58
- ? new DrizzleInterfaceRef(options.variant, table)
59
- : (getRefFromModel(table, this, 'interface') as InterfaceRef<SchemaTypes, unknown>);
48
+ ? isInterface
49
+ ? new DrizzleInterfaceRef(options.variant, table)
50
+ : new DrizzleObjectRef(options.variant, table)
51
+ : (getRefFromModel(table, builder, isInterface ? 'interface' : 'object') as ObjectRef<
52
+ SchemaTypes,
53
+ unknown
54
+ >);
60
55
 
61
56
  ref.name = name;
62
57
 
63
- this.interfaceType(ref, {
64
- ...(options as {}),
58
+ const config = {
59
+ ...options,
65
60
  extensions: {
66
61
  ...options.extensions,
67
62
  pothosDrizzleModel: table,
68
- pothosDrizzleTable: getSchemaConfig(this).relations[table],
63
+ pothosDrizzleTable: getSchemaConfig(builder).relations[table],
69
64
  pothosDrizzleSelect:
70
65
  typeof select === 'object' ? { columns: {}, ...select } : (select ?? true),
71
- pothosDrizzleLoader: ModelLoader.forModel(table, this),
66
+ pothosDrizzleLoader: ModelLoader.forModel(table, builder),
72
67
  },
73
68
  name,
74
69
  fields: fields
75
- ? () => fields(new DrizzleObjectFieldBuilder(ref.name, this, table, 'Interface'))
70
+ ? () => fields(new DrizzleObjectFieldBuilder(name, builder, table, kind) as never)
76
71
  : undefined,
77
- });
72
+ };
73
+
74
+ if (isInterface) {
75
+ builder.interfaceType(ref as never, config as never);
76
+ } else {
77
+ builder.objectType(ref as never, config as never);
78
+ }
79
+
80
+ return ref;
81
+ }
78
82
 
79
- return ref as never;
83
+ schemaBuilderProto.drizzleObject = function drizzleObject(table, options) {
84
+ return defineDrizzleType(this, table, options as never, 'Object') as never;
85
+ };
86
+
87
+ schemaBuilderProto.drizzleInterface = function drizzleInterface(table, options) {
88
+ return defineDrizzleType(this, table, options as never, 'Interface') as never;
80
89
  };
81
90
 
82
91
  schemaBuilderProto.drizzleNode = function drizzleNode(
@@ -104,7 +113,7 @@ schemaBuilderProto.drizzleNode = function drizzleNode(
104
113
  const idColumns = Array.isArray(idColumn) ? idColumn : [idColumn];
105
114
  const interfaceRef = this.nodeInterfaceRef?.();
106
115
  const resolve = getIDSerializer(idColumns, schemaConfig);
107
- const idParser = getIDParser(idColumns);
116
+ const idParser = getIDParser(idColumns, schemaConfig);
108
117
  const typeName = variant ?? name ?? table;
109
118
  const nodeRef = new DrizzleNodeRef(typeName, table, {
110
119
  parseId: (id) => {
@@ -176,142 +185,40 @@ schemaBuilderProto.drizzleNode = function drizzleNode(
176
185
  return nodeRef;
177
186
  } as never;
178
187
 
179
- schemaBuilderProto.drizzleObjectField = function drizzleObjectField(type, fieldName, field) {
180
- const ref = typeof type === 'string' ? getRefFromModel(type, this) : (type as never);
181
- this.configStore.onTypeConfig(ref, ({ name }) => {
182
- this.configStore.addFields(ref, () => ({
183
- [fieldName]: field(new DrizzleObjectFieldBuilder(name, this, ref.tableName)),
184
- }));
188
+ /**
189
+ * The body of `drizzleObjectField(s)` and `drizzleInterfaceField(s)`: they differ only in kind.
190
+ * A table name names the interface registered under it, as `drizzleInterface` registers one.
191
+ */
192
+ function addDrizzleFields(
193
+ builder: PothosSchemaTypes.SchemaBuilder<SchemaTypes>,
194
+ type: string | { tableName: string },
195
+ fields: (t: DrizzleObjectFieldBuilder<SchemaTypes, TableRelationalConfig, {}>) => {},
196
+ graphqlKind?: 'Interface',
197
+ ) {
198
+ const ref =
199
+ typeof type === 'string'
200
+ ? getRefFromModel(type, builder, graphqlKind ? 'interface' : 'object')
201
+ : (type as never);
202
+
203
+ builder.configStore.onTypeConfig(ref, ({ name }) => {
204
+ builder.configStore.addFields(ref, () =>
205
+ fields(new DrizzleObjectFieldBuilder(name, builder, ref.tableName, graphqlKind) as never),
206
+ );
185
207
  });
208
+ }
209
+
210
+ schemaBuilderProto.drizzleObjectField = function drizzleObjectField(type, fieldName, field) {
211
+ addDrizzleFields(this, type as never, (t) => ({ [fieldName]: field(t as never) }));
186
212
  };
187
213
 
188
214
  schemaBuilderProto.drizzleInterfaceField = function drizzleInterfaceField(type, fieldName, field) {
189
- const ref = typeof type === 'string' ? getRefFromModel(type, this) : (type as never);
190
- this.configStore.onTypeConfig(ref, ({ name }) => {
191
- this.configStore.addFields(ref, () => ({
192
- [fieldName]: field(new DrizzleObjectFieldBuilder(name, this, ref.tableName, 'Interface')),
193
- }));
194
- });
215
+ addDrizzleFields(this, type as never, (t) => ({ [fieldName]: field(t as never) }), 'Interface');
195
216
  };
196
217
 
197
218
  schemaBuilderProto.drizzleObjectFields = function drizzleObjectFields(type, fields) {
198
- const ref = typeof type === 'string' ? getRefFromModel(type, this) : (type as never);
199
- this.configStore.onTypeConfig(ref, ({ name }) => {
200
- this.configStore.addFields(ref, () =>
201
- fields(new DrizzleObjectFieldBuilder(name, this, ref.tableName)),
202
- );
203
- });
219
+ addDrizzleFields(this, type as never, fields as never);
204
220
  };
205
221
 
206
222
  schemaBuilderProto.drizzleInterfaceFields = function drizzleInterfaceFields(type, fields) {
207
- const ref = typeof type === 'string' ? getRefFromModel(type, this) : (type as never);
208
- this.configStore.onTypeConfig(ref, ({ name }) => {
209
- this.configStore.addFields(ref, () =>
210
- fields(new DrizzleObjectFieldBuilder(name, this, ref.tableName, 'Interface')),
211
- );
212
- });
223
+ addDrizzleFields(this, type as never, fields as never, 'Interface');
213
224
  };
214
-
215
- // schemaBuilderProto.drizzleGraphQLOrderBy = function drizzleGraphQLOrderBy(
216
- // this: PothosSchemaTypes.SchemaBuilder<SchemaTypes>,
217
- // table: string,
218
- // type: GraphQLInputObjectType,
219
- // options?: { extensions?: object },
220
- // ) {
221
- // return (
222
- // this as typeof this & {
223
- // addGraphQLInput: (
224
- // type: GraphQLInputObjectType,
225
- // options: Record<string, unknown>,
226
- // ) => InputObjectRef<SchemaTypes, {}>;
227
- // }
228
- // ).addGraphQLInput(type, {
229
- // ...options,
230
- // extensions: {
231
- // ...options?.extensions,
232
- // drizzleGraphQL: {
233
- // inputType: 'orderBy',
234
- // table,
235
- // tableConfig: getSchemaConfig(this).relations[table],
236
- // } satisfies DrizzleGraphQLInputExtensions,
237
- // },
238
- // });
239
- // } as never;
240
-
241
- // schemaBuilderProto.drizzleGraphQLFilters = function drizzleGraphQLFilters(
242
- // this: PothosSchemaTypes.SchemaBuilder<SchemaTypes>,
243
- // table: string,
244
- // type: GraphQLInputObjectType,
245
- // options?: { extensions?: object },
246
- // ) {
247
- // return (
248
- // this as typeof this & {
249
- // addGraphQLInput: (
250
- // type: GraphQLInputObjectType,
251
- // options: Record<string, unknown>,
252
- // ) => InputObjectRef<SchemaTypes, {}>;
253
- // }
254
- // ).addGraphQLInput(type, {
255
- // ...options,
256
- // extensions: {
257
- // ...options?.extensions,
258
- // drizzleGraphQL: {
259
- // inputType: 'filters',
260
- // table,
261
- // tableConfig: getSchemaConfig(this).relations[table],
262
- // } satisfies DrizzleGraphQLInputExtensions,
263
- // },
264
- // });
265
- // } as never;
266
-
267
- // schemaBuilderProto.drizzleGraphQLInsert = function drizzleGraphQLInsert(
268
- // this: PothosSchemaTypes.SchemaBuilder<SchemaTypes>,
269
- // table: string,
270
- // type: GraphQLInputObjectType,
271
- // options?: { extensions?: object },
272
- // ) {
273
- // return (
274
- // this as typeof this & {
275
- // addGraphQLInput: (
276
- // type: GraphQLInputObjectType,
277
- // options: Record<string, unknown>,
278
- // ) => InputObjectRef<SchemaTypes, {}>;
279
- // }
280
- // ).addGraphQLInput(type, {
281
- // ...options,
282
- // extensions: {
283
- // ...options?.extensions,
284
- // drizzleGraphQL: {
285
- // inputType: 'insert',
286
- // table,
287
- // tableConfig: getSchemaConfig(this).relations[table],
288
- // } satisfies DrizzleGraphQLInputExtensions,
289
- // },
290
- // });
291
- // } as never;
292
-
293
- // schemaBuilderProto.drizzleGraphQLUpdate = function drizzleGraphQLUpdate(
294
- // this: PothosSchemaTypes.SchemaBuilder<SchemaTypes>,
295
- // table: string,
296
- // type: GraphQLInputObjectType,
297
- // options?: { extensions?: object },
298
- // ) {
299
- // return (
300
- // this as typeof this & {
301
- // addGraphQLInput: (
302
- // type: GraphQLInputObjectType,
303
- // options: Record<string, unknown>,
304
- // ) => InputObjectRef<SchemaTypes, {}>;
305
- // }
306
- // ).addGraphQLInput(type, {
307
- // ...options,
308
- // extensions: {
309
- // ...options?.extensions,
310
- // drizzleGraphQL: {
311
- // inputType: 'update',
312
- // table,
313
- // tableConfig: getSchemaConfig(this).relations[table],
314
- // } satisfies DrizzleGraphQLInputExtensions,
315
- // },
316
- // });
317
- // } as never;