@avleon/core 0.0.45 → 0.0.48

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 (131) hide show
  1. package/README.md +355 -369
  2. package/dist/chunk-9hOWP6kD.cjs +64 -0
  3. package/dist/chunk-DORXReHP.js +37 -0
  4. package/dist/index-BxIMWhgy.d.ts +1284 -0
  5. package/dist/index-DPn7qtzq.d.cts +1283 -0
  6. package/dist/index.cjs +3194 -0
  7. package/dist/index.cjs.map +1 -0
  8. package/dist/index.js +3022 -83
  9. package/dist/index.js.map +1 -0
  10. package/dist/lib-Bk8hUm06.cjs +7847 -0
  11. package/dist/lib-Bk8hUm06.cjs.map +1 -0
  12. package/dist/lib-CvDxBMkR.js +7843 -0
  13. package/dist/lib-CvDxBMkR.js.map +1 -0
  14. package/package.json +67 -116
  15. package/dist/application.d.ts +0 -47
  16. package/dist/application.js +0 -50
  17. package/dist/authentication.d.ts +0 -13
  18. package/dist/authentication.js +0 -16
  19. package/dist/cache.d.ts +0 -12
  20. package/dist/cache.js +0 -78
  21. package/dist/cache.test.d.ts +0 -1
  22. package/dist/cache.test.js +0 -36
  23. package/dist/collection.d.ts +0 -43
  24. package/dist/collection.js +0 -231
  25. package/dist/collection.test.d.ts +0 -1
  26. package/dist/collection.test.js +0 -59
  27. package/dist/config.d.ts +0 -18
  28. package/dist/config.js +0 -58
  29. package/dist/config.test.d.ts +0 -1
  30. package/dist/config.test.js +0 -40
  31. package/dist/constants.d.ts +0 -1
  32. package/dist/constants.js +0 -4
  33. package/dist/container.d.ts +0 -30
  34. package/dist/container.js +0 -55
  35. package/dist/controller.d.ts +0 -50
  36. package/dist/controller.js +0 -71
  37. package/dist/controller.test.d.ts +0 -1
  38. package/dist/controller.test.js +0 -111
  39. package/dist/decorators.d.ts +0 -15
  40. package/dist/decorators.js +0 -41
  41. package/dist/environment-variables.d.ts +0 -49
  42. package/dist/environment-variables.js +0 -130
  43. package/dist/environment-variables.test.d.ts +0 -1
  44. package/dist/environment-variables.test.js +0 -70
  45. package/dist/event-dispatcher.d.ts +0 -23
  46. package/dist/event-dispatcher.js +0 -100
  47. package/dist/event-subscriber.d.ts +0 -14
  48. package/dist/event-subscriber.js +0 -87
  49. package/dist/exceptions/http-exceptions.d.ts +0 -50
  50. package/dist/exceptions/http-exceptions.js +0 -85
  51. package/dist/exceptions/index.d.ts +0 -1
  52. package/dist/exceptions/index.js +0 -17
  53. package/dist/exceptions/system-exception.d.ts +0 -22
  54. package/dist/exceptions/system-exception.js +0 -26
  55. package/dist/file-storage.d.ts +0 -69
  56. package/dist/file-storage.js +0 -323
  57. package/dist/file-storage.test.d.ts +0 -1
  58. package/dist/file-storage.test.js +0 -104
  59. package/dist/helpers.d.ts +0 -44
  60. package/dist/helpers.js +0 -419
  61. package/dist/helpers.test.d.ts +0 -1
  62. package/dist/helpers.test.js +0 -95
  63. package/dist/icore.d.ts +0 -226
  64. package/dist/icore.js +0 -968
  65. package/dist/icore.test.d.ts +0 -1
  66. package/dist/icore.test.js +0 -14
  67. package/dist/index.d.ts +0 -55
  68. package/dist/interfaces/avleon-application.d.ts +0 -27
  69. package/dist/interfaces/avleon-application.js +0 -1
  70. package/dist/kenx-provider.d.ts +0 -7
  71. package/dist/kenx-provider.js +0 -44
  72. package/dist/kenx-provider.test.d.ts +0 -1
  73. package/dist/kenx-provider.test.js +0 -36
  74. package/dist/logger.d.ts +0 -12
  75. package/dist/logger.js +0 -87
  76. package/dist/logger.test.d.ts +0 -1
  77. package/dist/logger.test.js +0 -42
  78. package/dist/map-types.d.ts +0 -17
  79. package/dist/map-types.js +0 -89
  80. package/dist/middleware.d.ts +0 -27
  81. package/dist/middleware.js +0 -64
  82. package/dist/middleware.test.d.ts +0 -1
  83. package/dist/middleware.test.js +0 -121
  84. package/dist/multipart.d.ts +0 -17
  85. package/dist/multipart.js +0 -70
  86. package/dist/multipart.test.d.ts +0 -1
  87. package/dist/multipart.test.js +0 -87
  88. package/dist/openapi.d.ts +0 -343
  89. package/dist/openapi.js +0 -27
  90. package/dist/openapi.test.d.ts +0 -1
  91. package/dist/openapi.test.js +0 -111
  92. package/dist/params.d.ts +0 -17
  93. package/dist/params.js +0 -64
  94. package/dist/params.test.d.ts +0 -1
  95. package/dist/params.test.js +0 -83
  96. package/dist/queue.d.ts +0 -29
  97. package/dist/queue.js +0 -84
  98. package/dist/response.d.ts +0 -16
  99. package/dist/response.js +0 -56
  100. package/dist/results.d.ts +0 -20
  101. package/dist/results.js +0 -32
  102. package/dist/route-methods.d.ts +0 -25
  103. package/dist/route-methods.js +0 -49
  104. package/dist/route-methods.test.d.ts +0 -1
  105. package/dist/route-methods.test.js +0 -129
  106. package/dist/swagger-schema.d.ts +0 -43
  107. package/dist/swagger-schema.js +0 -452
  108. package/dist/swagger-schema.test.d.ts +0 -1
  109. package/dist/swagger-schema.test.js +0 -105
  110. package/dist/testing.d.ts +0 -55
  111. package/dist/testing.js +0 -196
  112. package/dist/types/app-builder.interface.d.ts +0 -15
  113. package/dist/types/app-builder.interface.js +0 -8
  114. package/dist/types/application.interface.d.ts +0 -8
  115. package/dist/types/application.interface.js +0 -2
  116. package/dist/utils/hash.d.ts +0 -4
  117. package/dist/utils/hash.js +0 -15
  118. package/dist/utils/index.d.ts +0 -2
  119. package/dist/utils/index.js +0 -18
  120. package/dist/utils/optional-require.d.ts +0 -8
  121. package/dist/utils/optional-require.js +0 -70
  122. package/dist/validation.d.ts +0 -39
  123. package/dist/validation.js +0 -111
  124. package/dist/validation.test.d.ts +0 -1
  125. package/dist/validation.test.js +0 -61
  126. package/dist/validator-extend.d.ts +0 -7
  127. package/dist/validator-extend.js +0 -28
  128. package/dist/websocket.d.ts +0 -7
  129. package/dist/websocket.js +0 -20
  130. package/dist/websocket.test.d.ts +0 -1
  131. package/dist/websocket.test.js +0 -27
package/README.md CHANGED
@@ -1,63 +1,60 @@
1
- # Avleon
1
+ # Avleon
2
2
 
3
- ![npm version](https://img.shields.io/npm/v/@avleon/core.svg) ![Build](https://github.com/avleonjs/avleon-core/actions/workflows/release.yml/badge.svg)
4
- ## ⚠️ WARNING
3
+ ![npm version](https://img.shields.io/npm/v/@avleon/core.svg)
4
+ ![Build](https://github.com/avleonjs/avleon-core/actions/workflows/release.yml/badge.svg)
5
+ ![License](https://img.shields.io/npm/l/@avleon/core.svg)
5
6
 
6
- > **🚧 This project is in active development.**
7
+ > **🚧 This project is in active development. APIs may change between versions.**
7
8
 
8
- ## Overview
9
+ Avleon is a TypeScript-first web framework built on top of [Fastify](https://fastify.dev), designed for building scalable, maintainable REST APIs with minimal boilerplate. It provides decorator-based routing, built-in dependency injection, automatic OpenAPI documentation, and first-class validation support.
9
10
 
10
- Avleon is a powerful, TypeScript-based web framework built on top of Fastify, designed to simplify API development with a focus on decorators, dependency injection, and OpenAPI documentation. It provides a robust set of tools for building scalable, maintainable web applications with minimal boilerplate code.
11
+ ---
11
12
 
12
13
  ## Table of Contents
13
14
 
14
15
  - [Features](#features)
15
16
  - [Installation](#installation)
16
17
  - [Quick Start](#quick-start)
17
- - [Route Based](#route-based)
18
- - [Controller Based](#controller-based)
19
18
  - [Core Concepts](#core-concepts)
20
- - [Application Creation](#application-creation)
19
+ - [Application](#application)
21
20
  - [Controllers](#controllers)
22
21
  - [Route Methods](#route-methods)
23
22
  - [Parameter Decorators](#parameter-decorators)
24
- - [Response Handling](#response-handling)
23
+ - [Error Handling](#error-handling)
25
24
  - [Middleware](#middleware)
26
- - [Authentication & Authorization](#authentication--authorization)
25
+ - [Authorization](#authorization)
27
26
  - [Validation](#validation)
28
27
  - [OpenAPI Documentation](#openapi-documentation)
29
28
  - [Advanced Features](#advanced-features)
30
- - [Database Integration](#database-integration)
29
+ - [Database — Knex](#database--knex)
30
+ - [Database — TypeORM](#database--typeorm)
31
31
  - [File Uploads](#file-uploads)
32
32
  - [Static Files](#static-files)
33
- - [Testing](#testing)
34
- - [Configuration](#configuration)
35
- - [Route Mapping](#route-mapping)
36
- - [mapGet](#mapget)
37
- - [mapPost](#mappost)
38
- - [mapPut](#mapput)
39
- - [mapDelete](#mapdelete)
33
+ - [WebSocket (Socket.IO)](#websocket-socketio)
34
+ - [Route Mapping (Functional Style)](#route-mapping-functional-style)
40
35
  - [Testing](#testing)
41
- - [WebSocket](#websocket-intregation-socketio)
36
+ - [License](#license)
37
+
38
+ ---
42
39
 
43
40
  ## Features
44
41
 
45
- - **Decorator-based API Development**: Define controllers, routes, and middleware using TypeScript decorators
46
- - **Dependency Injection**: Built-in DI system using TypeDI for service management
47
- - **OpenAPI/Swagger Integration**: Automatic API documentation generation with support for both Swagger UI and Scalar
48
- - **Validation**: Request validation with support for class-validator and custom validation rules
49
- - **Middleware System**: Flexible middleware architecture for request processing
50
- - **Response Handling**: Standardized response formats with HTTP status codes
51
- - **File Upload**: Built-in support for multipart file uploads with file validation
52
- - **Authentication & Authorization**: Middleware for securing your API endpoints
53
- - **Database Integration**: Support for TypeORM for database operations
54
- - **Queue System**: Background job processing capabilities
55
- - **Environment Configuration**: Environment variable management
56
- - **Logging**: Integrated logging with Pino
57
- - **Testing**: Built-in testing utilities for API endpoints
42
+ - 🎯 **Decorator-based routing** define controllers and routes with TypeScript decorators
43
+ - 💉 **Dependency injection** powered by [TypeDI](https://github.com/typestack/typedi)
44
+ - 📄 **OpenAPI / Swagger** automatic docs with Swagger UI or [Scalar](https://scalar.com)
45
+ - **Validation** request validation via [class-validator](https://github.com/typestack/class-validator)
46
+ - 🔒 **Authorization** flexible middleware-based auth system
47
+ - 📁 **File uploads** multipart form support out of the box
48
+ - 🗄️ **Database** TypeORM and Knex integrations
49
+ - 🔌 **WebSocket** Socket.IO integration
50
+ - 🧪 **Testing** built-in test utilities
51
+
52
+ ---
58
53
 
59
54
  ## Installation
60
55
 
56
+ Scaffold a new project using the CLI:
57
+
61
58
  ```bash
62
59
  npx @avleon/cli new myapp
63
60
  # or
@@ -66,128 +63,133 @@ yarn dlx @avleon/cli new myapp
66
63
  pnpm dlx @avleon/cli new myapp
67
64
  ```
68
65
 
66
+ Or install manually:
67
+
68
+ ```bash
69
+ npm install @avleon/core reflect-metadata class-validator class-transformer
70
+ ```
71
+
72
+ ---
73
+
69
74
  ## Quick Start
70
75
 
71
- ### Minimal
76
+ ### Minimal (functional style)
77
+
72
78
  ```typescript
73
- import { Avleon } from "@avleon/core";
79
+ import { Avleon } from '@avleon/core';
74
80
 
75
81
  const app = Avleon.createApplication();
76
- app.mapGet("/", () => "Hello, Avleon");
77
- app.run(); // or app.run(3000);
82
+
83
+ app.mapGet('/', () => ({ message: 'Hello, Avleon!' }));
84
+
85
+ app.run(4000);
78
86
  ```
79
87
 
80
- ### Controller Based
88
+ ### Controller style
89
+
81
90
  ```typescript
82
- import { Avleon, ApiController, Get, Results } from "@avleon/core";
91
+ import { Avleon, ApiController, Get } from '@avleon/core';
83
92
 
84
- // Define a controller
85
- @ApiController
93
+ @ApiController('/')
86
94
  class HelloController {
87
95
  @Get()
88
96
  sayHello() {
89
- return "Hello, Avleon!";
97
+ return { message: 'Hello, Avleon!' };
90
98
  }
91
99
  }
92
100
 
93
- // Create and start the application
94
101
  const app = Avleon.createApplication();
95
102
  app.useControllers([HelloController]);
96
- app.run();
103
+ app.run(4000);
97
104
  ```
98
105
 
106
+ ---
107
+
99
108
  ## Core Concepts
100
109
 
101
- ### Application Creation
102
- Avleon provides a builder pattern for creating applications:
110
+ ### Application
103
111
 
104
112
  ```typescript
105
- import { Avleon } from "@avleon/core";
113
+ import { Avleon } from '@avleon/core';
106
114
 
107
- // Create an application
108
115
  const app = Avleon.createApplication();
109
116
 
110
- // Configure and run the application
111
- app.useCors();
117
+ app.useCors({ origin: '*' });
112
118
  app.useControllers([UserController]);
113
- // For auto register controller `app.useControllers({auto:true});`
119
+ // Auto-discover controllers from a directory:
120
+ // app.useControllers({ auto: true, path: 'src/controllers' });
114
121
 
115
- app.run(); // or app.run(port)
122
+ app.run(4000);
116
123
  ```
117
124
 
125
+ ---
126
+
118
127
  ### Controllers
119
- Controllers are the entry points for your API requests. They are defined using the `@ApiController` decorator:
120
128
 
121
129
  ```typescript
122
- @ApiController("/users")
130
+ import { ApiController, Get, Post, Put, Delete } from '@avleon/core';
131
+
132
+ @ApiController('/users')
123
133
  class UserController {
124
- // Route handlers go here
125
- }
126
- ```
134
+ @Get('/')
135
+ getAll() { ... }
127
136
 
128
- ### Route Methods
129
- Define HTTP methods using decorators:
137
+ @Post('/')
138
+ create() { ... }
130
139
 
131
- ```typescript
132
- @Get('/')
133
- async getUsers() {
134
- // Handle GET request
135
- }
140
+ @Put('/:id')
141
+ update() { ... }
136
142
 
137
- @Post('/')
138
- async createUser(@Body() user: UserDto) {
139
- // Handle POST request
143
+ @Delete('/:id')
144
+ remove() { ... }
140
145
  }
146
+ ```
141
147
 
142
- @Put('/:id')
143
- async updateUser(@Param('id') id: string, @Body() user: UserDto) {
144
- // Handle PUT request
145
- }
148
+ ---
146
149
 
147
- @Delete('/:id')
148
- async deleteUser(@Param('id') id: string) {
149
- // Handle DELETE request
150
- }
151
- ```
150
+ ### Route Methods
151
+
152
+ | Decorator | HTTP Method |
153
+ |-----------|-------------|
154
+ | `@Get(path?)` | GET |
155
+ | `@Post(path?)` | POST |
156
+ | `@Put(path?)` | PUT |
157
+ | `@Patch(path?)` | PATCH |
158
+ | `@Delete(path?)` | DELETE |
159
+
160
+ ---
152
161
 
153
162
  ### Parameter Decorators
154
- Extract data from requests using parameter decorators:
155
163
 
156
164
  ```typescript
157
165
  @Get('/:id')
158
166
  async getUser(
159
- @Param('id') id: string,
160
- @Query('include') include: string,
167
+ @Param('id') id: string,
168
+ @Query('include') include: string,
169
+ @Query() query: UserQuery, // maps full query to a DTO
170
+ @Body() body: CreateUserDto,
161
171
  @Header('authorization') token: string,
162
- @Body() data: UserDto
172
+ @AuthUser() user: CurrentUser,
163
173
  ) {
164
- // Access route parameters, query strings, headers, and request body
174
+ // ...
165
175
  }
166
176
  ```
167
177
 
168
- <!-- You can also access the current user and files:
169
-
170
- ```typescript
171
- @Post('/upload')
172
- async uploadFile(
173
- @User() currentUser: any,
174
- @File() file: any
175
- ) {
176
- // Access the current user and uploaded file
177
- }
178
+ | Decorator | Source |
179
+ |-----------|--------|
180
+ | `@Param(key?)` | Route path params |
181
+ | `@Query(key?)` | Query string |
182
+ | `@Body()` | Request body |
183
+ | `@Header(key?)` | Request headers |
184
+ | `@AuthUser()` | Current authenticated user |
178
185
 
179
- @Post('/upload-multiple')
180
- async uploadFiles(
181
- @Files() files: any[]
182
- ) {
183
- // Access multiple uploaded files
184
- }
185
- ``` -->
186
+ ---
186
187
 
187
188
  ### Error Handling
188
- Return standardized responses using the `HttpResponse` and `HttpExceptions` class:
189
189
 
190
190
  ```typescript
191
+ import { HttpExceptions, HttpResponse } from '@avleon/core';
192
+
191
193
  @Get('/:id')
192
194
  async getUser(@Param('id') id: string) {
193
195
  const user = await this.userService.findById(id);
@@ -200,95 +202,94 @@ async getUser(@Param('id') id: string) {
200
202
  }
201
203
  ```
202
204
 
205
+ Available exceptions: `NotFound`, `BadRequest`, `Unauthorized`, `Forbidden`, `InternalServerError`.
206
+
207
+ ---
208
+
203
209
  ### Middleware
204
- Create and apply middleware for cross-cutting concerns:
205
210
 
206
211
  ```typescript
212
+ import { Middleware, AppMiddleware, IRequest, UseMiddleware } from '@avleon/core';
213
+
207
214
  @Middleware
208
215
  class LoggingMiddleware extends AppMiddleware {
209
216
  async invoke(req: IRequest) {
210
- console.log(`Request: ${req.method} ${req.url}`);
217
+ console.log(`${req.method} ${req.url}`);
211
218
  return req;
212
219
  }
213
220
  }
214
221
 
222
+ // Apply to entire controller
215
223
  @UseMiddleware(LoggingMiddleware)
216
- @ApiController("/users")
217
- class UserController {
218
- // Controller methods
219
- }
220
- ```
221
-
222
- You can also apply middleware to specific routes:
224
+ @ApiController('/users')
225
+ class UserController { ... }
223
226
 
224
- ```typescript
225
- @ApiController("/users")
227
+ // Or apply to a specific route
228
+ @ApiController('/users')
226
229
  class UserController {
227
230
  @UseMiddleware(LoggingMiddleware)
228
- @Get("/")
229
- async getUsers() {
230
- // Only this route will use the LoggingMiddleware
231
- }
231
+ @Get('/')
232
+ getAll() { ... }
232
233
  }
233
234
  ```
234
235
 
235
- ### Authentication & Authorization
236
- Secure your API with authentication and authorization:
236
+ ---
237
+
238
+ ### Authorization
239
+
240
+ **1 — Define your authorization class:**
237
241
 
238
242
  ```typescript
239
- import { CanAuthorize } from "@avleon/core";
243
+ import { CanAuthorize, AuthorizeMiddleware, IRequest } from '@avleon/core';
240
244
 
241
245
  @CanAuthorize
242
246
  class JwtAuthorization extends AuthorizeMiddleware {
243
- authorize(roles: string[]) {
244
- return async (req: IRequest) => {
245
- // Implement JWT authentication logic
246
- return req;
247
- };
247
+ async authorize(req: IRequest, options?: any) {
248
+ const token = req.headers['authorization']?.split(' ')[1];
249
+ if (!token) throw HttpExceptions.Unauthorized('Missing token');
250
+ req.user = verifyToken(token); // attach user to request
248
251
  }
249
252
  }
250
253
  ```
251
254
 
252
- Now register the authrization class to our app by `useAuthorization` function;
255
+ **2 Register with the app:**
253
256
 
254
257
  ```typescript
255
258
  app.useAuthorization(JwtAuthorization);
256
259
  ```
257
260
 
258
- Then you have access the `AuthUser` on class lavel or method lavel depending on how you use the `@Authorized()` decorator.
261
+ **3 Protect controllers or routes:**
259
262
 
260
263
  ```typescript
261
- // admin.controller.ts
264
+ // Protect entire controller
262
265
  @Authorized()
263
- @ApiController("/admin")
266
+ @ApiController('/admin')
264
267
  class AdminController {
265
- // Protected controller methods
266
-
267
- // protected controller has access to AuthUser in each route method
268
- @Get()
269
- async account(@AuthUser() user: User) {
270
- ///
268
+ @Get('/')
269
+ dashboard(@AuthUser() user: User) {
270
+ return user;
271
271
  }
272
272
  }
273
273
 
274
- // Or protect specific routes with roles
275
- @ApiController("/admin")
274
+ // Protect specific route with roles
275
+ @ApiController('/admin')
276
276
  class AdminController {
277
- @Authorized({
278
- roles: ["admin"],
279
- })
280
- @Get("/")
281
- async adminDashboard() {
282
- // Only users with 'admin' role can access this
283
- }
277
+ @Authorized({ roles: ['admin'] })
278
+ @Get('/stats')
279
+ stats() { ... }
284
280
  }
285
281
  ```
286
282
 
283
+ ---
284
+
287
285
  ### Validation
288
- Validate request data using class-validator:
286
+
287
+ Validation is powered by `class-validator`. Decorate your DTOs and Avleon validates automatically:
289
288
 
290
289
  ```typescript
291
- class UserDto {
290
+ import { IsString, IsEmail, IsInt, Min, Max, IsOptional } from 'class-validator';
291
+
292
+ class CreateUserDto {
292
293
  @IsString()
293
294
  @IsNotEmpty()
294
295
  name: string;
@@ -296,386 +297,371 @@ class UserDto {
296
297
  @IsEmail()
297
298
  email: string;
298
299
 
299
- @IsNumber()
300
+ @IsInt()
300
301
  @Min(0)
301
302
  @Max(120)
302
303
  age: number;
304
+
305
+ @IsOptional()
306
+ @IsString()
307
+ role?: string;
303
308
  }
304
309
 
305
310
  @Post('/')
306
- async createUser(@Body() user: UserDto) {
307
- // User data is automatically validated
308
- return user;
311
+ async createUser(@Body() body: CreateUserDto) {
312
+ return this.userService.create(body);
309
313
  }
310
314
  ```
311
315
 
312
- You can also use custom validation rules:
313
-
314
- ```typescript
315
- class UserDto {
316
- @Validate({
317
- type: "string",
318
- required: true,
319
- message: "Name is required",
320
- })
321
- name: string;
322
-
323
- @Validate({
324
- type: "number",
325
- min: 0,
326
- max: 120,
327
- message: "Age must be between 0 and 120",
328
- })
329
- age: number;
330
- }
331
- ```
316
+ ---
332
317
 
333
318
  ### OpenAPI Documentation
334
- Generate API documentation automatically:
335
319
 
336
- ```typescript
320
+ **Inline config:**
337
321
 
322
+ ```typescript
338
323
  app.useOpenApi({
339
- info: {
340
- title: "User API",
341
- version: "1.0.0",
342
- description: "API for managing users",
343
- },
344
- servers: [
345
- {
346
- url: "http://localhost:3000",
347
- description: "Development server",
324
+ info: {
325
+ title: 'User API',
326
+ version: '1.0.0',
327
+ description: 'API for managing users',
328
+ },
329
+ servers: [{ url: 'http://localhost:4000', description: 'Dev server' }],
330
+ components: {
331
+ securitySchemes: {
332
+ bearerAuth: {
333
+ type: 'http',
334
+ scheme: 'bearer',
335
+ bearerFormat: 'JWT',
348
336
  },
349
- ],
350
- });
337
+ },
338
+ },
339
+ });
340
+ ```
351
341
 
352
- ```
342
+ **Config class:**
353
343
 
354
- You can also customize the OpenAPI UI:
344
+ ```typescript
345
+ import { AppConfig, IConfig, Environment } from '@avleon/core';
346
+
347
+ @AppConfig
348
+ export class OpenApiConfig implements IConfig {
349
+ config(env: Environment) {
350
+ return {
351
+ info: { title: 'My API', version: '1.0.0' },
352
+ routePrefix: '/docs',
353
+ provider: 'scalar', // or 'default' for Swagger UI
354
+ };
355
+ }
356
+ }
357
+
358
+ // In app.ts
359
+ if (app.isDevelopment()) {
360
+ app.useOpenApi(OpenApiConfig);
361
+ }
362
+ ```
363
+
364
+ **Route-level docs with `@OpenApi`:**
355
365
 
356
366
  ```typescript
357
- app.useOpenApi(OpenApiConfig, (config) => {
358
- // Modify the OpenAPI configuration
359
- config.info.title = "Custom API Title";
360
- return config;
361
- });
367
+ import { OpenApi, OpenApiProperty, OpenApiSchema } from '@avleon/core';
368
+
369
+ @OpenApiSchema()
370
+ export class UserQuery {
371
+ @OpenApiProperty({ type: 'string', example: 'john', required: false })
372
+ @IsOptional()
373
+ search?: string;
374
+
375
+ @OpenApiProperty({ type: 'integer', example: 1, required: false })
376
+ @IsOptional()
377
+ page?: number;
378
+ }
379
+
380
+ @OpenApi({
381
+ summary: 'Get all users',
382
+ tags: ['users'],
383
+ security: [{ bearerAuth: [] }],
384
+ response: {
385
+ 200: {
386
+ description: 'List of users',
387
+ type: 'object',
388
+ properties: {
389
+ data: { type: 'array' },
390
+ total: { type: 'integer', example: 100 },
391
+ },
392
+ },
393
+ 401: { description: 'Unauthorized' },
394
+ },
395
+ })
396
+ @Get('/')
397
+ getAll(@Query() query: UserQuery) { ... }
362
398
  ```
363
399
 
400
+ ---
401
+
364
402
  ## Advanced Features
365
403
 
366
- ### Database Integration
404
+ ### Database — Knex
367
405
 
368
- ## 1. Knex
369
406
  ```typescript
370
- const app = Avleon.createApplication();
371
407
  app.useKnex({
372
408
  client: 'mysql',
373
409
  connection: {
374
410
  host: '127.0.0.1',
375
411
  port: 3306,
376
- user: 'your_database_user',
377
- password: 'your_database_password',
378
- database: 'myapp_test',
412
+ user: 'root',
413
+ password: 'password',
414
+ database: 'myapp',
379
415
  },
380
- })
416
+ });
381
417
  ```
382
- or using config class
418
+
419
+ Using a config class:
383
420
 
384
421
  ```typescript
385
422
  @AppConfig
386
423
  export class KnexConfig implements IConfig {
387
- // config method is mendatory
388
- // config method has access to environment variables by default
389
424
  config(env: Environment) {
390
425
  return {
391
426
  client: 'mysql',
392
427
  connection: {
393
- host: env.get("DB_HOST") || '127.0.0.1',
394
- port: env.get("DB_PORT") || 3306,
395
- user: env.get("DB_USER")|| 'your_database_user',
396
- password: env.get("DB_PASS") || 'your_database_password',
397
- database: env.get("DB_NAME") || 'myapp_test',
428
+ host: env.get('DB_HOST') || '127.0.0.1',
429
+ port: env.get('DB_PORT') || 3306,
430
+ user: env.get('DB_USER') || 'root',
431
+ password: env.get('DB_PASS') || 'password',
432
+ database: env.get('DB_NAME') || 'myapp',
398
433
  },
399
434
  };
400
435
  }
401
436
  }
402
437
 
403
- // now we can register it with our app
404
-
405
- app.useKenx(KnexConfig)
438
+ app.useKnex(KnexConfig);
406
439
  ```
407
440
 
408
- ### Exmaple uses
441
+ Using in a service:
442
+
409
443
  ```typescript
410
- import { DB, AppService } from "@avleon/core";
444
+ import { DB, AppService } from '@avleon/core';
411
445
 
412
446
  @AppService
413
- export class UsersService{
414
- constructor(
415
- private readonly db: DB
416
- ){}
447
+ export class UsersService {
448
+ constructor(private readonly db: DB) {}
417
449
 
418
- async findAll(){
419
- const result = await this.db.client.select("*").from("users");
420
- return result;
450
+ async findAll() {
451
+ return this.db.client.select('*').from('users');
421
452
  }
422
453
  }
423
454
  ```
424
455
 
425
- ## 2. Typeorm
426
- Connect to databases using TypeORM:
456
+ ---
457
+
458
+ ### Database — TypeORM
427
459
 
428
460
  ```typescript
429
- const app = Avleon.createApplication();
430
461
  app.useDataSource({
431
- type: "postgres",
432
- host: "localhost",
462
+ type: 'postgres',
463
+ host: 'localhost',
433
464
  port: 5432,
434
- username: "postgres",
435
- password: "password",
436
- database: "avleon",
465
+ username: 'postgres',
466
+ password: 'password',
467
+ database: 'avleon',
437
468
  entities: [User],
438
469
  synchronize: true,
439
470
  });
440
471
  ```
441
472
 
442
- Or use the config class:
473
+ Using a config class:
443
474
 
444
475
  ```typescript
445
- // datasource.config.ts
446
- import { AppConfig, IConfig } from "@avleon/core";
447
-
448
476
  @AppConfig
449
477
  export class DataSourceConfig implements IConfig {
450
- // config method is mendatory
451
- // config method has access to environment variables by default
452
478
  config(env: Environment) {
453
479
  return {
454
- type: env.get("type") || "postgres",
455
- host: "localhost",
456
- port: 5432,
457
- username: "postgres",
458
- password: "password",
459
- database: "avleon",
460
- entities: [User],
480
+ type: 'postgres',
481
+ host: env.get('DB_HOST') || 'localhost',
482
+ port: Number(env.get('DB_PORT')) || 5432,
483
+ username: env.get('DB_USER') || 'postgres',
484
+ password: env.get('DB_PASS') || 'password',
485
+ database: env.get('DB_NAME') || 'avleon',
486
+ entities: [User],
461
487
  synchronize: true,
462
488
  };
463
489
  }
464
490
  }
465
- ```
466
491
 
467
- ```typescript
468
- // app.ts
469
- const app = Avleon.createApplication();
470
492
  app.useDataSource(DataSourceConfig);
471
- // ... other impments
472
493
  ```
473
494
 
474
- Now in your Controller or Injected service use can use like this
495
+ Using in a service:
475
496
 
476
497
  ```typescript
477
- import { AppService, InjectRepository } from "@avleon/core";
478
- import { Repository } from "typeorm";
479
- import { User } from "model_path";
498
+ import { AppService, InjectRepository } from '@avleon/core';
499
+ import { Repository } from 'typeorm';
500
+ import { User } from './user.entity';
480
501
 
481
502
  @AppService
482
503
  export class UserService {
483
504
  constructor(
484
505
  @InjectRepository(User)
485
- private readonly _userRepository: Repository<User>,
506
+ private readonly userRepo: Repository<User>,
486
507
  ) {}
487
508
 
488
509
  async findAll() {
489
- const users = await this._userRepository.find();
490
- return users;
510
+ return this.userRepo.find();
491
511
  }
492
512
  }
493
513
  ```
494
514
 
495
- ### File Uploads & File Storage
496
- Handle file uploads with multipart support:
515
+ ---
516
+
517
+ ### File Uploads
497
518
 
498
519
  ```typescript
499
- // Configure multipart file uploads
520
+ // Configure multipart support
500
521
  app.useMultipart({
501
522
  destination: path.join(process.cwd(), 'public/uploads'),
502
- limits: {
503
- fileSize: 5 * 1024 * 1024 // 5MB
504
- }
523
+ limits: { fileSize: 5 * 1024 * 1024 }, // 5MB
505
524
  });
506
525
  ```
526
+
507
527
  ```typescript
508
- // In your controller
509
- import {FileStorage} from '@avleon/core';
528
+ import { FileStorage, UploadFile, MultipartFile } from '@avleon/core';
510
529
 
511
- //inject FileStorage into constructor
512
- constructor(
513
- private readonly fileStorage: FileStorage
514
- ){}
530
+ @ApiController('/files')
531
+ class FileController {
532
+ constructor(private readonly fileStorage: FileStorage) {}
515
533
 
516
- @OpenApi({
517
- description: "Uploading single file"
518
- body:{
519
- type:"object",
520
- properties:{
521
- file:{
522
- type:"string",
523
- format:"binary"
524
- }
534
+ @OpenApi({
535
+ description: 'Upload a single file',
536
+ body: {
537
+ type: 'object',
538
+ properties: {
539
+ file: { type: 'string', format: 'binary' },
540
+ },
541
+ required: ['file'],
525
542
  },
526
- required:["file"]
543
+ })
544
+ @Post('/upload')
545
+ async upload(@UploadFile('file') file: MultipartFile) {
546
+ const result = await this.fileStorage.save(file);
547
+ // optionally rename: this.fileStorage.save(file, { as: 'newname.jpg' })
548
+ return result;
549
+ // { uploadPath: '/uploads/...', staticPath: '/static/...' }
527
550
  }
528
- })
529
- @Post('/upload')
530
- async uploadSingleFile(@UploadFile('file') file: MultipartFile) {
531
- // Process uploaded file
532
- const result = await this.fileStorage.save(file);
533
- // or with new name
534
- // const result = await this.fileStorage.save(file, {as:newname.ext});
535
- // result
536
- // {
537
- // uploadPath:"/uplaod",
538
- // staticPath: "/static/"
539
- //}
540
- return result;
541
551
  }
542
552
  ```
543
553
 
554
+ ---
555
+
544
556
  ### Static Files
545
- Serve static files:
546
557
 
547
558
  ```typescript
548
559
  import path from 'path';
549
560
 
550
-
551
561
  app.useStaticFiles({
552
- path: path.join(process.cwd(), "public"),
553
- prefix: "/static/",
562
+ path: path.join(process.cwd(), 'public'),
563
+ prefix: '/static/',
554
564
  });
555
565
  ```
556
566
 
557
- ## Configuration
558
- Coming soon...
567
+ ---
559
568
 
560
- ## Route Mapping
561
- Avleon provides several methods for mapping routes in your application:
562
-
563
- ### mapGet
564
- The `mapGet` method is used to define GET routes in your application. It takes a path string and a handler function as parameters.
569
+ ### WebSocket (Socket.IO)
565
570
 
566
571
  ```typescript
567
- app.mapGet("/users", async (req, res) => {
568
- // Handle GET request to /users
569
- return { users: [] };
570
- });
572
+ app.useSocketIo({ cors: { origin: '*' } });
571
573
  ```
572
574
 
573
- ### mapPost
574
- The `mapPost` method is used to define POST routes in your application. It takes a path string and a handler function as parameters.
575
+ Dispatch events from services:
575
576
 
576
577
  ```typescript
577
- app.mapPost("/users", async (req, res) => {
578
- // Handle POST request to /users
579
- const userData = req.body;
580
- // Process user data
581
- return { success: true };
582
- });
578
+ import { AppService, EventDispatcher } from '@avleon/core';
579
+
580
+ @AppService
581
+ export class UserService {
582
+ constructor(private readonly dispatcher: EventDispatcher) {}
583
+
584
+ async create(data: any) {
585
+ const user = await this.save(data);
586
+ await this.dispatcher.dispatch('users:created', { userId: user.id });
587
+ return user;
588
+ }
589
+ }
583
590
  ```
584
591
 
585
- ### mapPut
586
- The `mapPut` method is used to define PUT routes in your application. It takes a path string and a handler function as parameters.
592
+ ---
593
+
594
+ ## Route Mapping (Functional Style)
595
+
596
+ For simple routes without a controller class:
587
597
 
588
598
  ```typescript
589
- app.mapPut("/users/:id", async (req, res) => {
590
- // Handle PUT request to /users/:id
591
- const userId = req.params.id;
592
- const userData = req.body;
593
- // Update user data
599
+ app.mapGet('/users', async (req, res) => {
600
+ return { users: [] };
601
+ });
602
+
603
+ app.mapPost('/users', async (req, res) => {
594
604
  return { success: true };
595
605
  });
596
- ```
597
606
 
598
- ### mapDelete
599
- The `mapDelete` method is used to define DELETE routes in your application. It takes a path string and a handler function as parameters.
607
+ app.mapPut('/users/:id', async (req, res) => {
608
+ return { success: true };
609
+ });
600
610
 
601
- ```typescript
602
- app.mapDelete("/users/:id", async (req, res) => {
603
- // Handle DELETE request to /users/:id
604
- const userId = req.params.id;
605
- // Delete user
611
+ app.mapDelete('/users/:id', async (req, res) => {
606
612
  return { success: true };
607
613
  });
608
614
  ```
609
615
 
610
- ### Add openapi and middleware support for inline route
611
- Each of these methods returns a route object that can be used to add middleware or Swagger documentation to the route.
616
+ Add middleware and OpenAPI docs to functional routes:
612
617
 
613
618
  ```typescript
614
619
  app
615
- .mapGet("/users", async (req, res) => {
616
- // Handler function
620
+ .mapGet('/users', async (req, res) => {
621
+ return { users: [] };
617
622
  })
618
623
  .useMiddleware([AuthMiddleware])
619
624
  .useOpenApi({
620
- summary: "Get all users",
621
- description: "Retrieves a list of all users",
622
- tags: ["users"],
625
+ summary: 'Get all users',
626
+ tags: ['users'],
627
+ security: [{ bearerAuth: [] }],
623
628
  response: {
624
629
  200: {
625
- description: "Successful response",
626
- content: {
627
- "application/json": {
628
- schema: {
629
- type: "array",
630
- items: {
631
- type: "object",
632
- properties: {
633
- id: { type: "string" },
634
- name: { type: "string" },
635
- },
636
- },
637
- },
638
- },
639
- },
630
+ description: 'List of users',
631
+ type: 'array',
640
632
  },
641
633
  },
642
634
  });
643
635
  ```
644
- ### Websocket Intregation (Socket.io)
645
- ```typescript
646
- app.useSocketIO({
647
- cors:{origin:"*"}
648
- })
649
- ```
650
- Now in controller or service use EventDispatcher
651
636
 
637
+ ---
638
+
639
+ ## Testing
652
640
 
653
641
  ```typescript
654
- export class UserService{
655
- constructor(
656
- private readonly dispatcher: EventDispatcher
657
- )
642
+ import { AvleonTest } from '@avleon/core';
643
+ import { UserController } from './user.controller';
658
644
 
659
- async create(){
660
- ...rest code
661
-
662
- await this.dispatcher.dispatch("users:notifications",{created:true, userId: newUser.Id})
663
- }
664
-
665
- }
666
- ```
645
+ describe('UserController', () => {
646
+ let controller: UserController;
667
647
 
648
+ beforeAll(() => {
649
+ controller = AvleonTest.getController(UserController);
650
+ });
668
651
 
669
- ### Testing
652
+ it('should be defined', () => {
653
+ expect(controller).toBeDefined();
654
+ });
670
655
 
671
- Test your API endpoints with the built-in testing utilities:
656
+ it('should return users', async () => {
657
+ const result = await controller.getAll();
658
+ expect(Array.isArray(result)).toBe(true);
659
+ });
660
+ });
661
+ ```
672
662
 
673
- Coming soon...
663
+ ---
674
664
 
675
665
  ## License
676
666
 
677
- ISC
678
-
679
- ## Author
680
-
681
- Tareq Hossain - [GitHub](https://github.com/xtareq)
667
+ ISC © [Tareq Hossain](https://github.com/xtareq)