[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M71B1SMQQT2SA4QE6XVF7KZP":3,"skill-same:01M71B1SMQQT2SA4QE6XVF7KZP":70},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":23,"publishedAt":43,"linkOnly":44,"markdownContent":45,"markdownBody":46,"frontMatter":47,"refName":48,"commitSha":49,"skillDirectory":50,"forksCount":14,"repositoryHtmlUrl":51,"repositoryHomepageUrl":52,"ownerHtmlUrl":53,"ownerBlogUrl":54,"ownerDescription":55,"updatedAt":56,"install":57,"resources":69},"01M71B1SMQQT2SA4QE6XVF7KZP","hono",null,"Use when building Hono web applications or when the user asks about Hono APIs, routing, middleware, JSX, validation, testing, or streaming. TRIGGER when code imports from 'hono' or 'hono\u002F*', or user mentions Hono. Use `npx hono request` to test endpoints.","escwxyz\u002Fskills-re","escwxyz","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F124119483?v=4","01M71B1P3M16B4SZRCEAA6JD69","Jie Wang","01M71B1P47632T1KJ3RQJF6ME2",0,"Apache-2.0","https:\u002F\u002Fgithub.com\u002Fescwxyz\u002Fskills-re\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fhono\u002FSKILL.md",[18],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000008","测试与质量","SCENARIO","使用场景",[5,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42],"applications","user","validation","about","web","endpoints","test","routing","use","npx","trigger","jsx","request","imports","streaming","code","when","testing","middleware","2026-11-10 10:12:30",false,"---\nname: hono\ndescription: Use when building Hono web applications or when the user asks about Hono APIs, routing, middleware, JSX, validation, testing, or streaming. TRIGGER when code imports from 'hono' or 'hono\u002F*', or user mentions Hono. Use `npx hono request` to test endpoints.\n---\n\n# Hono Skill\n\nBuild Hono web applications. This skill provides inline API knowledge for AI. Use `npx hono request` to test endpoints. If the `hono-docs` MCP server is configured, prefer its tools for the latest documentation over the inline reference.\n\n## Hono CLI Usage\n\n### Request Testing\n\nTest endpoints without starting an HTTP server. Uses `app.request()` internally.\n\n```bash\n# GET request\nnpx hono request [file] -P \u002Fpath\n\n# POST request with JSON body\nnpx hono request [file] -X POST -P \u002Fapi\u002Fusers -d '{\"name\": \"test\"}'\n```\n\n**Note:** Do not pass credentials directly in CLI arguments. Use environment variables for sensitive values. `hono request` does not support Cloudflare Workers bindings (KV, D1, R2, etc.). When bindings are required, use `workers-fetch` instead:\n\n```bash\nnpx workers-fetch \u002Fpath\nnpx workers-fetch -X POST -H \"Content-Type:application\u002Fjson\" -d '{\"name\":\"test\"}' \u002Fapi\u002Fusers\n```\n\n---\n\n## Hono API Reference\n\n### App Constructor\n\n```ts\nimport { Hono } from \"hono\";\n\nconst app = new Hono();\n\n\u002F\u002F With TypeScript generics\ntype Env = {\n  Bindings: { DATABASE: D1Database; KV: KVNamespace };\n  Variables: { user: User };\n};\nconst app = new Hono\u003CEnv>();\n```\n\n### Routing Methods\n\n```ts\napp.get(\"\u002Fpath\", handler);\napp.post(\"\u002Fpath\", handler);\napp.put(\"\u002Fpath\", handler);\napp.delete(\"\u002Fpath\", handler);\napp.patch(\"\u002Fpath\", handler);\napp.options(\"\u002Fpath\", handler);\napp.all(\"\u002Fpath\", handler); \u002F\u002F all HTTP methods\napp.on(\"PURGE\", \"\u002Fpath\", handler); \u002F\u002F custom method\napp.on([\"PUT\", \"DELETE\"], \"\u002Fpath\", handler); \u002F\u002F multiple methods\n```\n\n### Routing Patterns\n\n```ts\n\u002F\u002F Path parameters\napp.get(\"\u002Fuser\u002F:name\", (c) => {\n  const name = c.req.param(\"name\");\n  return c.json({ name });\n});\n\n\u002F\u002F Multiple params\napp.get(\"\u002Fposts\u002F:id\u002Fcomments\u002F:commentId\", (c) => {\n  const { id, commentId } = c.req.param();\n});\n\n\u002F\u002F Optional parameters\napp.get(\"\u002Fapi\u002Fanimal\u002F:type?\", (c) => c.text(\"Animal!\"));\n\n\u002F\u002F Wildcards\napp.get(\"\u002Fwild\u002F*\u002Fcard\", (c) => c.text(\"Wildcard\"));\n\n\u002F\u002F Regexp constraints\napp.get(\"\u002Fpost\u002F:date{[0-9]+}\u002F:title{[a-z]+}\", (c) => {\n  const { date, title } = c.req.param();\n});\n\n\u002F\u002F Chained routes\napp\n  .get(\"\u002Fendpoint\", (c) => c.text(\"GET\"))\n  .post((c) => c.text(\"POST\"))\n  .delete((c) => c.text(\"DELETE\"));\n```\n\n### Route Grouping\n\n```ts\n\u002F\u002F Using route()\nconst api = new Hono();\napi.get(\"\u002Fusers\", (c) => c.json([]));\n\nconst app = new Hono();\napp.route(\"\u002Fapi\", api); \u002F\u002F mounts at \u002Fapi\u002Fusers\n\n\u002F\u002F Using basePath()\nconst app = new Hono().basePath(\"\u002Fapi\");\napp.get(\"\u002Fusers\", (c) => c.json([])); \u002F\u002F GET \u002Fapi\u002Fusers\n```\n\n### Error Handling\n\n```ts\napp.notFound((c) => c.json({ message: \"Not Found\" }, 404));\n\napp.onError((err, c) => {\n  console.error(err);\n  return c.json({ message: \"Internal Server Error\" }, 500);\n});\n```\n\n---\n\n## Context (c)\n\n### Response Methods\n\n```ts\nc.text(\"Hello\"); \u002F\u002F text\u002Fplain\nc.json({ message: \"Hello\" }); \u002F\u002F application\u002Fjson\nc.html(\"\u003Ch1>Hello\u003C\u002Fh1>\"); \u002F\u002F text\u002Fhtml\nc.redirect(\"\u002Fnew-path\"); \u002F\u002F 302 redirect\nc.redirect(\"\u002Fnew-path\", 301); \u002F\u002F 301 redirect\nc.body(\"raw body\", 200, headers); \u002F\u002F raw response\nc.notFound(); \u002F\u002F 404 response\n```\n\n### Headers & Status\n\n```ts\nc.status(201);\nc.header(\"X-Custom\", \"value\");\nc.header(\"Cache-Control\", \"no-store\");\n```\n\n### Variables (request-scoped data)\n\n```ts\n\u002F\u002F In middleware\nc.set(\"user\", { id: 1, name: \"Alice\" });\n\n\u002F\u002F In handler\nconst user = c.get(\"user\");\n\u002F\u002F or\nconst user = c.var.user;\n```\n\n### Environment (Cloudflare Workers)\n\n```ts\nconst value = await c.env.KV.get(\"key\");\nconst db = c.env.DATABASE;\nc.executionCtx.waitUntil(promise);\n```\n\n### Renderer\n\n```ts\napp.use(async (c, next) => {\n  c.setRenderer((content) =>\n    c.html(\n      \u003Chtml>\u003Cbody>{content}\u003C\u002Fbody>\u003C\u002Fhtml>\n    )\n  )\n  await next()\n})\n\napp.get('\u002F', (c) => c.render(\u003Ch1>Hello\u003C\u002Fh1>))\n```\n\n---\n\n## HonoRequest (c.req)\n\n```ts\nc.req.param(\"id\"); \u002F\u002F path parameter\nc.req.param(); \u002F\u002F all path params as object\nc.req.query(\"page\"); \u002F\u002F query string parameter\nc.req.query(); \u002F\u002F all query params as object\nc.req.queries(\"tags\"); \u002F\u002F multiple values: ?tags=A&tags=B → ['A', 'B']\nc.req.header(\"Authorization\"); \u002F\u002F request header\nc.req.header(); \u002F\u002F all headers (keys are lowercase)\n\n\u002F\u002F Body parsing\nawait c.req.json(); \u002F\u002F parse JSON body\nawait c.req.text(); \u002F\u002F parse text body\nawait c.req.formData(); \u002F\u002F parse as FormData\nawait c.req.parseBody(); \u002F\u002F parse multipart\u002Fform-data or urlencoded\nawait c.req.arrayBuffer(); \u002F\u002F parse as ArrayBuffer\nawait c.req.blob(); \u002F\u002F parse as Blob\n\n\u002F\u002F Validated data (used with validator middleware)\nc.req.valid(\"json\");\nc.req.valid(\"query\");\nc.req.valid(\"form\");\nc.req.valid(\"param\");\n\n\u002F\u002F Properties\nc.req.url; \u002F\u002F full URL string\nc.req.path; \u002F\u002F pathname\nc.req.method; \u002F\u002F HTTP method\nc.req.raw; \u002F\u002F underlying Request object\n```\n\n---\n\n## Middleware\n\n### Using Built-in Middleware\n\n```ts\nimport { cors } from \"hono\u002Fcors\";\nimport { logger } from \"hono\u002Flogger\";\nimport { basicAuth } from \"hono\u002Fbasic-auth\";\nimport { prettyJSON } from \"hono\u002Fpretty-json\";\nimport { secureHeaders } from \"hono\u002Fsecure-headers\";\nimport { etag } from \"hono\u002Fetag\";\nimport { compress } from \"hono\u002Fcompress\";\nimport { poweredBy } from \"hono\u002Fpowered-by\";\nimport { timing } from \"hono\u002Ftiming\";\nimport { cache } from \"hono\u002Fcache\";\nimport { bearerAuth } from \"hono\u002Fbearer-auth\";\nimport { jwt } from \"hono\u002Fjwt\";\nimport { csrf } from \"hono\u002Fcsrf\";\nimport { ipRestriction } from \"hono\u002Fip-restriction\";\nimport { bodyLimit } from \"hono\u002Fbody-limit\";\nimport { requestId } from \"hono\u002Frequest-id\";\nimport { methodOverride } from \"hono\u002Fmethod-override\";\nimport { trailingSlash, trimTrailingSlash } from \"hono\u002Ftrailing-slash\";\n\n\u002F\u002F Registration\napp.use(logger()); \u002F\u002F all routes\napp.use(\"\u002Fapi\u002F*\", cors()); \u002F\u002F specific path\napp.post(\"\u002Fapi\u002F*\", basicAuth({ username: \"admin\", password: \"secret\" }));\n```\n\n### Custom Middleware\n\n```ts\n\u002F\u002F Inline\napp.use(async (c, next) => {\n  const start = Date.now();\n  await next();\n  const elapsed = Date.now() - start;\n  c.res.headers.set(\"X-Response-Time\", `${elapsed}ms`);\n});\n\n\u002F\u002F Reusable with createMiddleware\nimport { createMiddleware } from \"hono\u002Ffactory\";\n\nconst auth = createMiddleware(async (c, next) => {\n  const token = c.req.header(\"Authorization\");\n  if (!token) return c.json({ error: \"Unauthorized\" }, 401);\n  await next();\n});\n\napp.use(\"\u002Fapi\u002F*\", auth);\n```\n\n### Middleware Execution Order\n\nMiddleware executes in registration order. `await next()` calls the next middleware\u002Fhandler, and code after `next()` runs on the way back:\n\n```\nRequest → mw1 before → mw2 before → handler → mw2 after → mw1 after → Response\n```\n\n```ts\napp.use(async (c, next) => {\n  \u002F\u002F before handler\n  await next();\n  \u002F\u002F after handler\n});\n```\n\n---\n\n## Validation\n\nValidation targets: `json`, `form`, `query`, `header`, `param`, `cookie`.\n\n### Zod Validator\n\n```ts\nimport { zValidator } from \"@hono\u002Fzod-validator\";\nimport { z } from \"zod\";\n\nconst schema = z.object({\n  title: z.string().min(1),\n  body: z.string(),\n});\n\napp.post(\"\u002Fposts\", zValidator(\"json\", schema), (c) => {\n  const data = c.req.valid(\"json\"); \u002F\u002F fully typed\n  return c.json(data, 201);\n});\n```\n\n### Valibot \u002F Standard Schema Validator\n\n```ts\nimport { sValidator } from \"@hono\u002Fstandard-validator\";\nimport * as v from \"valibot\";\n\nconst schema = v.object({ name: v.string(), age: v.number() });\n\napp.post(\"\u002Fusers\", sValidator(\"json\", schema), (c) => {\n  const data = c.req.valid(\"json\");\n  return c.json(data, 201);\n});\n```\n\n---\n\n## JSX\n\n### Setup\n\nIn `tsconfig.json`:\n\n```json\n{\n  \"compilerOptions\": {\n    \"jsx\": \"react-jsx\",\n    \"jsxImportSource\": \"hono\u002Fjsx\"\n  }\n}\n```\n\nOr use pragma: `\u002F** @jsxImportSource hono\u002Fjsx *\u002F`\n\n**Important:** Files using JSX must have a `.tsx` extension. Rename `.ts` to `.tsx` or the compiler will fail.\n\n### Components\n\n```tsx\nimport type { PropsWithChildren } from \"hono\u002Fjsx\";\n\nconst Layout = (props: PropsWithChildren) => (\n  \u003Chtml>\n    \u003Chead>\n      \u003Ctitle>My App\u003C\u002Ftitle>\n    \u003C\u002Fhead>\n    \u003Cbody>{props.children}\u003C\u002Fbody>\n  \u003C\u002Fhtml>\n);\n\nconst UserCard = ({ name }: { name: string }) => (\n  \u003Cdiv class=\"card\">\n    \u003Ch2>{name}\u003C\u002Fh2>\n  \u003C\u002Fdiv>\n);\n\napp.get(\"\u002F\", (c) => {\n  return c.html(\n    \u003CLayout>\n      \u003CUserCard name=\"Alice\" \u002F>\n    \u003C\u002FLayout>,\n  );\n});\n```\n\n### jsxRenderer Middleware\n\nUse `jsxRenderer` middleware for layouts. See `npx hono docs \u002Fdocs\u002Fmiddleware\u002Fbuiltin\u002Fjsx-renderer` for details.\n\n### Async Components\n\n```tsx\nconst UserList = async () => {\n  const users = await fetchUsers();\n  return (\n    \u003Cul>\n      {users.map((u) => (\n        \u003Cli>{u.name}\u003C\u002Fli>\n      ))}\n    \u003C\u002Ful>\n  );\n};\n```\n\n### Fragments\n\n```tsx\nconst Items = () => (\n  \u003C>\n    \u003Cli>Item 1\u003C\u002Fli>\n    \u003Cli>Item 2\u003C\u002Fli>\n  \u003C\u002F>\n);\n```\n\n---\n\n## Streaming\n\n```ts\nimport { stream, streamText, streamSSE } from \"hono\u002Fstreaming\";\n\n\u002F\u002F Basic stream\napp.get(\"\u002Fstream\", (c) => {\n  return stream(c, async (stream) => {\n    stream.onAbort(() => console.log(\"Aborted\"));\n    await stream.write(new Uint8Array([0x48, 0x65]));\n    await stream.pipe(readableStream);\n  });\n});\n\n\u002F\u002F Text stream\napp.get(\"\u002Fstream-text\", (c) => {\n  return streamText(c, async (stream) => {\n    await stream.writeln(\"Hello\");\n    await stream.sleep(1000);\n    await stream.write(\"World\");\n  });\n});\n\n\u002F\u002F Server-Sent Events\napp.get(\"\u002Fsse\", (c) => {\n  return streamSSE(c, async (stream) => {\n    let id = 0;\n    while (true) {\n      await stream.writeSSE({\n        data: JSON.stringify({ time: new Date().toISOString() }),\n        event: \"time-update\",\n        id: String(id++),\n      });\n      await stream.sleep(1000);\n    }\n  });\n});\n```\n\n---\n\n## Testing with app.request()\n\nTest endpoints without starting an HTTP server:\n\n```ts\n\u002F\u002F GET\nconst res = await app.request(\"\u002Fposts\");\nexpect(res.status).toBe(200);\nexpect(await res.json()).toEqual({ posts: [] });\n\n\u002F\u002F POST with JSON\nconst res = await app.request(\"\u002Fposts\", {\n  method: \"POST\",\n  body: JSON.stringify({ title: \"Hello\" }),\n  headers: { \"Content-Type\": \"application\u002Fjson\" },\n});\n\n\u002F\u002F POST with FormData\nconst formData = new FormData();\nformData.append(\"name\", \"Alice\");\nconst res = await app.request(\"\u002Fusers\", { method: \"POST\", body: formData });\n\n\u002F\u002F With mock env (Cloudflare Workers bindings)\nconst res = await app.request(\"\u002Fapi\u002Fdata\", {}, { KV: mockKV, DATABASE: mockDB });\n\n\u002F\u002F Using Request object\nconst req = new Request(\"http:\u002F\u002Flocalhost\u002Fapi\", { method: \"DELETE\" });\nconst res = await app.request(req);\n```\n\n---\n\n## Hono Client (RPC)\n\nType-safe API client using shared types between server and client.\n\n**IMPORTANT: Routes MUST be chained for type inference to work. Without chaining, the client cannot infer route types.**\n\n```ts\n\u002F\u002F Server: routes MUST be chained to preserve types\nconst route = app\n  .post(\"\u002Fposts\", zValidator(\"json\", schema), (c) => {\n    return c.json({ ok: true }, 201);\n  })\n  .get(\"\u002Fposts\", (c) => {\n    return c.json({ posts: [] });\n  });\nexport type AppType = typeof route;\n\n\u002F\u002F Client: use hc() with the exported type\nimport { hc } from \"hono\u002Fclient\";\nimport type { AppType } from \".\u002Fserver\";\n\nconst client = hc\u003CAppType>(\"http:\u002F\u002Flocalhost:8787\u002F\");\nconst res = await client.posts.$post({ json: { title: \"Hello\" } });\nconst data = await res.json(); \u002F\u002F fully typed\n```\n\nType utilities:\n\n```ts\nimport type { InferRequestType, InferResponseType } from \"hono\u002Fclient\";\n\ntype ReqType = InferRequestType\u003Ctypeof client.posts.$post>;\ntype ResType = InferResponseType\u003Ctypeof client.posts.$post, 200>;\n```\n\n---\n\n## Helpers\n\nHelpers are utility functions imported from `hono\u002F\u003Chelper-name>`:\n\n```ts\nimport { getConnInfo } from \"hono\u002Fconninfo\";\nimport { getCookie, setCookie, deleteCookie } from \"hono\u002Fcookie\";\nimport { css, Style } from \"hono\u002Fcss\";\nimport { createFactory } from \"hono\u002Ffactory\";\nimport { html, raw } from \"hono\u002Fhtml\";\nimport { stream, streamText, streamSSE } from \"hono\u002Fstreaming\";\nimport { testClient } from \"hono\u002Ftesting\";\nimport { upgradeWebSocket } from \"hono\u002Fcloudflare-workers\"; \u002F\u002F or other adapter\n```\n\nAvailable helpers: Accepts, Adapter, ConnInfo, Cookie, css, Dev, Factory, html, JWT, Proxy, Route, SSG, Streaming, Testing, WebSocket.\n\nFor details, use `npx hono docs \u002Fdocs\u002Fhelpers\u002F\u003Chelper-name>`.\n\n### Factory\n\nUse `createFactory` to define `Env` once and share it across app, middleware, and handlers:\n\n```ts\nimport { createFactory } from \"hono\u002Ffactory\";\n\nconst factory = createFactory\u003CEnv>();\n\n\u002F\u002F Create app (Env type is inherited)\nconst app = factory.createApp();\n\n\u002F\u002F Create middleware (Env type is inherited, no need to pass generics)\nconst mw = factory.createMiddleware(async (c, next) => {\n  await next();\n});\n\n\u002F\u002F Create handlers separately (preserves type inference)\nconst handlers = factory.createHandlers(logger(), (c) => c.json({ message: \"Hello\" }));\napp.get(\"\u002Fapi\", ...handlers);\n```\n\n---\n\n## Best Practices\n\n- Write handlers inline in route definitions for proper type inference of path params.\n- Use `app.route()` to organize large apps by feature, not Rails-style controllers.\n- Use `createFactory()` to share Env type across app, middleware, and handlers.\n- Use `c.set()`\u002F`c.get()` to pass data between middleware and handlers.\n- Chain validators for multiple request parts (param + query + json).\n- Export app type for RPC: `export type AppType = typeof routes`\n- Use `app.request()` for testing — no server startup needed.\n\n## Adapters\n\nHono runs on multiple runtimes. The default export works for Cloudflare Workers, Deno, and Bun. For Node.js, use the Node adapter:\n\n```ts\n\u002F\u002F Cloudflare Workers \u002F Deno \u002F Bun\nexport default app;\n\n\u002F\u002F Node.js\nimport { serve } from \"@hono\u002Fnode-server\";\nserve(app);\n```\n","# Hono Skill\n\nBuild Hono web applications. This skill provides inline API knowledge for AI. Use `npx hono request` to test endpoints. If the `hono-docs` MCP server is configured, prefer its tools for the latest documentation over the inline reference.\n\n## Hono CLI Usage\n\n### Request Testing\n\nTest endpoints without starting an HTTP server. Uses `app.request()` internally.\n\n```bash\n# GET request\nnpx hono request [file] -P \u002Fpath\n\n# POST request with JSON body\nnpx hono request [file] -X POST -P \u002Fapi\u002Fusers -d '{\"name\": \"test\"}'\n```\n\n**Note:** Do not pass credentials directly in CLI arguments. Use environment variables for sensitive values. `hono request` does not support Cloudflare Workers bindings (KV, D1, R2, etc.). When bindings are required, use `workers-fetch` instead:\n\n```bash\nnpx workers-fetch \u002Fpath\nnpx workers-fetch -X POST -H \"Content-Type:application\u002Fjson\" -d '{\"name\":\"test\"}' \u002Fapi\u002Fusers\n```\n\n---\n\n## Hono API Reference\n\n### App Constructor\n\n```ts\nimport { Hono } from \"hono\";\n\nconst app = new Hono();\n\n\u002F\u002F With TypeScript generics\ntype Env = {\n  Bindings: { DATABASE: D1Database; KV: KVNamespace };\n  Variables: { user: User };\n};\nconst app = new Hono\u003CEnv>();\n```\n\n### Routing Methods\n\n```ts\napp.get(\"\u002Fpath\", handler);\napp.post(\"\u002Fpath\", handler);\napp.put(\"\u002Fpath\", handler);\napp.delete(\"\u002Fpath\", handler);\napp.patch(\"\u002Fpath\", handler);\napp.options(\"\u002Fpath\", handler);\napp.all(\"\u002Fpath\", handler); \u002F\u002F all HTTP methods\napp.on(\"PURGE\", \"\u002Fpath\", handler); \u002F\u002F custom method\napp.on([\"PUT\", \"DELETE\"], \"\u002Fpath\", handler); \u002F\u002F multiple methods\n```\n\n### Routing Patterns\n\n```ts\n\u002F\u002F Path parameters\napp.get(\"\u002Fuser\u002F:name\", (c) => {\n  const name = c.req.param(\"name\");\n  return c.json({ name });\n});\n\n\u002F\u002F Multiple params\napp.get(\"\u002Fposts\u002F:id\u002Fcomments\u002F:commentId\", (c) => {\n  const { id, commentId } = c.req.param();\n});\n\n\u002F\u002F Optional parameters\napp.get(\"\u002Fapi\u002Fanimal\u002F:type?\", (c) => c.text(\"Animal!\"));\n\n\u002F\u002F Wildcards\napp.get(\"\u002Fwild\u002F*\u002Fcard\", (c) => c.text(\"Wildcard\"));\n\n\u002F\u002F Regexp constraints\napp.get(\"\u002Fpost\u002F:date{[0-9]+}\u002F:title{[a-z]+}\", (c) => {\n  const { date, title } = c.req.param();\n});\n\n\u002F\u002F Chained routes\napp\n  .get(\"\u002Fendpoint\", (c) => c.text(\"GET\"))\n  .post((c) => c.text(\"POST\"))\n  .delete((c) => c.text(\"DELETE\"));\n```\n\n### Route Grouping\n\n```ts\n\u002F\u002F Using route()\nconst api = new Hono();\napi.get(\"\u002Fusers\", (c) => c.json([]));\n\nconst app = new Hono();\napp.route(\"\u002Fapi\", api); \u002F\u002F mounts at \u002Fapi\u002Fusers\n\n\u002F\u002F Using basePath()\nconst app = new Hono().basePath(\"\u002Fapi\");\napp.get(\"\u002Fusers\", (c) => c.json([])); \u002F\u002F GET \u002Fapi\u002Fusers\n```\n\n### Error Handling\n\n```ts\napp.notFound((c) => c.json({ message: \"Not Found\" }, 404));\n\napp.onError((err, c) => {\n  console.error(err);\n  return c.json({ message: \"Internal Server Error\" }, 500);\n});\n```\n\n---\n\n## Context (c)\n\n### Response Methods\n\n```ts\nc.text(\"Hello\"); \u002F\u002F text\u002Fplain\nc.json({ message: \"Hello\" }); \u002F\u002F application\u002Fjson\nc.html(\"\u003Ch1>Hello\u003C\u002Fh1>\"); \u002F\u002F text\u002Fhtml\nc.redirect(\"\u002Fnew-path\"); \u002F\u002F 302 redirect\nc.redirect(\"\u002Fnew-path\", 301); \u002F\u002F 301 redirect\nc.body(\"raw body\", 200, headers); \u002F\u002F raw response\nc.notFound(); \u002F\u002F 404 response\n```\n\n### Headers & Status\n\n```ts\nc.status(201);\nc.header(\"X-Custom\", \"value\");\nc.header(\"Cache-Control\", \"no-store\");\n```\n\n### Variables (request-scoped data)\n\n```ts\n\u002F\u002F In middleware\nc.set(\"user\", { id: 1, name: \"Alice\" });\n\n\u002F\u002F In handler\nconst user = c.get(\"user\");\n\u002F\u002F or\nconst user = c.var.user;\n```\n\n### Environment (Cloudflare Workers)\n\n```ts\nconst value = await c.env.KV.get(\"key\");\nconst db = c.env.DATABASE;\nc.executionCtx.waitUntil(promise);\n```\n\n### Renderer\n\n```ts\napp.use(async (c, next) => {\n  c.setRenderer((content) =>\n    c.html(\n      \u003Chtml>\u003Cbody>{content}\u003C\u002Fbody>\u003C\u002Fhtml>\n    )\n  )\n  await next()\n})\n\napp.get('\u002F', (c) => c.render(\u003Ch1>Hello\u003C\u002Fh1>))\n```\n\n---\n\n## HonoRequest (c.req)\n\n```ts\nc.req.param(\"id\"); \u002F\u002F path parameter\nc.req.param(); \u002F\u002F all path params as object\nc.req.query(\"page\"); \u002F\u002F query string parameter\nc.req.query(); \u002F\u002F all query params as object\nc.req.queries(\"tags\"); \u002F\u002F multiple values: ?tags=A&tags=B → ['A', 'B']\nc.req.header(\"Authorization\"); \u002F\u002F request header\nc.req.header(); \u002F\u002F all headers (keys are lowercase)\n\n\u002F\u002F Body parsing\nawait c.req.json(); \u002F\u002F parse JSON body\nawait c.req.text(); \u002F\u002F parse text body\nawait c.req.formData(); \u002F\u002F parse as FormData\nawait c.req.parseBody(); \u002F\u002F parse multipart\u002Fform-data or urlencoded\nawait c.req.arrayBuffer(); \u002F\u002F parse as ArrayBuffer\nawait c.req.blob(); \u002F\u002F parse as Blob\n\n\u002F\u002F Validated data (used with validator middleware)\nc.req.valid(\"json\");\nc.req.valid(\"query\");\nc.req.valid(\"form\");\nc.req.valid(\"param\");\n\n\u002F\u002F Properties\nc.req.url; \u002F\u002F full URL string\nc.req.path; \u002F\u002F pathname\nc.req.method; \u002F\u002F HTTP method\nc.req.raw; \u002F\u002F underlying Request object\n```\n\n---\n\n## Middleware\n\n### Using Built-in Middleware\n\n```ts\nimport { cors } from \"hono\u002Fcors\";\nimport { logger } from \"hono\u002Flogger\";\nimport { basicAuth } from \"hono\u002Fbasic-auth\";\nimport { prettyJSON } from \"hono\u002Fpretty-json\";\nimport { secureHeaders } from \"hono\u002Fsecure-headers\";\nimport { etag } from \"hono\u002Fetag\";\nimport { compress } from \"hono\u002Fcompress\";\nimport { poweredBy } from \"hono\u002Fpowered-by\";\nimport { timing } from \"hono\u002Ftiming\";\nimport { cache } from \"hono\u002Fcache\";\nimport { bearerAuth } from \"hono\u002Fbearer-auth\";\nimport { jwt } from \"hono\u002Fjwt\";\nimport { csrf } from \"hono\u002Fcsrf\";\nimport { ipRestriction } from \"hono\u002Fip-restriction\";\nimport { bodyLimit } from \"hono\u002Fbody-limit\";\nimport { requestId } from \"hono\u002Frequest-id\";\nimport { methodOverride } from \"hono\u002Fmethod-override\";\nimport { trailingSlash, trimTrailingSlash } from \"hono\u002Ftrailing-slash\";\n\n\u002F\u002F Registration\napp.use(logger()); \u002F\u002F all routes\napp.use(\"\u002Fapi\u002F*\", cors()); \u002F\u002F specific path\napp.post(\"\u002Fapi\u002F*\", basicAuth({ username: \"admin\", password: \"secret\" }));\n```\n\n### Custom Middleware\n\n```ts\n\u002F\u002F Inline\napp.use(async (c, next) => {\n  const start = Date.now();\n  await next();\n  const elapsed = Date.now() - start;\n  c.res.headers.set(\"X-Response-Time\", `${elapsed}ms`);\n});\n\n\u002F\u002F Reusable with createMiddleware\nimport { createMiddleware } from \"hono\u002Ffactory\";\n\nconst auth = createMiddleware(async (c, next) => {\n  const token = c.req.header(\"Authorization\");\n  if (!token) return c.json({ error: \"Unauthorized\" }, 401);\n  await next();\n});\n\napp.use(\"\u002Fapi\u002F*\", auth);\n```\n\n### Middleware Execution Order\n\nMiddleware executes in registration order. `await next()` calls the next middleware\u002Fhandler, and code after `next()` runs on the way back:\n\n```\nRequest → mw1 before → mw2 before → handler → mw2 after → mw1 after → Response\n```\n\n```ts\napp.use(async (c, next) => {\n  \u002F\u002F before handler\n  await next();\n  \u002F\u002F after handler\n});\n```\n\n---\n\n## Validation\n\nValidation targets: `json`, `form`, `query`, `header`, `param`, `cookie`.\n\n### Zod Validator\n\n```ts\nimport { zValidator } from \"@hono\u002Fzod-validator\";\nimport { z } from \"zod\";\n\nconst schema = z.object({\n  title: z.string().min(1),\n  body: z.string(),\n});\n\napp.post(\"\u002Fposts\", zValidator(\"json\", schema), (c) => {\n  const data = c.req.valid(\"json\"); \u002F\u002F fully typed\n  return c.json(data, 201);\n});\n```\n\n### Valibot \u002F Standard Schema Validator\n\n```ts\nimport { sValidator } from \"@hono\u002Fstandard-validator\";\nimport * as v from \"valibot\";\n\nconst schema = v.object({ name: v.string(), age: v.number() });\n\napp.post(\"\u002Fusers\", sValidator(\"json\", schema), (c) => {\n  const data = c.req.valid(\"json\");\n  return c.json(data, 201);\n});\n```\n\n---\n\n## JSX\n\n### Setup\n\nIn `tsconfig.json`:\n\n```json\n{\n  \"compilerOptions\": {\n    \"jsx\": \"react-jsx\",\n    \"jsxImportSource\": \"hono\u002Fjsx\"\n  }\n}\n```\n\nOr use pragma: `\u002F** @jsxImportSource hono\u002Fjsx *\u002F`\n\n**Important:** Files using JSX must have a `.tsx` extension. Rename `.ts` to `.tsx` or the compiler will fail.\n\n### Components\n\n```tsx\nimport type { PropsWithChildren } from \"hono\u002Fjsx\";\n\nconst Layout = (props: PropsWithChildren) => (\n  \u003Chtml>\n    \u003Chead>\n      \u003Ctitle>My App\u003C\u002Ftitle>\n    \u003C\u002Fhead>\n    \u003Cbody>{props.children}\u003C\u002Fbody>\n  \u003C\u002Fhtml>\n);\n\nconst UserCard = ({ name }: { name: string }) => (\n  \u003Cdiv class=\"card\">\n    \u003Ch2>{name}\u003C\u002Fh2>\n  \u003C\u002Fdiv>\n);\n\napp.get(\"\u002F\", (c) => {\n  return c.html(\n    \u003CLayout>\n      \u003CUserCard name=\"Alice\" \u002F>\n    \u003C\u002FLayout>,\n  );\n});\n```\n\n### jsxRenderer Middleware\n\nUse `jsxRenderer` middleware for layouts. See `npx hono docs \u002Fdocs\u002Fmiddleware\u002Fbuiltin\u002Fjsx-renderer` for details.\n\n### Async Components\n\n```tsx\nconst UserList = async () => {\n  const users = await fetchUsers();\n  return (\n    \u003Cul>\n      {users.map((u) => (\n        \u003Cli>{u.name}\u003C\u002Fli>\n      ))}\n    \u003C\u002Ful>\n  );\n};\n```\n\n### Fragments\n\n```tsx\nconst Items = () => (\n  \u003C>\n    \u003Cli>Item 1\u003C\u002Fli>\n    \u003Cli>Item 2\u003C\u002Fli>\n  \u003C\u002F>\n);\n```\n\n---\n\n## Streaming\n\n```ts\nimport { stream, streamText, streamSSE } from \"hono\u002Fstreaming\";\n\n\u002F\u002F Basic stream\napp.get(\"\u002Fstream\", (c) => {\n  return stream(c, async (stream) => {\n    stream.onAbort(() => console.log(\"Aborted\"));\n    await stream.write(new Uint8Array([0x48, 0x65]));\n    await stream.pipe(readableStream);\n  });\n});\n\n\u002F\u002F Text stream\napp.get(\"\u002Fstream-text\", (c) => {\n  return streamText(c, async (stream) => {\n    await stream.writeln(\"Hello\");\n    await stream.sleep(1000);\n    await stream.write(\"World\");\n  });\n});\n\n\u002F\u002F Server-Sent Events\napp.get(\"\u002Fsse\", (c) => {\n  return streamSSE(c, async (stream) => {\n    let id = 0;\n    while (true) {\n      await stream.writeSSE({\n        data: JSON.stringify({ time: new Date().toISOString() }),\n        event: \"time-update\",\n        id: String(id++),\n      });\n      await stream.sleep(1000);\n    }\n  });\n});\n```\n\n---\n\n## Testing with app.request()\n\nTest endpoints without starting an HTTP server:\n\n```ts\n\u002F\u002F GET\nconst res = await app.request(\"\u002Fposts\");\nexpect(res.status).toBe(200);\nexpect(await res.json()).toEqual({ posts: [] });\n\n\u002F\u002F POST with JSON\nconst res = await app.request(\"\u002Fposts\", {\n  method: \"POST\",\n  body: JSON.stringify({ title: \"Hello\" }),\n  headers: { \"Content-Type\": \"application\u002Fjson\" },\n});\n\n\u002F\u002F POST with FormData\nconst formData = new FormData();\nformData.append(\"name\", \"Alice\");\nconst res = await app.request(\"\u002Fusers\", { method: \"POST\", body: formData });\n\n\u002F\u002F With mock env (Cloudflare Workers bindings)\nconst res = await app.request(\"\u002Fapi\u002Fdata\", {}, { KV: mockKV, DATABASE: mockDB });\n\n\u002F\u002F Using Request object\nconst req = new Request(\"http:\u002F\u002Flocalhost\u002Fapi\", { method: \"DELETE\" });\nconst res = await app.request(req);\n```\n\n---\n\n## Hono Client (RPC)\n\nType-safe API client using shared types between server and client.\n\n**IMPORTANT: Routes MUST be chained for type inference to work. Without chaining, the client cannot infer route types.**\n\n```ts\n\u002F\u002F Server: routes MUST be chained to preserve types\nconst route = app\n  .post(\"\u002Fposts\", zValidator(\"json\", schema), (c) => {\n    return c.json({ ok: true }, 201);\n  })\n  .get(\"\u002Fposts\", (c) => {\n    return c.json({ posts: [] });\n  });\nexport type AppType = typeof route;\n\n\u002F\u002F Client: use hc() with the exported type\nimport { hc } from \"hono\u002Fclient\";\nimport type { AppType } from \".\u002Fserver\";\n\nconst client = hc\u003CAppType>(\"http:\u002F\u002Flocalhost:8787\u002F\");\nconst res = await client.posts.$post({ json: { title: \"Hello\" } });\nconst data = await res.json(); \u002F\u002F fully typed\n```\n\nType utilities:\n\n```ts\nimport type { InferRequestType, InferResponseType } from \"hono\u002Fclient\";\n\ntype ReqType = InferRequestType\u003Ctypeof client.posts.$post>;\ntype ResType = InferResponseType\u003Ctypeof client.posts.$post, 200>;\n```\n\n---\n\n## Helpers\n\nHelpers are utility functions imported from `hono\u002F\u003Chelper-name>`:\n\n```ts\nimport { getConnInfo } from \"hono\u002Fconninfo\";\nimport { getCookie, setCookie, deleteCookie } from \"hono\u002Fcookie\";\nimport { css, Style } from \"hono\u002Fcss\";\nimport { createFactory } from \"hono\u002Ffactory\";\nimport { html, raw } from \"hono\u002Fhtml\";\nimport { stream, streamText, streamSSE } from \"hono\u002Fstreaming\";\nimport { testClient } from \"hono\u002Ftesting\";\nimport { upgradeWebSocket } from \"hono\u002Fcloudflare-workers\"; \u002F\u002F or other adapter\n```\n\nAvailable helpers: Accepts, Adapter, ConnInfo, Cookie, css, Dev, Factory, html, JWT, Proxy, Route, SSG, Streaming, Testing, WebSocket.\n\nFor details, use `npx hono docs \u002Fdocs\u002Fhelpers\u002F\u003Chelper-name>`.\n\n### Factory\n\nUse `createFactory` to define `Env` once and share it across app, middleware, and handlers:\n\n```ts\nimport { createFactory } from \"hono\u002Ffactory\";\n\nconst factory = createFactory\u003CEnv>();\n\n\u002F\u002F Create app (Env type is inherited)\nconst app = factory.createApp();\n\n\u002F\u002F Create middleware (Env type is inherited, no need to pass generics)\nconst mw = factory.createMiddleware(async (c, next) => {\n  await next();\n});\n\n\u002F\u002F Create handlers separately (preserves type inference)\nconst handlers = factory.createHandlers(logger(), (c) => c.json({ message: \"Hello\" }));\napp.get(\"\u002Fapi\", ...handlers);\n```\n\n---\n\n## Best Practices\n\n- Write handlers inline in route definitions for proper type inference of path params.\n- Use `app.route()` to organize large apps by feature, not Rails-style controllers.\n- Use `createFactory()` to share Env type across app, middleware, and handlers.\n- Use `c.set()`\u002F`c.get()` to pass data between middleware and handlers.\n- Chain validators for multiple request parts (param + query + json).\n- Export app type for RPC: `export type AppType = typeof routes`\n- Use `app.request()` for testing — no server startup needed.\n\n## Adapters\n\nHono runs on multiple runtimes. The default export works for Cloudflare Workers, Deno, and Bun. For Node.js, use the Node adapter:\n\n```ts\n\u002F\u002F Cloudflare Workers \u002F Deno \u002F Bun\nexport default app;\n\n\u002F\u002F Node.js\nimport { serve } from \"@hono\u002Fnode-server\";\nserve(app);\n```\n",{"name":5,"description":7},"main","a9b682706866a1a8a44c4621910184c29456acff",".agents\u002Fskills\u002Fhono","https:\u002F\u002Fgithub.com\u002Fescwxyz\u002Fskills-re","https:\u002F\u002Fskills.re","https:\u002F\u002Fgithub.com\u002Fescwxyz","https:\u002F\u002Fjiewa.ng","No Ads please","2026-09-25 02:54:36",{"source":8,"skillName":5,"commands":58,"prompt":67,"zipAvailable":68},[59,61,64],{"tool":33,"command":60},"npx skills add escwxyz\u002Fskills-re --skill hono",{"tool":62,"command":63},"bunx","bunx skills add escwxyz\u002Fskills-re --skill hono",{"tool":65,"command":66},"pnpm","pnpm dlx skills add escwxyz\u002Fskills-re --skill hono","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fescwxyz\u002Fskills-re\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fhono\u002FSKILL.md",true,[],[71,88,114,130,141,156],{"skillNo":72,"name":73,"nameZh":6,"description":74,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":75,"categories":76,"keywords":80,"publishedAt":43},"01M71B3BPYHEEQJYA6QV653HC0","demo-skill","Demonstrates a minimal Skills.re sandbox eval fixture.","https:\u002F\u002Fgithub.com\u002Fescwxyz\u002Fskills-re\u002Fblob\u002Fmain\u002Fapps\u002Fserver\u002Fsrc\u002Fskill-eval-sandbox\u002Ffixtures\u002Fdemo-skill\u002FSKILL.md",[77],{"categoryNo":78,"categoryName":79,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发",[81,82,83,84,85,86,87],"demo skill","demo","re","sandbox","eval","minimal","fixture",{"skillNo":89,"name":90,"nameZh":6,"description":91,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":92,"licenseFileUrl":6,"sourceHtmlUrl":93,"categories":94,"keywords":95,"publishedAt":43},"01M71B3A4941E8D6VHFE4FTDMQ","openspec-propose","Propose a new change with all artifacts generated in one step. Use when the user wants to quickly describe what they want to build and get a complete proposal with design, specs, and tasks ready for implementation.","MIT","https:\u002F\u002Fgithub.com\u002Fescwxyz\u002Fskills-re\u002Fblob\u002Fmain\u002F.codex\u002Fskills\u002Fopenspec-propose\u002FSKILL.md",[],[96,97,98,99,100,101,102,103,104,105,106,107,40,32,108,109,110,111,112,113],"openspec","openspec propose","ready","tasks","implementation","build","cli","change","with","one","proposal","artifacts","generated","new","design","complete","for","get",{"skillNo":115,"name":116,"nameZh":6,"description":117,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":92,"licenseFileUrl":6,"sourceHtmlUrl":118,"categories":119,"keywords":120,"publishedAt":43},"01M71B38FC73TG0F7BM5582Y8V","openspec-explore","Enter explore mode - a thinking partner for exploring ideas, investigating problems, and clarifying requirements. Use when the user wants to think through something before or during a change.","https:\u002F\u002Fgithub.com\u002Fescwxyz\u002Fskills-re\u002Fblob\u002Fmain\u002F.codex\u002Fskills\u002Fopenspec-explore\u002FSKILL.md",[],[96,121,122,25,123,124,125,102,103,126,40,32,127,112,128,129],"explore","openspec explore","think","thinking partner","partner","requirements","mode","thinking","ideas",{"skillNo":131,"name":132,"nameZh":6,"description":133,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":92,"licenseFileUrl":6,"sourceHtmlUrl":134,"categories":135,"keywords":136,"publishedAt":43},"01M71B36MP2EPJ15XAZNAWBN3P","openspec-archive-change","Archive a completed change in the experimental workflow. Use when the user wants to finalize and archive a change after implementation is complete.","https:\u002F\u002Fgithub.com\u002Fescwxyz\u002Fskills-re\u002Fblob\u002Fmain\u002F.codex\u002Fskills\u002Fopenspec-archive-change\u002FSKILL.md",[],[137,96,138,103,25,139,40,32,100,140,111,102],"openspec archive change","archive","workflow","finalize",{"skillNo":142,"name":143,"nameZh":6,"description":144,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":92,"licenseFileUrl":6,"sourceHtmlUrl":145,"categories":146,"keywords":147,"publishedAt":43},"01M71B34X135R0FZXKKYB8K8TP","openspec-apply-change","Implement tasks from an OpenSpec change. Use when the user wants to start implementing, continue implementation, or work through tasks.","https:\u002F\u002Fgithub.com\u002Fescwxyz\u002Fskills-re\u002Fblob\u002Fmain\u002F.codex\u002Fskills\u002Fopenspec-apply-change\u002FSKILL.md",[],[148,103,96,149,150,151,102,152,25,99,40,32,100,153,154,155],"openspec apply change","openspec apply","apply","work","implement","start","implementing","continue",{"skillNo":157,"name":90,"nameZh":6,"description":91,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":92,"licenseFileUrl":6,"sourceHtmlUrl":158,"categories":159,"keywords":160,"publishedAt":43},"01M71B33BXWGK230VKH621N147","https:\u002F\u002Fgithub.com\u002Fescwxyz\u002Fskills-re\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fopenspec-propose\u002FSKILL.md",[],[97,96,40,100,101,102,103,104,105,99,107,32,108,109,110,111,112,113,106,98]]