@pothos/plugin-drizzle 0.17.5 → 0.18.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.
- package/CHANGELOG.md +39 -0
- package/dts/connection-helpers.d.ts.map +1 -1
- package/dts/drizzle-field-builder.d.ts.map +1 -1
- package/dts/types.d.ts +5 -2
- package/dts/types.d.ts.map +1 -1
- package/dts/utils/config.d.ts +3 -0
- package/dts/utils/config.d.ts.map +1 -1
- package/dts/utils/cursors.d.ts +12 -8
- package/dts/utils/cursors.d.ts.map +1 -1
- package/dts/utils/selections.d.ts +1 -0
- package/dts/utils/selections.d.ts.map +1 -1
- package/esm/connection-helpers.d.ts.map +1 -1
- package/esm/connection-helpers.js +8 -7
- package/esm/connection-helpers.js.map +1 -1
- package/esm/drizzle-field-builder.d.ts.map +1 -1
- package/esm/drizzle-field-builder.js +8 -6
- package/esm/drizzle-field-builder.js.map +1 -1
- package/esm/types.d.ts +5 -2
- package/esm/types.d.ts.map +1 -1
- package/esm/types.js.map +1 -1
- package/esm/utils/config.d.ts +3 -0
- package/esm/utils/config.d.ts.map +1 -1
- package/esm/utils/config.js +95 -17
- package/esm/utils/config.js.map +1 -1
- package/esm/utils/cursors.d.ts +12 -8
- package/esm/utils/cursors.d.ts.map +1 -1
- package/esm/utils/cursors.js +199 -117
- package/esm/utils/cursors.js.map +1 -1
- package/esm/utils/selections.d.ts +1 -0
- package/esm/utils/selections.d.ts.map +1 -1
- package/esm/utils/selections.js +10 -4
- package/esm/utils/selections.js.map +1 -1
- package/lib/connection-helpers.js +7 -6
- package/lib/connection-helpers.js.map +1 -1
- package/lib/drizzle-field-builder.js +8 -6
- package/lib/drizzle-field-builder.js.map +1 -1
- package/lib/types.js.map +1 -1
- package/lib/utils/config.js +95 -17
- package/lib/utils/config.js.map +1 -1
- package/lib/utils/cursors.js +201 -116
- package/lib/utils/cursors.js.map +1 -1
- package/lib/utils/selections.js +13 -4
- package/lib/utils/selections.js.map +1 -1
- package/package.json +4 -4
- package/src/connection-helpers.ts +14 -7
- package/src/drizzle-field-builder.ts +10 -7
- package/src/types.ts +8 -4
- package/src/utils/config.ts +117 -20
- package/src/utils/cursors.ts +253 -92
- package/src/utils/selections.ts +12 -4
|
@@ -10,13 +10,19 @@ import type { DrizzleRef } from './interface-ref.js';
|
|
|
10
10
|
import type { QueryForDrizzleConnection } from './types.js';
|
|
11
11
|
import { getSchemaConfig } from './utils/config.js';
|
|
12
12
|
import {
|
|
13
|
+
type DrizzleCursorConnectionQueryOptions,
|
|
13
14
|
drizzleCursorConnectionQuery,
|
|
14
15
|
getCursorFormatter,
|
|
15
16
|
wrapConnectionResult,
|
|
16
17
|
} from './utils/cursors.js';
|
|
17
18
|
import { queryFromInfo } from './utils/map-query.js';
|
|
18
19
|
import { getRefFromModel } from './utils/refs.js';
|
|
19
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
createState,
|
|
22
|
+
mergeSelection,
|
|
23
|
+
omitUndefinedKeys,
|
|
24
|
+
selectionToQuery,
|
|
25
|
+
} from './utils/selections.js';
|
|
20
26
|
|
|
21
27
|
export function drizzleConnectionHelpers<
|
|
22
28
|
Types extends SchemaTypes,
|
|
@@ -88,8 +94,8 @@ export function drizzleConnectionHelpers<
|
|
|
88
94
|
ctx: Types['Context'],
|
|
89
95
|
parent?: Parent,
|
|
90
96
|
) {
|
|
91
|
-
const { select,
|
|
92
|
-
const formatCursor = getCursorFormatter(
|
|
97
|
+
const { select, cursorFields } = getQueryArgs(args, ctx);
|
|
98
|
+
const formatCursor = getCursorFormatter(cursorFields, config);
|
|
93
99
|
return wrapConnectionResult(
|
|
94
100
|
list,
|
|
95
101
|
args,
|
|
@@ -117,7 +123,7 @@ export function drizzleConnectionHelpers<
|
|
|
117
123
|
(typeof query === 'function' ? query(args, ctx) : query) ?? {};
|
|
118
124
|
const table = config.relations[tableName];
|
|
119
125
|
|
|
120
|
-
const {
|
|
126
|
+
const { cursorFields, columns, ...connectionQuery } = drizzleCursorConnectionQuery({
|
|
121
127
|
ctx,
|
|
122
128
|
maxSize,
|
|
123
129
|
defaultSize: typeof defaultSize === 'function' ? defaultSize(args, ctx) : defaultSize,
|
|
@@ -125,6 +131,7 @@ export function drizzleConnectionHelpers<
|
|
|
125
131
|
orderBy:
|
|
126
132
|
(typeof orderBy === 'function' ? orderBy(table) : orderBy) ??
|
|
127
133
|
getSchemaConfig(builder).getPrimaryKey(tableName),
|
|
134
|
+
extras: (fieldQuery as { extras?: DrizzleCursorConnectionQueryOptions['extras'] }).extras,
|
|
128
135
|
where,
|
|
129
136
|
config,
|
|
130
137
|
table,
|
|
@@ -140,7 +147,7 @@ export function drizzleConnectionHelpers<
|
|
|
140
147
|
},
|
|
141
148
|
limit: Math.abs(limit ?? connectionQuery.limit),
|
|
142
149
|
},
|
|
143
|
-
|
|
150
|
+
cursorFields,
|
|
144
151
|
};
|
|
145
152
|
};
|
|
146
153
|
|
|
@@ -181,10 +188,10 @@ export function drizzleConnectionHelpers<
|
|
|
181
188
|
const baseQuery = typeof query === 'function' ? query(args, ctx) : (query ?? {});
|
|
182
189
|
const queryResult = selectionToQuery(config, selectState);
|
|
183
190
|
|
|
184
|
-
return {
|
|
191
|
+
return omitUndefinedKeys({
|
|
185
192
|
...baseQuery,
|
|
186
193
|
...queryResult,
|
|
187
|
-
} as unknown as Omit<Selection, 'orderBy'> & {
|
|
194
|
+
}) as unknown as Omit<Selection, 'orderBy'> & {
|
|
188
195
|
orderBy: {
|
|
189
196
|
[K in TableConfig['table']['_'] extends { columns: infer Columns }
|
|
190
197
|
? keyof Columns
|
|
@@ -51,12 +51,13 @@ import type {
|
|
|
51
51
|
} from './types.js';
|
|
52
52
|
import { getClient, getSchemaConfig } from './utils/config.js';
|
|
53
53
|
import {
|
|
54
|
+
type DrizzleCursorConnectionQueryOptions,
|
|
54
55
|
drizzleCursorConnectionQuery,
|
|
55
56
|
getCursorFormatter,
|
|
56
57
|
wrapConnectionResult,
|
|
57
58
|
} from './utils/cursors.js';
|
|
58
59
|
import { getRefFromModel } from './utils/refs.js';
|
|
59
|
-
import type
|
|
60
|
+
import { omitUndefinedKeys, type SelectionMap } from './utils/selections.js';
|
|
60
61
|
|
|
61
62
|
// Workaround for FieldKind not being extended on Builder classes
|
|
62
63
|
const RootBuilder: {
|
|
@@ -236,9 +237,10 @@ export class DrizzleObjectFieldBuilder<
|
|
|
236
237
|
orderBy?: unknown;
|
|
237
238
|
where?: SQL;
|
|
238
239
|
columns?: Record<string, boolean>;
|
|
240
|
+
extras?: DrizzleCursorConnectionQueryOptions['extras'];
|
|
239
241
|
};
|
|
240
242
|
|
|
241
|
-
const {
|
|
243
|
+
const { cursorFields, columns, ...connectionQuery } = drizzleCursorConnectionQuery({
|
|
242
244
|
ctx,
|
|
243
245
|
maxSize,
|
|
244
246
|
defaultSize,
|
|
@@ -246,6 +248,7 @@ export class DrizzleObjectFieldBuilder<
|
|
|
246
248
|
orderBy:
|
|
247
249
|
(typeof orderBy === 'function' ? orderBy(relatedTable.table) : orderBy) ??
|
|
248
250
|
getSchemaConfig(this.builder).getPrimaryKey(relationField.targetTableName),
|
|
251
|
+
extras: fieldQuery.extras,
|
|
249
252
|
where,
|
|
250
253
|
config: schemaConfig,
|
|
251
254
|
table: relatedTable,
|
|
@@ -261,7 +264,7 @@ export class DrizzleObjectFieldBuilder<
|
|
|
261
264
|
},
|
|
262
265
|
limit: Math.abs(limit ?? connectionQuery.limit),
|
|
263
266
|
},
|
|
264
|
-
|
|
267
|
+
cursorFields,
|
|
265
268
|
};
|
|
266
269
|
};
|
|
267
270
|
|
|
@@ -376,13 +379,13 @@ export class DrizzleObjectFieldBuilder<
|
|
|
376
379
|
};
|
|
377
380
|
}
|
|
378
381
|
|
|
379
|
-
const { select,
|
|
382
|
+
const { select, cursorFields } = getQuery(args, context);
|
|
380
383
|
|
|
381
384
|
return wrapConnectionResult(
|
|
382
385
|
parentRecord[name] as readonly {}[],
|
|
383
386
|
args,
|
|
384
387
|
select.limit,
|
|
385
|
-
getCursorFormatter(
|
|
388
|
+
getCursorFormatter(cursorFields, schemaConfig),
|
|
386
389
|
undefined,
|
|
387
390
|
parent,
|
|
388
391
|
countValue,
|
|
@@ -526,7 +529,7 @@ export class DrizzleObjectFieldBuilder<
|
|
|
526
529
|
const relQuery = {
|
|
527
530
|
columns: {},
|
|
528
531
|
with: {
|
|
529
|
-
[name]: {
|
|
532
|
+
[name]: omitUndefinedKeys({
|
|
530
533
|
...nestedQuery(query),
|
|
531
534
|
...((typeof query === 'function'
|
|
532
535
|
? (query as (args: {}, context: {}, pathInfo: PathInfo) => {})(
|
|
@@ -535,7 +538,7 @@ export class DrizzleObjectFieldBuilder<
|
|
|
535
538
|
pathInfo,
|
|
536
539
|
)
|
|
537
540
|
: query) as {}),
|
|
538
|
-
},
|
|
541
|
+
}),
|
|
539
542
|
},
|
|
540
543
|
};
|
|
541
544
|
|
package/src/types.ts
CHANGED
|
@@ -68,6 +68,9 @@ type GetTableConfigFn<TTable = Table> = (table: TTable) => {
|
|
|
68
68
|
primaryKeys: {
|
|
69
69
|
readonly columns: Column[];
|
|
70
70
|
}[];
|
|
71
|
+
uniqueConstraints?: {
|
|
72
|
+
readonly columns: Column[];
|
|
73
|
+
}[];
|
|
71
74
|
readonly columns: Column[];
|
|
72
75
|
};
|
|
73
76
|
|
|
@@ -677,11 +680,12 @@ export type RelatedConnectionOptions<
|
|
|
677
680
|
export type ConnectionOrderBy<T extends TableRelationalConfig> =
|
|
678
681
|
| Column
|
|
679
682
|
| Column[]
|
|
683
|
+
// keys name a column, or an extra the same query selects. `string & {}` keeps
|
|
684
|
+
// the column names in autocomplete while letting extra names through
|
|
680
685
|
| {
|
|
681
|
-
[K in
|
|
682
|
-
| '
|
|
683
|
-
| 'desc'
|
|
684
|
-
| undefined;
|
|
686
|
+
[K in
|
|
687
|
+
| (T['table']['_'] extends { columns: infer Columns } ? keyof Columns : never)
|
|
688
|
+
| (string & {})]?: 'asc' | 'desc' | undefined;
|
|
685
689
|
};
|
|
686
690
|
|
|
687
691
|
export type ShapeFromConnection<T> = T extends { shape: unknown } ? T['shape'] : never;
|
package/src/utils/config.ts
CHANGED
|
@@ -5,7 +5,10 @@ import type { DrizzleClient } from '../types.js';
|
|
|
5
5
|
export interface PothosDrizzleSchemaConfig {
|
|
6
6
|
skipDeferredFragments: boolean;
|
|
7
7
|
relations: AnyRelations;
|
|
8
|
+
findPrimaryKey: (tableName: string) => Column[] | null;
|
|
9
|
+
findTieBreaker: (tableName: string) => Column[] | null;
|
|
8
10
|
getPrimaryKey: (tableName: string) => Column[];
|
|
11
|
+
getUniqueConstraints: (tableName: string) => Column[][];
|
|
9
12
|
columnToTsName: (column: Column) => string;
|
|
10
13
|
}
|
|
11
14
|
const configCache = createContextCache(
|
|
@@ -27,6 +30,113 @@ const configCache = createContextCache(
|
|
|
27
30
|
}
|
|
28
31
|
});
|
|
29
32
|
|
|
33
|
+
const buildTableConfig = (tableName: string) => {
|
|
34
|
+
const table = relations[tableName].table as Table;
|
|
35
|
+
const tableConfig = builder.options.drizzle.getTableConfig(table);
|
|
36
|
+
const tableColumns = Object.values(getColumns(table));
|
|
37
|
+
|
|
38
|
+
// The pg dialect builds primaryKeys and uniqueConstraints from
|
|
39
|
+
// ExtraConfigColumns rather than the table's own columns, so the objects
|
|
40
|
+
// it hands back are not the ones columnNameMappings knows about, and do
|
|
41
|
+
// not carry notNull. Look each one back up by name.
|
|
42
|
+
const toTableColumn = (column: Column) =>
|
|
43
|
+
tableColumns.find((candidate) => candidate.name === column.name) ?? column;
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
columns: tableConfig.columns.map(toTableColumn),
|
|
47
|
+
primaryKeys: tableConfig.primaryKeys.map((key) => key.columns.map(toTableColumn)),
|
|
48
|
+
uniqueConstraints: (tableConfig.uniqueConstraints ?? []).map((constraint) =>
|
|
49
|
+
constraint.columns.map(toTableColumn),
|
|
50
|
+
),
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const tableConfigs = new Map<string, ReturnType<typeof buildTableConfig>>();
|
|
55
|
+
|
|
56
|
+
// drizzle rebuilds this from the table's config callback on every call
|
|
57
|
+
const getTableConfig = (tableName: string) => {
|
|
58
|
+
let tableConfig = tableConfigs.get(tableName);
|
|
59
|
+
|
|
60
|
+
if (!tableConfig) {
|
|
61
|
+
tableConfig = buildTableConfig(tableName);
|
|
62
|
+
tableConfigs.set(tableName, tableConfig);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return tableConfig;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
// Every set of columns the database guarantees is unique. Unique indexes
|
|
69
|
+
// are not included: missing one only costs a redundant order column, while
|
|
70
|
+
// wrongly reporting a set as unique would break pagination.
|
|
71
|
+
const getUniqueConstraints = (tableName: string) => {
|
|
72
|
+
const tableConfig = getTableConfig(tableName);
|
|
73
|
+
|
|
74
|
+
return [
|
|
75
|
+
...tableConfig.columns
|
|
76
|
+
.filter((column) => column.primary || column.isUnique)
|
|
77
|
+
.map((column) => [column]),
|
|
78
|
+
...tableConfig.primaryKeys,
|
|
79
|
+
...tableConfig.uniqueConstraints,
|
|
80
|
+
].filter((columns) => columns.length > 0);
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
// What a cursor can rely on to break ties. Primary key columns are
|
|
84
|
+
// non-nullable whatever the drizzle schema says, because SQL requires it,
|
|
85
|
+
// so a composite key declared without notNull() on each column still
|
|
86
|
+
// counts. A column that is only unique has to say so itself: comparing a
|
|
87
|
+
// cursor against null matches nothing, and rows sharing a null tie anyway.
|
|
88
|
+
const findTieBreaker = (tableName: string) => {
|
|
89
|
+
const tableConfig = getTableConfig(tableName);
|
|
90
|
+
|
|
91
|
+
const primaryKey = tableConfig.columns.find((column) => column.primary);
|
|
92
|
+
|
|
93
|
+
if (primaryKey) {
|
|
94
|
+
return [primaryKey];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const compositeKey = tableConfig.primaryKeys.find((columns) => columns.length > 0);
|
|
98
|
+
|
|
99
|
+
if (compositeKey) {
|
|
100
|
+
return compositeKey;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const uniqueColumn = tableConfig.columns.find((column) => column.isUnique && column.notNull);
|
|
104
|
+
|
|
105
|
+
if (uniqueColumn) {
|
|
106
|
+
return [uniqueColumn];
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const uniqueConstraint = tableConfig.uniqueConstraints.find(
|
|
110
|
+
(columns) => columns.length > 0 && columns.every((column) => column.notNull),
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
return uniqueConstraint ?? null;
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const findPrimaryKey = (tableName: string) => {
|
|
117
|
+
const tableConfig = getTableConfig(tableName);
|
|
118
|
+
|
|
119
|
+
const primaryKey = tableConfig.columns.find((column) => column.primary);
|
|
120
|
+
|
|
121
|
+
if (primaryKey) {
|
|
122
|
+
return [primaryKey];
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const primaryKeys = tableConfig.primaryKeys.find((columns) => columns.length > 0);
|
|
126
|
+
|
|
127
|
+
if (primaryKeys) {
|
|
128
|
+
return primaryKeys;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const uniqueColumn = tableConfig.columns.find((column) => column.isUnique);
|
|
132
|
+
|
|
133
|
+
if (uniqueColumn) {
|
|
134
|
+
return [uniqueColumn];
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return null;
|
|
138
|
+
};
|
|
139
|
+
|
|
30
140
|
return {
|
|
31
141
|
skipDeferredFragments: builder.options.drizzle.skipDeferredFragments ?? true,
|
|
32
142
|
columnToTsName: (column) => {
|
|
@@ -38,30 +148,17 @@ const configCache = createContextCache(
|
|
|
38
148
|
|
|
39
149
|
return tsName;
|
|
40
150
|
},
|
|
151
|
+
findPrimaryKey,
|
|
152
|
+
findTieBreaker,
|
|
153
|
+
getUniqueConstraints,
|
|
41
154
|
getPrimaryKey: (tableName) => {
|
|
42
|
-
const
|
|
43
|
-
relations[tableName].table as Table,
|
|
44
|
-
);
|
|
45
|
-
|
|
46
|
-
const primaryKey = tableConfig.columns.find((column) => column.primary);
|
|
47
|
-
|
|
48
|
-
if (primaryKey) {
|
|
49
|
-
return [primaryKey];
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const primaryKeys = tableConfig.primaryKeys.find((key) => key.columns.length > 0);
|
|
53
|
-
|
|
54
|
-
if (primaryKeys) {
|
|
55
|
-
return primaryKeys.columns;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const uniqueColumn = tableConfig.columns.find((column) => column.isUnique);
|
|
155
|
+
const primaryKey = findPrimaryKey(tableName);
|
|
59
156
|
|
|
60
|
-
if (
|
|
61
|
-
|
|
157
|
+
if (!primaryKey) {
|
|
158
|
+
throw new Error(`Could not find primary key for table ${tableName}`);
|
|
62
159
|
}
|
|
63
160
|
|
|
64
|
-
|
|
161
|
+
return primaryKey;
|
|
65
162
|
},
|
|
66
163
|
relations,
|
|
67
164
|
};
|