@leaven-graphql/nestjs 0.1.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,1321 @@
1
+ // @bun
2
+ var __legacyDecorateClassTS = function(decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
5
+ r = Reflect.decorate(decorators, target, key, desc);
6
+ else
7
+ for (var i = decorators.length - 1;i >= 0; i--)
8
+ if (d = decorators[i])
9
+ r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
10
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
11
+ };
12
+ var __legacyDecorateParamTS = (index, decorator) => (target, key) => decorator(target, key, index);
13
+ var __legacyMetadataTS = (k, v) => {
14
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function")
15
+ return Reflect.metadata(k, v);
16
+ };
17
+ var __require = import.meta.require;
18
+
19
+ // src/module.ts
20
+ import {
21
+ Module,
22
+ Global
23
+ } from "@nestjs/common";
24
+
25
+ // src/driver.ts
26
+ import { Injectable } from "@nestjs/common";
27
+ import { LeavenExecutor } from "@leaven-graphql/core";
28
+ class LeavenDriver {
29
+ executor = null;
30
+ schema = null;
31
+ options;
32
+ constructor(options) {
33
+ this.options = options;
34
+ }
35
+ async onModuleInit() {
36
+ if (this.options.schema) {
37
+ this.schema = this.options.schema;
38
+ this.initializeExecutor();
39
+ }
40
+ }
41
+ async onModuleDestroy() {
42
+ if (this.executor) {
43
+ this.executor.clearCaches();
44
+ this.executor = null;
45
+ }
46
+ }
47
+ setSchema(schema) {
48
+ this.schema = schema;
49
+ this.initializeExecutor();
50
+ }
51
+ getSchema() {
52
+ return this.schema;
53
+ }
54
+ getExecutor() {
55
+ return this.executor;
56
+ }
57
+ initializeExecutor() {
58
+ if (!this.schema) {
59
+ throw new Error("Schema must be set before initializing executor");
60
+ }
61
+ const config = {
62
+ schema: this.schema,
63
+ cache: this.options.cache,
64
+ metrics: this.options.metrics,
65
+ maxComplexity: this.options.maxComplexity
66
+ };
67
+ this.executor = new LeavenExecutor(config);
68
+ }
69
+ async execute(query, variables, context, operationName) {
70
+ if (!this.executor) {
71
+ throw new Error("Executor not initialized. Set schema first.");
72
+ }
73
+ const queryString = typeof query === "string" ? query : this.documentToString(query);
74
+ try {
75
+ const result = await this.executor.execute({
76
+ query: queryString,
77
+ variables,
78
+ operationName
79
+ }, context);
80
+ const response = result.response;
81
+ let formattedErrors = response.errors;
82
+ if (formattedErrors && this.options.formatError) {
83
+ formattedErrors = formattedErrors.map(this.options.formatError);
84
+ }
85
+ let extensions = response.extensions;
86
+ if (this.options.metrics && result.metrics) {
87
+ extensions = {
88
+ ...extensions,
89
+ metrics: result.metrics
90
+ };
91
+ }
92
+ return {
93
+ data: response.data,
94
+ errors: formattedErrors,
95
+ extensions
96
+ };
97
+ } catch (error) {
98
+ const message = error instanceof Error ? error.message : "Execution failed";
99
+ const formattedError = {
100
+ message,
101
+ extensions: {
102
+ code: "INTERNAL_SERVER_ERROR",
103
+ ...this.options.includeStacktraceInErrorResponses && error instanceof Error ? { stacktrace: error.stack?.split(`
104
+ `) } : {}
105
+ }
106
+ };
107
+ return {
108
+ errors: [
109
+ this.options.formatError ? this.options.formatError(formattedError) : formattedError
110
+ ]
111
+ };
112
+ }
113
+ }
114
+ async handleRequest(request, response) {
115
+ const body = await this.parseRequestBody(request);
116
+ const context = await this.createContext(request, response);
117
+ return this.execute(body.query, body.variables, context, body.operationName);
118
+ }
119
+ async parseRequestBody(request) {
120
+ const contentType = request.headers.get("content-type") ?? "";
121
+ if (contentType.includes("application/json")) {
122
+ const body = await request.json();
123
+ return {
124
+ query: body.query,
125
+ variables: body.variables,
126
+ operationName: body.operationName
127
+ };
128
+ }
129
+ const url = new URL(request.url);
130
+ const query = url.searchParams.get("query");
131
+ const variables = url.searchParams.get("variables");
132
+ const operationName = url.searchParams.get("operationName");
133
+ if (!query) {
134
+ throw new Error("Query is required");
135
+ }
136
+ return {
137
+ query,
138
+ variables: variables ? JSON.parse(variables) : undefined,
139
+ operationName: operationName ?? undefined
140
+ };
141
+ }
142
+ async createContext(request, response) {
143
+ const baseContext = {
144
+ req: request,
145
+ res: response
146
+ };
147
+ if (this.options.context) {
148
+ const customContext = await this.options.context(request, response);
149
+ return { ...baseContext, ...customContext };
150
+ }
151
+ return baseContext;
152
+ }
153
+ documentToString(document) {
154
+ const { print } = __require("graphql");
155
+ return print(document);
156
+ }
157
+ isPlaygroundEnabled() {
158
+ if (this.options.playground !== undefined) {
159
+ return this.options.playground;
160
+ }
161
+ return true;
162
+ }
163
+ isIntrospectionEnabled() {
164
+ if (this.options.introspection !== undefined) {
165
+ return this.options.introspection;
166
+ }
167
+ return true;
168
+ }
169
+ getPath() {
170
+ return this.options.path ?? "/graphql";
171
+ }
172
+ getCacheStats() {
173
+ return this.executor?.getCacheStats() ?? null;
174
+ }
175
+ clearCaches() {
176
+ this.executor?.clearCaches();
177
+ }
178
+ }
179
+ LeavenDriver = __legacyDecorateClassTS([
180
+ Injectable(),
181
+ __legacyMetadataTS("design:paramtypes", [
182
+ typeof LeavenModuleOptions === "undefined" ? Object : LeavenModuleOptions
183
+ ])
184
+ ], LeavenDriver);
185
+
186
+ // src/module.ts
187
+ var LEAVEN_MODULE_OPTIONS = "LEAVEN_MODULE_OPTIONS";
188
+ var LEAVEN_DRIVER = "LEAVEN_DRIVER";
189
+
190
+ class LeavenModule {
191
+ static forRoot(options = {}) {
192
+ const optionsProvider = {
193
+ provide: LEAVEN_MODULE_OPTIONS,
194
+ useValue: options
195
+ };
196
+ const driverProvider = {
197
+ provide: LEAVEN_DRIVER,
198
+ useFactory: (opts) => new LeavenDriver(opts),
199
+ inject: [LEAVEN_MODULE_OPTIONS]
200
+ };
201
+ return {
202
+ module: LeavenModule,
203
+ providers: [optionsProvider, driverProvider],
204
+ exports: [LEAVEN_MODULE_OPTIONS, LEAVEN_DRIVER]
205
+ };
206
+ }
207
+ static forRootAsync(options) {
208
+ const asyncProviders = this.createAsyncProviders(options);
209
+ const driverProvider = {
210
+ provide: LEAVEN_DRIVER,
211
+ useFactory: (opts) => new LeavenDriver(opts),
212
+ inject: [LEAVEN_MODULE_OPTIONS]
213
+ };
214
+ return {
215
+ module: LeavenModule,
216
+ imports: options.imports ?? [],
217
+ providers: [...asyncProviders, driverProvider],
218
+ exports: [LEAVEN_MODULE_OPTIONS, LEAVEN_DRIVER]
219
+ };
220
+ }
221
+ static createAsyncProviders(options) {
222
+ if (options.useExisting || options.useFactory) {
223
+ return [this.createAsyncOptionsProvider(options)];
224
+ }
225
+ const useClass = options.useClass;
226
+ return [
227
+ this.createAsyncOptionsProvider(options),
228
+ {
229
+ provide: useClass,
230
+ useClass
231
+ }
232
+ ];
233
+ }
234
+ static createAsyncOptionsProvider(options) {
235
+ if (options.useFactory) {
236
+ return {
237
+ provide: LEAVEN_MODULE_OPTIONS,
238
+ useFactory: options.useFactory,
239
+ inject: options.inject ?? []
240
+ };
241
+ }
242
+ const inject = [
243
+ options.useClass ?? options.useExisting
244
+ ];
245
+ return {
246
+ provide: LEAVEN_MODULE_OPTIONS,
247
+ useFactory: async (optionsFactory) => optionsFactory.createLeavenOptions(),
248
+ inject
249
+ };
250
+ }
251
+ }
252
+ LeavenModule = __legacyDecorateClassTS([
253
+ Global(),
254
+ Module({})
255
+ ], LeavenModule);
256
+ // src/decorators.ts
257
+ import {
258
+ createParamDecorator,
259
+ SetMetadata,
260
+ applyDecorators
261
+ } from "@nestjs/common";
262
+ var COMPLEXITY_KEY = "leaven:complexity";
263
+ var DEPRECATED_KEY = "leaven:deprecated";
264
+ var DESCRIPTION_KEY = "leaven:description";
265
+ var CACHE_KEY = "leaven:cache";
266
+ var SUBSCRIPTION_FILTER_KEY = "leaven:subscription:filter";
267
+ var Context = createParamDecorator((data, ctx) => {
268
+ const gqlContext = getGqlContext(ctx);
269
+ if (data) {
270
+ return gqlContext[data];
271
+ }
272
+ return gqlContext;
273
+ });
274
+ var Info = createParamDecorator((_data, ctx) => {
275
+ const args = ctx.getArgs();
276
+ return args[3];
277
+ });
278
+ var Root = createParamDecorator((_data, ctx) => {
279
+ const args = ctx.getArgs();
280
+ return args[0];
281
+ });
282
+ var Parent = Root;
283
+ var Args = createParamDecorator((data, ctx) => {
284
+ const args = ctx.getArgs();
285
+ const gqlArgs = args[1];
286
+ if (data) {
287
+ return gqlArgs?.[data];
288
+ }
289
+ return gqlArgs;
290
+ });
291
+ function Complexity(value) {
292
+ return SetMetadata(COMPLEXITY_KEY, value);
293
+ }
294
+ function Deprecated(reason) {
295
+ return SetMetadata(DEPRECATED_KEY, reason);
296
+ }
297
+ function Description(text) {
298
+ return SetMetadata(DESCRIPTION_KEY, text);
299
+ }
300
+ function CacheHint(options) {
301
+ return SetMetadata(CACHE_KEY, options);
302
+ }
303
+ function SubscriptionFilter(filter) {
304
+ return SetMetadata(SUBSCRIPTION_FILTER_KEY, filter);
305
+ }
306
+ function Decorators(...decorators) {
307
+ return applyDecorators(...decorators);
308
+ }
309
+ function getGqlContext(ctx) {
310
+ const args = ctx.getArgs();
311
+ return args[2];
312
+ }
313
+ function createContextDecorator(key) {
314
+ return () => createParamDecorator((_data, ctx) => {
315
+ const gqlContext = getGqlContext(ctx);
316
+ return gqlContext[key];
317
+ })();
318
+ }
319
+ // src/guards.ts
320
+ import {
321
+ Injectable as Injectable2,
322
+ UnauthorizedException,
323
+ ForbiddenException,
324
+ SetMetadata as SetMetadata2,
325
+ Inject
326
+ } from "@nestjs/common";
327
+ import { Reflector } from "@nestjs/core";
328
+ var IS_PUBLIC_KEY = "leaven:isPublic";
329
+ var ROLES_KEY = "leaven:roles";
330
+ var PERMISSIONS_KEY = "leaven:permissions";
331
+ var Public = () => SetMetadata2(IS_PUBLIC_KEY, true);
332
+ var Roles = (...roles) => SetMetadata2(ROLES_KEY, roles);
333
+ var Permissions = (...permissions) => SetMetadata2(PERMISSIONS_KEY, permissions);
334
+
335
+ class AuthGuard {
336
+ reflector;
337
+ constructor(reflector) {
338
+ this.reflector = reflector;
339
+ }
340
+ canActivate(context) {
341
+ const isPublic = this.reflector.getAllAndOverride(IS_PUBLIC_KEY, [
342
+ context.getHandler(),
343
+ context.getClass()
344
+ ]);
345
+ if (isPublic) {
346
+ return true;
347
+ }
348
+ const gqlContext = this.getGqlContext(context);
349
+ const user = this.extractUser(gqlContext);
350
+ if (!user) {
351
+ throw new UnauthorizedException("Authentication required");
352
+ }
353
+ return true;
354
+ }
355
+ getGqlContext(context) {
356
+ const args = context.getArgs();
357
+ return args[2];
358
+ }
359
+ extractUser(context) {
360
+ return context.user ?? context.req?.user ?? null;
361
+ }
362
+ }
363
+ AuthGuard = __legacyDecorateClassTS([
364
+ Injectable2(),
365
+ __legacyMetadataTS("design:paramtypes", [
366
+ typeof Reflector === "undefined" ? Object : Reflector
367
+ ])
368
+ ], AuthGuard);
369
+
370
+ class RolesGuard {
371
+ reflector;
372
+ constructor(reflector) {
373
+ this.reflector = reflector;
374
+ }
375
+ canActivate(context) {
376
+ const requiredRoles = this.reflector.getAllAndOverride(ROLES_KEY, [
377
+ context.getHandler(),
378
+ context.getClass()
379
+ ]);
380
+ if (!requiredRoles || requiredRoles.length === 0) {
381
+ return true;
382
+ }
383
+ const gqlContext = this.getGqlContext(context);
384
+ const user = this.extractUser(gqlContext);
385
+ if (!user) {
386
+ throw new UnauthorizedException("Authentication required");
387
+ }
388
+ const userRoles = this.extractRoles(user);
389
+ const hasRole = requiredRoles.some((role) => userRoles.includes(role));
390
+ if (!hasRole) {
391
+ throw new ForbiddenException(`Required roles: ${requiredRoles.join(", ")}`);
392
+ }
393
+ return true;
394
+ }
395
+ getGqlContext(context) {
396
+ const args = context.getArgs();
397
+ return args[2];
398
+ }
399
+ extractUser(context) {
400
+ return context.user ?? context.req?.user ?? null;
401
+ }
402
+ extractRoles(user) {
403
+ const userObj = user;
404
+ const roles = userObj?.roles ?? userObj?.role;
405
+ if (Array.isArray(roles)) {
406
+ return roles.map(String);
407
+ }
408
+ if (typeof roles === "string") {
409
+ return [roles];
410
+ }
411
+ return [];
412
+ }
413
+ }
414
+ RolesGuard = __legacyDecorateClassTS([
415
+ Injectable2(),
416
+ __legacyMetadataTS("design:paramtypes", [
417
+ typeof Reflector === "undefined" ? Object : Reflector
418
+ ])
419
+ ], RolesGuard);
420
+
421
+ class PermissionsGuard {
422
+ reflector;
423
+ constructor(reflector) {
424
+ this.reflector = reflector;
425
+ }
426
+ canActivate(context) {
427
+ const requiredPermissions = this.reflector.getAllAndOverride(PERMISSIONS_KEY, [context.getHandler(), context.getClass()]);
428
+ if (!requiredPermissions || requiredPermissions.length === 0) {
429
+ return true;
430
+ }
431
+ const gqlContext = this.getGqlContext(context);
432
+ const user = this.extractUser(gqlContext);
433
+ if (!user) {
434
+ throw new UnauthorizedException("Authentication required");
435
+ }
436
+ const userPermissions = this.extractPermissions(user);
437
+ const hasAllPermissions = requiredPermissions.every((permission) => userPermissions.includes(permission));
438
+ if (!hasAllPermissions) {
439
+ throw new ForbiddenException(`Required permissions: ${requiredPermissions.join(", ")}`);
440
+ }
441
+ return true;
442
+ }
443
+ getGqlContext(context) {
444
+ const args = context.getArgs();
445
+ return args[2];
446
+ }
447
+ extractUser(context) {
448
+ return context.user ?? context.req?.user ?? null;
449
+ }
450
+ extractPermissions(user) {
451
+ const userObj = user;
452
+ const permissions = userObj?.permissions ?? userObj?.scopes;
453
+ if (Array.isArray(permissions)) {
454
+ return permissions.map(String);
455
+ }
456
+ if (typeof permissions === "string") {
457
+ return permissions.split(" ");
458
+ }
459
+ return [];
460
+ }
461
+ }
462
+ PermissionsGuard = __legacyDecorateClassTS([
463
+ Injectable2(),
464
+ __legacyMetadataTS("design:paramtypes", [
465
+ typeof Reflector === "undefined" ? Object : Reflector
466
+ ])
467
+ ], PermissionsGuard);
468
+
469
+ class ComplexityGuard {
470
+ maxComplexity;
471
+ constructor(maxComplexity) {
472
+ this.maxComplexity = maxComplexity;
473
+ }
474
+ canActivate(context) {
475
+ const gqlContext = this.getGqlContext(context);
476
+ const currentComplexity = gqlContext._queryComplexity;
477
+ if (currentComplexity !== undefined && currentComplexity > this.maxComplexity) {
478
+ throw new ForbiddenException(`Query complexity ${currentComplexity} exceeds maximum allowed ${this.maxComplexity}`);
479
+ }
480
+ return true;
481
+ }
482
+ getGqlContext(context) {
483
+ const args = context.getArgs();
484
+ return args[2];
485
+ }
486
+ }
487
+ ComplexityGuard = __legacyDecorateClassTS([
488
+ Injectable2(),
489
+ __legacyDecorateParamTS(0, Inject("LEAVEN_MAX_COMPLEXITY")),
490
+ __legacyMetadataTS("design:paramtypes", [
491
+ Number
492
+ ])
493
+ ], ComplexityGuard);
494
+
495
+ class DepthGuard {
496
+ maxDepth;
497
+ constructor(maxDepth) {
498
+ this.maxDepth = maxDepth;
499
+ }
500
+ canActivate(context) {
501
+ const gqlContext = this.getGqlContext(context);
502
+ const currentDepth = gqlContext._queryDepth;
503
+ if (currentDepth !== undefined && currentDepth > this.maxDepth) {
504
+ throw new ForbiddenException(`Query depth ${currentDepth} exceeds maximum allowed ${this.maxDepth}`);
505
+ }
506
+ return true;
507
+ }
508
+ getGqlContext(context) {
509
+ const args = context.getArgs();
510
+ return args[2];
511
+ }
512
+ }
513
+ DepthGuard = __legacyDecorateClassTS([
514
+ Injectable2(),
515
+ __legacyDecorateParamTS(0, Inject("LEAVEN_MAX_DEPTH")),
516
+ __legacyMetadataTS("design:paramtypes", [
517
+ Number
518
+ ])
519
+ ], DepthGuard);
520
+ // src/interceptors.ts
521
+ import {
522
+ Injectable as Injectable3
523
+ } from "@nestjs/common";
524
+ import { tap, catchError, throwError } from "rxjs";
525
+ import { Reflector as Reflector2 } from "@nestjs/core";
526
+ class LoggingInterceptor {
527
+ intercept(context, next) {
528
+ const className = context.getClass().name;
529
+ const methodName = context.getHandler().name;
530
+ const start = performance.now();
531
+ return next.handle().pipe(tap(() => {
532
+ const duration = (performance.now() - start).toFixed(2);
533
+ console.log(`[GraphQL] ${className}.${methodName} - ${duration}ms`);
534
+ }));
535
+ }
536
+ }
537
+ LoggingInterceptor = __legacyDecorateClassTS([
538
+ Injectable3()
539
+ ], LoggingInterceptor);
540
+
541
+ class ErrorFormattingInterceptor {
542
+ intercept(_context, next) {
543
+ return next.handle().pipe(catchError((error) => {
544
+ const formattedError = this.formatError(error);
545
+ return throwError(() => formattedError);
546
+ }));
547
+ }
548
+ formatError(error) {
549
+ if (error instanceof Error) {
550
+ if (false) {}
551
+ return error;
552
+ }
553
+ return new Error(String(error));
554
+ }
555
+ }
556
+ ErrorFormattingInterceptor = __legacyDecorateClassTS([
557
+ Injectable3()
558
+ ], ErrorFormattingInterceptor);
559
+
560
+ class ComplexityInterceptor {
561
+ reflector;
562
+ constructor(reflector) {
563
+ this.reflector = reflector;
564
+ }
565
+ intercept(context, next) {
566
+ const complexity = this.reflector.get(COMPLEXITY_KEY, context.getHandler());
567
+ if (complexity !== undefined) {
568
+ const args = context.getArgs();
569
+ const gqlContext = args[2];
570
+ if (gqlContext) {
571
+ gqlContext._fieldComplexity = complexity;
572
+ }
573
+ }
574
+ return next.handle();
575
+ }
576
+ }
577
+ ComplexityInterceptor = __legacyDecorateClassTS([
578
+ Injectable3(),
579
+ __legacyMetadataTS("design:paramtypes", [
580
+ typeof Reflector2 === "undefined" ? Object : Reflector2
581
+ ])
582
+ ], ComplexityInterceptor);
583
+
584
+ class CachingInterceptor {
585
+ reflector;
586
+ constructor(reflector) {
587
+ this.reflector = reflector;
588
+ }
589
+ intercept(context, next) {
590
+ const cacheHint = this.reflector.get(CACHE_KEY, context.getHandler());
591
+ return next.handle().pipe(tap(() => {
592
+ if (cacheHint) {
593
+ const args = context.getArgs();
594
+ const gqlInfo = args[3];
595
+ if (gqlInfo?.cacheControl) {
596
+ gqlInfo.cacheControl.setCacheHint({
597
+ maxAge: cacheHint.maxAge,
598
+ scope: cacheHint.scope
599
+ });
600
+ }
601
+ }
602
+ }));
603
+ }
604
+ }
605
+ CachingInterceptor = __legacyDecorateClassTS([
606
+ Injectable3(),
607
+ __legacyMetadataTS("design:paramtypes", [
608
+ typeof Reflector2 === "undefined" ? Object : Reflector2
609
+ ])
610
+ ], CachingInterceptor);
611
+
612
+ class MetricsInterceptor {
613
+ metrics = new Map;
614
+ intercept(context, next) {
615
+ const className = context.getClass().name;
616
+ const methodName = context.getHandler().name;
617
+ const key = `${className}.${methodName}`;
618
+ const start = performance.now();
619
+ return next.handle().pipe(tap(() => {
620
+ const duration = performance.now() - start;
621
+ this.recordSuccess(key, duration);
622
+ }), catchError((error) => {
623
+ const duration = performance.now() - start;
624
+ this.recordError(key, duration);
625
+ return throwError(() => error);
626
+ }));
627
+ }
628
+ recordSuccess(key, duration) {
629
+ const metrics = this.getOrCreateMetrics(key);
630
+ metrics.totalCount++;
631
+ metrics.successCount++;
632
+ metrics.totalDuration += duration;
633
+ metrics.lastDuration = duration;
634
+ metrics.minDuration = Math.min(metrics.minDuration, duration);
635
+ metrics.maxDuration = Math.max(metrics.maxDuration, duration);
636
+ }
637
+ recordError(key, duration) {
638
+ const metrics = this.getOrCreateMetrics(key);
639
+ metrics.totalCount++;
640
+ metrics.errorCount++;
641
+ metrics.totalDuration += duration;
642
+ metrics.lastDuration = duration;
643
+ }
644
+ getOrCreateMetrics(key) {
645
+ let metrics = this.metrics.get(key);
646
+ if (!metrics) {
647
+ metrics = {
648
+ totalCount: 0,
649
+ successCount: 0,
650
+ errorCount: 0,
651
+ totalDuration: 0,
652
+ lastDuration: 0,
653
+ minDuration: Infinity,
654
+ maxDuration: 0
655
+ };
656
+ this.metrics.set(key, metrics);
657
+ }
658
+ return metrics;
659
+ }
660
+ getMetrics() {
661
+ return new Map(this.metrics);
662
+ }
663
+ getSummary() {
664
+ return Array.from(this.metrics.entries()).map(([operation, metrics]) => ({
665
+ operation,
666
+ ...metrics,
667
+ averageDuration: metrics.totalCount > 0 ? metrics.totalDuration / metrics.totalCount : 0,
668
+ errorRate: metrics.totalCount > 0 ? metrics.errorCount / metrics.totalCount : 0
669
+ }));
670
+ }
671
+ reset() {
672
+ this.metrics.clear();
673
+ }
674
+ }
675
+ MetricsInterceptor = __legacyDecorateClassTS([
676
+ Injectable3()
677
+ ], MetricsInterceptor);
678
+ // src/middleware.ts
679
+ import {
680
+ Injectable as Injectable4,
681
+ Inject as Inject2
682
+ } from "@nestjs/common";
683
+ class GraphQLMiddleware {
684
+ driver;
685
+ options;
686
+ constructor(driver, options) {
687
+ this.driver = driver;
688
+ this.options = options;
689
+ }
690
+ async use(req, res, next) {
691
+ const path = this.driver.getPath();
692
+ if (!req.path.endsWith(path)) {
693
+ return next();
694
+ }
695
+ if (req.method === "GET" && this.driver.isPlaygroundEnabled()) {
696
+ this.servePlayground(res);
697
+ return;
698
+ }
699
+ if (req.method === "POST") {
700
+ await this.handleGraphQLRequest(req, res);
701
+ return;
702
+ }
703
+ if (req.method === "OPTIONS") {
704
+ this.handleCorsPreFlight(res);
705
+ return;
706
+ }
707
+ next();
708
+ }
709
+ async handleGraphQLRequest(req, res) {
710
+ try {
711
+ const { query, variables, operationName } = req.body ?? {};
712
+ if (!query) {
713
+ res.status(400).json({
714
+ errors: [{ message: "Query is required" }]
715
+ });
716
+ return;
717
+ }
718
+ const context = {
719
+ req,
720
+ res,
721
+ ...this.options.context ? await this.options.context(req, res) : {}
722
+ };
723
+ const result = await this.driver.execute(query, variables, context, operationName);
724
+ const status = result.errors?.length ? 200 : 200;
725
+ res.status(status).json(result);
726
+ } catch (error) {
727
+ const message = error instanceof Error ? error.message : "Internal server error";
728
+ res.status(500).json({
729
+ errors: [{ message }]
730
+ });
731
+ }
732
+ }
733
+ servePlayground(res) {
734
+ const path = this.driver.getPath();
735
+ const html = this.getPlaygroundHtml(path);
736
+ res.setHeader("Content-Type", "text/html");
737
+ res.send(html);
738
+ }
739
+ handleCorsPreFlight(res) {
740
+ const corsOptions = this.options.cors;
741
+ if (corsOptions === false) {
742
+ res.status(204).end();
743
+ return;
744
+ }
745
+ const options = corsOptions === true ? {} : corsOptions ?? {};
746
+ res.setHeader("Access-Control-Allow-Origin", Array.isArray(options.origin) ? options.origin.join(", ") : typeof options.origin === "string" ? options.origin : "*");
747
+ res.setHeader("Access-Control-Allow-Methods", Array.isArray(options.methods) ? options.methods.join(", ") : options.methods ?? "GET, POST, OPTIONS");
748
+ res.setHeader("Access-Control-Allow-Headers", Array.isArray(options.allowedHeaders) ? options.allowedHeaders.join(", ") : options.allowedHeaders ?? "Content-Type, Authorization");
749
+ if (options.credentials) {
750
+ res.setHeader("Access-Control-Allow-Credentials", "true");
751
+ }
752
+ if (options.maxAge) {
753
+ res.setHeader("Access-Control-Max-Age", String(options.maxAge));
754
+ }
755
+ res.status(204).end();
756
+ }
757
+ getPlaygroundHtml(endpoint) {
758
+ return `
759
+ <!DOCTYPE html>
760
+ <html lang="en">
761
+ <head>
762
+ <meta charset="UTF-8">
763
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
764
+ <title>Leaven GraphQL Playground</title>
765
+ <link rel="stylesheet" href="https://unpkg.com/graphiql@3.0.9/graphiql.min.css" />
766
+ <style>
767
+ body { margin: 0; height: 100vh; }
768
+ #graphiql { height: 100vh; }
769
+ </style>
770
+ </head>
771
+ <body>
772
+ <div id="graphiql">Loading...</div>
773
+ <script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
774
+ <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
775
+ <script crossorigin src="https://unpkg.com/graphiql@3.0.9/graphiql.min.js"></script>
776
+ <script>
777
+ const fetcher = GraphiQL.createFetcher({ url: '${endpoint}' });
778
+ ReactDOM.createRoot(document.getElementById('graphiql')).render(
779
+ React.createElement(GraphiQL, { fetcher })
780
+ );
781
+ </script>
782
+ </body>
783
+ </html>
784
+ `.trim();
785
+ }
786
+ }
787
+ GraphQLMiddleware = __legacyDecorateClassTS([
788
+ Injectable4(),
789
+ __legacyDecorateParamTS(0, Inject2(LEAVEN_DRIVER)),
790
+ __legacyDecorateParamTS(1, Inject2(LEAVEN_MODULE_OPTIONS)),
791
+ __legacyMetadataTS("design:paramtypes", [
792
+ typeof LeavenDriver === "undefined" ? Object : LeavenDriver,
793
+ typeof LeavenModuleOptions === "undefined" ? Object : LeavenModuleOptions
794
+ ])
795
+ ], GraphQLMiddleware);
796
+ function createGraphQLMiddleware() {
797
+ return GraphQLMiddleware;
798
+ }
799
+ // src/execution-context.ts
800
+ class GqlExecutionContext {
801
+ host;
802
+ args;
803
+ constructor(host) {
804
+ this.host = host;
805
+ this.args = host.getArgs();
806
+ }
807
+ static create(context) {
808
+ return new GqlExecutionContext(context);
809
+ }
810
+ getContext() {
811
+ return this.args[2];
812
+ }
813
+ getRoot() {
814
+ return this.args[0];
815
+ }
816
+ getArgs() {
817
+ return this.args[1];
818
+ }
819
+ getInfo() {
820
+ return this.args[3];
821
+ }
822
+ getArg(name) {
823
+ const args = this.getArgs();
824
+ return args[name];
825
+ }
826
+ getContextProperty(name) {
827
+ const ctx = this.getContext();
828
+ return ctx[name];
829
+ }
830
+ getHost() {
831
+ return this.host;
832
+ }
833
+ getType() {
834
+ return this.host.getType?.() ?? "unknown";
835
+ }
836
+ isGraphQL() {
837
+ return this.getType() === "graphql";
838
+ }
839
+ getHandler() {
840
+ return this.host.getHandler?.();
841
+ }
842
+ getClass() {
843
+ return this.host.getClass?.();
844
+ }
845
+ getFieldName() {
846
+ const info = this.getInfo();
847
+ return info?.fieldName;
848
+ }
849
+ getParentTypeName() {
850
+ const info = this.getInfo();
851
+ return info?.parentType?.name;
852
+ }
853
+ getReturnTypeName() {
854
+ const info = this.getInfo();
855
+ return info?.returnType?.toString();
856
+ }
857
+ getOperationName() {
858
+ const info = this.getInfo();
859
+ return info?.operation?.name?.value;
860
+ }
861
+ getOperationType() {
862
+ const info = this.getInfo();
863
+ return info?.operation?.operation;
864
+ }
865
+ getVariables() {
866
+ const info = this.getInfo();
867
+ return info?.variableValues ?? {};
868
+ }
869
+ getPath() {
870
+ const info = this.getInfo();
871
+ if (!info?.path)
872
+ return [];
873
+ const path = [];
874
+ let current = info.path;
875
+ while (current) {
876
+ if (typeof current.key === "string") {
877
+ path.unshift(current.key);
878
+ }
879
+ current = current.prev;
880
+ }
881
+ return path;
882
+ }
883
+ getSelectedFields() {
884
+ const info = this.getInfo();
885
+ if (!info?.fieldNodes?.[0]?.selectionSet?.selections)
886
+ return [];
887
+ return info.fieldNodes[0].selectionSet.selections.filter((selection) => selection.kind === "Field").map((field) => field.name.value);
888
+ }
889
+ }
890
+ function getGqlContext2(context) {
891
+ return GqlExecutionContext.create(context).getContext();
892
+ }
893
+ function getGqlArgs(context) {
894
+ return GqlExecutionContext.create(context).getArgs();
895
+ }
896
+ function getGqlInfo(context) {
897
+ return GqlExecutionContext.create(context).getInfo();
898
+ }
899
+ function getGqlRoot(context) {
900
+ return GqlExecutionContext.create(context).getRoot();
901
+ }
902
+ // src/schema-builder.ts
903
+ import { Injectable as Injectable5, Inject as Inject3 } from "@nestjs/common";
904
+ class SchemaBuilderService {
905
+ options;
906
+ driver;
907
+ builtSchema = null;
908
+ constructor(options, driver) {
909
+ this.options = options;
910
+ this.driver = driver;
911
+ }
912
+ async onModuleInit() {
913
+ const schema = await this.buildSchema();
914
+ if (schema) {
915
+ this.builtSchema = schema;
916
+ this.driver.setSchema(schema);
917
+ }
918
+ }
919
+ async buildSchema() {
920
+ if (this.options.schema) {
921
+ return this.options.schema;
922
+ }
923
+ if (this.options.typeDefs) {
924
+ return this.buildFromTypeDefs();
925
+ }
926
+ if (this.options.autoSchemaFile) {
927
+ return null;
928
+ }
929
+ return null;
930
+ }
931
+ buildFromTypeDefs() {
932
+ const { typeDefs, resolvers } = this.options;
933
+ if (!typeDefs || !resolvers) {
934
+ return null;
935
+ }
936
+ try {
937
+ const { buildSchema } = __require("graphql");
938
+ const { addResolversToSchema, makeExecutableSchema } = (()=>{throw new Error("Cannot require module "+"@graphql-tools/schema");})();
939
+ if (makeExecutableSchema) {
940
+ return makeExecutableSchema({
941
+ typeDefs,
942
+ resolvers
943
+ });
944
+ }
945
+ const schema = buildSchema(typeof typeDefs === "string" ? typeDefs : this.mergeTypeDefs(typeDefs));
946
+ if (addResolversToSchema) {
947
+ return addResolversToSchema({
948
+ schema,
949
+ resolvers: Array.isArray(resolvers) ? this.mergeResolvers(resolvers) : resolvers
950
+ });
951
+ }
952
+ return schema;
953
+ } catch (error) {
954
+ console.warn("Failed to build schema from typeDefs. Install @graphql-tools/schema for full support.", error);
955
+ return null;
956
+ }
957
+ }
958
+ mergeTypeDefs(typeDefs) {
959
+ if (typeof typeDefs === "string") {
960
+ return typeDefs;
961
+ }
962
+ if (Array.isArray(typeDefs)) {
963
+ return typeDefs.map((td) => {
964
+ if (typeof td === "string") {
965
+ return td;
966
+ }
967
+ const { print: print2 } = __require("graphql");
968
+ return print2(td);
969
+ }).join(`
970
+ `);
971
+ }
972
+ const { print } = __require("graphql");
973
+ return print(typeDefs);
974
+ }
975
+ mergeResolvers(resolvers) {
976
+ return resolvers.reduce((merged, resolver) => {
977
+ for (const [typeName, fields] of Object.entries(resolver)) {
978
+ merged[typeName] = {
979
+ ...merged[typeName] || {},
980
+ ...fields
981
+ };
982
+ }
983
+ return merged;
984
+ }, {});
985
+ }
986
+ getSchema() {
987
+ return this.builtSchema;
988
+ }
989
+ setSchema(schema) {
990
+ this.builtSchema = schema;
991
+ this.driver.setSchema(schema);
992
+ }
993
+ isSchemaReady() {
994
+ return this.builtSchema !== null;
995
+ }
996
+ }
997
+ SchemaBuilderService = __legacyDecorateClassTS([
998
+ Injectable5(),
999
+ __legacyDecorateParamTS(0, Inject3(LEAVEN_MODULE_OPTIONS)),
1000
+ __legacyDecorateParamTS(1, Inject3(LEAVEN_DRIVER)),
1001
+ __legacyMetadataTS("design:paramtypes", [
1002
+ typeof LeavenModuleOptions === "undefined" ? Object : LeavenModuleOptions,
1003
+ typeof LeavenDriver === "undefined" ? Object : LeavenDriver
1004
+ ])
1005
+ ], SchemaBuilderService);
1006
+ async function generateSchemaFile(schema, options) {
1007
+ const { printSchema, lexicographicSortSchema } = __require("graphql");
1008
+ const { writeFile } = __require("fs/promises");
1009
+ let schemaToWrite = schema;
1010
+ if (options.sortSchema) {
1011
+ schemaToWrite = lexicographicSortSchema(schema);
1012
+ }
1013
+ const sdl = printSchema(schemaToWrite);
1014
+ await writeFile(options.path, sdl, "utf-8");
1015
+ }
1016
+ var resolverMetadataStorage = [];
1017
+ function getResolverMetadata() {
1018
+ return [...resolverMetadataStorage];
1019
+ }
1020
+ function registerResolverMetadata(metadata) {
1021
+ resolverMetadataStorage.push(metadata);
1022
+ }
1023
+ function clearResolverMetadata() {
1024
+ resolverMetadataStorage.length = 0;
1025
+ }
1026
+ // src/subscriptions.ts
1027
+ import { Injectable as Injectable6, Inject as Inject4 } from "@nestjs/common";
1028
+ class SubscriptionManager {
1029
+ options;
1030
+ driver;
1031
+ connections = new Map;
1032
+ config;
1033
+ constructor(options, driver) {
1034
+ this.options = options;
1035
+ this.driver = driver;
1036
+ this.config = this.normalizeConfig();
1037
+ }
1038
+ async onModuleInit() {}
1039
+ async onModuleDestroy() {
1040
+ for (const [socket] of this.connections) {
1041
+ socket.close(1000, "Server shutdown");
1042
+ }
1043
+ this.connections.clear();
1044
+ }
1045
+ normalizeConfig() {
1046
+ return {
1047
+ path: this.options.path ?? "/graphql",
1048
+ keepAlive: 12000,
1049
+ connectionInitWaitTimeout: 3000,
1050
+ maxSubscriptionsPerConnection: 100
1051
+ };
1052
+ }
1053
+ async handleConnection(socket, request) {
1054
+ const state = {
1055
+ socket,
1056
+ request,
1057
+ subscriptions: new Map,
1058
+ initialized: false,
1059
+ connectionParams: undefined,
1060
+ context: undefined,
1061
+ keepAliveInterval: undefined
1062
+ };
1063
+ this.connections.set(socket, state);
1064
+ socket.addEventListener("message", (event) => {
1065
+ this.handleMessage(socket, event.data);
1066
+ });
1067
+ socket.addEventListener("close", () => {
1068
+ this.handleClose(socket);
1069
+ });
1070
+ socket.addEventListener("error", (error) => {
1071
+ this.handleError(socket, error);
1072
+ });
1073
+ setTimeout(() => {
1074
+ if (!state.initialized) {
1075
+ socket.close(4408, "Connection initialization timeout");
1076
+ }
1077
+ }, this.config.connectionInitWaitTimeout);
1078
+ }
1079
+ async handleMessage(socket, data) {
1080
+ const state = this.connections.get(socket);
1081
+ if (!state)
1082
+ return;
1083
+ try {
1084
+ const message = JSON.parse(data);
1085
+ switch (message.type) {
1086
+ case "connection_init":
1087
+ await this.handleConnectionInit(socket, state, message);
1088
+ break;
1089
+ case "ping":
1090
+ this.send(socket, { type: "pong" });
1091
+ break;
1092
+ case "pong":
1093
+ break;
1094
+ case "subscribe":
1095
+ await this.handleSubscribe(socket, state, message);
1096
+ break;
1097
+ case "complete":
1098
+ await this.handleComplete(socket, state, message);
1099
+ break;
1100
+ default:
1101
+ break;
1102
+ }
1103
+ } catch (error) {
1104
+ console.error("Error handling WebSocket message:", error);
1105
+ }
1106
+ }
1107
+ async handleConnectionInit(socket, state, message) {
1108
+ if (state.initialized) {
1109
+ socket.close(4429, "Too many initialization requests");
1110
+ return;
1111
+ }
1112
+ state.connectionParams = message.payload ?? {};
1113
+ const ctx = {
1114
+ socket,
1115
+ connectionParams: message.payload ?? {},
1116
+ request: state.request
1117
+ };
1118
+ if (this.config.onConnect) {
1119
+ const allowed = await this.config.onConnect(ctx);
1120
+ if (!allowed) {
1121
+ socket.close(4401, "Unauthorized");
1122
+ return;
1123
+ }
1124
+ }
1125
+ state.initialized = true;
1126
+ state.context = ctx;
1127
+ this.send(socket, { type: "connection_ack" });
1128
+ if (this.config.keepAlive) {
1129
+ state.keepAliveInterval = setInterval(() => {
1130
+ this.send(socket, { type: "ping" });
1131
+ }, this.config.keepAlive);
1132
+ }
1133
+ }
1134
+ async handleSubscribe(socket, state, message) {
1135
+ if (!state.initialized) {
1136
+ socket.close(4401, "Unauthorized");
1137
+ return;
1138
+ }
1139
+ if (state.subscriptions.size >= (this.config.maxSubscriptionsPerConnection ?? 100)) {
1140
+ this.sendError(socket, message.id, "Too many subscriptions");
1141
+ return;
1142
+ }
1143
+ if (this.config.onSubscribe && state.context) {
1144
+ await this.config.onSubscribe(state.context, message);
1145
+ }
1146
+ try {
1147
+ const schema = this.driver.getSchema();
1148
+ if (!schema) {
1149
+ this.sendError(socket, message.id, "Schema not available");
1150
+ return;
1151
+ }
1152
+ let context = {
1153
+ req: state.request,
1154
+ res: {},
1155
+ ...state.connectionParams
1156
+ };
1157
+ if (this.config.context && state.context) {
1158
+ context = await this.config.context(state.context);
1159
+ }
1160
+ const result = await this.driver.execute(message.payload.query, message.payload.variables, context, message.payload.operationName);
1161
+ if (result.data || result.errors) {
1162
+ this.send(socket, {
1163
+ id: message.id,
1164
+ type: "next",
1165
+ payload: result
1166
+ });
1167
+ this.send(socket, { id: message.id, type: "complete" });
1168
+ return;
1169
+ }
1170
+ state.subscriptions.set(message.id, {
1171
+ message,
1172
+ context
1173
+ });
1174
+ } catch (error) {
1175
+ const errorMessage = error instanceof Error ? error.message : "Subscription failed";
1176
+ this.sendError(socket, message.id, errorMessage);
1177
+ }
1178
+ }
1179
+ async handleComplete(socket, state, message) {
1180
+ const subscription = state.subscriptions.get(message.id);
1181
+ if (subscription) {
1182
+ state.subscriptions.delete(message.id);
1183
+ if (this.config.onComplete && state.context) {
1184
+ await this.config.onComplete(state.context, message);
1185
+ }
1186
+ }
1187
+ }
1188
+ async handleClose(ws) {
1189
+ const state = this.connections.get(ws);
1190
+ if (!state)
1191
+ return;
1192
+ if (state.keepAliveInterval) {
1193
+ clearInterval(state.keepAliveInterval);
1194
+ }
1195
+ if (this.config.onDisconnect && state.context) {
1196
+ await this.config.onDisconnect(state.context);
1197
+ }
1198
+ state.subscriptions.clear();
1199
+ this.connections.delete(ws);
1200
+ }
1201
+ handleError(ws, _error) {
1202
+ const state = this.connections.get(ws);
1203
+ if (state) {
1204
+ this.handleClose(ws);
1205
+ }
1206
+ }
1207
+ send(socket, message) {
1208
+ if (socket.readyState === WebSocket.OPEN) {
1209
+ socket.send(JSON.stringify(message));
1210
+ }
1211
+ }
1212
+ sendError(socket, id, message) {
1213
+ this.send(socket, {
1214
+ id,
1215
+ type: "error",
1216
+ payload: [{ message }]
1217
+ });
1218
+ }
1219
+ publish(id, payload) {
1220
+ for (const [socket, state] of this.connections) {
1221
+ const subscription = state.subscriptions.get(id);
1222
+ if (subscription) {
1223
+ this.send(socket, {
1224
+ id,
1225
+ type: "next",
1226
+ payload: { data: payload }
1227
+ });
1228
+ }
1229
+ }
1230
+ }
1231
+ getConnectionCount() {
1232
+ return this.connections.size;
1233
+ }
1234
+ getSubscriptionCount() {
1235
+ let count = 0;
1236
+ for (const [_, state] of this.connections) {
1237
+ count += state.subscriptions.size;
1238
+ }
1239
+ return count;
1240
+ }
1241
+ }
1242
+ SubscriptionManager = __legacyDecorateClassTS([
1243
+ Injectable6(),
1244
+ __legacyDecorateParamTS(0, Inject4(LEAVEN_MODULE_OPTIONS)),
1245
+ __legacyDecorateParamTS(1, Inject4(LEAVEN_DRIVER)),
1246
+ __legacyMetadataTS("design:paramtypes", [
1247
+ typeof LeavenModuleOptions === "undefined" ? Object : LeavenModuleOptions,
1248
+ typeof LeavenDriver === "undefined" ? Object : LeavenDriver
1249
+ ])
1250
+ ], SubscriptionManager);
1251
+ function Subscription(returns, options) {
1252
+ return (_target, _propertyKey, descriptor) => {
1253
+ if (descriptor.value) {
1254
+ Reflect.defineMetadata("leaven:subscription", { returns, options }, descriptor.value);
1255
+ }
1256
+ return descriptor;
1257
+ };
1258
+ }
1259
+ function InjectPubSub() {
1260
+ return (_target, propertyKey, parameterIndex) => {
1261
+ if (_target && propertyKey !== undefined) {
1262
+ const existingParams = Reflect.getMetadata("leaven:inject:pubsub", _target, propertyKey) || [];
1263
+ existingParams.push(parameterIndex);
1264
+ Reflect.defineMetadata("leaven:inject:pubsub", existingParams, _target, propertyKey);
1265
+ }
1266
+ };
1267
+ }
1268
+ export {
1269
+ registerResolverMetadata,
1270
+ getResolverMetadata,
1271
+ getGqlRoot,
1272
+ getGqlInfo,
1273
+ getGqlContext2 as getGqlContext,
1274
+ getGqlArgs,
1275
+ generateSchemaFile,
1276
+ createGraphQLMiddleware,
1277
+ createContextDecorator,
1278
+ clearResolverMetadata,
1279
+ SubscriptionManager,
1280
+ SubscriptionFilter,
1281
+ Subscription,
1282
+ SchemaBuilderService,
1283
+ SUBSCRIPTION_FILTER_KEY,
1284
+ Root,
1285
+ RolesGuard,
1286
+ Roles,
1287
+ ROLES_KEY,
1288
+ Public,
1289
+ PermissionsGuard,
1290
+ Permissions,
1291
+ Parent,
1292
+ PERMISSIONS_KEY,
1293
+ MetricsInterceptor,
1294
+ LoggingInterceptor,
1295
+ LeavenModule,
1296
+ LeavenDriver,
1297
+ LEAVEN_MODULE_OPTIONS,
1298
+ LEAVEN_DRIVER,
1299
+ InjectPubSub,
1300
+ Info,
1301
+ IS_PUBLIC_KEY,
1302
+ GraphQLMiddleware,
1303
+ GqlExecutionContext,
1304
+ ErrorFormattingInterceptor,
1305
+ Description,
1306
+ DepthGuard,
1307
+ Deprecated,
1308
+ Decorators,
1309
+ DESCRIPTION_KEY,
1310
+ DEPRECATED_KEY,
1311
+ Context,
1312
+ ComplexityInterceptor,
1313
+ ComplexityGuard,
1314
+ Complexity,
1315
+ CachingInterceptor,
1316
+ CacheHint,
1317
+ COMPLEXITY_KEY,
1318
+ CACHE_KEY,
1319
+ AuthGuard,
1320
+ Args
1321
+ };