[
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n- package-ecosystem: npm\n  directory: \"/\"\n  schedule:\n    interval: daily\n    time: \"10:00\"\n    timezone: Europe/Budapest\n  open-pull-requests-limit: 5\n  versioning-strategy: increase\n  commit-message:\n    prefix: build\n    include: scope\n  ignore:\n    - dependency-name: \"husky\"\n    - dependency-name: \"socket.io\"\n    - dependency-name: \"socket.io-client\""
  },
  {
    "path": ".github/semantic.yml",
    "content": "titleAndCommits: true\nallowMergeCommits: false\nscopes:\n  - deps\n  - deps-dev\ntypes:\n  - feat\n  - fix\n  - docs\n  - style\n  - refactor\n  - perf\n  - test\n  - build\n  - ci\n  - chore\n  - revert\n  - merge\n"
  },
  {
    "path": ".github/workflows/auto-approve-dependabot-workflow.yml",
    "content": "name: Dependabot auto-merge\non:\n  pull_request_target\njobs:\n  dependabot:\n    runs-on: ubuntu-latest\n    if: github.actor == 'dependabot[bot]'\n    steps:\n      - name: 'Auto approve PR by Dependabot'\n        uses: hmarr/auto-approve-action@v2.0.0\n        with:\n          github-token: \"${{ secrets.TYPESTACK_BOT_TOKEN }}\"\n      - name: 'Comment merge command'\n        uses: actions/github-script@v3\n        with:\n          github-token: ${{secrets.TYPESTACK_BOT_TOKEN }}\n          script: |\n            await github.issues.createComment({\n              owner: context.repo.owner,\n              repo: context.repo.repo,\n              issue_number: context.issue.number,\n              body: '@dependabot squash and merge'\n            })\n"
  },
  {
    "path": ".github/workflows/continuous-deployment-workflow.yml",
    "content": "name: CD\non:\n  release:\n    types: [created]\njobs:\n  publish:\n    name: Publish to NPM\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v3\n      - uses: actions/setup-node@v3\n        with:\n          node-version: 'lts/*'\n          registry-url: https://registry.npmjs.org\n      - run: npm ci --ignore-scripts\n      - run: npm run prettier:check\n      - run: npm run lint:check\n      - run: npm run test:ci\n      - run: npm run build\n      - run: cp LICENSE build/LICENSE\n      - run: cp README.md build/README.md\n      - run: jq 'del(.devDependencies) | del(.scripts)' package.json > build/package.json\n      - run: npm publish ./build\n        env:\n          NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}\n"
  },
  {
    "path": ".github/workflows/continuous-integration-workflow.yml",
    "content": "name: CI\non: [push, pull_request]\njobs:\n  checks:\n    name: Linters\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v3\n      - uses: actions/setup-node@v3\n        with:\n          node-version: 'lts/*'\n      - run: npm ci --ignore-scripts\n      - run: npm run prettier:check\n      - run: npm run lint:check\n  tests:\n    name: Tests\n    runs-on: ubuntu-latest\n    strategy:\n      matrix:\n        node-version: ['lts/*', 'current']\n      fail-fast: false\n    steps:\n      - uses: actions/checkout@v3\n      - name: Setting up Node.js (v${{ matrix.node-version }}.x)\n        uses: actions/setup-node@v3\n        with:\n          node-version: ${{ matrix.node-version }}\n      - run: npm ci --ignore-scripts\n      - run: npm run test:ci\n      - run: npm install codecov -g\n        if: ${{ matrix.node-version == 'current' }}\n      - run: codecov -f ./coverage/clover.xml -t ${{ secrets.CODECOV_TOKEN }} --commit=$GITHUB_SHA --branch=${GITHUB_REF##*/}\n        if: ${{ matrix.node-version == 'current' }}\n  build:\n    name: Build\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v3\n      - uses: actions/setup-node@v3\n        with:\n          node-version: 'lts/*'\n      - run: npm ci --ignore-scripts\n      - run: npm run build\n"
  },
  {
    "path": ".github/workflows/lock-closed-issues-workflow.yml",
    "content": "name: 'Lock inactive threads'\non:\n  schedule:\n    - cron: '0 0 * * *'\njobs:\n  lock:\n    name: Lock closed issues\n    runs-on: ubuntu-latest\n    steps:\n      - uses: dessant/lock-threads@v2\n        with:\n          github-token: ${{ github.token }}\n          issue-lock-inactive-days: 30\n          pr-lock-inactive-days: 30\n          issue-lock-comment: >\n            This issue has been automatically locked since there\n            has not been any recent activity after it was closed.\n            Please open a new issue for related bugs.\n          pr-lock-comment: >\n            This pull request has been automatically locked since there\n            has not been any recent activity after it was closed.\n            Please open a new issue for related bugs.\n"
  },
  {
    "path": ".gitignore",
    "content": "# Log files\nlogs\n*.log\n*.tmp\n*.tmp.*\nlog.txt\nnpm-debug.log*\n\n# Testing output\nlib-cov/**\ncoverage/**\n\n# Environment files\n.env\n\n# Dependency directories\nnode_modules\n\n# MacOS related files\n*.DS_Store\n.AppleDouble\n.LSOverride\n._*\nUserInterfaceState.xcuserstate\n\n# Windows related files\nThumbs.db\nDesktop.ini\n$RECYCLE.BIN/\n\n# IDE - Sublime\n*.sublime-project\n*.sublime-workspace\n\n# IDE - VSCode\n.vscode/**\n!.vscode/tasks.json\n!.vscode/launch.json\n\n# IDE - IntelliJ\n.idea\n\n# Compilation output folders\ndist/\nbuild/\ntmp/\nout-tsc/\ntemp\n\n# Files for playing around locally\nplayground.ts\nplayground.js"
  },
  {
    "path": ".prettierrc.yml",
    "content": "printWidth: 120\ntabWidth: 2\nuseTabs: false\nsemi: true\nsingleQuote: true\ntrailingComma: es5\nbracketSpacing: true\narrowParens: avoid"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Changelog\n\n_This changelog follows the [keep a changelog][keep-a-changelog]_ format to maintain a human readable changelog.\n\n## [0.3.1](https://github.com/typestack/socket-controllers/compare/v0.3.0...v0.3.1) (2024-04-21)\n\n### Changed\n\n- Added missing `@OnDisconnecting()` export to index\n\n## [0.3.0](https://github.com/typestack/socket-controllers/compare/v0.2.0...v0.3.0) (2024-04-21)\n\n### Breaking Changes\n\n- Middlewares without a defined namespace are registered on all namespaces\n\n### Changed\n\n- `glob` package updated from `10.0.0` to `10.3.12`\n- `path-to-regexp` package updated from `6.2.1` to `6.2.2`\n- `reflect-metadata` package updated from `0.1.13` to `0.2.2`\n- `socket.io` package updated from `4.5.4` to `4.7.5`\n\n## [0.2.0](https://github.com/typestack/socket-controllers/compare/v0.1.2...v0.2.0) (2023-04-10)\n\n### Breaking Changes\n\n- Replaced `ScopedContainerGetterParams` with `SocketEventContext`\n\n  BEFORE:\n\n  ```ts\n  scopedContainerGetter: (args: ScopedContainerGetterParams) => {\n    // ...\n  }\n  ```\n\n  AFTER:\n\n  ```ts\n  scopedContainerGetter: (args: SocketEventContext) => {\n    // ...\n  }\n  ```\n  Note: The new interface contains all properties of the previous\n\n\n### Added\n\n- Added scoped container dispose support  \n- Added interceptor support\n- Added ack support\n\n### Changed\n\n- `glob` package updated from `8.1.0` to `10.0.0`\n  \n## [0.1.2](https://github.com/typestack/socket-controllers/compare/v0.1.1...v0.1.2) (2023-01-30)\n\n### Added\n\n- Added scoped controller support\n\n  ```typescript\n  // create and run socket server\n  const server = new SocketControllers({\n    ...\n    scopedContainerGetter: (args: ScopedContainerGetterParams) => {\n      // Return a container instance to be used to instantiate \n      // the controllers and their dependencies on each event\n    }\n  });\n  ```\n\n## [0.1.1](https://github.com/typestack/socket-controllers/compare/v0.1.0...v0.1.1) (2023-01-27)\n\n### Added\n\n- Added `@OnDisconnecting()` decorator\n- Added error type filter option to `@EmitOnFail()` decorator\n  \n  Example: `@EmitOnFail('message', {errorType: TypeError})`\n\n- Added `index` option to `@MessageBody()` decorator to be able to get multiple event arguments\n\n  Note: If you don't specify the index it will return the first\n\n- Added support to use the same namespace for multiple controllers\n\n  Note: The namespaces must match exactly, providing a differnet pattern will not work due to a socket.io limitation\n\n### Changed\n\n- `glob` package updated from `8.0.3` to `8.1.0`\n\n## [0.1.0](https://github.com/typestack/socket-controllers/compare/v0.0.5...v0.1.0) (2023-01-18)\n\n### Breaking Changes\n\n- Removed `createSocketServer()` in favor of constructor initialization \n\n  BEFORE:\n\n  ```ts\n  import { useSocketServer } from 'socket-controllers';\n  import { Server } from 'socket.io';\n\n  const io = new Server(PORT);\n  useSocketServer(io);\n  ```\n\n  AFTER:\n\n  ```ts\n  import { SocketControllers } from \"socket-controllers\";\n  import { Server } from \"socket.io\";\n\n  const io = new Server(PORT);\n  new SocketControllers({io: io, container: YOUR_DI_CONTAINER});\n  ```\n- Removed `createSocketServer()` in favor of constructor initialization \n\n  BEFORE:\n\n  ```ts\n  import { createSocketServer } from 'socket-controllers';\n\n  const io = createSocketServer(PORT);\n  ```\n\n  AFTER:\n\n  ```ts\n  import { SocketControllers } from \"socket-controllers\";\n\n  const server = new SocketControllers({port: PORT, container: YOUR_DI_CONTAINER});\n  const io = server.io;\n  ```\n\n- Removed `useContainer()` in favor of constructor initialization\n\n  BEFORE:\n\n  ```ts\n  import { useContainer } from 'socket-controllers';\n  import { Container } from 'typedi';\n\n  useContainer(Container);\n  ```\n\n  AFTER:\n\n  ```ts\n  import { SocketControllers } from \"socket-controllers\";\n  import { Container } from 'typedi';\n\n  const server = new SocketControllers({port: PORT, container: Container});\n  ```\n  > Note: DI container is not included anymore, you have to provide your own.\n\n- Changed initialization parameters\n\n  Before:\n  ```typescript\n  interface SocketControllersOptions {\n    controllers?: Function[] | string[];\n    middlewares?: Function[] | string[];\n    useClassTransformer?: boolean;\n    classToPlainTransformOptions?: ClassTransformOptions;\n    plainToClassTransformOptions?: ClassTransformOptions;\n  }\n  ```\n\n  After:\n  ```typescript\n  interface SocketControllersOptions {\n    container: { get<T>(someClass: { new (...args: any[]): T } | Function): T };\n    io?: Server;\n    port?: number;\n    controllers?: Function[] | string[];\n    middlewares?: Function[] | string[];\n    transformOption?: Partial<{\n      transform?: boolean;\n      parameterTransformOptions?: ClassTransformOptions;\n      resultTransformOptions?: ClassTransformOptions;\n    }>;\n  }\n  ```\n\n- Changed class-transformer property name in decorators that support class-transformer\n\n  Before:\n  `classTransformOptions?: ClassTransformOptions`\n\n  After:\n  `transformOptions?: ClassTransformOptions`\n\n### Added\n- Namespace scope support for middlewares\n- `transform: boolean` option to decorators that support class-transformer\n\n### Changed\n\n- `class-transformer` package updated from `0.1.6` to `0.5.1`\n- `path-to-regexp` package updated from `3.0.0` to `6.2.1`\n- `reflect-metadata` package updated from `0.1.10` to `0.1.13`\n- `socket.io` package updated from `2.0.1` to `4.5.4`\n- updated various dev dependencies\n\n\n### [0.0.5][v0.0.5] - 2020-02-04\n\n#### Added\n\n- Added support dynamic namespace\n- Added `NspParams`, `NspParam` decorators to handle dynamic namespace name params\n- Allowed use function array for controllers and middlewares\n\n#### Fixed\n\n- Import middlewares from directory\n\n[v0.0.5]: https://github.com/typestack/socket-controllers/compare/v0.0.4...v0.0.5\n[keep-a-changelog]: https://keepachangelog.com/en/1.0.0/\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2018-2020 TypeStack\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# socket-controllers\n\n![Build Status](https://github.com/typestack/socket-controllers/workflows/CI/badge.svg)\n[![codecov](https://codecov.io/gh/typestack/socket-controllers/branch/develop/graph/badge.svg)](https://codecov.io/gh/typestack/socket-controllers)\n[![npm version](https://badge.fury.io/js/socket-controllers.svg)](https://badge.fury.io/js/socket-controllers)\n\nUse class-based controllers to handle websocket events. Helps to organize your code using websockets in classes.\n\n## Installation\n\n1. Install `socket-controllers`:\n\n   ```\n   npm install socket-controllers\n   ```\n\n2. Install `reflect-metadata` shim:\n\n   ```\n   npm install reflect-metadata\n   ```\n\n   and make sure to import it in a global place, like app.ts:\n\n   ```typescript\n   import 'reflect-metadata';\n   ```\n   \n3. Install a DI container, for example `typedi`;\n   \n   ```\n   npm install typedi\n   ```\n\n## Example of usage\n\n1. Create a file `MessageController.ts`\n\n   ```typescript\n   import {\n     OnConnect,\n     SocketController,\n     ConnectedSocket,\n     OnDisconnect,\n     MessageBody,\n     OnMessage,\n   } from 'socket-controllers';\n   import {Service} from 'typedi'; // Only if you are using typedi\n\n   @SocketController()\n   @Service() // Only if you are using typedi\n   export class MessageController {\n     @OnConnect()\n     connection(@ConnectedSocket() socket: any) {\n       console.log('client connected');\n     }\n\n     @OnDisconnect()\n     disconnect(@ConnectedSocket() socket: any) {\n       console.log('client disconnected');\n     }\n\n     @OnMessage('save')\n     save(@ConnectedSocket() socket: any, @MessageBody() message: any) {\n       console.log('received message:', message);\n       console.log('setting id to the message and sending it back to the client');\n       message.id = 1;\n       socket.emit('message_saved', message);\n     }\n   }\n   ```\n\n2. Create a file `app.ts`\n\n   ```typescript\n   import 'es6-shim'; // this shim is optional if you are using old version of node\n   import 'reflect-metadata'; // this shim is required\n   import { SocketControllers } from 'socket-controllers';\n   import { MessageController } from './MessageController'; \n   import {Container} from 'typedi'; // Only if you are using typedi\n\n   new SocketControllers({\n     port: 3001,\n     container: Container,\n     controllers: [MessageController],\n   });\n   ```\n\n3. Now you can send `save` websocket message using websocket-client.\n\n## More usage examples\n\n#### Run code on socket client connect / disconnect / disconnecting\n\nController action marked with `@OnConnect()` decorator is called once new client connected.\nController action marked with `@OnDisconnect()` decorator is called once client disconnected.\nController action marked with `@OnDisconnecting()` decorator is called when the client is disconnecting, before the disconnect event.\n\n```typescript\nimport { SocketController, OnConnect, OnDisconnect, OnDisconnecting } from 'socket-controllers';\n\n@SocketController()\nexport class MessageController {\n  @OnConnect()\n  save() {\n    console.log('client connected');\n  }\n\n  @OnDisconnect()\n  save() {\n    console.log('client disconnected');\n  }\n\n  @OnDisconnecting()\n  save() {\n    console.log('client is disconnecting');\n  }\n}\n```\n\n#### `@ConnectedSocket()` decorator\n\nTo get connected socket instance you need to use `@ConnectedSocket()` decorator.\n\n```typescript\nimport { SocketController, OnMessage, ConnectedSocket } from 'socket-controllers';\n\n@SocketController()\nexport class MessageController {\n  @OnMessage('save')\n  save(@ConnectedSocket() socket: any) {\n    socket.emit('save_success');\n  }\n}\n```\n\n#### `@MessageBody()` decorator\n\nTo get received message body use `@MessageBody()` decorator:\n\n```typescript\nimport { SocketController, OnMessage, MessageBody } from 'socket-controllers';\n\n@SocketController()\nexport class MessageController {\n  @OnMessage('save')\n  save(@MessageBody() message: any) {\n    console.log('received message: ', message);\n  }\n}\n```\n\nIf you specify a class type to parameter that is decorated with `@MessageBody()`,\nsocket-controllers will use [class-transformer][1] to create instance of the given class type with the data received in the message.\nTo disable this behaviour you need to specify `{ transformOption: { transform: false ] }` in SocketControllerOptions when creating a server.\n\nYou can define an index to get multiple parameters from the socket event.\n\n```typescript\nimport { SocketController, OnMessage, MessageBody } from 'socket-controllers';\n\n@SocketController()\nexport class MessageController {\n  @OnMessage('save')\n  save(@MessageBody({index: 0}) param1: any, @MessageBody({index: 1}) param2: any) {\n    console.log('received message: ', message1);\n    console.log('received message: ', message2);\n  }\n}\n```\n\n#### `@MessageAck()` decorator\n\nTo get received message ack use `@MessageAck()` decorator:\n\n```typescript\nimport { SocketController, OnMessage, MessageAck, MessageBody } from 'socket-controllers';\n\n@SocketController()\nexport class MessageController {\n  @OnMessage('save')\n  save(@MessageBody() message: any, @MessageAck() ack: Function) {\n    console.log('received message: ', message);\n    ack('callback message');\n  }\n}\n```\n\n> note: ack must be the last parameter in `emit`, otherwise it will be `null`\n\n#### `@SocketQueryParam()` decorator\n\nTo get received query parameter use `@SocketQueryParam()` decorator.\n\n```typescript\nimport { SocketController, OnMessage, MessageBody } from 'socket-controllers';\n\n@SocketController()\nexport class MessageController {\n  @OnMessage('save')\n  save(@SocketQueryParam('token') token: string) {\n    console.log('authorization token from query parameter: ', token);\n  }\n}\n```\n\n#### Get socket client id using `@SocketId()` decorator\n\nTo get connected client id use `@SocketId()` decorator.\n\n```typescript\nimport { SocketController, OnMessage, MessageBody } from 'socket-controllers';\n\n@SocketController()\nexport class MessageController {\n  @OnMessage('save')\n  save(@SocketId() id: string) {}\n}\n```\n\n#### Get access to using socket.io instance using `@SocketIO()` decorator\n\n```typescript\nimport { SocketController, OnMessage, MessageBody } from 'socket-controllers';\n\n@SocketController()\nexport class MessageController {\n  @OnMessage('save')\n  save(@SocketIO() io: any) {\n    // now you can broadcast messages to specific rooms or namespaces using io instance\n  }\n}\n```\n\n#### Send message back to client after method execution\n\nYou can use `@EmitOnSuccess` decorator:\n\n```typescript\nimport { SocketController, OnMessage, EmitOnSuccess } from 'socket-controllers';\n\n@SocketController()\nexport class MessageController {\n  @OnMessage('save')\n  @EmitOnSuccess('save_successfully')\n  save() {\n    // after this controller executed \"save_successfully\" message will be emitted back to the client\n  }\n}\n```\n\nIf you return something, it will be returned in the emitted message data:\n\n```typescript\nimport { SocketController, OnMessage, EmitOnSuccess } from 'socket-controllers';\n\n@SocketController()\nexport class MessageController {\n  @OnMessage('save')\n  @EmitOnSuccess('save_successfully')\n  save() {\n    // after this controller executed \"save_successfully\" message will be emitted back to the client with message object\n    return {\n      id: 1,\n      text: 'new message',\n    };\n  }\n}\n```\n\nYou can also control what message will be emitted if there is error/exception during execution:\n\n```typescript\nimport { SocketController, OnMessage, EmitOnSuccess, EmitOnFail } from 'socket-controllers';\n\n@SocketController()\nexport class MessageController {\n  @OnMessage('save')\n  @EmitOnSuccess('save_successfully')\n  @EmitOnFail('save_error_range', {errorType: RangeError})\n  @EmitOnFail('save_error_type', {errorType: TypeError})\n  @EmitOnFail('save_error')\n  save() {\n    if (1 === 1) {\n      throw new Error('One is equal to one! Fatal error!');\n    }\n    return {\n      id: 1,\n      text: 'new message',\n    };\n  }\n}\n```\n\nIn this case `save_error` message will be sent to the client with `One is equal to one! Fatal error!` error message.\nThe order is important when defining multiple `@EmitOnFail()` decorators, the first matching errorType will be served\n\nSometimes you may want to not emit success/error message if returned result is null or undefined.\nIn such cases you can use `@SkipEmitOnEmptyResult()` decorator.\n\n```typescript\nimport { SocketController, OnMessage, EmitOnSuccess, EmitOnFail, SkipEmitOnEmptyResult } from 'socket-controllers';\n\n@SocketController()\nexport class MessageController {\n  @OnMessage('get')\n  @EmitOnSuccess('get_success')\n  @SkipEmitOnEmptyResult()\n  get(): Promise<Message[]> {\n    return this.messageRepository.findAll();\n  }\n}\n```\n\nIn this case if findAll will return undefined, `get_success` message will not be emitted.\nIf findAll will return array of messages, they will be emitted back to the client in the `get_success` message.\nThis example also demonstrates Promises support.\nIf promise returned by controller action, message will be emitted only after promise will be resolved.\n\n#### Using exist server instead of creating a new one\n\nIf you need to create and configure socket.io server manually,\nyou can pass it to the `SocketControllers` constructor.\nHere is example of creating socket.io server and configuring it with express:\n\n```typescript\nimport 'reflect-metadata'; // this shim is required\nimport { SocketControllers } from 'socket-controllers';\nimport { Server } from 'socket.io';\nimport { Container } from 'typedi'; // Only if you are using typedi\n\nconst app = require('express')();\nconst server = require('http').Server(app);\nconst io = new Server(server);\n\nserver.listen(3001);\n\napp.get('/', function (req: any, res: any) {\n  res.send('hello express');\n});\n\nio.use((socket: any, next: Function) => {\n  console.log('Custom middleware');\n  next();\n});\nnew SocketControllers({io, container: Container});\n```\n\n#### Load all controllers from the given directory\n\nYou can load all controllers in once from specific directories, by specifying array of directories via options in\n`createSocketServer` or `useSocketServer`:\n\n```typescript\nimport 'reflect-metadata'; // this shim is required\nimport { SocketControllers } from 'socket-controllers';\nimport { Container } from 'typedi'; // Only if you are using typedi\n\nnew SocketControllers({\n  port: 3000,\n  container: Container, \n  controllers: [__dirname + '/controllers/*.js'],\n}); // registers all given controllers\n```\n\n#### Using socket.io namespaces\n\nTo listen to messages only of the specific namespace you can mark a controller with namespace:\n\n```typescript\n@SocketController('/messages')\nexport class MessageController {\n  // ...\n}\n```\n\nAlso you can use dynamic namespace, like `express router` patterns:\n\n```typescript\n@SocketController('/messages/:userId')\nexport class MessageController {\n  // ...\n}\n```\n\n## Using middlewares\n\nMiddlewares are the functions passed to the `socketIo.use` method.\nMiddlewares allows you to define a logic that will be executed each time client connected to the server.\nTo create your middlewares use `@Middleware` decorator:\n\n```typescript\nimport { Middleware, MiddlewareInterface } from 'socket-controllers';\n\n@Middleware()\nexport class CompressionMiddleware implements MiddlewareInterface {\n  use(socket: any, next: (err?: any) => any) {\n    console.log('do something, for example get authorization token and check authorization');\n    next();\n  }\n}\n```\n\nYou can limit middlewares to namespaces providing either a `string`, `RegExp` or `Array<string | RegExp>` to the `namespace` parameter:\n\n```typescript\nimport { Middleware, MiddlewareInterface } from 'socket-controllers';\n\n@Middleware({namespace: '/test'})\nexport class CompressionMiddleware implements MiddlewareInterface {\n  use(socket: any, next: (err?: any) => any) {\n    console.log('do something, for example get authorization token and check authorization');\n    next();\n  }\n}\n```\n\n## Don't forget to load your controllers and middlewares\n\nControllers and middlewares should be loaded:\n\n```typescript\nimport 'reflect-metadata';\nimport { SocketControllers } from 'socket-controllers';\nimport { MessageController } from './MessageController';\nimport { MyMiddleware } from './MyMiddleware'; // here we import it\nimport { Container } from 'typedi'; // Only if you are using typedi\nconst server = new SocketControllers({\n  port: 3000,\n  container: Container,\n  controllers: [MessageController],\n  middlewares: [MyMiddleware],\n});\n```\n\nAlso you can load them from directories. Also you can use glob patterns:\n\n```typescript\nimport 'reflect-metadata';\nimport { SocketControllers } from 'socket-controllers';\nimport { Container } from 'typedi'; // Only if you are using typedi\nconst server = new SocketControllers({\n   port: 3000,\n   container: Container,\n   controllers: [__dirname + '/controllers/**/*.js'],\n   middlewares: [__dirname + '/middlewares/**/*.js'],\n});\n```\n\n## Using DI container\n\n`socket-controllers` supports a DI container out of the box. You can inject your services into your controllers and\nmiddlewares. Container must be setup during application bootstrap.\nHere is example how to integrate socket-controllers with [typedi](https://github.com/pleerock/typedi):\n\n```typescript\nimport 'reflect-metadata';\nimport { SocketControllers } from 'socket-controllers';\nimport { Container } from 'typedi';\n\n// create and run socket server\nconst server = new SocketControllers({\n  port: 3000,\n  container: Container,\n  controllers: [__dirname + '/controllers/*.js'],\n  middlewares: [__dirname + '/middlewares/*.js'],\n});\n```\n\nThat's it, now you can inject your services into your controllers:\n\n```typescript\n@Service()\n@SocketController()\nexport class MessageController {\n  constructor(private messageRepository: MessageRepository) {}\n\n  // ... controller actions\n}\n```\n\n> Note: TypeDI won't create instances for unknown classes since 0.9.0, you have to decorate your Class as a `Service()` as well.\n\n### Scoped controllers\n\nYou can enable scoped controllers by providing a `scopedContainerGetter` function in SocketServerOptions. This function should return a new container that will be used to instantiate the controller and its dependencies.\n\nYou will get a new instance for each event in the controller.\n\nThe `scopedContainerGetter` function receives the `SocketEventContext`.\n\nThe `scopedContainerDisposer` function receives the container instance you created with `scopedContainerGetter` after the socket action is finished. Use this function to dispose the container if needed.\n\n```typescript\nimport 'reflect-metadata';\nimport { SocketControllers, SocketEventContext } from 'socket-controllers';\nimport { Container, ContainerInstance, Token } from \"typedi\";\n\nconst myDiToken = new Token();\n\n// create and run socket server\nconst server = new SocketControllers({\n   port: 3000,\n   container: Container,\n   scopedContainerGetter: (args: SocketEventContext) => {\n      const container = Container.of(YOUR_REQUEST_CONTEXT);\n      container.set(myDiToken, 'MY_VALUE');\n      return container;\n   },\n   scopedContainerDisposer: (container: ContainerInstance) => {\n     container.dispose();\n   },\n   controllers: [__dirname + '/controllers/*.js'],\n   middlewares: [__dirname + '/middlewares/*.js'],\n});\n```\n\n## Interceptors\n\nInterceptors allow you to wrap your event handlers in higher order functions.\nWith interceptors you can add logging or modify the incoming or outgoing data for event handlers.\n\n```typescript\nimport {\n   SocketController,\n   OnMessage,\n   EmitOnSuccess,\n   EmitOnFail,\n   SkipEmitOnEmptyResult,\n   UseInterceptor,\n   MessageBody\n} from 'socket-controllers';\n\nconst interceptor: InterceptorInterface = {\n   use: (ctx: SocketEventContext, next: () => any) => {\n     ctx.messageArgs[0] = 'modified message from controller - ' + ctx.messageArgs[0];\n     const resp = next();\n     return 'modified response from controller - ' + resp; // modified response from controller - modified response from method - reponse\n   },\n};\n\n@Service()\nclass Interceptor implements InterceptorInterface {\n   async use(ctx: SocketEventContext, next: () => any) {\n     ctx.messageArgs[0] = 'modified message from method - ' + ctx.messageArgs[0];\n     const resp = await next();\n     return 'modified response from method - ' + resp; // modified response from method - reponse\n   }\n}\n\n@SocketController()\n@UseInterceptor(interceptor)\nexport class MessageController {\n   @OnMessage('get')\n   @EmitOnSuccess('get_success')\n   @SkipEmitOnEmptyResult()\n   @UseInterceptor(Interceptor)\n   get(@MessageBody() message: string): Promise<Message[]> {\n     console.log(message); // modified message from controller - modified message from method - original message\n     return 'response';\n   }\n}\n```\n\nInterceptors are executed in order of definition, starting with the controller interceptors.\n\n\n## Decorators Reference\n\n| Signature                              | Description                                                                                                                                                                                                                                                                |\n|----------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `@SocketController(namespace?: string\\ | Regex)`                                                                                                                                                                                                                                                                    | Registers a class to be a socket controller that can listen to websocket events and respond to them.                                                                                                                                                                       |\n| `@OnMessage(messageName: string)`      | Registers controller's action to be executed when socket receives message with given name.                                                                                                                                                                                 |\n| `@OnConnect()`                         | Registers controller's action to be executed when client connects to the socket.                                                                                                                                                                                           |\n| `@OnDisconnect()`                      | Registers controller's action to be executed when client disconnects from the socket.                                                                                                                                                                                      |\n| `@OnDisconnecting()`                   | Registers controller's action to be executed when client is disconnecting from the socket.                                                                                                                                                                                 |\n| `@ConnectedSocket()`                   | Injects connected client's socket object to the controller action.                                                                                                                                                                                                         |\n| `@SocketIO()`                          | Injects socket.io object that initialized a connection.                                                                                                                                                                                                                    |\n| `@MessageBody()`                       | Injects received message body.                                                                                                                                                                                                                                             |\n| `@SocketQueryParam(paramName: string)` | Injects query parameter from the received socket request.                                                                                                                                                                                                                  |\n| `@SocketId()`                          | Injects socket id from the received request.                                                                                                                                                                                                                               |\n| `@SocketRequest()`                     | Injects request object received by socket.                                                                                                                                                                                                                                 |\n| `@SocketRooms()`                       | Injects rooms of the connected socket client.                                                                                                                                                                                                                              |\n| `@NspParams()`                         | Injects dynamic namespace params.                                                                                                                                                                                                                                          |\n| `@NspParam(paramName: string)`         | Injects param from the dynamic namespace.                                                                                                                                                                                                                                  |\n| `@Middleware()`                        | Registers a new middleware to be registered in the socket.io.                                                                                                                                                                                                              |\n| `@EmitOnSuccess(messageName: string)`  | If this decorator is set then after controller action will emit message with the given name after action execution. It will emit message only if controller succeed without errors. If result is a Promise then it will wait until promise is resolved and emit a message. |\n| `@EmitOnFail(messageName: string)`     | If this decorator is set then after controller action will emit message with the given name after action execution. It will emit message only if controller throw an exception. If result is a Promise then it will wait until promise throw an error and emit a message.  |\n| `@SkipEmitOnEmptyResult()`             | Used in conjunction with @EmitOnSuccess and @EmitOnFail decorators. If result returned by controller action is null or undefined then messages will not be emitted by @EmitOnSuccess or @EmitOnFail decorators.                                                            |     |\n\n## Samples\n\nTake a look on samples in [./sample](https://github.com/pleerock/socket-controllers/tree/master/sample) for more examples\nof usage.\n\n## Related projects\n\n- If you are interested to create controller-based express or koa server use [routing-controllers](https://github.com/pleerock/routing-controllers) module.\n- If you need to use dependency injection in use [typedi](https://github.com/pleerock/typedi) module.\n\n[1]: https://github.com/pleerock/class-transformer\n"
  },
  {
    "path": "codecov.yml",
    "content": "coverage:\n  range: 70..100\n  round: down\n  precision: 2\n  status:\n    project:\n      default:\n        threshold: 0%\n        paths: \n          - src/**/*.ts\ncomment: off\nignore:\n  - testing/**/*.ts\n  - src/**/*.interface.ts\n"
  },
  {
    "path": "eslint.config.mjs",
    "content": "import typescriptEslint from '@typescript-eslint/eslint-plugin';\nimport tsParser from '@typescript-eslint/parser';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport js from '@eslint/js';\nimport { FlatCompat } from '@eslint/eslintrc';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\nconst compat = new FlatCompat({\n  baseDirectory: __dirname,\n  recommendedConfig: js.configs.recommended,\n  allConfig: js.configs.all,\n});\n\nexport default [...compat.extends(\n  'plugin:@typescript-eslint/recommended',\n  'plugin:@typescript-eslint/recommended-requiring-type-checking',\n  'plugin:jest/recommended',\n  'prettier',\n), {\n  plugins: {\n    '@typescript-eslint': typescriptEslint,\n  },\n  languageOptions: {\n    parser: tsParser,\n    ecmaVersion: 2018,\n    sourceType: 'module',\n    parserOptions: {\n      project: ['./tsconfig.json', './tsconfig.spec.json'],\n    },\n  },\n  rules: {\n    '@typescript-eslint/explicit-member-accessibility': 'off',\n    '@typescript-eslint/no-angle-bracket-type-assertion': 'off',\n    '@typescript-eslint/no-parameter-properties': 'off',\n    '@typescript-eslint/explicit-function-return-type': 'off',\n    '@typescript-eslint/member-delimiter-style': 'off',\n    '@typescript-eslint/no-inferrable-types': 'off',\n    '@typescript-eslint/no-explicit-any': 'off',\n    '@typescript-eslint/member-ordering': 'error',\n    '@typescript-eslint/ban-types': 'off',\n    '@typescript-eslint/no-unsafe-return': 'off',\n    '@typescript-eslint/no-unsafe-assignment': 'off',\n    '@typescript-eslint/no-unsafe-call': 'off',\n    '@typescript-eslint/no-unsafe-member-access': 'off',\n    '@typescript-eslint/explicit-module-boundary-types': 'off',\n    '@typescript-eslint/no-unsafe-function-type': 'off',\n    '@typescript-eslint/no-wrapper-object-types': 'off',\n  },\n}];"
  },
  {
    "path": "jest.config.js",
    "content": "module.exports = {\n  preset: 'ts-jest',\n  testEnvironment: 'node',\n  collectCoverageFrom: ['src/**/*.ts', '!src/**/index.ts', '!src/**/*.interface.ts'],\n  globals: {},\n  setupFilesAfterEnv: [\"./jest.setup.js\"],\n  transform: {\n    '^.+\\\\.tsx?$': [\n      'ts-jest',\n      {tsconfig: './tsconfig.spec.json'},\n    ],\n  }\n};\n"
  },
  {
    "path": "jest.setup.js",
    "content": "jest.setTimeout(30000);\n\nrequire(\"reflect-metadata\");"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"socket-controllers\",\n  \"version\": \"0.3.1\",\n  \"description\": \"Use class-based controllers to handle websocket events.\",\n  \"license\": \"MIT\",\n  \"main\": \"index.js\",\n  \"author\": {\n    \"name\": \"TypeStack contributors\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/pleerock/socket-controllers.git\"\n  },\n  \"keywords\": [\n    \"websocket\",\n    \"typescript\",\n    \"typescript-websocket\",\n    \"socket-controllers\",\n    \"socket.io\",\n    \"socket-server\"\n  ],\n  \"scripts\": {\n    \"build\": \"rimraf build && tsc --project tsconfig.prod.json\",\n    \"prettier:fix\": \"prettier --write \\\"**/*.ts\\\"\",\n    \"prettier:check\": \"prettier --check \\\"**/*.ts\\\"\",\n    \"lint:fix\": \"eslint --max-warnings 0 --fix src/**/*.ts\",\n    \"lint:check\": \"eslint --max-warnings 0 src/**/*.ts\",\n    \"test\": \"jest --coverage --verbose\",\n    \"test:watch\": \"jest --watch\",\n    \"test:ci\": \"jest --runInBand --no-cache --coverage --verbose\"\n  },\n  \"dependencies\": {\n    \"class-transformer\": \"^0.5.1\",\n    \"glob\": \"^11.0.0\",\n    \"path-to-regexp\": \"^8.1.0\",\n    \"reflect-metadata\": \"^0.2.2\",\n    \"socket.io\": \"^4.7.5\"\n  },\n  \"devDependencies\": {\n    \"@types/glob\": \"^8.1.0\",\n    \"@types/jest\": \"^29.5.13\",\n    \"@types/node\": \"^20.14.11\",\n    \"@types/path-to-regexp\": \"^1.7.0\",\n    \"@types/socket.io\": \"^3.0.2\",\n    \"@typescript-eslint/eslint-plugin\": \"^8.6.0\",\n    \"@typescript-eslint/parser\": \"^8.6.0\",\n    \"eslint\": \"^9.10.0\",\n    \"eslint-config-prettier\": \"^9.1.0\",\n    \"eslint-plugin-jest\": \"^28.8.3\",\n    \"express\": \"^4.21.0\",\n    \"husky\": \"^9.1.6\",\n    \"jest\": \"^29.7.0\",\n    \"lint-staged\": \"^15.2.10\",\n    \"prettier\": \"^3.3.3\",\n    \"rimraf\": \"6.0.1\",\n    \"socket.io-client\": \"^4.7.5\",\n    \"ts-jest\": \"^29.2.5\",\n    \"ts-node\": \"^10.9.2\",\n    \"typedi\": \"^0.10.0\",\n    \"typescript\": \"^5.6.2\"\n  }\n}\n"
  },
  {
    "path": "sample/sample1-simple-controller/Message.ts",
    "content": "export class Message {\n  id: number;\n  text: string;\n}\n"
  },
  {
    "path": "sample/sample1-simple-controller/MessageController.ts",
    "content": "import { Message } from './Message';\nimport { ConnectedSocket, MessageBody, OnConnect, OnDisconnect, OnMessage, SocketController } from '../../src';\n\n@SocketController()\nexport class MessageController {\n  @OnConnect()\n  connection(@ConnectedSocket() socket: any) {\n    console.log('client connected');\n  }\n\n  @OnDisconnect()\n  disconnect(@ConnectedSocket() socket: any) {\n    console.log('client disconnected');\n  }\n\n  @OnMessage('save')\n  save(@ConnectedSocket() socket: any, @MessageBody() message: Message) {\n    console.log('received message:', message);\n    console.log('setting id to the message and sending it back to the client');\n    message.id = 1;\n    socket.emit('message_saved', message);\n  }\n}\n"
  },
  {
    "path": "sample/sample1-simple-controller/app.ts",
    "content": "import 'reflect-metadata';\nimport { SocketControllers } from '../../src/index';\nimport { MessageController } from './MessageController';\nimport { Container } from 'typedi';\n\nnew SocketControllers({\n  port: 3001,\n  container: Container,\n  controllers: [MessageController],\n}); // creates socket.io server and registers all controllers there\n\nconsole.log('Socket.io is up and running on port 3001. Send messages via socket-io client.');\n"
  },
  {
    "path": "sample/sample1-simple-controller/index.html",
    "content": "<html>\n<script src=\"../../node_modules/socket.io-client/dist/socket.io.js\"></script>\n<script>\n    var socket = io(\"http://localhost:3001\");\n    socket.on(\"message_saved\", function (message) {\n        console.log(\"Saved message received back: \", message);\n    });\n\n    function onClick() {\n        socket.emit(\"save\", { text: \"Hello this is message\" });\n    }\n</script>\n<body>\n\nWatch console for events.<br/>\n<button onclick=\"onClick()\">Click to send a save event to the socket server.</button>\n\n</body>\n</html>"
  },
  {
    "path": "sample/sample2-use-created-socket-io/Message.ts",
    "content": "export class Message {\n  id: number;\n  text: string;\n}\n"
  },
  {
    "path": "sample/sample2-use-created-socket-io/MessageController.ts",
    "content": "import { Message } from './Message';\nimport { ConnectedSocket, MessageBody, OnConnect, OnDisconnect, OnMessage, SocketController } from '../../src';\n\n@SocketController()\nexport class MessageController {\n  @OnConnect()\n  connection(@ConnectedSocket() socket: any) {\n    console.log('client connected');\n  }\n\n  @OnDisconnect()\n  disconnect(@ConnectedSocket() socket: any) {\n    console.log('client disconnected');\n  }\n\n  @OnMessage('save')\n  save(@ConnectedSocket() socket: any, @MessageBody() message: Message) {\n    console.log('received message:', message);\n    console.log('setting id to the message and sending it back to the client');\n    message.id = 1;\n    socket.emit('message_saved', message);\n  }\n}\n"
  },
  {
    "path": "sample/sample2-use-created-socket-io/app.ts",
    "content": "import 'reflect-metadata';\nimport { SocketControllers } from '../../src/index';\nimport { MessageController } from './MessageController';\nimport { Server } from 'socket.io';\nimport { Container } from 'typedi';\n\nconst app = require('express')();\nconst server = require('http').Server(app);\nconst io = new Server(server);\n\nserver.listen(3001);\n\napp.get('/', function (req: any, res: any) {\n  res.send('hello express');\n});\n\nio.use((socket: any, next: Function) => {\n  console.log('Custom middleware');\n  next();\n});\nnew SocketControllers({\n  io,\n  container: Container,\n  controllers: [MessageController],\n});\n\nconsole.log('Socket.io is up and running on port 3001. Send messages via socket-io client.');\n"
  },
  {
    "path": "sample/sample2-use-created-socket-io/index.html",
    "content": "<html>\n<script src=\"../../node_modules/socket.io-client/dist/socket.io.js\"></script>\n<script>\n    var socket = io(\"http://localhost:3001\");\n    socket.on(\"message_saved\", function (message) {\n        console.log(\"Saved message received back: \", message);\n    });\n\n    function onClick() {\n        socket.emit(\"save\", { text: \"Hello this is message\" });\n    }\n</script>\n<body>\n\nWatch console for events.<br/>\n<button onclick=\"onClick()\">Click to send a save event to the socket server.</button>\n\n</body>\n</html>"
  },
  {
    "path": "sample/sample3-namespaces/Message.ts",
    "content": "export class Message {\n  id: number;\n  text: string;\n}\n"
  },
  {
    "path": "sample/sample3-namespaces/MessageController.ts",
    "content": "import { Message } from './Message';\nimport { ConnectedSocket, MessageBody, OnConnect, OnDisconnect, OnMessage, SocketController } from '../../src';\n\n@SocketController('/messages')\nexport class MessageController {\n  @OnConnect()\n  connection(@ConnectedSocket() socket: any) {\n    console.log('client connected');\n  }\n\n  @OnDisconnect()\n  disconnect(@ConnectedSocket() socket: any) {\n    console.log('client disconnected');\n  }\n\n  @OnMessage('save')\n  save(@ConnectedSocket() socket: any, @MessageBody() message: Message) {\n    console.log('received message:', message);\n    console.log('setting id to the message and sending it back to the client');\n    message.id = 1;\n    socket.emit('message_saved', message);\n  }\n}\n"
  },
  {
    "path": "sample/sample3-namespaces/app.ts",
    "content": "import 'reflect-metadata';\nimport { SocketControllers } from '../../src/index';\nimport { MessageController } from './MessageController';\nimport { Container } from 'typedi';\n\nnew SocketControllers({\n  port: 3001,\n  container: Container,\n  controllers: [MessageController],\n}); // creates socket.io server and registers all controllers there\n\nconsole.log('Socket.io is up and running on port 3001. Send messages via socket-io client.');\n"
  },
  {
    "path": "sample/sample3-namespaces/index.html",
    "content": "<html>\n<script src=\"../../node_modules/socket.io-client/dist/socket.io.js\"></script>\n<script>\n    var socket = io(\"http://localhost:3001/messages\");\n    socket.on(\"message_saved\", function (message) {\n        console.log(\"Saved message received back: \", message);\n    });\n\n    function onClick() {\n        socket.emit(\"save\", { text: \"Hello this is message\" });\n    }\n</script>\n<body>\n\nWatch console for events.<br/>\n<button onclick=\"onClick()\">Click to send a save event to the /messages namespace.</button>\n\n</body>\n</html>"
  },
  {
    "path": "sample/sample4-emitters/Message.ts",
    "content": "export class Message {\n  id: number;\n  text: string;\n}\n"
  },
  {
    "path": "sample/sample4-emitters/MessageController.ts",
    "content": "import { Message } from './Message';\nimport {\n  ConnectedSocket,\n  EmitOnFail,\n  EmitOnSuccess,\n  MessageBody,\n  OnConnect,\n  OnDisconnect,\n  OnMessage,\n  SkipEmitOnEmptyResult,\n  SocketController,\n} from '../../src';\n\n@SocketController()\nexport class MessageController {\n  @OnConnect()\n  connection(@ConnectedSocket() socket: any) {\n    console.log('client connected');\n  }\n\n  @OnDisconnect()\n  disconnect(@ConnectedSocket() socket: any) {\n    console.log('client disconnected');\n  }\n\n  @OnMessage('save')\n  @EmitOnSuccess('message_save_success')\n  @EmitOnFail('message_save_failed')\n  @SkipEmitOnEmptyResult()\n  save(@ConnectedSocket() socket: any, @MessageBody() message: Message) {\n    console.log('received message:', message);\n    console.log('setting id to the message and sending it back to the client');\n    message.id = 1;\n    return message;\n  }\n\n  @OnMessage('try_to_save')\n  @EmitOnSuccess('message_save_success')\n  @EmitOnFail('message_save_failed')\n  @SkipEmitOnEmptyResult()\n  trySave(@ConnectedSocket() socket: any, @MessageBody() message: Message) {\n    console.log('received message:', message);\n    throw new Error('No, cannot save =(');\n  }\n}\n"
  },
  {
    "path": "sample/sample4-emitters/app.ts",
    "content": "import 'reflect-metadata';\nimport { SocketControllers } from '../../src/index';\nimport { MessageController } from './MessageController';\nimport { Container } from 'typedi';\n\nnew SocketControllers({\n  port: 3001,\n  container: Container,\n  controllers: [MessageController],\n}); // creates socket.io server and registers all controllers there\n\nconsole.log('Socket.io is up and running on port 3001. Send messages via socket-io client.');\n"
  },
  {
    "path": "sample/sample4-emitters/index.html",
    "content": "<html>\n<script src=\"../../node_modules/socket.io-client/dist/socket.io.js\"></script>\n<script>\n    var socket = io(\"http://localhost:3001\");\n    socket.on(\"message_save_success\", function (message) {\n        console.log(\"Saved message received back: \", message);\n    });\n    socket.on(\"message_save_failed\", function (error) {\n        console.log(\"Error during message save: \", error);\n    });\n\n    function onClick() {\n        socket.emit(\"save\", { text: \"Hello this is message\" });\n    }\n    function tryToSave() {\n        socket.emit(\"try_to_save\", { text: \"Hello this is message\" });\n    }\n</script>\n<body>\n\nWatch console for events.<br/>\n<button onclick=\"onClick()\">Click to send a save event to the socket server.</button><br/>\n<button onclick=\"tryToSave()\">Try to save and you'll get an error</button>\n\n</body>\n</html>"
  },
  {
    "path": "sample/sample5-middlewares/AuthenticationMiddleware.ts",
    "content": "import { Middleware, MiddlewareInterface } from '../../src';\n\n@Middleware()\nexport class AuthenticationMiddleware implements MiddlewareInterface {\n  use(socket: any, next: (err?: any) => any): any {\n    console.log('authentication...');\n    next();\n  }\n}\n"
  },
  {
    "path": "sample/sample5-middlewares/Message.ts",
    "content": "export class Message {\n  id: number;\n  text: string;\n}\n"
  },
  {
    "path": "sample/sample5-middlewares/MessageController.ts",
    "content": "import { Message } from './Message';\nimport { ConnectedSocket, MessageBody, OnConnect, OnDisconnect, OnMessage, SocketController } from '../../src';\n\n@SocketController()\nexport class MessageController {\n  @OnConnect()\n  connection(@ConnectedSocket() socket: any) {\n    console.log('client connected');\n  }\n\n  @OnDisconnect()\n  disconnect(@ConnectedSocket() socket: any) {\n    console.log('client disconnected');\n  }\n\n  @OnMessage('save')\n  save(@ConnectedSocket() socket: any, @MessageBody() message: Message) {\n    console.log('received message:', message);\n    console.log('setting id to the message and sending it back to the client');\n    message.id = 1;\n    socket.emit('message_saved', message);\n  }\n}\n"
  },
  {
    "path": "sample/sample5-middlewares/app.ts",
    "content": "import 'reflect-metadata';\nimport { SocketControllers } from '../../src/index';\nimport { AuthenticationMiddleware } from './AuthenticationMiddleware';\nimport { MessageController } from './MessageController';\nimport { Container } from 'typedi';\n\nnew SocketControllers({\n  port: 3001,\n  container: Container,\n  controllers: [MessageController],\n  middlewares: [AuthenticationMiddleware],\n}); // creates socket.io server and registers all controllers and middlewares there\n\nconsole.log('Socket.io is up and running on port 3001. Send messages via socket-io client.');\n"
  },
  {
    "path": "sample/sample5-middlewares/index.html",
    "content": "<html>\n<script src=\"../../node_modules/socket.io-client/dist/socket.io.js\"></script>\n<script>\n    var socket = io(\"http://localhost:3001\");\n    socket.on(\"message_saved\", function (message) {\n        console.log(\"Saved message received back: \", message);\n    });\n\n    function onClick() {\n        socket.emit(\"save\", { text: \"Hello this is message\" });\n    }\n</script>\n<body>\n\nWatch console for events.<br/>\n<button onclick=\"onClick()\">Click to send a save event to the socket server.</button>\n\n</body>\n</html>"
  },
  {
    "path": "sample/sample6-dynamic-namespaces/Message.ts",
    "content": "export class Message {\n  id: number;\n  text: string;\n}\n"
  },
  {
    "path": "sample/sample6-dynamic-namespaces/MessageController.ts",
    "content": "import { Message } from './Message';\nimport {\n  ConnectedSocket,\n  MessageBody,\n  NspParams,\n  OnConnect,\n  OnDisconnect,\n  OnMessage,\n  SocketController,\n} from '../../src';\n\n@SocketController('/messages/:id')\nexport class MessageController {\n  @OnConnect()\n  connection(@ConnectedSocket() socket: any) {\n    console.log('client connected');\n  }\n\n  @OnDisconnect()\n  disconnect(@ConnectedSocket() socket: any) {\n    console.log('client disconnected');\n  }\n\n  @OnMessage('save')\n  async save(@ConnectedSocket() socket: any, @MessageBody() message: Message, @NspParams() params: any[]) {\n    console.log('received message:', message);\n    console.log('namespace params:', params);\n    console.log('setting id to the message and sending it back to the client');\n    message.id = 1;\n    socket.emit('message_saved', message);\n  }\n}\n"
  },
  {
    "path": "sample/sample6-dynamic-namespaces/app.ts",
    "content": "import 'reflect-metadata';\nimport { SocketControllers } from '../../src/index';\nimport { MessageController } from './MessageController';\nimport { Container } from 'typedi';\n\nnew SocketControllers({\n  port: 3001,\n  container: Container,\n  controllers: [MessageController],\n}); // creates socket.io server and registers all controllers there\n\nconsole.log('Socket.io is up and running on port 3001. Send messages via socket-io client.');\n"
  },
  {
    "path": "sample/sample6-dynamic-namespaces/index.html",
    "content": "<html>\n<script src=\"../../node_modules/socket.io-client/dist/socket.io.js\"></script>\n<script>\n    var socket = io(\"http://localhost:3001/messages/1\");\n    socket.on(\"message_saved\", function (message) {\n        console.log(\"Saved message received back: \", message);\n    });\n\n    function onClick() {\n        socket.emit(\"save\", { text: \"Hello this is message\" });\n    }\n</script>\n<body>\n\nWatch console for events.<br/>\n<button onclick=\"onClick()\">Click to send a save event to the /messages/1 namespace.</button>\n\n</body>\n</html>"
  },
  {
    "path": "src/SocketControllers.ts",
    "content": "import { Namespace, Server, Socket } from 'socket.io';\nimport { sync } from 'glob';\nimport { normalize } from 'path';\nimport { SOCKET_CONTROLLER_META_KEY } from './types/SocketControllerMetaKey';\nimport { pathToRegexp } from 'path-to-regexp';\nimport { HandlerMetadata } from './types/HandlerMetadata';\nimport { HandlerType } from './types/enums/HandlerType';\nimport { SocketControllersOptions } from './types/SocketControllersOptions';\nimport { ControllerMetadata } from './types/ControllerMetadata';\nimport { MiddlewareMetadata } from './types/MiddlewareMetadata';\nimport { SocketEventType } from './types/enums/SocketEventType';\nimport { ActionMetadata } from './types/ActionMetadata';\nimport { ParameterMetadata } from './types/ParameterMetadata';\nimport { ParameterType } from './types/enums/ParameterType';\nimport { ResultType } from './types/enums/ResultType';\nimport { getMetadata } from './util/get-metadata';\nimport { TransformOptions } from './types/TransformOptions';\nimport { defaultTransformOptions } from './types/constants/defaultTransformOptions';\nimport { ActionTransformOptions } from './types/ActionTransformOptions';\nimport { instanceToPlain, plainToInstance } from 'class-transformer';\nimport { MiddlewareInterface } from './types/MiddlewareInterface';\nimport { InterceptorInterface } from './types/InterceptorInterface';\nimport { chainExecute } from './util/chain-execute';\nimport { SocketEventContext } from './types/SocketEventContext';\n\nexport class SocketControllers {\n  public container: { get<T>(someClass: { new (...args: any[]): T } | Function): T };\n  public controllers: HandlerMetadata<ControllerMetadata>[];\n  public middlewares: HandlerMetadata<MiddlewareMetadata>[];\n  public io: Server;\n  public transformOptions: TransformOptions;\n\n  constructor(private options: SocketControllersOptions) {\n    this.container = options.container;\n    this.io = options.io || new Server(options.port);\n    this.transformOptions = {\n      ...defaultTransformOptions,\n      ...options.transformOption,\n    };\n    this.controllers = this.loadHandlers<ControllerMetadata>(options.controllers || [], HandlerType.CONTROLLER);\n    this.middlewares = this.loadHandlers<MiddlewareMetadata>(options.middlewares || [], HandlerType.MIDDLEWARE);\n\n    this.registerMiddlewares();\n    this.registerControllers();\n  }\n\n  private loadHandlers<T extends Object>(handlers: Array<Function | string>, type: HandlerType): HandlerMetadata<T>[] {\n    const loadedHandlers: Function[] = [];\n\n    for (const handler of handlers) {\n      if (typeof handler === 'string') {\n        loadedHandlers.push(...this.loadHandlersFromPath(handler, type));\n      } else {\n        loadedHandlers.push(handler);\n      }\n    }\n\n    return loadedHandlers.map(handler => {\n      return {\n        metadata: getMetadata(handler),\n        target: handler,\n      };\n    });\n  }\n\n  private loadHandlersFromPath(path: string, handlerType: HandlerType): Function[] {\n    const files = sync(normalize(path).replace(/\\\\/g, '/'));\n\n    return files\n      .map(file => require(file))\n      .reduce((loadedFiles: Function[], loadedFile: Record<string, any>) => {\n        const handlersInFile = Object.values(loadedFile).filter(fileEntry => {\n          if (typeof fileEntry !== 'function') {\n            return false;\n          }\n\n          if (!(Reflect as any).hasMetadata(SOCKET_CONTROLLER_META_KEY, fileEntry as Function)) {\n            return false;\n          }\n\n          return (Reflect as any).getMetadata(SOCKET_CONTROLLER_META_KEY, fileEntry as Function).type === handlerType;\n        });\n        loadedFiles.push(...(handlersInFile as Function[]));\n\n        return loadedFiles;\n      }, []);\n  }\n\n  private registerMiddlewares() {\n    const middlewares = this.middlewares.slice().sort((middleware1, middleware2) => {\n      return (middleware1.metadata.priority || 0) - (middleware2.metadata.priority || 0);\n    });\n\n    const middlewaresWithoutNamespace = middlewares.filter(middleware => !middleware.metadata.namespace);\n\n    for (const middleware of middlewaresWithoutNamespace) {\n      this.registerMiddleware(this.io as unknown as Namespace, middleware);\n    }\n\n    this.io.on('new_namespace', (namespace: Namespace) => {\n      for (const middleware of middlewares) {\n        const middlewareNamespaces = Array.isArray(middleware.metadata.namespace)\n          ? middleware.metadata.namespace\n          : [middleware.metadata.namespace];\n\n        const shouldApply = middlewareNamespaces.some(nsp => {\n          // Register middlewares without namespace too\n          if (nsp == null) {\n            return true;\n          }\n\n          const nspRegexp = nsp instanceof RegExp ? nsp : pathToRegexp(nsp).regexp;\n          return nspRegexp.test(namespace.name);\n        });\n\n        if (shouldApply) {\n          this.registerMiddleware(namespace, middleware);\n        }\n      }\n    });\n  }\n\n  private registerControllers() {\n    const controllersWithoutNamespace = this.controllers.filter(controller => !controller.metadata.namespace);\n    const controllersWithNamespace = this.controllers.filter(controller => !!controller.metadata.namespace);\n\n    this.io.on('connection', (socket: Socket) => {\n      for (const controller of controllersWithoutNamespace) {\n        this.registerController(socket, controller);\n      }\n    });\n\n    const controllerNamespaceMap: Record<string, HandlerMetadata<ControllerMetadata>[]> = {};\n    const controllerNamespaceRegExpMap: Record<string, string | RegExp> = {};\n\n    for (const controller of controllersWithNamespace) {\n      const nsp = controller.metadata.namespace as string;\n      if (!controllerNamespaceMap[nsp]) {\n        controllerNamespaceMap[nsp] = [];\n      }\n      controllerNamespaceMap[nsp].push(controller);\n      controllerNamespaceRegExpMap[nsp] = nsp;\n    }\n\n    for (const [nsp, controllers] of Object.entries(controllerNamespaceMap)) {\n      const namespace = controllerNamespaceRegExpMap[nsp];\n      this.io\n        .of(namespace instanceof RegExp ? namespace : pathToRegexp(namespace).regexp)\n        .on('connection', (socket: Socket) => {\n          for (const controller of controllers) {\n            this.registerController(socket, controller);\n          }\n        });\n    }\n  }\n\n  private registerController(socket: Socket, controller: HandlerMetadata<ControllerMetadata>) {\n    const connectedAction = Object.values(controller.metadata.actions || {}).find(\n      action => action.type === SocketEventType.CONNECT\n    );\n    const disconnectedAction = Object.values(controller.metadata.actions || {}).find(\n      action => action.type === SocketEventType.DISCONNECT\n    );\n    const disconnectingAction = Object.values(controller.metadata.actions || {}).find(\n      action => action.type === SocketEventType.DISCONNECTING\n    );\n    const messageActions = Object.values(controller.metadata.actions || {}).filter(\n      action => action.type === SocketEventType.MESSAGE\n    );\n\n    if (connectedAction) {\n      void this.executeAction(socket, controller, connectedAction);\n    }\n\n    if (disconnectedAction) {\n      socket.on('disconnect', () => {\n        void this.executeAction(socket, controller, disconnectedAction);\n      });\n    }\n\n    if (disconnectingAction) {\n      socket.on('disconnecting', () => {\n        void this.executeAction(socket, controller, disconnectingAction);\n      });\n    }\n\n    for (const messageAction of messageActions) {\n      socket.on(messageAction.options.name, (...args: any[]) => {\n        const messages: any[] = args.slice(0, -1);\n        let ack: Function | null = args[args.length - 1];\n\n        if (!(ack instanceof Function)) {\n          messages.push(ack);\n          ack = null;\n        }\n\n        void this.executeAction(socket, controller, messageAction, messageAction.options.name as string, messages, ack);\n      });\n    }\n  }\n\n  private async executeAction(\n    socket: Socket,\n    controller: HandlerMetadata<ControllerMetadata>,\n    action: ActionMetadata,\n    eventName?: string,\n    data?: any[],\n    ack?: Function | null\n  ) {\n    const eventContext = this.resolveEventContext(\n      socket,\n      action.type,\n      eventName,\n      data,\n      controller.metadata.namespace,\n      ack\n    );\n\n    let container = this.container;\n    if (this.options.scopedContainerGetter) {\n      container = this.options.scopedContainerGetter(eventContext);\n    }\n\n    try {\n      const controllerInstance: any = container.get(controller.target);\n\n      const actions = [\n        ...(action.interceptors || []).map(interceptor => {\n          return (\n            ((interceptor as any) instanceof Function\n              ? container.get(interceptor)\n              : interceptor) as InterceptorInterface\n          ).use.bind(interceptor);\n        }),\n        (context: SocketEventContext) => {\n          const parameters = this.resolveParameters(\n            socket,\n            controller.metadata,\n            action.parameters || [],\n            context.messageArgs,\n            ack\n          );\n          return controllerInstance[action.methodName](...parameters);\n        },\n      ];\n\n      const actionResult = chainExecute(eventContext, actions);\n      const result = await Promise.resolve(actionResult);\n      this.handleActionResult(socket, action, result, ResultType.EMIT_ON_SUCCESS);\n    } catch (error: any) {\n      this.handleActionResult(socket, action, error, ResultType.EMIT_ON_FAIL);\n    }\n\n    if (this.options.scopedContainerDisposer) {\n      this.options.scopedContainerDisposer(container);\n    }\n  }\n\n  private handleActionResult(socket: Socket, action: ActionMetadata, result: any, resultType: ResultType) {\n    const allOnResultActions = action.results?.filter(result => result.type === resultType) || [];\n    const skipOnEmpty = action.results?.some(result => result.type === ResultType.SKIP_EMIT_ON_EMPTY_RESULT);\n\n    if (result == null && skipOnEmpty) {\n      return;\n    }\n\n    let onResultActions = allOnResultActions;\n    if (onResultActions.some(action => action.options.errorType)) {\n      const firstFittingAction = allOnResultActions.find(\n        action => action.options.errorType && result instanceof (action.options.errorType as Function)\n      );\n\n      if (!firstFittingAction) {\n        onResultActions = allOnResultActions.filter(action => !action.options.errorType);\n      } else {\n        onResultActions = [firstFittingAction];\n      }\n    }\n\n    for (const onResultAction of onResultActions) {\n      const transformedValue =\n        result instanceof Error\n          ? result.message\n          : this.transformActionValue(result as never, null, onResultAction.options, 'result');\n      socket.emit(onResultAction.options.messageName as never, transformedValue);\n    }\n  }\n\n  private registerMiddleware(namespace: Namespace, middleware: HandlerMetadata<MiddlewareMetadata>) {\n    namespace.use((socket: Socket, next: (err?: any) => void) => {\n      const instance: MiddlewareInterface = this.container.get(middleware.target);\n      instance.use(socket, next);\n    });\n  }\n\n  private resolveParameters(\n    socket: Socket,\n    controllerMetadata: ControllerMetadata,\n    parameterMetadatas: ParameterMetadata[],\n    data?: any[],\n    ack?: Function | null\n  ) {\n    const parameters = [];\n\n    for (const metadata of parameterMetadatas) {\n      const parameterValue = this.resolveParameter(socket, controllerMetadata, metadata, data, ack) as never;\n      parameters[metadata.index] = this.transformActionValue(\n        parameterValue,\n        metadata.reflectedType as never,\n        metadata.options,\n        'parameter'\n      );\n    }\n\n    return parameters;\n  }\n\n  private resolveParameter(\n    socket: Socket,\n    controller: ControllerMetadata,\n    parameter: ParameterMetadata,\n    data?: any[],\n    ack?: Function | null\n  ) {\n    switch (parameter.type) {\n      case ParameterType.CONNECTED_SOCKET:\n        return socket;\n      case ParameterType.SOCKET_ID:\n        return socket.id;\n      case ParameterType.SOCKET_IO:\n        return this.io;\n      case ParameterType.SOCKET_ROOMS:\n        return socket.rooms;\n      case ParameterType.MESSAGE_BODY:\n        return data?.[(parameter.options.index as number) || 0];\n      case ParameterType.MESSAGE_ACK:\n        return ack;\n      case ParameterType.SOCKET_QUERY_PARAM:\n        return socket.handshake.query[parameter.options.name as string];\n      case ParameterType.SOCKET_REQUEST:\n        return socket.request;\n      case ParameterType.NAMESPACE_PARAMS:\n        return this.extractNamespaceParameters(socket, controller.namespace, parameter);\n      case ParameterType.NAMESPACE_PARAM:\n        return this.extractNamespaceParameters(socket, controller.namespace, parameter)?.[\n          parameter.options.name as string\n        ];\n    }\n  }\n\n  private transformActionValue(\n    value: never,\n    reflectedType: unknown,\n    options: ActionTransformOptions,\n    transformType: 'parameter' | 'result'\n  ) {\n    const transformOptions: TransformOptions = {\n      transform: options.transform ?? this.transformOptions.transform,\n      parameterTransformOptions: options.transformOptions ?? this.transformOptions.parameterTransformOptions,\n      resultTransformOptions: options.transformOptions ?? this.transformOptions.resultTransformOptions,\n    };\n\n    if (!transformOptions.transform) {\n      return value;\n    }\n\n    if (typeof value !== 'object' || Array.isArray(value) || value == null) {\n      return value;\n    }\n\n    if (transformType === 'parameter') {\n      return plainToInstance(reflectedType as never, value, transformOptions.parameterTransformOptions);\n    }\n\n    if (transformType === 'result') {\n      return instanceToPlain(value, transformOptions.resultTransformOptions);\n    }\n\n    return value;\n  }\n\n  private resolveEventContext(\n    socket: Socket,\n    eventType: SocketEventType,\n    eventName?: string,\n    messageBody?: any[],\n    namespace?: string | RegExp,\n    ack?: Function | null\n  ): SocketEventContext {\n    return {\n      eventType,\n      eventName,\n      socket,\n      socketIo: this.io,\n      nspParams: this.extractNamespaceParameters(socket, namespace),\n      messageArgs: messageBody,\n      ack,\n    };\n  }\n\n  private extractNamespaceParameters(\n    socket: Socket,\n    namespace: string | RegExp | undefined,\n    parameterMetadata?: ParameterMetadata\n  ) {\n    let keys: any[];\n    let regexp: RegExp;\n\n    if (namespace instanceof RegExp) {\n      regexp = namespace;\n      keys = [];\n    } else {\n      const pathToRegexpResult = pathToRegexp(namespace || '/');\n      regexp = pathToRegexpResult.regexp;\n      keys = pathToRegexpResult.keys;\n    }\n\n    const parts: any[] = regexp.exec(socket.nsp.name) || [];\n    const params: Record<string, string> = {};\n    keys.forEach((key: any, index: number) => {\n      params[key.name as string] = parameterMetadata?.options?.transform\n        ? this.transformActionValue(\n            parts[index + 1] as never,\n            parameterMetadata.reflectedType,\n            parameterMetadata.options,\n            'parameter'\n          )\n        : parts[index + 1];\n    });\n    return params;\n  }\n}\n"
  },
  {
    "path": "src/decorators/ConnectedSocket.ts",
    "content": "import { addParameterToActionMetadata } from '../util/add-parameter-to-action-metadata';\nimport { ParameterType } from '../types/enums/ParameterType';\n\nexport function ConnectedSocket() {\n  return function (object: Object, methodName: string, index: number) {\n    const format = (Reflect as any).getMetadata('design:paramtypes', object, methodName)[index];\n\n    addParameterToActionMetadata(object.constructor, methodName, {\n      index,\n      reflectedType: format,\n      type: ParameterType.CONNECTED_SOCKET,\n      options: {\n        transform: false,\n      },\n    });\n  };\n}\n"
  },
  {
    "path": "src/decorators/EmitOnFail.ts",
    "content": "import { addResultToActionMetadata } from '../util/add-result-to-action-metadata';\nimport { ResultType } from '../types/enums/ResultType';\nimport { ActionTransformOptions } from '../types/ActionTransformOptions';\n\nexport function EmitOnFail(messageName: string, options?: ActionTransformOptions & { errorType: unknown }): Function {\n  return function (object: Object, methodName: string) {\n    addResultToActionMetadata(object.constructor, methodName, {\n      type: ResultType.EMIT_ON_FAIL,\n      options: {\n        messageName,\n        ...options,\n      },\n    });\n  };\n}\n"
  },
  {
    "path": "src/decorators/EmitOnSuccess.ts",
    "content": "import { addResultToActionMetadata } from '../util/add-result-to-action-metadata';\nimport { ResultType } from '../types/enums/ResultType';\nimport { ActionTransformOptions } from '../types/ActionTransformOptions';\n\nexport function EmitOnSuccess(messageName: string, options?: ActionTransformOptions): Function {\n  return function (object: Object, methodName: string) {\n    addResultToActionMetadata(object.constructor, methodName, {\n      type: ResultType.EMIT_ON_SUCCESS,\n      options: {\n        messageName,\n        ...options,\n      },\n    });\n  };\n}\n"
  },
  {
    "path": "src/decorators/MessageAck.ts",
    "content": "import { addParameterToActionMetadata } from '../util/add-parameter-to-action-metadata';\nimport { ParameterType } from '../types/enums/ParameterType';\n\nexport function MessageAck() {\n  return function (object: Object, methodName: string, index: number) {\n    const format = (Reflect as any).getMetadata('design:paramtypes', object, methodName)[index];\n    addParameterToActionMetadata(object.constructor, methodName, {\n      index,\n      reflectedType: format,\n      type: ParameterType.MESSAGE_ACK,\n      options: {},\n    });\n  };\n}\n"
  },
  {
    "path": "src/decorators/MessageBody.ts",
    "content": "import { addParameterToActionMetadata } from '../util/add-parameter-to-action-metadata';\nimport { ParameterType } from '../types/enums/ParameterType';\nimport { ActionTransformOptions } from '../types/ActionTransformOptions';\n\nexport function MessageBody(options?: ActionTransformOptions & { index?: number }) {\n  return function (object: Object, methodName: string, index: number) {\n    const format = (Reflect as any).getMetadata('design:paramtypes', object, methodName)[index];\n    addParameterToActionMetadata(object.constructor, methodName, {\n      index,\n      reflectedType: format,\n      type: ParameterType.MESSAGE_BODY,\n      options: {\n        ...options,\n      },\n    });\n  };\n}\n"
  },
  {
    "path": "src/decorators/Middleware.ts",
    "content": "import { HandlerType } from '../types/enums/HandlerType';\nimport { addMiddlewareMetadata } from '../util/add-middleware-metadata';\n\nexport function Middleware(options?: {\n  priority?: number;\n  namespace?: string | RegExp | Array<RegExp | string>;\n}): Function {\n  return function (object: Function) {\n    addMiddlewareMetadata(object, {\n      type: HandlerType.MIDDLEWARE,\n      namespace: options?.namespace,\n      priority: options?.priority,\n    });\n  };\n}\n"
  },
  {
    "path": "src/decorators/NspParam.ts",
    "content": "import { addParameterToActionMetadata } from '../util/add-parameter-to-action-metadata';\nimport { ParameterType } from '../types/enums/ParameterType';\n\nexport function NspParam(name: string) {\n  return function (object: Object, methodName: string, index: number) {\n    const format = (Reflect as any).getMetadata('design:paramtypes', object, methodName)[index];\n\n    addParameterToActionMetadata(object.constructor, methodName, {\n      index,\n      reflectedType: format,\n      type: ParameterType.NAMESPACE_PARAM,\n      options: { name, transform: false },\n    });\n  };\n}\n"
  },
  {
    "path": "src/decorators/NspParams.ts",
    "content": "import { addParameterToActionMetadata } from '../util/add-parameter-to-action-metadata';\nimport { ParameterType } from '../types/enums/ParameterType';\n\nexport function NspParams() {\n  return function (object: Object, methodName: string, index: number) {\n    const format = (Reflect as any).getMetadata('design:paramtypes', object, methodName)[index];\n\n    addParameterToActionMetadata(object.constructor, methodName, {\n      index,\n      reflectedType: format,\n      type: ParameterType.NAMESPACE_PARAMS,\n      options: { transform: false },\n    });\n  };\n}\n"
  },
  {
    "path": "src/decorators/OnConnect.ts",
    "content": "import { addActionToControllerMetadata } from '../util/add-action-to-controller-metadata';\nimport { SocketEventType } from '../types/enums/SocketEventType';\n\nexport function OnConnect(): Function {\n  return function (object: Object, methodName: string) {\n    addActionToControllerMetadata(object.constructor, {\n      methodName,\n      type: SocketEventType.CONNECT,\n      options: {},\n    });\n  };\n}\n"
  },
  {
    "path": "src/decorators/OnDisconnect.ts",
    "content": "import { addActionToControllerMetadata } from '../util/add-action-to-controller-metadata';\nimport { SocketEventType } from '../types/enums/SocketEventType';\n\nexport function OnDisconnect(): Function {\n  return function (object: Object, methodName: string) {\n    addActionToControllerMetadata(object.constructor, {\n      methodName,\n      type: SocketEventType.DISCONNECT,\n      options: {},\n    });\n  };\n}\n"
  },
  {
    "path": "src/decorators/OnDisconnecting.ts",
    "content": "import { addActionToControllerMetadata } from '../util/add-action-to-controller-metadata';\nimport { SocketEventType } from '../types/enums/SocketEventType';\n\nexport function OnDisconnecting(): Function {\n  return function (object: Object, methodName: string) {\n    addActionToControllerMetadata(object.constructor, {\n      methodName,\n      type: SocketEventType.DISCONNECTING,\n      options: {},\n    });\n  };\n}\n"
  },
  {
    "path": "src/decorators/OnMessage.ts",
    "content": "import { addActionToControllerMetadata } from '../util/add-action-to-controller-metadata';\nimport { SocketEventType } from '../types/enums/SocketEventType';\n\nexport function OnMessage(name?: string): Function {\n  return function (object: Object, methodName: string) {\n    addActionToControllerMetadata(object.constructor, {\n      methodName,\n      type: SocketEventType.MESSAGE,\n      options: { name },\n    });\n  };\n}\n"
  },
  {
    "path": "src/decorators/SkipEmitOnEmptyResult.ts",
    "content": "import { addResultToActionMetadata } from '../util/add-result-to-action-metadata';\nimport { ResultType } from '../types/enums/ResultType';\n\nexport function SkipEmitOnEmptyResult(): Function {\n  return function (object: Object, methodName: string) {\n    addResultToActionMetadata(object.constructor, methodName, {\n      type: ResultType.SKIP_EMIT_ON_EMPTY_RESULT,\n      options: {},\n    });\n  };\n}\n"
  },
  {
    "path": "src/decorators/SocketController.ts",
    "content": "import { HandlerType } from '../types/enums/HandlerType';\nimport { addControllerMetadata } from '../util/add-controller-metadata';\n\nexport function SocketController(namespace?: string | RegExp) {\n  return function (object: Function) {\n    addControllerMetadata(object, { namespace, type: HandlerType.CONTROLLER });\n  };\n}\n"
  },
  {
    "path": "src/decorators/SocketIO.ts",
    "content": "import { addParameterToActionMetadata } from '../util/add-parameter-to-action-metadata';\nimport { ParameterType } from '../types/enums/ParameterType';\n\nexport function SocketIO() {\n  return function (object: Object, methodName: string, index: number) {\n    const format = (Reflect as any).getMetadata('design:paramtypes', object, methodName)[index];\n\n    addParameterToActionMetadata(object.constructor, methodName, {\n      index,\n      reflectedType: format,\n      type: ParameterType.SOCKET_IO,\n      options: { transform: false },\n    });\n  };\n}\n"
  },
  {
    "path": "src/decorators/SocketId.ts",
    "content": "import { addParameterToActionMetadata } from '../util/add-parameter-to-action-metadata';\nimport { ParameterType } from '../types/enums/ParameterType';\n\nexport function SocketId() {\n  return function (object: Object, methodName: string, index: number) {\n    const format = (Reflect as any).getMetadata('design:paramtypes', object, methodName)[index];\n\n    addParameterToActionMetadata(object.constructor, methodName, {\n      index,\n      reflectedType: format,\n      type: ParameterType.SOCKET_ID,\n      options: { transform: false },\n    });\n  };\n}\n"
  },
  {
    "path": "src/decorators/SocketQueryParam.ts",
    "content": "import { addParameterToActionMetadata } from '../util/add-parameter-to-action-metadata';\nimport { ParameterType } from '../types/enums/ParameterType';\n\nexport function SocketQueryParam(name?: string) {\n  return function (object: Object, methodName: string, index: number) {\n    const format = (Reflect as any).getMetadata('design:paramtypes', object, methodName)[index];\n\n    addParameterToActionMetadata(object.constructor, methodName, {\n      index,\n      reflectedType: format,\n      type: ParameterType.SOCKET_QUERY_PARAM,\n      options: { name, transform: false },\n    });\n  };\n}\n"
  },
  {
    "path": "src/decorators/SocketRequest.ts",
    "content": "import { addParameterToActionMetadata } from '../util/add-parameter-to-action-metadata';\nimport { ParameterType } from '../types/enums/ParameterType';\n\nexport function SocketRequest() {\n  return function (object: Object, methodName: string, index: number) {\n    const format = (Reflect as any).getMetadata('design:paramtypes', object, methodName)[index];\n\n    addParameterToActionMetadata(object.constructor, methodName, {\n      index,\n      reflectedType: format,\n      type: ParameterType.SOCKET_REQUEST,\n      options: { transform: false },\n    });\n  };\n}\n"
  },
  {
    "path": "src/decorators/SocketRooms.ts",
    "content": "import { addParameterToActionMetadata } from '../util/add-parameter-to-action-metadata';\nimport { ParameterType } from '../types/enums/ParameterType';\n\nexport function SocketRooms() {\n  return function (object: Object, methodName: string, index: number) {\n    const format = (Reflect as any).getMetadata('design:paramtypes', object, methodName)[index];\n\n    addParameterToActionMetadata(object.constructor, methodName, {\n      index,\n      reflectedType: format,\n      type: ParameterType.SOCKET_ROOMS,\n      options: { transform: false },\n    });\n  };\n}\n"
  },
  {
    "path": "src/decorators/UseInterceptor.ts",
    "content": "import { addInterceptorToActionMetadata } from '../util/add-interceptor-to-action-metadata';\nimport { getMetadata } from '../util/get-metadata';\nimport { ControllerMetadata } from '../types/ControllerMetadata';\n\nexport function UseInterceptor(...interceptors: any[]): Function {\n  return function (object: Function | Object, methodName?: string) {\n    for (const interceptor of interceptors) {\n      if (object instanceof Function) {\n        // Class interceptor\n        const existingMetadata: ControllerMetadata = getMetadata(object);\n        for (const key of Object.keys(existingMetadata?.actions || {})) {\n          addInterceptorToActionMetadata(object, key, interceptor as Function);\n        }\n      } else {\n        // Method interceptor\n        addInterceptorToActionMetadata(object.constructor, methodName as string, interceptor as Function);\n      }\n    }\n  };\n}\n"
  },
  {
    "path": "src/index.ts",
    "content": "export * from './decorators/ConnectedSocket';\nexport * from './decorators/EmitOnFail';\nexport * from './decorators/EmitOnSuccess';\nexport * from './decorators/MessageBody';\nexport * from './decorators/MessageAck';\nexport * from './decorators/Middleware';\nexport * from './decorators/NspParam';\nexport * from './decorators/NspParams';\nexport * from './decorators/OnConnect';\nexport * from './decorators/OnDisconnect';\nexport * from './decorators/OnDisconnecting';\nexport * from './decorators/OnMessage';\nexport * from './decorators/SkipEmitOnEmptyResult';\nexport * from './decorators/SocketController';\nexport * from './decorators/SocketId';\nexport * from './decorators/SocketIO';\nexport * from './decorators/SocketQueryParam';\nexport * from './decorators/SocketRequest';\nexport * from './decorators/SocketRooms';\n\nexport * from './types/MiddlewareInterface';\nexport * from './types/InterceptorInterface';\nexport * from './types/TransformOptions';\nexport * from './types/SocketControllersOptions';\nexport * from './types/enums/SocketEventType';\nexport * from './types/SocketEventContext';\n\nexport * from './SocketControllers';\n"
  },
  {
    "path": "src/types/ActionMetadata.ts",
    "content": "import { ParameterMetadata } from './ParameterMetadata';\nimport { ResultMetadata } from './ResultMetadata';\nimport { SocketEventType } from './enums/SocketEventType';\n\nexport interface ActionMetadata {\n  type: SocketEventType;\n  methodName: string;\n  options: any;\n  parameters: ParameterMetadata[];\n  results: ResultMetadata[];\n  interceptors: Function[];\n}\n"
  },
  {
    "path": "src/types/ActionTransformOptions.ts",
    "content": "import { ClassTransformOptions } from 'class-transformer';\n\nexport interface ActionTransformOptions {\n  transform?: boolean;\n  transformOptions?: ClassTransformOptions;\n}\n"
  },
  {
    "path": "src/types/ControllerMetadata.ts",
    "content": "import { HandlerType } from './enums/HandlerType';\nimport { ActionMetadata } from './ActionMetadata';\n\nexport interface ControllerMetadata {\n  namespace?: string | RegExp;\n  type: HandlerType.CONTROLLER;\n  actions: {\n    [methodName: string]: ActionMetadata;\n  };\n}\n"
  },
  {
    "path": "src/types/HandlerMetadata.ts",
    "content": "export interface HandlerMetadata<T> {\n  metadata: T;\n  target: Function;\n}\n"
  },
  {
    "path": "src/types/InterceptorInterface.ts",
    "content": "import { SocketEventContext } from './SocketEventContext';\n\nexport interface InterceptorInterface {\n  use(context: SocketEventContext, next: () => any): any;\n}\n"
  },
  {
    "path": "src/types/MiddlewareInterface.ts",
    "content": "import { Socket } from 'socket.io';\n\nexport interface MiddlewareInterface {\n  use(socket: Socket, next: (err?: any) => any): any;\n}\n"
  },
  {
    "path": "src/types/MiddlewareMetadata.ts",
    "content": "import { HandlerType } from './enums/HandlerType';\n\nexport interface MiddlewareMetadata {\n  namespace?: string | RegExp | Array<RegExp | string>;\n  priority?: number;\n  type: HandlerType.MIDDLEWARE;\n}\n"
  },
  {
    "path": "src/types/ParameterMetadata.ts",
    "content": "import { ParameterType } from './enums/ParameterType';\nimport { ActionTransformOptions } from './ActionTransformOptions';\n\nexport interface ParameterMetadata {\n  type: ParameterType;\n  index: number;\n  reflectedType: any;\n  options: ActionTransformOptions & Record<string, unknown>;\n}\n"
  },
  {
    "path": "src/types/ResultMetadata.ts",
    "content": "import { ResultType } from './enums/ResultType';\nimport { ActionTransformOptions } from './ActionTransformOptions';\n\nexport interface ResultMetadata {\n  type: ResultType;\n  options: ActionTransformOptions & Record<string, unknown>;\n}\n"
  },
  {
    "path": "src/types/SocketControllerMetaKey.ts",
    "content": "export const SOCKET_CONTROLLER_META_KEY = Symbol('SocketControllerMetaKey');\n"
  },
  {
    "path": "src/types/SocketControllersOptions.ts",
    "content": "import { Server } from 'socket.io';\nimport { TransformOptions } from './TransformOptions';\nimport { SocketEventContext } from './SocketEventContext';\n\nexport interface SocketControllersOptions {\n  container: { get<T>(someClass: { new (...args: any[]): T } | Function): T };\n\n  scopedContainerGetter?: (context: SocketEventContext) => {\n    get<T>(someClass: { new (...args: any[]): T } | Function): T;\n  };\n\n  scopedContainerDisposer?: (container: { get<T>(someClass: { new (...args: any[]): T } | Function): T }) => void;\n\n  io?: Server;\n\n  port?: number;\n\n  controllers?: Function[] | string[];\n\n  middlewares?: Function[] | string[];\n\n  transformOption?: Partial<TransformOptions>;\n}\n"
  },
  {
    "path": "src/types/SocketEventContext.ts",
    "content": "import { SocketEventType } from './enums/SocketEventType';\nimport { Server, Socket } from 'socket.io';\n\nexport interface SocketEventContext {\n  socketIo: Server;\n  socket: Socket;\n  eventType: SocketEventType;\n  eventName?: string;\n  messageArgs?: any[];\n  nspParams?: Record<string, string>;\n  ack?: Function | null;\n}\n"
  },
  {
    "path": "src/types/TransformOptions.ts",
    "content": "import { ClassTransformOptions } from 'class-transformer';\n\nexport interface TransformOptions {\n  transform?: boolean;\n  parameterTransformOptions?: ClassTransformOptions;\n  resultTransformOptions?: ClassTransformOptions;\n}\n"
  },
  {
    "path": "src/types/constants/defaultTransformOptions.ts",
    "content": "import { TransformOptions } from '../TransformOptions';\n\nexport const defaultTransformOptions: TransformOptions = {\n  transform: true,\n};\n"
  },
  {
    "path": "src/types/enums/HandlerType.ts",
    "content": "export enum HandlerType {\n  CONTROLLER,\n  MIDDLEWARE,\n}\n"
  },
  {
    "path": "src/types/enums/ParameterType.ts",
    "content": "export enum ParameterType {\n  CUSTOM,\n  CONNECTED_SOCKET,\n  MESSAGE_BODY,\n  MESSAGE_ACK,\n  SOCKET_QUERY_PARAM,\n  SOCKET_IO,\n  SOCKET_ID,\n  SOCKET_REQUEST,\n  SOCKET_ROOMS,\n  NAMESPACE_PARAMS,\n  NAMESPACE_PARAM,\n}\n"
  },
  {
    "path": "src/types/enums/ResultType.ts",
    "content": "export enum ResultType {\n  EMIT_ON_SUCCESS,\n  EMIT_ON_FAIL,\n  SKIP_EMIT_ON_EMPTY_RESULT,\n}\n"
  },
  {
    "path": "src/types/enums/SocketEventType.ts",
    "content": "export enum SocketEventType {\n  MESSAGE,\n  CONNECT,\n  DISCONNECT,\n  DISCONNECTING,\n}\n"
  },
  {
    "path": "src/util/add-action-to-controller-metadata.ts",
    "content": "import { SOCKET_CONTROLLER_META_KEY } from '../types/SocketControllerMetaKey';\nimport { ActionMetadata } from '../types/ActionMetadata';\nimport { getMetadata } from './get-metadata';\nimport { ControllerMetadata } from '../types/ControllerMetadata';\n\nexport const addActionToControllerMetadata = (\n  target: Function,\n  actionMetadata: Pick<ActionMetadata, 'type' | 'methodName' | 'options'>\n) => {\n  const existingMetadata = getMetadata<any, ControllerMetadata>(target);\n  (Reflect as any).defineMetadata(\n    SOCKET_CONTROLLER_META_KEY,\n    {\n      ...existingMetadata,\n      actions: {\n        ...existingMetadata?.actions,\n        [actionMetadata.methodName]: {\n          ...existingMetadata?.actions?.[actionMetadata.methodName],\n          ...actionMetadata,\n        },\n      },\n    },\n    target\n  );\n};\n"
  },
  {
    "path": "src/util/add-controller-metadata.ts",
    "content": "import { SOCKET_CONTROLLER_META_KEY } from '../types/SocketControllerMetaKey';\nimport { ControllerMetadata } from '../types/ControllerMetadata';\nimport { getMetadata } from './get-metadata';\n\nexport const addControllerMetadata = (target: Function, metadata: Pick<ControllerMetadata, 'namespace' | 'type'>) => {\n  const existingMetadata = getMetadata<any, ControllerMetadata>(target);\n  (Reflect as any).defineMetadata(\n    SOCKET_CONTROLLER_META_KEY,\n    {\n      ...existingMetadata,\n      ...metadata,\n    },\n    target\n  );\n};\n"
  },
  {
    "path": "src/util/add-interceptor-to-action-metadata.ts",
    "content": "import { SOCKET_CONTROLLER_META_KEY } from '../types/SocketControllerMetaKey';\nimport { getMetadata } from './get-metadata';\nimport { ControllerMetadata } from '../types/ControllerMetadata';\n\nexport const addInterceptorToActionMetadata = (target: Function, methodName: string, interceptor: Function) => {\n  const existingMetadata = getMetadata<any, ControllerMetadata>(target);\n  (Reflect as any).defineMetadata(\n    SOCKET_CONTROLLER_META_KEY,\n    {\n      ...existingMetadata,\n      actions: {\n        ...existingMetadata?.actions,\n        [methodName]: {\n          ...existingMetadata?.actions?.[methodName],\n          interceptors: [interceptor, ...(existingMetadata?.actions?.[methodName]?.interceptors || [])],\n        },\n      },\n    },\n    target\n  );\n};\n"
  },
  {
    "path": "src/util/add-middleware-metadata.ts",
    "content": "import { SOCKET_CONTROLLER_META_KEY } from '../types/SocketControllerMetaKey';\nimport { MiddlewareMetadata } from '../types/MiddlewareMetadata';\n\nexport const addMiddlewareMetadata = (target: Function, metadata: MiddlewareMetadata) => {\n  (Reflect as any).defineMetadata(SOCKET_CONTROLLER_META_KEY, metadata, target);\n};\n"
  },
  {
    "path": "src/util/add-parameter-to-action-metadata.ts",
    "content": "import { SOCKET_CONTROLLER_META_KEY } from '../types/SocketControllerMetaKey';\nimport { ParameterMetadata } from '../types/ParameterMetadata';\nimport { getMetadata } from './get-metadata';\nimport { ControllerMetadata } from '../types/ControllerMetadata';\n\nexport const addParameterToActionMetadata = (target: Function, methodName: string, args: ParameterMetadata) => {\n  const existingMetadata = getMetadata<any, ControllerMetadata>(target);\n  (Reflect as any).defineMetadata(\n    SOCKET_CONTROLLER_META_KEY,\n    {\n      ...existingMetadata,\n      actions: {\n        ...existingMetadata?.actions,\n        [methodName]: {\n          ...existingMetadata?.actions?.[methodName],\n          parameters: [args, ...(existingMetadata?.actions?.[methodName]?.parameters || [])],\n        },\n      },\n    },\n    target\n  );\n};\n"
  },
  {
    "path": "src/util/add-result-to-action-metadata.ts",
    "content": "import { SOCKET_CONTROLLER_META_KEY } from '../types/SocketControllerMetaKey';\nimport { ResultMetadata } from '../types/ResultMetadata';\nimport { getMetadata } from './get-metadata';\nimport { ControllerMetadata } from '../types/ControllerMetadata';\n\nexport const addResultToActionMetadata = (target: Function, methodName: string, args: ResultMetadata) => {\n  const existingMetadata = getMetadata<any, ControllerMetadata>(target);\n  (Reflect as any).defineMetadata(\n    SOCKET_CONTROLLER_META_KEY,\n    {\n      ...existingMetadata,\n      actions: {\n        ...existingMetadata?.actions,\n        [methodName]: {\n          ...existingMetadata?.actions?.[methodName],\n          results: [args, ...(existingMetadata?.actions?.[methodName]?.results || [])],\n        },\n      },\n    },\n    target\n  );\n};\n"
  },
  {
    "path": "src/util/chain-execute.ts",
    "content": "export function chainExecute(context: any, chain: Function[]) {\n  function next() {\n    const middleware: Function = chain.shift() as Function;\n\n    if (middleware && typeof middleware === 'function') {\n      return middleware(context, next);\n    }\n  }\n\n  return next();\n}\n"
  },
  {
    "path": "src/util/get-metadata.ts",
    "content": "import { SOCKET_CONTROLLER_META_KEY } from '../types/SocketControllerMetaKey';\n\nexport const getMetadata = <T extends Object, U>(target: T): U => {\n  return (Reflect as any).getMetadata(SOCKET_CONTROLLER_META_KEY, target);\n};\n"
  },
  {
    "path": "test/functional/connected-socket.spec.ts",
    "content": "import { createServer, Server as HttpServer } from 'http';\nimport { Server } from 'socket.io';\nimport { io, Socket } from 'socket.io-client';\nimport { SocketControllers } from '../../src/SocketControllers';\nimport { Container, Service } from 'typedi';\nimport { SocketController } from '../../src/decorators/SocketController';\nimport { OnConnect } from '../../src/decorators/OnConnect';\nimport { ConnectedSocket } from '../../src/decorators/ConnectedSocket';\nimport { waitForEvent } from '../utilities/waitForEvent';\n\ndescribe('ConnectedSocket', () => {\n  const PORT = 8080;\n  const PATH_FOR_CLIENT = `ws://localhost:${PORT}`;\n\n  let httpServer: HttpServer;\n  let wsApp: Server;\n  let wsClient: Socket;\n  let testResult;\n  let socketControllers: SocketControllers;\n\n  beforeEach(done => {\n    httpServer = createServer();\n    wsApp = new Server(httpServer, {\n      cors: {\n        origin: '*',\n      },\n    });\n    httpServer.listen(PORT, () => {\n      done();\n    });\n  });\n\n  afterEach(() => {\n    testResult = undefined;\n\n    Container.reset();\n    wsClient.close();\n    wsClient = null;\n    socketControllers = null;\n    return new Promise(resolve => {\n      if (wsApp)\n        return wsApp.close(() => {\n          resolve(null);\n        });\n      resolve(null);\n    });\n  });\n\n  it('Connected socket is retrieved correctly', async () => {\n    @SocketController('/string')\n    @Service()\n    class TestController {\n      @OnConnect()\n      connected(@ConnectedSocket() socket: Socket) {\n        testResult = socket.id;\n        socket.emit('connected');\n      }\n    }\n\n    socketControllers = new SocketControllers({\n      io: wsApp,\n      container: Container,\n      controllers: [TestController],\n    });\n    wsClient = io(PATH_FOR_CLIENT + '/string', { reconnection: false, timeout: 5000, forceNew: true });\n\n    await waitForEvent(wsClient, 'connected');\n    expect(wsClient.id).toEqual(testResult);\n  });\n});\n"
  },
  {
    "path": "test/functional/controllers/test.controller.ts",
    "content": "import { ConnectedSocket, OnConnect, SocketController } from '../../../src';\nimport { Service } from 'typedi';\n\n@SocketController()\n@Service()\nexport class TestController {\n  @OnConnect()\n  connected(@ConnectedSocket() socket: any) {\n    socket.emit('connected');\n  }\n}\n"
  },
  {
    "path": "test/functional/controllers/test2.controller.ts",
    "content": "import { ConnectedSocket, OnMessage, SocketController } from '../../../src';\nimport { Service } from 'typedi';\n\n@SocketController()\n@Service()\nexport class Test2Controller {\n  @OnMessage('test')\n  connected(@ConnectedSocket() socket: any) {\n    socket.emit('response');\n  }\n}\n"
  },
  {
    "path": "test/functional/create-socket-server.spec.ts",
    "content": "import { SocketController, SocketControllers } from '../../src';\nimport { testConnection } from '../utilities/testSocketConnection';\nimport { Container, Service } from 'typedi';\n\ndescribe('Create socket server', () => {\n  let wsApp: SocketControllers;\n  const PORT = 8080;\n  const PATH_FOR_CLIENT = `ws://localhost:${PORT}`;\n\n  afterEach(() => {\n    return new Promise(resolve => {\n      if (wsApp)\n        return wsApp.io.close(() => {\n          resolve(null);\n        });\n      resolve(null);\n    });\n  });\n\n  it('should create socket server without options', async () => {\n    expect.assertions(1);\n    wsApp = new SocketControllers({ port: PORT, container: Container });\n    expect(await testConnection(PATH_FOR_CLIENT)).toEqual(0);\n  });\n\n  it('should create socket server with empty controllers array in options', async () => {\n    expect.assertions(1);\n    wsApp = new SocketControllers({ port: PORT, controllers: [], container: Container });\n    expect(await testConnection(PATH_FOR_CLIENT)).toEqual(0);\n  });\n\n  it('should create socket server with controllers array in options', async () => {\n    expect.assertions(1);\n\n    @SocketController()\n    @Service()\n    class TestController {}\n\n    wsApp = new SocketControllers({ port: PORT, container: Container, controllers: [TestController] });\n    expect(await testConnection(PATH_FOR_CLIENT)).toEqual(0);\n  });\n});\n"
  },
  {
    "path": "test/functional/emit-on-fail.spec.ts",
    "content": "import { createServer, Server as HttpServer } from 'http';\nimport { Server } from 'socket.io';\nimport { io, Socket } from 'socket.io-client';\nimport { SocketControllers } from '../../src/SocketControllers';\nimport { Container, Service } from 'typedi';\nimport { SocketController } from '../../src/decorators/SocketController';\nimport { OnConnect } from '../../src/decorators/OnConnect';\nimport { ConnectedSocket } from '../../src/decorators/ConnectedSocket';\nimport { waitForEvent } from '../utilities/waitForEvent';\nimport { EmitOnFail, OnMessage } from '../../src';\nimport { waitForTime } from '../utilities/waitForTime';\n\ndescribe('EmitOnFail', () => {\n  const PORT = 8080;\n  const PATH_FOR_CLIENT = `ws://localhost:${PORT}`;\n\n  let httpServer: HttpServer;\n  let wsApp: Server;\n  let wsClient: Socket;\n  let testResult;\n  let socketControllers: SocketControllers;\n\n  beforeEach(done => {\n    httpServer = createServer();\n    wsApp = new Server(httpServer, {\n      cors: {\n        origin: '*',\n      },\n    });\n    httpServer.listen(PORT, () => {\n      done();\n    });\n  });\n\n  afterEach(() => {\n    testResult = undefined;\n\n    Container.reset();\n    wsClient.close();\n    wsClient = null;\n    socketControllers = null;\n    return new Promise(resolve => {\n      if (wsApp)\n        return wsApp.close(() => {\n          resolve(null);\n        });\n      resolve(null);\n    });\n  });\n\n  it('Emit defined event on failing sync execution', async () => {\n    @SocketController('/string')\n    @Service()\n    class TestController {\n      @OnConnect()\n      @EmitOnFail('fail')\n      connected(@ConnectedSocket() socket: Socket) {\n        socket.emit('connected');\n      }\n\n      @OnMessage('request')\n      @EmitOnFail('fail')\n      testEvent() {\n        throw new Error('error string');\n      }\n    }\n\n    socketControllers = new SocketControllers({\n      io: wsApp,\n      container: Container,\n      controllers: [TestController],\n    });\n    wsClient = io(PATH_FOR_CLIENT + '/string', { reconnection: false, timeout: 5000, forceNew: true });\n\n    const errors = [];\n\n    wsClient.on('fail', data => {\n      errors.push(data);\n    });\n\n    await waitForEvent(wsClient, 'connected');\n    expect(errors.length).toEqual(0);\n\n    wsClient.emit('request');\n    await waitForEvent(wsClient, 'fail');\n    expect(errors[0]).toEqual('error string');\n    expect(errors.length).toEqual(1);\n  });\n\n  it('Emit defined event on failing async execution', async () => {\n    @SocketController('/string')\n    @Service()\n    class TestController {\n      @OnConnect()\n      @EmitOnFail('fail')\n      connected(@ConnectedSocket() socket: Socket) {\n        socket.emit('connected');\n      }\n\n      @OnMessage('request')\n      @EmitOnFail('fail')\n      async testEvent() {\n        throw new Error('error string');\n      }\n    }\n\n    socketControllers = new SocketControllers({\n      io: wsApp,\n      container: Container,\n      controllers: [TestController],\n    });\n    wsClient = io(PATH_FOR_CLIENT + '/string', { reconnection: false, timeout: 5000, forceNew: true });\n\n    const errors = [];\n\n    wsClient.on('fail', data => {\n      errors.push(data);\n    });\n\n    await waitForEvent(wsClient, 'connected');\n    expect(errors.length).toEqual(0);\n\n    wsClient.emit('request');\n    await waitForEvent(wsClient, 'fail');\n    expect(errors[0]).toEqual('error string');\n    expect(errors.length).toEqual(1);\n  });\n\n  it('Emit defined event on failing with specific error type', async () => {\n    @SocketController('/string')\n    @Service()\n    class TestController {\n      @OnConnect()\n      connected(@ConnectedSocket() socket: Socket) {\n        socket.emit('connected');\n      }\n\n      @OnMessage('request')\n      @EmitOnFail('fail1', { errorType: RangeError })\n      @EmitOnFail('fail2', { errorType: TypeError })\n      @EmitOnFail('fail3')\n      async testEvent() {\n        throw new RangeError('range error');\n      }\n\n      @OnMessage('request2')\n      @EmitOnFail('fail1', { errorType: RangeError })\n      @EmitOnFail('fail2', { errorType: TypeError })\n      @EmitOnFail('fail3')\n      async testEvent2() {\n        throw new TypeError('type error');\n      }\n\n      @OnMessage('request3')\n      @EmitOnFail('fail1', { errorType: RangeError })\n      @EmitOnFail('fail2', { errorType: TypeError })\n      @EmitOnFail('fail3')\n      async testEvent3() {\n        throw new Error('test error');\n      }\n\n      @OnMessage('request4')\n      @EmitOnFail('fail1', { errorType: Error })\n      @EmitOnFail('fail2', { errorType: TypeError })\n      @EmitOnFail('fail3')\n      async testEvent4() {\n        throw new TypeError('type error 2');\n      }\n    }\n\n    socketControllers = new SocketControllers({\n      io: wsApp,\n      container: Container,\n      controllers: [TestController],\n    });\n    wsClient = io(PATH_FOR_CLIENT + '/string', { reconnection: false, timeout: 5000, forceNew: true });\n\n    const errors = { fail1: [], fail2: [], fail3: [] };\n\n    wsClient.on('fail1', data => {\n      errors.fail1.push(data);\n    });\n\n    wsClient.on('fail2', data => {\n      errors.fail2.push(data);\n    });\n\n    wsClient.on('fail3', data => {\n      errors.fail3.push(data);\n    });\n\n    await waitForEvent(wsClient, 'connected');\n\n    wsClient.emit('request');\n    wsClient.emit('request2');\n    wsClient.emit('request3');\n    wsClient.emit('request4');\n\n    await waitForTime(1000);\n\n    expect(errors).toEqual({\n      fail1: ['range error', 'type error 2'],\n      fail2: ['type error'],\n      fail3: ['test error'],\n    });\n  });\n});\n"
  },
  {
    "path": "test/functional/emit-on-success.spec.ts",
    "content": "import { createServer, Server as HttpServer } from 'http';\nimport { Server } from 'socket.io';\nimport { io, Socket } from 'socket.io-client';\nimport { SocketControllers } from '../../src/SocketControllers';\nimport { Container, Service } from 'typedi';\nimport { SocketController } from '../../src/decorators/SocketController';\nimport { OnConnect } from '../../src/decorators/OnConnect';\nimport { ConnectedSocket } from '../../src/decorators/ConnectedSocket';\nimport { waitForEvent } from '../utilities/waitForEvent';\nimport { EmitOnSuccess, OnMessage } from '../../src';\n\ndescribe('EmitOnSuccess', () => {\n  const PORT = 8080;\n  const PATH_FOR_CLIENT = `ws://localhost:${PORT}`;\n\n  let httpServer: HttpServer;\n  let wsApp: Server;\n  let wsClient: Socket;\n  let testResult;\n  let socketControllers: SocketControllers;\n\n  beforeEach(done => {\n    httpServer = createServer();\n    wsApp = new Server(httpServer, {\n      cors: {\n        origin: '*',\n      },\n    });\n    httpServer.listen(PORT, () => {\n      done();\n    });\n  });\n\n  afterEach(() => {\n    testResult = undefined;\n\n    Container.reset();\n    wsClient.close();\n    wsClient = null;\n    socketControllers = null;\n    return new Promise(resolve => {\n      if (wsApp)\n        return wsApp.close(() => {\n          resolve(null);\n        });\n      resolve(null);\n    });\n  });\n\n  it('Emit defined event on successful sync execution', async () => {\n    @SocketController('/string')\n    @Service()\n    class TestController {\n      @OnConnect()\n      connected(@ConnectedSocket() socket: Socket) {\n        socket.emit('connected');\n      }\n\n      @OnMessage('request')\n      @EmitOnSuccess('response')\n      testEvent() {\n        throw new Error('error string');\n      }\n\n      @OnMessage('request2')\n      @EmitOnSuccess('response')\n      testEvent2() {\n        return 'data';\n      }\n    }\n\n    socketControllers = new SocketControllers({\n      io: wsApp,\n      container: Container,\n      controllers: [TestController],\n    });\n    wsClient = io(PATH_FOR_CLIENT + '/string', { reconnection: false, timeout: 5000, forceNew: true });\n\n    const responses = [];\n\n    wsClient.on('response', data => {\n      responses.push(data);\n    });\n\n    await waitForEvent(wsClient, 'connected');\n    expect(responses.length).toEqual(0);\n\n    wsClient.emit('request');\n    wsClient.emit('request2');\n    await waitForEvent(wsClient, 'response');\n    expect(responses[0]).toEqual('data');\n    expect(responses.length).toEqual(1);\n  });\n\n  it('Emit defined event on successful async execution', async () => {\n    @SocketController('/string')\n    @Service()\n    class TestController {\n      @OnConnect()\n      connected(@ConnectedSocket() socket: Socket) {\n        socket.emit('connected');\n      }\n\n      @OnMessage('request')\n      @EmitOnSuccess('response')\n      async testEvent() {\n        throw new Error('error string');\n      }\n\n      @OnMessage('request2')\n      @EmitOnSuccess('response')\n      async testEvent2() {\n        return 'data';\n      }\n    }\n\n    socketControllers = new SocketControllers({\n      io: wsApp,\n      container: Container,\n      controllers: [TestController],\n    });\n    wsClient = io(PATH_FOR_CLIENT + '/string', { reconnection: false, timeout: 5000, forceNew: true });\n\n    const responses = [];\n\n    wsClient.on('response', data => {\n      responses.push(data);\n    });\n\n    await waitForEvent(wsClient, 'connected');\n    expect(responses.length).toEqual(0);\n\n    wsClient.emit('request');\n    wsClient.emit('request2');\n    await waitForEvent(wsClient, 'response');\n    expect(responses[0]).toEqual('data');\n    expect(responses.length).toEqual(1);\n  });\n});\n"
  },
  {
    "path": "test/functional/load-controllers-from-directory.spec.ts",
    "content": "import { createServer, Server as HttpServer } from 'http';\nimport { Server } from 'socket.io';\nimport { io, Socket } from 'socket.io-client';\nimport { SocketControllers } from '../../src/SocketControllers';\nimport { Container } from 'typedi';\nimport { waitForEvent } from '../utilities/waitForEvent';\nimport path from 'path';\n\ndescribe('Load controllers from directory', () => {\n  const PORT = 8080;\n  const PATH_FOR_CLIENT = `ws://localhost:${PORT}`;\n\n  let httpServer: HttpServer;\n  let wsApp: Server;\n  let wsClient: Socket;\n  let testResult;\n  let socketControllers: SocketControllers;\n\n  beforeEach(done => {\n    httpServer = createServer();\n    wsApp = new Server(httpServer, {\n      cors: {\n        origin: '*',\n      },\n    });\n    httpServer.listen(PORT, () => {\n      done();\n    });\n  });\n\n  afterEach(() => {\n    testResult = undefined;\n\n    Container.reset();\n    wsClient.close();\n    wsClient = null;\n    socketControllers = null;\n    return new Promise(resolve => {\n      if (wsApp)\n        return wsApp.close(() => {\n          resolve(null);\n        });\n      resolve(null);\n    });\n  });\n\n  it('Load controllers from directory', async () => {\n    socketControllers = new SocketControllers({\n      io: wsApp,\n      container: Container,\n      controllers: [path.join(__dirname, './controllers/**.*')],\n    });\n    wsClient = io(PATH_FOR_CLIENT, { reconnection: false, timeout: 5000, forceNew: true });\n\n    await waitForEvent(wsClient, 'connected');\n    wsClient.emit('test');\n    await waitForEvent(wsClient, 'response');\n    expect(true).toEqual(true);\n  });\n});\n"
  },
  {
    "path": "test/functional/middlewares.spec.ts",
    "content": "import { Server } from 'socket.io';\nimport { Socket, io } from 'socket.io-client';\nimport { Container, Service } from 'typedi';\nimport { waitForEvent } from '../utilities/waitForEvent';\nimport { createServer, Server as HttpServer } from 'http';\nimport { Middleware } from '../../src/decorators/Middleware';\nimport { MiddlewareInterface } from '../../src/types/MiddlewareInterface';\nimport { SocketController } from '../../src/decorators/SocketController';\nimport { OnConnect } from '../../src/decorators/OnConnect';\nimport { ConnectedSocket } from '../../src/decorators/ConnectedSocket';\nimport { SocketControllers } from '../../src/SocketControllers';\n\ndescribe('Middlewares', () => {\n  const PORT = 8080;\n  const PATH_FOR_CLIENT = `ws://localhost:${PORT}`;\n\n  let httpServer: HttpServer;\n  let wsApp: Server;\n  let wsClient: Socket;\n  let testResult: string[] = [];\n  let socketControllers: SocketControllers;\n\n  beforeEach(done => {\n    httpServer = createServer();\n    wsApp = new Server(httpServer, {\n      cors: {\n        origin: '*',\n      },\n    });\n    httpServer.listen(PORT, () => {\n      done();\n    });\n  });\n\n  afterEach(() => {\n    testResult = [];\n\n    Container.reset();\n    wsClient.close();\n    wsClient = null;\n    socketControllers = null;\n    return new Promise(resolve => {\n      if (wsApp)\n        return wsApp.close(() => {\n          resolve(null);\n        });\n      resolve(null);\n    });\n  });\n\n  it('no namespace', async () => {\n    @Middleware()\n    @Service()\n    class GlobalMiddleware implements MiddlewareInterface {\n      use(socket: any, next: (err?: any) => any): any {\n        testResult.push('global middleware');\n        next();\n      }\n    }\n\n    @SocketController()\n    @Service()\n    class Controller {\n      @OnConnect()\n      connected(@ConnectedSocket() socket: Socket) {\n        socket.emit('connected');\n      }\n    }\n\n    socketControllers = new SocketControllers({\n      io: wsApp,\n      container: Container,\n      middlewares: [GlobalMiddleware],\n      controllers: [Controller],\n    });\n    wsClient = io(PATH_FOR_CLIENT, { reconnection: false, timeout: 5000, forceNew: true });\n\n    await waitForEvent(wsClient, 'connected');\n    expect(testResult).toEqual(['global middleware']);\n  });\n\n  describe('string namespace', () => {\n    it('correct namespace', async () => {\n      @Middleware({ namespace: '/string' })\n      @Service()\n      class StringNamespaceMiddleware implements MiddlewareInterface {\n        use(socket: any, next: (err?: any) => any): any {\n          testResult.push('string middleware');\n          next();\n        }\n      }\n\n      @Middleware()\n      @Service()\n      class MiddlewareWithoutNamespace implements MiddlewareInterface {\n        use(socket: any, next: (err?: any) => any): any {\n          testResult.push('middleware without namespace');\n          next();\n        }\n      }\n\n      @SocketController('/string')\n      @Service()\n      class StringNamespaceController {\n        @OnConnect()\n        connected(@ConnectedSocket() socket: Socket) {\n          socket.emit('connected');\n        }\n      }\n\n      socketControllers = new SocketControllers({\n        io: wsApp,\n        container: Container,\n        middlewares: [StringNamespaceMiddleware, MiddlewareWithoutNamespace],\n        controllers: [StringNamespaceController],\n      });\n      wsClient = io(PATH_FOR_CLIENT + '/string', { reconnection: false, timeout: 5000, forceNew: true });\n\n      await waitForEvent(wsClient, 'connected');\n      expect(testResult).toEqual(['string middleware', 'middleware without namespace']);\n    });\n\n    it('incorrect namespace', async () => {\n      @Middleware({ namespace: '/string' })\n      @Service()\n      class StringNamespaceMiddleware implements MiddlewareInterface {\n        use(socket: any, next: (err?: any) => any): any {\n          testResult.push('string middleware');\n          next();\n        }\n      }\n\n      @Middleware()\n      @Service()\n      class MiddlewareWithoutNamespace implements MiddlewareInterface {\n        use(socket: any, next: (err?: any) => any): any {\n          testResult.push('middleware without namespace');\n          next();\n        }\n      }\n\n      @SocketController('/string2')\n      @Service()\n      class String2NamespaceController {\n        @OnConnect()\n        connected(@ConnectedSocket() socket: Socket) {\n          socket.emit('connected');\n        }\n      }\n\n      socketControllers = new SocketControllers({\n        io: wsApp,\n        container: Container,\n        middlewares: [StringNamespaceMiddleware, MiddlewareWithoutNamespace],\n        controllers: [String2NamespaceController],\n      });\n      wsClient = io(PATH_FOR_CLIENT + '/string2', { reconnection: false, timeout: 5000, forceNew: true });\n\n      await waitForEvent(wsClient, 'connected');\n      expect(testResult).toEqual(['middleware without namespace']);\n    });\n  });\n\n  describe('regexp namespace', () => {\n    it('correct namespace', async () => {\n      @Middleware({ namespace: /^\\/dynamic-\\d+$/ })\n      @Service()\n      class RegexpNamespaceMiddleware implements MiddlewareInterface {\n        use(socket: any, next: (err?: any) => any): any {\n          testResult.push(socket.nsp.name as string);\n          next();\n        }\n      }\n\n      @Middleware()\n      @Service()\n      class MiddlewareWithoutNamespace implements MiddlewareInterface {\n        use(socket: any, next: (err?: any) => any): any {\n          testResult.push('middleware without namespace');\n          next();\n        }\n      }\n\n      @SocketController(/^\\/dynamic-\\d+$/)\n      @Service()\n      class RegexpNamespaceController {\n        @OnConnect()\n        connected(@ConnectedSocket() socket: Socket) {\n          socket.emit('connected');\n        }\n      }\n\n      socketControllers = new SocketControllers({\n        io: wsApp,\n        container: Container,\n        middlewares: [RegexpNamespaceMiddleware, MiddlewareWithoutNamespace],\n        controllers: [RegexpNamespaceController],\n      });\n      wsClient = io(PATH_FOR_CLIENT + '/dynamic-1', { reconnection: false, timeout: 5000, forceNew: true });\n\n      await waitForEvent(wsClient, 'connected');\n      expect(testResult).toEqual(['/dynamic-1', 'middleware without namespace']);\n    });\n\n    it('incorrect namespace', async () => {\n      @Middleware({ namespace: /^\\/dynamic-\\s+$/ })\n      @Service()\n      class RegexpNamespaceMiddleware implements MiddlewareInterface {\n        use(socket: any, next: (err?: any) => any): any {\n          testResult.push(socket.nsp.name as string);\n          next();\n        }\n      }\n\n      @Middleware()\n      @Service()\n      class MiddlewareWithoutNamespace implements MiddlewareInterface {\n        use(socket: any, next: (err?: any) => any): any {\n          testResult.push('middleware without namespace');\n          next();\n        }\n      }\n\n      @SocketController(/^\\/dynamic-\\d+$/)\n      @Service()\n      class RegexpNamespaceController {\n        @OnConnect()\n        connected(@ConnectedSocket() socket: Socket) {\n          socket.emit('connected');\n        }\n      }\n\n      socketControllers = new SocketControllers({\n        io: wsApp,\n        container: Container,\n        middlewares: [RegexpNamespaceMiddleware, MiddlewareWithoutNamespace],\n        controllers: [RegexpNamespaceController],\n      });\n      wsClient = io(PATH_FOR_CLIENT + '/dynamic-1', { reconnection: false, timeout: 5000, forceNew: true });\n\n      await waitForEvent(wsClient, 'connected');\n      expect(testResult).toEqual(['middleware without namespace']);\n    });\n  });\n\n  describe('array namespace', () => {\n    it('correct namespace', async () => {\n      @Middleware({ namespace: [/^\\/dynamic-\\d+$/] })\n      @Service()\n      class RegexpArrayNamespaceMiddleware implements MiddlewareInterface {\n        use(socket: any, next: (err?: any) => any): any {\n          testResult.push(socket.nsp.name as string);\n          next();\n        }\n      }\n\n      @Middleware()\n      @Service()\n      class MiddlewareWithoutNamespace implements MiddlewareInterface {\n        use(socket: any, next: (err?: any) => any): any {\n          testResult.push('middleware without namespace');\n          next();\n        }\n      }\n\n      @SocketController(/^\\/dynamic-\\d+$/)\n      @Service()\n      class RegexpNamespaceController {\n        @OnConnect()\n        connected(@ConnectedSocket() socket: Socket) {\n          socket.emit('connected');\n        }\n      }\n\n      socketControllers = new SocketControllers({\n        io: wsApp,\n        container: Container,\n        middlewares: [RegexpArrayNamespaceMiddleware, MiddlewareWithoutNamespace],\n        controllers: [RegexpNamespaceController],\n      });\n      wsClient = io(PATH_FOR_CLIENT + '/dynamic-1', { reconnection: false, timeout: 5000, forceNew: true });\n\n      await waitForEvent(wsClient, 'connected');\n      expect(testResult).toEqual(['/dynamic-1', 'middleware without namespace']);\n    });\n\n    it('incorrect namespace', async () => {\n      @Middleware({ namespace: [/^\\/dynamic-\\s+$/] })\n      @Service()\n      class RegexpArrayNamespaceMiddleware implements MiddlewareInterface {\n        use(socket: any, next: (err?: any) => any): any {\n          testResult.push(socket.nsp.name as string);\n          next();\n        }\n      }\n\n      @Middleware()\n      @Service()\n      class MiddlewareWithoutNamespace implements MiddlewareInterface {\n        use(socket: any, next: (err?: any) => any): any {\n          testResult.push('middleware without namespace');\n          next();\n        }\n      }\n\n      @SocketController(/^\\/dynamic-\\d+$/)\n      @Service()\n      class RegexpNamespaceController {\n        @OnConnect()\n        connected(@ConnectedSocket() socket: Socket) {\n          socket.emit('connected');\n        }\n      }\n\n      socketControllers = new SocketControllers({\n        io: wsApp,\n        container: Container,\n        middlewares: [RegexpArrayNamespaceMiddleware, MiddlewareWithoutNamespace],\n        controllers: [RegexpNamespaceController],\n      });\n      wsClient = io(PATH_FOR_CLIENT + '/dynamic-1', { reconnection: false, timeout: 5000, forceNew: true });\n\n      await waitForEvent(wsClient, 'connected');\n      expect(testResult).toEqual(['middleware without namespace']);\n    });\n  });\n});\n"
  },
  {
    "path": "test/functional/multiple-controllers-on-same-namespace.spec.ts",
    "content": "import { createServer, Server as HttpServer } from 'http';\nimport { Server } from 'socket.io';\nimport { io, Socket } from 'socket.io-client';\nimport { ConnectedSocket, OnConnect, SocketController, SocketControllers } from '../../src';\nimport { Container, Service } from 'typedi';\nimport { waitForTime } from '../utilities/waitForTime';\n\ndescribe('Multiple controllers with same namespace', () => {\n  const PORT = 8080;\n  const PATH_FOR_CLIENT = `ws://localhost:${PORT}`;\n\n  let httpServer: HttpServer;\n  let wsApp: Server;\n  let wsClient: Socket;\n  let testResult;\n  let socketControllers: SocketControllers;\n\n  beforeEach(done => {\n    httpServer = createServer();\n    wsApp = new Server(httpServer, {\n      cors: {\n        origin: '*',\n      },\n    });\n    httpServer.listen(PORT, () => {\n      done();\n    });\n  });\n\n  afterEach(() => {\n    testResult = undefined;\n\n    Container.reset();\n    wsClient.close();\n    wsClient = null;\n    socketControllers = null;\n    return new Promise(resolve => {\n      if (wsApp)\n        return wsApp.close(() => {\n          resolve(null);\n        });\n      resolve(null);\n    });\n  });\n\n  it('using string namespace', async () => {\n    @SocketController('/string')\n    @Service()\n    class TestController {\n      @OnConnect()\n      connected(@ConnectedSocket() socket: Socket) {\n        testResult = [...(testResult || []), '1'];\n        socket.emit('connected');\n      }\n    }\n\n    @SocketController('/string')\n    @Service()\n    class TestController2 {\n      @OnConnect()\n      connected(@ConnectedSocket() socket: Socket) {\n        testResult = [...(testResult || []), '2'];\n        socket.emit('connected2');\n      }\n    }\n\n    socketControllers = new SocketControllers({\n      io: wsApp,\n      container: Container,\n      controllers: [TestController, TestController2],\n    });\n    wsClient = io(PATH_FOR_CLIENT + '/string', { reconnection: false, timeout: 5000, forceNew: true });\n\n    await waitForTime(100);\n    expect(testResult).toContain('1');\n    expect(testResult).toContain('2');\n  });\n});\n"
  },
  {
    "path": "test/functional/nsp-param.spec.ts",
    "content": "import { createServer, Server as HttpServer } from 'http';\nimport { Server } from 'socket.io';\nimport { io, Socket } from 'socket.io-client';\nimport { SocketControllers } from '../../src/SocketControllers';\nimport { Container, Service } from 'typedi';\nimport { SocketController } from '../../src/decorators/SocketController';\nimport { OnConnect } from '../../src/decorators/OnConnect';\nimport { ConnectedSocket } from '../../src/decorators/ConnectedSocket';\nimport { waitForEvent } from '../utilities/waitForEvent';\nimport { NspParam } from '../../src';\n\ndescribe('NspParam', () => {\n  const PORT = 8080;\n  const PATH_FOR_CLIENT = `ws://localhost:${PORT}`;\n\n  let httpServer: HttpServer;\n  let wsApp: Server;\n  let wsClient: Socket;\n  let testResult;\n  let socketControllers: SocketControllers;\n\n  beforeEach(done => {\n    httpServer = createServer();\n    wsApp = new Server(httpServer, {\n      cors: {\n        origin: '*',\n      },\n    });\n    httpServer.listen(PORT, () => {\n      done();\n    });\n  });\n\n  afterEach(() => {\n    testResult = undefined;\n\n    Container.reset();\n    wsClient.close();\n    wsClient = null;\n    socketControllers = null;\n    return new Promise(resolve => {\n      if (wsApp)\n        return wsApp.close(() => {\n          resolve(null);\n        });\n      resolve(null);\n    });\n  });\n\n  it('Namespace param is retrieved correctly', async () => {\n    @SocketController('/:first/:second')\n    @Service()\n    class TestController {\n      @OnConnect()\n      connected(@ConnectedSocket() socket: Socket, @NspParam('second') parameter: string) {\n        testResult = parameter;\n        socket.emit('connected');\n      }\n    }\n\n    socketControllers = new SocketControllers({\n      io: wsApp,\n      container: Container,\n      controllers: [TestController],\n    });\n    wsClient = io(PATH_FOR_CLIENT + '/test1/test2', { reconnection: false, timeout: 5000, forceNew: true });\n\n    await waitForEvent(wsClient, 'connected');\n    expect(testResult).toEqual('test2');\n  });\n});\n"
  },
  {
    "path": "test/functional/nsp-params.spec.ts",
    "content": "import { createServer, Server as HttpServer } from 'http';\nimport { Server } from 'socket.io';\nimport { io, Socket } from 'socket.io-client';\nimport { SocketControllers } from '../../src/SocketControllers';\nimport { Container, Service } from 'typedi';\nimport { SocketController } from '../../src/decorators/SocketController';\nimport { OnConnect } from '../../src/decorators/OnConnect';\nimport { ConnectedSocket } from '../../src/decorators/ConnectedSocket';\nimport { waitForEvent } from '../utilities/waitForEvent';\nimport { NspParams } from '../../src';\n\ndescribe('NspParams', () => {\n  const PORT = 8080;\n  const PATH_FOR_CLIENT = `ws://localhost:${PORT}`;\n\n  let httpServer: HttpServer;\n  let wsApp: Server;\n  let wsClient: Socket;\n  let testResult;\n  let socketControllers: SocketControllers;\n\n  beforeEach(done => {\n    httpServer = createServer();\n    wsApp = new Server(httpServer, {\n      cors: {\n        origin: '*',\n      },\n    });\n    httpServer.listen(PORT, () => {\n      done();\n    });\n  });\n\n  afterEach(() => {\n    testResult = undefined;\n\n    Container.reset();\n    wsClient.close();\n    wsClient = null;\n    socketControllers = null;\n    return new Promise(resolve => {\n      if (wsApp)\n        return wsApp.close(() => {\n          resolve(null);\n        });\n      resolve(null);\n    });\n  });\n\n  it('Namespace params are retrieved correctly', async () => {\n    @SocketController('/:first/:second')\n    @Service()\n    class TestController {\n      @OnConnect()\n      connected(@ConnectedSocket() socket: Socket, @NspParams() parameters: { first: string; second: string }) {\n        testResult = parameters;\n        socket.emit('connected');\n      }\n    }\n\n    socketControllers = new SocketControllers({\n      io: wsApp,\n      container: Container,\n      controllers: [TestController],\n    });\n    wsClient = io(PATH_FOR_CLIENT + '/test1/test2', { reconnection: false, timeout: 5000, forceNew: true });\n\n    await waitForEvent(wsClient, 'connected');\n    expect(testResult).toEqual({ first: 'test1', second: 'test2' });\n  });\n});\n"
  },
  {
    "path": "test/functional/on-disconnect.spec.ts",
    "content": "import { createServer, Server as HttpServer } from 'http';\nimport { Server } from 'socket.io';\nimport { io, Socket } from 'socket.io-client';\nimport { SocketControllers } from '../../src/SocketControllers';\nimport { Container, Service } from 'typedi';\nimport { SocketController } from '../../src/decorators/SocketController';\nimport { OnConnect } from '../../src/decorators/OnConnect';\nimport { ConnectedSocket } from '../../src/decorators/ConnectedSocket';\nimport { waitForEvent } from '../utilities/waitForEvent';\nimport { OnDisconnect } from '../../src';\nimport { waitForTime } from '../utilities/waitForTime';\n\ndescribe('OnDisconnect', () => {\n  const PORT = 8080;\n  const PATH_FOR_CLIENT = `ws://localhost:${PORT}`;\n\n  let httpServer: HttpServer;\n  let wsApp: Server;\n  let wsClient: Socket;\n  let testResult;\n  let socketControllers: SocketControllers;\n\n  beforeEach(done => {\n    httpServer = createServer();\n    wsApp = new Server(httpServer, {\n      cors: {\n        origin: '*',\n      },\n    });\n    httpServer.listen(PORT, () => {\n      done();\n    });\n  });\n\n  afterEach(() => {\n    testResult = undefined;\n\n    Container.reset();\n    wsClient.close();\n    wsClient = null;\n    socketControllers = null;\n    return new Promise(resolve => {\n      if (wsApp)\n        return wsApp.close(() => {\n          resolve(null);\n        });\n      resolve(null);\n    });\n  });\n\n  it('OnDisconnect is called correctly', async () => {\n    @SocketController('/string')\n    @Service()\n    class TestController {\n      @OnConnect()\n      connected(@ConnectedSocket() socket: Socket) {\n        socket.emit('connected');\n      }\n\n      @OnDisconnect()\n      disconnected() {\n        testResult = 'disconnected';\n      }\n    }\n\n    socketControllers = new SocketControllers({\n      io: wsApp,\n      container: Container,\n      controllers: [TestController],\n    });\n    wsClient = io(PATH_FOR_CLIENT + '/string', { reconnection: false, timeout: 5000, forceNew: true });\n\n    await waitForEvent(wsClient, 'connected');\n    wsClient.disconnect();\n    await waitForTime(1000);\n    expect(testResult).toEqual('disconnected');\n  });\n});\n"
  },
  {
    "path": "test/functional/on-disconnecting.spec.ts",
    "content": "import { createServer, Server as HttpServer } from 'http';\nimport { Server } from 'socket.io';\nimport { io, Socket } from 'socket.io-client';\nimport { SocketControllers } from '../../src/SocketControllers';\nimport { Container, Service } from 'typedi';\nimport { SocketController } from '../../src/decorators/SocketController';\nimport { OnConnect } from '../../src/decorators/OnConnect';\nimport { ConnectedSocket } from '../../src/decorators/ConnectedSocket';\nimport { waitForEvent } from '../utilities/waitForEvent';\nimport { OnDisconnect } from '../../src';\nimport { waitForTime } from '../utilities/waitForTime';\nimport { OnDisconnecting } from '../../src/decorators/OnDisconnecting';\n\ndescribe('OnDisconnecting', () => {\n  const PORT = 8080;\n  const PATH_FOR_CLIENT = `ws://localhost:${PORT}`;\n\n  let httpServer: HttpServer;\n  let wsApp: Server;\n  let wsClient: Socket;\n  let testResult = [];\n  let socketControllers: SocketControllers;\n\n  beforeEach(done => {\n    httpServer = createServer();\n    wsApp = new Server(httpServer, {\n      cors: {\n        origin: '*',\n      },\n    });\n    httpServer.listen(PORT, () => {\n      done();\n    });\n  });\n\n  afterEach(() => {\n    testResult = [];\n\n    Container.reset();\n    wsClient.close();\n    wsClient = null;\n    socketControllers = null;\n    return new Promise(resolve => {\n      if (wsApp)\n        return wsApp.close(() => {\n          resolve(null);\n        });\n      resolve(null);\n    });\n  });\n\n  it('OnDisconnect is called correctly', async () => {\n    @SocketController('/string')\n    @Service()\n    class TestController {\n      @OnConnect()\n      connected(@ConnectedSocket() socket: Socket) {\n        socket.emit('connected');\n      }\n\n      @OnDisconnect()\n      disconnected() {\n        testResult.push('disconnected');\n      }\n\n      @OnDisconnecting()\n      disconnecting() {\n        testResult.push('disconnecting');\n      }\n    }\n\n    socketControllers = new SocketControllers({\n      io: wsApp,\n      container: Container,\n      controllers: [TestController],\n    });\n    wsClient = io(PATH_FOR_CLIENT + '/string', { reconnection: false, timeout: 5000, forceNew: true });\n\n    await waitForEvent(wsClient, 'connected');\n    wsClient.disconnect();\n    await waitForTime(1000);\n    expect(testResult).toEqual(['disconnecting', 'disconnected']);\n  });\n});\n"
  },
  {
    "path": "test/functional/parameter-transformation.spec.ts",
    "content": "import { createServer, Server as HttpServer } from 'http';\nimport { Server } from 'socket.io';\nimport { io, Socket } from 'socket.io-client';\nimport { SocketControllers } from '../../src/SocketControllers';\nimport { Container, Service } from 'typedi';\nimport { SocketController } from '../../src/decorators/SocketController';\nimport { OnConnect } from '../../src/decorators/OnConnect';\nimport { ConnectedSocket } from '../../src/decorators/ConnectedSocket';\nimport { waitForEvent } from '../utilities/waitForEvent';\nimport { MessageBody, OnMessage } from '../../src';\nimport { Expose } from 'class-transformer';\n\ndescribe('Parameter transformation', () => {\n  const PORT = 8080;\n  const PATH_FOR_CLIENT = `ws://localhost:${PORT}`;\n\n  let httpServer: HttpServer;\n  let wsApp: Server;\n  let wsClient: Socket;\n  let testResult;\n  let socketControllers: SocketControllers;\n\n  beforeEach(done => {\n    httpServer = createServer();\n    wsApp = new Server(httpServer, {\n      cors: {\n        origin: '*',\n      },\n    });\n    httpServer.listen(PORT, () => {\n      done();\n    });\n  });\n\n  afterEach(() => {\n    testResult = undefined;\n\n    Container.reset();\n    wsClient.close();\n    wsClient = null;\n    socketControllers = null;\n    return new Promise(resolve => {\n      if (wsApp)\n        return wsApp.close(() => {\n          resolve(null);\n        });\n      resolve(null);\n    });\n  });\n\n  it('Parameters are converted correctly with the given options', async () => {\n    class Body {\n      @Expose() prop1: string;\n      @Expose() prop2: number;\n    }\n\n    @SocketController('/string')\n    @Service()\n    class TestController {\n      @OnConnect()\n      connected(@ConnectedSocket() socket: Socket) {\n        testResult = socket.id;\n        socket.emit('connected');\n      }\n\n      @OnMessage('test')\n      test(\n        @ConnectedSocket() socket: Socket,\n        @MessageBody({\n          transform: true,\n          transformOptions: { excludeExtraneousValues: true, enableImplicitConversion: true },\n        })\n        body: Body\n      ) {\n        testResult = body;\n        socket.emit('result');\n      }\n    }\n\n    socketControllers = new SocketControllers({\n      io: wsApp,\n      container: Container,\n      controllers: [TestController],\n    });\n    wsClient = io(PATH_FOR_CLIENT + '/string', { reconnection: false, timeout: 5000, forceNew: true });\n\n    await waitForEvent(wsClient, 'connected');\n\n    wsClient.emit('test', { prop1: 'test', prop2: '2', prop3: 10 });\n    await waitForEvent(wsClient, 'result');\n    expect(testResult).toBeInstanceOf(Body);\n    expect(testResult.prop1).toEqual('test');\n    expect(testResult.prop2).toEqual(2);\n    expect(testResult.prop3).toEqual(undefined);\n  });\n});\n"
  },
  {
    "path": "test/functional/scoped-controllers.spec.ts",
    "content": "import { createServer, Server as HttpServer } from 'http';\nimport { Server } from 'socket.io';\nimport { io, Socket } from 'socket.io-client';\nimport { SocketControllers } from '../../src/SocketControllers';\nimport { Container, ContainerInstance, Inject, Service, Token } from 'typedi';\nimport { SocketController } from '../../src/decorators/SocketController';\nimport { OnConnect } from '../../src/decorators/OnConnect';\nimport { ConnectedSocket } from '../../src/decorators/ConnectedSocket';\nimport { waitForEvent } from '../utilities/waitForEvent';\nimport { EmitOnSuccess, OnMessage, SocketEventContext } from '../../src';\n\ndescribe('Scoped controllers', () => {\n  const PORT = 8080;\n  const PATH_FOR_CLIENT = `ws://localhost:${PORT}`;\n\n  let httpServer: HttpServer;\n  let wsApp: Server;\n  let wsClient: Socket;\n  let testResult = [];\n  let socketControllers: SocketControllers;\n\n  beforeEach(done => {\n    httpServer = createServer();\n    wsApp = new Server(httpServer, {\n      cors: {\n        origin: '*',\n      },\n    });\n    httpServer.listen(PORT, () => {\n      done();\n    });\n  });\n\n  afterEach(() => {\n    testResult = [];\n\n    Container.reset();\n    wsClient.close();\n    wsClient = null;\n    socketControllers = null;\n    return new Promise(resolve => {\n      if (wsApp)\n        return wsApp.close(() => {\n          resolve(null);\n        });\n      resolve(null);\n    });\n  });\n\n  it('two instances should be different', async () => {\n    @Service()\n    class TestService {}\n\n    @SocketController('/string')\n    @Service()\n    class TestController {\n      constructor(private testService: TestService) {}\n\n      @OnConnect()\n      connected(@ConnectedSocket() socket: Socket) {\n        socket.emit('connected');\n      }\n\n      @OnMessage('test')\n      @EmitOnSuccess('done')\n      test() {\n        testResult.push(this.testService);\n      }\n    }\n\n    socketControllers = new SocketControllers({\n      io: wsApp,\n      container: Container,\n      controllers: [TestController],\n      scopedContainerGetter: (args: SocketEventContext) => {\n        return Container.of(Math.random().toString());\n      },\n    });\n    wsClient = io(PATH_FOR_CLIENT + '/string', { reconnection: false, timeout: 5000, forceNew: true });\n\n    await waitForEvent(wsClient, 'connected');\n    wsClient.emit('test');\n    await waitForEvent(wsClient, 'done');\n    wsClient.emit('test');\n    await waitForEvent(wsClient, 'done');\n    expect(testResult[0]).not.toBe(testResult[1]);\n  });\n\n  it('two global instances should be the same', async () => {\n    @Service({ global: true })\n    class TestService {}\n\n    @SocketController('/string')\n    @Service()\n    class TestController {\n      constructor(private testService: TestService) {}\n\n      @OnConnect()\n      connected(@ConnectedSocket() socket: Socket) {\n        socket.emit('connected');\n      }\n\n      @OnMessage('test')\n      @EmitOnSuccess('done')\n      test() {\n        testResult.push(this.testService);\n      }\n    }\n\n    socketControllers = new SocketControllers({\n      io: wsApp,\n      container: Container,\n      controllers: [TestController],\n      scopedContainerGetter: (args: SocketEventContext) => {\n        return Container.of(Math.random().toString());\n      },\n    });\n    wsClient = io(PATH_FOR_CLIENT + '/string', { reconnection: false, timeout: 5000, forceNew: true });\n\n    await waitForEvent(wsClient, 'connected');\n    wsClient.emit('test');\n    await waitForEvent(wsClient, 'done');\n    wsClient.emit('test');\n    await waitForEvent(wsClient, 'done');\n    expect(testResult[0]).toBe(testResult[1]);\n  });\n\n  it('additional injectables should be retrievable', async () => {\n    const token = new Token('ADDITIONAL');\n    let counter = 0;\n\n    @Service({ global: true })\n    class TestService {}\n\n    @SocketController('/string')\n    @Service()\n    class TestController {\n      constructor(\n        private testService: TestService,\n        @Inject(token) public myAdditional: number\n      ) {}\n\n      @OnConnect()\n      connected(@ConnectedSocket() socket: Socket) {\n        socket.emit('connected');\n      }\n\n      @OnMessage('test')\n      @EmitOnSuccess('done')\n      test() {\n        testResult.push(this.myAdditional);\n      }\n    }\n\n    socketControllers = new SocketControllers({\n      io: wsApp,\n      container: Container,\n      controllers: [TestController],\n      scopedContainerGetter: (args: SocketEventContext) => {\n        const container = Container.of(counter.toString());\n        container.set(token, counter);\n        counter++;\n        return container;\n      },\n    });\n    wsClient = io(PATH_FOR_CLIENT + '/string', { reconnection: false, timeout: 5000, forceNew: true });\n\n    await waitForEvent(wsClient, 'connected');\n    wsClient.emit('test');\n    await waitForEvent(wsClient, 'done');\n    wsClient.emit('test');\n    await waitForEvent(wsClient, 'done');\n    expect(testResult[0]).toBe(1);\n    expect(testResult[1]).toBe(2);\n  });\n\n  it('arguments should be provided correctly to getter', async () => {\n    @SocketController('/:test')\n    @Service()\n    class TestController {\n      @OnConnect()\n      @EmitOnSuccess('connected')\n      connected(@ConnectedSocket() socket: Socket) {}\n\n      @OnMessage('test')\n      @EmitOnSuccess('done')\n      test() {}\n    }\n\n    socketControllers = new SocketControllers({\n      io: wsApp,\n      container: Container,\n      controllers: [TestController],\n      scopedContainerGetter: (args: SocketEventContext) => {\n        testResult.push(args);\n        return Container.of('');\n      },\n    });\n    wsClient = io(PATH_FOR_CLIENT + '/string', { reconnection: false, timeout: 5000, forceNew: true });\n\n    await waitForEvent(wsClient, 'connected');\n    wsClient.emit('test', 'args1');\n    await waitForEvent(wsClient, 'done');\n\n    expect(testResult[1].socket.id).toEqual(wsClient.id);\n    expect(testResult[1].socketIo).toBe(socketControllers.io);\n    expect(testResult[1].eventName).toBe('test');\n    expect(testResult[1].messageArgs).toEqual(['args1']);\n    expect(testResult[1].nspParams).toEqual({ test: 'string' });\n  });\n\n  it('container should be disposed', async () => {\n    const token = new Token('ADDITIONAL');\n\n    @Service({ global: true })\n    class TestService {}\n\n    @SocketController('/string')\n    @Service()\n    class TestController {\n      constructor(\n        private testService: TestService,\n        @Inject(token) public myAdditional: number\n      ) {}\n\n      @OnConnect()\n      connected(@ConnectedSocket() socket: Socket) {\n        socket.emit('connected');\n      }\n\n      @OnMessage('test')\n      @EmitOnSuccess('done')\n      test() {\n        testResult.push(this.myAdditional);\n      }\n    }\n\n    let container;\n    socketControllers = new SocketControllers({\n      io: wsApp,\n      container: Container,\n      controllers: [TestController],\n      scopedContainerGetter: () => {\n        container = Container.of('test');\n        container.set(token, 'test');\n        return container;\n      },\n      scopedContainerDisposer: (scopedContainer: ContainerInstance) => {\n        scopedContainer.reset({ strategy: 'resetServices' });\n      },\n    });\n    wsClient = io(PATH_FOR_CLIENT + '/string', { reconnection: false, timeout: 5000, forceNew: true });\n\n    await waitForEvent(wsClient, 'connected');\n    wsClient.emit('test');\n    await waitForEvent(wsClient, 'done');\n\n    expect(Container.has(TestService)).toBe(true);\n    expect(container.has(token)).toBe(false);\n    expect(container.has(TestController)).toBe(false);\n  });\n});\n"
  },
  {
    "path": "test/functional/skip-emit-on-empty-result.spec.ts",
    "content": "import { createServer, Server as HttpServer } from 'http';\nimport { Server } from 'socket.io';\nimport { io, Socket } from 'socket.io-client';\nimport { SocketControllers } from '../../src/SocketControllers';\nimport { Container, Service } from 'typedi';\nimport { SocketController } from '../../src/decorators/SocketController';\nimport { OnConnect } from '../../src/decorators/OnConnect';\nimport { ConnectedSocket } from '../../src/decorators/ConnectedSocket';\nimport { waitForEvent } from '../utilities/waitForEvent';\nimport { EmitOnFail, EmitOnSuccess, OnMessage, SkipEmitOnEmptyResult } from '../../src';\n\ndescribe('SkipEmitOnEmptyResult', () => {\n  const PORT = 8080;\n  const PATH_FOR_CLIENT = `ws://localhost:${PORT}`;\n\n  let httpServer: HttpServer;\n  let wsApp: Server;\n  let wsClient: Socket;\n  let testResult;\n  let socketControllers: SocketControllers;\n\n  beforeEach(done => {\n    httpServer = createServer();\n    wsApp = new Server(httpServer, {\n      cors: {\n        origin: '*',\n      },\n    });\n    httpServer.listen(PORT, () => {\n      done();\n    });\n  });\n\n  afterEach(() => {\n    testResult = undefined;\n\n    Container.reset();\n    wsClient.close();\n    wsClient = null;\n    socketControllers = null;\n    return new Promise(resolve => {\n      if (wsApp)\n        return wsApp.close(() => {\n          resolve(null);\n        });\n      resolve(null);\n    });\n  });\n\n  it('Skip emit of defined event on successful sync execution', async () => {\n    @SocketController('/string')\n    @Service()\n    class TestController {\n      @OnConnect()\n      connected(@ConnectedSocket() socket: Socket) {\n        socket.emit('connected');\n      }\n\n      @OnMessage('request')\n      @EmitOnSuccess('response')\n      @SkipEmitOnEmptyResult()\n      testEvent() {\n        return { data: true };\n      }\n\n      @OnMessage('request2')\n      @EmitOnSuccess('response')\n      @SkipEmitOnEmptyResult()\n      testEvent2() {\n        return null;\n      }\n    }\n\n    socketControllers = new SocketControllers({\n      io: wsApp,\n      container: Container,\n      controllers: [TestController],\n    });\n    wsClient = io(PATH_FOR_CLIENT + '/string', { reconnection: false, timeout: 5000, forceNew: true });\n\n    const responses = [];\n\n    wsClient.on('response', data => {\n      responses.push(data);\n    });\n\n    await waitForEvent(wsClient, 'connected');\n    expect(responses.length).toEqual(0);\n\n    wsClient.emit('request2');\n    wsClient.emit('request');\n\n    await waitForEvent(wsClient, 'response');\n    expect(responses.length).toEqual(1);\n    expect(responses[0]).toEqual({ data: true });\n  });\n\n  it('Skip emit of defined event on successful async execution', async () => {\n    @SocketController('/string')\n    @Service()\n    class TestController {\n      @OnConnect()\n      async connected(@ConnectedSocket() socket: Socket) {\n        socket.emit('connected');\n      }\n\n      @OnMessage('request')\n      @EmitOnSuccess('response')\n      @SkipEmitOnEmptyResult()\n      async testEvent() {\n        return { data: true };\n      }\n\n      @OnMessage('request2')\n      @EmitOnSuccess('response')\n      @SkipEmitOnEmptyResult()\n      async testEvent2() {\n        return null;\n      }\n    }\n\n    socketControllers = new SocketControllers({\n      io: wsApp,\n      container: Container,\n      controllers: [TestController],\n    });\n    wsClient = io(PATH_FOR_CLIENT + '/string', { reconnection: false, timeout: 5000, forceNew: true });\n\n    const responses = [];\n\n    wsClient.on('response', data => {\n      responses.push(data);\n    });\n\n    await waitForEvent(wsClient, 'connected');\n    expect(responses.length).toEqual(0);\n\n    wsClient.emit('request2');\n    wsClient.emit('request');\n\n    await waitForEvent(wsClient, 'response');\n    expect(responses.length).toEqual(1);\n    expect(responses[0]).toEqual({ data: true });\n  });\n\n  it('Skip emit of defined event on failing sync execution', async () => {\n    @SocketController('/string')\n    @Service()\n    class TestController {\n      @OnConnect()\n      connected(@ConnectedSocket() socket: Socket) {\n        socket.emit('connected');\n      }\n\n      @OnMessage('request')\n      @EmitOnFail('response')\n      @SkipEmitOnEmptyResult()\n      testEvent() {\n        throw new Error('error string');\n      }\n\n      @OnMessage('request2')\n      @EmitOnFail('response')\n      @SkipEmitOnEmptyResult()\n      testEvent2() {\n        return Promise.reject(null);\n      }\n    }\n\n    socketControllers = new SocketControllers({\n      io: wsApp,\n      container: Container,\n      controllers: [TestController],\n    });\n    wsClient = io(PATH_FOR_CLIENT + '/string', { reconnection: false, timeout: 5000, forceNew: true });\n\n    const responses = [];\n\n    wsClient.on('response', data => {\n      responses.push(data);\n    });\n\n    await waitForEvent(wsClient, 'connected');\n    expect(responses.length).toEqual(0);\n\n    wsClient.emit('request2');\n    wsClient.emit('request');\n\n    await waitForEvent(wsClient, 'response');\n    expect(responses.length).toEqual(1);\n    expect(responses[0]).toEqual('error string');\n  });\n\n  it('Skip emit of defined event on failing async execution', async () => {\n    @SocketController('/string')\n    @Service()\n    class TestController {\n      @OnConnect()\n      async connected(@ConnectedSocket() socket: Socket) {\n        socket.emit('connected');\n      }\n\n      @OnMessage('request')\n      @EmitOnFail('response')\n      @SkipEmitOnEmptyResult()\n      async testEvent() {\n        throw new Error('error string');\n      }\n\n      @OnMessage('request2')\n      @EmitOnFail('response2')\n      @SkipEmitOnEmptyResult()\n      async testEvent2(@ConnectedSocket() socket: Socket) {\n        return Promise.reject(null);\n      }\n    }\n\n    socketControllers = new SocketControllers({\n      io: wsApp,\n      container: Container,\n      controllers: [TestController],\n    });\n    wsClient = io(PATH_FOR_CLIENT + '/string', { reconnection: false, timeout: 5000, forceNew: true });\n\n    const responses = [];\n\n    wsClient.on('response', data => {\n      responses.push(data);\n    });\n\n    await waitForEvent(wsClient, 'connected');\n    expect(responses.length).toEqual(0);\n\n    wsClient.emit('request2');\n    wsClient.emit('request');\n\n    await waitForEvent(wsClient, 'response');\n    expect(responses.length).toEqual(1);\n    expect(responses[0]).toEqual('error string');\n  });\n});\n"
  },
  {
    "path": "test/functional/socket-id.spec.ts",
    "content": "import { createServer, Server as HttpServer } from 'http';\nimport { Server } from 'socket.io';\nimport { io, Socket } from 'socket.io-client';\nimport { SocketControllers } from '../../src/SocketControllers';\nimport { Container, Service } from 'typedi';\nimport { SocketController } from '../../src/decorators/SocketController';\nimport { OnConnect } from '../../src/decorators/OnConnect';\nimport { ConnectedSocket } from '../../src/decorators/ConnectedSocket';\nimport { waitForEvent } from '../utilities/waitForEvent';\nimport { SocketId } from '../../src';\n\ndescribe('SocketId', () => {\n  const PORT = 8080;\n  const PATH_FOR_CLIENT = `ws://localhost:${PORT}`;\n\n  let httpServer: HttpServer;\n  let wsApp: Server;\n  let wsClient: Socket;\n  let testResult;\n  let socketControllers: SocketControllers;\n\n  beforeEach(done => {\n    httpServer = createServer();\n    wsApp = new Server(httpServer, {\n      cors: {\n        origin: '*',\n      },\n    });\n    httpServer.listen(PORT, () => {\n      done();\n    });\n  });\n\n  afterEach(() => {\n    testResult = undefined;\n\n    Container.reset();\n    wsClient.close();\n    wsClient = null;\n    socketControllers = null;\n    return new Promise(resolve => {\n      if (wsApp)\n        return wsApp.close(() => {\n          resolve(null);\n        });\n      resolve(null);\n    });\n  });\n\n  it('Connected socket id is retrieved correctly', async () => {\n    @SocketController('/string')\n    @Service()\n    class TestController {\n      @OnConnect()\n      connected(@ConnectedSocket() socket: Socket, @SocketId() socketId: string) {\n        testResult = socketId;\n        socket.emit('connected');\n      }\n    }\n\n    socketControllers = new SocketControllers({\n      io: wsApp,\n      container: Container,\n      controllers: [TestController],\n    });\n    wsClient = io(PATH_FOR_CLIENT + '/string', { reconnection: false, timeout: 5000, forceNew: true });\n\n    await waitForEvent(wsClient, 'connected');\n    expect(wsClient.id).toEqual(testResult);\n  });\n});\n"
  },
  {
    "path": "test/functional/socket-io.spec.ts",
    "content": "import { createServer, Server as HttpServer } from 'http';\nimport { Server } from 'socket.io';\nimport { io, Socket } from 'socket.io-client';\nimport { SocketControllers } from '../../src/SocketControllers';\nimport { Container, Service } from 'typedi';\nimport { SocketController } from '../../src/decorators/SocketController';\nimport { OnConnect } from '../../src/decorators/OnConnect';\nimport { ConnectedSocket } from '../../src/decorators/ConnectedSocket';\nimport { waitForEvent } from '../utilities/waitForEvent';\nimport { SocketIO } from '../../src';\n\ndescribe('SocketIo', () => {\n  const PORT = 8080;\n  const PATH_FOR_CLIENT = `ws://localhost:${PORT}`;\n\n  let httpServer: HttpServer;\n  let wsApp: Server;\n  let wsClient: Socket;\n  let testResult;\n  let socketControllers: SocketControllers;\n\n  beforeEach(done => {\n    httpServer = createServer();\n    wsApp = new Server(httpServer, {\n      cors: {\n        origin: '*',\n      },\n    });\n    httpServer.listen(PORT, () => {\n      done();\n    });\n  });\n\n  afterEach(() => {\n    testResult = undefined;\n\n    Container.reset();\n    wsClient.close();\n    wsClient = null;\n    socketControllers = null;\n    return new Promise(resolve => {\n      if (wsApp)\n        return wsApp.close(() => {\n          resolve(null);\n        });\n      resolve(null);\n    });\n  });\n\n  it('Socket.io instance is retrieved correctly', async () => {\n    @SocketController('/string')\n    @Service()\n    class TestController {\n      @OnConnect()\n      connected(@ConnectedSocket() socket: Socket, @SocketIO() socketIO: Server) {\n        testResult = socketIO;\n        socket.emit('connected');\n      }\n    }\n\n    socketControllers = new SocketControllers({\n      io: wsApp,\n      container: Container,\n      controllers: [TestController],\n    });\n    wsClient = io(PATH_FOR_CLIENT + '/string', { reconnection: false, timeout: 5000, forceNew: true });\n\n    await waitForEvent(wsClient, 'connected');\n    expect(socketControllers.io).toEqual(testResult);\n  });\n});\n"
  },
  {
    "path": "test/functional/socket-message-ack.spec.ts",
    "content": "import { createServer, Server as HttpServer } from 'http';\nimport { Server } from 'socket.io';\nimport { io, Socket } from 'socket.io-client';\nimport { SocketControllers } from '../../src/SocketControllers';\nimport { Container, Service } from 'typedi';\nimport { SocketController } from '../../src/decorators/SocketController';\nimport { OnConnect } from '../../src/decorators/OnConnect';\nimport { ConnectedSocket } from '../../src/decorators/ConnectedSocket';\nimport { waitForEvent } from '../utilities/waitForEvent';\nimport { MessageAck, MessageBody, OnMessage, SocketId } from '../../src';\n\ndescribe('MessageAck', () => {\n  const PORT = 8080;\n  const PATH_FOR_CLIENT = `ws://localhost:${PORT}`;\n\n  let httpServer: HttpServer;\n  let wsApp: Server;\n  let wsClient: Socket;\n  let testResult;\n  let testAckResult;\n  let socketControllers: SocketControllers;\n\n  beforeEach(done => {\n    httpServer = createServer();\n    wsApp = new Server(httpServer, {\n      cors: {\n        origin: '*',\n      },\n    });\n    httpServer.listen(PORT, () => {\n      done();\n    });\n  });\n\n  afterEach(() => {\n    testResult = undefined;\n    testAckResult = undefined;\n\n    Container.reset();\n    wsClient.close();\n    wsClient = null;\n    socketControllers = null;\n    return new Promise(resolve => {\n      if (wsApp)\n        return wsApp.close(() => {\n          resolve(null);\n        });\n      resolve(null);\n    });\n  });\n\n  it('Event ack is retrieved correctly', async () => {\n    @SocketController('/string')\n    @Service()\n    class TestController {\n      @OnConnect()\n      connected(@ConnectedSocket() socket: Socket, @SocketId() socketId: string) {\n        testResult = socketId;\n        socket.emit('connected');\n      }\n\n      @OnMessage('test')\n      test(@MessageBody() data: any, @ConnectedSocket() socket: Socket, @MessageAck() ack: Function) {\n        testResult = data;\n        testAckResult = ack;\n        socket.emit('return');\n      }\n\n      @OnMessage('test2')\n      test2(\n        @MessageAck() ack: Function,\n        @MessageBody({ index: 1 }) data1: any,\n        @MessageBody({ index: 0 }) data0: any,\n        @ConnectedSocket() socket: Socket\n      ) {\n        testResult = { data1, data0 };\n        ack?.('test ack2');\n        socket.emit('return2');\n      }\n\n      @OnMessage('test3')\n      test3(\n        @ConnectedSocket() socket: Socket,\n        @MessageAck() ack: Function,\n        @MessageBody({ index: 1 }) data1: any,\n        @MessageBody({ index: 0 }) data0: any\n      ) {\n        testResult = { data1, data0 };\n        testAckResult = ack;\n        socket.emit('return3');\n      }\n    }\n\n    socketControllers = new SocketControllers({\n      io: wsApp,\n      container: Container,\n      controllers: [TestController],\n    });\n    wsClient = io(PATH_FOR_CLIENT + '/string', { reconnection: false, timeout: 5000, forceNew: true });\n\n    await waitForEvent(wsClient, 'connected');\n\n    const ack = (ack: any) => (testAckResult = ack);\n\n    wsClient.emit('test', 'test data');\n    await waitForEvent(wsClient, 'return');\n    expect(testResult).toEqual('test data');\n    expect(testAckResult).toBeNull();\n\n    wsClient.emit('test2', 'test data 0', 'test data 1', 'test data 2', ack);\n    await waitForEvent(wsClient, 'return2');\n    expect(testResult).toEqual({ data0: 'test data 0', data1: 'test data 1' });\n    expect(testAckResult).toEqual('test ack2');\n\n    // ack should be the last parameter\n    wsClient.emit('test3', 'test data 0', 'test data 1', ack, 'test data 2');\n    await waitForEvent(wsClient, 'return3');\n    expect(testResult).toEqual({ data0: 'test data 0', data1: 'test data 1' });\n    expect(testAckResult).toBeNull();\n  });\n});\n"
  },
  {
    "path": "test/functional/socket-message-body.spec.ts",
    "content": "import { createServer, Server as HttpServer } from 'http';\nimport { Server } from 'socket.io';\nimport { io, Socket } from 'socket.io-client';\nimport { SocketControllers } from '../../src/SocketControllers';\nimport { Container, Service } from 'typedi';\nimport { SocketController } from '../../src/decorators/SocketController';\nimport { OnConnect } from '../../src/decorators/OnConnect';\nimport { ConnectedSocket } from '../../src/decorators/ConnectedSocket';\nimport { waitForEvent } from '../utilities/waitForEvent';\nimport { MessageBody, OnMessage, SocketId } from '../../src';\n\ndescribe('MessageBody', () => {\n  const PORT = 8080;\n  const PATH_FOR_CLIENT = `ws://localhost:${PORT}`;\n\n  let httpServer: HttpServer;\n  let wsApp: Server;\n  let wsClient: Socket;\n  let testResult;\n  let socketControllers: SocketControllers;\n\n  beforeEach(done => {\n    httpServer = createServer();\n    wsApp = new Server(httpServer, {\n      cors: {\n        origin: '*',\n      },\n    });\n    httpServer.listen(PORT, () => {\n      done();\n    });\n  });\n\n  afterEach(() => {\n    testResult = undefined;\n\n    Container.reset();\n    wsClient.close();\n    wsClient = null;\n    socketControllers = null;\n    return new Promise(resolve => {\n      if (wsApp)\n        return wsApp.close(() => {\n          resolve(null);\n        });\n      resolve(null);\n    });\n  });\n\n  it('Event body is retrieved correctly', async () => {\n    @SocketController('/string')\n    @Service()\n    class TestController {\n      @OnConnect()\n      connected(@ConnectedSocket() socket: Socket, @SocketId() socketId: string) {\n        testResult = socketId;\n        socket.emit('connected');\n      }\n\n      @OnMessage('test')\n      test(@MessageBody() data: any, @ConnectedSocket() socket: Socket) {\n        testResult = data;\n        socket.emit('return');\n      }\n\n      @OnMessage('test2')\n      test2(\n        @MessageBody({ index: 1 }) data1: any,\n        @MessageBody({ index: 0 }) data0: any,\n        @ConnectedSocket() socket: Socket\n      ) {\n        testResult = { data1, data0 };\n        socket.emit('return2');\n      }\n    }\n\n    socketControllers = new SocketControllers({\n      io: wsApp,\n      container: Container,\n      controllers: [TestController],\n    });\n    wsClient = io(PATH_FOR_CLIENT + '/string', { reconnection: false, timeout: 5000, forceNew: true });\n\n    await waitForEvent(wsClient, 'connected');\n\n    wsClient.emit('test', 'test data');\n    await waitForEvent(wsClient, 'return');\n    expect(testResult).toEqual('test data');\n\n    wsClient.emit('test2', 'test data 0', 'test data 1', 'test data 2', ack => {\n      console.log(ack);\n    });\n    await waitForEvent(wsClient, 'return2');\n    expect(testResult).toEqual({ data0: 'test data 0', data1: 'test data 1' });\n  });\n});\n"
  },
  {
    "path": "test/functional/socket-query-param.spec.ts",
    "content": "import { createServer, Server as HttpServer } from 'http';\nimport { Server } from 'socket.io';\nimport { io, Socket } from 'socket.io-client';\nimport { ConnectedSocket, OnConnect, SocketController, SocketControllers, SocketQueryParam } from '../../src';\nimport { Container, Service } from 'typedi';\nimport { waitForEvent } from '../utilities/waitForEvent';\n\ndescribe('SocketQueryParam', () => {\n  const PORT = 8080;\n  const PATH_FOR_CLIENT = `ws://localhost:${PORT}`;\n\n  let httpServer: HttpServer;\n  let wsApp: Server;\n  let wsClient: Socket;\n  let testResult;\n  let socketControllers: SocketControllers;\n\n  beforeEach(done => {\n    httpServer = createServer();\n    wsApp = new Server(httpServer, {\n      cors: {\n        origin: '*',\n      },\n    });\n    httpServer.listen(PORT, () => {\n      done();\n    });\n  });\n\n  afterEach(() => {\n    testResult = undefined;\n\n    Container.reset();\n    wsClient.close();\n    wsClient = null;\n    socketControllers = null;\n    return new Promise(resolve => {\n      if (wsApp)\n        return wsApp.close(() => {\n          resolve(null);\n        });\n      resolve(null);\n    });\n  });\n\n  it('Socket query param is retrieved correctly', async () => {\n    @SocketController()\n    @Service()\n    class TestController {\n      @OnConnect()\n      connected(@ConnectedSocket() socket: Socket, @SocketQueryParam('testParam') parameter: string) {\n        testResult = parameter;\n        socket.emit('connected');\n      }\n    }\n\n    socketControllers = new SocketControllers({\n      io: wsApp,\n      container: Container,\n      controllers: [TestController],\n    });\n    wsClient = io(PATH_FOR_CLIENT + '?testParam=testValue', { reconnection: false, timeout: 5000, forceNew: true });\n\n    await waitForEvent(wsClient, 'connected');\n    expect(testResult).toEqual('testValue');\n  });\n});\n"
  },
  {
    "path": "test/functional/socket-request.spec.ts",
    "content": "import { createServer, Server as HttpServer } from 'http';\nimport { Server } from 'socket.io';\nimport { io, Socket } from 'socket.io-client';\nimport {\n  ConnectedSocket,\n  OnConnect,\n  SocketController,\n  SocketControllers,\n  SocketQueryParam,\n  SocketRequest,\n} from '../../src';\nimport { Container, Service } from 'typedi';\nimport { waitForEvent } from '../utilities/waitForEvent';\n\ndescribe('SocketRequest', () => {\n  const PORT = 8080;\n  const PATH_FOR_CLIENT = `ws://localhost:${PORT}`;\n\n  let httpServer: HttpServer;\n  let wsApp: Server;\n  let wsClient: Socket;\n  let testResult;\n  let socketControllers: SocketControllers;\n\n  beforeEach(done => {\n    httpServer = createServer();\n    wsApp = new Server(httpServer, {\n      cors: {\n        origin: '*',\n      },\n    });\n    httpServer.listen(PORT, () => {\n      done();\n    });\n  });\n\n  afterEach(() => {\n    testResult = undefined;\n\n    Container.reset();\n    wsClient.close();\n    wsClient = null;\n    socketControllers = null;\n    return new Promise(resolve => {\n      if (wsApp)\n        return wsApp.close(() => {\n          resolve(null);\n        });\n      resolve(null);\n    });\n  });\n\n  it('Socket request is retrieved correctly', async () => {\n    @SocketController()\n    @Service()\n    class TestController {\n      @OnConnect()\n      connected(@ConnectedSocket() socket: Socket, @SocketRequest() request: any) {\n        testResult = request;\n        socket.emit('connected');\n      }\n    }\n\n    socketControllers = new SocketControllers({\n      io: wsApp,\n      container: Container,\n      controllers: [TestController],\n    });\n    wsClient = io(PATH_FOR_CLIENT + '?testParam=testValue', { reconnection: false, timeout: 5000, forceNew: true });\n\n    await waitForEvent(wsClient, 'connected');\n    expect(testResult.url).toContain('/socket.io/?testParam=testValue&EIO=');\n  });\n});\n"
  },
  {
    "path": "test/functional/socket-rooms.spec.ts",
    "content": "import { createServer, Server as HttpServer } from 'http';\nimport { Server } from 'socket.io';\nimport { io, Socket } from 'socket.io-client';\nimport { SocketControllers } from '../../src/SocketControllers';\nimport { Container, Service } from 'typedi';\nimport { SocketController } from '../../src/decorators/SocketController';\nimport { OnConnect } from '../../src/decorators/OnConnect';\nimport { ConnectedSocket } from '../../src/decorators/ConnectedSocket';\nimport { waitForEvent } from '../utilities/waitForEvent';\nimport { SocketRooms } from '../../src';\n\ndescribe('SocketRooms', () => {\n  const PORT = 8080;\n  const PATH_FOR_CLIENT = `ws://localhost:${PORT}`;\n\n  let httpServer: HttpServer;\n  let wsApp: Server;\n  let wsClient: Socket;\n  let testResult;\n  let socketControllers: SocketControllers;\n\n  beforeEach(done => {\n    httpServer = createServer();\n    wsApp = new Server(httpServer, {\n      cors: {\n        origin: '*',\n      },\n    });\n    httpServer.listen(PORT, () => {\n      done();\n    });\n  });\n\n  afterEach(() => {\n    testResult = undefined;\n\n    Container.reset();\n    wsClient.close();\n    wsClient = null;\n    socketControllers = null;\n    return new Promise(resolve => {\n      if (wsApp)\n        return wsApp.close(() => {\n          resolve(null);\n        });\n      resolve(null);\n    });\n  });\n\n  it('Socket rooms set is retrieved correctly', async () => {\n    @SocketController('/string')\n    @Service()\n    class TestController {\n      @OnConnect()\n      connected(@ConnectedSocket() socket: Socket, @SocketRooms() rooms: any) {\n        testResult = rooms;\n        socket.emit('connected');\n      }\n    }\n\n    socketControllers = new SocketControllers({\n      io: wsApp,\n      container: Container,\n      controllers: [TestController],\n    });\n    wsClient = io(PATH_FOR_CLIENT + '/string', { reconnection: false, timeout: 5000, forceNew: true });\n\n    await waitForEvent(wsClient, 'connected');\n    expect([...(testResult as Set<string>).keys()][0]).toEqual(wsClient.id);\n  });\n});\n"
  },
  {
    "path": "test/functional/use-interceptor.spec.ts",
    "content": "import { createServer, Server as HttpServer } from 'http';\nimport { Server } from 'socket.io';\nimport { io, Socket } from 'socket.io-client';\nimport { SocketControllers } from '../../src/SocketControllers';\nimport { Container, Service } from 'typedi';\nimport { SocketController } from '../../src/decorators/SocketController';\nimport { OnConnect } from '../../src/decorators/OnConnect';\nimport { ConnectedSocket } from '../../src/decorators/ConnectedSocket';\nimport { waitForEvent } from '../utilities/waitForEvent';\nimport { EmitOnSuccess, MessageBody, OnMessage, SocketEventType } from '../../src';\nimport { UseInterceptor } from '../../src/decorators/UseInterceptor';\nimport { InterceptorInterface } from '../../src/types/InterceptorInterface';\nimport { SocketEventContext } from '../../src/types/SocketEventContext';\n\ndescribe('UseInterceptor', () => {\n  const PORT = 8080;\n  const PATH_FOR_CLIENT = `ws://localhost:${PORT}`;\n\n  let httpServer: HttpServer;\n  let wsApp: Server;\n  let wsClient: Socket;\n  let testResult = [];\n  let socketControllers: SocketControllers;\n\n  beforeEach(done => {\n    httpServer = createServer();\n    wsApp = new Server(httpServer, {\n      cors: {\n        origin: '*',\n      },\n    });\n    httpServer.listen(PORT, () => {\n      done();\n    });\n  });\n\n  afterEach(() => {\n    testResult = [];\n\n    Container.reset();\n    wsClient.close();\n    wsClient = null;\n    socketControllers = null;\n    return new Promise(resolve => {\n      if (wsApp)\n        return wsApp.close(() => {\n          resolve(null);\n        });\n      resolve(null);\n    });\n  });\n\n  it('interceptor should be executed in the correct order', async () => {\n    @Service()\n    class testInterceptor implements InterceptorInterface {\n      use(ctx: SocketEventContext, next: any) {\n        testResult.push('testInterceptor start');\n        const resp = next();\n        testResult.push('testInterceptor end');\n        return resp;\n      }\n    }\n\n    const plain: InterceptorInterface = {\n      use: (ctx: SocketEventContext, next: () => any) => {\n        testResult.push('plain start');\n        const resp = next();\n        testResult.push('plain end');\n        return resp;\n      },\n    };\n\n    @SocketController('/string')\n    @Service()\n    @UseInterceptor(plain)\n    class TestController {\n      @OnConnect()\n      connected(@ConnectedSocket() socket: Socket) {\n        socket.emit('connected');\n      }\n\n      @OnMessage('test')\n      @UseInterceptor(testInterceptor)\n      @EmitOnSuccess('finished')\n      test() {\n        testResult.push('action');\n        return 'test';\n      }\n    }\n\n    socketControllers = new SocketControllers({\n      io: wsApp,\n      container: Container,\n      controllers: [TestController],\n    });\n    wsClient = io(PATH_FOR_CLIENT + '/string', { reconnection: false, timeout: 5000, forceNew: true });\n\n    await waitForEvent(wsClient, 'connected');\n    wsClient.emit('test');\n    const response = await waitForEvent(wsClient, 'finished');\n    expect(testResult).toEqual([\n      'plain start',\n      'plain end',\n      'plain start',\n      'testInterceptor start',\n      'action',\n      'testInterceptor end',\n      'plain end',\n    ]);\n    expect(response).toEqual('test');\n  });\n\n  it('interceptor should be able to skip further actions', async () => {\n    @Service()\n    class testInterceptor implements InterceptorInterface {\n      use(ctx: SocketEventContext, next: any) {\n        testResult.push('testInterceptor start');\n        const response = next();\n        testResult.push('testInterceptor end');\n        return response;\n      }\n    }\n\n    const plain: InterceptorInterface = {\n      use: (ctx: SocketEventContext, next: () => any) => {\n        testResult.push('plain start');\n\n        if (ctx.eventType === SocketEventType.CONNECT) {\n          next();\n        }\n\n        testResult.push('plain end');\n        return 'plain response';\n      },\n    };\n\n    @SocketController('/string')\n    @Service()\n    @UseInterceptor(plain)\n    class TestController {\n      @OnConnect()\n      connected(@ConnectedSocket() socket: Socket) {\n        socket.emit('connected');\n      }\n\n      @OnMessage('test')\n      @UseInterceptor(testInterceptor)\n      @EmitOnSuccess('finished')\n      test() {\n        testResult.push('action');\n        return 'test';\n      }\n    }\n\n    socketControllers = new SocketControllers({\n      io: wsApp,\n      container: Container,\n      controllers: [TestController],\n    });\n    wsClient = io(PATH_FOR_CLIENT + '/string', { reconnection: false, timeout: 5000, forceNew: true });\n\n    await waitForEvent(wsClient, 'connected');\n    wsClient.emit('test');\n    const response = await waitForEvent(wsClient, 'finished');\n    expect(testResult).toEqual(['plain start', 'plain end', 'plain start', 'plain end']);\n    expect(response).toEqual('plain response');\n  });\n\n  it('interceptor should be able to mutate the context', async () => {\n    @Service()\n    class testInterceptor implements InterceptorInterface {\n      use(ctx: SocketEventContext, next: any) {\n        testResult.push(ctx.messageArgs?.[0]);\n        ctx.messageArgs = ['testInterceptor'];\n        return next();\n      }\n    }\n\n    const plain: InterceptorInterface = {\n      use: (ctx: SocketEventContext, next: () => any) => {\n        testResult.push(ctx.messageArgs?.[0]);\n        ctx.messageArgs = ['plain'];\n        return next();\n      },\n    };\n\n    @SocketController('/string')\n    @Service()\n    @UseInterceptor(plain)\n    class TestController {\n      @OnConnect()\n      connected(@ConnectedSocket() socket: Socket, @MessageBody() message: any) {\n        testResult.push(message);\n        socket.emit('connected');\n      }\n\n      @OnMessage('test')\n      @UseInterceptor(testInterceptor)\n      @EmitOnSuccess('finished')\n      test(@MessageBody() message: any) {\n        testResult.push(message);\n        return 'test';\n      }\n    }\n\n    socketControllers = new SocketControllers({\n      io: wsApp,\n      container: Container,\n      controllers: [TestController],\n    });\n    wsClient = io(PATH_FOR_CLIENT + '/string', { reconnection: false, timeout: 5000, forceNew: true });\n\n    await waitForEvent(wsClient, 'connected');\n    wsClient.emit('test', 'my body');\n    const response = await waitForEvent(wsClient, 'finished');\n    expect(testResult).toEqual([undefined, 'plain', 'my body', 'plain', 'testInterceptor']);\n    expect(response).toEqual('test');\n  });\n});\n"
  },
  {
    "path": "test/utilities/testSocketConnection.ts",
    "content": "import socketio from 'socket.io-client';\n\nexport async function testConnection(path: string) {\n  return await new Promise<number>((resolve, reject) => {\n    const socket = socketio(path, { reconnection: false, timeout: 5000 });\n    socket.on('connect', () => socket.disconnect());\n    socket.on('connect_error', reject);\n    socket.on('disconnect', () => {\n      resolve(0);\n    });\n  });\n}\n"
  },
  {
    "path": "test/utilities/waitForEvent.ts",
    "content": "import { Socket } from 'socket.io-client';\nimport { Server } from 'socket.io';\n\nexport const waitForEvent = (socket: Socket | Server, event: string): Promise<unknown> => {\n  return new Promise(resolve => {\n    socket.on(event, data => {\n      resolve(data);\n    });\n  });\n};\n"
  },
  {
    "path": "test/utilities/waitForTime.ts",
    "content": "export const waitForTime = (time: number): Promise<unknown> => {\n  return new Promise(resolve => {\n    setTimeout(() => {\n      resolve(null);\n    }, time);\n  });\n};\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"module\": \"commonjs\",\n    \"target\": \"es2018\",\n    \"lib\": [\"es2018\"],\n    \"outDir\": \"build\",\n    \"rootDir\": \"./src\",\n    \"strict\": true,\n    \"sourceMap\": true,\n    \"esModuleInterop\": true,\n    \"experimentalDecorators\": true,\n    \"emitDecoratorMetadata\": true,\n    \"forceConsistentCasingInFileNames\": true\n  },\n  \"exclude\": [\"node_modules\", \"sample\", \"**/*.spec.ts\", \"testing/**\", \"test/**\"],\n}\n"
  },
  {
    "path": "tsconfig.prod.json",
    "content": "{\n  \"extends\": \"./tsconfig.json\",\n  \"compilerOptions\": {\n    \"strict\": false,\n    \"sourceMap\": true,\n    \"removeComments\": false,\n    \"declaration\": true,\n  },\n}\n"
  },
  {
    "path": "tsconfig.spec.json",
    "content": "{\n  \"extends\": \"./tsconfig.json\",\n  \"compilerOptions\": {\n    \"strict\": false,\n    \"strictPropertyInitialization\": false,\n    \"sourceMap\": true,\n    \"inlineSourceMap\": true,\n    \"removeComments\": true,\n    \"noImplicitAny\": false,\n    \"experimentalDecorators\": true,\n    \"emitDecoratorMetadata\": true,\n    \"rootDir\": \"./\"\n  },\n  \"exclude\": [\"node_modules\"]\n}\n"
  }
]