@pawells/nestjs-auth 1.0.0-dev.4c8c698

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 (194) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +602 -0
  3. package/build/LICENSE +21 -0
  4. package/build/README.md +602 -0
  5. package/build/admin/client/client.d.ts +82 -0
  6. package/build/admin/client/client.d.ts.map +1 -0
  7. package/build/admin/client/client.js +157 -0
  8. package/build/admin/client/client.js.map +1 -0
  9. package/build/admin/client/errors/base-error.d.ts +58 -0
  10. package/build/admin/client/errors/base-error.d.ts.map +1 -0
  11. package/build/admin/client/errors/base-error.js +100 -0
  12. package/build/admin/client/errors/base-error.js.map +1 -0
  13. package/build/admin/client/errors/index.d.ts +2 -0
  14. package/build/admin/client/errors/index.d.ts.map +1 -0
  15. package/build/admin/client/errors/index.js +2 -0
  16. package/build/admin/client/errors/index.js.map +1 -0
  17. package/build/admin/client/index.d.ts +6 -0
  18. package/build/admin/client/index.d.ts.map +1 -0
  19. package/build/admin/client/index.js +11 -0
  20. package/build/admin/client/index.js.map +1 -0
  21. package/build/admin/client/services/authentication.service.d.ts +54 -0
  22. package/build/admin/client/services/authentication.service.d.ts.map +1 -0
  23. package/build/admin/client/services/authentication.service.js +99 -0
  24. package/build/admin/client/services/authentication.service.js.map +1 -0
  25. package/build/admin/client/services/base-service.d.ts +39 -0
  26. package/build/admin/client/services/base-service.d.ts.map +1 -0
  27. package/build/admin/client/services/base-service.js +107 -0
  28. package/build/admin/client/services/base-service.js.map +1 -0
  29. package/build/admin/client/services/client.service.d.ts +86 -0
  30. package/build/admin/client/services/client.service.d.ts.map +1 -0
  31. package/build/admin/client/services/client.service.js +193 -0
  32. package/build/admin/client/services/client.service.js.map +1 -0
  33. package/build/admin/client/services/event.service.d.ts +84 -0
  34. package/build/admin/client/services/event.service.d.ts.map +1 -0
  35. package/build/admin/client/services/event.service.js +155 -0
  36. package/build/admin/client/services/event.service.js.map +1 -0
  37. package/build/admin/client/services/federated-identity.service.d.ts +89 -0
  38. package/build/admin/client/services/federated-identity.service.d.ts.map +1 -0
  39. package/build/admin/client/services/federated-identity.service.js +120 -0
  40. package/build/admin/client/services/federated-identity.service.js.map +1 -0
  41. package/build/admin/client/services/group.service.d.ts +52 -0
  42. package/build/admin/client/services/group.service.d.ts.map +1 -0
  43. package/build/admin/client/services/group.service.js +105 -0
  44. package/build/admin/client/services/group.service.js.map +1 -0
  45. package/build/admin/client/services/identity-provider.service.d.ts +47 -0
  46. package/build/admin/client/services/identity-provider.service.d.ts.map +1 -0
  47. package/build/admin/client/services/identity-provider.service.js +86 -0
  48. package/build/admin/client/services/identity-provider.service.js.map +1 -0
  49. package/build/admin/client/services/index.d.ts +11 -0
  50. package/build/admin/client/services/index.d.ts.map +1 -0
  51. package/build/admin/client/services/index.js +11 -0
  52. package/build/admin/client/services/index.js.map +1 -0
  53. package/build/admin/client/services/realm.service.d.ts +41 -0
  54. package/build/admin/client/services/realm.service.d.ts.map +1 -0
  55. package/build/admin/client/services/realm.service.js +80 -0
  56. package/build/admin/client/services/realm.service.js.map +1 -0
  57. package/build/admin/client/services/role.service.d.ts +45 -0
  58. package/build/admin/client/services/role.service.d.ts.map +1 -0
  59. package/build/admin/client/services/role.service.js +92 -0
  60. package/build/admin/client/services/role.service.js.map +1 -0
  61. package/build/admin/client/services/user.service.d.ts +84 -0
  62. package/build/admin/client/services/user.service.d.ts.map +1 -0
  63. package/build/admin/client/services/user.service.js +216 -0
  64. package/build/admin/client/services/user.service.js.map +1 -0
  65. package/build/admin/client/types/config.types.d.ts +59 -0
  66. package/build/admin/client/types/config.types.d.ts.map +1 -0
  67. package/build/admin/client/types/config.types.js +13 -0
  68. package/build/admin/client/types/config.types.js.map +1 -0
  69. package/build/admin/client/types/event.types.d.ts +176 -0
  70. package/build/admin/client/types/event.types.d.ts.map +1 -0
  71. package/build/admin/client/types/event.types.js +2 -0
  72. package/build/admin/client/types/event.types.js.map +1 -0
  73. package/build/admin/client/types/index.d.ts +4 -0
  74. package/build/admin/client/types/index.d.ts.map +1 -0
  75. package/build/admin/client/types/index.js +4 -0
  76. package/build/admin/client/types/index.js.map +1 -0
  77. package/build/admin/client/types/keycloak.types.d.ts +169 -0
  78. package/build/admin/client/types/keycloak.types.d.ts.map +1 -0
  79. package/build/admin/client/types/keycloak.types.js +2 -0
  80. package/build/admin/client/types/keycloak.types.js.map +1 -0
  81. package/build/admin/client/utils/index.d.ts +2 -0
  82. package/build/admin/client/utils/index.d.ts.map +1 -0
  83. package/build/admin/client/utils/index.js +2 -0
  84. package/build/admin/client/utils/index.js.map +1 -0
  85. package/build/admin/client/utils/retry.d.ts +40 -0
  86. package/build/admin/client/utils/retry.d.ts.map +1 -0
  87. package/build/admin/client/utils/retry.js +72 -0
  88. package/build/admin/client/utils/retry.js.map +1 -0
  89. package/build/admin/config/keycloak.config.d.ts +33 -0
  90. package/build/admin/config/keycloak.config.d.ts.map +1 -0
  91. package/build/admin/config/keycloak.config.js +2 -0
  92. package/build/admin/config/keycloak.config.js.map +1 -0
  93. package/build/admin/config/keycloak.defaults.d.ts +11 -0
  94. package/build/admin/config/keycloak.defaults.d.ts.map +1 -0
  95. package/build/admin/config/keycloak.defaults.js +60 -0
  96. package/build/admin/config/keycloak.defaults.js.map +1 -0
  97. package/build/admin/health/keycloak.health.d.ts +13 -0
  98. package/build/admin/health/keycloak.health.d.ts.map +1 -0
  99. package/build/admin/health/keycloak.health.js +54 -0
  100. package/build/admin/health/keycloak.health.js.map +1 -0
  101. package/build/admin/index.d.ts +10 -0
  102. package/build/admin/index.d.ts.map +1 -0
  103. package/build/admin/index.js +9 -0
  104. package/build/admin/index.js.map +1 -0
  105. package/build/admin/keycloak-admin.interfaces.d.ts +45 -0
  106. package/build/admin/keycloak-admin.interfaces.d.ts.map +1 -0
  107. package/build/admin/keycloak-admin.interfaces.js +2 -0
  108. package/build/admin/keycloak-admin.interfaces.js.map +1 -0
  109. package/build/admin/keycloak-admin.module.d.ts +23 -0
  110. package/build/admin/keycloak-admin.module.d.ts.map +1 -0
  111. package/build/admin/keycloak-admin.module.js +101 -0
  112. package/build/admin/keycloak-admin.module.js.map +1 -0
  113. package/build/admin/keycloak.constants.d.ts +16 -0
  114. package/build/admin/keycloak.constants.d.ts.map +1 -0
  115. package/build/admin/keycloak.constants.js +16 -0
  116. package/build/admin/keycloak.constants.js.map +1 -0
  117. package/build/admin/permissions/index.d.ts +2 -0
  118. package/build/admin/permissions/index.d.ts.map +1 -0
  119. package/build/admin/permissions/index.js +2 -0
  120. package/build/admin/permissions/index.js.map +1 -0
  121. package/build/admin/permissions/keycloak-admin.permissions.d.ts +45 -0
  122. package/build/admin/permissions/keycloak-admin.permissions.d.ts.map +1 -0
  123. package/build/admin/permissions/keycloak-admin.permissions.js +68 -0
  124. package/build/admin/permissions/keycloak-admin.permissions.js.map +1 -0
  125. package/build/admin/services/keycloak-admin.service.d.ts +64 -0
  126. package/build/admin/services/keycloak-admin.service.d.ts.map +1 -0
  127. package/build/admin/services/keycloak-admin.service.js +152 -0
  128. package/build/admin/services/keycloak-admin.service.js.map +1 -0
  129. package/build/decorators/auth-decorators.d.ts +217 -0
  130. package/build/decorators/auth-decorators.d.ts.map +1 -0
  131. package/build/decorators/auth-decorators.js +251 -0
  132. package/build/decorators/auth-decorators.js.map +1 -0
  133. package/build/decorators/context-utils.d.ts +101 -0
  134. package/build/decorators/context-utils.d.ts.map +1 -0
  135. package/build/decorators/context-utils.js +178 -0
  136. package/build/decorators/context-utils.js.map +1 -0
  137. package/build/decorators/graphql-auth-decorators.d.ts +144 -0
  138. package/build/decorators/graphql-auth-decorators.d.ts.map +1 -0
  139. package/build/decorators/graphql-auth-decorators.js +152 -0
  140. package/build/decorators/graphql-auth-decorators.js.map +1 -0
  141. package/build/decorators/index.d.ts +5 -0
  142. package/build/decorators/index.d.ts.map +1 -0
  143. package/build/decorators/index.js +4 -0
  144. package/build/decorators/index.js.map +1 -0
  145. package/build/guards/index.d.ts +4 -0
  146. package/build/guards/index.d.ts.map +1 -0
  147. package/build/guards/index.js +4 -0
  148. package/build/guards/index.js.map +1 -0
  149. package/build/guards/jwt-auth.guard.d.ts +52 -0
  150. package/build/guards/jwt-auth.guard.d.ts.map +1 -0
  151. package/build/guards/jwt-auth.guard.js +97 -0
  152. package/build/guards/jwt-auth.guard.js.map +1 -0
  153. package/build/guards/permission.guard.d.ts +37 -0
  154. package/build/guards/permission.guard.d.ts.map +1 -0
  155. package/build/guards/permission.guard.js +73 -0
  156. package/build/guards/permission.guard.js.map +1 -0
  157. package/build/guards/role.guard.d.ts +33 -0
  158. package/build/guards/role.guard.d.ts.map +1 -0
  159. package/build/guards/role.guard.js +69 -0
  160. package/build/guards/role.guard.js.map +1 -0
  161. package/build/index.d.ts +92 -0
  162. package/build/index.d.ts.map +1 -0
  163. package/build/index.js +98 -0
  164. package/build/index.js.map +1 -0
  165. package/build/keycloak/index.d.ts +7 -0
  166. package/build/keycloak/index.d.ts.map +1 -0
  167. package/build/keycloak/index.js +5 -0
  168. package/build/keycloak/index.js.map +1 -0
  169. package/build/keycloak/keycloak.constants.d.ts +2 -0
  170. package/build/keycloak/keycloak.constants.d.ts.map +1 -0
  171. package/build/keycloak/keycloak.constants.js +2 -0
  172. package/build/keycloak/keycloak.constants.js.map +1 -0
  173. package/build/keycloak/keycloak.interfaces.d.ts +12 -0
  174. package/build/keycloak/keycloak.interfaces.d.ts.map +1 -0
  175. package/build/keycloak/keycloak.interfaces.js +2 -0
  176. package/build/keycloak/keycloak.interfaces.js.map +1 -0
  177. package/build/keycloak/keycloak.module.d.ts +56 -0
  178. package/build/keycloak/keycloak.module.d.ts.map +1 -0
  179. package/build/keycloak/keycloak.module.js +104 -0
  180. package/build/keycloak/keycloak.module.js.map +1 -0
  181. package/build/keycloak/keycloak.types.d.ts +60 -0
  182. package/build/keycloak/keycloak.types.d.ts.map +1 -0
  183. package/build/keycloak/keycloak.types.js +2 -0
  184. package/build/keycloak/keycloak.types.js.map +1 -0
  185. package/build/keycloak/services/jwks-cache.service.d.ts +64 -0
  186. package/build/keycloak/services/jwks-cache.service.d.ts.map +1 -0
  187. package/build/keycloak/services/jwks-cache.service.js +176 -0
  188. package/build/keycloak/services/jwks-cache.service.js.map +1 -0
  189. package/build/keycloak/services/keycloak-token-validation.service.d.ts +88 -0
  190. package/build/keycloak/services/keycloak-token-validation.service.d.ts.map +1 -0
  191. package/build/keycloak/services/keycloak-token-validation.service.js +243 -0
  192. package/build/keycloak/services/keycloak-token-validation.service.js.map +1 -0
  193. package/build/package.json +72 -0
  194. package/package.json +93 -0
@@ -0,0 +1,12 @@
1
+ import { ModuleMetadata } from '@nestjs/common';
2
+ import type { InjectionToken, OptionalFactoryDependency } from '@nestjs/common';
3
+ import type { KeycloakModuleOptions } from './keycloak.types.js';
4
+ /**
5
+ * Async options for KeycloakModule configuration
6
+ */
7
+ export interface KeycloakModuleAsyncOptions {
8
+ imports?: ModuleMetadata['imports'];
9
+ useFactory: (...args: unknown[]) => Promise<KeycloakModuleOptions> | KeycloakModuleOptions;
10
+ inject?: Array<InjectionToken | OptionalFactoryDependency>;
11
+ }
12
+ //# sourceMappingURL=keycloak.interfaces.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keycloak.interfaces.d.ts","sourceRoot":"","sources":["../../src/keycloak/keycloak.interfaces.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EAAE,cAAc,EAAE,yBAAyB,EAAE,MAAM,gBAAgB,CAAC;AAChF,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAEjE;;GAEG;AACH,MAAM,WAAW,0BAA0B;IAC1C,OAAO,CAAC,EAAE,cAAc,CAAC,SAAS,CAAC,CAAC;IACpC,UAAU,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,qBAAqB,CAAC,GAAG,qBAAqB,CAAC;IAC3F,MAAM,CAAC,EAAE,KAAK,CAAC,cAAc,GAAG,yBAAyB,CAAC,CAAC;CAC3D"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=keycloak.interfaces.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keycloak.interfaces.js","sourceRoot":"","sources":["../../src/keycloak/keycloak.interfaces.ts"],"names":[],"mappings":""}
@@ -0,0 +1,56 @@
1
+ import { DynamicModule } from '@nestjs/common';
2
+ import type { KeycloakModuleOptions } from './keycloak.types.js';
3
+ import type { KeycloakModuleAsyncOptions } from './keycloak.interfaces.js';
4
+ /**
5
+ * Keycloak Token Validation Module
6
+ *
7
+ * Provides Keycloak token validation for NestJS resource servers. Supports two validation modes:
8
+ * - **Online mode (default)**: Uses Keycloak's token introspection endpoint for real-time token validation
9
+ * - **Offline mode (opt-in)**: Validates JWTs using cached JWKS (JSON Web Key Set) for stateless validation
10
+ *
11
+ * Export the `JwtAuthGuard` and decorate controllers/routes with `@UseGuards(JwtAuthGuard)` to enable
12
+ * token validation. The guard respects `@Public()` to bypass authentication on specific endpoints.
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * // Online mode (default)
17
+ * KeycloakModule.forRoot({
18
+ * authServerUrl: 'https://keycloak.example.com',
19
+ * clientId: 'my-client',
20
+ * clientSecret: 'secret',
21
+ * validationMode: 'online'
22
+ * })
23
+ *
24
+ * // Offline mode (JWKS-based)
25
+ * KeycloakModule.forRoot({
26
+ * authServerUrl: 'https://keycloak.example.com',
27
+ * clientId: 'my-client',
28
+ * validationMode: 'offline'
29
+ * })
30
+ * ```
31
+ */
32
+ export declare class KeycloakModule {
33
+ /**
34
+ * Register Keycloak module with static configuration
35
+ *
36
+ * @param options - Configuration options for the Keycloak module
37
+ * @returns Dynamic module configuration with KeycloakTokenValidationService and JwksCacheService
38
+ */
39
+ static forRoot(options: KeycloakModuleOptions): DynamicModule;
40
+ /**
41
+ * Register Keycloak module with asynchronous configuration
42
+ *
43
+ * Defers module configuration until runtime using a factory function.
44
+ * Useful for reading configuration from environment variables or other async sources.
45
+ *
46
+ * @param options - Async factory configuration
47
+ * @param options.useFactory - Function that returns KeycloakModuleOptions or a promise that resolves to it
48
+ * @param options.inject - Optional array of providers to inject into the factory function
49
+ * @param options.imports - Optional array of modules to import for dependency injection
50
+ * @returns Dynamic module configuration with KeycloakTokenValidationService and JwksCacheService.
51
+ * Note: JwksCacheService is always provided in async mode (validationMode is not known at
52
+ * module definition time). In online mode, it initializes but skips the JWKS fetch.
53
+ */
54
+ static forRootAsync(options: KeycloakModuleAsyncOptions): DynamicModule;
55
+ }
56
+ //# sourceMappingURL=keycloak.module.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keycloak.module.d.ts","sourceRoot":"","sources":["../../src/keycloak/keycloak.module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAGvD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AACjE,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,0BAA0B,CAAC;AAI3E;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,qBACa,cAAc;IAC1B;;;;;OAKG;WACW,OAAO,CAAC,OAAO,EAAE,qBAAqB,GAAG,aAAa;IAuBpE;;;;;;;;;;;;;OAaG;WACW,YAAY,CAAC,OAAO,EAAE,0BAA0B,GAAG,aAAa;CAgB9E"}
@@ -0,0 +1,104 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var KeycloakModule_1;
8
+ import { Module } from '@nestjs/common';
9
+ import { JwtModule } from '@nestjs/jwt';
10
+ import { KEYCLOAK_MODULE_OPTIONS } from './keycloak.constants.js';
11
+ import { JwksCacheService } from './services/jwks-cache.service.js';
12
+ import { KeycloakTokenValidationService } from './services/keycloak-token-validation.service.js';
13
+ /**
14
+ * Keycloak Token Validation Module
15
+ *
16
+ * Provides Keycloak token validation for NestJS resource servers. Supports two validation modes:
17
+ * - **Online mode (default)**: Uses Keycloak's token introspection endpoint for real-time token validation
18
+ * - **Offline mode (opt-in)**: Validates JWTs using cached JWKS (JSON Web Key Set) for stateless validation
19
+ *
20
+ * Export the `JwtAuthGuard` and decorate controllers/routes with `@UseGuards(JwtAuthGuard)` to enable
21
+ * token validation. The guard respects `@Public()` to bypass authentication on specific endpoints.
22
+ *
23
+ * @example
24
+ * ```typescript
25
+ * // Online mode (default)
26
+ * KeycloakModule.forRoot({
27
+ * authServerUrl: 'https://keycloak.example.com',
28
+ * clientId: 'my-client',
29
+ * clientSecret: 'secret',
30
+ * validationMode: 'online'
31
+ * })
32
+ *
33
+ * // Offline mode (JWKS-based)
34
+ * KeycloakModule.forRoot({
35
+ * authServerUrl: 'https://keycloak.example.com',
36
+ * clientId: 'my-client',
37
+ * validationMode: 'offline'
38
+ * })
39
+ * ```
40
+ */
41
+ let KeycloakModule = KeycloakModule_1 = class KeycloakModule {
42
+ /**
43
+ * Register Keycloak module with static configuration
44
+ *
45
+ * @param options - Configuration options for the Keycloak module
46
+ * @returns Dynamic module configuration with KeycloakTokenValidationService and JwksCacheService
47
+ */
48
+ static forRoot(options) {
49
+ const isOffline = options.validationMode === 'offline';
50
+ const providers = [
51
+ {
52
+ provide: KEYCLOAK_MODULE_OPTIONS,
53
+ useValue: options,
54
+ },
55
+ KeycloakTokenValidationService,
56
+ ...(isOffline ? [JwksCacheService] : []),
57
+ ];
58
+ return {
59
+ module: KeycloakModule_1,
60
+ imports: [JwtModule.register({})],
61
+ providers,
62
+ exports: [
63
+ KeycloakTokenValidationService,
64
+ ...(isOffline ? [JwksCacheService] : []),
65
+ KEYCLOAK_MODULE_OPTIONS,
66
+ ],
67
+ };
68
+ }
69
+ /**
70
+ * Register Keycloak module with asynchronous configuration
71
+ *
72
+ * Defers module configuration until runtime using a factory function.
73
+ * Useful for reading configuration from environment variables or other async sources.
74
+ *
75
+ * @param options - Async factory configuration
76
+ * @param options.useFactory - Function that returns KeycloakModuleOptions or a promise that resolves to it
77
+ * @param options.inject - Optional array of providers to inject into the factory function
78
+ * @param options.imports - Optional array of modules to import for dependency injection
79
+ * @returns Dynamic module configuration with KeycloakTokenValidationService and JwksCacheService.
80
+ * Note: JwksCacheService is always provided in async mode (validationMode is not known at
81
+ * module definition time). In online mode, it initializes but skips the JWKS fetch.
82
+ */
83
+ static forRootAsync(options) {
84
+ return {
85
+ module: KeycloakModule_1,
86
+ imports: [JwtModule.register({}), ...(options.imports ?? [])],
87
+ providers: [
88
+ {
89
+ provide: KEYCLOAK_MODULE_OPTIONS,
90
+ useFactory: options.useFactory,
91
+ inject: options.inject ?? [],
92
+ },
93
+ JwksCacheService,
94
+ KeycloakTokenValidationService,
95
+ ],
96
+ exports: [KeycloakTokenValidationService, JwksCacheService, KEYCLOAK_MODULE_OPTIONS],
97
+ };
98
+ }
99
+ };
100
+ KeycloakModule = KeycloakModule_1 = __decorate([
101
+ Module({})
102
+ ], KeycloakModule);
103
+ export { KeycloakModule };
104
+ //# sourceMappingURL=keycloak.module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keycloak.module.js","sourceRoot":"","sources":["../../src/keycloak/keycloak.module.ts"],"names":[],"mappings":";;;;;;;AAAA,OAAO,EAAE,MAAM,EAAiB,MAAM,gBAAgB,CAAC;AACvD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAGlE,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACpE,OAAO,EAAE,8BAA8B,EAAE,MAAM,iDAAiD,CAAC;AAEjG;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEI,IAAM,cAAc,sBAApB,MAAM,cAAc;IAC1B;;;;;OAKG;IACI,MAAM,CAAC,OAAO,CAAC,OAA8B;QACnD,MAAM,SAAS,GAAG,OAAO,CAAC,cAAc,KAAK,SAAS,CAAC;QACvD,MAAM,SAAS,GAAG;YACjB;gBACC,OAAO,EAAE,uBAAuB;gBAChC,QAAQ,EAAE,OAAO;aACjB;YACD,8BAA8B;YAC9B,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;SACxC,CAAC;QAEF,OAAO;YACN,MAAM,EAAE,gBAAc;YACtB,OAAO,EAAE,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;YACjC,SAAS;YACT,OAAO,EAAE;gBACR,8BAA8B;gBAC9B,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;gBACxC,uBAAuB;aACvB;SACD,CAAC;IACH,CAAC;IAED;;;;;;;;;;;;;OAaG;IACI,MAAM,CAAC,YAAY,CAAC,OAAmC;QAC7D,OAAO;YACN,MAAM,EAAE,gBAAc;YACtB,OAAO,EAAE,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;YAC7D,SAAS,EAAE;gBACV;oBACC,OAAO,EAAE,uBAAuB;oBAChC,UAAU,EAAE,OAAO,CAAC,UAAU;oBAC9B,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,EAAE;iBAC5B;gBACD,gBAAgB;gBAChB,8BAA8B;aAC9B;YACD,OAAO,EAAE,CAAC,8BAA8B,EAAE,gBAAgB,EAAE,uBAAuB,CAAC;SACpF,CAAC;IACH,CAAC;CACD,CAAA;AA5DY,cAAc;IAD1B,MAAM,CAAC,EAAE,CAAC;GACE,cAAc,CA4D1B"}
@@ -0,0 +1,60 @@
1
+ export interface KeycloakModuleOptions {
2
+ /** Keycloak realm base URL — e.g. 'https://auth.example.com/realms/myrealm' */
3
+ authServerUrl: string;
4
+ /** Realm name */
5
+ realm: string;
6
+ /** This service's Keycloak client ID — used for audience validation and client role extraction */
7
+ clientId: string;
8
+ /**
9
+ * Token validation mode.
10
+ * - 'online' (default): validate via Keycloak introspection — authoritative, detects revocation immediately
11
+ * - 'offline': validate JWT locally using JWKS — fast, no network hop, does not detect revocation
12
+ */
13
+ validationMode?: 'online' | 'offline';
14
+ /** Required when validationMode is 'online' (the default). Client secret for the introspection endpoint. */
15
+ clientSecret?: string;
16
+ /** JWKS cache TTL in milliseconds. Used in offline mode only. Default: 300000 (5 minutes). */
17
+ jwksCacheTtlMs?: number;
18
+ /**
19
+ * Expected token issuer. Must match the 'iss' claim exactly.
20
+ * Defaults to authServerUrl.
21
+ */
22
+ issuer?: string;
23
+ }
24
+ export interface KeycloakTokenClaims {
25
+ sub: string;
26
+ iss: string;
27
+ aud: string | string[];
28
+ exp: number;
29
+ iat: number;
30
+ jti?: string;
31
+ azp?: string;
32
+ session_state?: string;
33
+ email?: string;
34
+ email_verified?: boolean;
35
+ preferred_username?: string;
36
+ name?: string;
37
+ given_name?: string;
38
+ family_name?: string;
39
+ realm_access?: {
40
+ roles: string[];
41
+ };
42
+ resource_access?: Record<string, {
43
+ roles: string[];
44
+ }>;
45
+ scope?: string;
46
+ [key: string]: unknown;
47
+ }
48
+ export interface KeycloakUser {
49
+ /** The user's unique ID (sub claim) */
50
+ id: string;
51
+ email?: string;
52
+ /** preferred_username claim */
53
+ username?: string;
54
+ name?: string;
55
+ /** Roles from realm_access.roles */
56
+ realmRoles: string[];
57
+ /** Roles from resource_access[clientId].roles */
58
+ clientRoles: string[];
59
+ }
60
+ //# sourceMappingURL=keycloak.types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keycloak.types.d.ts","sourceRoot":"","sources":["../../src/keycloak/keycloak.types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,qBAAqB;IACrC,+EAA+E;IAC/E,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,kGAAkG;IAClG,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,cAAc,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;IACtC,4GAA4G;IAC5G,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,8FAA8F;IAC9F,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,mBAAmB;IACnC,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IACnC,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;IACtD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,YAAY;IAC5B,uCAAuC;IACvC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+BAA+B;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,oCAAoC;IACpC,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,iDAAiD;IACjD,WAAW,EAAE,MAAM,EAAE,CAAC;CACtB"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=keycloak.types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keycloak.types.js","sourceRoot":"","sources":["../../src/keycloak/keycloak.types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,64 @@
1
+ import { OnModuleInit } from '@nestjs/common';
2
+ import type { KeycloakModuleOptions } from '../keycloak.types.js';
3
+ /**
4
+ * JWKS Cache Service
5
+ *
6
+ * Fetches and caches JWKS (JSON Web Key Set) from Keycloak for offline token validation.
7
+ * Used exclusively in offline validation mode to verify JWT signatures locally without
8
+ * contacting the Keycloak introspection endpoint.
9
+ *
10
+ * Caches keys with automatic expiration (default 5 minutes). On key rotation or cache expiry,
11
+ * automatically re-fetches the latest JWKS from Keycloak. Prevents concurrent fetches to avoid
12
+ * stampedes.
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * constructor(private jwksCache: JwksCacheService) {}
17
+ *
18
+ * async validateJwt(token: string): Promise<boolean> {
19
+ * const decoded = jwt.decode(token, { complete: true });
20
+ * const key = await this.jwksCache.getKey(decoded.header.kid);
21
+ * return jwt.verify(token, key);
22
+ * }
23
+ * ```
24
+ */
25
+ export declare class JwksCacheService implements OnModuleInit {
26
+ private readonly keyCache;
27
+ private cacheExpiresAt;
28
+ private fetchPromise;
29
+ private logger?;
30
+ private readonly options;
31
+ constructor(options: KeycloakModuleOptions);
32
+ private initializeLogger;
33
+ onModuleInit(): Promise<void>;
34
+ /**
35
+ * Get a public key from cache by key ID (kid)
36
+ *
37
+ * Checks the cache for the requested key. If found and not expired, returns it immediately.
38
+ * If not found or cache is expired, automatically re-fetches all keys from Keycloak.
39
+ * Prevents concurrent fetches with an internal lock.
40
+ *
41
+ * On key rotation, the next key request for a missing `kid` will trigger a refresh and
42
+ * cache the new key set.
43
+ *
44
+ * @param kid - The Key ID (from JWT header) to retrieve
45
+ * @returns The PEM-encoded public key (SPKI format)
46
+ * @throws {UnauthorizedException} If the key ID is not found after re-fetch attempt
47
+ *
48
+ * @example
49
+ * ```typescript
50
+ * try {
51
+ * const key = await this.jwksCache.getKey('abc123');
52
+ * // Use key for JWT verification
53
+ * } catch (error) {
54
+ * // Handle unknown key ID
55
+ * }
56
+ * ```
57
+ */
58
+ getKey(kid: string): Promise<string>;
59
+ private fetchJwks;
60
+ private doFetch;
61
+ private convertJwkToPem;
62
+ private log;
63
+ }
64
+ //# sourceMappingURL=jwks-cache.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jwks-cache.service.d.ts","sourceRoot":"","sources":["../../../src/keycloak/services/jwks-cache.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,YAAY,EAAiC,MAAM,gBAAgB,CAAC;AAIzF,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAgBlE;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBACa,gBAAiB,YAAW,YAAY;IACpD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAkC;IAE3D,OAAO,CAAC,cAAc,CAAa;IAEnC,OAAO,CAAC,YAAY,CAA8B;IAElD,OAAO,CAAC,MAAM,CAAC,CAAY;IAE3B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAwB;gBAGd,OAAO,EAAE,qBAAqB;IAMhE,OAAO,CAAC,gBAAgB;IAQX,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ1C;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACU,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;YA2BnC,SAAS;YAaT,OAAO;IA6BrB,OAAO,CAAC,eAAe;IAWvB,OAAO,CAAC,GAAG;CASX"}
@@ -0,0 +1,176 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
11
+ return function (target, key) { decorator(target, key, paramIndex); }
12
+ };
13
+ var JwksCacheService_1;
14
+ import { Injectable, Inject, UnauthorizedException } from '@nestjs/common';
15
+ import { createPublicKey } from 'node:crypto';
16
+ import { AppLogger, getErrorMessage } from '@pawells/nestjs-shared/common';
17
+ import { KEYCLOAK_MODULE_OPTIONS } from '../keycloak.constants.js';
18
+ const DEFAULT_JWKS_CACHE_TTL_MS = 300_000;
19
+ /**
20
+ * JWKS Cache Service
21
+ *
22
+ * Fetches and caches JWKS (JSON Web Key Set) from Keycloak for offline token validation.
23
+ * Used exclusively in offline validation mode to verify JWT signatures locally without
24
+ * contacting the Keycloak introspection endpoint.
25
+ *
26
+ * Caches keys with automatic expiration (default 5 minutes). On key rotation or cache expiry,
27
+ * automatically re-fetches the latest JWKS from Keycloak. Prevents concurrent fetches to avoid
28
+ * stampedes.
29
+ *
30
+ * @example
31
+ * ```typescript
32
+ * constructor(private jwksCache: JwksCacheService) {}
33
+ *
34
+ * async validateJwt(token: string): Promise<boolean> {
35
+ * const decoded = jwt.decode(token, { complete: true });
36
+ * const key = await this.jwksCache.getKey(decoded.header.kid);
37
+ * return jwt.verify(token, key);
38
+ * }
39
+ * ```
40
+ */
41
+ let JwksCacheService = JwksCacheService_1 = class JwksCacheService {
42
+ keyCache = new Map();
43
+ cacheExpiresAt = 0;
44
+ fetchPromise = null;
45
+ logger;
46
+ options;
47
+ constructor(options) {
48
+ this.options = options;
49
+ this.initializeLogger();
50
+ }
51
+ initializeLogger() {
52
+ try {
53
+ this.logger = new AppLogger(undefined, JwksCacheService_1.name);
54
+ }
55
+ catch {
56
+ // Logger unavailable, fall back to console
57
+ }
58
+ }
59
+ async onModuleInit() {
60
+ // Only fetch JWKS if in offline validation mode
61
+ if (this.options.validationMode !== 'offline') {
62
+ return;
63
+ }
64
+ await this.fetchJwks();
65
+ }
66
+ /**
67
+ * Get a public key from cache by key ID (kid)
68
+ *
69
+ * Checks the cache for the requested key. If found and not expired, returns it immediately.
70
+ * If not found or cache is expired, automatically re-fetches all keys from Keycloak.
71
+ * Prevents concurrent fetches with an internal lock.
72
+ *
73
+ * On key rotation, the next key request for a missing `kid` will trigger a refresh and
74
+ * cache the new key set.
75
+ *
76
+ * @param kid - The Key ID (from JWT header) to retrieve
77
+ * @returns The PEM-encoded public key (SPKI format)
78
+ * @throws {UnauthorizedException} If the key ID is not found after re-fetch attempt
79
+ *
80
+ * @example
81
+ * ```typescript
82
+ * try {
83
+ * const key = await this.jwksCache.getKey('abc123');
84
+ * // Use key for JWT verification
85
+ * } catch (error) {
86
+ * // Handle unknown key ID
87
+ * }
88
+ * ```
89
+ */
90
+ async getKey(kid) {
91
+ // Check if key is in cache and not expired
92
+ if (this.keyCache.has(kid) && Date.now() < this.cacheExpiresAt) {
93
+ const key = this.keyCache.get(kid);
94
+ if (key) {
95
+ return key;
96
+ }
97
+ }
98
+ // Key not found or cache expired, re-fetch
99
+ try {
100
+ await this.fetchJwks();
101
+ }
102
+ catch (error) {
103
+ this.log('warn', `Failed to re-fetch JWKS during key lookup: ${getErrorMessage(error)}`);
104
+ }
105
+ // Check cache again after re-fetch — only return if cache is still valid
106
+ if (this.keyCache.has(kid) && Date.now() < this.cacheExpiresAt) {
107
+ const key = this.keyCache.get(kid);
108
+ if (key) {
109
+ return key;
110
+ }
111
+ }
112
+ throw new UnauthorizedException('Unknown signing key');
113
+ }
114
+ async fetchJwks() {
115
+ // If a fetch is already in-flight, await it instead of making another request
116
+ if (this.fetchPromise !== null) {
117
+ await this.fetchPromise;
118
+ return;
119
+ }
120
+ this.fetchPromise = this.doFetch().finally(() => {
121
+ this.fetchPromise = null;
122
+ });
123
+ await this.fetchPromise;
124
+ }
125
+ async doFetch() {
126
+ try {
127
+ const jwksUrl = `${this.options.authServerUrl}/realms/${this.options.realm}/protocol/openid-connect/certs`;
128
+ const response = await fetch(jwksUrl);
129
+ if (!response.ok) {
130
+ throw new Error(`JWKS fetch failed with status ${response.status}`);
131
+ }
132
+ const jwksData = await response.json();
133
+ if (!Array.isArray(jwksData.keys)) {
134
+ throw new Error('Invalid JWKS response: keys is not an array');
135
+ }
136
+ this.keyCache.clear();
137
+ for (const jwk of jwksData.keys) {
138
+ const pem = this.convertJwkToPem(jwk);
139
+ this.keyCache.set(jwk.kid, pem);
140
+ }
141
+ const ttlMs = this.options.jwksCacheTtlMs ?? DEFAULT_JWKS_CACHE_TTL_MS;
142
+ this.cacheExpiresAt = Date.now() + ttlMs;
143
+ }
144
+ catch (error) {
145
+ this.log('warn', `Failed to fetch JWKS: ${getErrorMessage(error)}`);
146
+ throw error;
147
+ }
148
+ }
149
+ convertJwkToPem(jwk) {
150
+ const key = createPublicKey({
151
+ key: jwk,
152
+ format: 'jwk',
153
+ });
154
+ return key.export({
155
+ type: 'spki',
156
+ format: 'pem',
157
+ });
158
+ }
159
+ log(level, message) {
160
+ if (this.logger) {
161
+ if (level === 'warn') {
162
+ this.logger.warn(message);
163
+ }
164
+ else {
165
+ this.logger.info(message);
166
+ }
167
+ }
168
+ }
169
+ };
170
+ JwksCacheService = JwksCacheService_1 = __decorate([
171
+ Injectable(),
172
+ __param(0, Inject(KEYCLOAK_MODULE_OPTIONS)),
173
+ __metadata("design:paramtypes", [Object])
174
+ ], JwksCacheService);
175
+ export { JwksCacheService };
176
+ //# sourceMappingURL=jwks-cache.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jwks-cache.service.js","sourceRoot":"","sources":["../../../src/keycloak/services/jwks-cache.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,88 @@
1
+ import { JwtService } from '@nestjs/jwt';
2
+ import type { KeycloakModuleOptions, KeycloakTokenClaims, KeycloakUser } from '../keycloak.types.js';
3
+ import { JwksCacheService } from './jwks-cache.service.js';
4
+ export interface TokenValidationResult {
5
+ valid: boolean;
6
+ claims?: KeycloakTokenClaims;
7
+ error?: string;
8
+ }
9
+ /**
10
+ * Keycloak Token Validation Service
11
+ *
12
+ * Validates JWT tokens issued by Keycloak in two modes:
13
+ * - **Online mode (default)**: Calls Keycloak's token introspection endpoint to validate the token
14
+ * (requires real-time network access to Keycloak)
15
+ * - **Offline mode**: Validates JWTs locally using JWKS (no network call; suitable for high-traffic scenarios)
16
+ *
17
+ * After successful validation, extracts user identity and roles from token claims.
18
+ *
19
+ * @example
20
+ * ```typescript
21
+ * // Online mode validation
22
+ * const result = await this.validationService.validateToken(token);
23
+ * if (result.valid) {
24
+ * const user = this.validationService.extractUser(result.claims!);
25
+ * }
26
+ *
27
+ * // Offline mode uses JWKS-based verification (faster, no network call)
28
+ * ```
29
+ */
30
+ export declare class KeycloakTokenValidationService {
31
+ private logger?;
32
+ private readonly options;
33
+ private readonly jwtService;
34
+ private readonly jwksCacheService?;
35
+ constructor(options: KeycloakModuleOptions, jwtService: JwtService, jwksCacheService?: JwksCacheService);
36
+ private initializeLogger;
37
+ /**
38
+ * Validate a JWT token issued by Keycloak
39
+ *
40
+ * Routes to the appropriate validation mode based on configuration:
41
+ * - **Online**: Calls the Keycloak introspection endpoint (requires network access)
42
+ * - **Offline**: Verifies JWT signature using cached JWKS (no network call)
43
+ *
44
+ * Both modes verify token expiration and audience/issuer claims.
45
+ *
46
+ * @param token - The JWT to validate (Bearer token without "Bearer " prefix)
47
+ * @returns Result object with validation status and optional claims on success, or error code on failure
48
+ * @returns `{ valid: true, claims: KeycloakTokenClaims }` on success
49
+ * @returns `{ valid: false, error: string }` on failure (includes error codes like 'token_expired', 'invalid_issuer', etc.)
50
+ *
51
+ * @example
52
+ * ```typescript
53
+ * const result = await this.validateToken(jwtToken);
54
+ * if (result.valid && result.claims) {
55
+ * const user = this.extractUser(result.claims);
56
+ * }
57
+ * ```
58
+ */
59
+ validateToken(token: string): Promise<TokenValidationResult>;
60
+ private validateTokenOnline;
61
+ private validateTokenOffline;
62
+ /**
63
+ * Extract user identity and roles from validated token claims
64
+ *
65
+ * Maps Keycloak token claims to a simplified `KeycloakUser` object.
66
+ * Extracts both realm-level roles (`realm_access.roles`) and client-specific roles
67
+ * (`resource_access[clientId].roles`).
68
+ *
69
+ * @param claims - The validated Keycloak token claims
70
+ * @returns User object with ID, email, username, name, and both realm and client roles
71
+ *
72
+ * @example
73
+ * ```typescript
74
+ * const user = this.extractUser(claims);
75
+ * // {
76
+ * // id: 'user-uuid',
77
+ * // email: 'user@example.com',
78
+ * // username: 'john_doe',
79
+ * // name: 'John Doe',
80
+ * // realmRoles: ['admin', 'user'],
81
+ * // clientRoles: ['read', 'write']
82
+ * // }
83
+ * ```
84
+ */
85
+ extractUser(claims: KeycloakTokenClaims): KeycloakUser;
86
+ private log;
87
+ }
88
+ //# sourceMappingURL=keycloak-token-validation.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keycloak-token-validation.service.d.ts","sourceRoot":"","sources":["../../../src/keycloak/services/keycloak-token-validation.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAGzC,OAAO,KAAK,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACrG,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAE3D,MAAM,WAAW,qBAAqB;IACrC,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,mBAAmB,CAAC;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAID;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBACa,8BAA8B;IAC1C,OAAO,CAAC,MAAM,CAAC,CAAY;IAE3B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAwB;IAEhD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAa;IAExC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAmB;gBAGnB,OAAO,EAAE,qBAAqB,EAC/D,UAAU,EAAE,UAAU,EACV,gBAAgB,CAAC,EAAE,gBAAgB;IAQhD,OAAO,CAAC,gBAAgB;IAQxB;;;;;;;;;;;;;;;;;;;;;OAqBG;IACU,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC;YAe3D,mBAAmB;YA8CnB,oBAAoB;IAiElC;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACI,WAAW,CAAC,MAAM,EAAE,mBAAmB,GAAG,YAAY;IAW7D,OAAO,CAAC,GAAG;CASX"}