joist-graphql-resolver-utils 2.3.0-next.41 → 2.3.0-next.43

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 (115) hide show
  1. package/build/context.cjs +2 -0
  2. package/build/context.d.cts +7 -0
  3. package/build/context.d.cts.map +1 -0
  4. package/build/context.d.mts +7 -0
  5. package/build/context.d.mts.map +1 -0
  6. package/build/context.js +2 -3
  7. package/build/cursorPagination.cjs +85 -0
  8. package/build/cursorPagination.cjs.map +1 -0
  9. package/build/cursorPagination.d.cts +35 -0
  10. package/build/cursorPagination.d.cts.map +1 -0
  11. package/build/cursorPagination.d.mts +35 -0
  12. package/build/cursorPagination.d.mts.map +1 -0
  13. package/build/cursorPagination.js +69 -62
  14. package/build/cursorPagination.js.map +1 -1
  15. package/build/entityResolver.cjs +95 -0
  16. package/build/entityResolver.cjs.map +1 -0
  17. package/build/entityResolver.d.cts +23 -0
  18. package/build/entityResolver.d.cts.map +1 -0
  19. package/build/entityResolver.d.mts +23 -0
  20. package/build/entityResolver.d.mts.map +1 -0
  21. package/build/entityResolver.js +84 -146
  22. package/build/entityResolver.js.map +1 -1
  23. package/build/hint.cjs +50 -0
  24. package/build/hint.cjs.map +1 -0
  25. package/build/hint.d.cts +12 -0
  26. package/build/hint.d.cts.map +1 -0
  27. package/build/hint.d.mts +12 -0
  28. package/build/hint.d.mts.map +1 -0
  29. package/build/hint.js +36 -65
  30. package/build/hint.js.map +1 -1
  31. package/build/index.cjs +14 -0
  32. package/build/index.d.cts +6 -0
  33. package/build/index.d.mts +6 -0
  34. package/build/index.js +6 -17
  35. package/build/limitPagination.cjs +65 -0
  36. package/build/limitPagination.cjs.map +1 -0
  37. package/build/limitPagination.d.cts +30 -0
  38. package/build/limitPagination.d.cts.map +1 -0
  39. package/build/limitPagination.d.mts +30 -0
  40. package/build/limitPagination.d.mts.map +1 -0
  41. package/build/limitPagination.js +58 -51
  42. package/build/limitPagination.js.map +1 -1
  43. package/build/makeRunInputMutation.cjs +21 -0
  44. package/build/makeRunInputMutation.cjs.map +1 -0
  45. package/build/{makeRunInputMutation.d.ts → makeRunInputMutation.d.cts} +9 -6
  46. package/build/makeRunInputMutation.d.cts.map +1 -0
  47. package/build/makeRunInputMutation.d.mts +24 -0
  48. package/build/makeRunInputMutation.d.mts.map +1 -0
  49. package/build/makeRunInputMutation.js +16 -14
  50. package/build/makeRunInputMutation.js.map +1 -1
  51. package/build/makeRunObjectField.cjs +22 -0
  52. package/build/makeRunObjectField.cjs.map +1 -0
  53. package/build/{makeRunObjectField.d.ts → makeRunObjectField.d.cts} +7 -4
  54. package/build/makeRunObjectField.d.cts.map +1 -0
  55. package/build/makeRunObjectField.d.mts +16 -0
  56. package/build/makeRunObjectField.d.mts.map +1 -0
  57. package/build/makeRunObjectField.js +17 -16
  58. package/build/makeRunObjectField.js.map +1 -1
  59. package/build/makeRunObjectFields.cjs +24 -0
  60. package/build/makeRunObjectFields.cjs.map +1 -0
  61. package/build/{makeRunObjectFields.d.ts → makeRunObjectFields.d.cts} +8 -7
  62. package/build/makeRunObjectFields.d.cts.map +1 -0
  63. package/build/makeRunObjectFields.d.mts +15 -0
  64. package/build/makeRunObjectFields.d.mts.map +1 -0
  65. package/build/makeRunObjectFields.js +19 -19
  66. package/build/makeRunObjectFields.js.map +1 -1
  67. package/build/makeRunQuery.cjs +20 -0
  68. package/build/makeRunQuery.cjs.map +1 -0
  69. package/build/{makeRunQuery.d.ts → makeRunQuery.d.cts} +7 -4
  70. package/build/makeRunQuery.d.cts.map +1 -0
  71. package/build/makeRunQuery.d.mts +20 -0
  72. package/build/makeRunQuery.d.mts.map +1 -0
  73. package/build/makeRunQuery.js +15 -14
  74. package/build/makeRunQuery.js.map +1 -1
  75. package/build/paginationUtils.cjs +8 -0
  76. package/build/paginationUtils.cjs.map +1 -0
  77. package/build/paginationUtils.d.cts +10 -0
  78. package/build/paginationUtils.d.cts.map +1 -0
  79. package/build/paginationUtils.d.mts +10 -0
  80. package/build/paginationUtils.d.mts.map +1 -0
  81. package/build/paginationUtils.js +6 -4
  82. package/build/paginationUtils.js.map +1 -1
  83. package/build/saveEntity.cjs +24 -0
  84. package/build/saveEntity.cjs.map +1 -0
  85. package/build/saveEntity.d.cts +15 -0
  86. package/build/saveEntity.d.cts.map +1 -0
  87. package/build/saveEntity.d.mts +15 -0
  88. package/build/saveEntity.d.mts.map +1 -0
  89. package/build/saveEntity.js +15 -13
  90. package/build/saveEntity.js.map +1 -1
  91. package/build/tests.cjs +9 -0
  92. package/build/tests.d.cts +6 -0
  93. package/build/tests.d.mts +6 -0
  94. package/build/tests.js +5 -27
  95. package/build/typeUtils.cjs +2 -0
  96. package/build/typeUtils.d.cts +11 -0
  97. package/build/typeUtils.d.cts.map +1 -0
  98. package/build/typeUtils.d.mts +11 -0
  99. package/build/typeUtils.d.mts.map +1 -0
  100. package/build/typeUtils.js +2 -3
  101. package/package.json +57 -10
  102. package/build/context.d.ts +0 -3
  103. package/build/context.js.map +0 -1
  104. package/build/cursorPagination.d.ts +0 -32
  105. package/build/entityResolver.d.ts +0 -24
  106. package/build/hint.d.ts +0 -8
  107. package/build/index.d.ts +0 -5
  108. package/build/index.js.map +0 -1
  109. package/build/limitPagination.d.ts +0 -27
  110. package/build/paginationUtils.d.ts +0 -6
  111. package/build/saveEntity.d.ts +0 -11
  112. package/build/tests.d.ts +0 -5
  113. package/build/tests.js.map +0 -1
  114. package/build/typeUtils.d.ts +0 -9
  115. package/build/typeUtils.js.map +0 -1
@@ -0,0 +1,2 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ require("graphql/type");
@@ -0,0 +1,7 @@
1
+ import { GraphQLResolveInfo } from "graphql/type";
2
+ //#region src/context.d.ts
3
+ type MaybePromise<T> = T | Promise<T>;
4
+ type Resolver<R, A, T> = (root: R, args: A, ctx: any, info: GraphQLResolveInfo) => MaybePromise<T>;
5
+ //#endregion
6
+ export { MaybePromise, Resolver };
7
+ //# sourceMappingURL=context.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.cts","names":[],"sources":["../src/context.ts"],"mappings":";;KAEY,aAAa,KAAK,IAAI,QAAQ;KAG9B,SAAS,GAAG,GAAG,MAAM,MAAM,GAAG,MAAM,GAAG,UAAU,MAAM,uBAAuB,aAAa"}
@@ -0,0 +1,7 @@
1
+ import { GraphQLResolveInfo } from "graphql/type";
2
+ //#region src/context.d.ts
3
+ type MaybePromise<T> = T | Promise<T>;
4
+ type Resolver<R, A, T> = (root: R, args: A, ctx: any, info: GraphQLResolveInfo) => MaybePromise<T>;
5
+ //#endregion
6
+ export { MaybePromise, Resolver };
7
+ //# sourceMappingURL=context.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.mts","names":[],"sources":["../src/context.ts"],"mappings":";;KAEY,aAAa,KAAK,IAAI,QAAQ;KAG9B,SAAS,GAAG,GAAG,MAAM,MAAM,GAAG,MAAM,GAAG,UAAU,MAAM,uBAAuB,aAAa"}
package/build/context.js CHANGED
@@ -1,3 +1,2 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- //# sourceMappingURL=context.js.map
1
+ import "graphql/type";
2
+ export {};
@@ -0,0 +1,85 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ require("./paginationUtils.cjs");
3
+ require("joist-core");
4
+ //#region src/cursorPagination.ts
5
+ /** Returns a cursor connection shape for a generated query resolver. */
6
+ async function paginateCursor(ctx, type, args) {
7
+ const limit = args.first ?? args.last ?? 100;
8
+ const baseFilter = args.filter ?? {};
9
+ const filter = withCursorFilter(baseFilter, args);
10
+ const orderBy = { id: args.last ? "DESC" : "ASC" };
11
+ const nodes = await ctx.em.findGql(type, filter, {
12
+ limit,
13
+ orderBy
14
+ });
15
+ const orderedNodes = args.last ? [...nodes].reverse() : nodes;
16
+ const edges = orderedNodes.map((node) => ({
17
+ node,
18
+ cursor: encodeCursor(String(node.id))
19
+ }));
20
+ return {
21
+ edges,
22
+ nodes: orderedNodes,
23
+ pageInfo: new CursorPageInfo(ctx, type, baseFilter, edges)
24
+ };
25
+ }
26
+ /** Lazily computes cursor page fields. */
27
+ var CursorPageInfo = class {
28
+ #ctx;
29
+ #edges;
30
+ #filter;
31
+ #hasNextPagePromise;
32
+ #hasPreviousPagePromise;
33
+ #totalCountPromise;
34
+ #type;
35
+ constructor(ctx, type, filter, edges) {
36
+ this.#ctx = ctx;
37
+ this.#type = type;
38
+ this.#filter = filter;
39
+ this.#edges = edges;
40
+ }
41
+ get startCursor() {
42
+ return this.#edges[0]?.cursor;
43
+ }
44
+ get endCursor() {
45
+ return this.#edges[this.#edges.length - 1]?.cursor;
46
+ }
47
+ get hasNextPage() {
48
+ return this.#hasNextPagePromise ??= this.#countPastCursor("after", this.endCursor);
49
+ }
50
+ get hasPreviousPage() {
51
+ return this.#hasPreviousPagePromise ??= this.#countPastCursor("before", this.startCursor);
52
+ }
53
+ get totalCount() {
54
+ return this.#totalCountPromise ??= this.#ctx.em.findCount(this.#type, this.#filter);
55
+ }
56
+ /** Counts rows past a cursor only when the field is requested. */
57
+ async #countPastCursor(direction, cursor) {
58
+ if (!cursor) return false;
59
+ const filter = withCursorFilter(this.#filter, { [direction]: cursor });
60
+ return await this.#ctx.em.findCount(this.#type, filter) > 0;
61
+ }
62
+ };
63
+ /** Adds cursor bounds to a filter. */
64
+ function withCursorFilter(filter, args) {
65
+ const cursor = args.after ?? args.before;
66
+ if (!cursor) return filter;
67
+ const op = args.after ? "gt" : "lt";
68
+ return {
69
+ ...filter,
70
+ id: { [op]: decodeCursor(cursor) }
71
+ };
72
+ }
73
+ /** Encodes an entity id as an opaque cursor. */
74
+ function encodeCursor(id) {
75
+ return Buffer.from(id).toString("base64");
76
+ }
77
+ /** Decodes an opaque cursor back into an entity id. */
78
+ function decodeCursor(cursor) {
79
+ return Buffer.from(cursor, "base64").toString("utf8");
80
+ }
81
+ //#endregion
82
+ exports.CursorPageInfo = CursorPageInfo;
83
+ exports.paginateCursor = paginateCursor;
84
+
85
+ //# sourceMappingURL=cursorPagination.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cursorPagination.cjs","names":["#ctx","#type","#filter","#edges","#hasNextPagePromise","#countPastCursor","#hasPreviousPagePromise","#totalCountPromise"],"sources":["../src/cursorPagination.ts"],"sourcesContent":["import {\n type Entity,\n type FindGqlFilterOptions,\n type MaybeAbstractEntityConstructor,\n type ValueGraphQLFilter,\n} from \"joist-core\";\n\nimport { type ContextWithEm, type PaginationFilter, defaultLimit } from \"./paginationUtils.ts\";\n\ntype CursorArgs<T extends Entity, F extends object = PaginationFilter<T>> = {\n filter?: F | null;\n first?: number | null;\n after?: string | null;\n last?: number | null;\n before?: string | null;\n};\n\n/** Returns a cursor connection shape for a generated query resolver. */\nexport async function paginateCursor<T extends Entity, F extends object = PaginationFilter<T>>(\n ctx: ContextWithEm,\n type: MaybeAbstractEntityConstructor<T>,\n args: CursorArgs<T, F>,\n): Promise<{ edges: { node: T; cursor: string }[]; nodes: T[]; pageInfo: CursorPageInfo<T> }> {\n const limit = args.first ?? args.last ?? defaultLimit;\n const baseFilter = (args.filter ?? {}) as PaginationFilter<T>;\n const filter = withCursorFilter(baseFilter, args);\n const orderBy = { id: args.last ? \"DESC\" : \"ASC\" } as FindGqlFilterOptions<T>[\"orderBy\"];\n const nodes = await ctx.em.findGql(type, filter, { limit, orderBy });\n const orderedNodes = args.last ? [...nodes].reverse() : nodes;\n const edges = orderedNodes.map((node) => ({ node, cursor: encodeCursor(String(node.id)) }));\n return {\n edges,\n nodes: orderedNodes,\n pageInfo: new CursorPageInfo(ctx, type, baseFilter, edges),\n };\n}\n\n/** Lazily computes cursor page fields. */\nexport class CursorPageInfo<T extends Entity = Entity> {\n #ctx: ContextWithEm;\n #edges: { node: T; cursor: string }[];\n #filter: PaginationFilter<T>;\n #hasNextPagePromise: Promise<boolean> | undefined;\n #hasPreviousPagePromise: Promise<boolean> | undefined;\n #totalCountPromise: Promise<number> | undefined;\n #type: MaybeAbstractEntityConstructor<T>;\n\n constructor(\n ctx: ContextWithEm,\n type: MaybeAbstractEntityConstructor<T>,\n filter: PaginationFilter<T>,\n edges: { node: T; cursor: string }[],\n ) {\n this.#ctx = ctx;\n this.#type = type;\n this.#filter = filter;\n this.#edges = edges;\n }\n\n get startCursor(): string | undefined {\n return this.#edges[0]?.cursor;\n }\n\n get endCursor(): string | undefined {\n return this.#edges[this.#edges.length - 1]?.cursor;\n }\n\n get hasNextPage(): Promise<boolean> {\n return (this.#hasNextPagePromise ??= this.#countPastCursor(\"after\", this.endCursor));\n }\n\n get hasPreviousPage(): Promise<boolean> {\n return (this.#hasPreviousPagePromise ??= this.#countPastCursor(\"before\", this.startCursor));\n }\n\n get totalCount(): Promise<number> {\n return (this.#totalCountPromise ??= this.#ctx.em.findCount(this.#type, this.#filter));\n }\n\n /** Counts rows past a cursor only when the field is requested. */\n async #countPastCursor(direction: \"after\" | \"before\", cursor: string | undefined): Promise<boolean> {\n if (!cursor) return false;\n const filter = withCursorFilter(this.#filter, { [direction]: cursor });\n return (await this.#ctx.em.findCount(this.#type, filter)) > 0;\n }\n}\n\n/** Adds cursor bounds to a filter. */\nfunction withCursorFilter<T extends Entity>(\n filter: PaginationFilter<T>,\n args: { after?: string | null; before?: string | null },\n): PaginationFilter<T> {\n const cursor = args.after ?? args.before;\n if (!cursor) return filter;\n const op = args.after ? \"gt\" : \"lt\";\n return { ...filter, id: { [op]: decodeCursor(cursor) } as ValueGraphQLFilter<string> } as PaginationFilter<T>;\n}\n\n/** Encodes an entity id as an opaque cursor. */\nfunction encodeCursor(id: string): string {\n return Buffer.from(id).toString(\"base64\");\n}\n\n/** Decodes an opaque cursor back into an entity id. */\nfunction decodeCursor(cursor: string): string {\n return Buffer.from(cursor, \"base64\").toString(\"utf8\");\n}\n"],"mappings":";;;;;AAkBA,eAAsB,eACpB,KACA,MACA,MAC4F;CAC5F,MAAM,QAAQ,KAAK,SAAS,KAAK,QAAA;CACjC,MAAM,aAAc,KAAK,UAAU,CAAC;CACpC,MAAM,SAAS,iBAAiB,YAAY,IAAI;CAChD,MAAM,UAAU,EAAE,IAAI,KAAK,OAAO,SAAS,MAAM;CACjD,MAAM,QAAQ,MAAM,IAAI,GAAG,QAAQ,MAAM,QAAQ;EAAE;EAAO;CAAQ,CAAC;CACnE,MAAM,eAAe,KAAK,OAAO,CAAC,GAAG,KAAK,CAAC,CAAC,QAAQ,IAAI;CACxD,MAAM,QAAQ,aAAa,KAAK,UAAU;EAAE;EAAM,QAAQ,aAAa,OAAO,KAAK,EAAE,CAAC;CAAE,EAAE;CAC1F,OAAO;EACL;EACA,OAAO;EACP,UAAU,IAAI,eAAe,KAAK,MAAM,YAAY,KAAK;CAC3D;AACF;;AAGA,IAAa,iBAAb,MAAuD;CACrD;CACA;CACA;CACA;CACA;CACA;CACA;CAEA,YACE,KACA,MACA,QACA,OACA;EACA,KAAKA,OAAO;EACZ,KAAKC,QAAQ;EACb,KAAKC,UAAU;EACf,KAAKC,SAAS;CAChB;CAEA,IAAI,cAAkC;EACpC,OAAO,KAAKA,OAAO,EAAE,EAAE;CACzB;CAEA,IAAI,YAAgC;EAClC,OAAO,KAAKA,OAAO,KAAKA,OAAO,SAAS,EAAE,EAAE;CAC9C;CAEA,IAAI,cAAgC;EAClC,OAAQ,KAAKC,wBAAwB,KAAKC,iBAAiB,SAAS,KAAK,SAAS;CACpF;CAEA,IAAI,kBAAoC;EACtC,OAAQ,KAAKC,4BAA4B,KAAKD,iBAAiB,UAAU,KAAK,WAAW;CAC3F;CAEA,IAAI,aAA8B;EAChC,OAAQ,KAAKE,uBAAuB,KAAKP,KAAK,GAAG,UAAU,KAAKC,OAAO,KAAKC,OAAO;CACrF;;CAGA,MAAMG,iBAAiB,WAA+B,QAA8C;EAClG,IAAI,CAAC,QAAQ,OAAO;EACpB,MAAM,SAAS,iBAAiB,KAAKH,SAAS,GAAG,YAAY,OAAO,CAAC;EACrE,OAAQ,MAAM,KAAKF,KAAK,GAAG,UAAU,KAAKC,OAAO,MAAM,IAAK;CAC9D;AACF;;AAGA,SAAS,iBACP,QACA,MACqB;CACrB,MAAM,SAAS,KAAK,SAAS,KAAK;CAClC,IAAI,CAAC,QAAQ,OAAO;CACpB,MAAM,KAAK,KAAK,QAAQ,OAAO;CAC/B,OAAO;EAAE,GAAG;EAAQ,IAAI,GAAG,KAAK,aAAa,MAAM,EAAE;CAAgC;AACvF;;AAGA,SAAS,aAAa,IAAoB;CACxC,OAAO,OAAO,KAAK,EAAE,CAAC,CAAC,SAAS,QAAQ;AAC1C;;AAGA,SAAS,aAAa,QAAwB;CAC5C,OAAO,OAAO,KAAK,QAAQ,QAAQ,CAAC,CAAC,SAAS,MAAM;AACtD"}
@@ -0,0 +1,35 @@
1
+ import { ContextWithEm, PaginationFilter } from "./paginationUtils.cjs";
2
+ import { Entity, MaybeAbstractEntityConstructor } from "joist-core";
3
+ //#region src/cursorPagination.d.ts
4
+ type CursorArgs<T extends Entity, F extends object = PaginationFilter<T>> = {
5
+ filter?: F | null;
6
+ first?: number | null;
7
+ after?: string | null;
8
+ last?: number | null;
9
+ before?: string | null;
10
+ };
11
+ /** Returns a cursor connection shape for a generated query resolver. */
12
+ declare function paginateCursor<T extends Entity, F extends object = PaginationFilter<T>>(ctx: ContextWithEm, type: MaybeAbstractEntityConstructor<T>, args: CursorArgs<T, F>): Promise<{
13
+ edges: {
14
+ node: T;
15
+ cursor: string;
16
+ }[];
17
+ nodes: T[];
18
+ pageInfo: CursorPageInfo<T>;
19
+ }>;
20
+ /** Lazily computes cursor page fields. */
21
+ declare class CursorPageInfo<T extends Entity = Entity> {
22
+ #private;
23
+ constructor(ctx: ContextWithEm, type: MaybeAbstractEntityConstructor<T>, filter: PaginationFilter<T>, edges: {
24
+ node: T;
25
+ cursor: string;
26
+ }[]);
27
+ get startCursor(): string | undefined;
28
+ get endCursor(): string | undefined;
29
+ get hasNextPage(): Promise<boolean>;
30
+ get hasPreviousPage(): Promise<boolean>;
31
+ get totalCount(): Promise<number>;
32
+ }
33
+ //#endregion
34
+ export { CursorPageInfo, paginateCursor };
35
+ //# sourceMappingURL=cursorPagination.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cursorPagination.d.cts","names":[],"sources":["../src/cursorPagination.ts"],"mappings":";;;KASK,WAAW,UAAU,QAAQ,mBAAmB,iBAAiB;EACpE,SAAS;EACT;EACA;EACA;EACA;;;iBAIoB,eAAe,UAAU,QAAQ,mBAAmB,iBAAiB,IACzF,KAAK,eACL,MAAM,+BAA+B,IACrC,MAAM,WAAW,GAAG,KACnB;EAAU;IAAS,MAAM;IAAG;;EAAoB,OAAO;EAAK,UAAU,eAAe;;;cAgB3E,eAAe,UAAU,SAAS;;EAS7C,YACE,KAAK,eACL,MAAM,+BAA+B,IACrC,QAAQ,iBAAiB,IACzB;IAAS,MAAM;IAAG;;MAQhB;MAIA;MAIA,eAAe;MAIf,mBAAmB;MAInB,cAAc"}
@@ -0,0 +1,35 @@
1
+ import { ContextWithEm, PaginationFilter } from "./paginationUtils.mjs";
2
+ import { Entity, MaybeAbstractEntityConstructor } from "joist-core";
3
+ //#region src/cursorPagination.d.ts
4
+ type CursorArgs<T extends Entity, F extends object = PaginationFilter<T>> = {
5
+ filter?: F | null;
6
+ first?: number | null;
7
+ after?: string | null;
8
+ last?: number | null;
9
+ before?: string | null;
10
+ };
11
+ /** Returns a cursor connection shape for a generated query resolver. */
12
+ declare function paginateCursor<T extends Entity, F extends object = PaginationFilter<T>>(ctx: ContextWithEm, type: MaybeAbstractEntityConstructor<T>, args: CursorArgs<T, F>): Promise<{
13
+ edges: {
14
+ node: T;
15
+ cursor: string;
16
+ }[];
17
+ nodes: T[];
18
+ pageInfo: CursorPageInfo<T>;
19
+ }>;
20
+ /** Lazily computes cursor page fields. */
21
+ declare class CursorPageInfo<T extends Entity = Entity> {
22
+ #private;
23
+ constructor(ctx: ContextWithEm, type: MaybeAbstractEntityConstructor<T>, filter: PaginationFilter<T>, edges: {
24
+ node: T;
25
+ cursor: string;
26
+ }[]);
27
+ get startCursor(): string | undefined;
28
+ get endCursor(): string | undefined;
29
+ get hasNextPage(): Promise<boolean>;
30
+ get hasPreviousPage(): Promise<boolean>;
31
+ get totalCount(): Promise<number>;
32
+ }
33
+ //#endregion
34
+ export { CursorPageInfo, paginateCursor };
35
+ //# sourceMappingURL=cursorPagination.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cursorPagination.d.mts","names":[],"sources":["../src/cursorPagination.ts"],"mappings":";;;KASK,WAAW,UAAU,QAAQ,mBAAmB,iBAAiB;EACpE,SAAS;EACT;EACA;EACA;EACA;;;iBAIoB,eAAe,UAAU,QAAQ,mBAAmB,iBAAiB,IACzF,KAAK,eACL,MAAM,+BAA+B,IACrC,MAAM,WAAW,GAAG,KACnB;EAAU;IAAS,MAAM;IAAG;;EAAoB,OAAO;EAAK,UAAU,eAAe;;;cAgB3E,eAAe,UAAU,SAAS;;EAS7C,YACE,KAAK,eACL,MAAM,+BAA+B,IACrC,QAAQ,iBAAiB,IACzB;IAAS,MAAM;IAAG;;MAQhB;MAIA;MAIA,eAAe;MAIf,mBAAmB;MAInB,cAAc"}
@@ -1,76 +1,83 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.CursorPageInfo = void 0;
4
- exports.paginateCursor = paginateCursor;
5
- const paginationUtils_1 = require("./paginationUtils");
1
+ import "./paginationUtils.js";
2
+ import "joist-core";
3
+ //#region src/cursorPagination.ts
6
4
  /** Returns a cursor connection shape for a generated query resolver. */
7
5
  async function paginateCursor(ctx, type, args) {
8
- const limit = args.first ?? args.last ?? paginationUtils_1.defaultLimit;
9
- const baseFilter = (args.filter ?? {});
10
- const filter = withCursorFilter(baseFilter, args);
11
- const orderBy = { id: args.last ? "DESC" : "ASC" };
12
- const nodes = await ctx.em.findGql(type, filter, { limit, orderBy });
13
- const orderedNodes = args.last ? [...nodes].reverse() : nodes;
14
- const edges = orderedNodes.map((node) => ({ node, cursor: encodeCursor(String(node.id)) }));
15
- return {
16
- edges,
17
- nodes: orderedNodes,
18
- pageInfo: new CursorPageInfo(ctx, type, baseFilter, edges),
19
- };
6
+ const limit = args.first ?? args.last ?? 100;
7
+ const baseFilter = args.filter ?? {};
8
+ const filter = withCursorFilter(baseFilter, args);
9
+ const orderBy = { id: args.last ? "DESC" : "ASC" };
10
+ const nodes = await ctx.em.findGql(type, filter, {
11
+ limit,
12
+ orderBy
13
+ });
14
+ const orderedNodes = args.last ? [...nodes].reverse() : nodes;
15
+ const edges = orderedNodes.map((node) => ({
16
+ node,
17
+ cursor: encodeCursor(String(node.id))
18
+ }));
19
+ return {
20
+ edges,
21
+ nodes: orderedNodes,
22
+ pageInfo: new CursorPageInfo(ctx, type, baseFilter, edges)
23
+ };
20
24
  }
21
25
  /** Lazily computes cursor page fields. */
22
- class CursorPageInfo {
23
- #ctx;
24
- #edges;
25
- #filter;
26
- #hasNextPagePromise;
27
- #hasPreviousPagePromise;
28
- #totalCountPromise;
29
- #type;
30
- constructor(ctx, type, filter, edges) {
31
- this.#ctx = ctx;
32
- this.#type = type;
33
- this.#filter = filter;
34
- this.#edges = edges;
35
- }
36
- get startCursor() {
37
- return this.#edges[0]?.cursor;
38
- }
39
- get endCursor() {
40
- return this.#edges[this.#edges.length - 1]?.cursor;
41
- }
42
- get hasNextPage() {
43
- return (this.#hasNextPagePromise ??= this.#countPastCursor("after", this.endCursor));
44
- }
45
- get hasPreviousPage() {
46
- return (this.#hasPreviousPagePromise ??= this.#countPastCursor("before", this.startCursor));
47
- }
48
- get totalCount() {
49
- return (this.#totalCountPromise ??= this.#ctx.em.findCount(this.#type, this.#filter));
50
- }
51
- /** Counts rows past a cursor only when the field is requested. */
52
- async #countPastCursor(direction, cursor) {
53
- if (!cursor)
54
- return false;
55
- const filter = withCursorFilter(this.#filter, { [direction]: cursor });
56
- return (await this.#ctx.em.findCount(this.#type, filter)) > 0;
57
- }
58
- }
59
- exports.CursorPageInfo = CursorPageInfo;
26
+ var CursorPageInfo = class {
27
+ #ctx;
28
+ #edges;
29
+ #filter;
30
+ #hasNextPagePromise;
31
+ #hasPreviousPagePromise;
32
+ #totalCountPromise;
33
+ #type;
34
+ constructor(ctx, type, filter, edges) {
35
+ this.#ctx = ctx;
36
+ this.#type = type;
37
+ this.#filter = filter;
38
+ this.#edges = edges;
39
+ }
40
+ get startCursor() {
41
+ return this.#edges[0]?.cursor;
42
+ }
43
+ get endCursor() {
44
+ return this.#edges[this.#edges.length - 1]?.cursor;
45
+ }
46
+ get hasNextPage() {
47
+ return this.#hasNextPagePromise ??= this.#countPastCursor("after", this.endCursor);
48
+ }
49
+ get hasPreviousPage() {
50
+ return this.#hasPreviousPagePromise ??= this.#countPastCursor("before", this.startCursor);
51
+ }
52
+ get totalCount() {
53
+ return this.#totalCountPromise ??= this.#ctx.em.findCount(this.#type, this.#filter);
54
+ }
55
+ /** Counts rows past a cursor only when the field is requested. */
56
+ async #countPastCursor(direction, cursor) {
57
+ if (!cursor) return false;
58
+ const filter = withCursorFilter(this.#filter, { [direction]: cursor });
59
+ return await this.#ctx.em.findCount(this.#type, filter) > 0;
60
+ }
61
+ };
60
62
  /** Adds cursor bounds to a filter. */
61
63
  function withCursorFilter(filter, args) {
62
- const cursor = args.after ?? args.before;
63
- if (!cursor)
64
- return filter;
65
- const op = args.after ? "gt" : "lt";
66
- return { ...filter, id: { [op]: decodeCursor(cursor) } };
64
+ const cursor = args.after ?? args.before;
65
+ if (!cursor) return filter;
66
+ const op = args.after ? "gt" : "lt";
67
+ return {
68
+ ...filter,
69
+ id: { [op]: decodeCursor(cursor) }
70
+ };
67
71
  }
68
72
  /** Encodes an entity id as an opaque cursor. */
69
73
  function encodeCursor(id) {
70
- return Buffer.from(id).toString("base64");
74
+ return Buffer.from(id).toString("base64");
71
75
  }
72
76
  /** Decodes an opaque cursor back into an entity id. */
73
77
  function decodeCursor(cursor) {
74
- return Buffer.from(cursor, "base64").toString("utf8");
78
+ return Buffer.from(cursor, "base64").toString("utf8");
75
79
  }
80
+ //#endregion
81
+ export { CursorPageInfo, paginateCursor };
82
+
76
83
  //# sourceMappingURL=cursorPagination.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"cursorPagination.js","sourceRoot":"","sources":["../src/cursorPagination.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cursorPagination.js","names":["#ctx","#type","#filter","#edges","#hasNextPagePromise","#countPastCursor","#hasPreviousPagePromise","#totalCountPromise"],"sources":["../src/cursorPagination.ts"],"sourcesContent":["import {\n type Entity,\n type FindGqlFilterOptions,\n type MaybeAbstractEntityConstructor,\n type ValueGraphQLFilter,\n} from \"joist-core\";\n\nimport { type ContextWithEm, type PaginationFilter, defaultLimit } from \"./paginationUtils.ts\";\n\ntype CursorArgs<T extends Entity, F extends object = PaginationFilter<T>> = {\n filter?: F | null;\n first?: number | null;\n after?: string | null;\n last?: number | null;\n before?: string | null;\n};\n\n/** Returns a cursor connection shape for a generated query resolver. */\nexport async function paginateCursor<T extends Entity, F extends object = PaginationFilter<T>>(\n ctx: ContextWithEm,\n type: MaybeAbstractEntityConstructor<T>,\n args: CursorArgs<T, F>,\n): Promise<{ edges: { node: T; cursor: string }[]; nodes: T[]; pageInfo: CursorPageInfo<T> }> {\n const limit = args.first ?? args.last ?? defaultLimit;\n const baseFilter = (args.filter ?? {}) as PaginationFilter<T>;\n const filter = withCursorFilter(baseFilter, args);\n const orderBy = { id: args.last ? \"DESC\" : \"ASC\" } as FindGqlFilterOptions<T>[\"orderBy\"];\n const nodes = await ctx.em.findGql(type, filter, { limit, orderBy });\n const orderedNodes = args.last ? [...nodes].reverse() : nodes;\n const edges = orderedNodes.map((node) => ({ node, cursor: encodeCursor(String(node.id)) }));\n return {\n edges,\n nodes: orderedNodes,\n pageInfo: new CursorPageInfo(ctx, type, baseFilter, edges),\n };\n}\n\n/** Lazily computes cursor page fields. */\nexport class CursorPageInfo<T extends Entity = Entity> {\n #ctx: ContextWithEm;\n #edges: { node: T; cursor: string }[];\n #filter: PaginationFilter<T>;\n #hasNextPagePromise: Promise<boolean> | undefined;\n #hasPreviousPagePromise: Promise<boolean> | undefined;\n #totalCountPromise: Promise<number> | undefined;\n #type: MaybeAbstractEntityConstructor<T>;\n\n constructor(\n ctx: ContextWithEm,\n type: MaybeAbstractEntityConstructor<T>,\n filter: PaginationFilter<T>,\n edges: { node: T; cursor: string }[],\n ) {\n this.#ctx = ctx;\n this.#type = type;\n this.#filter = filter;\n this.#edges = edges;\n }\n\n get startCursor(): string | undefined {\n return this.#edges[0]?.cursor;\n }\n\n get endCursor(): string | undefined {\n return this.#edges[this.#edges.length - 1]?.cursor;\n }\n\n get hasNextPage(): Promise<boolean> {\n return (this.#hasNextPagePromise ??= this.#countPastCursor(\"after\", this.endCursor));\n }\n\n get hasPreviousPage(): Promise<boolean> {\n return (this.#hasPreviousPagePromise ??= this.#countPastCursor(\"before\", this.startCursor));\n }\n\n get totalCount(): Promise<number> {\n return (this.#totalCountPromise ??= this.#ctx.em.findCount(this.#type, this.#filter));\n }\n\n /** Counts rows past a cursor only when the field is requested. */\n async #countPastCursor(direction: \"after\" | \"before\", cursor: string | undefined): Promise<boolean> {\n if (!cursor) return false;\n const filter = withCursorFilter(this.#filter, { [direction]: cursor });\n return (await this.#ctx.em.findCount(this.#type, filter)) > 0;\n }\n}\n\n/** Adds cursor bounds to a filter. */\nfunction withCursorFilter<T extends Entity>(\n filter: PaginationFilter<T>,\n args: { after?: string | null; before?: string | null },\n): PaginationFilter<T> {\n const cursor = args.after ?? args.before;\n if (!cursor) return filter;\n const op = args.after ? \"gt\" : \"lt\";\n return { ...filter, id: { [op]: decodeCursor(cursor) } as ValueGraphQLFilter<string> } as PaginationFilter<T>;\n}\n\n/** Encodes an entity id as an opaque cursor. */\nfunction encodeCursor(id: string): string {\n return Buffer.from(id).toString(\"base64\");\n}\n\n/** Decodes an opaque cursor back into an entity id. */\nfunction decodeCursor(cursor: string): string {\n return Buffer.from(cursor, \"base64\").toString(\"utf8\");\n}\n"],"mappings":";;;;AAkBA,eAAsB,eACpB,KACA,MACA,MAC4F;CAC5F,MAAM,QAAQ,KAAK,SAAS,KAAK,QAAA;CACjC,MAAM,aAAc,KAAK,UAAU,CAAC;CACpC,MAAM,SAAS,iBAAiB,YAAY,IAAI;CAChD,MAAM,UAAU,EAAE,IAAI,KAAK,OAAO,SAAS,MAAM;CACjD,MAAM,QAAQ,MAAM,IAAI,GAAG,QAAQ,MAAM,QAAQ;EAAE;EAAO;CAAQ,CAAC;CACnE,MAAM,eAAe,KAAK,OAAO,CAAC,GAAG,KAAK,CAAC,CAAC,QAAQ,IAAI;CACxD,MAAM,QAAQ,aAAa,KAAK,UAAU;EAAE;EAAM,QAAQ,aAAa,OAAO,KAAK,EAAE,CAAC;CAAE,EAAE;CAC1F,OAAO;EACL;EACA,OAAO;EACP,UAAU,IAAI,eAAe,KAAK,MAAM,YAAY,KAAK;CAC3D;AACF;;AAGA,IAAa,iBAAb,MAAuD;CACrD;CACA;CACA;CACA;CACA;CACA;CACA;CAEA,YACE,KACA,MACA,QACA,OACA;EACA,KAAKA,OAAO;EACZ,KAAKC,QAAQ;EACb,KAAKC,UAAU;EACf,KAAKC,SAAS;CAChB;CAEA,IAAI,cAAkC;EACpC,OAAO,KAAKA,OAAO,EAAE,EAAE;CACzB;CAEA,IAAI,YAAgC;EAClC,OAAO,KAAKA,OAAO,KAAKA,OAAO,SAAS,EAAE,EAAE;CAC9C;CAEA,IAAI,cAAgC;EAClC,OAAQ,KAAKC,wBAAwB,KAAKC,iBAAiB,SAAS,KAAK,SAAS;CACpF;CAEA,IAAI,kBAAoC;EACtC,OAAQ,KAAKC,4BAA4B,KAAKD,iBAAiB,UAAU,KAAK,WAAW;CAC3F;CAEA,IAAI,aAA8B;EAChC,OAAQ,KAAKE,uBAAuB,KAAKP,KAAK,GAAG,UAAU,KAAKC,OAAO,KAAKC,OAAO;CACrF;;CAGA,MAAMG,iBAAiB,WAA+B,QAA8C;EAClG,IAAI,CAAC,QAAQ,OAAO;EACpB,MAAM,SAAS,iBAAiB,KAAKH,SAAS,GAAG,YAAY,OAAO,CAAC;EACrE,OAAQ,MAAM,KAAKF,KAAK,GAAG,UAAU,KAAKC,OAAO,MAAM,IAAK;CAC9D;AACF;;AAGA,SAAS,iBACP,QACA,MACqB;CACrB,MAAM,SAAS,KAAK,SAAS,KAAK;CAClC,IAAI,CAAC,QAAQ,OAAO;CACpB,MAAM,KAAK,KAAK,QAAQ,OAAO;CAC/B,OAAO;EAAE,GAAG;EAAQ,IAAI,GAAG,KAAK,aAAa,MAAM,EAAE;CAAgC;AACvF;;AAGA,SAAS,aAAa,IAAoB;CACxC,OAAO,OAAO,KAAK,EAAE,CAAC,CAAC,SAAS,QAAQ;AAC1C;;AAGA,SAAS,aAAa,QAAwB;CAC5C,OAAO,OAAO,KAAK,QAAQ,QAAQ,CAAC,CAAC,SAAS,MAAM;AACtD"}
@@ -0,0 +1,95 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ require("./context.cjs");
3
+ const require_hint = require("./hint.cjs");
4
+ require("graphql/type");
5
+ let joist_core = require("joist-core");
6
+ //#region src/entityResolver.ts
7
+ /**
8
+ * Creates field resolvers for each of the fields on our entity.
9
+ */
10
+ function entityResolver(entity, aliases) {
11
+ const entityMetadata = (0, joist_core.getMetadata)(entity);
12
+ const idResolver = (entityOrId) => {
13
+ return typeof entityOrId === "string" ? entityOrId : entityOrId.id;
14
+ };
15
+ const primitiveResolvers = Object.values(entityMetadata.allFields).filter((ormField) => !isPrimaryKeyField(ormField) && !isReferenceField(ormField) && !isCollectionField(ormField)).map((ormField) => {
16
+ if ("derived" in ormField && ormField.derived === "async") return [ormField.fieldName, (entity) => entity[ormField.fieldName].get];
17
+ else if ("lazy" in ormField && ormField.lazy) return [ormField.fieldName, (entity) => {
18
+ const lazyField = entity[ormField.fieldName];
19
+ return (0, joist_core.isLoadedLazyField)(lazyField) ? lazyField.get : lazyField.load();
20
+ }];
21
+ else return [ormField.fieldName, (entity) => entity[ormField.fieldName]];
22
+ });
23
+ const referenceResolvers = Object.values(entityMetadata.allFields).filter((ormField) => isReferenceField(ormField)).map((ormField) => [ormField.fieldName, (entity, args, ctx, info) => {
24
+ if ((ormField.kind === "m2o" || ormField.kind === "poly") && info?.fieldNodes.length === 1) {
25
+ const selectionSet = info.fieldNodes[0].selectionSet;
26
+ const hasAbstractType = ormField.kind === "m2o" && ormField.otherMetadata().inheritanceType !== void 0 || ormField.kind === "poly" && ormField.components.some((c) => c.otherMetadata().inheritanceType !== void 0);
27
+ if (selectionSet && !hasAbstractType) {
28
+ if (selectionSet.selections.length === 1 && selectionSet.selections[0].kind === "Field" && selectionSet.selections[0].name.value === "id") return entity[ormField.fieldName].idMaybe;
29
+ }
30
+ }
31
+ const reference = entity[ormField.fieldName];
32
+ if ((0, joist_core.isLoadedReference)(reference)) return reference.get;
33
+ if (info && ((0, joist_core.isManyToOneField)(ormField) || (0, joist_core.isOneToManyField)(ormField))) {
34
+ const loadHint = require_hint.convertInfoToLoadHint(ormField.otherMetadata(), info);
35
+ if (loadHint) {
36
+ const parentHint = { [ormField.fieldName]: loadHint };
37
+ return entity.em.populate(entity, parentHint).then(() => reference.get);
38
+ }
39
+ }
40
+ return reference.load();
41
+ }]);
42
+ const collectionResolvers = Object.values(entityMetadata.allFields).filter((ormField) => isCollectionField(ormField)).map((ormField) => [ormField.fieldName, (entity, args, ctx, info) => {
43
+ const field = ormField;
44
+ const collection = entity[field.fieldName];
45
+ if ((0, joist_core.isLoadedCollection)(collection)) return collection.get;
46
+ if (info) {
47
+ const loadHint = require_hint.convertInfoToLoadHint(field.otherMetadata(), info);
48
+ if (loadHint) {
49
+ const parentHint = { [field.fieldName]: loadHint };
50
+ return entity.em.populate(entity, parentHint).then(() => collection.get);
51
+ }
52
+ }
53
+ return collection.load();
54
+ }]);
55
+ const ormResolvers = Object.fromEntries([
56
+ ...primitiveResolvers,
57
+ ...referenceResolvers,
58
+ ...collectionResolvers
59
+ ]);
60
+ const alreadyFoundKeys = Object.keys(ormResolvers);
61
+ const customResolvers = Object.keys((0, joist_core.getProperties)(entityMetadata)).filter((n) => !alreadyFoundKeys.includes(n)).map((key) => [key, (entity) => {
62
+ const property = entity[key];
63
+ if (typeof property === "function") return property.apply(entity);
64
+ else if ((0, joist_core.isReactiveGetter)(property)) return property.get;
65
+ else if ((0, joist_core.isReference)(property) || (0, joist_core.isCollection)(property) || (0, joist_core.isProperty)(property) || (0, joist_core.isAsyncProperty)(property)) {
66
+ if ((0, joist_core.isLoadedReference)(property) || (0, joist_core.isLoadedCollection)(property) || (0, joist_core.isLoadedProperty)(property) || (0, joist_core.isLoadedAsyncProperty)(property)) return property.get;
67
+ return property.load();
68
+ } else if ((0, joist_core.isReadOnlyCollection)(property)) {
69
+ if ((0, joist_core.isLoadedReadOnlyCollection)(property)) return property.get;
70
+ return property.load();
71
+ } else return property;
72
+ }]);
73
+ let resolvers = {
74
+ id: idResolver,
75
+ ...ormResolvers,
76
+ ...Object.fromEntries(customResolvers)
77
+ };
78
+ if (aliases) Object.entries(aliases).forEach(([alias, field]) => {
79
+ resolvers[alias] = resolvers[field];
80
+ });
81
+ return resolvers;
82
+ }
83
+ function isReferenceField(ormField) {
84
+ return ormField.kind === "m2o" || ormField.kind === "o2o" || ormField.kind === "poly";
85
+ }
86
+ function isCollectionField(ormField) {
87
+ return ormField.kind === "o2m" || ormField.kind === "m2m";
88
+ }
89
+ function isPrimaryKeyField(ormField) {
90
+ return ormField.kind === "primaryKey";
91
+ }
92
+ //#endregion
93
+ exports.entityResolver = entityResolver;
94
+
95
+ //# sourceMappingURL=entityResolver.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entityResolver.cjs","names":["getMetadata","isLoadedLazyField","isLoadedReference","isManyToOneField","isOneToManyField","convertInfoToLoadHint","isLoadedCollection","getProperties","isReactiveGetter","isReference","isCollection","isProperty","isAsyncProperty","isLoadedProperty","isLoadedAsyncProperty","isReadOnlyCollection","isLoadedReadOnlyCollection"],"sources":["../src/entityResolver.ts"],"sourcesContent":["import { type GraphQLResolveInfo } from \"graphql/type\";\nimport {\n type Collection,\n type Entity,\n type EntityMetadata,\n type Field,\n type IdOf,\n type LoadHint,\n type ManyToManyField,\n type ManyToOneField,\n type MaybeAbstractEntityConstructor,\n type OneToManyField,\n type OneToOneField,\n type PolymorphicField,\n type PrimaryKeyField,\n type Property,\n type ReactiveGetter,\n type ReadOnlyCollection,\n type Reference,\n getMetadata,\n getProperties,\n isAsyncProperty,\n isCollection,\n isLoadedAsyncProperty,\n isLoadedCollection,\n isLoadedLazyField,\n isLoadedProperty,\n isLoadedReadOnlyCollection,\n isLoadedReference,\n isManyToOneField,\n isOneToManyField,\n isProperty,\n isReactiveGetter,\n isReadOnlyCollection,\n isReference,\n} from \"joist-core\";\n\nimport { type Resolver } from \"./context.ts\";\nimport { convertInfoToLoadHint } from \"./hint.ts\";\n\ntype GraphQLPrimitive = string | Date | boolean | number | bigint | null | undefined;\n\n/**\n * Maps properties like `Entity.firstName: string` to field resolver functions like `firstName(): Promise<string>`.\n *\n * Note that we don't necessarily know/care if `firstName` is in the `Entity` GraphQL type, we just map\n * every field to a potential resolver, and then will let the `EntityResolvers` type-check effectively\n * do the union of \"what's defined on the ORM type vs. what's defined on the GraphQL type\".\n *\n * Note this might require setting GraphQL options like `requireResolversToMatchSchema: ignore` to avoid\n * the GraphQL infra complaining about the extra field resolvers that Joist creates for fields that\n * are not technically mapped in your GraphQL schema.\n */\nexport type EntityResolver<T extends Entity> = {\n [P in keyof T]: P extends \"id\"\n ? Resolver<T, Record<string, any>, IdOf<T>>\n : T[P] extends GraphQLPrimitive | GraphQLPrimitive[]\n ? Resolver<T, Record<string, any>, T[P]>\n : T[P] extends Collection<any, infer U>\n ? Resolver<T, Record<string, any>, U[]>\n : T[P] extends ReadOnlyCollection<any, infer U>\n ? Resolver<T, Record<string, any>, U[]>\n : T[P] extends Reference<any, infer U, infer N>\n ? Resolver<T, Record<string, any>, U | N>\n : T[P] extends Property<any, infer V>\n ? Resolver<T, Record<string, any>, V>\n : T[P] extends ReactiveGetter<any, infer V>\n ? Resolver<T, Record<string, any>, V>\n : T[P] extends Promise<infer V>\n ? Resolver<T, Record<string, any>, V>\n : T[P] extends () => Promise<infer V>\n ? Resolver<T, Record<string, any>, V>\n : T[P] extends () => infer V\n ? Resolver<T, Record<string, any>, V>\n : Resolver<T, Record<string, any>, T[P]>;\n};\n\n/**\n * Creates field resolvers for each of the fields on our entity.\n */\nexport function entityResolver<T extends Entity, A extends Record<string, keyof T> = {}>(\n entity: MaybeAbstractEntityConstructor<T> | EntityMetadata<T>,\n aliases?: A,\n): EntityResolver<T> & { [K in keyof A]: EntityResolver<T>[A[K]] } {\n const entityMetadata = getMetadata(entity as any);\n\n const idResolver = (entityOrId: T | string) => {\n return typeof entityOrId === \"string\" ? entityOrId : entityOrId.id;\n };\n\n const primitiveResolvers = Object.values(entityMetadata.allFields)\n .filter((ormField) => !isPrimaryKeyField(ormField) && !isReferenceField(ormField) && !isCollectionField(ormField))\n .map((ormField) => {\n if (\"derived\" in ormField && ormField.derived === \"async\") {\n return [ormField.fieldName, (entity: T) => (entity as any)[ormField.fieldName].get];\n } else if (\"lazy\" in ormField && ormField.lazy) {\n // `lazy` columns are excluded from the default SELECT and exposed as `LazyField`s, so put the\n // value on the wire by loading it on demand (or reading `.get` if already loaded/populated).\n return [\n ormField.fieldName,\n (entity: T) => {\n const lazyField = (entity as any)[ormField.fieldName];\n return isLoadedLazyField(lazyField) ? lazyField.get : lazyField.load();\n },\n ];\n } else {\n // Currently, we only support primitives, i.e. strings/numbers/etc. and not collections.\n return [ormField.fieldName, (entity: T) => (entity as any)[ormField.fieldName]];\n }\n });\n\n const referenceResolvers: [string, Resolver<any, any, any>][] = Object.values(entityMetadata.allFields)\n .filter((ormField) => isReferenceField(ormField))\n .map((ormField) => [\n ormField.fieldName,\n (entity: T, args, ctx, info: GraphQLResolveInfo | undefined) => {\n // Use the `info` to see if the query is only returning `{ id }` and if so avoid fetching the entity\n if ((ormField.kind === \"m2o\" || ormField.kind === \"poly\") && info?.fieldNodes.length === 1) {\n const selectionSet = info.fieldNodes[0].selectionSet;\n // We can't do this if the expected return type uses inheritance, as graphql wouldn't know how to map to a\n // resolver with just an id since there could be multiple implementations\n const hasAbstractType =\n (ormField.kind === \"m2o\" && ormField.otherMetadata().inheritanceType !== undefined) ||\n (ormField.kind === \"poly\" &&\n ormField.components.some((c) => c.otherMetadata().inheritanceType !== undefined));\n if (selectionSet && !hasAbstractType) {\n if (\n selectionSet.selections.length === 1 &&\n selectionSet.selections[0].kind === \"Field\" &&\n selectionSet.selections[0].name.value === \"id\"\n ) {\n // This is cheating and returning the \"wrong root\", but our `id` field resolvers\n // knows to look for it, to optimize this case and avoiding the unnecessary load\n return (entity as any)[ormField.fieldName].idMaybe;\n }\n }\n }\n const reference = (entity as any)[ormField.fieldName];\n if (isLoadedReference(reference)) {\n return reference.get;\n }\n // We skip polys b/c they can't have load hints anyway\n if (info && (isManyToOneField(ormField) || isOneToManyField(ormField))) {\n // See if we can populate the collection from the GraphQL selection set\n const loadHint = convertInfoToLoadHint(ormField.otherMetadata(), info);\n if (loadHint) {\n const parentHint = { [ormField.fieldName]: loadHint } as LoadHint<T>;\n return entity.em.populate(entity, parentHint).then(() => reference.get);\n }\n }\n return reference.load();\n },\n ]);\n\n const collectionResolvers: [string, Resolver<T, any, any>][] = Object.values(entityMetadata.allFields)\n .filter((ormField) => isCollectionField(ormField))\n .map((ormField) => [\n ormField.fieldName,\n (entity, args, ctx, info: GraphQLResolveInfo | undefined) => {\n const field = ormField as OneToManyField | ManyToManyField;\n const collection = (entity as any)[field.fieldName];\n if (isLoadedCollection(collection)) {\n return collection.get;\n }\n if (info) {\n // See if we can populate the collection from the GraphQL selection set\n const loadHint = convertInfoToLoadHint(field.otherMetadata(), info);\n if (loadHint) {\n const parentHint = { [field.fieldName]: loadHint } as LoadHint<T>;\n return entity.em.populate(entity, parentHint).then(() => collection.get);\n }\n }\n return collection.load();\n },\n ]);\n\n const ormResolvers = Object.fromEntries([...primitiveResolvers, ...referenceResolvers, ...collectionResolvers]);\n\n // Look for non-orm properties on the domain object's prototype\n // and on an instance of the domain object itself to get\n // non-prototype properties like CustomReferences\n const alreadyFoundKeys = Object.keys(ormResolvers);\n const customProperties = Object.keys(getProperties(entityMetadata)).filter((n) => !alreadyFoundKeys.includes(n));\n const customResolvers: [string, Resolver<T, any, any>][] = customProperties.map((key) => [\n key,\n (entity) => {\n const property = (entity as any)[key];\n if (typeof property === \"function\") {\n // Support methods like `async name(): Promise<string>`\n return (property as Function).apply(entity);\n } else if (isReactiveGetter(property)) {\n return property.get;\n } else if (isReference(property) || isCollection(property) || isProperty(property) || isAsyncProperty(property)) {\n if (\n isLoadedReference(property) ||\n isLoadedCollection(property) ||\n isLoadedProperty(property) ||\n isLoadedAsyncProperty(property)\n ) {\n return property.get;\n }\n // ...we need to know the `property.otherMetadata()` return type, which isn't available right now\n // const loadHint = convertInfoToLoadHint(getMetadata(entity), info);\n // if (loadHint) {\n // return entity.em.populate(entity, loadHint).then(() => (property as any).get);\n // }\n return property.load();\n } else if (isReadOnlyCollection(property)) {\n if (isLoadedReadOnlyCollection(property)) {\n return property.get;\n }\n return property.load();\n } else {\n return property;\n }\n },\n ]);\n\n let resolvers = {\n id: idResolver,\n ...ormResolvers,\n ...Object.fromEntries(customResolvers),\n } as any;\n\n if (aliases) {\n Object.entries(aliases).forEach(([alias, field]) => {\n resolvers[alias] = resolvers[field];\n });\n }\n\n return resolvers;\n}\n\nfunction isReferenceField(ormField: Field): ormField is ManyToOneField | OneToOneField | PolymorphicField {\n return ormField.kind === \"m2o\" || ormField.kind === \"o2o\" || ormField.kind === \"poly\";\n}\n\nfunction isCollectionField(ormField: Field): ormField is OneToManyField | ManyToManyField {\n return ormField.kind === \"o2m\" || ormField.kind === \"m2m\";\n}\n\nfunction isPrimaryKeyField(ormField: Field): ormField is PrimaryKeyField {\n return ormField.kind === \"primaryKey\";\n}\n"],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import { Resolver } from "./context.cjs";
2
+ import { Collection, Entity, EntityMetadata, IdOf, MaybeAbstractEntityConstructor, Property, ReactiveGetter, ReadOnlyCollection, Reference } from "joist-core";
3
+ //#region src/entityResolver.d.ts
4
+ type GraphQLPrimitive = string | Date | boolean | number | bigint | null | undefined;
5
+ /**
6
+ * Maps properties like `Entity.firstName: string` to field resolver functions like `firstName(): Promise<string>`.
7
+ *
8
+ * Note that we don't necessarily know/care if `firstName` is in the `Entity` GraphQL type, we just map
9
+ * every field to a potential resolver, and then will let the `EntityResolvers` type-check effectively
10
+ * do the union of "what's defined on the ORM type vs. what's defined on the GraphQL type".
11
+ *
12
+ * Note this might require setting GraphQL options like `requireResolversToMatchSchema: ignore` to avoid
13
+ * the GraphQL infra complaining about the extra field resolvers that Joist creates for fields that
14
+ * are not technically mapped in your GraphQL schema.
15
+ */
16
+ type EntityResolver<T extends Entity> = { [P in keyof T]: P extends "id" ? Resolver<T, Record<string, any>, IdOf<T>> : T[P] extends GraphQLPrimitive | GraphQLPrimitive[] ? Resolver<T, Record<string, any>, T[P]> : T[P] extends Collection<any, infer U> ? Resolver<T, Record<string, any>, U[]> : T[P] extends ReadOnlyCollection<any, infer U> ? Resolver<T, Record<string, any>, U[]> : T[P] extends Reference<any, infer U, infer N> ? Resolver<T, Record<string, any>, U | N> : T[P] extends Property<any, infer V> ? Resolver<T, Record<string, any>, V> : T[P] extends ReactiveGetter<any, infer V> ? Resolver<T, Record<string, any>, V> : T[P] extends Promise<infer V> ? Resolver<T, Record<string, any>, V> : T[P] extends (() => Promise<infer V>) ? Resolver<T, Record<string, any>, V> : T[P] extends (() => infer V) ? Resolver<T, Record<string, any>, V> : Resolver<T, Record<string, any>, T[P]>; };
17
+ /**
18
+ * Creates field resolvers for each of the fields on our entity.
19
+ */
20
+ declare function entityResolver<T extends Entity, A extends Record<string, keyof T> = {}>(entity: MaybeAbstractEntityConstructor<T> | EntityMetadata<T>, aliases?: A): EntityResolver<T> & { [K in keyof A]: EntityResolver<T>[A[K]]; };
21
+ //#endregion
22
+ export { EntityResolver, entityResolver };
23
+ //# sourceMappingURL=entityResolver.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entityResolver.d.cts","names":[],"sources":["../src/entityResolver.ts"],"mappings":";;;KAwCK,4BAA4B;;;;;;;;;;;;KAarB,eAAe,UAAU,aAClC,WAAW,IAAI,iBACZ,SAAS,GAAG,qBAAqB,KAAK,MACtC,EAAE,WAAW,mBAAmB,qBAC9B,SAAS,GAAG,qBAAqB,EAAE,MACnC,EAAE,WAAW,sBAAsB,KACjC,SAAS,GAAG,qBAAqB,OACjC,EAAE,WAAW,8BAA8B,KACzC,SAAS,GAAG,qBAAqB,OACjC,EAAE,WAAW,qBAAqB,SAAS,KACzC,SAAS,GAAG,qBAAqB,IAAI,KACrC,EAAE,WAAW,oBAAoB,KAC/B,SAAS,GAAG,qBAAqB,KACjC,EAAE,WAAW,0BAA0B,KACrC,SAAS,GAAG,qBAAqB,KACjC,EAAE,WAAW,cAAc,KACzB,SAAS,GAAG,qBAAqB,KACjC,EAAE,kBAAiB,cAAc,MAC/B,SAAS,GAAG,qBAAqB,KACjC,EAAE,wBAAuB,KACvB,SAAS,GAAG,qBAAqB,KACjC,SAAS,GAAG,qBAAqB,EAAE;;;;iBAM3C,eAAe,UAAU,QAAQ,UAAU,qBAAqB,SAC9E,QAAQ,+BAA+B,KAAK,eAAe,IAC3D,UAAU,IACT,eAAe,QAAQ,WAAW,IAAI,eAAe,GAAG,EAAE"}
@@ -0,0 +1,23 @@
1
+ import { Resolver } from "./context.mjs";
2
+ import { Collection, Entity, EntityMetadata, IdOf, MaybeAbstractEntityConstructor, Property, ReactiveGetter, ReadOnlyCollection, Reference } from "joist-core";
3
+ //#region src/entityResolver.d.ts
4
+ type GraphQLPrimitive = string | Date | boolean | number | bigint | null | undefined;
5
+ /**
6
+ * Maps properties like `Entity.firstName: string` to field resolver functions like `firstName(): Promise<string>`.
7
+ *
8
+ * Note that we don't necessarily know/care if `firstName` is in the `Entity` GraphQL type, we just map
9
+ * every field to a potential resolver, and then will let the `EntityResolvers` type-check effectively
10
+ * do the union of "what's defined on the ORM type vs. what's defined on the GraphQL type".
11
+ *
12
+ * Note this might require setting GraphQL options like `requireResolversToMatchSchema: ignore` to avoid
13
+ * the GraphQL infra complaining about the extra field resolvers that Joist creates for fields that
14
+ * are not technically mapped in your GraphQL schema.
15
+ */
16
+ type EntityResolver<T extends Entity> = { [P in keyof T]: P extends "id" ? Resolver<T, Record<string, any>, IdOf<T>> : T[P] extends GraphQLPrimitive | GraphQLPrimitive[] ? Resolver<T, Record<string, any>, T[P]> : T[P] extends Collection<any, infer U> ? Resolver<T, Record<string, any>, U[]> : T[P] extends ReadOnlyCollection<any, infer U> ? Resolver<T, Record<string, any>, U[]> : T[P] extends Reference<any, infer U, infer N> ? Resolver<T, Record<string, any>, U | N> : T[P] extends Property<any, infer V> ? Resolver<T, Record<string, any>, V> : T[P] extends ReactiveGetter<any, infer V> ? Resolver<T, Record<string, any>, V> : T[P] extends Promise<infer V> ? Resolver<T, Record<string, any>, V> : T[P] extends (() => Promise<infer V>) ? Resolver<T, Record<string, any>, V> : T[P] extends (() => infer V) ? Resolver<T, Record<string, any>, V> : Resolver<T, Record<string, any>, T[P]>; };
17
+ /**
18
+ * Creates field resolvers for each of the fields on our entity.
19
+ */
20
+ declare function entityResolver<T extends Entity, A extends Record<string, keyof T> = {}>(entity: MaybeAbstractEntityConstructor<T> | EntityMetadata<T>, aliases?: A): EntityResolver<T> & { [K in keyof A]: EntityResolver<T>[A[K]]; };
21
+ //#endregion
22
+ export { EntityResolver, entityResolver };
23
+ //# sourceMappingURL=entityResolver.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entityResolver.d.mts","names":[],"sources":["../src/entityResolver.ts"],"mappings":";;;KAwCK,4BAA4B;;;;;;;;;;;;KAarB,eAAe,UAAU,aAClC,WAAW,IAAI,iBACZ,SAAS,GAAG,qBAAqB,KAAK,MACtC,EAAE,WAAW,mBAAmB,qBAC9B,SAAS,GAAG,qBAAqB,EAAE,MACnC,EAAE,WAAW,sBAAsB,KACjC,SAAS,GAAG,qBAAqB,OACjC,EAAE,WAAW,8BAA8B,KACzC,SAAS,GAAG,qBAAqB,OACjC,EAAE,WAAW,qBAAqB,SAAS,KACzC,SAAS,GAAG,qBAAqB,IAAI,KACrC,EAAE,WAAW,oBAAoB,KAC/B,SAAS,GAAG,qBAAqB,KACjC,EAAE,WAAW,0BAA0B,KACrC,SAAS,GAAG,qBAAqB,KACjC,EAAE,WAAW,cAAc,KACzB,SAAS,GAAG,qBAAqB,KACjC,EAAE,kBAAiB,cAAc,MAC/B,SAAS,GAAG,qBAAqB,KACjC,EAAE,wBAAuB,KACvB,SAAS,GAAG,qBAAqB,KACjC,SAAS,GAAG,qBAAqB,EAAE;;;;iBAM3C,eAAe,UAAU,QAAQ,UAAU,qBAAqB,SAC9E,QAAQ,+BAA+B,KAAK,eAAe,IAC3D,UAAU,IACT,eAAe,QAAQ,WAAW,IAAI,eAAe,GAAG,EAAE"}