{"_id":"hono-jev-router","_rev":"3-368363eacc730508f76bba059711625c","name":"hono-jev-router","dist-tags":{"latest":"0.2.0"},"versions":{"0.0.1":{"name":"hono-jev-router","version":"0.0.1","keywords":["hono","jev","router","semantic","typesafe"],"author":{"url":"https://github.com/yusukebe","name":"Yusuke Wada","email":"yusuke@kamawada.com"},"license":"MIT","_id":"hono-jev-router@0.0.1","maintainers":[{"name":"yusukebe","email":"yusuke@kamawada.com"}],"homepage":"https://github.com/yusukebe/hono-jev-router","bugs":{"url":"https://github.com/yusukebe/hono-jev-router/issues"},"dist":{"shasum":"19a94b7d822e3a32ac2b93f696b70a7082746732","tarball":"https://registry.npmjs.org/hono-jev-router/-/hono-jev-router-0.0.1.tgz","fileCount":5,"integrity":"sha512-EJMiIHiIaMcMCuwTJ6H7wiAOQLoMw2+L+W2A2p5N7Zju+rJsKDeupfMCbdkIMhtJ9GAsmcygXp8nVgN8BpxpsA==","signatures":[{"sig":"MEYCIQDSvCCa0buWvSo6d0TYN6d1gnTkNO4dJ9I6yz+YmHbjawIhAIpWIpqkUtVrnru6Mbi5K6mNaJGl1Gn+v/2YXKitz4Pu","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":14136},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"603f404b9395d09f739e31492b35a03a3ab7b40b","scripts":{"lint":"oxlint","test":"node --test 'test/*.test.ts'","build":"tsc -p tsconfig.build.json","format":"oxfmt --check","release":"np --no-publish","lint:fix":"oxlint --fix","typecheck":"tsc --noEmit","format:fix":"oxfmt"},"_npmUser":{"name":"yusukebe","email":"yusuke@kamawada.com"},"repository":{"url":"git+https://github.com/yusukebe/hono-jev-router.git","type":"git"},"_npmVersion":"11.14.1","description":"Route HTTP requests by meaning. A semantic router for Hono powered by Jev.","directories":{},"_nodeVersion":"22.21.1","publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"packageManager":"pnpm@11.24.0","devDependencies":{"np":"^12.1.1","hono":"^4.13.8","oxfmt":"^0.68.0","oxlint":"^1.83.0","typescript":"^7.0.2","@types/node":"^26.6.1"},"peerDependencies":{"hono":">=4.0.0"},"_npmOperationalInternal":{"tmp":"tmp/hono-jev-router_0.0.1_1789717900992_0.6582652593201344","host":"s3://npm-registry-packages-npm-production"}},"0.1.0":{"name":"hono-jev-router","version":"0.1.0","keywords":["hono","jev","router","semantic","typesafe"],"author":{"url":"https://github.com/yusukebe","name":"Yusuke Wada","email":"yusuke@kamawada.com"},"license":"MIT","_id":"hono-jev-router@0.1.0","maintainers":[{"name":"yusukebe","email":"yusuke@kamawada.com"}],"homepage":"https://github.com/yusukebe/hono-jev-router","bugs":{"url":"https://github.com/yusukebe/hono-jev-router/issues"},"dist":{"shasum":"598260ddf650f6abb03657bd06a9dbc60a7f16e3","tarball":"https://registry.npmjs.org/hono-jev-router/-/hono-jev-router-0.1.0.tgz","fileCount":5,"integrity":"sha512-9gmf36jlcM1SUjMvpRFydyLwdkq11ff8eZrq0PbNz09tMiOGfLzd8/SJUyoGVyp49VveSaHD+EJjqFdAw4YSuQ==","signatures":[{"sig":"MEUCIDKZDdSxveUOHcqu0wzv31WmX06gk7BvgaKz4rG/sX/uAiEA8j9qUXCVlaTGPGd5j0UkJ5DEIwuvr2NLDHpQLFzHiiU=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/hono-jev-router@0.1.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":14864},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"e4cd49a7a949a5a7ea66ad8f7b9ec890f93e908a","scripts":{"lint":"oxlint","test":"node --test 'test/*.test.ts'","build":"tsc -p tsconfig.build.json","format":"oxfmt --check","release":"np --no-publish","lint:fix":"oxlint --fix","typecheck":"tsc --noEmit","format:fix":"oxfmt"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","approver":{"name":"yusukebe","email":"yusuke@kamawada.com"},"trustedPublisher":{"id":"github","oidcConfigId":"oidc:7465a905-702b-4a7d-b741-c46050ec2984"}},"repository":{"url":"git+https://github.com/yusukebe/hono-jev-router.git","type":"git"},"_npmVersion":"11.17.0","description":"Route HTTP requests by meaning. A semantic router for Hono powered by Jev.","directories":{},"_nodeVersion":"24.20.0","publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"packageManager":"pnpm@11.24.0","devDependencies":{"np":"^12.1.1","hono":"^4.13.8","oxfmt":"^0.68.0","oxlint":"^1.83.0","typescript":"^7.0.2","@types/node":"^26.6.1"},"peerDependencies":{"hono":">=4.0.0"},"_npmOperationalInternal":{"tmp":"tmp/hono-jev-router_0.1.0_1789718994713_0.6950798085242926","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"_id":"hono-jev-router@0.2.0","bugs":{"url":"https://github.com/yusukebe/hono-jev-router/issues"},"dist":{"shasum":"30f06e938efbbf371fe48007acb8966aa95f0d2a","tarball":"https://registry.npmjs.org/hono-jev-router/-/hono-jev-router-0.2.0.tgz","integrity":"sha512-WcvPoI+BktgtqsCPmqDxMEqYVsL4Xc2pXxc0hM3nt3kNsmnIXotG4tbQR3mMCVUliZHQsg79JTUjFqvsYUrkdw==","fileCount":5,"unpackedSize":16514,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/hono-jev-router@0.2.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIB131jZ4edK9Y7p9MxMAj6KnaryCCWTBYbh7hWA2PKytAiAi+e3zfzIVtfaJCeyWY0hl/fk4sxgbSj23c8q2/e8siQ=="}]},"main":"dist/index.js","name":"hono-jev-router","type":"module","types":"dist/index.d.ts","author":{"url":"https://github.com/yusukebe","name":"Yusuke Wada","email":"yusuke@kamawada.com"},"engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"fddb44d476de9b62cf653e182744700be7f0f0e6","license":"MIT","scripts":{"lint":"oxlint","test":"node --test 'test/*.test.ts'","build":"tsc -p tsconfig.build.json","format":"oxfmt --check","release":"np --no-publish","lint:fix":"oxlint --fix","typecheck":"tsc --noEmit","format:fix":"oxfmt"},"version":"0.2.0","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:7465a905-702b-4a7d-b741-c46050ec2984"},"approver":{"name":"yusukebe","email":"yusuke@kamawada.com"}},"homepage":"https://github.com/yusukebe/hono-jev-router","keywords":["hono","jev","router","semantic","typesafe"],"repository":{"url":"git+https://github.com/yusukebe/hono-jev-router.git","type":"git"},"_npmVersion":"11.17.0","description":"Route HTTP requests by meaning. A semantic router for Hono powered by Jev.","directories":{},"maintainers":[{"name":"yusukebe","email":"yusuke@kamawada.com"}],"_nodeVersion":"24.20.0","publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"packageManager":"pnpm@11.24.0","devDependencies":{"np":"^12.1.1","hono":"^4.13.8","oxfmt":"^0.68.0","oxlint":"^1.83.0","typescript":"^7.0.2","@types/node":"^26.6.1"},"peerDependencies":{"hono":">=4.0.0"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/hono-jev-router_0.2.0_1789721313832_0.24077835068891495"},"_hasShrinkwrap":false}},"time":{"created":"2026-09-18T07:51:40.823Z","modified":"2026-09-18T08:48:34.400Z","0.0.1":"2026-09-18T07:51:41.122Z","0.1.0":"2026-09-18T08:09:54.792Z","0.2.0":"2026-09-18T08:48:33.918Z"},"bugs":{"url":"https://github.com/yusukebe/hono-jev-router/issues"},"author":{"url":"https://github.com/yusukebe","name":"Yusuke Wada","email":"yusuke@kamawada.com"},"license":"MIT","homepage":"https://github.com/yusukebe/hono-jev-router","keywords":["hono","jev","router","semantic","typesafe"],"repository":{"url":"git+https://github.com/yusukebe/hono-jev-router.git","type":"git"},"description":"Route HTTP requests by meaning. A semantic router for Hono powered by Jev.","maintainers":[{"name":"yusukebe","email":"yusuke@kamawada.com"}],"readme":"# hono-jev-router\n\nRoute HTTP requests by meaning.\n\n`hono-jev-router` is an experimental router for [Hono](https://hono.dev). Instead of a method and a path, you describe a request in plain words, and [Jev](https://typesafe.ai) by TypeSafe AI decides which description fits the incoming request.\n\n```ts\nimport { Hono } from 'hono'\nimport { JevRouter } from 'hono-jev-router'\n\nconst app = new Hono({\n  router: new JevRouter({ apiKey: process.env.TYPESAFE_API_KEY }),\n})\n\napp.on('jev', 'a request from an AI agent', (c) => {\n  return c.text('# Documentation', 200, { 'Content-Type': 'text/markdown' })\n})\n\napp.on('jev', 'a request from a human browser', (c) => {\n  return c.html('<h1>Documentation</h1>')\n})\n\napp.on('jev', 'suspicious automated traffic', (c) => {\n  return c.text('Forbidden', 403)\n})\n\nexport default app\n```\n\n> [!WARNING]\n> This is an experiment. Every semantically routed request calls a model, so it adds latency and cost, and the answer is a probability, not a guarantee.\n\n## Before you use it\n\n- **Do not use it for authentication or authorization.** The request is the input of the model, so whoever sends the request can try to steer the answer. `'suspicious automated traffic'` is a heuristic, not a security boundary.\n- **Requests are sent to a third party.** The method, URL, headers and the first `maxBodyLength` bytes of a textual body go to Jev. The values of `Authorization`, `Cookie`, `Proxy-Authorization`, `X-API-Key` and `X-Auth-Token` are replaced with `[redacted]`. Change the list with `redactHeaders`.\n- **Every semantically routed request is a model call you pay for.** Put a rate limit in front of it.\n\n## Install\n\n```bash\nnpm i hono-jev-router\n```\n\n## How it works\n\n```\nHTTP Request → JevRouter → Jev: \"does the request match this description?\" → first match → handler → Response\n```\n\n- `app.on('jev', '<description>', handler)` registers a semantic route.\n- The method, URL, headers and body of the request are sent to Jev with one yes/no ([Noul](https://docs.typesafe.ai/primitives/noul)) question per description. They are evaluated in parallel in a single call.\n- Like a normal router, **the first registered route that matches wins**. A route matches when its probability reaches the `threshold`.\n- The handler is a normal Hono handler: `c.req.method` and `c.req.path` are those of the real request.\n- If no description matches, the request falls through, usually to `app.notFound()`.\n- Everything else works as usual. Path routes and middleware are handled by Hono's `TrieRouter`, and a path route that returns a response wins before Jev is asked.\n\n```ts\napp.use(logger())\napp.get('/health', (c) => c.text('ok')) // Jev is not called\n\n// Order matters: put specific descriptions first, broad ones last\napp.on('jev', 'suspicious automated traffic', (c) => c.text('Forbidden', 403))\napp.on('jev', 'any GET request', (c) => c.text('Hello!'))\n```\n\n## The result\n\nThe decision is available as `c.get('jev')`.\n\n```ts\nimport type { JevResult } from 'hono-jev-router'\n\nconst app = new Hono<{ Variables: { jev: JevResult } }>({ router: new JevRouter({ apiKey }) })\n\napp.on('jev', 'a request from an AI agent', (c) => {\n  const { route, confidence, probabilities } = c.get('jev')\n  // route: 'a request from an AI agent'\n  // confidence: 0.94\n  // probabilities: { 'a request from an AI agent': 0.94, 'a request from a human browser': 0.2 }\n  return c.json({ route, confidence, probabilities })\n})\n```\n\nThe probabilities are independent of each other, so they do not add up to 1.\n\n## Options\n\n```ts\nnew JevRouter({\n  apiKey, // string | (c) => string\n  baseURL, // default: 'https://api.typesafe.ai'\n  threshold, // a route matches when its probability is at least this. default: 0.5\n  maxBodyLength, // how much of the request body Jev sees. default: 4096\n  redactHeaders, // header values Jev must not see. default: authorization, cookie, ...\n  run, // reach Jev some other way\n  choose, // replace the whole decision\n})\n```\n\n### `threshold`\n\nRaise it to make every route stricter. Requests that match nothing fall through:\n\n```ts\nconst app = new Hono({ router: new JevRouter({ apiKey, threshold: 0.8 }) })\n\napp.notFound((c) => c.text('Not sure what you are', 404))\n```\n\n### `apiKey`\n\nYour TypeSafe API key as a string:\n\n```ts\nnew JevRouter({ apiKey: process.env.TYPESAFE_API_KEY })\n```\n\nOn Cloudflare Workers, `process.env` holds your variables and secrets only when the `nodejs_compat` flag is on and the compatibility date is `2025-04-01` or later. Otherwise pass a function. It receives the Context, so the key can come from `c.env`:\n\n```ts\nnew JevRouter({ apiKey: (c) => c.env.TYPESAFE_API_KEY })\n```\n\n### `run`: Workers AI / AI Gateway\n\nJev is available on Cloudflare as [`typesafe/jev`](https://developers.cloudflare.com/ai/models/typesafe/jev/), so you can use the AI binding instead of an API key:\n\n```ts\ntype Bindings = { AI: Ai }\n\nconst app = new Hono<{ Bindings: Bindings }>({\n  router: new JevRouter({\n    run: async (c, request) => {\n      // If 'typesafe/jev' is not in your generated types yet, cast `c.env.AI`\n      const { result } = await c.env.AI.run('typesafe/jev', request, {\n        gateway: { id: 'default' },\n      })\n      return result\n    },\n  }),\n})\n```\n\n### `choose`\n\nReplaces the decision itself. It receives the request state, the descriptions and the threshold, and returns a `JevResult`. Useful for tests, caching, or delegating to something that holds the credentials:\n\n```ts\nnew JevRouter({\n  choose: (c, input) => c.env.JEV.choose(input), // input: { state, routes, threshold }\n})\n```\n\n## Author\n\nYusuke Wada <https://github.com/yusukebe>\n\n## License\n\nMIT\n","readmeFilename":"README.md"}