{"_id":"@api-policy/elysia","_rev":"6-1458a1594f1bb6d874e1e049f7cf45f8","name":"@api-policy/elysia","dist-tags":{"latest":"3.1.1"},"versions":{"1.0.0":{"name":"@api-policy/elysia","version":"1.0.0","keywords":["elysia","api-policy","auth","permission","middleware","framework-adapter"],"author":{"name":"minhtaimc"},"license":"MIT","_id":"@api-policy/elysia@1.0.0","maintainers":[{"name":"minhtaimc","email":"minhtaimc@gmail.com"}],"dist":{"shasum":"31cb61ac3f84cdfd027f26a09c78e7dd9c5d9440","tarball":"https://registry.npmjs.org/@api-policy/elysia/-/elysia-1.0.0.tgz","fileCount":26,"integrity":"sha512-N02ZFeRtG5m4fVr7Ci6piPDMwRXoRXUYfLOsBC13wFb3YCkvy9leCZvVevKv8lOE5V3d+NVTY09LhVajlsUuiA==","signatures":[{"sig":"MEQCIFvEAzbPV/LnFibAP8enR0Lve/uEAR8Q5DFc+IrLnYoWAiBNsjTcXH0jIIHdtO6WSxl6mrD2mij9q6doMk1ve73leg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":71053},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"c7f2fd5117e9eddda25427c587be46fa837a4dae","scripts":{"dev":"tsc --watch","test":"vitest run","build":"tsc","clean":"rm -rf dist","typecheck":"tsc --noEmit","test:watch":"vitest"},"_npmUser":{"name":"minhtaimc","email":"minhtaimc@gmail.com"},"_npmVersion":"11.6.2","description":"Elysia adapter for API Policy. Auth plugin, permissions, routing.","directories":{},"_nodeVersion":"24.11.1","dependencies":{"jose":"^6.2.0","ts-micro-result":"^3.3.0","@sinclair/typebox":"^0.34.48","@api-policy/server":"workspace:*"},"_hasShrinkwrap":false,"devDependencies":{"elysia":"^1.4.27","vitest":"^4.0.18","typescript":"^5.9.3","@types/node":"^25.3.5"},"peerDependencies":{"elysia":">=1.4.0"},"_npmOperationalInternal":{"tmp":"tmp/elysia_1.0.0_1773068097964_0.6953220302627621","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"@api-policy/elysia","version":"1.0.1","keywords":["elysia","api-policy","auth","permission","middleware","framework-adapter"],"author":{"name":"minhtaimc"},"license":"MIT","_id":"@api-policy/elysia@1.0.1","maintainers":[{"name":"minhtaimc","email":"minhtaimc@gmail.com"}],"dist":{"shasum":"515abd1bf52befff5210cd98cc01fe5ae2b63213","tarball":"https://registry.npmjs.org/@api-policy/elysia/-/elysia-1.0.1.tgz","fileCount":26,"integrity":"sha512-ZV+6ZffBeFltBP3Bf5N4kh+LPBWBg45NO2dq+45UP6WyxCMVrsKFtcQjlrIM6afXM9QyVl7t16arZTpVWDBa+w==","signatures":[{"sig":"MEYCIQDnCeCiqclT48Vyed70erGC6P44oqKhm+u/CALb3tFdAwIhAJnt/ADMLqeOUY7BokPJUhL+h/oZSXHcGQyo5Gc7Z3Bv","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":71048},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"1dfb68d711990705804119c4cdce30bcd56a3656","scripts":{"dev":"tsc --watch","test":"vitest run","build":"tsc","clean":"rm -rf dist","typecheck":"tsc --noEmit","test:watch":"vitest"},"_npmUser":{"name":"minhtaimc","email":"minhtaimc@gmail.com"},"_npmVersion":"11.6.2","description":"Elysia adapter for API Policy. Auth plugin, permissions, routing.","directories":{},"_nodeVersion":"24.11.1","dependencies":{"jose":"^6.2.0","ts-micro-result":"^3.3.0","@sinclair/typebox":"^0.34.48","@api-policy/server":"^1.0.1"},"_hasShrinkwrap":false,"devDependencies":{"elysia":"^1.4.27","vitest":"^4.0.18","typescript":"^5.9.3","@types/node":"^25.3.5"},"peerDependencies":{"elysia":">=1.4.0"},"_npmOperationalInternal":{"tmp":"tmp/elysia_1.0.1_1773102363550_0.1815093223483617","host":"s3://npm-registry-packages-npm-production"}},"1.0.2":{"name":"@api-policy/elysia","version":"1.0.2","keywords":["elysia","api-policy","auth","permission","middleware","framework-adapter"],"author":{"name":"minhtaimc"},"license":"MIT","_id":"@api-policy/elysia@1.0.2","maintainers":[{"name":"minhtaimc","email":"minhtaimc@gmail.com"}],"dist":{"shasum":"c87c8162bfd9a594f0c9c21f88548a501fff0a60","tarball":"https://registry.npmjs.org/@api-policy/elysia/-/elysia-1.0.2.tgz","fileCount":26,"integrity":"sha512-v+q0KizDSY0+dy9fnguYw00g2UE2Sl7Z2JqMYDtXI1YrEfKotKMCaOBHY56L39sOd0QEsGTTxOe2tRyDFu6Mug==","signatures":[{"sig":"MEYCIQCdgUziyWM6DT+eSt1rWFkxf25LAlMwiWqzhb+olQ+kugIhANBzOJlgIX8YqdtOVViqTvnGcsvgcYbOaRHsKugy+HTt","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":64681},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"1dfb68d711990705804119c4cdce30bcd56a3656","scripts":{"dev":"tsc --watch","test":"vitest run","build":"tsc","clean":"rm -rf dist","typecheck":"tsc --noEmit","test:watch":"vitest"},"_npmUser":{"name":"minhtaimc","email":"minhtaimc@gmail.com"},"_npmVersion":"11.6.2","description":"Elysia adapter for API Policy. Auth plugin, permissions, routing.","directories":{},"_nodeVersion":"24.11.1","dependencies":{"jose":"^6.2.0","ts-micro-result":"^3.3.0","@sinclair/typebox":"^0.34.48","@api-policy/server":"^1.0.2"},"_hasShrinkwrap":false,"devDependencies":{"elysia":"^1.4.27","vitest":"^4.0.18","typescript":"^5.9.3","@types/node":"^25.3.5"},"peerDependencies":{"elysia":">=1.4.0"},"_npmOperationalInternal":{"tmp":"tmp/elysia_1.0.2_1773107132395_0.3590024623280412","host":"s3://npm-registry-packages-npm-production"}},"2.0.0":{"name":"@api-policy/elysia","version":"2.0.0","keywords":["elysia","api-policy","auth","permission","middleware","framework-adapter"],"author":{"name":"minhtaimc"},"license":"MIT","_id":"@api-policy/elysia@2.0.0","maintainers":[{"name":"minhtaimc","email":"minhtaimc@gmail.com"}],"dist":{"shasum":"89de2a5783e08f7c7e18b88425577bd59c7c76d8","tarball":"https://registry.npmjs.org/@api-policy/elysia/-/elysia-2.0.0.tgz","fileCount":26,"integrity":"sha512-D/S4maBE7ZM0KuqXNuXfpZavgEfkHUdsX52H+bb22C5RKHkauzwR4x/HlZMYpI6VLgw1lT+tJvsbRUkcHBfTXw==","signatures":[{"sig":"MEUCIQDNqG7GFAlDbgojluUwpnJQLMB8iXhQiGzEIYWz25m58gIgE9FhqKRWxz//lY+Cv3V2uEDOvxUVoGGo74GlymCD42s=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":71158},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"f327e84a65c54d79fed8e22c01c2a5993fc74529","scripts":{"dev":"tsc --watch","test":"vitest run","build":"tsc","clean":"rm -rf dist","typecheck":"tsc --noEmit","test:watch":"vitest"},"_npmUser":{"name":"minhtaimc","email":"minhtaimc@gmail.com"},"_npmVersion":"11.6.2","description":"Elysia adapter for API Policy. Auth plugin, permissions, routing.","directories":{},"_nodeVersion":"24.11.1","dependencies":{"jose":"^6.2.0","ts-micro-result":"^3.3.0","@sinclair/typebox":"^0.34.48","@api-policy/server":"^2.0.0"},"_hasShrinkwrap":false,"devDependencies":{"elysia":"^1.4.27","vitest":"^4.0.18","typescript":"^5.9.3","@types/node":"^25.3.5"},"peerDependencies":{"elysia":">=1.4.0"},"_npmOperationalInternal":{"tmp":"tmp/elysia_2.0.0_1773299867363_0.30793855874080545","host":"s3://npm-registry-packages-npm-production"}},"3.1.0":{"name":"@api-policy/elysia","version":"3.1.0","keywords":["elysia","api-policy","auth","permission","middleware","framework-adapter"],"author":{"name":"minhtaimc"},"license":"MIT","_id":"@api-policy/elysia@3.1.0","maintainers":[{"name":"minhtaimc","email":"minhtaimc@gmail.com"}],"dist":{"shasum":"295180c08692ce899a4cabc8eb2f04ae62b8a97f","tarball":"https://registry.npmjs.org/@api-policy/elysia/-/elysia-3.1.0.tgz","fileCount":26,"integrity":"sha512-Ov0sRcSJU3i/L5j5u3i05mCLLROfarzgfbYP9WiXbvHOFIUzOj+ejuS8+UsS806FfyHhHZNXF5s2ykquIbBKcw==","signatures":[{"sig":"MEYCIQDQ5PjuEw4jHH0LN2+Dz43J0JJvAnJIKiQ5iOE4HkpQ8AIhAJ6JboBa9cY9d882dUHW93Z7wB2e6qXxd0urC5rF/LoJ","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":71105},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"16ba5dc244324cebdcfc88b4c02266c9b22f4121","scripts":{"dev":"tsc --watch","test":"vitest run","build":"tsc","clean":"rm -rf dist","typecheck":"tsc --noEmit","test:watch":"vitest"},"_npmUser":{"name":"minhtaimc","email":"minhtaimc@gmail.com"},"_npmVersion":"11.12.1","description":"Elysia adapter for API Policy. Auth plugin, permissions, routing.","directories":{},"_nodeVersion":"24.15.0","dependencies":{"jose":"^6.2.3","ts-micro-result":"^3.3.0","@sinclair/typebox":"^0.34.49","@api-policy/server":"3.1.1"},"_hasShrinkwrap":false,"devDependencies":{"elysia":"^1.4.28","vitest":"^4.1.8","typescript":"^6.0.3","@types/node":"^25.9.3"},"peerDependencies":{"elysia":">=1.4.0"},"_npmOperationalInternal":{"tmp":"tmp/elysia_3.1.0_1781191841180_0.9047087437726475","host":"s3://npm-registry-packages-npm-production"}},"3.1.1":{"name":"@api-policy/elysia","version":"3.1.1","description":"Elysia adapter for API Policy. Auth plugin, permissions, routing.","type":"module","main":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"scripts":{"build":"tsc","dev":"tsc --watch","clean":"rm -rf dist","typecheck":"tsc --noEmit","test":"vitest run","test:watch":"vitest"},"dependencies":{"@sinclair/typebox":"^0.34.49","@api-policy/server":"3.1.2","jose":"^6.2.3","ts-micro-result":"^3.3.0"},"devDependencies":{"@types/node":"^25.9.3","elysia":"^1.4.28","typescript":"^6.0.3","vitest":"^4.1.8"},"peerDependencies":{"elysia":">=1.4.0"},"keywords":["elysia","api-policy","auth","permission","middleware","framework-adapter"],"author":{"name":"minhtaimc"},"license":"MIT","gitHead":"16ba5dc244324cebdcfc88b4c02266c9b22f4121","_id":"@api-policy/elysia@3.1.1","_nodeVersion":"24.15.0","_npmVersion":"11.12.1","dist":{"integrity":"sha512-ethFkk4gLa+cMDBowmnmnGMfoZvc2u8TAUdNW2FI+cBUwF05gu6SpgssEh2dhI7X7ghX0oZH8Ds0c5zRdB7Wpg==","shasum":"191a2cd53dc5099885786f415f1e8ddccbbae78b","tarball":"https://registry.npmjs.org/@api-policy/elysia/-/elysia-3.1.1.tgz","fileCount":26,"unpackedSize":71105,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIEJAJhiSlCPpr2Vb+M9KIHMejp/OygnXIVbP5A4arkHnAiAXKjrbT757asT7koKCKfsj6uaQBEKTSVKqlmKMeU15Iw=="}]},"_npmUser":{"name":"minhtaimc","email":"minhtaimc@gmail.com"},"directories":{},"maintainers":[{"name":"minhtaimc","email":"minhtaimc@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/elysia_3.1.1_1781194084739_0.8565930612082335"},"_hasShrinkwrap":false}},"time":{"created":"2026-03-09T14:54:57.852Z","modified":"2026-06-11T16:08:05.050Z","1.0.0":"2026-03-09T14:54:58.100Z","1.0.1":"2026-03-10T00:26:03.701Z","1.0.2":"2026-03-10T01:45:32.547Z","2.0.0":"2026-03-12T07:17:47.511Z","3.1.0":"2026-06-11T15:30:41.315Z","3.1.1":"2026-06-11T16:08:04.911Z"},"author":{"name":"minhtaimc"},"license":"MIT","keywords":["elysia","api-policy","auth","permission","middleware","framework-adapter"],"description":"Elysia adapter for API Policy. Auth plugin, permissions, routing.","maintainers":[{"name":"minhtaimc","email":"minhtaimc@gmail.com"}],"readme":"# @api-policy/elysia\n\nElysia adapter for [@api-policy/server](../server). Auth middleware, permission enforcement, and route registration for Elysia applications.\n\n```bash\nnpm install @api-policy/elysia @api-policy/server elysia jose\n```\n\n> Uses [jose](https://github.com/panva/jose) for JWT verification. Install it alongside.\n\n> Validation schemas use [TypeBox](https://github.com/sinclairzx81/typebox) (`@sinclair/typebox`), which is bundled with Elysia.\n\n---\n\n## Setup\n\nTwo usage patterns: **plugin** (recommended) or **adapter directly**.\n\n### Plugin (recommended)\n\n```ts\nimport { Elysia } from 'elysia'\nimport {\n  policyPlugin,\n  createElysiaAdapter,\n  defineRoute,\n  definePublicRoute,\n  PERM, BOUNDARY,\n} from '@api-policy/elysia'\nimport { ok } from 'ts-micro-result'\n\n// 1. Mount plugin — injects authEngine, permissionEngine, errorHttpMap into context\nconst app = new Elysia()\n  .use(policyPlugin({\n    auth: {\n      jwt: { secret: process.env.JWT_SECRET! },\n    },\n  }))\n\n// 2. Create adapter from plugin context\nconst { authEngine, permissionEngine } = app // accessed via context\nconst adapter = createElysiaAdapter(authEngine, permissionEngine)\n\n// 3. Define routes using @api-policy/server's defineRoute\nconst listPosts = defineRoute({\n  method: 'GET',\n  path: '/posts',\n  auth: true,\n  permission: { resource: 'post', action: PERM.READ, boundary: BOUNDARY.GLOBAL },\n  handler: async ({ user }) => {\n    const posts = await db.posts.findByAuthor(user.id)\n    return ok(posts)\n  },\n})\n\nconst health = definePublicRoute({\n  method: 'GET',\n  path: '/health',\n  handler: async () => ok({ status: 'ok' }),\n})\n\n// 4. Register\nadapter.registerAll(app, [listPosts, health])\n\napp.listen(3000)\n```\n\n### Adapter without plugin\n\n```ts\nimport { Elysia } from 'elysia'\nimport {\n  createElysiaAdapter,\n  createDefaultJwtVerify,\n  createAuthPolicyEngine,\n  createPermissionEngine,\n  defineRoute,\n} from '@api-policy/elysia'\nimport { createJwtAuthMethod } from '@api-policy/server'\nimport { ok } from 'ts-micro-result'\n\nconst jwtConfig = { publicKey: process.env.JWT_PUBLIC_KEY!, algorithm: 'EdDSA' as const }\nconst jwtMethod = createJwtAuthMethod(jwtConfig, createDefaultJwtVerify(jwtConfig))\n\nconst authEngine = createAuthPolicyEngine([jwtMethod])\nconst permissionEngine = createPermissionEngine()\n\nconst adapter = createElysiaAdapter(authEngine, permissionEngine)\n\nconst app = new Elysia()\nadapter.registerAll(app, [listPosts, createPost])\napp.listen(3000)\n```\n\n---\n\n## `policyPlugin`\n\nCreates auth and permission engines and injects them into Elysia's context via `decorate` and `derive`.\n\n```ts\nimport { policyPlugin } from '@api-policy/elysia'\nimport { createGatewayJwtAuthMethod, createDefaultJwtVerify } from '@api-policy/server'\n\napp.use(policyPlugin({\n  auth: {\n    // Default methods when route has auth: true\n    defaultMethods: ['jwt', 'apiKey'],\n\n    // JWT — symmetric (HS256/384/512)\n    jwt: { secret: process.env.JWT_SECRET! },\n\n    // JWT — asymmetric (RS256, ES256, EdDSA, etc.)\n    // jwt: { publicKey: process.env.JWT_PUBLIC_KEY!, algorithm: 'EdDSA' },\n\n    // JWT — custom verify function\n    // jwt: { publicKey: '...', verify: myVerifyFn },\n\n    // API Key\n    apiKey: {\n      lookup: async (key) => {\n        const record = await db.apiKeys.findOne({ key })\n        return record ? { ownerId: record.userId, perms: record.perms, tenantId: record.tenantId } : null\n      },\n    },\n\n    // Custom auth methods (e.g. Gateway JWT for BFF → backend)\n    customMethods: [\n      createGatewayJwtAuthMethod({\n        gateway: {\n          publicKeyHex: process.env.BFF_PUBLIC_KEY_HEX,  // omit for local dev\n          expectedIssuer: 'bff-worker',\n          expectedAudience: 'my-backend',\n        },\n        accessJwt: {\n          config: { secret: process.env.JWT_SECRET! },\n          verifyFn: createDefaultJwtVerify({ secret: process.env.JWT_SECRET! }),\n        },\n      }),\n    ],\n  },\n\n  // Custom error → HTTP status codes (merged with defaults)\n  errorHttpMap: {\n    PRODUCT_NOT_FOUND: 404,\n    ORDER_ALREADY_EXISTS: 409,\n  },\n}))\n\n// Injects into context:\n// ctx.authEngine       → AuthPolicyEngine\n// ctx.permissionEngine → PermissionEngine\n// ctx.errorHttpMap     → Record<string, number>\n// ctx.user             → UserContext | null (set after auth)\n// ctx.authMethod       → string | null (set after auth)\n```\n\nAccess engines inside custom handlers:\n\n```ts\nimport { getAuthEngine, getPermissionEngine } from '@api-policy/elysia'\n\napp.get('/custom', (ctx) => {\n  const authEngine      = getAuthEngine(ctx)\n  const permissionEngine = getPermissionEngine(ctx)\n})\n```\n\n---\n\n## Route definition\n\nUse `defineRoute` and `definePublicRoute` from `@api-policy/server` (also re-exported from this package):\n\n```ts\nimport { defineRoute, definePublicRoute, PERM, BOUNDARY } from '@api-policy/elysia'\nimport { t } from 'elysia'  // TypeBox — Elysia's built-in schema\nimport { ok } from 'ts-micro-result'\n\nconst createPost = defineRoute({\n  method: 'POST',\n  path: '/posts',\n  auth: true,                    // use default methods\n  // auth: 'jwt'                 // specific method\n  // auth: ['jwt', 'apiKey']     // try list in order\n  permission: {\n    resource: 'post',\n    action: PERM.WRITE,\n    boundary: BOUNDARY.TENANT,\n    roles: ['editor', 'admin'],  // optional role gate (OR logic)\n  },\n  input: {\n    body: t.Object({ title: t.String(), content: t.String() }),\n    query: t.Object({ draft: t.Optional(t.Boolean()) }),\n  },\n  handler: async ({ user, body, query, params }) => {\n    const post = await db.posts.create({ ...body, authorId: user.id })\n    return ok(post)\n  },\n})\n```\n\n**Handler receives `PolicyContext`:**\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `user` | `UserContext` | Authenticated user |\n| `body` | `TBody` | Parsed + validated request body |\n| `query` | `TQuery` | Parsed + validated query params |\n| `params` | `TParams` | Parsed + validated path params |\n\n**Permission check flow (in order):**\n\n```\n1. Auth        → verify credentials (JWT / API Key / Gateway JWT)\n2. Permission  → roles gate → owner bypass → bitmask → boundary\n3. Handler     → business logic, returns Result<T>\n```\n\nFor routes with `boundary !== 'global'`, use `loadResource` to provide the resource for boundary check:\n\n```ts\nconst updatePost = defineRoute({\n  method: 'PUT',\n  path: '/posts/:id',\n  auth: true,\n  permission: {\n    resource: 'post',\n    action: PERM.WRITE,\n    boundary: BOUNDARY.OWNER,\n    allowOwner: true,\n  },\n  loadResource: async ({ params }) => {\n    const post = await db.posts.findById(params.id)\n    return post ? { ownerId: post.authorId, tenantId: post.tenantId } : undefined\n  },\n  handler: async ({ user, body, params }) => {\n    const post = await db.posts.update(params.id, body)\n    return ok(post)\n  },\n})\n```\n\nIf `loadResource` returns `undefined`, the request is denied with `PermissionDenied`.\n\n---\n\n## `createElysiaAdapter`\n\nRegisters `RouteSpec` routes onto an Elysia app.\n\n```ts\nimport { createElysiaAdapter } from '@api-policy/elysia'\n\nconst adapter = createElysiaAdapter(\n  authEngine,\n  permissionEngine,\n  errorHttpMap,     // optional, defaults to built-in map\n  { requiredTenantType: 'merchant' }  // optional adapter options\n)\n\n// Register single route\nadapter.register(app, myRoute)\nadapter.register(app, myRoute, { prefix: '/v1', tags: ['Posts'], summary: 'List posts' })\n\n// Register multiple routes\nadapter.registerAll(app, [route1, route2, route3])\nadapter.registerAll(app, routes, { prefix: '/v1' })\n```\n\n**Adapter options:**\n\n| Option | Type | Description |\n|--------|------|-------------|\n| `requiredTenantType` | `string` | Reject requests where `user.tenantType !== value` |\n\n**Route registration options (`RouteOptions`):**\n\n| Option | Type | Description |\n|--------|------|-------------|\n| `prefix` | `string` | Additional path prefix |\n| `tags` | `string[]` | OpenAPI tags |\n| `summary` | `string` | OpenAPI summary |\n| `description` | `string` | OpenAPI description |\n| `deprecated` | `boolean` | Mark route as deprecated in OpenAPI |\n\n---\n\n## Route groups\n\n```ts\nimport { createRouteGroup } from '@api-policy/elysia'\n\nconst postGroup = createRouteGroup(app, '/posts', authEngine, permissionEngine)\n\npostGroup\n  .registerAll([listPosts, getPost, createPost, deletePost])\n  .mount()\n\n// Routes registered at /posts, /posts/:id, etc.\n```\n\nOr with per-route options:\n\n```ts\npostGroup.register(createPost, { tags: ['Posts'], summary: 'Create a post' })\npostGroup.register(deletePost, { tags: ['Posts'], summary: 'Delete a post' })\npostGroup.mount()\n```\n\n---\n\n## OpenAPI\n\nThe adapter automatically injects security schemes and `x-policy` metadata:\n\n```ts\nadapter.register(app, updatePost, {\n  tags: ['Posts'],\n  summary: 'Update a post',\n})\n```\n\nGenerated OpenAPI:\n\n```json\n{\n  \"paths\": {\n    \"/posts/{id}\": {\n      \"put\": {\n        \"tags\": [\"Posts\"],\n        \"summary\": \"Update a post\",\n        \"security\": [{ \"bearerAuth\": [] }],\n        \"x-policy\": {\n          \"action\": 2,\n          \"resource\": \"post\",\n          \"boundary\": \"owner\"\n        }\n      }\n    }\n  }\n}\n```\n\nSecurity schemes added per auth method: `bearerAuth` (JWT / Gateway JWT), `apiKeyAuth` (API Key).\n\n---\n\n## Response mapping\n\nHandlers return `Result<T>` from `ts-micro-result`. The adapter maps them to HTTP responses automatically.\n\n```ts\nimport { ok, err } from 'ts-micro-result'\nimport { NotFound, ValidationError } from '@api-policy/server'\n\n// Success → 200\nhandler: async () => ok({ id: 1, title: 'Hello' })\n\n// Empty success → 204 No Content\nhandler: async () => ok()\n\n// Error → mapped status via errorHttpMap\nhandler: async () => err(NotFound({ message: 'Post not found' }))\n// → 404 { ok: false, errors: [{ code: 'NOT_FOUND', message: 'Post not found' }] }\n```\n\n**Default error → HTTP status map:**\n\n| Code | Status |\n|------|--------|\n| `AUTH_NOT_AUTHENTICATED`, `AUTH_TOKEN_EXPIRED`, etc. | 401 |\n| `PERM_DENIED` | 403 |\n| `VALIDATION_ERROR` | 400 |\n| `NOT_FOUND`, `RESOURCE_NOT_FOUND` | 404 |\n| `INTERNAL_ERROR` | 500 |\n\nOverride by passing a custom map to `createElysiaAdapter` or `policyPlugin`:\n\n```ts\nconst adapter = createElysiaAdapter(authEngine, permissionEngine, {\n  ...defaultErrorHttpMap,\n  PRODUCT_NOT_FOUND: 404,\n  PRODUCT_SLUG_EXISTS: 409,\n})\n```\n\n---\n\n## Re-exported from @api-policy/server\n\nAll commonly used exports are available directly from `@api-policy/elysia`:\n\n```ts\nimport {\n  defineRoute, definePublicRoute,\n  createAuthPolicyEngine, createPermissionEngine,\n  // types\n  type RouteSpec, type PolicyContext,\n  type UserContext, type ResourceContext,\n  type PermissionSpec, type BoundaryType,\n} from '@api-policy/elysia'\n```\n\n---\n\n## License\n\nMIT\n","readmeFilename":"README.md"}