{"_id":"@byteholic/nelysia","name":"@byteholic/nelysia","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@byteholic/nelysia","version":"0.1.0","description":"Native Elysia modular framework","author":{"name":"byteholic"},"license":"MIT","keywords":["elysia","bun","decorator","di","plugin","typescript","websocket","framework"],"type":"module","exports":{".":{"import":"./dist/index.js","require":"./dist/index.cjs","types":"./dist/index.d.ts"}},"main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","scripts":{"build":"bun run build.ts","dev":"bun run --watch demo/main.ts","start":"bun run demo/main.ts","prepublishOnly":"bun run build"},"peerDependencies":{"elysia":">=1.0.0"},"devDependencies":{"bun-types":"latest","elysia":"latest","@elysiajs/swagger":"latest"},"dependencies":{"reflect-metadata":"^0.2.2"},"_id":"@byteholic/nelysia@0.1.0","_nodeVersion":"25.2.0","_npmVersion":"11.6.2","dist":{"integrity":"sha512-XCjBKCD4nQqzRhMCHfKngbpJCN5/oBKXJr89kTord7S7uXJzMF8ndmYw5/rkZOim3nTnKOAtx3j6hILLuSlRFQ==","shasum":"53b1492f964bee97a7f541a071503d8eb8b36da2","tarball":"https://registry.npmjs.org/@byteholic/nelysia/-/nelysia-0.1.0.tgz","fileCount":20,"unpackedSize":128611,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIEXhlCOB5i++5wkJGYvsLQs3r3R4oxgu2bUKHtJZpj9YAiBxt3MWrFjeoG1+PZL/Erdkc7IV/DyxF6GBtfTpMGLCUg=="}]},"_npmUser":{"name":"byteholic","email":"yamlengine@gmail.com"},"directories":{},"maintainers":[{"name":"byteholic","email":"yamlengine@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/nelysia_0.1.0_1772094040158_0.4963441589491884"},"_hasShrinkwrap":false}},"time":{"created":"2026-02-26T08:20:40.046Z","0.1.0":"2026-02-26T08:20:40.305Z","modified":"2026-02-26T08:20:40.491Z"},"maintainers":[{"name":"byteholic","email":"yamlengine@gmail.com"}],"description":"Native Elysia modular framework","keywords":["elysia","bun","decorator","di","plugin","typescript","websocket","framework"],"author":{"name":"byteholic"},"license":"MIT","readme":"# Nelysia\n\n> NestJS-style modular framework built natively on [ElysiaJS](https://elysiajs.com) + Bun.\n\n[![npm version](https://img.shields.io/npm/v/nelysia)](https://www.npmjs.com/package/nelysia)\n[![bun](https://img.shields.io/badge/runtime-bun-black)](https://bun.sh)\n[![license](https://img.shields.io/npm/l/nelysia)](./LICENSE)\n\n---\n\n## Features\n\n- 🧩 **`@Plugin`** — NestJS-style `@Module` powered by native Elysia `.use()`\n- 💉 **`@Service` + `@Inject`** — Lightweight DI container\n- 🌐 **`@Controller` + HTTP verbs** — `@Get`, `@Post`, `@Put`, `@Patch`, `@Delete`\n- 🔌 **`@WsController` + `@Ws`** — Native WebSocket support with pub/sub\n- 🎯 **Context decorators** — `@Body`, `@Query`, `@Params`, `@Path`, `@Headers`, `@Cookie`\n- 🛡️ **`@BeforeHandle`** — Per-route guards\n- 📋 **`@Schema`** — Elysia `t` validation per route\n- 📖 **`@Detail`** — OpenAPI / Swagger metadata\n- ⚙️ **`@Macro`** — Elysia `.macro()` as class-based decorators\n- 🔁 **Lifecycle hooks** — `onRequest`, `onError`, `onAfterResponse`, etc. in `@Plugin`\n\n---\n\n## Install\n\n```bash\nbun add @byteholic/nelysia elysia\nbun add -d bun-types\n```\n\n### `tsconfig.json` requirements\n\n```json\n{\n  \"compilerOptions\": {\n    \"experimentalDecorators\": true,\n    \"strict\": true\n  }\n}\n```\n\n---\n\n## Quick Start\n\n```typescript\n// main.ts\nimport { Elysia } from \"elysia\";\nimport { buildPlugin } from \"@byteholic/nelysia\";\nimport { AppPlugin } from \"./app.plugin\";\n\nnew Elysia()\n  .use(buildPlugin(AppPlugin))\n  .listen(3000);\n```\n\n---\n\n## Core Concepts\n\n### `@Service` — Injectable class\n\nRegister a class in the DI container. Pass deps as an explicit array.\n\n```typescript\nimport { Service } from \"@byteholic/nelysia\";\n\n@Service()\nexport class UserRepo {\n  private users = [{ id: 1, name: \"Alice\" }];\n  findAll() { return this.users; }\n}\n\n@Service([UserRepo])\nexport class UsersService {\n  constructor(private repo: UserRepo) {}\n  all() { return this.repo.findAll(); }\n}\n```\n\n### `@Inject` — Per-parameter injection\n\nUse on constructor parameters instead of (or alongside) `@Service([])`.\n\n```typescript\nimport { Inject, Controller, Get } from \"@byteholic/nelysia\";\n\n@Controller(\"/users\")\nexport class UsersController {\n  constructor(@Inject(UsersService) private svc: UsersService) {}\n\n  @Get(\"/\")\n  getAll() { return this.svc.all(); }\n}\n```\n\nBoth styles are equivalent:\n\n```typescript\n// Style A — class-level deps\n@Service([UsersService])\n@Controller(\"/users\")\nexport class UsersController {\n  constructor(private svc: UsersService) {}\n}\n\n// Style B — per-param @Inject\n@Controller(\"/users\")\nexport class UsersController {\n  constructor(@Inject(UsersService) private svc: UsersService) {}\n}\n```\n\n### `@Plugin` — Feature module\n\nComposes services, controllers, sub-plugins, guards and hooks.\n\n```typescript\nimport { Plugin } from \"@byteholic/nelysia\";\n\n@Plugin({\n  name:        \"users\",\n  imports:     [OtherPlugin],          // sub-plugins (shared DI)\n  services:    [UserRepo, UsersService],\n  controllers: [UsersController],\n  wsControllers: [UsersWsController],  // WebSocket controllers\n  guard: {\n    beforeHandle: requireAuth,         // applied to ALL routes\n  },\n  hooks: {\n    onRequest: ({ request }) =>\n      console.log(request.method, request.url),\n    onError: ({ error }) =>\n      console.error(error.message),\n  },\n})\nexport class UsersPlugin {}\n```\n\nThen in `main.ts`:\n\n```typescript\nnew Elysia().use(buildPlugin(UsersPlugin)).listen(3000);\n```\n\n---\n\n## HTTP Routes\n\n```typescript\nimport { t } from \"elysia\";\nimport {\n  Controller, Get, Post, Delete,\n  Schema, BeforeHandle, Detail,\n  Body, Path, Query, Set,\n} from \"@byteholic/nelysia\";\n\nconst adminOnly = ({ headers, status }: any) => {\n  if (headers[\"x-role\"] !== \"admin\") return status(403);\n};\n\n@Controller(\"/users\")\nexport class UsersController {\n  constructor(@Inject(UsersService) private svc: UsersService) {}\n\n  @Detail({ tags: [\"Users\"], summary: \"List users\" })\n  @Schema({ query: t.Object({ page: t.Optional(t.Numeric({ default: 1 })) }) })\n  @Get(\"/\")\n  getAll(@Query() query: { page?: number }) {\n    return this.svc.all();\n  }\n\n  @Detail({ tags: [\"Users\"], summary: \"Get user by id\" })\n  @Schema({ params: t.Object({ id: t.Numeric() }) })\n  @Get(\"/:id\")\n  getOne(@Path(\"id\") id: number, @Set() set: any) {\n    const user = this.svc.byId(id);\n    if (!user) { set.status = 404; return { error: \"Not found\" }; }\n    return user;\n  }\n\n  @Detail({ tags: [\"Users\"], summary: \"Create user\" })\n  @Schema({ body: t.Object({ name: t.String({ minLength: 1 }) }) })\n  @Post(\"/\")\n  create(@Body() body: { name: string }, @Set() set: any) {\n    set.status = 201;\n    return this.svc.create(body);\n  }\n\n  @BeforeHandle(adminOnly)\n  @Schema({ params: t.Object({ id: t.Numeric() }) })\n  @Delete(\"/:id\")\n  remove(@Path(\"id\") id: number) {\n    return this.svc.remove(id);\n  }\n}\n```\n\n---\n\n## WebSocket\n\n```typescript\nimport { t } from \"elysia\";\nimport { WsController, Ws, WsSchema, WsHandlers, Inject } from \"@byteholic/nelysia\";\n\n@WsController()\nexport class ChatWsController {\n  constructor(@Inject(ChatService) private chat: ChatService) {}\n\n  @WsSchema({\n    body:   t.Object({ user: t.String(), text: t.String() }),\n    params: t.Object({ room: t.String() }),\n  })\n  @Ws(\"/chat/:room\")\n  chat(): WsHandlers {\n    const svc = this.chat;\n    return {\n      open(ws) {\n        ws.subscribe(ws.data.params.room);\n        ws.send(\"Welcome!\");\n      },\n      message(ws, body) {\n        const msg = svc.format(body.user, body.text, ws.data.params.room);\n        svc.save(msg);\n        ws.publish(ws.data.params.room, JSON.stringify(msg));\n      },\n      close(ws) {\n        ws.unsubscribe(ws.data.params.room);\n      },\n    };\n  }\n}\n\n// Register in @Plugin:\n@Plugin({\n  name:          \"chat\",\n  services:      [ChatService],\n  wsControllers: [ChatWsController],\n})\nexport class ChatPlugin {}\n```\n\nConnect with:\n\n```bash\nbun add -g wscat\nwscat -c \"ws://localhost:3000/chat/general\"\n# send: {\"user\":\"Alice\",\"text\":\"Hello!\"}\n```\n\n---\n\n## Context Decorators\n\n| Decorator | Extracts |\n|---|---|\n| `@Ctx()` | Full Elysia context |\n| `@Body()` | `ctx.body` |\n| `@Query()` | `ctx.query` (all) |\n| `@Params()` | `ctx.params` (all) |\n| `@Headers()` | `ctx.headers` |\n| `@Cookie()` | `ctx.cookie` |\n| `@Set()` | `ctx.set` (status/headers) |\n| `@Path(\"id\")` | `ctx.params.id` (single) |\n| `@Q(\"page\")` | `ctx.query.page` (single) |\n\n---\n\n## Lifecycle Hooks\n\nAvailable in `hooks:` inside `@Plugin`:\n\n| Hook | Fires when |\n|---|---|\n| `onRequest` | Every incoming request |\n| `onParse` | Body parsing |\n| `onTransform` | Before validation |\n| `onBeforeHandle` | Before route handler |\n| `onAfterHandle` | After route handler |\n| `onAfterResponse` | After response is sent |\n| `onError` | On any error |\n| `mapResponse` | Transform response before send |\n\n---\n\n## Macro\n\nWraps Elysia's native `.macro()` API:\n\n```typescript\nimport { Macro, MacroHandler } from \"@byteholic/nelysia\";\n\n@Macro({ name: \"auth\" })\nexport class AuthMacro {\n  @MacroHandler(\"roles\")\n  roles(required: string[]) {\n    return {\n      beforeHandle({ cookie, status }: any) {\n        if (!required.includes(cookie.session?.value?.role))\n          return status(403);\n      },\n    };\n  }\n}\n\n// Register in @Plugin:\n@Plugin({ macros: [AuthMacro], controllers: [AdminController] })\nexport class AdminPlugin {}\n```\n\n---\n\n## Full Decorator Reference\n\n### DI\n| Decorator | Description |\n|---|---|\n| `@Service(deps?)` | Register class in DI container |\n| `@Inject(Token)` | Per-parameter dependency injection |\n\n### Plugin\n| Decorator / Function | Description |\n|---|---|\n| `@Plugin(meta)` | Define a feature module |\n| `buildPlugin(MyPlugin)` | Convert to native Elysia instance |\n\n### HTTP\n| Decorator | Description |\n|---|---|\n| `@Controller(prefix)` | HTTP controller class |\n| `@Get / @Post / @Put / @Patch / @Delete / @Options / @Head / @All` | Route method |\n| `@Schema({ body, query, params, headers, response })` | Elysia `t` validation |\n| `@BeforeHandle(fn)` | Per-route guard / hook |\n| `@AfterHandle(fn)` | Per-route after hook |\n| `@OnError(fn)` | Per-route error handler |\n| `@Detail({ tags, summary })` | OpenAPI metadata |\n\n### WebSocket\n| Decorator | Description |\n|---|---|\n| `@WsController(prefix?)` | WebSocket controller class |\n| `@Ws(path?)` | WebSocket endpoint (returns `WsHandlers`) |\n| `@WsSchema({ body, query, params })` | WS message validation |\n\n---\n\n## Project Structure\n\n```\nsrc/\n  core/\n    container.ts   — DI Container\n    service.ts     — @Service, @Inject\n    controller.ts  — @Controller, @Get, @Post, @Schema ...\n    context.ts     — @Body, @Query, @Path, @Headers ...\n    websocket.ts   — @WsController, @Ws, @WsSchema\n    plugin.ts      — @Plugin, buildPlugin()\n    macro.ts       — @Macro, @MacroHandler\n  index.ts         — Public API barrel\n```\n\n---\n\n## With Swagger\n\n```bash\nbun add @elysiajs/swagger\n```\n\n```typescript\nimport { swagger } from \"@elysiajs/swagger\";\n\nnew Elysia()\n  .use(swagger({ documentation: { info: { title: \"My API\", version: \"1.0.0\" } } }))\n  .use(buildPlugin(AppPlugin))\n  .listen(3000);\n\n// Docs at: http://localhost:3000/swagger\n```\n\n---\n\n## Requirements\n\n- [Bun](https://bun.sh) ≥ 1.0\n- [ElysiaJS](https://elysiajs.com) ≥ 1.0\n- `\"experimentalDecorators\": true` in `tsconfig.json`\n\n---\n\n## License\n\nMIT © byteholic","readmeFilename":"README.md","_rev":"1-adc5d53ca4176eab36fa6d82f43b9e02"}