@pothos/plugin-drizzle 0.8.1 → 0.10.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 (122) hide show
  1. package/CHANGELOG.md +102 -0
  2. package/TODO.md +25 -4
  3. package/dts/connection-helpers.d.ts +34 -0
  4. package/dts/connection-helpers.d.ts.map +1 -0
  5. package/dts/drizzle-field-builder.d.ts +18 -53
  6. package/dts/drizzle-field-builder.d.ts.map +1 -1
  7. package/dts/global-types.d.ts +22 -41
  8. package/dts/global-types.d.ts.map +1 -1
  9. package/dts/index.d.ts +5 -2
  10. package/dts/index.d.ts.map +1 -1
  11. package/dts/interface-ref.d.ts +3 -2
  12. package/dts/interface-ref.d.ts.map +1 -1
  13. package/dts/model-loader.d.ts +3 -2
  14. package/dts/model-loader.d.ts.map +1 -1
  15. package/dts/node-ref.d.ts +1 -1
  16. package/dts/node-ref.d.ts.map +1 -1
  17. package/dts/object-ref.d.ts +2 -2
  18. package/dts/object-ref.d.ts.map +1 -1
  19. package/dts/types.d.ts +61 -41
  20. package/dts/types.d.ts.map +1 -1
  21. package/dts/utils/config.d.ts +4 -3
  22. package/dts/utils/config.d.ts.map +1 -1
  23. package/dts/utils/cursors.d.ts +13 -7
  24. package/dts/utils/cursors.d.ts.map +1 -1
  25. package/dts/utils/drizzle-graphql.d.ts +0 -17
  26. package/dts/utils/drizzle-graphql.d.ts.map +1 -1
  27. package/dts/utils/map-query.d.ts.map +1 -1
  28. package/dts/utils/selections.d.ts +5 -5
  29. package/dts/utils/selections.d.ts.map +1 -1
  30. package/esm/connection-helpers.d.ts +36 -0
  31. package/esm/connection-helpers.d.ts.map +1 -0
  32. package/esm/connection-helpers.js +82 -0
  33. package/esm/connection-helpers.js.map +1 -0
  34. package/esm/drizzle-field-builder.d.ts +18 -73
  35. package/esm/drizzle-field-builder.d.ts.map +1 -1
  36. package/esm/drizzle-field-builder.js +25 -14
  37. package/esm/drizzle-field-builder.js.map +1 -1
  38. package/esm/field-builder.js +2 -3
  39. package/esm/field-builder.js.map +1 -1
  40. package/esm/global-types.d.ts +35 -60
  41. package/esm/global-types.d.ts.map +1 -1
  42. package/esm/global-types.js.map +1 -1
  43. package/esm/index.d.ts +5 -2
  44. package/esm/index.d.ts.map +1 -1
  45. package/esm/index.js +71 -61
  46. package/esm/index.js.map +1 -1
  47. package/esm/interface-ref.d.ts +3 -2
  48. package/esm/interface-ref.d.ts.map +1 -1
  49. package/esm/interface-ref.js.map +1 -1
  50. package/esm/model-loader.d.ts +3 -2
  51. package/esm/model-loader.d.ts.map +1 -1
  52. package/esm/model-loader.js +17 -10
  53. package/esm/model-loader.js.map +1 -1
  54. package/esm/node-ref.d.ts +1 -1
  55. package/esm/node-ref.d.ts.map +1 -1
  56. package/esm/object-ref.d.ts +2 -2
  57. package/esm/object-ref.d.ts.map +1 -1
  58. package/esm/schema-builder.js +113 -67
  59. package/esm/schema-builder.js.map +1 -1
  60. package/esm/types.d.ts +61 -41
  61. package/esm/types.d.ts.map +1 -1
  62. package/esm/types.js.map +1 -1
  63. package/esm/utils/config.d.ts +4 -3
  64. package/esm/utils/config.d.ts.map +1 -1
  65. package/esm/utils/config.js +32 -15
  66. package/esm/utils/config.js.map +1 -1
  67. package/esm/utils/cursors.d.ts +13 -7
  68. package/esm/utils/cursors.d.ts.map +1 -1
  69. package/esm/utils/cursors.js +137 -63
  70. package/esm/utils/cursors.js.map +1 -1
  71. package/esm/utils/drizzle-graphql.d.ts +0 -17
  72. package/esm/utils/drizzle-graphql.d.ts.map +1 -1
  73. package/esm/utils/drizzle-graphql.js +230 -198
  74. package/esm/utils/drizzle-graphql.js.map +1 -1
  75. package/esm/utils/map-query.d.ts.map +1 -1
  76. package/esm/utils/map-query.js +10 -6
  77. package/esm/utils/map-query.js.map +1 -1
  78. package/esm/utils/selections.d.ts +5 -5
  79. package/esm/utils/selections.d.ts.map +1 -1
  80. package/esm/utils/selections.js +19 -18
  81. package/esm/utils/selections.js.map +1 -1
  82. package/lib/connection-helpers.js +93 -0
  83. package/lib/connection-helpers.js.map +1 -0
  84. package/lib/drizzle-field-builder.js +24 -13
  85. package/lib/drizzle-field-builder.js.map +1 -1
  86. package/lib/field-builder.js +2 -3
  87. package/lib/field-builder.js.map +1 -1
  88. package/lib/index.js +79 -60
  89. package/lib/index.js.map +1 -1
  90. package/lib/interface-ref.js.map +1 -1
  91. package/lib/model-loader.js +17 -10
  92. package/lib/model-loader.js.map +1 -1
  93. package/lib/schema-builder.js +113 -67
  94. package/lib/schema-builder.js.map +1 -1
  95. package/lib/types.js.map +1 -1
  96. package/lib/utils/config.js +31 -14
  97. package/lib/utils/config.js.map +1 -1
  98. package/lib/utils/cursors.js +135 -63
  99. package/lib/utils/cursors.js.map +1 -1
  100. package/lib/utils/drizzle-graphql.js +230 -221
  101. package/lib/utils/drizzle-graphql.js.map +1 -1
  102. package/lib/utils/map-query.js +10 -6
  103. package/lib/utils/map-query.js.map +1 -1
  104. package/lib/utils/selections.js +18 -17
  105. package/lib/utils/selections.js.map +1 -1
  106. package/package.json +14 -14
  107. package/src/connection-helpers.ts +206 -0
  108. package/src/drizzle-field-builder.ts +30 -26
  109. package/src/field-builder.ts +4 -7
  110. package/src/global-types.ts +112 -119
  111. package/src/index.ts +91 -70
  112. package/src/interface-ref.ts +7 -3
  113. package/src/model-loader.ts +32 -17
  114. package/src/node-ref.ts +1 -1
  115. package/src/object-ref.ts +2 -2
  116. package/src/schema-builder.ts +116 -113
  117. package/src/types.ts +95 -62
  118. package/src/utils/config.ts +39 -30
  119. package/src/utils/cursors.ts +134 -101
  120. package/src/utils/drizzle-graphql.ts +273 -274
  121. package/src/utils/map-query.ts +20 -8
  122. package/src/utils/selections.ts +34 -28
@@ -5,13 +5,14 @@ import {
5
5
  decodeBase64,
6
6
  encodeBase64,
7
7
  } from '@pothos/core';
8
- import {
9
- type Column,
10
- type DBQueryConfig,
11
- type SQL,
12
- type TableRelationalConfig,
13
- getOperators,
14
- getOrderByOperators,
8
+ import type {
9
+ Column,
10
+ DBQueryConfig,
11
+ OrderByOperators,
12
+ SQL,
13
+ Table,
14
+ TableConfig,
15
+ TableRelationalConfig,
15
16
  } from 'drizzle-orm';
16
17
  import type { GraphQLResolveInfo } from 'graphql';
17
18
  import type { ConnectionOrderBy, QueryForDrizzleConnection } from '../types';
@@ -73,6 +74,7 @@ export function getIDSerializer(fields: Column[], config: PothosDrizzleSchemaCon
73
74
 
74
75
  return (value: Record<string, unknown>) => {
75
76
  if (fields.length > 1) {
77
+ fields.map((field) => field.keyAsName);
76
78
  return `${JSON.stringify(fields.map((col) => value[config.columnToTsName(col)]))}`;
77
79
  }
78
80
 
@@ -286,49 +288,58 @@ export interface DrizzleCursorConnectionQueryOptions {
286
288
  ctx: {};
287
289
  defaultSize?: number | ((args: {}, ctx: {}) => number);
288
290
  maxSize?: number | ((args: {}, ctx: {}) => number);
289
- orderBy: ConnectionOrderBy;
291
+ orderBy: ConnectionOrderBy<TableRelationalConfig>;
290
292
  where?: SQL;
293
+ config: PothosDrizzleSchemaConfig;
294
+ table: TableRelationalConfig | TableConfig;
291
295
  }
292
296
 
293
- const orderByOps = getOrderByOperators();
294
-
295
- function parseOrderBy(orderBy: ConnectionOrderBy, invert: boolean) {
296
- if (!Array.isArray(orderBy)) {
297
- return parseOrderBy([orderBy], invert);
298
- }
297
+ function parseOrderBy(
298
+ config: PothosDrizzleSchemaConfig,
299
+ table: TableRelationalConfig | TableConfig,
300
+ orderBy: ConnectionOrderBy<TableRelationalConfig>,
301
+ invert: boolean,
302
+ ) {
303
+ const entries: [name: string, direction: 'asc' | 'desc'][] = [];
304
+ const columns: Column[] = [];
305
+ const normalized: { direction: 'asc' | 'desc'; column: Column }[] = [];
299
306
 
300
307
  const asc = invert ? 'desc' : 'asc';
301
308
  const desc = invert ? 'asc' : 'desc';
302
309
 
303
- const normalized: { direction: 'asc' | 'desc'; column: Column }[] = orderBy.map((field) => {
304
- if (typeof field === 'object' && 'asc' in field) {
305
- return {
306
- direction: asc,
307
- column: field.asc,
308
- };
310
+ if ('table' in orderBy && orderBy.table && typeof orderBy.table === 'object') {
311
+ entries.push([config.columnToTsName(orderBy as Column), asc]);
312
+ columns.push(orderBy as Column);
313
+ normalized.push({ direction: 'asc', column: orderBy as Column });
314
+ } else if (Array.isArray(orderBy)) {
315
+ for (const field of orderBy) {
316
+ entries.push([config.columnToTsName(field), asc]);
317
+ columns.push(field);
318
+ normalized.push({ direction: 'asc', column: field });
309
319
  }
310
- if (typeof field === 'object' && 'desc' in field) {
311
- return {
312
- direction: desc,
313
- column: field.desc,
314
- };
315
- }
316
-
317
- return {
318
- direction: asc,
319
- column: field,
320
- };
321
- });
320
+ } else {
321
+ // biome-ignore lint/complexity/noForEach: <explanation>
322
+ Object.entries(
323
+ orderBy as {
324
+ [k: string]: 'asc' | 'desc' | undefined;
325
+ },
326
+ ).forEach(([name, direction]) => {
327
+ if (direction) {
328
+ const column = table.columns[name] as Column;
329
+ columns.push(column);
330
+ entries.push([name, direction === 'asc' ? asc : desc]);
331
+ normalized.push({ direction: direction, column });
332
+ }
333
+ });
334
+ }
322
335
 
323
336
  return {
324
337
  normalized,
325
- columns: normalized.map((field) => field.column),
326
- sql: normalized.map((field) => orderByOps[field.direction](field.column)),
338
+ columns,
339
+ orderBy: Object.fromEntries(entries),
327
340
  };
328
341
  }
329
342
 
330
- const ops = getOperators();
331
-
332
343
  export function drizzleCursorConnectionQuery({
333
344
  args,
334
345
  ctx,
@@ -336,6 +347,8 @@ export function drizzleCursorConnectionQuery({
336
347
  defaultSize = DEFAULT_SIZE,
337
348
  orderBy,
338
349
  where,
350
+ config,
351
+ table,
339
352
  }: DrizzleCursorConnectionQueryOptions) {
340
353
  const { before, after, first, last } = args;
341
354
  if (first != null && first < 0) {
@@ -346,79 +359,95 @@ export function drizzleCursorConnectionQuery({
346
359
  throw new PothosValidationError('Argument "last" must be a non-negative integer');
347
360
  }
348
361
 
349
- if (before && after) {
362
+ if (first != null && last != null) {
350
363
  throw new PothosValidationError(
351
- 'Arguments "before" and "after" are not supported at the same time',
364
+ 'Arguments "first" and "last" are not supported at the same time',
352
365
  );
353
366
  }
354
367
 
355
- if (before != null && first != null) {
356
- throw new PothosValidationError(
357
- 'Arguments "before" and "first" are not supported at the same time',
358
- );
359
- }
360
-
361
- if (after != null && last != null) {
362
- throw new PothosValidationError(
363
- 'Arguments "after" and "last" are not supported at the same time',
364
- );
365
- }
366
-
367
- const cursor = before ?? after;
368
-
369
368
  const maxSizeForConnection = typeof maxSize === 'function' ? maxSize(args, ctx) : maxSize;
370
369
  const defaultSizeForConnection =
371
370
  typeof defaultSize === 'function' ? defaultSize(args, ctx) : defaultSize;
372
371
 
373
- let limit = Math.min(first ?? last ?? defaultSizeForConnection, maxSizeForConnection) + 1;
372
+ const limit = Math.min(first ?? last ?? defaultSizeForConnection, maxSizeForConnection) + 1;
373
+ const inverted = !first && !!last;
374
374
 
375
- if (before ?? last) {
376
- limit = -limit;
377
- }
378
-
379
- const parsedOrderBy = parseOrderBy(orderBy, limit < 0);
375
+ const parsedOrderBy = parseOrderBy(config, table, orderBy, inverted);
380
376
 
381
377
  const columns: Record<string, boolean> = {};
382
378
 
383
379
  for (const column of parsedOrderBy.columns) {
384
- columns[column.name] = true;
380
+ columns[config.columnToTsName(column)] = true;
385
381
  }
386
382
 
387
- const whereClauses: (SQL | undefined)[] = where ? [where] : [];
383
+ const whereClauses: {}[] = [];
384
+
385
+ if (where) {
386
+ whereClauses.push(where);
387
+ }
388
388
 
389
- if (cursor) {
389
+ if (after) {
390
390
  const cursorParser = getCursorParser(parsedOrderBy.columns);
391
- const parsedCursor = cursorParser(cursor);
392
-
393
- whereClauses.push(
394
- ops.or(
395
- ...parsedOrderBy.normalized.map(({ direction, column }, index) => {
396
- const compare =
397
- direction === 'asc'
398
- ? ops.gt(column, parsedCursor[column.name])
399
- : ops.lt(column, parsedCursor[column.name]);
400
-
401
- if (index === 0) {
402
- return compare;
403
- }
391
+ const parsedCursor = cursorParser(after);
404
392
 
405
- return ops.and(
406
- ...parsedOrderBy.normalized
407
- .slice(0, index)
408
- .map(({ column: c }) => ops.eq(c, parsedCursor[c.name])),
409
- compare,
410
- );
411
- }),
412
- ),
413
- );
393
+ const parts = parsedOrderBy.normalized.map(({ direction, column }, index) => {
394
+ const columnName = config.columnToTsName(column);
395
+ const compare =
396
+ direction === 'asc'
397
+ ? { [columnName]: { gt: parsedCursor[column.name] } }
398
+ : { [columnName]: { lt: parsedCursor[column.name] } };
399
+
400
+ if (index === 0) {
401
+ return compare;
402
+ }
403
+
404
+ return {
405
+ AND: [
406
+ ...parsedOrderBy.normalized.slice(0, index).map(({ column: c }) => {
407
+ return { [config.columnToTsName(c)]: parsedCursor[c.name] };
408
+ }),
409
+ compare,
410
+ ],
411
+ };
412
+ });
413
+
414
+ whereClauses.push(parts.length > 1 ? { OR: parts } : parts[0]);
415
+ }
416
+
417
+ if (before) {
418
+ const cursorParser = getCursorParser(parsedOrderBy.columns);
419
+ const parsedCursor = cursorParser(before);
420
+
421
+ const parts = parsedOrderBy.normalized.map(({ direction, column }, index) => {
422
+ const columnName = config.columnToTsName(column);
423
+ const compare =
424
+ direction === 'desc'
425
+ ? { [columnName]: { gt: parsedCursor[column.name] } }
426
+ : { [columnName]: { lt: parsedCursor[column.name] } };
427
+
428
+ if (index === 0) {
429
+ return compare;
430
+ }
431
+
432
+ return {
433
+ AND: [
434
+ ...parsedOrderBy.normalized.slice(0, index).map(({ column: c }) => {
435
+ return { [config.columnToTsName(c)]: parsedCursor[c.name] };
436
+ }),
437
+ compare,
438
+ ],
439
+ };
440
+ });
441
+
442
+ whereClauses.push(parts.length > 1 ? { OR: parts } : parts[0]);
414
443
  }
415
444
 
416
445
  return {
417
446
  cursorColumns: parsedOrderBy.columns,
418
447
  columns,
419
- orderBy: parsedOrderBy.sql,
448
+ orderBy: parsedOrderBy.orderBy,
420
449
  limit,
421
- where: ops.and(...whereClauses),
450
+ where: whereClauses.length > 1 ? { AND: whereClauses } : whereClauses[0],
422
451
  };
423
452
  }
424
453
 
@@ -428,15 +457,15 @@ export function wrapConnectionResult<T extends {}>(
428
457
  limit: number,
429
458
  cursor: (node: T) => string,
430
459
  resolveNode?: (node: Record<string, unknown>) => unknown,
460
+ parent?: unknown,
431
461
  ) {
432
462
  const gotFullResults = results.length === Math.abs(limit);
433
463
  const hasNextPage = args.before ? true : args.last ? false : gotFullResults;
434
- const hasPreviousPage = args.after ? true : (args.before ?? args.last) ? gotFullResults : false;
435
- const nodes = gotFullResults
436
- ? results.slice(limit < 0 ? 1 : 0, limit < 0 ? results.length : -1)
437
- : results;
464
+ const hasPreviousPage = args.after ? true : !args.first && !!args.last ? gotFullResults : false;
465
+ const nodes = gotFullResults ? results.slice(0, -1) : results;
438
466
 
439
467
  const connection = {
468
+ parent,
440
469
  args,
441
470
  edges: [] as ({ cursor: string; node: unknown } | null)[],
442
471
  pageInfo: {
@@ -476,30 +505,29 @@ export function wrapConnectionResult<T extends {}>(
476
505
  }
477
506
 
478
507
  export async function resolveDrizzleCursorConnection<T extends {}>(
479
- table: TableRelationalConfig,
508
+ tableName: string,
480
509
  info: GraphQLResolveInfo,
481
510
  typeName: string,
482
511
  config: PothosDrizzleSchemaConfig,
483
- options: Omit<DrizzleCursorConnectionQueryOptions, 'orderBy'>,
512
+ options: Omit<DrizzleCursorConnectionQueryOptions, 'orderBy' | 'config' | 'table'>,
484
513
  resolve: (
485
514
  queryFn: (query: QueryForDrizzleConnection<SchemaTypes, TableRelationalConfig>) => SelectionMap,
486
515
  ) => MaybePromise<readonly T[]>,
487
516
  ) {
488
- let query: DBQueryConfig<'many', false>;
517
+ const table = config.relations.tablesConfig[tableName];
518
+ let query: DBQueryConfig<'many'>;
489
519
  let formatter: (node: Record<string, unknown>) => string;
490
520
  const results = await resolve((q = {}) => {
491
521
  const { cursorColumns, ...connectionQuery } = drizzleCursorConnectionQuery({
492
522
  ...options,
493
- orderBy: q.orderBy
494
- ? typeof q.orderBy === 'function'
495
- ? q.orderBy(table.columns)
496
- : q.orderBy
497
- : table.primaryKey,
523
+ config,
524
+ orderBy:
525
+ (typeof q.orderBy === 'function' ? q.orderBy(table.columns) : q.orderBy) ??
526
+ config.getPrimaryKey(table.tsName),
527
+ table,
498
528
  });
499
529
  formatter = getCursorFormatter(cursorColumns, config);
500
530
 
501
- const where = typeof q.where === 'function' ? q.where(table.columns, getOperators()) : q.where;
502
-
503
531
  query = queryFromInfo({
504
532
  context: options.ctx,
505
533
  info,
@@ -509,8 +537,13 @@ export async function resolveDrizzleCursorConnection<T extends {}>(
509
537
  ...q.columns,
510
538
  ...connectionQuery.columns,
511
539
  },
512
- where: ops.and(connectionQuery.where, where),
513
- },
540
+ where:
541
+ connectionQuery.where && q.where
542
+ ? {
543
+ AND: [q.where, connectionQuery.where],
544
+ }
545
+ : q.where || connectionQuery.where,
546
+ } as never,
514
547
  paths: [['nodes'], ['edges', 'node']],
515
548
  typeName,
516
549
  config,