@pothos/plugin-drizzle 0.14.0 → 0.16.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 (40) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +417 -49
  3. package/dts/drizzle-field-builder.d.ts +14 -3
  4. package/dts/drizzle-field-builder.d.ts.map +1 -1
  5. package/dts/types.d.ts +6 -0
  6. package/dts/types.d.ts.map +1 -1
  7. package/dts/utils/cursors.d.ts +6 -2
  8. package/dts/utils/cursors.d.ts.map +1 -1
  9. package/dts/utils/map-query.d.ts.map +1 -1
  10. package/esm/drizzle-field-builder.d.ts +16 -3
  11. package/esm/drizzle-field-builder.d.ts.map +1 -1
  12. package/esm/drizzle-field-builder.js +115 -7
  13. package/esm/drizzle-field-builder.js.map +1 -1
  14. package/esm/field-builder.js +33 -2
  15. package/esm/field-builder.js.map +1 -1
  16. package/esm/types.d.ts +6 -0
  17. package/esm/types.d.ts.map +1 -1
  18. package/esm/types.js.map +1 -1
  19. package/esm/utils/cursors.d.ts +6 -2
  20. package/esm/utils/cursors.d.ts.map +1 -1
  21. package/esm/utils/cursors.js +18 -2
  22. package/esm/utils/cursors.js.map +1 -1
  23. package/esm/utils/map-query.d.ts.map +1 -1
  24. package/esm/utils/map-query.js +25 -6
  25. package/esm/utils/map-query.js.map +1 -1
  26. package/lib/drizzle-field-builder.js +113 -5
  27. package/lib/drizzle-field-builder.js.map +1 -1
  28. package/lib/field-builder.js +33 -2
  29. package/lib/field-builder.js.map +1 -1
  30. package/lib/types.js.map +1 -1
  31. package/lib/utils/cursors.js +18 -2
  32. package/lib/utils/cursors.js.map +1 -1
  33. package/lib/utils/map-query.js +24 -6
  34. package/lib/utils/map-query.js.map +1 -1
  35. package/package.json +4 -4
  36. package/src/drizzle-field-builder.ts +207 -3
  37. package/src/field-builder.ts +40 -1
  38. package/src/types.ts +20 -1
  39. package/src/utils/cursors.ts +24 -3
  40. package/src/utils/map-query.ts +26 -6
@@ -1,7 +1,8 @@
1
- import type { FieldRef } from '@pothos/core';
1
+ import type { FieldRef, MaybePromise } from '@pothos/core';
2
2
  import { type FieldKind, ObjectRef, RootFieldBuilder, type SchemaTypes } from '@pothos/core';
3
3
  import type { TableRelationalConfig } from 'drizzle-orm';
4
4
  import type { GraphQLResolveInfo } from 'graphql';
5
+ import { isInterfaceType, isObjectType, Kind } from 'graphql';
5
6
  import type { DrizzleRef } from './interface-ref';
6
7
  import type { DrizzleConnectionFieldOptions } from './types';
7
8
  import { getSchemaConfig } from './utils/config';
@@ -101,6 +102,7 @@ fieldBuilderProto.drizzleConnection = function drizzleConnection<
101
102
  maxSize = this.builder.options.drizzle?.maxConnectionSize,
102
103
  defaultSize = this.builder.options.drizzle?.defaultConnectionSize,
103
104
  resolve,
105
+ totalCount,
104
106
  ...options
105
107
  }: DrizzleConnectionFieldOptions<
106
108
  SchemaTypes,
@@ -133,6 +135,21 @@ fieldBuilderProto.drizzleConnection = function drizzleConnection<
133
135
  context: {},
134
136
  info: GraphQLResolveInfo,
135
137
  ) => {
138
+ const returnType = info.returnType;
139
+ const fields =
140
+ isObjectType(returnType) || isInterfaceType(returnType) ? returnType.getFields() : {};
141
+
142
+ const selections = info.fieldNodes;
143
+
144
+ const totalCountOnly = selections.every((selection) =>
145
+ selection.selectionSet?.selections.every(
146
+ (s) =>
147
+ s.kind === Kind.FIELD &&
148
+ (fields[s.name.value]?.extensions?.pothosDrizzleTotalCount ||
149
+ s.name.value === '__typename'),
150
+ ),
151
+ );
152
+
136
153
  return resolveDrizzleCursorConnection(
137
154
  tableName,
138
155
  info,
@@ -143,8 +160,13 @@ fieldBuilderProto.drizzleConnection = function drizzleConnection<
143
160
  maxSize,
144
161
  defaultSize,
145
162
  args,
163
+ totalCount: totalCount && (() => totalCount(parent, args as never, context, info)),
146
164
  },
147
165
  (q) => {
166
+ if (totalCountOnly) {
167
+ return [];
168
+ }
169
+
148
170
  // return checkIfQueryIsUsed(
149
171
  // this.builder,
150
172
  // query,
@@ -160,6 +182,23 @@ fieldBuilderProto.drizzleConnection = function drizzleConnection<
160
182
  ? connectionOptions
161
183
  : {
162
184
  ...connectionOptions,
185
+ fields: totalCount
186
+ ? (
187
+ t: PothosSchemaTypes.ObjectFieldBuilder<
188
+ SchemaTypes,
189
+ { totalCount?: () => MaybePromise<number> }
190
+ >,
191
+ ) => ({
192
+ totalCount: t.int({
193
+ nullable: false,
194
+ extensions: {
195
+ pothosDrizzleTotalCount: true,
196
+ },
197
+ resolve: (parent) => parent.totalCount?.(),
198
+ }),
199
+ ...(connectionOptions as { fields?: (t: unknown) => {} }).fields?.(t),
200
+ })
201
+ : (connectionOptions as { fields: undefined }).fields,
163
202
  extensions: {
164
203
  ...(connectionOptions as Record<string, object> | undefined)?.extensions,
165
204
  },
package/src/types.ts CHANGED
@@ -51,6 +51,7 @@ export type DrizzleClient = {
51
51
  readonly relations: AnyRelations;
52
52
  };
53
53
  query: {};
54
+ $count: (table: Table, filter?: SQL) => SQL<number>;
54
55
  };
55
56
 
56
57
  export type DrizzlePluginOptions<Types extends SchemaTypes> = {
@@ -428,6 +429,18 @@ export type RefForRelation<Types extends SchemaTypes, Rel extends Relation> = Re
428
429
  ? ObjectRef<Types, TypesForRelation<Types, Rel>>
429
430
  : [ObjectRef<Types, TypesForRelation<Types, Rel>>];
430
431
 
432
+ export type RelatedCountOptions<
433
+ Types extends SchemaTypes,
434
+ Shape,
435
+ Args extends InputFieldMap,
436
+ Where,
437
+ > = Omit<
438
+ PothosSchemaTypes.ObjectFieldOptions<Types, Shape, 'Int', false, Args, number>,
439
+ 'type' | InferredFieldOptionKeys
440
+ > & {
441
+ where?: Where | ((args: InputShapeFromFields<Args>, context: Types['Context']) => Where);
442
+ };
443
+
431
444
  export type TypesForRelation<Types extends SchemaTypes, Rel extends Relation> = BuildQueryResult<
432
445
  Types['DrizzleRelations'],
433
446
  Types['DrizzleRelations'][Rel['targetTable']['_']['name']],
@@ -543,6 +556,12 @@ export type DrizzleConnectionFieldOptions<
543
556
  type: Type;
544
557
  defaultSize?: number | ((args: ConnectionArgs, ctx: Types['Context']) => number);
545
558
  maxSize?: number | ((args: ConnectionArgs, ctx: Types['Context']) => number);
559
+ totalCount?: (
560
+ parent: ParentShape,
561
+ args: ConnectionArgs,
562
+ context: Types['Context'],
563
+ info: GraphQLResolveInfo,
564
+ ) => MaybePromise<number>;
546
565
 
547
566
  resolve: (
548
567
  query: <T = {}>(
@@ -626,7 +645,7 @@ export type RelatedConnectionOptions<
626
645
 
627
646
  defaultSize?: number | ((args: ConnectionArgs, ctx: Types['Context']) => number);
628
647
  maxSize?: number | ((args: ConnectionArgs, ctx: Types['Context']) => number);
629
- // totalCount?: NeedsResolve extends false ? boolean : false;
648
+ totalCount?: boolean;
630
649
  }
631
650
  : never);
632
651
 
@@ -462,6 +462,7 @@ export function wrapConnectionResult<T extends {}>(
462
462
  cursor: (node: T) => string,
463
463
  resolveNode?: (node: Record<string, unknown>) => unknown,
464
464
  parent?: unknown,
465
+ totalCount?: number | (() => MaybePromise<number>) | null,
465
466
  ) {
466
467
  const gotFullResults = results.length === Math.abs(limit);
467
468
  const hasNextPage = args.before ? true : args.last ? false : gotFullResults;
@@ -471,6 +472,7 @@ export function wrapConnectionResult<T extends {}>(
471
472
  const connection = {
472
473
  parent,
473
474
  args,
475
+ totalCount,
474
476
  edges: [] as ({ cursor: string; node: unknown } | null)[],
475
477
  pageInfo: {
476
478
  startCursor: null as string | null,
@@ -513,14 +515,16 @@ export async function resolveDrizzleCursorConnection<T extends {}>(
513
515
  info: GraphQLResolveInfo,
514
516
  typeName: string,
515
517
  config: PothosDrizzleSchemaConfig,
516
- options: Omit<DrizzleCursorConnectionQueryOptions, 'orderBy' | 'config' | 'table'>,
518
+ options: Omit<DrizzleCursorConnectionQueryOptions, 'orderBy' | 'config' | 'table'> & {
519
+ totalCount?: () => MaybePromise<number>;
520
+ },
517
521
  resolve: (
518
522
  queryFn: (query: QueryForDrizzleConnection<SchemaTypes, TableRelationalConfig>) => SelectionMap,
519
523
  ) => MaybePromise<readonly T[]>,
520
524
  parent: unknown,
521
525
  ) {
522
526
  const table = config.relations[tableName];
523
- let query: DBQueryConfig<'many'>;
527
+ let query: DBQueryConfig<'many'> | undefined;
524
528
  let formatter: (node: Record<string, unknown>) => string;
525
529
  const results = await resolve((q = {}) => {
526
530
  const { cursorColumns, ...connectionQuery } = drizzleCursorConnectionQuery({
@@ -562,12 +566,29 @@ export async function resolveDrizzleCursorConnection<T extends {}>(
562
566
  return results;
563
567
  }
564
568
 
569
+ // Handle totalCountOnly case where resolve returns [] without calling query function
570
+ if (!query) {
571
+ return {
572
+ parent,
573
+ args: options.args,
574
+ totalCount: options.totalCount,
575
+ edges: [],
576
+ pageInfo: {
577
+ startCursor: null,
578
+ endCursor: null,
579
+ hasPreviousPage: false,
580
+ hasNextPage: false,
581
+ },
582
+ };
583
+ }
584
+
565
585
  return wrapConnectionResult(
566
586
  results,
567
587
  options.args,
568
- query!.limit as number,
588
+ query.limit as number,
569
589
  formatter!,
570
590
  undefined,
571
591
  parent,
592
+ options.totalCount,
572
593
  );
573
594
  }
@@ -401,15 +401,35 @@ function addFieldSelection(
401
401
 
402
402
  if (fieldSelect && selectionCompatible(state, fieldSelectionMap, true)) {
403
403
  mergeSelection(config, state, fieldSelectionMap);
404
- state.mappings[selection.alias?.value ?? selection.name.value] = {
405
- field: selection.name.value,
406
- type: type.name,
407
- mappings,
408
- indirectPath,
409
- };
404
+
405
+ state.mappings = mergeMappings(state.mappings, {
406
+ [selection.alias?.value ?? selection.name.value]: {
407
+ field: selection.name.value,
408
+ type: type.name,
409
+ mappings,
410
+ indirectPath,
411
+ },
412
+ });
410
413
  }
411
414
  }
412
415
 
416
+ function mergeMappings(existing: LoaderMappings, incoming: LoaderMappings): LoaderMappings {
417
+ const result: LoaderMappings = { ...existing };
418
+
419
+ for (const [key, value] of Object.entries(incoming)) {
420
+ if (result[key]) {
421
+ result[key] = {
422
+ ...result[key],
423
+ mappings: mergeMappings(result[key].mappings, value.mappings),
424
+ };
425
+ } else {
426
+ result[key] = value;
427
+ }
428
+ }
429
+
430
+ return result;
431
+ }
432
+
413
433
  export interface QueryFromInfoOptions<T extends SelectionMap> {
414
434
  config: PothosDrizzleSchemaConfig;
415
435
  context: object;