@hazeljs/swagger 0.8.6 β†’ 0.8.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,564 +1,139 @@
1
1
  # @hazeljs/swagger
2
2
 
3
- **API docs that write themselves.**
4
-
5
- Add `@ApiOperation` and `@ApiResponse` β€” get Swagger UI. No manual OpenAPI JSON. Decorators on your controllers, interactive docs at `/api-docs`. Your frontend team will thank you.
3
+ OpenAPI 3.0 documents and Swagger UI for HazelJS: class-level `@Swagger`, method-level `@ApiOperation`, automatic operation stubs for undocumented routes, and a small runtime config API.
6
4
 
7
5
  [![npm version](https://img.shields.io/npm/v/@hazeljs/swagger.svg)](https://www.npmjs.com/package/@hazeljs/swagger)
8
- [![npm downloads](https://img.shields.io/npm/dm/@hazeljs/swagger)](https://www.npmjs.com/package/@hazeljs/swagger)
9
6
  [![License: Apache-2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0)
10
7
 
11
- ## Features
12
-
13
- - πŸ“š **Auto-Generated Docs** - Automatic OpenAPI spec generation
14
- - 🎨 **Swagger UI** - Interactive API explorer
15
- - 🏷️ **Decorator-Based** - Document APIs with decorators
16
- - πŸ“ **Type Safety** - TypeScript integration
17
- - πŸ” **Authentication** - Document auth requirements
18
- - πŸ“Š **Request/Response Examples** - Add example payloads
19
- - 🎯 **Tags & Groups** - Organize endpoints
20
- - πŸ”„ **Multiple Formats** - JSON, YAML export
21
-
22
8
  ## Installation
23
9
 
24
10
  ```bash
25
- npm install @hazeljs/swagger
11
+ npm install @hazeljs/swagger @hazeljs/core
26
12
  ```
27
13
 
28
- ## Quick Start
14
+ ## Quick start
29
15
 
30
- ### 1. Configure Swagger Module
16
+ ### 1. Import the module and register your app root
31
17
 
32
18
  ```typescript
33
19
  import { HazelModule } from '@hazeljs/core';
34
20
  import { SwaggerModule } from '@hazeljs/swagger';
35
21
 
36
22
  @HazelModule({
37
- imports: [
38
- SwaggerModule.forRoot({
39
- title: 'My API',
40
- description: 'API documentation',
41
- version: '1.0.0',
42
- path: '/api-docs',
43
- }),
23
+ imports: [SwaggerModule],
24
+ controllers: [
25
+ /* ... */
44
26
  ],
45
27
  })
46
28
  export class AppModule {}
47
- ```
48
-
49
- ### 2. Document Controllers
50
-
51
- ```typescript
52
- import { Controller, Get, Post, Body, Param } from '@hazeljs/core';
53
- import { ApiOperation, ApiResponse, ApiTags } from '@hazeljs/swagger';
54
-
55
- @Controller('/users')
56
- @ApiTags('Users')
57
- export class UserController {
58
- @Get()
59
- @ApiOperation({ summary: 'Get all users' })
60
- @ApiResponse({ status: 200, description: 'List of users' })
61
- findAll() {
62
- return { users: [] };
63
- }
64
-
65
- @Get('/:id')
66
- @ApiOperation({ summary: 'Get user by ID' })
67
- @ApiResponse({ status: 200, description: 'User found' })
68
- @ApiResponse({ status: 404, description: 'User not found' })
69
- findOne(@Param('id') id: string) {
70
- return { id, name: 'John Doe' };
71
- }
72
-
73
- @Post()
74
- @ApiOperation({ summary: 'Create a new user' })
75
- @ApiResponse({ status: 201, description: 'User created' })
76
- @ApiResponse({ status: 400, description: 'Invalid input' })
77
- create(@Body() createUserDto: CreateUserDto) {
78
- return createUserDto;
79
- }
80
- }
81
- ```
82
-
83
- ### 3. Access Documentation
84
-
85
- Navigate to `http://localhost:3000/api-docs` to view the interactive Swagger UI.
86
-
87
- ## Decorators
88
-
89
- ### @ApiTags()
90
-
91
- Group endpoints by tags:
92
-
93
- ```typescript
94
- @Controller('/products')
95
- @ApiTags('Products', 'Catalog')
96
- export class ProductController {
97
- // All endpoints will be tagged with 'Products' and 'Catalog'
98
- }
99
- ```
100
-
101
- ### @ApiOperation()
102
-
103
- Document endpoint details:
104
-
105
- ```typescript
106
- @Get('/search')
107
- @ApiOperation({
108
- summary: 'Search products',
109
- description: 'Search for products by name, category, or tags',
110
- operationId: 'searchProducts',
111
- })
112
- searchProducts() {
113
- return [];
114
- }
115
- ```
116
-
117
- ### @ApiResponse()
118
-
119
- Document response types:
120
-
121
- ```typescript
122
- @Get('/:id')
123
- @ApiResponse({
124
- status: 200,
125
- description: 'Product found',
126
- type: ProductDto,
127
- })
128
- @ApiResponse({
129
- status: 404,
130
- description: 'Product not found',
131
- schema: {
132
- type: 'object',
133
- properties: {
134
- statusCode: { type: 'number' },
135
- message: { type: 'string' },
136
- },
137
- },
138
- })
139
- findOne(@Param('id') id: string) {
140
- return this.productService.findOne(id);
141
- }
142
- ```
143
-
144
- ### @ApiProperty()
145
-
146
- Document DTO properties:
147
-
148
- ```typescript
149
- import { ApiProperty } from '@hazeljs/swagger';
150
-
151
- export class CreateUserDto {
152
- @ApiProperty({
153
- description: 'User email address',
154
- example: 'user@example.com',
155
- })
156
- email: string;
157
-
158
- @ApiProperty({
159
- description: 'User password',
160
- minLength: 8,
161
- example: 'SecurePass123!',
162
- })
163
- password: string;
164
-
165
- @ApiProperty({
166
- description: 'User full name',
167
- example: 'John Doe',
168
- })
169
- name: string;
170
-
171
- @ApiProperty({
172
- description: 'User age',
173
- minimum: 18,
174
- maximum: 120,
175
- example: 25,
176
- required: false,
177
- })
178
- age?: number;
179
- }
180
- ```
181
-
182
- ### @ApiParam()
183
-
184
- Document path parameters:
185
-
186
- ```typescript
187
- @Get('/:id')
188
- @ApiParam({
189
- name: 'id',
190
- description: 'User ID',
191
- type: 'string',
192
- example: '123',
193
- })
194
- findOne(@Param('id') id: string) {
195
- return this.userService.findOne(id);
196
- }
197
- ```
198
-
199
- ### @ApiQuery()
200
-
201
- Document query parameters:
202
-
203
- ```typescript
204
- @Get()
205
- @ApiQuery({
206
- name: 'page',
207
- required: false,
208
- type: Number,
209
- description: 'Page number',
210
- example: 1,
211
- })
212
- @ApiQuery({
213
- name: 'limit',
214
- required: false,
215
- type: Number,
216
- description: 'Items per page',
217
- example: 10,
218
- })
219
- findAll(
220
- @Query('page') page: number = 1,
221
- @Query('limit') limit: number = 10
222
- ) {
223
- return this.userService.findAll(page, limit);
224
- }
225
- ```
226
-
227
- ### @ApiBody()
228
-
229
- Document request body:
230
-
231
- ```typescript
232
- @Post()
233
- @ApiBody({
234
- description: 'User data',
235
- type: CreateUserDto,
236
- examples: {
237
- user1: {
238
- summary: 'Example user',
239
- value: {
240
- email: 'john@example.com',
241
- password: 'SecurePass123!',
242
- name: 'John Doe',
243
- },
244
- },
245
- },
246
- })
247
- create(@Body() createUserDto: CreateUserDto) {
248
- return this.userService.create(createUserDto);
249
- }
250
- ```
251
-
252
- ### @ApiHeader()
253
-
254
- Document required headers:
255
-
256
- ```typescript
257
- @Get('/protected')
258
- @ApiHeader({
259
- name: 'Authorization',
260
- description: 'Bearer token',
261
- required: true,
262
- example: 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
263
- })
264
- getProtectedData() {
265
- return { data: 'protected' };
266
- }
267
- ```
268
-
269
- ### @ApiBearerAuth()
270
-
271
- Document bearer authentication:
272
-
273
- ```typescript
274
- @Controller('/admin')
275
- @ApiBearerAuth()
276
- export class AdminController {
277
- @Get('/dashboard')
278
- getDashboard() {
279
- return { data: 'admin dashboard' };
280
- }
281
- }
282
- ```
283
-
284
- ### @ApiSecurity()
285
-
286
- Document custom security:
287
29
 
288
- ```typescript
289
- @Controller('/api')
290
- @ApiSecurity('api_key')
291
- export class ApiController {
292
- @Get('/data')
293
- getData() {
294
- return { data: [] };
295
- }
296
- }
30
+ // After you create the app (e.g. where you bootstrap HazelApp):
31
+ SwaggerModule.setRootModule(AppModule);
297
32
  ```
298
33
 
299
- ## Configuration
300
-
301
- ### Full Configuration
34
+ ### 2. Optional: document metadata, servers, auth, UI CDN, global prefix
302
35
 
303
36
  ```typescript
304
- SwaggerModule.forRoot({
305
- // Basic info
37
+ SwaggerModule.configure({
306
38
  title: 'My API',
307
- description: 'Comprehensive API documentation',
39
+ description: 'Production API',
308
40
  version: '1.0.0',
309
-
310
- // Server info
311
- servers: [
312
- {
313
- url: 'http://localhost:3000',
314
- description: 'Development server',
315
- },
316
- {
317
- url: 'https://api.example.com',
318
- description: 'Production server',
319
- },
320
- ],
321
-
322
- // Contact info
323
- contact: {
324
- name: 'API Support',
325
- email: 'support@example.com',
326
- url: 'https://example.com/support',
327
- },
328
-
329
- // License
330
- license: {
331
- name: 'Apache-2.0',
332
- url: 'https://www.apache.org/licenses/LICENSE-2.0',
41
+ servers: [{ url: 'http://localhost:3000', description: 'Local' }],
42
+ globalPrefix: '/api', // match app.setGlobalPrefix('/api') so paths and Swagger UI spec URL align
43
+ securitySchemes: {
44
+ bearer: { type: 'http', scheme: 'bearer', bearerFormat: 'JWT' },
333
45
  },
334
-
335
- // Terms of service
336
- termsOfService: 'https://example.com/terms',
337
-
338
- // External docs
339
- externalDocs: {
340
- description: 'Find more info here',
341
- url: 'https://docs.example.com',
342
- },
343
-
344
- // Security schemes
345
- security: [
346
- {
347
- name: 'bearer',
348
- type: 'http',
349
- scheme: 'bearer',
350
- bearerFormat: 'JWT',
351
- },
352
- {
353
- name: 'api_key',
354
- type: 'apiKey',
355
- in: 'header',
356
- name: 'X-API-Key',
357
- },
358
- ],
359
-
360
- // Swagger UI options
361
- swaggerOptions: {
362
- persistAuthorization: true,
363
- displayRequestDuration: true,
364
- filter: true,
365
- showExtensions: true,
366
- },
367
-
368
- // Path to serve docs
369
- path: '/api-docs',
370
-
371
- // Custom CSS
372
- customCss: '.swagger-ui .topbar { display: none }',
373
-
374
- // Custom site title
375
- customSiteTitle: 'My API Documentation',
46
+ security: [{ bearer: [] }],
47
+ swaggerUiCdnBase: 'https://unpkg.com/swagger-ui-dist@5.11.0',
376
48
  });
377
- ```
378
49
 
379
- ## Authentication
50
+ // Replace all options (e.g. in tests):
51
+ SwaggerModule.configure({}, true);
52
+ ```
380
53
 
381
- ### JWT Bearer
54
+ ### 3. Decorate controllers
382
55
 
383
56
  ```typescript
384
- // Configure security scheme
385
- SwaggerModule.forRoot({
386
- security: [
387
- {
388
- name: 'bearer',
389
- type: 'http',
390
- scheme: 'bearer',
391
- bearerFormat: 'JWT',
392
- },
393
- ],
394
- });
57
+ import { Controller, Get, Post, Body } from '@hazeljs/core';
58
+ import { Swagger, ApiOperation } from '@hazeljs/swagger';
395
59
 
396
- // Use in controller
397
- @Controller('/protected')
398
- @ApiBearerAuth()
399
- export class ProtectedController {
60
+ @Swagger({
61
+ title: 'Users API',
62
+ description: 'User operations',
63
+ version: '1.0.0',
64
+ tags: [{ name: 'users', description: 'Users' }],
65
+ })
66
+ @Controller({ path: '/users' })
67
+ export class UserController {
400
68
  @Get()
401
- getData() {
402
- return { data: 'protected' };
69
+ @ApiOperation({
70
+ summary: 'List users',
71
+ responses: { '200': { description: 'OK' } },
72
+ })
73
+ list() {
74
+ return [];
403
75
  }
404
- }
405
- ```
406
-
407
- ### API Key
408
76
 
409
- ```typescript
410
- // Configure security scheme
411
- SwaggerModule.forRoot({
412
- security: [
413
- {
414
- name: 'api_key',
415
- type: 'apiKey',
416
- in: 'header',
417
- name: 'X-API-Key',
77
+ @Post()
78
+ @ApiOperation({
79
+ summary: 'Create user',
80
+ requestBody: {
81
+ required: true,
82
+ content: { 'application/json': { schema: { type: 'object' } } },
418
83
  },
419
- ],
420
- });
421
-
422
- // Use in controller
423
- @Controller('/api')
424
- @ApiSecurity('api_key')
425
- export class ApiController {
426
- @Get()
427
- getData() {
428
- return { data: [] };
84
+ responses: { '201': { description: 'Created' } },
85
+ })
86
+ create(@Body() body: unknown) {
87
+ return body;
429
88
  }
430
89
  }
431
90
  ```
432
91
 
433
- ### OAuth2
92
+ Routes **without** `@ApiOperation` still appear in the spec when `autoGenerateOperations` is true (default): summaries and placeholder request bodies are inferred from HTTP method and handler name. Set `autoGenerateOperations: false` in `SwaggerModule.configure` or pass `{ autoGenerateOperations: false }` to `SwaggerService.generateSpec` to disable that for programmatic builds.
434
93
 
435
- ```typescript
436
- SwaggerModule.forRoot({
437
- security: [
438
- {
439
- name: 'oauth2',
440
- type: 'oauth2',
441
- flows: {
442
- authorizationCode: {
443
- authorizationUrl: 'https://example.com/oauth/authorize',
444
- tokenUrl: 'https://example.com/oauth/token',
445
- scopes: {
446
- 'read:users': 'Read user information',
447
- 'write:users': 'Modify user information',
448
- },
449
- },
450
- },
451
- },
452
- ],
453
- });
454
- ```
94
+ ### 4. Open the UI and raw spec
455
95
 
456
- ## Examples
96
+ - **Swagger UI:** `GET /swagger/` (or `GET {globalPrefix}/swagger/` if configured)
97
+ - **OpenAPI JSON:** `GET /swagger/spec`
457
98
 
458
- ### Complete CRUD Example
99
+ ## Programmatic export (CI / codegen)
459
100
 
460
101
  ```typescript
461
- import { Controller, Get, Post, Put, Delete, Body, Param } from '@hazeljs/core';
462
- import {
463
- ApiTags,
464
- ApiOperation,
465
- ApiResponse,
466
- ApiParam,
467
- ApiBody,
468
- ApiBearerAuth,
469
- } from '@hazeljs/swagger';
470
-
471
- @Controller('/products')
472
- @ApiTags('Products')
473
- @ApiBearerAuth()
474
- export class ProductController {
475
- @Get()
476
- @ApiOperation({ summary: 'Get all products' })
477
- @ApiResponse({
478
- status: 200,
479
- description: 'List of products',
480
- type: [ProductDto],
481
- })
482
- findAll() {
483
- return this.productService.findAll();
484
- }
485
-
486
- @Get('/:id')
487
- @ApiOperation({ summary: 'Get product by ID' })
488
- @ApiParam({ name: 'id', description: 'Product ID' })
489
- @ApiResponse({ status: 200, description: 'Product found', type: ProductDto })
490
- @ApiResponse({ status: 404, description: 'Product not found' })
491
- findOne(@Param('id') id: string) {
492
- return this.productService.findOne(id);
493
- }
102
+ import { createOpenApiDocument } from '@hazeljs/swagger';
103
+ import { AppModule } from './app.module';
104
+ import * as fs from 'node:fs';
494
105
 
495
- @Post()
496
- @ApiOperation({ summary: 'Create a new product' })
497
- @ApiBody({ type: CreateProductDto })
498
- @ApiResponse({ status: 201, description: 'Product created', type: ProductDto })
499
- @ApiResponse({ status: 400, description: 'Invalid input' })
500
- create(@Body() createProductDto: CreateProductDto) {
501
- return this.productService.create(createProductDto);
502
- }
503
-
504
- @Put('/:id')
505
- @ApiOperation({ summary: 'Update a product' })
506
- @ApiParam({ name: 'id', description: 'Product ID' })
507
- @ApiBody({ type: UpdateProductDto })
508
- @ApiResponse({ status: 200, description: 'Product updated', type: ProductDto })
509
- @ApiResponse({ status: 404, description: 'Product not found' })
510
- update(@Param('id') id: string, @Body() updateProductDto: UpdateProductDto) {
511
- return this.productService.update(id, updateProductDto);
512
- }
513
-
514
- @Delete('/:id')
515
- @ApiOperation({ summary: 'Delete a product' })
516
- @ApiParam({ name: 'id', description: 'Product ID' })
517
- @ApiResponse({ status: 200, description: 'Product deleted' })
518
- @ApiResponse({ status: 404, description: 'Product not found' })
519
- delete(@Param('id') id: string) {
520
- return this.productService.delete(id);
521
- }
522
- }
106
+ const doc = createOpenApiDocument(AppModule, {
107
+ title: 'My API',
108
+ version: '1.0.0',
109
+ globalPrefix: '/api',
110
+ });
111
+ fs.writeFileSync('openapi.json', JSON.stringify(doc, null, 2));
523
112
  ```
524
113
 
525
- ## Export OpenAPI Spec
114
+ YAML is not built in; pipe JSON through your preferred YAML tool if needed.
526
115
 
527
- ### JSON Format
116
+ ## API reference
528
117
 
529
- ```typescript
530
- import { SwaggerModule } from '@hazeljs/swagger';
118
+ | Export | Role |
119
+ | ----------------------- | --------------------------------------------------------------------------- |
120
+ | `SwaggerModule` | Nest-style module; `setRootModule`, `configure`, `getOptions` |
121
+ | `SwaggerService` | `generateAutoSpec(module, options?)`, `generateSpec(controllers, options?)` |
122
+ | `createOpenApiDocument` | Stateless helper around `generateAutoSpec` |
123
+ | `@Swagger` | Class-level OpenAPI `info` / default `tags` |
124
+ | `@ApiOperation` | Per-route operation (summary, parameters, requestBody, responses) |
531
125
 
532
- const document = SwaggerModule.createDocument(app);
533
- const json = JSON.stringify(document, null, 2);
126
+ Default components include `Error` and `ValidationError` schemas. Auto-generated error responses reference `#/components/schemas/Error`.
534
127
 
535
- // Save to file
536
- fs.writeFileSync('./openapi.json', json);
537
- ```
128
+ ## Roadmap / not implemented
538
129
 
539
- ### YAML Format
130
+ The following are **not** in this package today (do not rely on READMEs or examples that mention Nest’s full `@nestjs/swagger` surface):
540
131
 
541
- ```typescript
542
- import { SwaggerModule } from '@hazeljs/swagger';
543
- import * as yaml from 'js-yaml';
544
-
545
- const document = SwaggerModule.createDocument(app);
546
- const yamlString = yaml.dump(document);
547
-
548
- // Save to file
549
- fs.writeFileSync('./openapi.yaml', yamlString);
550
- ```
132
+ - `@ApiTags`, `@ApiResponse`, `@ApiProperty`, `@ApiParam`, `@ApiQuery`, `@ApiBody`, `@ApiHeader`, `@ApiBearerAuth`, `@ApiSecurity` as separate decorators
133
+ - DTO / class reflection for schemas
134
+ - Built-in YAML export or `SwaggerModule.forRoot` static module factory
551
135
 
552
- ## Best Practices
553
-
554
- 1. **Document All Endpoints** - Add decorators to every route
555
- 2. **Use DTOs** - Define request/response types with `@ApiProperty`
556
- 3. **Add Examples** - Include realistic examples in documentation
557
- 4. **Group by Tags** - Organize endpoints with `@ApiTags`
558
- 5. **Document Errors** - Include all possible error responses
559
- 6. **Security** - Document authentication requirements
560
- 7. **Versioning** - Update version number with API changes
561
- 8. **External Docs** - Link to additional documentation
136
+ Contributions welcome for any of the above.
562
137
 
563
138
  ## Testing
564
139
 
@@ -566,19 +141,6 @@ fs.writeFileSync('./openapi.yaml', yamlString);
566
141
  npm test
567
142
  ```
568
143
 
569
- ## Contributing
570
-
571
- Contributions are welcome! Please read our [Contributing Guide](../../CONTRIBUTING.md) for details.
572
-
573
144
  ## License
574
145
 
575
146
  Apache 2.0 Β© [HazelJS](https://hazeljs.ai)
576
-
577
- ## Links
578
-
579
- - [Documentation](https://hazeljs.ai/docs/packages/swagger)
580
- - [OpenAPI Specification](https://swagger.io/specification/)
581
- - [Swagger UI](https://swagger.io/tools/swagger-ui/)
582
- - [GitHub](https://github.com/hazel-js/hazeljs)
583
- - [Issues](https://github.com/hazel-js/hazeljs/issues)
584
- - [Discord](https://discord.gg/PxNBPzvQk7)
package/dist/index.d.ts CHANGED
@@ -1,9 +1,12 @@
1
1
  /**
2
2
  * @hazeljs/swagger - Swagger/OpenAPI module for HazelJS
3
3
  */
4
+ import type { Type } from '@hazeljs/core';
5
+ import type { SwaggerBuildOptions, SwaggerSpec } from './swagger.types';
4
6
  export { SwaggerModule } from './swagger.module';
5
- export { SwaggerService, type SwaggerSpec } from './swagger.service';
7
+ export { SwaggerService } from './swagger.service';
6
8
  export { Swagger, ApiOperation, getSwaggerMetadata, getOperationMetadata, } from './swagger.decorator';
7
- export type { SwaggerOptions, SwaggerOperation, SwaggerSchema } from './swagger.types';
8
- export type { AutoSwaggerOptions } from './swagger.service';
9
+ export type { SwaggerOptions, SwaggerOperation, SwaggerSchema, SwaggerSpec, SwaggerServer, SwaggerBuildOptions, AutoSwaggerOptions, SwaggerModuleOptions, } from './swagger.types';
10
+ /** Build an OpenAPI document without serving HTTP (e.g. CI export). */
11
+ export declare function createOpenApiDocument(rootModule: Type<unknown>, options?: SwaggerBuildOptions): SwaggerSpec;
9
12
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrE,OAAO,EACL,OAAO,EACP,YAAY,EACZ,kBAAkB,EAClB,oBAAoB,GACrB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EAAE,cAAc,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAGvF,YAAY,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAE1C,OAAO,KAAK,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAExE,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EACL,OAAO,EACP,YAAY,EACZ,kBAAkB,EAClB,oBAAoB,GACrB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EACV,cAAc,EACd,gBAAgB,EAChB,aAAa,EACb,WAAW,EACX,aAAa,EACb,mBAAmB,EACnB,kBAAkB,EAClB,oBAAoB,GACrB,MAAM,iBAAiB,CAAC;AAEzB,uEAAuE;AACvE,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,IAAI,CAAC,OAAO,CAAC,EACzB,OAAO,CAAC,EAAE,mBAAmB,GAC5B,WAAW,CAEb"}