@tinkerstack/graphql-annotation 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,511 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var __decorateClass = (decorators, target, key, kind) => {
20
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
21
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
22
+ if (decorator = decorators[i])
23
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
24
+ if (kind && result) __defProp(target, key, result);
25
+ return result;
26
+ };
27
+ var __decorateParam = (index, decorator) => (target, key) => decorator(target, key, index);
28
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ ANNOTATION_METADATA: () => ANNOTATION_METADATA,
34
+ ANNOTATION_RESOLVER_METADATA: () => ANNOTATION_RESOLVER_METADATA,
35
+ Annotate: () => Annotate,
36
+ GRAPHQL_ANNOTATION_RESOLVER_METADATA: () => GRAPHQL_ANNOTATION_RESOLVER_METADATA,
37
+ GraphQLAnnotatedSchemaLoader: () => GraphQLAnnotatedSchemaLoader,
38
+ GraphQLAnnotationModule: () => GraphQLAnnotationModule,
39
+ ResolveAnnotation: () => ResolveAnnotation
40
+ });
41
+ module.exports = __toCommonJS(index_exports);
42
+
43
+ // src/modules/core/annotation.decorators.ts
44
+ var import_common = require("@nestjs/common");
45
+ var import_graphql = require("@nestjs/graphql");
46
+ var import_reflect_metadata = require("reflect-metadata");
47
+
48
+ // src/modules/core/annotation.constants.ts
49
+ var ANNOTATION_METADATA = "graphql-annotation";
50
+ var ANNOTATION_RESOLVER_METADATA = "graphql-annotation-resolver";
51
+ var GRAPHQL_ANNOTATION_RESOLVER_METADATA = "graphql-annotation-provider";
52
+
53
+ // src/modules/core/annotation.decorators.ts
54
+ function Annotate(name, opts) {
55
+ const annotationName = name;
56
+ const annotationData = opts?.data;
57
+ return (target, propertyKey, descriptorOrParameterIndex) => {
58
+ switch (typeof descriptorOrParameterIndex) {
59
+ // Method
60
+ case "object": {
61
+ const descriptor = descriptorOrParameterIndex;
62
+ _updateAnnotation(descriptor.value ?? target, (meta) => ({
63
+ ...meta,
64
+ [annotationName]: {
65
+ annotation: annotationName,
66
+ data: annotationData,
67
+ target: {
68
+ type: "method",
69
+ name: propertyKey
70
+ }
71
+ }
72
+ }));
73
+ break;
74
+ }
75
+ // Parameter
76
+ case "number": {
77
+ const paramIndex = descriptorOrParameterIndex;
78
+ const paramName = opts?.parameter;
79
+ if (!paramName)
80
+ throw new Error(
81
+ `Unable to apply annotation "${annotationName}". Parameter name not specified.`
82
+ );
83
+ (0, import_graphql.Args)(paramName)(target, propertyKey, paramIndex);
84
+ _updateAnnotation(
85
+ target.constructor,
86
+ (meta) => ({
87
+ ...meta,
88
+ [`${annotationName}@${paramIndex}`]: {
89
+ annotation: annotationName,
90
+ data: annotationData,
91
+ target: {
92
+ type: "parameter",
93
+ paramIndex,
94
+ paramName
95
+ }
96
+ }
97
+ }),
98
+ propertyKey
99
+ );
100
+ break;
101
+ }
102
+ default:
103
+ throw new Error(
104
+ `Unable to attach annotation "${name}". Unsupported target.`
105
+ );
106
+ }
107
+ };
108
+ }
109
+ function _updateAnnotation(target, updateFn, propertyKey) {
110
+ const meta = Reflect.getMetadata(ANNOTATION_METADATA, target) ?? {};
111
+ if (propertyKey)
112
+ Reflect.defineMetadata(
113
+ ANNOTATION_METADATA,
114
+ updateFn(meta),
115
+ target,
116
+ propertyKey
117
+ );
118
+ else Reflect.defineMetadata(ANNOTATION_METADATA, updateFn(meta), target);
119
+ }
120
+ function ResolveAnnotation(name) {
121
+ return (target, property, descriptor) => {
122
+ (0, import_common.SetMetadata)(ANNOTATION_RESOLVER_METADATA, {
123
+ annotation: name ?? property
124
+ })(target, property, descriptor);
125
+ };
126
+ }
127
+
128
+ // src/modules/core/annotation.loader.ts
129
+ var import_stitch = require("@graphql-tools/stitch");
130
+ var import_utils = require("@graphql-tools/utils");
131
+ var import_wrap2 = require("@graphql-tools/wrap");
132
+ var import_common3 = require("@nestjs/common");
133
+ var import_graphql3 = require("@nestjs/graphql");
134
+ var import_gql_paramtype = require("@nestjs/graphql/dist/enums/gql-paramtype.enum");
135
+ var import_graphql_request = require("graphql-request");
136
+
137
+ // src/modules/core/annotation.resolver.ts
138
+ var import_common2 = require("@nestjs/common");
139
+ var import_graphql2 = require("@nestjs/graphql");
140
+ var import_graphql_scalars = require("graphql-scalars");
141
+ var import_reflect_metadata2 = require("reflect-metadata");
142
+ var GraphQLAnnotationMeta = class {
143
+ name;
144
+ resolvers;
145
+ };
146
+ __decorateClass([
147
+ (0, import_graphql2.Field)(() => String)
148
+ ], GraphQLAnnotationMeta.prototype, "name", 2);
149
+ __decorateClass([
150
+ (0, import_graphql2.Field)(() => import_graphql_scalars.GraphQLJSONObject, { name: "resolvers" })
151
+ ], GraphQLAnnotationMeta.prototype, "resolvers", 2);
152
+ GraphQLAnnotationMeta = __decorateClass([
153
+ (0, import_graphql2.ObjectType)()
154
+ ], GraphQLAnnotationMeta);
155
+ var GraphQLAnnotationResolver = class {
156
+ constructor(metadataScanner, resolverDiscoveryService, meta) {
157
+ this.metadataScanner = metadataScanner;
158
+ this.resolverDiscoveryService = resolverDiscoveryService;
159
+ this.meta = meta;
160
+ }
161
+ _logger = new import_common2.Logger("GraphQLAnnotationModule");
162
+ async getGraphQLAnnotations() {
163
+ return {
164
+ ...this.meta,
165
+ resolvers: this.resolverAnnotations
166
+ };
167
+ }
168
+ resolverAnnotations = {};
169
+ async onModuleInit() {
170
+ this.resolverAnnotations = this.compileResolversAnnotations();
171
+ }
172
+ compileResolversAnnotations() {
173
+ const instances = this.resolverDiscoveryService.explore();
174
+ const resolversAnnotations = {};
175
+ for (const { instance } of instances)
176
+ for (const methodName of this.metadataScanner.getAllMethodNames(
177
+ Object.getPrototypeOf(instance)
178
+ )) {
179
+ const annotations = [
180
+ ...Object.values(
181
+ Reflect.getMetadata(ANNOTATION_METADATA, instance[methodName]) ?? {}
182
+ ),
183
+ ...Object.values(
184
+ Reflect.getMetadata(
185
+ ANNOTATION_METADATA,
186
+ instance.constructor,
187
+ methodName
188
+ ) ?? {}
189
+ )
190
+ ];
191
+ if (annotations.length <= 0) continue;
192
+ resolversAnnotations[methodName] = annotations;
193
+ this._logger.log(
194
+ `Discovered annotated resolver "${instance.constructor.name}.${methodName}" with ${annotations.length} annotation(s)`
195
+ );
196
+ }
197
+ return resolversAnnotations;
198
+ }
199
+ };
200
+ __publicField(GraphQLAnnotationResolver, "QUERY_NAME", "_annotations");
201
+ __decorateClass([
202
+ (0, import_graphql2.Query)(() => GraphQLAnnotationMeta, {
203
+ name: GraphQLAnnotationResolver.QUERY_NAME
204
+ })
205
+ ], GraphQLAnnotationResolver.prototype, "getGraphQLAnnotations", 1);
206
+ GraphQLAnnotationResolver = __decorateClass([
207
+ (0, import_graphql2.Resolver)(),
208
+ (0, import_common2.Injectable)(),
209
+ __decorateParam(2, (0, import_common2.Inject)(ANNOTATION_RESOLVER_METADATA))
210
+ ], GraphQLAnnotationResolver);
211
+
212
+ // src/modules/core/annotation.utils.ts
213
+ var import_executor_http = require("@graphql-tools/executor-http");
214
+ var import_wrap = require("@graphql-tools/wrap");
215
+ async function loadGraphQLSchema(url) {
216
+ const executor = (0, import_executor_http.buildHTTPExecutor)({
217
+ endpoint: url
218
+ });
219
+ return {
220
+ schema: await (0, import_wrap.schemaFromExecutor)(executor),
221
+ executor
222
+ // Has to be included.
223
+ };
224
+ }
225
+ function split(array, predicate) {
226
+ const match = [];
227
+ const rest = [];
228
+ array.forEach((item) => {
229
+ const matchedPredicate = predicate(item);
230
+ const target = matchedPredicate ? match : rest;
231
+ target.push(item);
232
+ });
233
+ return [
234
+ match,
235
+ rest
236
+ ];
237
+ }
238
+ function shallowMerge(objects) {
239
+ const res = {};
240
+ objects.forEach((o) => Object.assign(res, o));
241
+ return res;
242
+ }
243
+ function isPlainObject(obj) {
244
+ const prototype = Object.getPrototypeOf(obj);
245
+ return prototype === Object.getPrototypeOf({}) || prototype === null;
246
+ }
247
+
248
+ // src/modules/core/annotation.loader.ts
249
+ var import_class_transformer = require("class-transformer");
250
+ var GraphQLAnnotatedSchemaLoader = class {
251
+ constructor(resolverDiscoveryService, metadataScanner, externalContextCreator) {
252
+ this.resolverDiscoveryService = resolverDiscoveryService;
253
+ this.metadataScanner = metadataScanner;
254
+ this.externalContextCreator = externalContextCreator;
255
+ }
256
+ _logger = new import_common3.Logger("GraphQLAnnotationModule");
257
+ async load(sources) {
258
+ const loadedSources = await this.batchLoadSources(sources);
259
+ const schema = (0, import_stitch.stitchSchemas)({
260
+ subschemas: loadedSources.map((s) => s.subschema)
261
+ });
262
+ const annotations = shallowMerge(
263
+ loadedSources.map((s) => s.annotation)
264
+ );
265
+ const annotationResolvers = await this.discoverAnnotationResolvers();
266
+ return this.embedAnnotationResolversToSchema(
267
+ schema,
268
+ annotations,
269
+ annotationResolvers
270
+ );
271
+ }
272
+ async discoverAnnotationResolvers() {
273
+ const annotationResolvers = /* @__PURE__ */ new Map();
274
+ const paramFactory = new GraphQLAnnotationResolverParamsFactory();
275
+ const instances = this.resolverDiscoveryService.explore();
276
+ for (const { instance } of instances)
277
+ for (const methodName of this.metadataScanner.getAllMethodNames(
278
+ Object.getPrototypeOf(instance)
279
+ )) {
280
+ const resolverMeta = Reflect.getMetadata(
281
+ ANNOTATION_RESOLVER_METADATA,
282
+ instance[methodName]
283
+ );
284
+ if (!resolverMeta) continue;
285
+ this._logger.log(
286
+ `Discovered resolver for annotation "${resolverMeta.annotation}"`
287
+ );
288
+ annotationResolvers.set(
289
+ resolverMeta.annotation,
290
+ this.externalContextCreator.create(
291
+ instance,
292
+ instance[methodName],
293
+ methodName,
294
+ import_graphql3.PARAM_ARGS_METADATA,
295
+ paramFactory,
296
+ void 0,
297
+ void 0,
298
+ void 0,
299
+ "graphql"
300
+ )
301
+ );
302
+ }
303
+ return annotationResolvers;
304
+ }
305
+ async batchLoadSources(sources) {
306
+ const pending = Object.entries(sources).map(async ([name, url]) => {
307
+ const loadId = `${name}@${url}`;
308
+ try {
309
+ const loaded2 = {
310
+ id: loadId,
311
+ url,
312
+ name,
313
+ subschema: await loadGraphQLSchema(url),
314
+ annotation: await this.loadAnnotations(url)
315
+ };
316
+ loaded2.subschema.transforms = [
317
+ new import_wrap2.FilterRootFields(
318
+ (op, fieldName) => !fieldName.endsWith(GraphQLAnnotationResolver.QUERY_NAME)
319
+ )
320
+ ];
321
+ this._logger.log(`Loaded schema for ${loadId}`);
322
+ return loaded2;
323
+ } catch (err) {
324
+ this._logger.error(`Unable to load schema for ${loadId}`);
325
+ throw err;
326
+ }
327
+ });
328
+ const [loaded, failed] = split(
329
+ await Promise.allSettled(pending),
330
+ (q) => q.status === "fulfilled"
331
+ );
332
+ if (failed.length > 0)
333
+ throw new Error(
334
+ `Failed to load ${failed.length} schema(s) from sources.`
335
+ );
336
+ return loaded.map((l) => l.value);
337
+ }
338
+ async loadAnnotations(url) {
339
+ const client = new import_graphql_request.GraphQLClient(url);
340
+ try {
341
+ const res = await client.rawRequest(`
342
+ query IntrospectAnnotations {
343
+ ${GraphQLAnnotationResolver.QUERY_NAME} {
344
+ name
345
+ resolvers
346
+ }
347
+ }
348
+ `);
349
+ this._logger.log(`Loaded annotations for ${url}`);
350
+ return res.data[GraphQLAnnotationResolver.QUERY_NAME];
351
+ } catch (err) {
352
+ this._logger.warn(`Failed to load annotations at ${url}, assuming empty`);
353
+ return {};
354
+ }
355
+ }
356
+ async embedAnnotationResolversToSchema(schema, annotations, annotationResolvers) {
357
+ return (0, import_utils.mapSchema)(schema, {
358
+ [import_utils.MapperKind.ROOT_FIELD]: (fieldSchema, name, type) => {
359
+ const fieldAnnotations = (annotations.resolvers[name] ?? []).map((info) => ({
360
+ ...info,
361
+ resolver: annotationResolvers.get(info.annotation)
362
+ // NOTE: should be validated in filter
363
+ })).filter((info) => {
364
+ const annotationHasResolver = info.resolver !== void 0;
365
+ if (!annotationHasResolver)
366
+ this._logger.warn(
367
+ `Found unhandled annotation "${info.annotation}", skipping linking process`
368
+ );
369
+ return annotationHasResolver;
370
+ });
371
+ if (fieldAnnotations.length <= 0) return fieldSchema;
372
+ for (const annotation of fieldAnnotations)
373
+ if (annotation.target.type === "parameter" && fieldSchema.args)
374
+ delete fieldSchema.args[annotation.target.paramName];
375
+ const defaultResolver = fieldSchema.resolve;
376
+ fieldSchema.resolve = async function(parent, args, context, info) {
377
+ const annotationCallbacks = await Promise.all(
378
+ fieldAnnotations.map(async (annotation) => ({
379
+ annotation,
380
+ return: await annotation.resolver(
381
+ ...[
382
+ info.rootValue,
383
+ annotation.data,
384
+ context,
385
+ info
386
+ ]
387
+ )
388
+ }))
389
+ );
390
+ const resolvedParams = annotationCallbacks.reduce(
391
+ (params, call) => {
392
+ if (call.annotation.target.type !== "parameter") return params;
393
+ const returnedClassType = typeof call.return === "object" && !isPlainObject(call.return);
394
+ const returnValue = returnedClassType ? (0, import_class_transformer.instanceToPlain)(call.return) : call.return;
395
+ params[call.annotation.target.paramName] = returnValue;
396
+ return params;
397
+ },
398
+ {}
399
+ );
400
+ return defaultResolver(
401
+ parent,
402
+ { ...args, ...resolvedParams },
403
+ context,
404
+ info
405
+ );
406
+ };
407
+ return fieldSchema;
408
+ }
409
+ });
410
+ }
411
+ };
412
+ GraphQLAnnotatedSchemaLoader = __decorateClass([
413
+ (0, import_common3.Injectable)()
414
+ ], GraphQLAnnotatedSchemaLoader);
415
+ var GraphQLAnnotationResolverParamsFactory = class {
416
+ // This param factory extends graphql's own just with different inputs from annotations instead.
417
+ // https://github.com/nestjs/graphql/blob/master/packages/graphql/lib/factories/params.factory.ts#L9
418
+ exchangeKeyForValue(type, possibleKey, argsContext) {
419
+ if (!argsContext) return null;
420
+ console.log(type, possibleKey, argsContext);
421
+ const args = {
422
+ parentValue: argsContext[0],
423
+ data: argsContext[1],
424
+ context: argsContext[2],
425
+ info: argsContext[3]
426
+ };
427
+ switch (type) {
428
+ case import_gql_paramtype.GqlParamtype.ROOT:
429
+ return args.parentValue;
430
+ case import_gql_paramtype.GqlParamtype.ARGS:
431
+ return possibleKey && args.data ? args.data[possibleKey] : args.data;
432
+ case import_gql_paramtype.GqlParamtype.CONTEXT:
433
+ return possibleKey && args.context ? args.context[possibleKey] : args.context;
434
+ case import_gql_paramtype.GqlParamtype.INFO:
435
+ return possibleKey && args.context ? args.info[possibleKey] : args.info;
436
+ default:
437
+ return null;
438
+ }
439
+ }
440
+ };
441
+
442
+ // src/modules/core/annotation.module.ts
443
+ var import_common5 = require("@nestjs/common");
444
+ var import_core = require("@nestjs/core");
445
+
446
+ // src/modules/core/resolver.explorer.ts
447
+ var import_common4 = require("@nestjs/common");
448
+ var import_graphql4 = require("@nestjs/graphql");
449
+ var ResolverDiscoveryService = class {
450
+ constructor(discoveryService) {
451
+ this.discoveryService = discoveryService;
452
+ }
453
+ explore() {
454
+ const wrappedProviders = this.discoveryService.getProviders();
455
+ return wrappedProviders.flatMap((wrapped) => {
456
+ const { instance } = wrapped;
457
+ if (!instance) return [];
458
+ const isResolver = Reflect.hasMetadata(
459
+ import_graphql4.RESOLVER_TYPE_METADATA,
460
+ instance.constructor
461
+ );
462
+ if (!isResolver) return [];
463
+ return wrapped;
464
+ });
465
+ }
466
+ };
467
+ ResolverDiscoveryService = __decorateClass([
468
+ (0, import_common4.Injectable)()
469
+ ], ResolverDiscoveryService);
470
+
471
+ // src/modules/core/annotation.module.ts
472
+ var GraphQLAnnotationModule = class {
473
+ static forRoot(opts) {
474
+ if (opts?.serveAnnotations)
475
+ return {
476
+ global: true,
477
+ module: GraphQLAnnotationModule,
478
+ imports: [import_core.DiscoveryModule],
479
+ providers: [
480
+ ResolverDiscoveryService,
481
+ GraphQLAnnotatedSchemaLoader,
482
+ {
483
+ provide: ANNOTATION_RESOLVER_METADATA,
484
+ useValue: opts.serveConfig
485
+ },
486
+ GraphQLAnnotationResolver
487
+ ],
488
+ exports: [GraphQLAnnotatedSchemaLoader]
489
+ };
490
+ return {
491
+ global: true,
492
+ module: GraphQLAnnotationModule,
493
+ imports: [import_core.DiscoveryModule],
494
+ providers: [ResolverDiscoveryService, GraphQLAnnotatedSchemaLoader],
495
+ exports: [GraphQLAnnotatedSchemaLoader]
496
+ };
497
+ }
498
+ };
499
+ GraphQLAnnotationModule = __decorateClass([
500
+ (0, import_common5.Module)({})
501
+ ], GraphQLAnnotationModule);
502
+ // Annotate the CommonJS export names for ESM import in node:
503
+ 0 && (module.exports = {
504
+ ANNOTATION_METADATA,
505
+ ANNOTATION_RESOLVER_METADATA,
506
+ Annotate,
507
+ GRAPHQL_ANNOTATION_RESOLVER_METADATA,
508
+ GraphQLAnnotatedSchemaLoader,
509
+ GraphQLAnnotationModule,
510
+ ResolveAnnotation
511
+ });