@pothos/plugin-drizzle 0.0.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 (179) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/LICENSE +6 -0
  3. package/README.md +437 -0
  4. package/TODO.md +21 -0
  5. package/drizzle/meta/_journal.json +1 -0
  6. package/drizzle.config.ts +10 -0
  7. package/dts/drizzle-field-builder.d.ts +124 -0
  8. package/dts/drizzle-field-builder.d.ts.map +1 -0
  9. package/dts/field-builder.d.ts +2 -0
  10. package/dts/field-builder.d.ts.map +1 -0
  11. package/dts/global-types.d.ts +92 -0
  12. package/dts/global-types.d.ts.map +1 -0
  13. package/dts/index.d.ts +13 -0
  14. package/dts/index.d.ts.map +1 -0
  15. package/dts/interface-ref.d.ts +9 -0
  16. package/dts/interface-ref.d.ts.map +1 -0
  17. package/dts/model-loader.d.ts +44 -0
  18. package/dts/model-loader.d.ts.map +1 -0
  19. package/dts/node-ref.d.ts +5 -0
  20. package/dts/node-ref.d.ts.map +1 -0
  21. package/dts/object-ref.d.ts +14 -0
  22. package/dts/object-ref.d.ts.map +1 -0
  23. package/dts/schema-builder.d.ts +2 -0
  24. package/dts/schema-builder.d.ts.map +1 -0
  25. package/dts/types.d.ts +147 -0
  26. package/dts/types.d.ts.map +1 -0
  27. package/dts/utils/config.d.ts +7 -0
  28. package/dts/utils/config.d.ts.map +1 -0
  29. package/dts/utils/cursors.d.ts +58 -0
  30. package/dts/utils/cursors.d.ts.map +1 -0
  31. package/dts/utils/deep-equal.d.ts +2 -0
  32. package/dts/utils/deep-equal.d.ts.map +1 -0
  33. package/dts/utils/drizzle-graphql.d.ts +18 -0
  34. package/dts/utils/drizzle-graphql.d.ts.map +1 -0
  35. package/dts/utils/loader-map.d.ts +11 -0
  36. package/dts/utils/loader-map.d.ts.map +1 -0
  37. package/dts/utils/map-query.d.ts +30 -0
  38. package/dts/utils/map-query.d.ts.map +1 -0
  39. package/dts/utils/refs.d.ts +5 -0
  40. package/dts/utils/refs.d.ts.map +1 -0
  41. package/dts/utils/selections.d.ts +22 -0
  42. package/dts/utils/selections.d.ts.map +1 -0
  43. package/dts/utils/usage.d.ts +5 -0
  44. package/dts/utils/usage.d.ts.map +1 -0
  45. package/esm/drizzle-field-builder.d.ts +216 -0
  46. package/esm/drizzle-field-builder.d.ts.map +1 -0
  47. package/esm/field-builder.d.ts +2 -0
  48. package/esm/field-builder.d.ts.map +1 -0
  49. package/esm/global-types.d.ts +124 -0
  50. package/esm/global-types.d.ts.map +1 -0
  51. package/esm/index.d.ts +13 -0
  52. package/esm/index.d.ts.map +1 -0
  53. package/esm/interface-ref.d.ts +9 -0
  54. package/esm/interface-ref.d.ts.map +1 -0
  55. package/esm/model-loader.d.ts +45 -0
  56. package/esm/model-loader.d.ts.map +1 -0
  57. package/esm/node-ref.d.ts +5 -0
  58. package/esm/node-ref.d.ts.map +1 -0
  59. package/esm/object-ref.d.ts +14 -0
  60. package/esm/object-ref.d.ts.map +1 -0
  61. package/esm/package.json +3 -0
  62. package/esm/schema-builder.d.ts +2 -0
  63. package/esm/schema-builder.d.ts.map +1 -0
  64. package/esm/src/drizzle-field-builder.js +215 -0
  65. package/esm/src/drizzle-field-builder.js.map +1 -0
  66. package/esm/src/field-builder.js +77 -0
  67. package/esm/src/field-builder.js.map +1 -0
  68. package/esm/src/global-types.js +2 -0
  69. package/esm/src/global-types.js.map +1 -0
  70. package/esm/src/index.js +107 -0
  71. package/esm/src/index.js.map +1 -0
  72. package/esm/src/interface-ref.js +26 -0
  73. package/esm/src/interface-ref.js.map +1 -0
  74. package/esm/src/model-loader.js +168 -0
  75. package/esm/src/model-loader.js.map +1 -0
  76. package/esm/src/node-ref.js +4 -0
  77. package/esm/src/node-ref.js.map +1 -0
  78. package/esm/src/object-ref.js +38 -0
  79. package/esm/src/object-ref.js.map +1 -0
  80. package/esm/src/schema-builder.js +190 -0
  81. package/esm/src/schema-builder.js.map +1 -0
  82. package/esm/src/types.js +2 -0
  83. package/esm/src/types.js.map +1 -0
  84. package/esm/src/utils/config.js +15 -0
  85. package/esm/src/utils/config.js.map +1 -0
  86. package/esm/src/utils/cursors.js +316 -0
  87. package/esm/src/utils/cursors.js.map +1 -0
  88. package/esm/src/utils/deep-equal.js +44 -0
  89. package/esm/src/utils/deep-equal.js.map +1 -0
  90. package/esm/src/utils/drizzle-graphql.js +176 -0
  91. package/esm/src/utils/drizzle-graphql.js.map +1 -0
  92. package/esm/src/utils/loader-map.js +35 -0
  93. package/esm/src/utils/loader-map.js.map +1 -0
  94. package/esm/src/utils/map-query.js +284 -0
  95. package/esm/src/utils/map-query.js.map +1 -0
  96. package/esm/src/utils/refs.js +14 -0
  97. package/esm/src/utils/refs.js.map +1 -0
  98. package/esm/src/utils/selections.js +136 -0
  99. package/esm/src/utils/selections.js.map +1 -0
  100. package/esm/src/utils/usage.js +59 -0
  101. package/esm/src/utils/usage.js.map +1 -0
  102. package/esm/types.d.ts +161 -0
  103. package/esm/types.d.ts.map +1 -0
  104. package/esm/utils/config.d.ts +7 -0
  105. package/esm/utils/config.d.ts.map +1 -0
  106. package/esm/utils/cursors.d.ts +58 -0
  107. package/esm/utils/cursors.d.ts.map +1 -0
  108. package/esm/utils/deep-equal.d.ts +2 -0
  109. package/esm/utils/deep-equal.d.ts.map +1 -0
  110. package/esm/utils/drizzle-graphql.d.ts +18 -0
  111. package/esm/utils/drizzle-graphql.d.ts.map +1 -0
  112. package/esm/utils/loader-map.d.ts +11 -0
  113. package/esm/utils/loader-map.d.ts.map +1 -0
  114. package/esm/utils/map-query.d.ts +30 -0
  115. package/esm/utils/map-query.d.ts.map +1 -0
  116. package/esm/utils/refs.d.ts +5 -0
  117. package/esm/utils/refs.d.ts.map +1 -0
  118. package/esm/utils/selections.d.ts +22 -0
  119. package/esm/utils/selections.d.ts.map +1 -0
  120. package/esm/utils/usage.d.ts +5 -0
  121. package/esm/utils/usage.d.ts.map +1 -0
  122. package/lib/src/drizzle-field-builder.js +225 -0
  123. package/lib/src/drizzle-field-builder.js.map +1 -0
  124. package/lib/src/field-builder.js +82 -0
  125. package/lib/src/field-builder.js.map +1 -0
  126. package/lib/src/global-types.js +6 -0
  127. package/lib/src/global-types.js.map +1 -0
  128. package/lib/src/index.js +180 -0
  129. package/lib/src/index.js.map +1 -0
  130. package/lib/src/interface-ref.js +36 -0
  131. package/lib/src/interface-ref.js.map +1 -0
  132. package/lib/src/model-loader.js +176 -0
  133. package/lib/src/model-loader.js.map +1 -0
  134. package/lib/src/node-ref.js +15 -0
  135. package/lib/src/node-ref.js.map +1 -0
  136. package/lib/src/object-ref.js +56 -0
  137. package/lib/src/object-ref.js.map +1 -0
  138. package/lib/src/schema-builder.js +236 -0
  139. package/lib/src/schema-builder.js.map +1 -0
  140. package/lib/src/types.js +13 -0
  141. package/lib/src/types.js.map +1 -0
  142. package/lib/src/utils/config.js +26 -0
  143. package/lib/src/utils/config.js.map +1 -0
  144. package/lib/src/utils/cursors.js +362 -0
  145. package/lib/src/utils/cursors.js.map +1 -0
  146. package/lib/src/utils/deep-equal.js +55 -0
  147. package/lib/src/utils/deep-equal.js.map +1 -0
  148. package/lib/src/utils/drizzle-graphql.js +192 -0
  149. package/lib/src/utils/drizzle-graphql.js.map +1 -0
  150. package/lib/src/utils/loader-map.js +57 -0
  151. package/lib/src/utils/loader-map.js.map +1 -0
  152. package/lib/src/utils/map-query.js +308 -0
  153. package/lib/src/utils/map-query.js.map +1 -0
  154. package/lib/src/utils/refs.js +33 -0
  155. package/lib/src/utils/refs.js.map +1 -0
  156. package/lib/src/utils/selections.js +163 -0
  157. package/lib/src/utils/selections.js.map +1 -0
  158. package/lib/src/utils/usage.js +84 -0
  159. package/lib/src/utils/usage.js.map +1 -0
  160. package/package.json +66 -0
  161. package/src/drizzle-field-builder.ts +500 -0
  162. package/src/field-builder.ts +168 -0
  163. package/src/global-types.ts +481 -0
  164. package/src/index.ts +175 -0
  165. package/src/interface-ref.ts +24 -0
  166. package/src/model-loader.ts +235 -0
  167. package/src/node-ref.ts +8 -0
  168. package/src/object-ref.ts +51 -0
  169. package/src/schema-builder.ts +299 -0
  170. package/src/types.ts +640 -0
  171. package/src/utils/config.ts +28 -0
  172. package/src/utils/cursors.ts +446 -0
  173. package/src/utils/deep-equal.ts +58 -0
  174. package/src/utils/drizzle-graphql.ts +235 -0
  175. package/src/utils/loader-map.ts +51 -0
  176. package/src/utils/map-query.ts +582 -0
  177. package/src/utils/refs.ts +25 -0
  178. package/src/utils/selections.ts +209 -0
  179. package/src/utils/usage.ts +64 -0
package/src/index.ts ADDED
@@ -0,0 +1,175 @@
1
+ import './global-types';
2
+ import './field-builder';
3
+ import './schema-builder';
4
+ import { GraphQLFieldResolver } from 'graphql';
5
+ import SchemaBuilder, {
6
+ BasePlugin,
7
+ createInputValueMapper,
8
+ mapInputFields,
9
+ PartialResolveInfo,
10
+ PothosOutputFieldConfig,
11
+ SchemaTypes,
12
+ } from '@pothos/core';
13
+ import { ModelLoader } from './model-loader';
14
+ import { DrizzleGraphQLInputExtensions } from './types';
15
+ import {
16
+ extractFilters,
17
+ extractOrderBy,
18
+ remapFromGraphQLSingleInput,
19
+ } from './utils/drizzle-graphql';
20
+ import { getLoaderMapping, setLoaderMappings } from './utils/loader-map';
21
+
22
+ export * from './types';
23
+
24
+ const pluginName = 'drizzle';
25
+
26
+ export default pluginName;
27
+
28
+ export class PothosDrizzlePlugin<Types extends SchemaTypes> extends BasePlugin<Types> {
29
+ override onOutputFieldConfig(
30
+ fieldConfig: PothosOutputFieldConfig<Types>,
31
+ ): PothosOutputFieldConfig<Types> | null {
32
+ const argMappings = mapInputFields<Types, DrizzleGraphQLInputExtensions>(
33
+ fieldConfig.args,
34
+ this.buildCache,
35
+ (inputField) => {
36
+ if (inputField.type.kind === 'InputObject') {
37
+ const config = this.buildCache.getTypeConfig(inputField.type.ref);
38
+
39
+ return (config.extensions?.drizzleGraphQL as DrizzleGraphQLInputExtensions) ?? null;
40
+ }
41
+
42
+ return null;
43
+ },
44
+ );
45
+
46
+ const argMapper = argMappings
47
+ ? createInputValueMapper(
48
+ argMappings,
49
+ (input, mappings, ctx: Types['Context'], info: PartialResolveInfo) => {
50
+ if (!mappings.value) {
51
+ return input;
52
+ }
53
+
54
+ const { table, tableConfig, inputType } = mappings.value;
55
+
56
+ switch (inputType) {
57
+ case 'orderBy':
58
+ return extractOrderBy(tableConfig, input as never);
59
+ case 'filters':
60
+ return extractFilters(tableConfig, table, input as never);
61
+ case 'insert':
62
+ return remapFromGraphQLSingleInput(input as never, tableConfig);
63
+ case 'update':
64
+ return remapFromGraphQLSingleInput(input as never, tableConfig);
65
+
66
+ default:
67
+ throw new Error(`Unknown drizzle input type: ${inputType}`);
68
+ }
69
+ },
70
+ )
71
+ : null;
72
+
73
+ const argMappers: typeof fieldConfig.argMappers = argMapper
74
+ ? [
75
+ ...(fieldConfig.argMappers ?? []),
76
+ (args, context, info) => argMapper(args, undefined, context, info),
77
+ ]
78
+ : fieldConfig.argMappers;
79
+
80
+ if (fieldConfig.kind === 'DrizzleObject' && fieldConfig.pothosOptions.select) {
81
+ const { select } = fieldConfig.pothosOptions;
82
+ return {
83
+ ...fieldConfig,
84
+ argMappers,
85
+ extensions: {
86
+ ...fieldConfig.extensions,
87
+ pothosDrizzleSelect:
88
+ typeof select === 'function'
89
+ ? (
90
+ args: {},
91
+ ctx: Types['Context'],
92
+ nestedQuery: (query: unknown, path?: string[]) => never,
93
+ ) => ({
94
+ select: (select as (args: unknown, ctx: unknown, nestedQuery: unknown) => {})(
95
+ args,
96
+ ctx,
97
+ nestedQuery,
98
+ ),
99
+ })
100
+ : select,
101
+ },
102
+ };
103
+ }
104
+
105
+ return {
106
+ ...fieldConfig,
107
+ argMappers,
108
+ };
109
+ }
110
+
111
+ override wrapResolve(
112
+ resolver: GraphQLFieldResolver<unknown, Types['Context'], object, unknown>,
113
+ fieldConfig: PothosOutputFieldConfig<Types>,
114
+ ): GraphQLFieldResolver<unknown, Types['Context'], object> {
115
+ if (fieldConfig.kind !== 'DrizzleObject' || !fieldConfig.extensions?.pothosDrizzleSelect) {
116
+ return resolver;
117
+ }
118
+
119
+ const parentConfig = this.buildCache.getTypeConfig(fieldConfig.parentType);
120
+
121
+ const parentTypes = new Set([fieldConfig.parentType]);
122
+
123
+ if (parentConfig.kind === 'Interface' || parentConfig.kind === 'Object') {
124
+ parentConfig.interfaces.forEach((iface) => {
125
+ const interfaceConfig = this.buildCache.getTypeConfig(iface, 'Interface');
126
+ if (interfaceConfig.extensions?.pothosDrizzleModel) {
127
+ parentTypes.add(interfaceConfig.name);
128
+ }
129
+ });
130
+ }
131
+
132
+ const modelLoader = parentConfig.extensions?.pothosDrizzleLoader as ReturnType<
133
+ typeof ModelLoader.forModel
134
+ >;
135
+
136
+ if (!modelLoader) {
137
+ throw new Error(`ModelLoader not found for type ${parentConfig.name}`);
138
+ }
139
+
140
+ return (parent, args, context, info) => {
141
+ let mapping = getLoaderMapping(context, info.path, info.parentType.name);
142
+
143
+ if (!mapping) {
144
+ for (const parentType of parentTypes) {
145
+ mapping = getLoaderMapping(context, info.path, parentType);
146
+ if (mapping) {
147
+ break;
148
+ }
149
+ }
150
+ }
151
+
152
+ if (mapping) {
153
+ setLoaderMappings(context, info, mapping);
154
+
155
+ return resolver(parent, args, context, info);
156
+ }
157
+
158
+ const primaryKeys = (
159
+ parentConfig.extensions!.pothosDrizzleTable as { primaryKey?: { name: string }[] }
160
+ ).primaryKey;
161
+
162
+ if (!primaryKeys || primaryKeys.length === 0) {
163
+ throw new Error(
164
+ `Field ${fieldConfig.name} not resolved in initial query and no primary key found for type ${parentConfig.name}`,
165
+ );
166
+ }
167
+
168
+ return modelLoader(context)
169
+ .loadSelection(info, parent as object)
170
+ .then((result) => resolver(result, args, context, info));
171
+ };
172
+ }
173
+ }
174
+
175
+ SchemaBuilder.registerPlugin(pluginName, PothosDrizzlePlugin, {});
@@ -0,0 +1,24 @@
1
+ import { InterfaceRef, SchemaTypes } from '@pothos/core';
2
+ import { DrizzleObjectRef, drizzleTableKey } from './object-ref';
3
+
4
+ export type DrizzleRef<
5
+ Types extends SchemaTypes,
6
+ Table extends keyof Types['DrizzleRelationSchema'] = keyof Types['DrizzleRelationSchema'],
7
+ T = {},
8
+ > = DrizzleInterfaceRef<Types, Table, T> | DrizzleObjectRef<Types, Table, T>;
9
+
10
+ export class DrizzleInterfaceRef<
11
+ Types extends SchemaTypes,
12
+ Table extends keyof Types['DrizzleRelationSchema'] = keyof Types['DrizzleRelationSchema'],
13
+ T = {},
14
+ > extends InterfaceRef<Types, T> {
15
+ [drizzleTableKey]!: Table;
16
+
17
+ tableName: string;
18
+
19
+ constructor(name: string, tableName: string) {
20
+ super(name);
21
+
22
+ this.tableName = tableName;
23
+ }
24
+ }
@@ -0,0 +1,235 @@
1
+ import { GraphQLResolveInfo } from 'graphql';
2
+ import {
3
+ Column,
4
+ inArray,
5
+ RelationalSchemaConfig,
6
+ sql,
7
+ TableRelationalConfig,
8
+ TablesRelationalConfig,
9
+ } from 'drizzle-orm';
10
+ import { createContextCache, SchemaTypes } from '@pothos/core';
11
+ import { cacheKey, setLoaderMappings } from './utils/loader-map';
12
+ import { selectionStateFromInfo, stateFromInfo } from './utils/map-query';
13
+ import {
14
+ mergeSelection,
15
+ selectionCompatible,
16
+ SelectionMap,
17
+ SelectionState,
18
+ selectionToQuery,
19
+ } from './utils/selections';
20
+ import { getSchemaConfig, PothosDrizzleSchemaConfig } from './utils/config';
21
+
22
+ interface ResolvablePromise<T> {
23
+ promise: Promise<T>;
24
+ resolve: (value: T) => void;
25
+ reject: (err: unknown) => void;
26
+ }
27
+ export class ModelLoader {
28
+ context: object;
29
+
30
+ builder: PothosSchemaTypes.SchemaBuilder<never>;
31
+
32
+ modelName: string;
33
+
34
+ queryCache = new Map<string, { selection: SelectionState; query: SelectionMap }>();
35
+
36
+ staged = new Set<{
37
+ state: SelectionState;
38
+ models: Map<object, ResolvablePromise<Record<string, unknown> | null>>;
39
+ }>();
40
+
41
+ config: PothosDrizzleSchemaConfig;
42
+ table: TableRelationalConfig;
43
+ columns: Column[];
44
+ selectSQL;
45
+
46
+ constructor(
47
+ context: object,
48
+ builder: PothosSchemaTypes.SchemaBuilder<never>,
49
+ modelName: string,
50
+ columns?: Column[],
51
+ ) {
52
+ this.context = context;
53
+ this.builder = builder;
54
+ this.modelName = modelName;
55
+ this.config = getSchemaConfig(builder);
56
+ this.table = this.config.schema[modelName];
57
+ this.columns = columns ?? this.table.primaryKey;
58
+ this.selectSQL =
59
+ this.columns.length > 1
60
+ ? sql`(${sql.join(this.columns, sql`, `)})`
61
+ : this.columns[0].getSQL();
62
+ }
63
+
64
+ static forModel<Types extends SchemaTypes>(
65
+ modelName: string,
66
+ builder: PothosSchemaTypes.SchemaBuilder<Types>,
67
+ columns?: Column[],
68
+ ) {
69
+ return createContextCache(
70
+ (model) => new ModelLoader(model, builder as never, modelName, columns),
71
+ );
72
+ }
73
+
74
+ sqlForModel(model: object) {
75
+ const values = this.columns.map((key) => {
76
+ const columnName = key.name as keyof typeof model;
77
+ if (columnName in model) {
78
+ return model[columnName] as string | number;
79
+ }
80
+
81
+ throw new Error(`Primary key column ${columnName} not found on ${this.modelName}`);
82
+ });
83
+
84
+ return this.columns.length > 1 ? values : values[0];
85
+ }
86
+
87
+ getSelection(info: GraphQLResolveInfo) {
88
+ const key = cacheKey(info.parentType.name, info.path);
89
+ if (!this.queryCache.has(key)) {
90
+ const selection = selectionStateFromInfo(this.config, this.context, info);
91
+ this.queryCache.set(key, {
92
+ selection,
93
+ query: selectionToQuery(selection),
94
+ });
95
+ }
96
+
97
+ return this.queryCache.get(key)!;
98
+ }
99
+
100
+ getSelectionForField(info: GraphQLResolveInfo, typeName: string) {
101
+ const key = cacheKey(typeName, info.path);
102
+ if (!this.queryCache.has(key)) {
103
+ const selection = stateFromInfo({
104
+ config: this.config,
105
+ context: this.context,
106
+ info,
107
+ typeName,
108
+ });
109
+
110
+ this.queryCache.set(key, {
111
+ selection,
112
+ query: selectionToQuery(selection),
113
+ });
114
+ }
115
+
116
+ return this.queryCache.get(key)!;
117
+ }
118
+
119
+ async loadSelection(info: GraphQLResolveInfo, model: object) {
120
+ const { selection, query } = this.getSelection(info);
121
+
122
+ const result = await this.stageQuery(selection, query, model);
123
+
124
+ if (result) {
125
+ const mappings = selection.mappings[info.path.key];
126
+
127
+ if (mappings) {
128
+ setLoaderMappings(this.context, info, mappings.mappings);
129
+ }
130
+ }
131
+
132
+ return result;
133
+ }
134
+
135
+ async loadSelectionForField(info: GraphQLResolveInfo, model: object, returnType: string) {
136
+ const { selection, query } = this.getSelectionForField(info, returnType);
137
+
138
+ const result = await this.stageQuery(selection, query, model);
139
+
140
+ if (result) {
141
+ setLoaderMappings(this.context, info, selection.mappings);
142
+ }
143
+
144
+ return result;
145
+ }
146
+
147
+ async stageQuery(selection: SelectionState, query: SelectionMap, model: object) {
148
+ for (const entry of this.staged) {
149
+ if (selectionCompatible(entry.state, query)) {
150
+ mergeSelection(this.config, entry.state, query);
151
+
152
+ if (!entry.models.has(model)) {
153
+ entry.models.set(model, createResolvablePromise<Record<string, unknown> | null>());
154
+ }
155
+
156
+ return entry.models.get(model)!.promise;
157
+ }
158
+ }
159
+
160
+ return this.initLoad(selection, model);
161
+ }
162
+
163
+ initLoad(selection: SelectionState, model: object) {
164
+ const promise = createResolvablePromise<Record<string, unknown> | null>();
165
+ const entry = {
166
+ state: selection,
167
+ models: new Map([[model, promise]]),
168
+ };
169
+ this.staged.add(entry);
170
+
171
+ const nextTick = createResolvablePromise<void>();
172
+
173
+ nextTick.promise
174
+ .then(() => {
175
+ const api = (
176
+ this.builder.options.drizzle.client.query as Record<
177
+ string,
178
+ { findMany: (...args: unknown[]) => Promise<Record<string, unknown>[]> }
179
+ >
180
+ )[this.modelName];
181
+
182
+ const query = api.findMany({
183
+ ...selectionToQuery(selection),
184
+ where: inArray(
185
+ this.selectSQL,
186
+ [entry.models.keys()].map(([model]) => this.sqlForModel(model)),
187
+ ),
188
+ });
189
+
190
+ query.then(
191
+ (results) => {
192
+ for (const [model, promise] of entry.models.entries()) {
193
+ const result = results.find((row) =>
194
+ this.table.primaryKey.every(
195
+ (key) => row[key.name] === (model as Record<string, unknown>)[key.name],
196
+ ),
197
+ );
198
+
199
+ if (result) {
200
+ promise.resolve(result ?? null);
201
+ } else
202
+ promise.reject(
203
+ new Error(`Model ${this.modelName}(${this.sqlForModel(model)}) not found`),
204
+ );
205
+ }
206
+ },
207
+ (err) => {
208
+ for (const promise of entry.models.values()) {
209
+ promise.reject(err);
210
+ }
211
+ },
212
+ );
213
+ })
214
+ .catch((err) => {
215
+ for (const promise of entry.models.values()) {
216
+ promise.reject(err);
217
+ }
218
+ });
219
+
220
+ setTimeout(() => void nextTick.resolve(), 0);
221
+
222
+ return promise.promise;
223
+ }
224
+ }
225
+
226
+ function createResolvablePromise<T = unknown>(): ResolvablePromise<T> {
227
+ let resolveFn!: (value: T) => void;
228
+ let rejectFn!: (reason?: unknown) => void;
229
+ const promise = new Promise<T>((resolve, reject) => {
230
+ resolveFn = resolve;
231
+ rejectFn = reject;
232
+ });
233
+
234
+ return { promise, resolve: resolveFn, reject: rejectFn };
235
+ }
@@ -0,0 +1,8 @@
1
+ import { SchemaTypes } from '@pothos/core';
2
+ import { DrizzleObjectRef } from './object-ref';
3
+
4
+ export class DrizzleNodeRef<
5
+ Types extends SchemaTypes,
6
+ Table extends keyof Types['DrizzleRelationSchema'] = keyof Types['DrizzleRelationSchema'],
7
+ T = {},
8
+ > extends DrizzleObjectRef<Types, Table, T> {}
@@ -0,0 +1,51 @@
1
+ import {
2
+ abstractReturnShapeKey,
3
+ brandWithType,
4
+ ObjectRef,
5
+ SchemaTypes,
6
+ typeBrandKey,
7
+ } from '@pothos/core';
8
+ import type { WithBrand } from './types';
9
+
10
+ export const drizzleTableKey = Symbol.for('Pothos.drizzleTableKey');
11
+
12
+ export class DrizzleObjectRef<
13
+ Types extends SchemaTypes,
14
+ Table extends keyof Types['DrizzleRelationSchema'] = keyof Types['DrizzleRelationSchema'],
15
+ T = {},
16
+ > extends ObjectRef<Types, T> {
17
+ [drizzleTableKey]!: Types['DrizzleRelationSchema'][Table];
18
+
19
+ [abstractReturnShapeKey]!: WithBrand<T>;
20
+
21
+ tableName: string;
22
+
23
+ constructor(name: string, tableName: string) {
24
+ super(name);
25
+
26
+ this.tableName = tableName;
27
+ }
28
+
29
+ addBrand<V extends T | T[]>(
30
+ value: V,
31
+ ): V extends T[] ? { [K in keyof V]: WithBrand<V[K]> } : WithBrand<V> {
32
+ if (Array.isArray(value)) {
33
+ value.forEach((val) => void brandWithType(val, this.name as never));
34
+
35
+ return value as never;
36
+ }
37
+
38
+ brandWithType(value, this.name as never);
39
+
40
+ return value as never;
41
+ }
42
+
43
+ hasBrand(value: unknown) {
44
+ return (
45
+ typeof value === 'object' &&
46
+ value !== null &&
47
+ typeBrandKey in value &&
48
+ (value as { [typeBrandKey]?: unknown })[typeBrandKey] === this.name
49
+ );
50
+ }
51
+ }