{"_id":"@alliage/webserver","_rev":"4-3915063ee723779d74ca5fd655266873","name":"@alliage/webserver","dist-tags":{"latest":"0.1.0-beta.2"},"versions":{"0.1.0-beta.0":{"name":"@alliage/webserver","version":"0.1.0-beta.0","author":{"name":"bo.lebon@gmail.com"},"license":"ISC","_id":"@alliage/webserver@0.1.0-beta.0","maintainers":[{"name":"borislebon","email":"bo.lebon@gmail.com"},{"name":"thehumblejester","email":"the.humble.jester@gmail.com"}],"homepage":"https://github.com/alliage-framework/web#readme","bugs":{"url":"https://github.com/alliage-framework/web/issues"},"dist":{"shasum":"68b9ae894aa3dca1cb55256034a961efa3c61c61","tarball":"https://registry.npmjs.org/@alliage/webserver/-/webserver-0.1.0-beta.0.tgz","fileCount":40,"integrity":"sha512-tTDA82fne8gi64aFNkcA3vfcjJapA+W/MLHRYjGXx+UBdRB1Wbnrim0Fs65e1wKND8iW57Gs3Y9q234dmgVCkw==","signatures":[{"sig":"MEYCIQD1SaJ+ytPYaovneGMyhDL9o0eEZWeaDPnk9CqysYRUyAIhAOGqRlnswlVI1n8j4/yCK5w4ws2elOlshLwbq0nBxFck","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":57787},"main":"dist/index.js","types":"dist/index.d.ts","module":"dist/index.js","gitHead":"b31bc17873915029257fb5629eea2472120c6f91","scripts":{"test":"echo \"Error: no test specified\" && exit 1"},"_npmUser":{"name":"borislebon","email":"bo.lebon@gmail.com"},"repository":{"url":"git+https://github.com/alliage-framework/web.git","type":"git"},"_npmVersion":"lerna/6.6.2/node@v18.16.0+x64 (linux)","description":"Webserver module for Alliage","directories":{},"_nodeVersion":"18.16.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"alliageManifest":{"type":"module","dependencies":["@alliage/module-installer","@alliage/config-loader","@alliage/process-manager"],"installationProcedures":{"copyFiles":[["base-files/config.yaml","config/webserver.yaml"]]}},"peerDependencies":{"@alliage/framework":"^1.0.0-beta.2","@alliage/config-loader":"^0.1.0-beta.1","@alliage/process-manager":"^0.1.0-beta.1","@alliage/module-installer":"^0.1.0-beta.1"},"_npmOperationalInternal":{"tmp":"tmp/webserver_0.1.0-beta.0_1683908387730_0.6193824077979508","host":"s3://npm-registry-packages"}},"0.1.0-beta.1":{"name":"@alliage/webserver","version":"0.1.0-beta.1","author":{"name":"bo.lebon@gmail.com"},"license":"ISC","_id":"@alliage/webserver@0.1.0-beta.1","maintainers":[{"name":"borislebon","email":"bo.lebon@gmail.com"}],"homepage":"https://github.com/alliage-framework/web#readme","bugs":{"url":"https://github.com/alliage-framework/web/issues"},"dist":{"shasum":"62e7c879af8f4e216c61850ccbd5fc50c8790db8","tarball":"https://registry.npmjs.org/@alliage/webserver/-/webserver-0.1.0-beta.1.tgz","fileCount":51,"integrity":"sha512-FMMsYq3PZ8VjnyAijSJIseihZx0aXqXuKVlzGYVHddgX/4BPdfsmcTVcMYUU6IGUSn4LWlnBihOJet5yNUVmoQ==","signatures":[{"sig":"MEUCICDrevFbewhNhG30ESS8+reuE+WhPS8yMNQ7ZNaiyaNxAiEA961hveDIK+7/waDn6MKDoBbZempuwE2nQ95Ilhji1H0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":73273},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","module":"dist/index.js","gitHead":"fa2b5c23766c2d6ed8bc7f628f26691407e43075","scripts":{"test":"echo \"Error: no test specified\" && exit 1"},"_npmUser":{"name":"borislebon","email":"bo.lebon@gmail.com"},"repository":{"url":"git+https://github.com/alliage-framework/web.git","type":"git"},"_npmVersion":"10.8.2","description":"Webserver module for Alliage","directories":{},"_nodeVersion":"20.19.2","dependencies":{"json-schema-to-ts":"^3.1.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"alliageManifest":{"type":"module","dependencies":["@alliage/module-installer","@alliage/config-loader","@alliage/process-manager"],"installationProcedures":{"copyFiles":[["base-files/config.yaml","config/webserver.yaml"]]}},"peerDependencies":{"@alliage/framework":"^1.0.0-beta.3","@alliage/config-loader":"^0.1.0-beta.6","@alliage/process-manager":"^0.1.0-beta.6","@alliage/module-installer":"^0.1.0-beta.6"},"_npmOperationalInternal":{"tmp":"tmp/webserver_0.1.0-beta.1_1748632342806_0.7246694526259692","host":"s3://npm-registry-packages-npm-production"}},"0.1.0-beta.2":{"name":"@alliage/webserver","version":"0.1.0-beta.2","description":"Webserver module for Alliage","type":"module","publishConfig":{"access":"public"},"main":"dist/index.js","module":"dist/index.js","types":"dist/index.d.ts","scripts":{"test":"echo \"Error: no test specified\" && exit 1"},"repository":{"type":"git","url":"git+https://github.com/alliage-framework/web.git"},"author":{"name":"bo.lebon@gmail.com"},"license":"ISC","bugs":{"url":"https://github.com/alliage-framework/web/issues"},"homepage":"https://github.com/alliage-framework/web#readme","alliageManifest":{"type":"module","dependencies":["@alliage/module-installer","@alliage/config-loader","@alliage/process-manager"],"installationProcedures":{"copyFiles":[["base-files/config.yaml","config/webserver.yaml"]]}},"peerDependencies":{"@alliage/config-loader":"^0.1.0-beta.6","@alliage/framework":"^1.0.0-beta.3","@alliage/module-installer":"^0.1.0-beta.6","@alliage/process-manager":"^0.1.0-beta.6"},"dependencies":{"json-schema-to-ts":"^3.1.1"},"_id":"@alliage/webserver@0.1.0-beta.2","gitHead":"4696313051fed9f147bb2db23b19f720264cbe12","_nodeVersion":"20.19.5","_npmVersion":"10.8.2","dist":{"integrity":"sha512-WRw/b3Es6WpMo33U04Yx/48iXzgu2NuRE8AklIA0L9RfcSUTyYcaOyg/Av1yzqm15bOoumd9rKjjLit05bOz1A==","shasum":"8cde3eceb83f19b427f494fcb6242358b2259345","tarball":"https://registry.npmjs.org/@alliage/webserver/-/webserver-0.1.0-beta.2.tgz","fileCount":51,"unpackedSize":73392,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCOcLdpIClB4DCt2MeQmdHcJqE5LSWziz0Rf0yUkULzogIhANUFdcT0EESKyBrC8M8TNY7pLSIAyf4D8BzX0N4Ehyop"}]},"_npmUser":{"name":"borislebon","email":"bo.lebon@gmail.com"},"directories":{},"maintainers":[{"name":"borislebon","email":"bo.lebon@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/webserver_0.1.0-beta.2_1758477477098_0.09288589085621335"},"_hasShrinkwrap":false}},"time":{"created":"2023-05-12T16:19:47.659Z","modified":"2025-09-21T17:57:57.504Z","0.1.0-beta.0":"2023-05-12T16:19:47.946Z","0.1.0-beta.1":"2025-05-30T19:12:22.981Z","0.1.0-beta.2":"2025-09-21T17:57:57.285Z"},"bugs":{"url":"https://github.com/alliage-framework/web/issues"},"author":{"name":"bo.lebon@gmail.com"},"license":"ISC","homepage":"https://github.com/alliage-framework/web#readme","repository":{"type":"git","url":"git+https://github.com/alliage-framework/web.git"},"description":"Webserver module for Alliage","maintainers":[{"name":"borislebon","email":"bo.lebon@gmail.com"}],"readme":"# Alliage Webserver\n\nAdd web server capabilities to your Alliage application with automatic controller registration and middleware support.\n\n## Dependencies\n\n- [@alliage/config-loader](https://github.com/alliage-framework/core/beta/main/packages/configuration-loader) - Configuration management\n- [@alliage/module-installer](https://github.com/alliage-framework/core/beta/main/packages/module-installer) - Module installation utilities\n- [@alliage/process-manager](https://github.com/alliage-framework/core/beta/main/packages/process-manager) - Process management\n\n## Installation\n\n```bash\nyarn add @alliage/webserver\n```\n\nOr with npm:\n\n```bash\nnpm install @alliage/webserver\n```\n\n\n## Registration\n\nIf you've already installed [@alliage/module-installer](https://github.com/alliage-framework/core/beta/main/packages/module-installer), simply run:\n\n```bash\nnpx alliage-scripts install @alliage/webserver\n```\n\nOtherwise, update your `alliage-modules.json` file by adding:\n\n```json\n{\n  // ... other modules\n  \"@alliage/webserver\": {\n    \"module\": \"@alliage/webserver\",\n    \"deps\": [\n      \"@alliage/config-loader\",\n      \"@alliage/module-installer\", \n      \"@alliage/process-manager\"\n    ],\n    \"envs\": []\n  }\n}\n```\n\n## Usage\n\nThis module provides a web server process that can handle HTTP requests through controllers and process them through middlewares. It uses an adapter pattern to support different HTTP server implementations.\n\n### Configuration\n\nUpon installation, a `config/webserver.yaml` file will be created with default settings:\n\n```yaml\nport: 8080\nhost: localhost\nisSecured: false\n# privateKey: \"\"\n# certificate: \"\"\n```\n\n#### Configuration Options\n\n- `port` (number, required): The port number for the web server\n- `host` (string, optional): The hostname to bind to (defaults to localhost)\n- `isSecured` (boolean): Whether to use HTTPS\n- `privateKey` (string, required if isSecured is true): Content of the private key file for HTTPS\n- `certificate` (string, required if isSecured is true): Content of the certificate file for HTTPS\n\n### Adapters\n\nThe webserver module requires an adapter to handle the actual HTTP server implementation. Adapters translate between the Alliage webserver abstractions and specific HTTP server libraries.\n\n#### Available Adapters\n\n- [**@alliage/webserver-express**](../webserver-express/): Express.js adapter for Node.js applications\n\n#### Installing an Adapter\n\n```bash\n# Install the Express adapter\nyarn add @alliage/webserver-express\n\n# Register it in your alliage-modules.json\n{\n  \"@alliage/webserver-express\": {\n    \"module\": \"@alliage/webserver-express\",\n    \"deps\": [\"@alliage/webserver\"],\n    \"envs\": []\n  }\n}\n```\n\n### Defining Controllers\n\nControllers handle HTTP requests and define routes. They must extend `AbstractController` and use route decorators.\n\n```typescript\nimport {\n  AbstractController,\n  AbstractRequest,\n  AbstractResponse,\n  Get,\n  Post,\n  Put,\n  Delete\n} from '@alliage/webserver';\nimport { Service } from '@alliage/service-loader';\n\ninterface CreateUserBody {\n  name: string;\n  email: string;\n}\n\n@Service('user-controller')\nexport default class UserController extends AbstractController {\n  @Get('/users')\n  async listUsers(request: AbstractRequest, response: AbstractResponse) {\n    // Fetch users from database\n    const users = await this.userService.findAll();\n    response.setBody({ users });\n  }\n\n  @Get('/users/:id')\n  async getUser(request: AbstractRequest, response: AbstractResponse) {\n    const { id } = request.getParams();\n    const user = await this.userService.findById(id);\n    \n    if (!user) {\n      response.setStatus(404).setBody({ error: 'User not found' });\n      return;\n    }\n    \n    response.setBody({ user });\n  }\n\n  @Post('/users')\n  async createUser(\n    request: AbstractRequest<unknown, unknown, CreateUserBody>,\n    response: AbstractResponse\n  ) {\n    const { name, email } = request.getBody();\n    const user = await this.userService.create({ name, email });\n    response.setStatus(201).setBody({ user });\n  }\n\n  @Put('/users/:id')\n  async updateUser(\n    request: AbstractRequest<unknown, unknown, Partial<CreateUserBody>>,\n    response: AbstractResponse\n  ) {\n    const { id } = request.getParams();\n    const updates = request.getBody();\n    const user = await this.userService.update(id, updates);\n    response.setBody({ user });\n  }\n\n  @Delete('/users/:id')\n  async deleteUser(request: AbstractRequest, response: AbstractResponse) {\n    const { id } = request.getParams();\n    await this.userService.delete(id);\n    response.setStatus(204);\n  }\n}\n```\n\n#### Route Decorators\n\nAvailable HTTP method decorators:\n\n- `@Get(path)` - Handle GET requests\n- `@Post(path)` - Handle POST requests  \n- `@Put(path)` - Handle PUT requests\n- `@Delete(path)` - Handle DELETE requests\n- `@Head(path)` - Handle HEAD requests\n- `@Options(path)` - Handle OPTIONS requests\n- `@Connect(path)` - Handle CONNECT requests\n- `@Trace(path)` - Handle TRACE requests\n\nPath parameters are supported using Express-style syntax: `/users/:id`, `/posts/:postId/comments/:commentId`\n\n### Defining Middlewares\n\nMiddlewares process requests before or after controllers. They must extend `AbstractMiddleware`.\n\n#### Custom Middleware Example\n\n```typescript\nimport { \n  AbstractMiddleware, \n  REQUEST_PHASE, \n  Context \n} from '@alliage/webserver';\nimport { Service } from '@alliage/service-loader';\n\n@Service('auth-middleware')\nexport default class AuthMiddleware extends AbstractMiddleware {\n  getRequestPhase = () => REQUEST_PHASE.PRE_CONTROLLER;\n\n  async apply(context: Context) {\n    const request = context.getRequest();\n    const response = context.getResponse();\n    \n    const authHeader = request.getHeaders().authorization;\n    \n    if (!authHeader || !authHeader.startsWith('Bearer ')) {\n      response.setStatus(401).setBody({ error: 'Unauthorized' }).end();\n      return;\n    }\n    \n    const token = authHeader.substring(7);\n    try {\n      const user = await this.authService.validateToken(token);\n      request.setExtraPayload('user', user);\n    } catch (error) {\n      response.setStatus(401).setBody({ error: 'Invalid token' }).end();\n    }\n  }\n}\n```\n\n#### Middleware Ordering\n\nMiddlewares can specify their execution order relative to other middlewares:\n\n```typescript\nimport { \n  AbstractMiddleware, \n  REQUEST_PHASE, \n  Context \n} from '@alliage/webserver';\nimport { Service } from '@alliage/service-loader';\n\n@Service('cors-middleware')\nexport default class CorsMiddleware extends AbstractMiddleware {\n  // This middleware should run before AuthMiddleware\n  applyBefore = () => [AuthMiddleware];\n  \n  // This middleware should run after LoggingMiddleware  \n  applyAfter = () => [LoggingMiddleware];\n\n  getRequestPhase = () => REQUEST_PHASE.PRE_CONTROLLER;\n\n  apply(context: Context) {\n    const response = context.getResponse();\n    response.setHeader('Access-Control-Allow-Origin', '*');\n    response.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');\n  }\n}\n```\n\n#### Request Phases\n\n- `REQUEST_PHASE.PRE_CONTROLLER`: Execute before controller actions\n- `REQUEST_PHASE.POST_CONTROLLER`: Execute after controller actions\n\n### Request and Response Objects\n\n#### AbstractRequest\n\nThe request object provides access to HTTP request data:\n\n```typescript\n// Get URL parameters\nconst { id } = request.getParams();\n\n// Get query parameters  \nconst { page, limit } = request.getQuery();\n\n// Get request body\nconst body = request.getBody();\n\n// Get headers\nconst contentType = request.getHeaders()['content-type'];\n\n// Get HTTP method\nconst method = request.getMethod();\n\n// Get URL path\nconst path = request.getPath();\n\n// Store/retrieve extra data\nrequest.setExtraPayload('user', userData);\nconst user = request.getExtraPayload('user');\n```\n\n#### AbstractResponse\n\nThe response object allows you to build HTTP responses:\n\n```typescript\n// Set response body\nresponse.setBody({ message: 'Success' });\n\n// Set status code\nresponse.setStatus(201);\n\n// Set headers\nresponse.setHeader('Content-Type', 'application/json');\n\n// Check if response is finished\nif (!response.isFinished()) {\n  response.setBody({ error: 'Something went wrong' });\n}\n\n// End the response (usually done automatically)\nresponse.end();\n```\n\n### Running the Web Server\n\nStart the web server using the process manager:\n\n```bash\n# Start with default configuration\n$ npx alliage-scripts start web\n\n# Start on a specific port\n$ npx alliage-scripts start web --port 3000\n```\n\n### Integration with Service Loader\n\nControllers and middlewares are automatically discovered and registered when using [@alliage/service-loader](https://github.com/alliage-framework/core/tree/main/packages/service-loader):\n\n1. Place controllers in files matching the service loader patterns (e.g., `src/controllers/**/*`)\n2. Export them as default exports with the `@Service` decorator\n3. They will be automatically registered with the webserver\n\n## Events\n\nThe webserver module emits several events during the request lifecycle that you can listen to for logging, monitoring, or custom processing.\n\n### Adapter Events\n\nThese events are emitted by adapters during request processing:\n\n| Event Type | Event Object | Description |\n|------------|--------------|-------------|\n| `ADAPTER_EVENTS.PRE_REQUEST` | [AdapterPreRequestEvent](#adapterprerequestevent) | Triggered before processing a request |\n| `ADAPTER_EVENTS.PRE_CONTROLLER` | [AdapterPreControllerEvent](#adapterprecontrollerevent) | Triggered before executing a controller action |\n| `ADAPTER_EVENTS.POST_CONTROLLER` | [AdapterPostControllerEvent](#adapterpostcontrollerevent) | Triggered after executing a controller action |\n| `ADAPTER_EVENTS.POST_REQUEST` | [AdapterPostRequestEvent](#adapterpostrequestevent) | Triggered after processing a request |\n| `ADAPTER_EVENTS.NOT_FOUND` | [AdapterNotFoundEvent](#adapternotfoundevent) | Triggered when no route matches the request |\n| `ADAPTER_EVENTS.SERVER_INITIALIZED` | [AdapterServerInitializedEvent](#adapterserverinitializedevent) | Triggered when the server is initialized |\n| `ADAPTER_EVENTS.SERVER_STARTED` | [AdapterServerStartedEvent](#adapterserverstartedevent) | Triggered when the server starts |\n| `ADAPTER_EVENTS.SERVER_STOPPED` | [AdapterServerStoppedEvent](#adapterserverstopedevent) | Triggered when the server stops |\n\n#### AdapterPreRequestEvent\n\nAvailable methods for this event:\n\n- `getRequest(): AbstractRequest` - Returns the request object\n- `getResponse(): AbstractResponse` - Returns the response object  \n- `getAdapterName(): string` - Returns the name of the adapter\n\n#### AdapterPreControllerEvent\n\nAvailable methods for this event:\n\n- `getController(): AbstractController` - Returns the controller instance\n- `getHandler(): RouteHandler` - Returns the route handler function\n- `getRequest(): AbstractRequest` - Returns the request object\n- `getResponse(): AbstractResponse` - Returns the response object\n- `getArguments(): unknown[]` - Returns the arguments that will be passed to the controller\n- `setArguments(args: unknown[]): void` - Allows modifying the arguments passed to the controller\n- `getAdapterName(): string` - Returns the name of the adapter\n\n#### AdapterPostControllerEvent\n\nAvailable methods for this event:\n\n- `getController(): AbstractController` - Returns the controller instance\n- `getHandler(): RouteHandler` - Returns the route handler function\n- `getRequest(): AbstractRequest` - Returns the request object\n- `getResponse(): AbstractResponse` - Returns the response object\n- `getReturnedValue(): unknown` - Returns the value returned by the controller\n- `getAdapterName(): string` - Returns the name of the adapter\n\n#### AdapterPostRequestEvent\n\nAvailable methods for this event:\n\n- `getRequest(): AbstractRequest` - Returns the request object\n- `getResponse(): AbstractResponse` - Returns the response object\n- `getAdapterName(): string` - Returns the name of the adapter\n\n#### AdapterNotFoundEvent\n\nAvailable methods for this event:\n\n- `getRequest(): AbstractRequest` - Returns the request object\n- `getResponse(): AbstractResponse` - Returns the response object\n- `getAdapterName(): string` - Returns the name of the adapter\n\n#### AdapterServerInitializedEvent\n\nAvailable methods for this event:\n\n- `getOptions(): ServerOptions` - Returns the server configuration options\n- `getAdapterName(): string` - Returns the name of the adapter\n- `getNativeServer(): http.Server | https.Server` - Returns the native HTTP server instance\n\n#### AdapterServerStartedEvent\n\nAvailable methods for this event:\n\n- `getOptions(): ServerOptions` - Returns the server configuration options\n- `getAdapterName(): string` - Returns the name of the adapter\n\n#### AdapterServerStoppedEvent\n\nAvailable methods for this event:\n\n- `getAdapterName(): string` - Returns the name of the adapter\n\n### Event Usage Example\n\n```typescript\nimport { AbstractModule } from '@alliage/framework';\nimport { ADAPTER_EVENTS } from '@alliage/webserver';\n\nexport default class LoggingModule extends AbstractModule {\n  getEventHandlers() {\n    return {\n      [ADAPTER_EVENTS.PRE_REQUEST]: this.onPreRequest,\n      [ADAPTER_EVENTS.POST_REQUEST]: this.onPostRequest,\n      [ADAPTER_EVENTS.NOT_FOUND]: this.onNotFound,\n    };\n  }\n\n  onPreRequest = async (event) => {\n    const request = event.getRequest();\n    console.log(`${request.getMethod()} ${request.getPath()}`);\n  };\n\n  onPostRequest = async (event) => {\n    const response = event.getResponse();\n    console.log(`Response status: ${response.getStatus()}`);\n  };\n\n  onNotFound = async (event) => {\n    const request = event.getRequest();\n    console.log(`404 - Route not found: ${request.getPath()}`);\n  };\n}\n```\n\n## Advanced Usage\n\n### Custom Adapters\n\nYou can create custom adapters by extending `AbstractAdapter`:\n\n```typescript\nimport { AbstractAdapter, InitializeParameters, ServerOptions } from '@alliage/webserver';\nimport http from 'http';\n\nexport class CustomAdapter extends AbstractAdapter {\n  private server?: http.Server;\n\n  getName() {\n    return 'custom-adapter';\n  }\n\n  initialize({ middlewares, controllers, options }: InitializeParameters) {\n    // Set up your HTTP server with controllers and middlewares\n    this.server = http.createServer(/* your implementation */);\n    return this;\n  }\n\n  async start(options: ServerOptions) {\n    return new Promise<void>((resolve, reject) => {\n      this.server!.listen(options.port, options.host, () => resolve());\n    });\n  }\n\n  async stop() {\n    return new Promise<void>((resolve) => {\n      this.server!.close(() => resolve());\n    });\n  }\n\n  getNativeServer() {\n    return this.server!;\n  }\n}\n```\n\n### Adapter Development Guide\n\nCreating a custom adapter requires understanding the theoretical rules and patterns that govern how adapters should handle requests, middlewares, controllers, and events.\n\n#### Core Principles\n\n1. **Request/Response Abstraction**: Adapters must implement concrete Request and Response classes that extend `AbstractRequest` and `AbstractResponse`\n2. **Event-Driven Architecture**: Adapters must emit specific events at precise moments in the request lifecycle\n3. **Middleware Chain**: Adapters must respect middleware ordering and phases (PRE_CONTROLLER vs POST_CONTROLLER)\n4. **Controller Routing**: Adapters must route requests to the appropriate controller methods based on HTTP method and path\n5. **Error Handling**: Adapters must handle errors thrown by controllers and middlewares gracefully\n6. **Resource Management**: Adapters must manage request/response object lifecycle to prevent memory leaks\n\n#### Request Lifecycle and Event Flow\n\nEvery request processed by an adapter must follow this exact sequence:\n\n```\n1. Request arrives at adapter\n2. Create Request/Response abstractions\n3. Emit PRE_REQUEST event\n4. Execute PRE_CONTROLLER middlewares (in order)\n5. Route matching and controller selection\n6. Emit PRE_CONTROLLER event\n7. Execute controller handler\n8. Emit POST_CONTROLLER event\n9. Execute POST_CONTROLLER middlewares (in order)\n10. If no route matched, emit NOT_FOUND event\n11. End response if not already ended\n12. Emit POST_REQUEST event\n13. Clean up Request/Response objects\n```\n\n#### Required Event Emissions\n\nAdapters **MUST** emit these events at the specified times:\n\n```typescript\n// 1. At server initialization (in initialize() method)\nconst initializedEvent = new AdapterServerInitializedEvent(\n  options,\n  this.getName(),\n  this.getNativeServer()\n);\nawait this.eventManager.emit(initializedEvent.getType(), initializedEvent);\n\n// 2. When server starts (in start() method)\nconst startedEvent = new AdapterServerStartedEvent(options, this.getName());\nawait this.eventManager.emit(startedEvent.getType(), startedEvent);\n\n// 3. Before processing any request\nawait this.eventManager.emit(\n  ...AdapterPreRequestEvent.getParams(request, response, this.getName())\n);\n\n// 4. Before calling a controller (if route matches)\nconst preControllerEvent = new AdapterPreControllerEvent(\n  controller,\n  handler,\n  request,\n  response,\n  [request, response, this.getName()], // Default arguments\n  this.getName()\n);\nawait this.eventManager.emit(preControllerEvent.getType(), preControllerEvent);\n\n// 5. After calling a controller\nawait this.eventManager.emit(\n  ...AdapterPostControllerEvent.getParams(\n    controller,\n    handler,\n    request,\n    response,\n    returnedValue,\n    this.getName()\n  )\n);\n\n// 6. When no route matches\nawait this.eventManager.emit(\n  ...AdapterNotFoundEvent.getParams(request, response, this.getName())\n);\n\n// 7. After processing request\nawait this.eventManager.emit(\n  ...AdapterPostRequestEvent.getParams(request, response, this.getName())\n);\n\n// 8. When server stops (in stop() method)\nconst stoppedEvent = new AdapterServerStoppedEvent(this.getName());\nawait this.eventManager.emit(stoppedEvent.getType(), stoppedEvent);\n```\n\n#### Middleware Handling Rules\n\nMiddlewares must be processed according to these rules:\n\n1. **Phase Separation**: Split middlewares into PRE_CONTROLLER and POST_CONTROLLER phases\n2. **Ordering**: Respect `applyBefore()` and `applyAfter()` constraints within each phase\n3. **Context**: Always pass a `Context` object containing request, response, and adapter name\n4. **Error Handling**: Check middleware arity to determine if it handles errors (2+ parameters)\n5. **Response State**: Check `response.isFinished()` before calling middleware\n6. **Error Propagation**: Catch middleware errors and pass them to the next middleware in the chain\n\n```typescript\n// Example middleware processing\nprivate async processMiddlewares(middlewares: AbstractMiddleware[], request: AbstractRequest, response: AbstractResponse, error?: Error) {\n  for (const middleware of middlewares) {\n    if (response.isFinished()) break;\n    \n    try {\n      const context = new Context(request, response, this.getName());\n      const handlesError = middleware.apply.length > 1;\n      \n      if (handlesError && error) {\n        await middleware.apply(context, error);\n        error = undefined; // Error was handled\n      } else if (!handlesError && !error) {\n        await middleware.apply(context);\n      }\n    } catch (e) {\n      error = e; // Propagate error to next middleware\n    }\n  }\n  \n  if (error) {\n    // Handle unprocessed error (e.g., send 500 response)\n  }\n}\n```\n\n#### Controller Handling Rules\n\nControllers must be processed according to these rules:\n\n1. **Route Registration**: Register all routes from all controllers during initialization\n2. **Route Matching**: Match incoming requests to registered routes by HTTP method and path\n3. **Parameter Extraction**: Extract path parameters and make them available via `request.getParams()`\n4. **Controller Execution**: Call the matched handler with the arguments from PRE_CONTROLLER event\n5. **Error Handling**: Catch controller errors and handle them appropriately\n6. **Response Management**: Ensure response is ended if controller doesn't end it\n\n```typescript\n// Example controller processing\ncontrollers.forEach((controller: AbstractController) => {\n  const routes = controller.getRoutes();\n  routes.forEach(([method, path, handler]) => {\n    this.registerRoute(method, path, async (req, res) => {\n      const request = this.getRequest(req);\n      const response = this.getResponse(res);\n      \n      try {\n        // Mark that a controller was matched\n        request.setExtraPayload('controller_matched', true);\n        \n        if (!response.isFinished()) {\n          const preControllerEvent = new AdapterPreControllerEvent(\n            controller,\n            handler,\n            request,\n            response,\n            [request, response, this.getName()],\n            this.getName()\n          );\n          await this.eventManager.emit(preControllerEvent.getType(), preControllerEvent);\n          \n          const returnedValue = await handler.call(\n            controller,\n            ...preControllerEvent.getArguments()\n          );\n          \n          await this.eventManager.emit(\n            ...AdapterPostControllerEvent.getParams(\n              controller,\n              handler,\n              request,\n              response,\n              returnedValue,\n              this.getName()\n            )\n          );\n        }\n      } catch (error) {\n        // Handle controller error\n        if (!response.isFinished()) {\n          response.setStatus(500).setBody({ error: 'Internal Server Error' });\n        }\n      }\n    });\n  });\n});\n```\n\n#### Request/Response Object Management\n\nAdapters must manage Request/Response object lifecycle:\n\n1. **Object Creation**: Create one Request/Response pair per HTTP request\n2. **Object Reuse**: Reuse the same objects throughout the request lifecycle\n3. **Object Storage**: Store objects in a way that allows retrieval by native request/response\n4. **Object Cleanup**: Remove objects after request processing to prevent memory leaks\n\n```typescript\nprivate requests = new Map<NativeRequest, Request>();\nprivate responses = new Map<NativeResponse, Response>();\n\nprivate getRequest(nativeReq: NativeRequest): Request {\n  let request = this.requests.get(nativeReq);\n  if (!request) {\n    request = new Request(nativeReq);\n    this.requests.set(nativeReq, request);\n  }\n  return request;\n}\n\nprivate removeRequest(nativeReq: NativeRequest) {\n  this.requests.delete(nativeReq);\n}\n```\n\n#### Error Handling Strategies\n\nAdapters should implement comprehensive error handling:\n\n1. **Controller Errors**: Catch and convert to HTTP error responses\n2. **Middleware Errors**: Pass to error-handling middlewares or convert to HTTP responses\n3. **System Errors**: Log and send generic 500 responses\n4. **Response State**: Never modify finished responses\n\n#### Complete Request and Response API\n\n##### AbstractRequest Interface\n\nThe `AbstractRequest` class provides access to HTTP request data and must be implemented by all adapters. Here's the complete API organized by functionality:\n\n###### Basic Request Information\n\n| Method | Return Type | Description |\n|--------|-------------|-------------|\n| `getMethod()` | `HTTP_METHOD` | Returns the HTTP method (GET, POST, PUT, DELETE, etc.) |\n| `getPath()` | `string` | Returns the URL path (e.g., \"/users/123\") |\n| `getOriginalUrl()` | `string` | Returns the full original URL as received |\n| `getBaseUrl()` | `string` | Returns the base URL without the path component |\n| `getProtocol()` | `string` | Returns the protocol (\"http\" or \"https\") |\n| `getHostName()` | `string` | Returns the hostname from the Host header |\n| `getHttpVersion()` | `string` | Returns the HTTP version (e.g., \"1.1\", \"2.0\") |\n\n###### Request Parameters and Data\n\n| Method | Return Type | Description |\n|--------|-------------|-------------|\n| `getQuery<Q = Params>()` | `Q` | Returns query string parameters as an object |\n| `getParams<P = Params>()` | `P` | Returns path parameters extracted from route (e.g., {id: \"123\"}) |\n| `getBody<B = any>()` | `B` | Returns the parsed request body |\n| `setBody<B>(body: B)` | `this` | Sets the request body (useful in middlewares) |\n| `getCookies()` | `{ [key: string]: string }` | Returns request cookies as key-value pairs |\n| `getSignedCookies()` | `Params` | Returns cryptographically signed cookies |\n\n###### Headers and Content Negotiation\n\n| Method | Return Type | Description |\n|--------|-------------|-------------|\n| `getHeader(name: string)` | `string \\| undefined` | Gets a specific header value |\n| `accepts(accept: string \\| string[])` | `false \\| string` | Checks if specific content types are accepted |\n| `accepts()` | `string[]` | Returns all accepted content types |\n| `acceptsCharsets(accept?: string \\| string[])` | `false \\| string \\| string[]` | Checks/gets accepted character sets |\n| `acceptsEncodings(accept?: string \\| string[])` | `false \\| string \\| string[]` | Checks/gets accepted encodings |\n| `acceptsLanguages(accept?: string \\| string[])` | `false \\| string \\| string[]` | Checks/gets accepted languages |\n| `is(type: string \\| string[])` | `string \\| false \\| null` | Checks if request matches content type |\n\n###### Network and Client Information\n\n| Method | Return Type | Description |\n|--------|-------------|-------------|\n| `getIP()` | `string \\| undefined` | Returns the client IP address |\n| `getIPs()` | `string[]` | Returns array of IP addresses (including proxies) |\n| `getSubdomains()` | `string[]` | Returns array of subdomains |\n| `getSocket()` | `Duplex` | Returns the underlying TCP socket |\n| `getConnection()` | `Duplex \\| null` | Returns the connection object |\n\n###### Request State and Validation\n\n| Method | Return Type | Description |\n|--------|-------------|-------------|\n| `isFresh()` | `boolean` | Checks if request is fresh (for cache validation) |\n| `isStale()` | `boolean` | Checks if request is stale (opposite of fresh) |\n| `isSecure()` | `boolean` | Checks if request is over HTTPS |\n| `isXHR()` | `boolean` | Checks if request is an XMLHttpRequest (AJAX) |\n| `isAborted()` | `boolean` | Checks if request was aborted by client |\n| `isComplete()` | `boolean` | Checks if request has been fully received |\n\n###### Stream and Low-Level Access\n\n| Method | Return Type | Description |\n|--------|-------------|-------------|\n| `getReadableStream()` | `Readable` | Returns request as a readable stream |\n| `getTrailers()` | `NodeJS.Dict<string>` | Returns HTTP trailers |\n| `destroy(error?: Error)` | `this` | Destroys the request stream |\n\n###### Event Handling\n\n| Method | Return Type | Description |\n|--------|-------------|-------------|\n| `onClose(callback: () => any)` | `this` | Registers callback for request close event |\n| `onAborted(callback: () => any)` | `this` | Registers callback for request abort event |\n\n###### Custom Data Storage\n\n| Method | Return Type | Description |\n|--------|-------------|-------------|\n| `getExtraPayload<T = any>(name: string)` | `T` | Retrieves custom data stored with the request |\n| `setExtraPayload<T>(name: string, value: T)` | `this` | Stores custom data with the request |\n\n###### Native Access\n\n| Method | Return Type | Description |\n|--------|-------------|-------------|\n| `getNativeRequest<N = any>()` | `N` | Returns the underlying native request object |\n\n##### AbstractResponse Interface\n\nThe `AbstractResponse` class provides methods to build and send HTTP responses. Here's the complete API:\n\n###### Status and Headers\n\n| Method | Return Type | Description |\n|--------|-------------|-------------|\n| `setStatus(status: number)` | `this` | Sets the HTTP status code |\n| `getStatus()` | `number` | Gets the current status code |\n| `setHeader(name: string, value: string)` | `this` | Sets a response header |\n| `getHeader(name: string)` | `string \\| number \\| string[] \\| undefined` | Gets a header value |\n| `removeHeader(name: string)` | `this` | Removes a response header |\n| `append(field: string, value: string \\| string[])` | `this` | Appends value to existing header |\n| `headersAreSent()` | `boolean` | Checks if headers have been sent to client |\n\n###### Response Body\n\n| Method | Return Type | Description |\n|--------|-------------|-------------|\n| `setBody<B>(body: B)` | `this` | Sets the response body (object, string, or buffer) |\n| `getBody<B = any>()` | `B \\| undefined` | Gets the current response body |\n| `send(body: string \\| Buffer \\| object)` | `this` | Sends the response body and ends the response |\n\n###### Cookies\n\n| Method | Return Type | Description |\n|--------|-------------|-------------|\n| `setCookie(name: string, value: ParamsValue, options: CookieOptions)` | `this` | Sets a cookie with options |\n| `clearCookie(name: string, options: CookieOptions)` | `this` | Clears a cookie |\n\n**CookieOptions Interface:**\n```typescript\ninterface CookieOptions {\n  domain?: string;          // Cookie domain\n  encode?: (value: string) => string;  // Custom encoding function\n  expires?: Date;           // Expiration date\n  httpOnly?: boolean;       // HTTP-only flag (prevents client-side access)\n  maxAge?: number;          // Maximum age in milliseconds\n  path?: string;            // Cookie path\n  secure?: boolean;         // Secure flag (HTTPS only)\n  signed?: boolean;         // Signed cookie flag\n  sameSite?: boolean | string;  // SameSite policy (\"strict\", \"lax\", \"none\")\n}\n```\n\n###### Response Control\n\n| Method | Return Type | Description |\n|--------|-------------|-------------|\n| `end()` | `void` | Ends the response (sends to client) |\n| `redirect(url: string, code?: number)` | `this` | Sends a redirect response |\n| `addAttachment(filename?: string)` | `this` | Sets Content-Disposition header for file download |\n\n###### Response State\n\n| Method | Return Type | Description |\n|--------|-------------|-------------|\n| `isFinished()` | `boolean` | Checks if response has been finished |\n| `isClosed()` | `boolean` | Checks if response connection is closed |\n\n###### Stream and Low-Level Access\n\n| Method | Return Type | Description |\n|--------|-------------|-------------|\n| `getWritableStream()` | `Writable` | Returns response as a writable stream |\n| `getConnection()` | `Duplex \\| null` | Gets the underlying connection |\n| `addTrailers(headers: { [name: string]: string })` | `this` | Adds HTTP trailers |\n| `writeHeaders(code: number, headers: { [name: string]: string })` | `this` | Writes headers with status code |\n| `flushHeaders()` | `this` | Flushes headers to client immediately |\n\n###### Event Handling\n\n| Method | Return Type | Description |\n|--------|-------------|-------------|\n| `onClose(callback: () => any)` | `this` | Registers callback for response close event |\n| `onFinish(callback: () => any)` | `this` | Registers callback for response finish event |\n\n###### Native Access\n\n| Method | Return Type | Description |\n|--------|-------------|-------------|\n| `getNativeResponse<N = any>()` | `N` | Returns the underlying native response object |\n\n##### Usage Examples\n\n###### Working with Request Data\n\n```typescript\n// In a controller or middleware\nasync handleRequest(request: AbstractRequest, response: AbstractResponse) {\n  // Get request information\n  const method = request.getMethod();\n  const path = request.getPath();\n  const { id } = request.getParams();\n  const { page, limit } = request.getQuery();\n  \n  // Check content type\n  if (request.is('application/json')) {\n    const body = request.getBody();\n    // Process JSON body\n  }\n  \n  // Store custom data for later use\n  request.setExtraPayload('startTime', Date.now());\n  \n  // Check if client accepts JSON\n  if (request.accepts('application/json')) {\n    response.setHeader('Content-Type', 'application/json');\n  }\n}\n```\n\n###### Building Responses\n\n```typescript\n// Set status and headers\nresponse\n  .setStatus(201)\n  .setHeader('Content-Type', 'application/json')\n  .setHeader('X-Custom-Header', 'value');\n\n// Set cookies\nresponse.setCookie('sessionId', 'abc123', {\n  httpOnly: true,\n  secure: true,\n  maxAge: 3600000, // 1 hour\n  sameSite: 'strict'\n});\n\n// Send JSON response\nresponse.setBody({ \n  message: 'Success',\n  data: results \n});\n\n// Or send and end in one call\nresponse.send({ error: 'Not found' });\n\n// Redirect\nresponse.redirect('/login', 302);\n```\n","readmeFilename":"README.md"}