orchid-graphql 1.4.4 → 1.5.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.
@@ -0,0 +1,179 @@
1
+ import { GraphQLResolveInfo } from 'graphql';
2
+ import { DbTable } from 'orchid-orm';
3
+ import { Query } from 'pqb';
4
+
5
+ interface FieldResolverOptions<Orm extends OrmAdapter, Context> {
6
+ /** Table field (if different from GraphQL field) */
7
+ tableField?: string;
8
+ /** Legacy alias for `tableField`. */
9
+ modelField?: string;
10
+ /** Custom query modifier (if different than simply selecting a field). */
11
+ modify?: FieldResolveModifier<Orm, Context>;
12
+ /** Post-process selected value. Return a new value or a promise. */
13
+ transform?(value: any, instance: any, context: FieldResolveContext<Orm, Context>): any;
14
+ }
15
+ type FieldResolveModifier<Orm extends OrmAdapter, Context> = (query: Orm["Query"], context: FieldResolveContext<Orm, Context>) => Orm["Query"];
16
+ interface FieldResolveContext<Orm extends OrmAdapter, Context> extends TableResolveContext<Orm, Context> {
17
+ /** GraphQL field */
18
+ field: string;
19
+ }
20
+ type FieldResolver<Orm extends OrmAdapter, Context> = FieldResolveModifier<Orm, Context>;
21
+ declare function defineFieldResolver<Orm extends OrmAdapter, Context>(options?: FieldResolverOptions<Orm, Context>): FieldResolver<Orm, Context>;
22
+
23
+ interface TableResolverOptions<Orm extends OrmAdapter, Context> {
24
+ /** allow to resolve all table fields without explicitly listing them */
25
+ allowAllFields?: boolean;
26
+ /** allow to filter all table fields without explicitly listing them */
27
+ allowAllFilters?: boolean;
28
+ fields?: Record<string, SimpleFieldResolver<Orm, Context>>;
29
+ modifiers?: Record<string, ApplyFiltersModifier<Orm, Context>>;
30
+ modify?: TableResolveModifier<Orm, Context>;
31
+ transform?(instance: any, context: any): void | PromiseLike<void>;
32
+ }
33
+ type SimpleFieldResolver<Orm extends OrmAdapter, Context> = true | string | FieldResolver<Orm, Context>;
34
+ type TableResolveModifier<Orm extends OrmAdapter, Context> = (query: Orm["Query"], context: TableResolveContext<Orm, Context>) => Orm["Query"];
35
+ interface TableResolveContext<Orm extends OrmAdapter, Context> extends GraphResolveContext<Context> {
36
+ graph: GraphResolver<Orm, Context>;
37
+ /** GraphQL type */
38
+ type: string;
39
+ }
40
+ declare class TableResolver<Orm extends OrmAdapter, Context> {
41
+ readonly orm: Orm;
42
+ readonly table: Orm["Table"];
43
+ readonly options: TableResolverOptions<Orm, Context>;
44
+ readonly relations: Set<string>;
45
+ readonly virtual_fields: Set<string>;
46
+ readonly modifiers?: Record<string, ApplyFiltersModifier<Orm, Context>>;
47
+ readonly table_field_resolvers?: Record<string, FieldResolver<Orm, Context>>;
48
+ constructor(orm: Orm, table: Orm["Table"], options?: TableResolverOptions<Orm, Context>);
49
+ /** Modify query to select fields/relations and filter the result set. */
50
+ resolve(query: Orm["Query"], context: TableResolveContext<Orm, Context>): Orm["Query"];
51
+ /**
52
+ * Create field resolver which will modify query to resolve GraphQL field.
53
+ */
54
+ _get_default_field_resolver(field: string, tableField?: string): FieldResolver<Orm, Context>;
55
+ }
56
+
57
+ type FiltersDef = boolean;
58
+ type ApplyFiltersModifier<Orm extends OrmAdapter, Context> = (query: Orm["Query"], value: any, context: TableResolveContext<Orm, Context>) => Orm["Query"];
59
+
60
+ interface FieldsByTypeName {
61
+ [str: string]: {
62
+ [str: string]: ResolveTree;
63
+ };
64
+ }
65
+ interface ResolveTree {
66
+ name: string;
67
+ alias: string;
68
+ args: {
69
+ [str: string]: unknown;
70
+ };
71
+ fieldsByTypeName: FieldsByTypeName;
72
+ }
73
+
74
+ type GraphResolverOptions<Orm extends OrmAdapter, Context> = Pick<TableResolverOptions<Orm, Context>, "allowAllFields" | "allowAllFilters">;
75
+ interface GraphResolveOptions<Context> {
76
+ info: GraphQLResolveInfo;
77
+ context: Context;
78
+ filters?: FiltersDef;
79
+ /** Subfield path if not resolving root query, e.g.: ['user'] */
80
+ path?: string[];
81
+ }
82
+ type GraphResolveContext<Context> = Omit<GraphResolveOptions<Context>, "info"> & {
83
+ tree: ResolveTree;
84
+ };
85
+ declare class GraphResolver<Orm extends OrmAdapter, Context> {
86
+ readonly orm: Orm;
87
+ readonly type_resolvers: Record<string, TableResolver<Orm, Context>>;
88
+ readonly options: GraphResolverOptions<Orm, Context>;
89
+ constructor(orm: Orm, type_resolvers: Record<string, TableResolver<Orm, Context>>, options?: GraphResolverOptions<Orm, Context>);
90
+ resolve(query: Orm["Query"], { info, ...context }: GraphResolveOptions<Context>): Orm["Query"] | undefined;
91
+ resolvePage(query: Orm["Query"], paginator: Paginator<Orm, Context>, { info, ...context }: GraphResolveOptions<Context>): Orm["QueryTransform"];
92
+ _get_resolve_tree(info: GraphQLResolveInfo): ResolveTree;
93
+ _resolve_type(query: Orm["Query"], context: GraphResolveContext<Context>): Orm["Query"] | undefined;
94
+ _resolve_page(query: Orm["Query"], paginator: Paginator<Orm, Context>, context: GraphResolveContext<Context>): Orm["QueryTransform"];
95
+ }
96
+
97
+ interface Paginator<Orm extends OrmAdapter, Context> {
98
+ /** GraphQL path, for example: `["nodes"]` */
99
+ readonly path: string[];
100
+ paginate(query: Orm["Query"], context: PaginateContext<Orm, Context>): Orm["QueryTransform"];
101
+ }
102
+ interface PaginateContext<Orm extends OrmAdapter, Context> extends GraphResolveContext<Context> {
103
+ graph: GraphResolver<Orm, Context>;
104
+ }
105
+ type GetPageFn = (nodes: any[]) => any;
106
+
107
+ type OrmModifier<Orm extends OrmAdapter> = (query: Orm["Query"], ...args: any[]) => Orm["Query"];
108
+ interface OrmAdapter<Table = any, Query = any, QueryTransform = any> {
109
+ Table: Table;
110
+ Query: Query;
111
+ QueryTransform: QueryTransform;
112
+ /** ORM table -> db table */
113
+ get_table_table(table: Table): string;
114
+ /** ORM table -> relation names */
115
+ get_table_relations(table: Table): string[];
116
+ /** ORM table -> virtual fields */
117
+ get_table_virtual_fields(table: Table): string[];
118
+ get_table_modifiers(table: Table): Record<string, OrmModifier<this>> | undefined;
119
+ /** ORM query -> db table */
120
+ get_query_table(query: Query): string;
121
+ select_field(query: Query, opts: {
122
+ field: string;
123
+ as: string;
124
+ }): Query;
125
+ select_relation(query: Query, opts: {
126
+ relation: string;
127
+ as: string;
128
+ modify: (subquery: Query) => Query;
129
+ }): Query;
130
+ where(query: Query, field: string, op: string | undefined, value: any): Query;
131
+ where_raw(query: Query, expression: string, bindings: Record<string, any>): Query;
132
+ reset_query_order(query: Query): Query;
133
+ add_query_order(query: Query, field: string, desc: boolean): Query;
134
+ set_query_limit(query: Query, limit: number): Query;
135
+ set_query_page_result(query: Query, get_page: GetPageFn): QueryTransform;
136
+ modify_subquery_pagination(subquery: Query, context: Record<string, any>): Query;
137
+ finish_query_pagination(query: Query, field: string, context: Record<string, any>): Query;
138
+ /** Set ORM query to run callback afterwards */
139
+ run_after_query(query: Query, fn: (result: any) => any): Query;
140
+ /** Prevent `SELECT *` by explicitly selecting ID */
141
+ prevent_select_all(query: Query): Query;
142
+ }
143
+
144
+ declare function defineCursorPaginator(options?: Partial<CursorPaginatorOptions>): CursorPaginator<OrmAdapter<any, any, any>, unknown>;
145
+ interface CursorPaginatorOptions {
146
+ fields: string[];
147
+ take: number;
148
+ }
149
+ declare class CursorPaginator<Orm extends OrmAdapter, Context> implements Paginator<Orm, Context> {
150
+ readonly path: string[];
151
+ readonly options: CursorPaginatorOptions;
152
+ readonly fields: Array<{
153
+ name: string;
154
+ desc: boolean;
155
+ }>;
156
+ constructor(options?: Partial<CursorPaginatorOptions>);
157
+ paginate(query: Orm["Query"], context: PaginateContext<Orm, Context>): any;
158
+ _create_cursor(instance: any): string;
159
+ _parse_cursor(cursor: string): {
160
+ expression: string;
161
+ bindings: Record<string, any>;
162
+ };
163
+ }
164
+
165
+ interface RelationResolverOptions<Orm extends OrmAdapter, Context> extends Omit<FieldResolverOptions<Orm, Context>, "modify"> {
166
+ filters?: FiltersDef;
167
+ modify?: TableResolveModifier<Orm, Context>;
168
+ }
169
+ declare function defineRelationResolver<Orm extends OrmAdapter, Context>(options?: RelationResolverOptions<Orm, Context>): FieldResolver<Orm, Context>;
170
+
171
+ declare function definePageResolver<Orm extends OrmAdapter, Context>(paginator: Paginator<Orm, Context>, options?: RelationResolverOptions<Orm, Context>): FieldResolver<Orm, Context>;
172
+
173
+ type OrchidOrm = OrmAdapter<DbTable<any>, Query, Pick<Promise<any>, "then" | "catch">>;
174
+
175
+ declare function createGraphResolver<Context = unknown>(types: Record<string, TableResolver<OrchidOrm, Context>>, options?: GraphResolverOptions<OrchidOrm, Context>): GraphResolver<OrchidOrm, Context>;
176
+
177
+ declare function defineTableResolver<Context = unknown>(table: DbTable<any>, options?: TableResolverOptions<OrchidOrm, Context>): TableResolver<OrchidOrm, Context>;
178
+
179
+ export { createGraphResolver, defineCursorPaginator as cursor, defineCursorPaginator, defineFieldResolver, definePageResolver, defineRelationResolver, defineTableResolver, defineFieldResolver as field, createGraphResolver as graph, definePageResolver as page, defineRelationResolver as relation, defineTableResolver as table };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import { GraphQLResolveInfo } from 'graphql';
2
- import { ResolveTree } from 'graphql-parse-resolve-info';
3
2
  import { DbTable } from 'orchid-orm';
4
3
  import { Query } from 'pqb';
5
4
 
@@ -58,6 +57,20 @@ declare class TableResolver<Orm extends OrmAdapter, Context> {
58
57
  type FiltersDef = boolean;
59
58
  type ApplyFiltersModifier<Orm extends OrmAdapter, Context> = (query: Orm["Query"], value: any, context: TableResolveContext<Orm, Context>) => Orm["Query"];
60
59
 
60
+ interface FieldsByTypeName {
61
+ [str: string]: {
62
+ [str: string]: ResolveTree;
63
+ };
64
+ }
65
+ interface ResolveTree {
66
+ name: string;
67
+ alias: string;
68
+ args: {
69
+ [str: string]: unknown;
70
+ };
71
+ fieldsByTypeName: FieldsByTypeName;
72
+ }
73
+
61
74
  type GraphResolverOptions<Orm extends OrmAdapter, Context> = Pick<TableResolverOptions<Orm, Context>, "allowAllFields" | "allowAllFilters">;
62
75
  interface GraphResolveOptions<Context> {
63
76
  info: GraphQLResolveInfo;
package/dist/index.js CHANGED
@@ -123,8 +123,289 @@ function defineFieldResolver(options = {}) {
123
123
  };
124
124
  }
125
125
 
126
+ // ../graphql-orm/src/utils/graphql-parse-resolve-info.ts
127
+ import assert from "node:assert";
128
+ import {
129
+ getNamedType,
130
+ GraphQLObjectType,
131
+ GraphQLUnionType,
132
+ isCompositeType
133
+ } from "graphql";
134
+ import { getArgumentValues } from "graphql";
135
+
136
+ // ../graphql-orm/src/utils/graphql-parse-resolve-info-debug.ts
137
+ function debugFactory(name) {
138
+ function debug2() {
139
+ }
140
+ debug2.enabled = false;
141
+ return debug2;
142
+ }
143
+
144
+ // ../graphql-orm/src/utils/graphql-parse-resolve-info.ts
145
+ var debug = debugFactory("graphql-parse-resolve-info");
146
+ var DEBUG_ENABLED = debug.enabled;
147
+ function getArgVal(resolveInfo, argument) {
148
+ if (argument.kind === "Variable") {
149
+ return resolveInfo.variableValues[argument.name.value];
150
+ } else if (argument.kind === "BooleanValue") {
151
+ return argument.value;
152
+ }
153
+ }
154
+ function argNameIsIf(arg) {
155
+ return arg && arg.name ? arg.name.value === "if" : false;
156
+ }
157
+ function skipField(resolveInfo, { directives = [] }) {
158
+ let skip = false;
159
+ directives.forEach((directive) => {
160
+ const directiveName = directive.name.value;
161
+ if (Array.isArray(directive.arguments)) {
162
+ const ifArgumentAst = directive.arguments.find(argNameIsIf);
163
+ if (ifArgumentAst) {
164
+ const argumentValueAst = ifArgumentAst.value;
165
+ if (directiveName === "skip") {
166
+ skip = skip || getArgVal(resolveInfo, argumentValueAst);
167
+ } else if (directiveName === "include") {
168
+ skip = skip || !getArgVal(resolveInfo, argumentValueAst);
169
+ }
170
+ }
171
+ }
172
+ });
173
+ return skip;
174
+ }
175
+ function parseResolveInfo(resolveInfo, inOptions = {}) {
176
+ const fieldNodes = (
177
+ // @ts-ignore Property 'fieldASTs' does not exist on type 'GraphQLResolveInfo'.
178
+ resolveInfo.fieldNodes || resolveInfo.fieldASTs
179
+ );
180
+ const options = inOptions === true ? {} : inOptions;
181
+ const forceParse = inOptions === true || inOptions.forceParse;
182
+ const { parentType } = resolveInfo;
183
+ if (!fieldNodes) {
184
+ throw new Error("No fieldNodes provided!");
185
+ }
186
+ if (options.keepRoot == null) {
187
+ options.keepRoot = false;
188
+ }
189
+ if (options.deep == null) {
190
+ options.deep = true;
191
+ }
192
+ const tree = fieldTreeFromAST(
193
+ fieldNodes,
194
+ resolveInfo,
195
+ void 0,
196
+ options,
197
+ parentType
198
+ );
199
+ if (!options.keepRoot) {
200
+ const typeKey = firstKey(tree);
201
+ if (!typeKey) {
202
+ if (forceParse) {
203
+ throw new Error(
204
+ `GraphQL schema issue: simplified parseResolveInfo failed (tree had no keys); perhaps you need to use the keepRoot option?`
205
+ );
206
+ }
207
+ return null;
208
+ }
209
+ const fields = tree[typeKey];
210
+ const fieldKey = firstKey(fields);
211
+ if (!fieldKey) {
212
+ if (forceParse) {
213
+ throw new Error(
214
+ `GraphQL schema issue: simplified parseResolveInfo failed (could not get key from fields); perhaps you need to use the keepRoot option?`
215
+ );
216
+ }
217
+ return null;
218
+ }
219
+ return fields[fieldKey];
220
+ }
221
+ return tree;
222
+ }
223
+ function getFieldFromAST(ast, parentType) {
224
+ if (ast.kind === "Field") {
225
+ const fieldNode = ast;
226
+ const fieldName = fieldNode.name.value;
227
+ if (!(parentType instanceof GraphQLUnionType)) {
228
+ const type = parentType;
229
+ return type.getFields()[fieldName];
230
+ } else {
231
+ }
232
+ }
233
+ return void 0;
234
+ }
235
+ var iNum = 1;
236
+ function fieldTreeFromAST(inASTs, resolveInfo, initTree = {}, options = {}, parentType, depth = "") {
237
+ const instance = iNum++;
238
+ if (DEBUG_ENABLED)
239
+ debug(
240
+ "%s[%d] Entering fieldTreeFromAST with parent type '%s'",
241
+ depth,
242
+ instance,
243
+ parentType
244
+ );
245
+ const { variableValues } = resolveInfo;
246
+ const fragments = resolveInfo.fragments || {};
247
+ const asts = Array.isArray(inASTs) ? inASTs : [inASTs];
248
+ if (!initTree[parentType.name]) {
249
+ initTree[parentType.name] = {};
250
+ }
251
+ const outerDepth = depth;
252
+ return asts.reduce((tree, selectionVal, idx) => {
253
+ const depth2 = DEBUG_ENABLED ? `${outerDepth} ` : null;
254
+ if (DEBUG_ENABLED)
255
+ debug(
256
+ "%s[%d] Processing AST %d of %d; kind = %s",
257
+ depth2,
258
+ instance,
259
+ idx + 1,
260
+ asts.length,
261
+ selectionVal.kind
262
+ );
263
+ if (skipField(resolveInfo, selectionVal)) {
264
+ if (DEBUG_ENABLED)
265
+ debug("%s[%d] IGNORING due to directive", depth2, instance);
266
+ } else if (selectionVal.kind === "Field") {
267
+ const val = selectionVal;
268
+ const name = val.name.value;
269
+ const isReserved = name[0] === "_" && name[1] === "_" && name !== "__id";
270
+ if (isReserved) {
271
+ if (DEBUG_ENABLED)
272
+ debug(
273
+ "%s[%d] IGNORING because field '%s' is reserved",
274
+ depth2,
275
+ instance,
276
+ name
277
+ );
278
+ } else {
279
+ const alias = val.alias && val.alias.value ? val.alias.value : name;
280
+ if (DEBUG_ENABLED)
281
+ debug(
282
+ "%s[%d] Field '%s' (alias = '%s')",
283
+ depth2,
284
+ instance,
285
+ name,
286
+ alias
287
+ );
288
+ const field = getFieldFromAST(val, parentType);
289
+ if (field == null) {
290
+ return tree;
291
+ }
292
+ const fieldGqlTypeOrUndefined = getNamedType(field.type);
293
+ if (!fieldGqlTypeOrUndefined) {
294
+ return tree;
295
+ }
296
+ const fieldGqlType = fieldGqlTypeOrUndefined;
297
+ const args = getArgumentValues(
298
+ field,
299
+ val,
300
+ variableValues
301
+ ) || {};
302
+ if (parentType.name && !tree[parentType.name][alias]) {
303
+ const newTreeRoot = {
304
+ name,
305
+ alias,
306
+ args,
307
+ fieldsByTypeName: isCompositeType(fieldGqlType) ? {
308
+ [fieldGqlType.name]: {}
309
+ } : {}
310
+ };
311
+ tree[parentType.name][alias] = newTreeRoot;
312
+ }
313
+ const selectionSet = val.selectionSet;
314
+ if (selectionSet != null && options.deep && isCompositeType(fieldGqlType)) {
315
+ const newParentType = fieldGqlType;
316
+ if (DEBUG_ENABLED)
317
+ debug("%s[%d] Recursing into subfields", depth2, instance);
318
+ fieldTreeFromAST(
319
+ selectionSet.selections,
320
+ resolveInfo,
321
+ tree[parentType.name][alias].fieldsByTypeName,
322
+ options,
323
+ newParentType,
324
+ `${depth2} `
325
+ );
326
+ } else {
327
+ if (DEBUG_ENABLED)
328
+ debug("%s[%d] Exiting (no fields to add)", depth2, instance);
329
+ }
330
+ }
331
+ } else if (selectionVal.kind === "FragmentSpread" && options.deep) {
332
+ const val = selectionVal;
333
+ const name = val.name && val.name.value;
334
+ if (DEBUG_ENABLED)
335
+ debug("%s[%d] Fragment spread '%s'", depth2, instance, name);
336
+ const fragment = fragments[name];
337
+ assert(fragment, 'unknown fragment "' + name + '"');
338
+ let fragmentType = parentType;
339
+ if (fragment.typeCondition) {
340
+ fragmentType = getType(resolveInfo, fragment.typeCondition);
341
+ }
342
+ if (fragmentType && isCompositeType(fragmentType)) {
343
+ const newParentType = fragmentType;
344
+ fieldTreeFromAST(
345
+ fragment.selectionSet.selections,
346
+ resolveInfo,
347
+ tree,
348
+ options,
349
+ newParentType,
350
+ `${depth2} `
351
+ );
352
+ }
353
+ } else if (selectionVal.kind === "InlineFragment" && options.deep) {
354
+ const val = selectionVal;
355
+ const fragment = val;
356
+ let fragmentType = parentType;
357
+ if (fragment.typeCondition) {
358
+ fragmentType = getType(resolveInfo, fragment.typeCondition);
359
+ }
360
+ if (DEBUG_ENABLED)
361
+ debug(
362
+ "%s[%d] Inline fragment (parent = '%s', type = '%s')",
363
+ depth2,
364
+ instance,
365
+ parentType,
366
+ fragmentType
367
+ );
368
+ if (fragmentType && isCompositeType(fragmentType)) {
369
+ const newParentType = fragmentType;
370
+ fieldTreeFromAST(
371
+ fragment.selectionSet.selections,
372
+ resolveInfo,
373
+ tree,
374
+ options,
375
+ newParentType,
376
+ `${depth2} `
377
+ );
378
+ }
379
+ } else {
380
+ if (DEBUG_ENABLED)
381
+ debug(
382
+ "%s[%d] IGNORING because kind '%s' not understood",
383
+ depth2,
384
+ instance,
385
+ selectionVal.kind
386
+ );
387
+ }
388
+ return tree;
389
+ }, initTree);
390
+ }
391
+ var hasOwnProperty = Object.prototype.hasOwnProperty;
392
+ function firstKey(obj) {
393
+ for (const key in obj) {
394
+ if (hasOwnProperty.call(obj, key)) {
395
+ return key;
396
+ }
397
+ }
398
+ }
399
+ function getType(resolveInfo, typeCondition) {
400
+ const { schema } = resolveInfo;
401
+ const { kind, name } = typeCondition;
402
+ if (kind === "NamedType") {
403
+ const typeName = name.value;
404
+ return schema.getType(typeName);
405
+ }
406
+ }
407
+
126
408
  // ../graphql-orm/src/resolvers/graph.ts
127
- import { parseResolveInfo } from "graphql-parse-resolve-info";
128
409
  var GraphResolver = class {
129
410
  constructor(orm2, type_resolvers, options = {}) {
130
411
  this.orm = orm2;
@@ -341,6 +622,7 @@ var TableResolver = class {
341
622
  };
342
623
 
343
624
  // src/orm/orm.ts
625
+ import { raw } from "orchid-orm";
344
626
  var orm = {
345
627
  Table: void 0,
346
628
  Query: void 0,
@@ -378,7 +660,7 @@ var orm = {
378
660
  return query.where({ [field]: op ? { [op]: value } : value });
379
661
  },
380
662
  where_raw(query, expression, bindings) {
381
- return query.where(query.sql({ raw: expression }).values(bindings));
663
+ return query.where(raw({ raw: expression, values: bindings }));
382
664
  },
383
665
  // Order & Limit
384
666
  reset_query_order(query) {