fastify-wrap 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.
Files changed (150) hide show
  1. package/README.md +980 -0
  2. package/dist/src/client/TestClient.d.ts +33 -0
  3. package/dist/src/client/TestClient.d.ts.map +1 -0
  4. package/dist/src/client/TestClient.js +359 -0
  5. package/dist/src/client/TestClient.js.map +1 -0
  6. package/dist/src/client/index.d.ts +4 -0
  7. package/dist/src/client/index.d.ts.map +1 -0
  8. package/dist/src/client/index.js +3 -0
  9. package/dist/src/client/index.js.map +1 -0
  10. package/dist/src/client/sign.d.ts +12 -0
  11. package/dist/src/client/sign.d.ts.map +1 -0
  12. package/dist/src/client/sign.js +44 -0
  13. package/dist/src/client/sign.js.map +1 -0
  14. package/dist/src/client/types.d.ts +41 -0
  15. package/dist/src/client/types.d.ts.map +1 -0
  16. package/dist/src/client/types.js +20 -0
  17. package/dist/src/client/types.js.map +1 -0
  18. package/dist/src/common/auth/AuthStrategy.d.ts +6 -0
  19. package/dist/src/common/auth/AuthStrategy.d.ts.map +1 -0
  20. package/dist/src/common/auth/AuthStrategy.js +2 -0
  21. package/dist/src/common/auth/AuthStrategy.js.map +1 -0
  22. package/dist/src/common/auth/JwtStrategy.d.ts +9 -0
  23. package/dist/src/common/auth/JwtStrategy.d.ts.map +1 -0
  24. package/dist/src/common/auth/JwtStrategy.js +34 -0
  25. package/dist/src/common/auth/JwtStrategy.js.map +1 -0
  26. package/dist/src/common/auth/NonceStore.d.ts +22 -0
  27. package/dist/src/common/auth/NonceStore.d.ts.map +1 -0
  28. package/dist/src/common/auth/NonceStore.js +27 -0
  29. package/dist/src/common/auth/NonceStore.js.map +1 -0
  30. package/dist/src/common/auth/SecretResolvers.d.ts +18 -0
  31. package/dist/src/common/auth/SecretResolvers.d.ts.map +1 -0
  32. package/dist/src/common/auth/SecretResolvers.js +30 -0
  33. package/dist/src/common/auth/SecretResolvers.js.map +1 -0
  34. package/dist/src/common/auth/SignatureStrategy.d.ts +23 -0
  35. package/dist/src/common/auth/SignatureStrategy.d.ts.map +1 -0
  36. package/dist/src/common/auth/SignatureStrategy.js +115 -0
  37. package/dist/src/common/auth/SignatureStrategy.js.map +1 -0
  38. package/dist/src/common/auth/index.d.ts +4 -0
  39. package/dist/src/common/auth/index.d.ts.map +1 -0
  40. package/dist/src/common/auth/index.js +3 -0
  41. package/dist/src/common/auth/index.js.map +1 -0
  42. package/dist/src/common/auth/signToken.d.ts +4 -0
  43. package/dist/src/common/auth/signToken.d.ts.map +1 -0
  44. package/dist/src/common/auth/signToken.js +11 -0
  45. package/dist/src/common/auth/signToken.js.map +1 -0
  46. package/dist/src/common/config/loader.d.ts +10 -0
  47. package/dist/src/common/config/loader.d.ts.map +1 -0
  48. package/dist/src/common/config/loader.js +20 -0
  49. package/dist/src/common/config/loader.js.map +1 -0
  50. package/dist/src/common/container/index.d.ts +7 -0
  51. package/dist/src/common/container/index.d.ts.map +1 -0
  52. package/dist/src/common/container/index.js +15 -0
  53. package/dist/src/common/container/index.js.map +1 -0
  54. package/dist/src/common/db/index.d.ts +19 -0
  55. package/dist/src/common/db/index.d.ts.map +1 -0
  56. package/dist/src/common/db/index.js +64 -0
  57. package/dist/src/common/db/index.js.map +1 -0
  58. package/dist/src/common/dto/PageResult.d.ts +12 -0
  59. package/dist/src/common/dto/PageResult.d.ts.map +1 -0
  60. package/dist/src/common/dto/PageResult.js +2 -0
  61. package/dist/src/common/dto/PageResult.js.map +1 -0
  62. package/dist/src/common/errorHandler.d.ts +3 -0
  63. package/dist/src/common/errorHandler.d.ts.map +1 -0
  64. package/dist/src/common/errorHandler.js +47 -0
  65. package/dist/src/common/errorHandler.js.map +1 -0
  66. package/dist/src/common/exceptions/AuthException.d.ts +5 -0
  67. package/dist/src/common/exceptions/AuthException.d.ts.map +1 -0
  68. package/dist/src/common/exceptions/AuthException.js +8 -0
  69. package/dist/src/common/exceptions/AuthException.js.map +1 -0
  70. package/dist/src/common/exceptions/BaseException.d.ts +4 -0
  71. package/dist/src/common/exceptions/BaseException.d.ts.map +1 -0
  72. package/dist/src/common/exceptions/BaseException.js +7 -0
  73. package/dist/src/common/exceptions/BaseException.js.map +1 -0
  74. package/dist/src/common/exceptions/BusinessException.d.ts +5 -0
  75. package/dist/src/common/exceptions/BusinessException.d.ts.map +1 -0
  76. package/dist/src/common/exceptions/BusinessException.js +8 -0
  77. package/dist/src/common/exceptions/BusinessException.js.map +1 -0
  78. package/dist/src/common/exceptions/NotFoundException.d.ts +5 -0
  79. package/dist/src/common/exceptions/NotFoundException.d.ts.map +1 -0
  80. package/dist/src/common/exceptions/NotFoundException.js +8 -0
  81. package/dist/src/common/exceptions/NotFoundException.js.map +1 -0
  82. package/dist/src/common/exceptions/ServiceException.d.ts +5 -0
  83. package/dist/src/common/exceptions/ServiceException.d.ts.map +1 -0
  84. package/dist/src/common/exceptions/ServiceException.js +8 -0
  85. package/dist/src/common/exceptions/ServiceException.js.map +1 -0
  86. package/dist/src/common/exceptions/index.d.ts +6 -0
  87. package/dist/src/common/exceptions/index.d.ts.map +1 -0
  88. package/dist/src/common/exceptions/index.js +6 -0
  89. package/dist/src/common/exceptions/index.js.map +1 -0
  90. package/dist/src/common/migrate/index.d.ts +3 -0
  91. package/dist/src/common/migrate/index.d.ts.map +1 -0
  92. package/dist/src/common/migrate/index.js +49 -0
  93. package/dist/src/common/migrate/index.js.map +1 -0
  94. package/dist/src/common/redis/index.d.ts +14 -0
  95. package/dist/src/common/redis/index.d.ts.map +1 -0
  96. package/dist/src/common/redis/index.js +41 -0
  97. package/dist/src/common/redis/index.js.map +1 -0
  98. package/dist/src/common/scanner/auth-factory.d.ts +42 -0
  99. package/dist/src/common/scanner/auth-factory.d.ts.map +1 -0
  100. package/dist/src/common/scanner/auth-factory.js +86 -0
  101. package/dist/src/common/scanner/auth-factory.js.map +1 -0
  102. package/dist/src/common/scanner/decorators.d.ts +123 -0
  103. package/dist/src/common/scanner/decorators.d.ts.map +1 -0
  104. package/dist/src/common/scanner/decorators.js +158 -0
  105. package/dist/src/common/scanner/decorators.js.map +1 -0
  106. package/dist/src/common/scanner/handler-factory.d.ts +38 -0
  107. package/dist/src/common/scanner/handler-factory.d.ts.map +1 -0
  108. package/dist/src/common/scanner/handler-factory.js +76 -0
  109. package/dist/src/common/scanner/handler-factory.js.map +1 -0
  110. package/dist/src/common/scanner/index.d.ts +16 -0
  111. package/dist/src/common/scanner/index.d.ts.map +1 -0
  112. package/dist/src/common/scanner/index.js +157 -0
  113. package/dist/src/common/scanner/index.js.map +1 -0
  114. package/dist/src/common/validation/decorators.d.ts +28 -0
  115. package/dist/src/common/validation/decorators.d.ts.map +1 -0
  116. package/dist/src/common/validation/decorators.js +70 -0
  117. package/dist/src/common/validation/decorators.js.map +1 -0
  118. package/dist/src/common/validation/errorRules.d.ts +5 -0
  119. package/dist/src/common/validation/errorRules.d.ts.map +1 -0
  120. package/dist/src/common/validation/errorRules.js +56 -0
  121. package/dist/src/common/validation/errorRules.js.map +1 -0
  122. package/dist/src/common/validation/index.d.ts +4 -0
  123. package/dist/src/common/validation/index.d.ts.map +1 -0
  124. package/dist/src/common/validation/index.js +3 -0
  125. package/dist/src/common/validation/index.js.map +1 -0
  126. package/dist/src/common/validation/schema.d.ts +35 -0
  127. package/dist/src/common/validation/schema.d.ts.map +1 -0
  128. package/dist/src/common/validation/schema.js +67 -0
  129. package/dist/src/common/validation/schema.js.map +1 -0
  130. package/dist/src/index.d.ts +26 -0
  131. package/dist/src/index.d.ts.map +1 -0
  132. package/dist/src/index.js +293 -0
  133. package/dist/src/index.js.map +1 -0
  134. package/dist/src/main.d.ts +2 -0
  135. package/dist/src/main.d.ts.map +1 -0
  136. package/dist/src/main.js +27 -0
  137. package/dist/src/main.js.map +1 -0
  138. package/dist/src/modules/health/controller/HealthController.d.ts +11 -0
  139. package/dist/src/modules/health/controller/HealthController.d.ts.map +1 -0
  140. package/dist/src/modules/health/controller/HealthController.js +31 -0
  141. package/dist/src/modules/health/controller/HealthController.js.map +1 -0
  142. package/dist/src/modules/health/service/HealthService.d.ts +4 -0
  143. package/dist/src/modules/health/service/HealthService.d.ts.map +1 -0
  144. package/dist/src/modules/health/service/HealthService.js +6 -0
  145. package/dist/src/modules/health/service/HealthService.js.map +1 -0
  146. package/dist/src/types.d.ts +75 -0
  147. package/dist/src/types.d.ts.map +1 -0
  148. package/dist/src/types.js +2 -0
  149. package/dist/src/types.js.map +1 -0
  150. package/package.json +53 -0
@@ -0,0 +1,158 @@
1
+ import 'reflect-metadata';
2
+ // Metadata keys
3
+ export const CONTROLLER_PREFIX = Symbol('controller:prefix');
4
+ export const ROUTES = Symbol('controller:routes');
5
+ export const LOGIN_META = Symbol('route:login');
6
+ export const PUBLIC_META = Symbol('route:public');
7
+ export const VALID_META = Symbol('route:valid');
8
+ export const STREAM_META = Symbol('route:stream');
9
+ export const RPC_META = Symbol('controller:rpc');
10
+ export const FILE_META = Symbol('route:file');
11
+ export const MULTIPART_META = Symbol('route:multipart');
12
+ /**
13
+ * Marks a class as a controller and sets the route prefix.
14
+ */
15
+ export function Controller(prefix) {
16
+ return (target) => {
17
+ Reflect.defineMetadata(CONTROLLER_PREFIX, prefix, target);
18
+ };
19
+ }
20
+ /**
21
+ * Marks a controller class as RPC-style.
22
+ *
23
+ * RPC controllers differ from regular controllers:
24
+ * - All routes must use POST (JSON body)
25
+ * - URL must not contain path parameters (e.g. /user/:id)
26
+ * - Handler methods receive (body, user) instead of (request, reply)
27
+ * - Routes are auto-derived from module directory, controller name, and method name
28
+ *
29
+ * URL pattern: `/{module}/{controller}/{method}`
30
+ * - Module: folder name under src/modules/, overridable via route.config.ts
31
+ * - Controller: set via the `name` argument (defaults to class name)
32
+ * - Method name stays as-is in code
33
+ *
34
+ * Does not require @Controller — @Rpc() on its own is sufficient.
35
+ *
36
+ * @example
37
+ * ```ts
38
+ * @Rpc('users')
39
+ * class UserRpc {
40
+ * list(body: { page: number }, user: User) { ... }
41
+ * // → POST /{module}/users/list
42
+ * }
43
+ * ```
44
+ */
45
+ export function Rpc(name) {
46
+ return (target) => {
47
+ const meta = name ? { name } : {};
48
+ Reflect.defineMetadata(RPC_META, meta, target);
49
+ };
50
+ }
51
+ function createRouteDecorator(method) {
52
+ return (path) => {
53
+ return (target, propertyKey) => {
54
+ const existingRoutes = Reflect.getMetadata(ROUTES, target.constructor) || [];
55
+ existingRoutes.push({ method, path, handlerName: propertyKey });
56
+ Reflect.defineMetadata(ROUTES, existingRoutes, target.constructor);
57
+ };
58
+ };
59
+ }
60
+ export const Get = createRouteDecorator('GET');
61
+ export const Post = createRouteDecorator('POST');
62
+ export const Put = createRouteDecorator('PUT');
63
+ export const Delete = createRouteDecorator('DELETE');
64
+ /**
65
+ * Requires login authentication for the route.
66
+ * Can be used as method decorator (on individual methods) or class decorator
67
+ * (applies to all methods in the controller, overridable by method-level @Login or @Public).
68
+ * @param module Required module (subsystem) e.g. 'admin', 'bd', 'merchant'. Checks against user.type in JWT.
69
+ */
70
+ export function Login(module) {
71
+ return ((target, propertyKey) => {
72
+ const meta = {};
73
+ if (module)
74
+ meta.module = module;
75
+ if (propertyKey !== undefined) {
76
+ Reflect.defineMetadata(LOGIN_META, meta, target.constructor, propertyKey);
77
+ }
78
+ else {
79
+ Reflect.defineMetadata(LOGIN_META, meta, target);
80
+ }
81
+ });
82
+ }
83
+ /**
84
+ * Explicitly marks a route as public (no authentication).
85
+ */
86
+ export function Public() {
87
+ return (target, propertyKey) => {
88
+ Reflect.defineMetadata(PUBLIC_META, true, target.constructor, propertyKey);
89
+ };
90
+ }
91
+ /**
92
+ * Attaches a JSON Schema for Fastify validation.
93
+ * @param schema A Fastify route schema object (body/query/params/headers).
94
+ */
95
+ export function Valid(schema) {
96
+ return (target, propertyKey) => {
97
+ const existing = Reflect.getMetadata(VALID_META, target.constructor, propertyKey) || {};
98
+ Reflect.defineMetadata(VALID_META, { ...existing, ...schema }, target.constructor, propertyKey);
99
+ };
100
+ }
101
+ /**
102
+ * Marks a route that streams content as a downloadable file.
103
+ * The controller method must return `StreamResponse { name, content }`.
104
+ *
105
+ * @param description Optional OpenAPI description for the response.
106
+ */
107
+ export function Stream(description) {
108
+ return (target, propertyKey) => {
109
+ Reflect.defineMetadata(STREAM_META, true, target.constructor, propertyKey);
110
+ if (description) {
111
+ const existing = Reflect.getMetadata(VALID_META, target.constructor, propertyKey) || {};
112
+ const responseEntry = {
113
+ description,
114
+ content: {
115
+ 'application/octet-stream': {
116
+ schema: { type: 'string', format: 'binary' },
117
+ },
118
+ },
119
+ };
120
+ Reflect.defineMetadata(VALID_META, { ...existing, response: { 200: responseEntry } }, target.constructor, propertyKey);
121
+ }
122
+ };
123
+ }
124
+ /**
125
+ * Marks a route that sends a file from the server filesystem as download.
126
+ * The controller method must return `FileResponse { name, path }`.
127
+ * The framework reads the file at `path` and sends it with the given filename.
128
+ *
129
+ * @param description Optional OpenAPI description for the response.
130
+ */
131
+ export function File(description) {
132
+ return (target, propertyKey) => {
133
+ Reflect.defineMetadata(FILE_META, true, target.constructor, propertyKey);
134
+ if (description) {
135
+ const existing = Reflect.getMetadata(VALID_META, target.constructor, propertyKey) || {};
136
+ const responseEntry = {
137
+ description,
138
+ content: {
139
+ 'application/octet-stream': {
140
+ schema: { type: 'string', format: 'binary' },
141
+ },
142
+ },
143
+ };
144
+ Reflect.defineMetadata(VALID_META, { ...existing, response: { 200: responseEntry } }, target.constructor, propertyKey);
145
+ }
146
+ };
147
+ }
148
+ /**
149
+ * Marks a route that handles multipart/form-data file upload.
150
+ * No @Body is required — the file cannot be validated as JSON Schema.
151
+ * This is a pure marker; the framework does not attach any runtime behavior.
152
+ */
153
+ export function Multipart() {
154
+ return (target, propertyKey) => {
155
+ Reflect.defineMetadata(MULTIPART_META, true, target.constructor, propertyKey);
156
+ };
157
+ }
158
+ //# sourceMappingURL=decorators.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decorators.js","sourceRoot":"","sources":["../../../../src/common/scanner/decorators.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ import type { FastifyRequest, FastifyReply } from 'fastify';
2
+ export type RouteHandler<R = unknown> = (request: FastifyRequest, reply: FastifyReply) => R;
3
+ export interface HandlerMeta {
4
+ /** Route belongs to an RPC controller */
5
+ isRpc: boolean;
6
+ /** Route has @File decorator */
7
+ isFile: boolean;
8
+ /** Route has @Stream decorator */
9
+ isStream: boolean;
10
+ }
11
+ export interface HandlerFactory {
12
+ /** Returns true if this factory can handle the given route metadata */
13
+ accept(meta: HandlerMeta): boolean;
14
+ /** Creates a Fastify route handler — call style is baked in at creation time */
15
+ create(boundHandler: (...args: unknown[]) => unknown, meta: HandlerMeta): RouteHandler;
16
+ }
17
+ /** Sends file from disk with Content-Disposition header */
18
+ export declare class FileHandlerFactory implements HandlerFactory {
19
+ accept(meta: HandlerMeta): boolean;
20
+ create(boundHandler: (...args: unknown[]) => unknown, meta: HandlerMeta): RouteHandler;
21
+ }
22
+ /** Sends content with Content-Disposition header for streaming */
23
+ export declare class StreamHandlerFactory implements HandlerFactory {
24
+ accept(meta: HandlerMeta): boolean;
25
+ create(boundHandler: (...args: unknown[]) => unknown, meta: HandlerMeta): RouteHandler;
26
+ }
27
+ /** Default handler: passes request & reply, returns result as-is */
28
+ export declare class DefaultHandlerFactory implements HandlerFactory {
29
+ accept(meta: HandlerMeta): boolean;
30
+ create(boundHandler: (...args: unknown[]) => unknown, meta: HandlerMeta): RouteHandler;
31
+ }
32
+ /** Registry that holds HandlerFactory instances and delegates via first-match */
33
+ export declare class HandlerFactoryRegistry {
34
+ private factories;
35
+ register(factory: HandlerFactory): void;
36
+ create(boundHandler: (...args: unknown[]) => unknown, meta: HandlerMeta): RouteHandler;
37
+ }
38
+ //# sourceMappingURL=handler-factory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler-factory.d.ts","sourceRoot":"","sources":["../../../../src/common/scanner/handler-factory.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAG5D,MAAM,MAAM,YAAY,CAAC,CAAC,GAAG,OAAO,IAAI,CAAC,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,YAAY,KAAK,CAAC,CAAC;AAE5F,MAAM,WAAW,WAAW;IAC1B,yCAAyC;IACzC,KAAK,EAAE,OAAO,CAAC;IACf,gCAAgC;IAChC,MAAM,EAAE,OAAO,CAAC;IAChB,kCAAkC;IAClC,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,uEAAuE;IACvE,MAAM,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC;IACnC,gFAAgF;IAChF,MAAM,CAAC,YAAY,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,EAAE,IAAI,EAAE,WAAW,GAAG,YAAY,CAAC;CACxF;AAYD,2DAA2D;AAC3D,qBAAa,kBAAmB,YAAW,cAAc;IACvD,MAAM,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO;IAGlC,MAAM,CAAC,YAAY,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,EAAE,IAAI,EAAE,WAAW,GAAG,YAAY;CAiBvF;AAED,kEAAkE;AAClE,qBAAa,oBAAqB,YAAW,cAAc;IACzD,MAAM,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO;IAGlC,MAAM,CAAC,YAAY,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,EAAE,IAAI,EAAE,WAAW,GAAG,YAAY;CAavF;AAED,oEAAoE;AACpE,qBAAa,qBAAsB,YAAW,cAAc;IAC1D,MAAM,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO;IAGlC,MAAM,CAAC,YAAY,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,EAAE,IAAI,EAAE,WAAW,GAAG,YAAY;CAGvF;AAED,iFAAiF;AACjF,qBAAa,sBAAsB;IACjC,OAAO,CAAC,SAAS,CAAwB;IAEzC,QAAQ,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI;IAIvC,MAAM,CAAC,YAAY,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,EAAE,IAAI,EAAE,WAAW,GAAG,YAAY;CAUvF"}
@@ -0,0 +1,76 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ function createCaller(boundHandler, meta) {
3
+ if (meta.isRpc) {
4
+ return (req) => boundHandler(req.body, req.user);
5
+ }
6
+ return boundHandler;
7
+ }
8
+ /** Sends file from disk with Content-Disposition header */
9
+ export class FileHandlerFactory {
10
+ accept(meta) {
11
+ return meta.isFile;
12
+ }
13
+ create(boundHandler, meta) {
14
+ const caller = createCaller(boundHandler, meta);
15
+ return async (request, reply) => {
16
+ try {
17
+ const result = await caller(request, reply);
18
+ const fr = result;
19
+ if (typeof fr.path !== 'string' || typeof fr.name !== 'string') {
20
+ throw new Error('@File must return FileResponse { name, path }');
21
+ }
22
+ const content = await readFile(fr.path);
23
+ reply.header('Content-Disposition', `attachment; filename*=UTF-8''${encodeURIComponent(fr.name)}`);
24
+ return reply.send(content);
25
+ }
26
+ catch (err) {
27
+ reply.status(500).send({ error: err instanceof Error ? err.message : 'Internal error' });
28
+ }
29
+ };
30
+ }
31
+ }
32
+ /** Sends content with Content-Disposition header for streaming */
33
+ export class StreamHandlerFactory {
34
+ accept(meta) {
35
+ return meta.isStream;
36
+ }
37
+ create(boundHandler, meta) {
38
+ const caller = createCaller(boundHandler, meta);
39
+ return async (request, reply) => {
40
+ try {
41
+ const result = await caller(request, reply);
42
+ const sr = result;
43
+ reply.header('Content-Disposition', `attachment; filename*=UTF-8''${encodeURIComponent(sr.name)}`);
44
+ return reply.send(sr.content);
45
+ }
46
+ catch (err) {
47
+ reply.status(500).send({ error: err instanceof Error ? err.message : 'Internal error' });
48
+ }
49
+ };
50
+ }
51
+ }
52
+ /** Default handler: passes request & reply, returns result as-is */
53
+ export class DefaultHandlerFactory {
54
+ accept(meta) {
55
+ return true;
56
+ }
57
+ create(boundHandler, meta) {
58
+ return createCaller(boundHandler, meta);
59
+ }
60
+ }
61
+ /** Registry that holds HandlerFactory instances and delegates via first-match */
62
+ export class HandlerFactoryRegistry {
63
+ factories = [];
64
+ register(factory) {
65
+ this.factories.push(factory);
66
+ }
67
+ create(boundHandler, meta) {
68
+ for (const factory of this.factories) {
69
+ if (factory.accept(meta)) {
70
+ return factory.create(boundHandler, meta);
71
+ }
72
+ }
73
+ throw new Error(`No HandlerFactory found for meta: isRpc=${meta.isRpc}, isFile=${meta.isFile}, isStream=${meta.isStream}`);
74
+ }
75
+ }
76
+ //# sourceMappingURL=handler-factory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler-factory.js","sourceRoot":"","sources":["../../../../src/common/scanner/handler-factory.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import type { FastifyInstance } from 'fastify';
2
+ import type { AuthStrategy } from '../auth/AuthStrategy';
3
+ export { HandlerFactoryRegistry, FileHandlerFactory, StreamHandlerFactory, DefaultHandlerFactory } from './handler-factory';
4
+ export type { HandlerFactory, HandlerMeta, RouteHandler } from './handler-factory';
5
+ export { AuthHookFactoryRegistry, PublicAuthHookFactory, HmacAuthHookFactory, JwtAuthHookFactory, authLabel } from './auth-factory';
6
+ export type { AuthHookFactory, AuthHook, AuthHookMeta } from './auth-factory';
7
+ export interface ScanOptions {
8
+ /** Base directory for glob (absolute path to project root). */
9
+ cwd: string;
10
+ /** Auth strategy for signature verification (HMAC). */
11
+ signStrategy?: AuthStrategy;
12
+ /** Auth strategy for login (JWT). Only used when @Login is present. */
13
+ loginStrategy?: AuthStrategy;
14
+ }
15
+ export declare function scanControllers(fastify: FastifyInstance, options: ScanOptions): Promise<void>;
16
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/common/scanner/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAe/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAgBzD,OAAO,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAC5H,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACnF,OAAO,EAAE,uBAAuB,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AACpI,YAAY,EAAE,eAAe,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAa9E,MAAM,WAAW,WAAW;IAC1B,+DAA+D;IAC/D,GAAG,EAAE,MAAM,CAAC;IACZ,uDAAuD;IACvD,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,uEAAuE;IACvE,aAAa,CAAC,EAAE,YAAY,CAAC;CAC9B;AAED,wBAAsB,eAAe,CAAC,OAAO,EAAE,eAAe,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAqJnG"}
@@ -0,0 +1,157 @@
1
+ import path from 'node:path';
2
+ import { pathToFileURL } from 'node:url';
3
+ import fg from 'fast-glob';
4
+ import { container } from '../container/index';
5
+ import { CONTROLLER_PREFIX, ROUTES, LOGIN_META, PUBLIC_META, VALID_META, STREAM_META, RPC_META, FILE_META, } from './decorators';
6
+ import { HandlerFactoryRegistry, FileHandlerFactory, StreamHandlerFactory, DefaultHandlerFactory, } from './handler-factory';
7
+ import { AuthHookFactoryRegistry, PublicAuthHookFactory, HmacAuthHookFactory, JwtAuthHookFactory, authLabel, } from './auth-factory';
8
+ export { HandlerFactoryRegistry, FileHandlerFactory, StreamHandlerFactory, DefaultHandlerFactory } from './handler-factory';
9
+ export { AuthHookFactoryRegistry, PublicAuthHookFactory, HmacAuthHookFactory, JwtAuthHookFactory, authLabel } from './auth-factory';
10
+ // Default registries — can be replaced or extended by callers
11
+ const handlerRegistry = new HandlerFactoryRegistry();
12
+ handlerRegistry.register(new FileHandlerFactory());
13
+ handlerRegistry.register(new StreamHandlerFactory());
14
+ handlerRegistry.register(new DefaultHandlerFactory());
15
+ const authHookRegistry = new AuthHookFactoryRegistry();
16
+ authHookRegistry.register(new PublicAuthHookFactory());
17
+ authHookRegistry.register(new JwtAuthHookFactory());
18
+ authHookRegistry.register(new HmacAuthHookFactory());
19
+ export async function scanControllers(fastify, options) {
20
+ const pattern = 'src/modules/**/controller/*.{ts,js}';
21
+ const files = await fg(pattern, { cwd: options.cwd, absolute: true });
22
+ // Cache: module folder → resolved module config (nulls = use defaults)
23
+ const moduleConfigCache = new Map();
24
+ for (const file of files) {
25
+ const mod = await import(pathToFileURL(file).href);
26
+ for (const exportKey of Object.keys(mod)) {
27
+ const exported = mod[exportKey];
28
+ // Only process classes
29
+ if (typeof exported !== 'function' || !exported.prototype) {
30
+ continue;
31
+ }
32
+ const prefix = Reflect.getMetadata(CONTROLLER_PREFIX, exported);
33
+ const rpcMeta = Reflect.getMetadata(RPC_META, exported);
34
+ const isRpc = rpcMeta !== undefined;
35
+ // Skip if neither @Controller nor @Rpc
36
+ if (prefix === undefined && !isRpc) {
37
+ continue;
38
+ }
39
+ const routes = Reflect.getMetadata(ROUTES, exported) || [];
40
+ // RPC auto-scan: generate routes from all public methods
41
+ if (routes.length === 0 && isRpc) {
42
+ const proto = exported.prototype;
43
+ const methodNames = Object.getOwnPropertyNames(proto)
44
+ .filter(n => n !== 'constructor' && typeof proto[n] === 'function');
45
+ for (const name of methodNames) {
46
+ routes.push({ method: 'POST', path: '/' + name, handlerName: name });
47
+ }
48
+ }
49
+ if (routes.length === 0) {
50
+ continue;
51
+ }
52
+ // RPC path derivation: if no @Controller, derive from module directory + controller name
53
+ let effectivePrefix = prefix;
54
+ let moduleCfg;
55
+ if (isRpc && prefix === undefined && rpcMeta) {
56
+ const relativePath = path.relative(options.cwd, file).replace(/\\/g, '/');
57
+ const moduleMatch = relativePath.match(/src\/modules\/([^/]+)\/controller\//);
58
+ const moduleFolder = moduleMatch ? moduleMatch[1] : 'unknown';
59
+ // Resolve module config from route.config.ts (cached per module)
60
+ if (!moduleConfigCache.has(moduleFolder)) {
61
+ moduleConfigCache.set(moduleFolder, await resolveModuleConfig(options.cwd, moduleFolder));
62
+ }
63
+ moduleCfg = moduleConfigCache.get(moduleFolder) ?? { urlPrefix: null, module: null };
64
+ const urlPrefix = moduleCfg.urlPrefix ?? moduleFolder;
65
+ const controllerName = rpcMeta.name ?? exportKey;
66
+ effectivePrefix = '/' + urlPrefix + '/' + controllerName;
67
+ }
68
+ // Instantiate controller via container (DI)
69
+ let instance;
70
+ try {
71
+ instance = container.resolve(exported);
72
+ }
73
+ catch {
74
+ // No explicit registration — try creating with default constructor
75
+ instance = new exported();
76
+ }
77
+ // Collect route info for logging
78
+ const routeDescs = [];
79
+ // Register each route
80
+ for (const route of routes) {
81
+ const handler = instance[route.handlerName];
82
+ if (typeof handler !== 'function') {
83
+ throw new Error(`${exported.name}.${route.handlerName} is not a method`);
84
+ }
85
+ const loginMeta = Reflect.getMetadata(LOGIN_META, exported, route.handlerName);
86
+ const isPublic = Reflect.getMetadata(PUBLIC_META, exported, route.handlerName) === true;
87
+ // Build effective login meta: method-level → class-level → module-level
88
+ let effectiveLogin;
89
+ const classLoginMeta = loginMeta ?? Reflect.getMetadata(LOGIN_META, exported);
90
+ if (!isPublic) {
91
+ const module = classLoginMeta?.module ?? moduleCfg?.module ?? undefined;
92
+ if (module || classLoginMeta) {
93
+ effectiveLogin = module ? { module } : {};
94
+ }
95
+ }
96
+ const isStream = Reflect.getMetadata(STREAM_META, exported, route.handlerName) === true;
97
+ const isFile = Reflect.getMetadata(FILE_META, exported, route.handlerName) === true;
98
+ const schema = Reflect.getMetadata(VALID_META, exported, route.handlerName);
99
+ const fullPath = (effectivePrefix + route.path).replace(/\/$/, '') || '/';
100
+ // RPC constraints: POST only, no path params
101
+ if (isRpc) {
102
+ if (route.method !== 'POST') {
103
+ throw new Error(`RPC controller ${exported.name} only supports POST method, got ${route.method} on ${route.handlerName}`);
104
+ }
105
+ if (fullPath.includes(':')) {
106
+ throw new Error(`RPC controller ${exported.name} does not support URL parameters, got ${fullPath} on ${route.handlerName}`);
107
+ }
108
+ }
109
+ // Bind handler to instance
110
+ const boundHandler = handler.bind(instance);
111
+ // Delegate handler creation to factory registry
112
+ const handlerMeta = { isRpc, isFile, isStream };
113
+ const routeHandler = handlerRegistry.create(boundHandler, handlerMeta);
114
+ // Delegate auth hook creation to factory registry
115
+ const authMeta = { isPublic, loginMeta: effectiveLogin };
116
+ const preValidationHooks = authHookRegistry.create(authMeta, options);
117
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
118
+ const routeDef = {
119
+ method: route.method,
120
+ url: fullPath,
121
+ handler: routeHandler,
122
+ };
123
+ if (schema) {
124
+ routeDef.schema = schema;
125
+ }
126
+ if (preValidationHooks.length > 0) {
127
+ routeDef.preValidation = preValidationHooks;
128
+ }
129
+ fastify.route(routeDef);
130
+ routeDescs.push(`${route.method} ${fullPath} [${authLabel(authMeta)}]`);
131
+ }
132
+ fastify.log.info({ controller: exported.name, routes: routeDescs }, 'Controller registered');
133
+ }
134
+ }
135
+ }
136
+ /**
137
+ * Try to load route.config.{ts,js} from the module directory.
138
+ * Returns { module, role } — null values mean "use defaults".
139
+ */
140
+ async function resolveModuleConfig(cwd, moduleFolder) {
141
+ const base = path.join(cwd, 'src', 'modules', moduleFolder, 'route.config');
142
+ for (const ext of ['.ts', '.js']) {
143
+ try {
144
+ const mod = await import(pathToFileURL(base + ext).href);
145
+ const config = (mod.default ?? mod);
146
+ return {
147
+ urlPrefix: (config && typeof config.urlPrefix === 'string' && config.urlPrefix.length > 0) ? config.urlPrefix : null,
148
+ module: (config && typeof config.module === 'string' && config.module.length > 0) ? config.module : null,
149
+ };
150
+ }
151
+ catch {
152
+ // file doesn't exist or can't be imported, try next extension
153
+ }
154
+ }
155
+ return { urlPrefix: null, module: null };
156
+ }
157
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/common/scanner/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ import { type SchemaProp } from './schema';
2
+ /** Accepts either @fastify-api/api SchemaProp map or a raw JSON Schema (e.g. from Typebox). */
3
+ export type SchemaLike = Record<string, SchemaProp> | Record<string, unknown>;
4
+ /** Declare request body validation schema. */
5
+ export declare const Body: (def: SchemaLike) => MethodDecorator;
6
+ /** Declare query string validation schema. */
7
+ export declare const Query: (def: SchemaLike) => MethodDecorator;
8
+ /** Declare path params validation schema. */
9
+ export declare const Params: (def: SchemaLike) => MethodDecorator;
10
+ /**
11
+ * Declare response schema for a given HTTP status code.
12
+ * Accepts @fastify-api/api SchemaProp map OR a raw JSON Schema (e.g. from Typebox).
13
+ *
14
+ * @param description Response description
15
+ * @param def Schema definition — either `{ token: Required(String) }` or a Typebox schema
16
+ * @param statusCode HTTP status code, default 200
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * // @fastify-api/api style
21
+ * @Response('登录成功', { token: Required(String) })
22
+ *
23
+ * // Typebox style
24
+ * @Response('登录成功', Type.Object({ token: Type.String() }))
25
+ * ```
26
+ */
27
+ export declare function Response(description: string, def: SchemaLike, statusCode?: number): MethodDecorator;
28
+ //# sourceMappingURL=decorators.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decorators.d.ts","sourceRoot":"","sources":["../../../../src/common/validation/decorators.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,UAAU,EAAE,MAAM,UAAU,CAAC;AAG9D,+FAA+F;AAC/F,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAoB9E,8CAA8C;AAC9C,eAAO,MAAM,IAAI,QAlBF,UAAU,KAAG,eAkByB,CAAC;AAEtD,8CAA8C;AAC9C,eAAO,MAAM,KAAK,QArBH,UAAU,KAAG,eAqBiC,CAAC;AAE9D,6CAA6C;AAC7C,eAAO,MAAM,MAAM,QAxBJ,UAAU,KAAG,eAwB6B,CAAC;AAmB1D;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,QAAQ,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,EAAE,UAAU,GAAE,MAAY,GAAG,eAAe,CAuBxG"}
@@ -0,0 +1,70 @@
1
+ import { buildObjectSchema } from './schema';
2
+ import { VALID_META } from '../scanner/decorators';
3
+ function createValidationDecorator(targetKey) {
4
+ return (def) => {
5
+ return (target, propertyKey) => {
6
+ const schema = isRawJsonSchema(def)
7
+ ? def
8
+ : buildObjectSchema(def);
9
+ const existing = Reflect.getMetadata(VALID_META, target.constructor, propertyKey) || {};
10
+ Reflect.defineMetadata(VALID_META, { ...existing, [targetKey]: schema }, target.constructor, propertyKey);
11
+ };
12
+ };
13
+ }
14
+ /** Declare request body validation schema. */
15
+ export const Body = createValidationDecorator('body');
16
+ /** Declare query string validation schema. */
17
+ export const Query = createValidationDecorator('querystring');
18
+ /** Declare path params validation schema. */
19
+ export const Params = createValidationDecorator('params');
20
+ function isRawJsonSchema(def) {
21
+ return typeof def.type === 'string';
22
+ }
23
+ const DEFAULT_DESCRIPTIONS = {
24
+ 200: 'OK',
25
+ 201: 'Created',
26
+ 204: 'No Content',
27
+ 400: 'Bad Request',
28
+ 401: 'Unauthorized',
29
+ 403: 'Forbidden',
30
+ 404: 'Not Found',
31
+ 422: 'Unprocessable Entity',
32
+ 429: 'Too Many Requests',
33
+ 500: 'Internal Server Error',
34
+ };
35
+ /**
36
+ * Declare response schema for a given HTTP status code.
37
+ * Accepts @fastify-api/api SchemaProp map OR a raw JSON Schema (e.g. from Typebox).
38
+ *
39
+ * @param description Response description
40
+ * @param def Schema definition — either `{ token: Required(String) }` or a Typebox schema
41
+ * @param statusCode HTTP status code, default 200
42
+ *
43
+ * @example
44
+ * ```ts
45
+ * // @fastify-api/api style
46
+ * @Response('登录成功', { token: Required(String) })
47
+ *
48
+ * // Typebox style
49
+ * @Response('登录成功', Type.Object({ token: Type.String() }))
50
+ * ```
51
+ */
52
+ export function Response(description, def, statusCode = 200) {
53
+ return (target, propertyKey) => {
54
+ const bodySchema = isRawJsonSchema(def)
55
+ ? def
56
+ : buildObjectSchema(def);
57
+ const desc = description || DEFAULT_DESCRIPTIONS[statusCode] || 'Unknown';
58
+ // OpenAPI 3 format: description + content.application/json.schema
59
+ const responseEntry = {
60
+ description: desc,
61
+ content: {
62
+ 'application/json': { schema: bodySchema },
63
+ },
64
+ };
65
+ const responseWrapper = { [String(statusCode)]: responseEntry };
66
+ const existing = Reflect.getMetadata(VALID_META, target.constructor, propertyKey) || {};
67
+ Reflect.defineMetadata(VALID_META, { ...existing, response: responseWrapper }, target.constructor, propertyKey);
68
+ };
69
+ }
70
+ //# sourceMappingURL=decorators.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decorators.js","sourceRoot":"","sources":["../../../../src/common/validation/decorators.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAmB,MAAM,UAAU,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAKnD,SAAS,yBAAyB,CAAC,SAAiB;IAClD,OAAO,CAAC,GAAe,EAAmB,EAAE;QAC1C,OAAO,CAAC,MAAc,EAAE,WAA4B,EAAE,EAAE;YACtD,MAAM,MAAM,GAA4B,eAAe,CAAC,GAAG,CAAC;gBAC1D,CAAC,CAAE,GAA+B;gBAClC,CAAC,CAAC,iBAAiB,CAAC,GAAiC,CAAC,CAAC;YACzD,MAAM,QAAQ,GACZ,OAAO,CAAC,WAAW,CAAC,UAAU,EAAE,MAAM,CAAC,WAAW,EAAE,WAAW,CAAC,IAAI,EAAE,CAAC;YACzE,OAAO,CAAC,cAAc,CACpB,UAAU,EACV,EAAE,GAAG,QAAQ,EAAE,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,EACpC,MAAM,CAAC,WAAW,EAClB,WAAW,CACZ,CAAC;QACJ,CAAC,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC;AAED,8CAA8C;AAC9C,MAAM,CAAC,MAAM,IAAI,GAAG,yBAAyB,CAAC,MAAM,CAAC,CAAC;AAEtD,8CAA8C;AAC9C,MAAM,CAAC,MAAM,KAAK,GAAG,yBAAyB,CAAC,aAAa,CAAC,CAAC;AAE9D,6CAA6C;AAC7C,MAAM,CAAC,MAAM,MAAM,GAAG,yBAAyB,CAAC,QAAQ,CAAC,CAAC;AAE1D,SAAS,eAAe,CAAC,GAA4B;IACnD,OAAO,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ,CAAC;AACtC,CAAC;AAED,MAAM,oBAAoB,GAA2B;IACnD,GAAG,EAAE,IAAI;IACT,GAAG,EAAE,SAAS;IACd,GAAG,EAAE,YAAY;IACjB,GAAG,EAAE,aAAa;IAClB,GAAG,EAAE,cAAc;IACnB,GAAG,EAAE,WAAW;IAChB,GAAG,EAAE,WAAW;IAChB,GAAG,EAAE,sBAAsB;IAC3B,GAAG,EAAE,mBAAmB;IACxB,GAAG,EAAE,uBAAuB;CAC7B,CAAC;AAEF;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,QAAQ,CAAC,WAAmB,EAAE,GAAe,EAAE,aAAqB,GAAG;IACrF,OAAO,CAAC,MAAc,EAAE,WAA4B,EAAE,EAAE;QACtD,MAAM,UAAU,GAA4B,eAAe,CAAC,GAAG,CAAC;YAC9D,CAAC,CAAE,GAA+B;YAClC,CAAC,CAAC,iBAAiB,CAAC,GAAiC,CAAC,CAAC;QACzD,MAAM,IAAI,GAAG,WAAW,IAAI,oBAAoB,CAAC,UAAU,CAAC,IAAI,SAAS,CAAC;QAC1E,kEAAkE;QAClE,MAAM,aAAa,GAA4B;YAC7C,WAAW,EAAE,IAAI;YACjB,OAAO,EAAE;gBACP,kBAAkB,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE;aAC3C;SACF,CAAC;QACF,MAAM,eAAe,GAA4B,EAAE,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,EAAE,aAAa,EAAE,CAAC;QACzF,MAAM,QAAQ,GACZ,OAAO,CAAC,WAAW,CAAC,UAAU,EAAE,MAAM,CAAC,WAAW,EAAE,WAAW,CAAC,IAAI,EAAE,CAAC;QACzE,OAAO,CAAC,cAAc,CACpB,UAAU,EACV,EAAE,GAAG,QAAQ,EAAE,QAAQ,EAAE,eAAe,EAAE,EAC1C,MAAM,CAAC,WAAW,EAClB,WAAW,CACZ,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,5 @@
1
+ import type { ErrorObject } from 'ajv';
2
+ export type RuleFormatter = (fieldName: string) => string;
3
+ export declare function addRule(keyword: string, formatter: RuleFormatter): void;
4
+ export declare function formatError(errors: ErrorObject[] | undefined, dataVar?: string): string;
5
+ //# sourceMappingURL=errorRules.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errorRules.d.ts","sourceRoot":"","sources":["../../../../src/common/validation/errorRules.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,KAAK,CAAC;AAEvC,MAAM,MAAM,aAAa,GAAG,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,CAAC;AAI1D,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,aAAa,GAAG,IAAI,CAEvE;AAUD,wBAAgB,WAAW,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,SAAS,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAWvF"}