lapeeh 1.0.11 → 1.0.13

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 (76) hide show
  1. package/dist/lib/bootstrap.d.ts +3 -0
  2. package/dist/lib/bootstrap.d.ts.map +1 -0
  3. package/dist/lib/bootstrap.js +184 -0
  4. package/dist/lib/core/realtime.d.ts +3 -0
  5. package/dist/lib/core/realtime.d.ts.map +1 -0
  6. package/dist/lib/core/realtime.js +36 -0
  7. package/dist/lib/core/redis.d.ts +9 -0
  8. package/dist/lib/core/redis.d.ts.map +1 -0
  9. package/dist/lib/core/redis.js +133 -0
  10. package/dist/lib/core/serializer.d.ts +43 -0
  11. package/dist/lib/core/serializer.d.ts.map +1 -0
  12. package/dist/lib/core/serializer.js +66 -0
  13. package/dist/lib/core/server.d.ts +2 -0
  14. package/dist/lib/core/server.d.ts.map +1 -0
  15. package/dist/lib/core/server.js +60 -0
  16. package/dist/lib/core/store.d.ts +55 -0
  17. package/dist/lib/core/store.d.ts.map +1 -0
  18. package/dist/lib/core/store.js +66 -0
  19. package/dist/lib/middleware/auth.d.ts +4 -0
  20. package/dist/lib/middleware/auth.d.ts.map +1 -0
  21. package/dist/lib/middleware/auth.js +55 -0
  22. package/dist/lib/middleware/error.d.ts +3 -0
  23. package/dist/lib/middleware/error.d.ts.map +1 -0
  24. package/dist/lib/middleware/error.js +41 -0
  25. package/dist/lib/middleware/multipart.d.ts +4 -0
  26. package/dist/lib/middleware/multipart.d.ts.map +1 -0
  27. package/dist/lib/middleware/multipart.js +17 -0
  28. package/dist/lib/middleware/rateLimit.d.ts +2 -0
  29. package/dist/lib/middleware/rateLimit.d.ts.map +1 -0
  30. package/dist/lib/middleware/rateLimit.js +19 -0
  31. package/dist/lib/middleware/requestLogger.d.ts +3 -0
  32. package/dist/lib/middleware/requestLogger.d.ts.map +1 -0
  33. package/dist/lib/middleware/requestLogger.js +22 -0
  34. package/dist/lib/middleware/visitor.d.ts +3 -0
  35. package/dist/lib/middleware/visitor.d.ts.map +1 -0
  36. package/dist/lib/middleware/visitor.js +144 -0
  37. package/dist/lib/utils/logger.d.ts +11 -0
  38. package/dist/lib/utils/logger.d.ts.map +1 -0
  39. package/dist/lib/utils/logger.js +81 -0
  40. package/dist/lib/utils/pagination.d.ts +19 -0
  41. package/dist/lib/utils/pagination.d.ts.map +1 -0
  42. package/dist/lib/utils/pagination.js +34 -0
  43. package/dist/lib/utils/response.d.ts +11 -0
  44. package/dist/lib/utils/response.d.ts.map +1 -0
  45. package/dist/lib/utils/response.js +57 -0
  46. package/dist/lib/utils/validator.d.ts +38 -0
  47. package/dist/lib/utils/validator.d.ts.map +1 -0
  48. package/dist/lib/utils/validator.js +346 -0
  49. package/dist/src/config/app.d.ts +10 -0
  50. package/dist/src/config/app.d.ts.map +1 -0
  51. package/dist/src/config/app.js +12 -0
  52. package/dist/src/config/cors.d.ts +6 -0
  53. package/dist/src/config/cors.d.ts.map +1 -0
  54. package/dist/src/config/cors.js +8 -0
  55. package/dist/src/modules/Auth/auth.controller.d.ts +11 -0
  56. package/dist/src/modules/Auth/auth.controller.d.ts.map +1 -0
  57. package/dist/src/modules/Auth/auth.controller.js +427 -0
  58. package/dist/src/modules/Auth/auth.routes.d.ts +2 -0
  59. package/dist/src/modules/Auth/auth.routes.d.ts.map +1 -0
  60. package/dist/src/modules/Auth/auth.routes.js +45 -0
  61. package/dist/src/modules/Auth/auth.service.d.ts +3 -0
  62. package/dist/src/modules/Auth/auth.service.d.ts.map +1 -0
  63. package/dist/src/modules/Auth/auth.service.js +8 -0
  64. package/dist/src/modules/Rbac/rbac.controller.d.ts +16 -0
  65. package/dist/src/modules/Rbac/rbac.controller.d.ts.map +1 -0
  66. package/dist/src/modules/Rbac/rbac.controller.js +438 -0
  67. package/dist/src/modules/Rbac/rbac.routes.d.ts +2 -0
  68. package/dist/src/modules/Rbac/rbac.routes.d.ts.map +1 -0
  69. package/dist/src/modules/Rbac/rbac.routes.js +23 -0
  70. package/dist/src/modules/Rbac/rbac.service.d.ts +3 -0
  71. package/dist/src/modules/Rbac/rbac.service.d.ts.map +1 -0
  72. package/dist/src/modules/Rbac/rbac.service.js +8 -0
  73. package/dist/src/routes/index.d.ts +2 -0
  74. package/dist/src/routes/index.d.ts.map +1 -0
  75. package/dist/src/routes/index.js +9 -0
  76. package/package.json +23 -1
@@ -0,0 +1,38 @@
1
+ import { ZodSchema } from "zod";
2
+ export declare class Validator {
3
+ private data;
4
+ private schema;
5
+ private customMessages;
6
+ private result;
7
+ private errorResult;
8
+ private hasRun;
9
+ constructor(data: any, schema: ZodSchema<any> | Record<string, any>, messages?: Record<string, string>);
10
+ /**
11
+ * Create a new Validator instance
12
+ * @param data The input data to validate
13
+ * @param schema Zod schema or object of Zod schemas / string-based rules
14
+ * @param messages Optional custom error messages (style: 'field.rule' => 'message')
15
+ */
16
+ static make(data: any, schema: ZodSchema<any> | Record<string, any>, messages?: Record<string, string>): Validator;
17
+ private static parseStringRule;
18
+ /**
19
+ * Check if validation fails
20
+ */
21
+ fails(): Promise<boolean>;
22
+ /**
23
+ * Check if validation passes
24
+ */
25
+ passes(): Promise<boolean>;
26
+ /**
27
+ * Get the validation errors
28
+ */
29
+ errors(): any;
30
+ /**
31
+ * Get the validated data
32
+ */
33
+ validated(): Promise<any>;
34
+ private run;
35
+ private formatErrors;
36
+ private getCustomMessage;
37
+ }
38
+ //# sourceMappingURL=validator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../../lib/utils/validator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAK,SAAS,EAAsB,MAAM,KAAK,CAAC;AAWvD,qBAAa,SAAS;IACpB,OAAO,CAAC,IAAI,CAAM;IAClB,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,cAAc,CAAyB;IAC/C,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,WAAW,CAAa;IAChC,OAAO,CAAC,MAAM,CAAkB;gBAG9B,IAAI,EAAE,GAAG,EACT,MAAM,EAAE,SAAS,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC5C,QAAQ,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM;IAavC;;;;;OAKG;IACH,MAAM,CAAC,IAAI,CACT,IAAI,EAAE,GAAG,EACT,MAAM,EAAE,SAAS,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC5C,QAAQ,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM;IAiDvC,OAAO,CAAC,MAAM,CAAC,eAAe;IAsL9B;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,OAAO,CAAC;IAK/B;;OAEG;IACG,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC;IAIhC;;OAEG;IACH,MAAM;IAKN;;OAEG;IACG,SAAS;YAUD,GAAG;IAgBjB,OAAO,CAAC,YAAY;IAuBpB,OAAO,CAAC,gBAAgB;CAoDzB"}
@@ -0,0 +1,346 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Validator = void 0;
4
+ const zod_1 = require("zod");
5
+ function isZodSchema(schema) {
6
+ return (schema instanceof zod_1.ZodSchema ||
7
+ (schema &&
8
+ typeof schema === "object" &&
9
+ typeof schema.safeParseAsync === "function"));
10
+ }
11
+ class Validator {
12
+ constructor(data, schema, messages = {}) {
13
+ this.result = null;
14
+ this.errorResult = null;
15
+ this.hasRun = false;
16
+ this.data = data;
17
+ this.customMessages = messages;
18
+ // If it's a raw object, wrap it in z.object()
19
+ if (isZodSchema(schema)) {
20
+ this.schema = schema;
21
+ }
22
+ else {
23
+ this.schema = zod_1.z.object(schema);
24
+ }
25
+ }
26
+ /**
27
+ * Create a new Validator instance
28
+ * @param data The input data to validate
29
+ * @param schema Zod schema or object of Zod schemas / string-based rules
30
+ * @param messages Optional custom error messages (style: 'field.rule' => 'message')
31
+ */
32
+ static make(data, schema, messages = {}) {
33
+ if (isZodSchema(schema)) {
34
+ return new Validator(data, schema, messages);
35
+ }
36
+ const parsedSchema = {};
37
+ const sameRules = [];
38
+ for (const [key, rule] of Object.entries(schema)) {
39
+ if (isZodSchema(rule)) {
40
+ parsedSchema[key] = rule;
41
+ }
42
+ else if (typeof rule === "string" || Array.isArray(rule)) {
43
+ const ruleStr = Array.isArray(rule) ? rule.join("|") : rule;
44
+ // Check for 'same:target'
45
+ const parts = ruleStr.split("|");
46
+ for (const part of parts) {
47
+ const [name, args] = part.split(":");
48
+ if (name === "same" && args) {
49
+ sameRules.push({ field: key, target: args });
50
+ }
51
+ }
52
+ parsedSchema[key] = Validator.parseStringRule(rule);
53
+ }
54
+ else {
55
+ parsedSchema[key] = rule;
56
+ }
57
+ }
58
+ let objectSchema = zod_1.z.object(parsedSchema);
59
+ if (sameRules.length > 0) {
60
+ objectSchema = objectSchema.superRefine((val, ctx) => {
61
+ sameRules.forEach(({ field, target }) => {
62
+ if (val[field] !== val[target]) {
63
+ ctx.addIssue({
64
+ code: zod_1.z.ZodIssueCode.custom,
65
+ message: `The ${field} and ${target} must match.`,
66
+ path: [field],
67
+ });
68
+ }
69
+ });
70
+ });
71
+ }
72
+ return new Validator(data, objectSchema, messages);
73
+ }
74
+ static parseStringRule(rule) {
75
+ const rules = Array.isArray(rule)
76
+ ? rule
77
+ : rule.split("|").map((r) => r.trim());
78
+ let schema = zod_1.z.string(); // Default base
79
+ let isOptional = false;
80
+ let isNullable = false;
81
+ // 1. Determine Base Type
82
+ const isFile = rules.includes("file") ||
83
+ rules.includes("image") ||
84
+ rules.includes("mimes");
85
+ if (rules.includes("numeric") || rules.includes("integer")) {
86
+ schema = zod_1.z.coerce.number();
87
+ if (rules.includes("integer"))
88
+ schema = schema.int();
89
+ }
90
+ else if (rules.includes("boolean")) {
91
+ schema = zod_1.z.boolean();
92
+ }
93
+ else if (rules.includes("array")) {
94
+ schema = zod_1.z.array(zod_1.z.any());
95
+ }
96
+ else if (rules.includes("date")) {
97
+ schema = zod_1.z.coerce.date();
98
+ }
99
+ else if (isFile) {
100
+ schema = zod_1.z.any().refine((val) => {
101
+ // Check if it looks like a Multer file object
102
+ return (typeof val === "object" &&
103
+ val !== null &&
104
+ "fieldname" in val &&
105
+ "originalname" in val &&
106
+ "encoding" in val &&
107
+ "mimetype" in val &&
108
+ "size" in val);
109
+ }, { message: "The field must be a file." });
110
+ }
111
+ else {
112
+ schema = zod_1.z.string();
113
+ }
114
+ // 2. Constraints & Modifiers
115
+ for (const r of rules) {
116
+ const [name, args] = r.split(":");
117
+ const params = args ? args.split(",") : [];
118
+ switch (name) {
119
+ case "email":
120
+ if (schema instanceof zod_1.z.ZodString)
121
+ schema = schema.email();
122
+ break;
123
+ case "url":
124
+ if (schema instanceof zod_1.z.ZodString)
125
+ schema = schema.url();
126
+ break;
127
+ case "uuid":
128
+ if (schema instanceof zod_1.z.ZodString)
129
+ schema = schema.uuid();
130
+ break;
131
+ case "min":
132
+ if (!isNaN(Number(params[0])))
133
+ schema = schema.min(Number(params[0]));
134
+ break;
135
+ case "max":
136
+ if (!isNaN(Number(params[0]))) {
137
+ if (isFile) {
138
+ schema = schema.refine((val) => {
139
+ if (!val)
140
+ return true; // Let nullable/optional handle nulls
141
+ // But wait, if we are in strict mode (not nullable), we should fail?
142
+ // No, max() usually doesn't enforce existence. required() does.
143
+ // But here val is the file object.
144
+ // If val is missing, and it's required, Base Type would have failed (if I revert Base Type).
145
+ // So here val is likely a file object.
146
+ return val.size <= Number(params[0]) * 1024;
147
+ }, {
148
+ message: `The file may not be greater than ${params[0]} kilobytes.`,
149
+ });
150
+ }
151
+ else {
152
+ schema = schema.max(Number(params[0]));
153
+ }
154
+ }
155
+ break;
156
+ case "image":
157
+ schema = schema.refine((val) => {
158
+ if (!val || typeof val !== "object" || !val.mimetype)
159
+ return false;
160
+ return val.mimetype.startsWith("image/");
161
+ }, { message: "The field must be an image." });
162
+ break;
163
+ case "in":
164
+ if (params.length > 0) {
165
+ // For strings, we can use regex or refine
166
+ schema = schema.refine((val) => params.includes(String(val)), {
167
+ message: `The selected ${name} is invalid.`,
168
+ });
169
+ }
170
+ break;
171
+ case "not_in":
172
+ if (params.length > 0) {
173
+ schema = schema.refine((val) => !params.includes(String(val)), {
174
+ message: `The selected ${name} is invalid.`,
175
+ });
176
+ }
177
+ break;
178
+ case "alpha":
179
+ if (schema instanceof zod_1.z.ZodString)
180
+ schema = schema.regex(/^[a-zA-Z]+$/, "The field must only contain letters.");
181
+ break;
182
+ case "alpha_dash":
183
+ if (schema instanceof zod_1.z.ZodString)
184
+ schema = schema.regex(/^[a-zA-Z0-9_-]+$/, "The field must only contain letters, numbers, dashes, and underscores.");
185
+ break;
186
+ case "alpha_num":
187
+ if (schema instanceof zod_1.z.ZodString)
188
+ schema = schema.regex(/^[a-zA-Z0-9]+$/, "The field must only contain letters and numbers.");
189
+ break;
190
+ case "mimes":
191
+ // For file validation
192
+ schema = schema.refine((val) => {
193
+ if (!val || typeof val !== "object" || !val.mimetype)
194
+ return false;
195
+ // params are extensions (jpg, png). We need to map to mimetypes or check loosely.
196
+ // Simplification: check if mimetype includes one of the params
197
+ // Or better: map extensions to mimetypes? Too big.
198
+ // Let's assume params are extensions, and we check if mimetype matches.
199
+ // Actually, typically mimes:jpg,png means we check extension or mime.
200
+ // For now, let's just check if mimetype contains the string (imperfect but simple)
201
+ // OR check originalname extension.
202
+ const extension = val.originalname
203
+ ?.split(".")
204
+ .pop()
205
+ ?.toLowerCase();
206
+ return params.includes(extension);
207
+ }, {
208
+ message: `The file must be a file of type: ${params.join(", ")}.`,
209
+ });
210
+ break;
211
+ case "unique":
212
+ // unique:table,column,ignore,idColumn
213
+ // NOTE: Unique check requires Database implementation.
214
+ // Since v3.0.0 (No-ORM), this rule is disabled by default.
215
+ // You should implement your own uniqueness check manually in the controller.
216
+ break;
217
+ }
218
+ }
219
+ // 3. Modifiers
220
+ const isRequired = rules.includes("required");
221
+ isNullable = rules.includes("nullable");
222
+ isOptional = rules.includes("sometimes") || !isRequired;
223
+ if (isNullable) {
224
+ schema = schema.nullable();
225
+ }
226
+ if (isOptional) {
227
+ schema = schema.optional();
228
+ }
229
+ return schema;
230
+ }
231
+ /**
232
+ * Check if validation fails
233
+ */
234
+ async fails() {
235
+ await this.run();
236
+ return this.errorResult !== null;
237
+ }
238
+ /**
239
+ * Check if validation passes
240
+ */
241
+ async passes() {
242
+ return !(await this.fails());
243
+ }
244
+ /**
245
+ * Get the validation errors
246
+ */
247
+ errors() {
248
+ // Should be called after fails()
249
+ return this.errorResult || {};
250
+ }
251
+ /**
252
+ * Get the validated data
253
+ */
254
+ async validated() {
255
+ await this.run();
256
+ if (this.errorResult) {
257
+ throw new Error("Validation failed. Check errors() before calling validated().");
258
+ }
259
+ return this.result;
260
+ }
261
+ async run() {
262
+ if (this.hasRun)
263
+ return;
264
+ this.hasRun = true;
265
+ // safeParseAsync to handle async refinements (like unique)
266
+ const parseResult = await this.schema.safeParseAsync(this.data);
267
+ if (parseResult.success) {
268
+ this.result = parseResult.data;
269
+ this.errorResult = null;
270
+ }
271
+ else {
272
+ this.result = null;
273
+ this.errorResult = this.formatErrors(parseResult.error);
274
+ }
275
+ }
276
+ formatErrors(error) {
277
+ // If we have custom messages, we try to apply them
278
+ if (Object.keys(this.customMessages).length > 0) {
279
+ error.issues = error.issues.map((issue) => {
280
+ const message = this.getCustomMessage(issue);
281
+ if (message) {
282
+ return { ...issue, message };
283
+ }
284
+ return issue;
285
+ });
286
+ }
287
+ const flattened = error.flatten();
288
+ const errors = flattened.fieldErrors;
289
+ // Add formErrors if any
290
+ if (flattened.formErrors.length > 0) {
291
+ errors.formErrors = flattened.formErrors;
292
+ }
293
+ return errors;
294
+ }
295
+ getCustomMessage(issue) {
296
+ const path = issue.path.join(".");
297
+ let rule = "";
298
+ // Map Zod issue codes to "Laravel-like" rules for key lookup
299
+ switch (issue.code) {
300
+ case "invalid_type":
301
+ if (issue.received === "undefined" || issue.received === "null") {
302
+ rule = "required";
303
+ }
304
+ else {
305
+ // e.g. string, number, boolean
306
+ rule = issue.expected;
307
+ }
308
+ break;
309
+ case "invalid_string":
310
+ if (typeof issue.validation === "string") {
311
+ rule = issue.validation; // email, url, uuid, etc.
312
+ }
313
+ else {
314
+ rule = "string";
315
+ }
316
+ break;
317
+ case "too_small":
318
+ rule = "min";
319
+ break;
320
+ case "too_big":
321
+ rule = "max";
322
+ break;
323
+ case "custom":
324
+ // Custom rules often have messages already, but we can override
325
+ // For unique, mimes, etc.
326
+ // We might need to inspect the message to guess the rule?
327
+ // Or just use the path.
328
+ rule = "custom";
329
+ break;
330
+ // Add more mappings as needed
331
+ default:
332
+ rule = issue.code;
333
+ }
334
+ // Try keys: "field.rule" -> "field"
335
+ const specificKey = `${path}.${rule}`;
336
+ if (this.customMessages[specificKey]) {
337
+ return this.customMessages[specificKey];
338
+ }
339
+ // Fallback for generic field message "field"
340
+ if (this.customMessages[path]) {
341
+ return this.customMessages[path];
342
+ }
343
+ return undefined;
344
+ }
345
+ }
346
+ exports.Validator = Validator;
@@ -0,0 +1,10 @@
1
+ export declare const appConfig: {
2
+ name: string;
3
+ timeout: number;
4
+ compression: boolean;
5
+ rateLimit: {
6
+ windowMs: number;
7
+ max: number;
8
+ };
9
+ };
10
+ //# sourceMappingURL=app.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../../src/config/app.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,SAAS;;;;;;;;CAQrB,CAAC"}
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.appConfig = void 0;
4
+ exports.appConfig = {
5
+ name: "lapeeh API",
6
+ timeout: 30000, // 30 seconds
7
+ compression: true,
8
+ rateLimit: {
9
+ windowMs: 15 * 60 * 1000, // 15 minutes
10
+ max: 100, // Limit each IP to 100 requests per windowMs
11
+ },
12
+ };
@@ -0,0 +1,6 @@
1
+ export declare const corsConfig: {
2
+ origin: string;
3
+ credentials: boolean;
4
+ exposedHeaders: string[];
5
+ };
6
+ //# sourceMappingURL=cors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cors.d.ts","sourceRoot":"","sources":["../../../src/config/cors.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,UAAU;;;;CAItB,CAAC"}
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.corsConfig = void 0;
4
+ exports.corsConfig = {
5
+ origin: process.env.CORS_ORIGIN || '*',
6
+ credentials: true,
7
+ exposedHeaders: ['x-access-token', 'x-access-expires-at'],
8
+ };
@@ -0,0 +1,11 @@
1
+ import { Request, Response } from "express";
2
+ export declare const ACCESS_TOKEN_EXPIRES_IN_SECONDS: number;
3
+ export declare function register(req: Request, res: Response): Promise<void>;
4
+ export declare function login(req: Request, res: Response): Promise<void>;
5
+ export declare function me(req: Request, res: Response): Promise<void>;
6
+ export declare function logout(_req: Request, res: Response): Promise<void>;
7
+ export declare function refreshToken(req: Request, res: Response): Promise<void>;
8
+ export declare function updateAvatar(req: Request, res: Response): Promise<void>;
9
+ export declare function updatePassword(req: Request, res: Response): Promise<void>;
10
+ export declare function updateProfile(req: Request, res: Response): Promise<void>;
11
+ //# sourceMappingURL=auth.controller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.controller.d.ts","sourceRoot":"","sources":["../../../../src/modules/Auth/auth.controller.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAU5C,eAAO,MAAM,+BAA+B,QAAmB,CAAC;AA4EhE,wBAAsB,QAAQ,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,iBA0DzD;AAED,wBAAsB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,iBA6EtD;AAED,wBAAsB,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,iBAuDnD;AAED,wBAAsB,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,iBASxD;AAED,wBAAsB,YAAY,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,iBAoE7D;AAED,wBAAsB,YAAY,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,iBAwD7D;AAED,wBAAsB,cAAc,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,iBA6C/D;AAED,wBAAsB,aAAa,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,iBAkD9D"}