Showing preview only (2,460K chars total). Download the full file or copy to clipboard to get everything.
Repository: honojs/hono
Branch: main
Commit: fe689eceb783
Files: 479
Total size: 2.3 MB
Directory structure:
gitextract_1kthqqhw/
├── .devcontainer/
│ ├── Dockerfile
│ ├── devcontainer.json
│ └── docker-compose.yml
├── .editorconfig
├── .github/
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE/
│ │ ├── 1-bug-report.yml
│ │ ├── 2-feature-request.yml
│ │ └── config.yml
│ ├── actions/
│ │ └── perf-measures/
│ │ └── action.yml
│ ├── pull_request_template.md
│ └── workflows/
│ ├── autofix.yml
│ ├── ci.yml
│ ├── cr.yml
│ ├── no-response.yml
│ └── release.yml
├── .gitignore
├── .gitpod.yml
├── .prettierrc
├── .tool-versions
├── .vitest.config/
│ └── setup-vitest.ts
├── .vscode/
│ ├── extensions.json
│ └── settings.json
├── LICENSE
├── README.md
├── benchmarks/
│ ├── deno/
│ │ ├── .gitignore
│ │ ├── .vscode/
│ │ │ └── settings.json
│ │ ├── fast.ts
│ │ ├── faster.ts
│ │ ├── hono.ts
│ │ ├── magalo.ts
│ │ ├── oak.ts
│ │ └── opine.ts
│ ├── handle-event/
│ │ ├── index.js
│ │ └── package.json
│ ├── http-server/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ └── benchmark.ts
│ ├── jsx/
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── benchmark.ts
│ │ │ ├── hono.ts
│ │ │ ├── nano.ts
│ │ │ ├── page-react.tsx
│ │ │ ├── page.tsx
│ │ │ ├── preact.ts
│ │ │ ├── react-jsx/
│ │ │ │ ├── benchmark.ts
│ │ │ │ ├── hono.ts
│ │ │ │ ├── nano.ts
│ │ │ │ ├── page-hono.tsx
│ │ │ │ ├── page-nano.tsx
│ │ │ │ ├── page-preact.tsx
│ │ │ │ ├── page-react.tsx
│ │ │ │ ├── preact.ts
│ │ │ │ ├── react.ts
│ │ │ │ └── tsconfig.json
│ │ │ └── react.ts
│ │ └── tsconfig.json
│ ├── query-param/
│ │ ├── bun.lockb
│ │ ├── package.json
│ │ └── src/
│ │ ├── bench.mts
│ │ ├── fast-querystring.mts
│ │ ├── hono.mts
│ │ └── qs.mts
│ ├── routers/
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── bench-includes-init.mts
│ │ │ ├── bench.mts
│ │ │ ├── express.mts
│ │ │ ├── find-my-way.mts
│ │ │ ├── hono.mts
│ │ │ ├── koa-router.mts
│ │ │ ├── koa-tree-router.mts
│ │ │ ├── medley-router.mts
│ │ │ ├── memoirist.mts
│ │ │ ├── radix3.mts
│ │ │ ├── rou3.mts
│ │ │ ├── tool.mts
│ │ │ └── trek-router.mts
│ │ └── tsconfig.json
│ ├── routers-deno/
│ │ ├── .vscode/
│ │ │ └── settings.json
│ │ ├── README.md
│ │ ├── deno.json
│ │ └── src/
│ │ ├── bench.mts
│ │ ├── find-my-way.mts
│ │ ├── hono.mts
│ │ ├── koa-router.mts
│ │ ├── koa-tree-router.mts
│ │ ├── medley-router.mts
│ │ ├── tool.mts
│ │ └── trek-router.mts
│ ├── utils/
│ │ ├── .gitignore
│ │ ├── package.json
│ │ └── src/
│ │ ├── get-path.ts
│ │ └── loop.js
│ └── webapp/
│ ├── .gitignore
│ ├── hono.js
│ ├── itty-router.js
│ ├── package.json
│ └── sunder.js
├── build/
│ ├── build.ts
│ ├── remove-private-fields.test.ts
│ ├── remove-private-fields.ts
│ ├── validate-exports.test.ts
│ └── validate-exports.ts
├── bunfig.toml
├── codecov.yml
├── docs/
│ ├── CODE_OF_CONDUCT.md
│ ├── CONTRIBUTING.md
│ ├── MIGRATION.md
│ └── images/
│ ├── hono-logo.pxm
│ └── hono-title.pxm
├── eslint.config.mjs
├── jsr.json
├── package.cjs.json
├── package.json
├── perf-measures/
│ ├── .octocov.consolidated.perf-measures.main.yml
│ ├── .octocov.consolidated.perf-measures.yml
│ ├── bundle-check/
│ │ ├── .gitignore
│ │ └── scripts/
│ │ └── check-bundle-size.ts
│ └── type-check/
│ ├── .gitignore
│ ├── client.ts
│ ├── scripts/
│ │ ├── generate-app.ts
│ │ ├── process-results.ts
│ │ └── tsconfig.json
│ └── tsconfig.build.json
├── runtime-tests/
│ ├── bun/
│ │ ├── .static/
│ │ │ └── plain.txt
│ │ ├── color.test.ts
│ │ ├── index.test.tsx
│ │ ├── static/
│ │ │ ├── download
│ │ │ ├── hello.world/
│ │ │ │ └── index.html
│ │ │ ├── helloworld/
│ │ │ │ └── index.html
│ │ │ └── plain.txt
│ │ ├── static-absolute-root/
│ │ │ └── plain.txt
│ │ └── tsconfig.json
│ ├── deno/
│ │ ├── .static/
│ │ │ └── plain.txt
│ │ ├── .vscode/
│ │ │ └── settings.json
│ │ ├── deno.json
│ │ ├── hono.test.ts
│ │ ├── middleware.test.tsx
│ │ ├── ssg.test.tsx
│ │ ├── static/
│ │ │ ├── download
│ │ │ ├── hello.world/
│ │ │ │ └── index.html
│ │ │ ├── helloworld/
│ │ │ │ └── index.html
│ │ │ └── plain.txt
│ │ ├── static-absolute-root/
│ │ │ └── plain.txt
│ │ └── stream.test.ts
│ ├── deno-jsx/
│ │ ├── deno.precompile.json
│ │ ├── deno.react-jsx.json
│ │ └── jsx.test.tsx
│ ├── fastly/
│ │ ├── index.test.ts
│ │ ├── tsconfig.json
│ │ └── vitest.config.ts
│ ├── lambda/
│ │ ├── index.test.ts
│ │ ├── mock.ts
│ │ ├── stream-mock.ts
│ │ ├── stream.test.ts
│ │ ├── tsconfig.json
│ │ └── vitest.config.ts
│ ├── lambda-edge/
│ │ ├── index.test.ts
│ │ ├── tsconfig.json
│ │ └── vitest.config.ts
│ ├── node/
│ │ ├── index.test.ts
│ │ ├── tsconfig.json
│ │ └── vitest.config.ts
│ └── workerd/
│ ├── index.test.ts
│ ├── index.ts
│ ├── tsconfig.json
│ └── vitest.config.ts
├── src/
│ ├── adapter/
│ │ ├── aws-lambda/
│ │ │ ├── conninfo.test.ts
│ │ │ ├── conninfo.ts
│ │ │ ├── handler.test.ts
│ │ │ ├── handler.ts
│ │ │ ├── index.ts
│ │ │ └── types.ts
│ │ ├── bun/
│ │ │ ├── conninfo.test.ts
│ │ │ ├── conninfo.ts
│ │ │ ├── index.ts
│ │ │ ├── serve-static.ts
│ │ │ ├── server.test.ts
│ │ │ ├── server.ts
│ │ │ ├── ssg.ts
│ │ │ ├── websocket.test.ts
│ │ │ └── websocket.ts
│ │ ├── cloudflare-pages/
│ │ │ ├── conninfo.test.ts
│ │ │ ├── conninfo.ts
│ │ │ ├── handler.test.ts
│ │ │ ├── handler.ts
│ │ │ └── index.ts
│ │ ├── cloudflare-workers/
│ │ │ ├── conninfo.test.ts
│ │ │ ├── conninfo.ts
│ │ │ ├── index.ts
│ │ │ ├── serve-static-module.ts
│ │ │ ├── serve-static.test.ts
│ │ │ ├── serve-static.ts
│ │ │ ├── utils.test.ts
│ │ │ ├── utils.ts
│ │ │ ├── websocket.test.ts
│ │ │ └── websocket.ts
│ │ ├── deno/
│ │ │ ├── conninfo.test.ts
│ │ │ ├── conninfo.ts
│ │ │ ├── deno.d.ts
│ │ │ ├── index.ts
│ │ │ ├── serve-static.ts
│ │ │ ├── ssg.ts
│ │ │ ├── websocket.test.ts
│ │ │ └── websocket.ts
│ │ ├── lambda-edge/
│ │ │ ├── conninfo.test.ts
│ │ │ ├── conninfo.ts
│ │ │ ├── handler.test.ts
│ │ │ ├── handler.ts
│ │ │ └── index.ts
│ │ ├── netlify/
│ │ │ ├── conninfo.test.ts
│ │ │ ├── conninfo.ts
│ │ │ ├── handler.ts
│ │ │ ├── index.ts
│ │ │ └── mod.ts
│ │ ├── service-worker/
│ │ │ ├── handler.test.ts
│ │ │ ├── handler.ts
│ │ │ ├── index.ts
│ │ │ └── types.ts
│ │ └── vercel/
│ │ ├── conninfo.test.ts
│ │ ├── conninfo.ts
│ │ ├── handler.test.ts
│ │ ├── handler.ts
│ │ └── index.ts
│ ├── client/
│ │ ├── client.test.ts
│ │ ├── client.ts
│ │ ├── fetch-result-please.ts
│ │ ├── index.ts
│ │ ├── types.test.ts
│ │ ├── types.ts
│ │ ├── utils.test.ts
│ │ └── utils.ts
│ ├── compose.test.ts
│ ├── compose.ts
│ ├── context.test.ts
│ ├── context.ts
│ ├── helper/
│ │ ├── accepts/
│ │ │ ├── accepts.test.ts
│ │ │ ├── accepts.ts
│ │ │ └── index.ts
│ │ ├── adapter/
│ │ │ ├── index.test.ts
│ │ │ └── index.ts
│ │ ├── conninfo/
│ │ │ ├── index.ts
│ │ │ └── types.ts
│ │ ├── cookie/
│ │ │ ├── index.test.ts
│ │ │ └── index.ts
│ │ ├── css/
│ │ │ ├── common.case.test.tsx
│ │ │ ├── common.ts
│ │ │ ├── index.test.tsx
│ │ │ └── index.ts
│ │ ├── dev/
│ │ │ ├── index.test.ts
│ │ │ └── index.ts
│ │ ├── factory/
│ │ │ ├── index.test.ts
│ │ │ └── index.ts
│ │ ├── html/
│ │ │ ├── index.test.ts
│ │ │ └── index.ts
│ │ ├── proxy/
│ │ │ ├── index.test.ts
│ │ │ └── index.ts
│ │ ├── route/
│ │ │ ├── index.test.ts
│ │ │ └── index.ts
│ │ ├── ssg/
│ │ │ ├── index.ts
│ │ │ ├── middleware.ts
│ │ │ ├── plugins.test.tsx
│ │ │ ├── plugins.ts
│ │ │ ├── ssg.test.tsx
│ │ │ ├── ssg.ts
│ │ │ ├── utils.test.ts
│ │ │ └── utils.ts
│ │ ├── streaming/
│ │ │ ├── index.ts
│ │ │ ├── sse.test.tsx
│ │ │ ├── sse.ts
│ │ │ ├── stream.test.ts
│ │ │ ├── stream.ts
│ │ │ ├── text.test.ts
│ │ │ ├── text.ts
│ │ │ └── utils.ts
│ │ ├── testing/
│ │ │ ├── index.test.ts
│ │ │ └── index.ts
│ │ └── websocket/
│ │ ├── index.test.ts
│ │ └── index.ts
│ ├── hono-base.ts
│ ├── hono.test.ts
│ ├── hono.ts
│ ├── http-exception.test.ts
│ ├── http-exception.ts
│ ├── index.ts
│ ├── jsx/
│ │ ├── base.test.tsx
│ │ ├── base.ts
│ │ ├── children.test.ts
│ │ ├── children.ts
│ │ ├── components.test.tsx
│ │ ├── components.ts
│ │ ├── constants.ts
│ │ ├── context.ts
│ │ ├── dom/
│ │ │ ├── client.test.tsx
│ │ │ ├── client.ts
│ │ │ ├── components.test.tsx
│ │ │ ├── components.ts
│ │ │ ├── context.test.tsx
│ │ │ ├── context.ts
│ │ │ ├── css.test.tsx
│ │ │ ├── css.ts
│ │ │ ├── hooks/
│ │ │ │ ├── index.test.tsx
│ │ │ │ └── index.ts
│ │ │ ├── index.test.tsx
│ │ │ ├── index.ts
│ │ │ ├── intrinsic-element/
│ │ │ │ ├── components.test.tsx
│ │ │ │ └── components.ts
│ │ │ ├── jsx-dev-runtime.ts
│ │ │ ├── jsx-runtime.ts
│ │ │ ├── render.ts
│ │ │ ├── server.test.tsx
│ │ │ ├── server.ts
│ │ │ └── utils.ts
│ │ ├── hooks/
│ │ │ ├── dom.test.tsx
│ │ │ ├── index.ts
│ │ │ └── string.test.tsx
│ │ ├── index.test.tsx
│ │ ├── index.ts
│ │ ├── intrinsic-element/
│ │ │ ├── common.ts
│ │ │ ├── components.test.tsx
│ │ │ └── components.ts
│ │ ├── intrinsic-elements.ts
│ │ ├── jsx-dev-runtime.ts
│ │ ├── jsx-runtime.test.tsx
│ │ ├── jsx-runtime.ts
│ │ ├── streaming.test.tsx
│ │ ├── streaming.ts
│ │ ├── types.ts
│ │ ├── utils.test.ts
│ │ └── utils.ts
│ ├── middleware/
│ │ ├── basic-auth/
│ │ │ ├── index.test.ts
│ │ │ └── index.ts
│ │ ├── bearer-auth/
│ │ │ ├── index.test.ts
│ │ │ └── index.ts
│ │ ├── body-limit/
│ │ │ ├── index.test.ts
│ │ │ └── index.ts
│ │ ├── cache/
│ │ │ ├── index.test.ts
│ │ │ └── index.ts
│ │ ├── combine/
│ │ │ ├── index.test.ts
│ │ │ └── index.ts
│ │ ├── compress/
│ │ │ ├── index.test.ts
│ │ │ └── index.ts
│ │ ├── context-storage/
│ │ │ ├── index.test.ts
│ │ │ └── index.ts
│ │ ├── cors/
│ │ │ ├── index.test.ts
│ │ │ └── index.ts
│ │ ├── csrf/
│ │ │ ├── index.test.ts
│ │ │ └── index.ts
│ │ ├── etag/
│ │ │ ├── digest.ts
│ │ │ ├── index.test.ts
│ │ │ └── index.ts
│ │ ├── ip-restriction/
│ │ │ ├── index.test.ts
│ │ │ └── index.ts
│ │ ├── jsx-renderer/
│ │ │ ├── index.test.tsx
│ │ │ └── index.ts
│ │ ├── jwk/
│ │ │ ├── index.test.ts
│ │ │ ├── index.ts
│ │ │ ├── jwk.ts
│ │ │ └── keys.test.json
│ │ ├── jwt/
│ │ │ ├── index.test.ts
│ │ │ ├── index.ts
│ │ │ └── jwt.ts
│ │ ├── language/
│ │ │ ├── index.test.ts
│ │ │ ├── index.ts
│ │ │ └── language.ts
│ │ ├── logger/
│ │ │ ├── index.test.ts
│ │ │ └── index.ts
│ │ ├── method-override/
│ │ │ ├── index.test.ts
│ │ │ └── index.ts
│ │ ├── powered-by/
│ │ │ ├── index.test.ts
│ │ │ └── index.ts
│ │ ├── pretty-json/
│ │ │ ├── index.test.ts
│ │ │ └── index.ts
│ │ ├── request-id/
│ │ │ ├── index.test.ts
│ │ │ ├── index.ts
│ │ │ └── request-id.ts
│ │ ├── secure-headers/
│ │ │ ├── index.test.ts
│ │ │ ├── index.ts
│ │ │ ├── permissions-policy.ts
│ │ │ └── secure-headers.ts
│ │ ├── serve-static/
│ │ │ ├── index.test.ts
│ │ │ ├── index.ts
│ │ │ ├── path.test.ts
│ │ │ └── path.ts
│ │ ├── timeout/
│ │ │ ├── index.test.ts
│ │ │ └── index.ts
│ │ ├── timing/
│ │ │ ├── index.test.ts
│ │ │ ├── index.ts
│ │ │ └── timing.ts
│ │ └── trailing-slash/
│ │ ├── index.test.ts
│ │ └── index.ts
│ ├── preset/
│ │ ├── quick.test.ts
│ │ ├── quick.ts
│ │ ├── tiny.test.ts
│ │ └── tiny.ts
│ ├── request/
│ │ └── constants.ts
│ ├── request.test.ts
│ ├── request.ts
│ ├── router/
│ │ ├── common.case.test.ts
│ │ ├── linear-router/
│ │ │ ├── index.ts
│ │ │ ├── router.test.ts
│ │ │ └── router.ts
│ │ ├── pattern-router/
│ │ │ ├── index.ts
│ │ │ ├── router.test.ts
│ │ │ └── router.ts
│ │ ├── reg-exp-router/
│ │ │ ├── index.ts
│ │ │ ├── matcher.ts
│ │ │ ├── node.ts
│ │ │ ├── prepared-router.test.ts
│ │ │ ├── prepared-router.ts
│ │ │ ├── router.test.ts
│ │ │ ├── router.ts
│ │ │ └── trie.ts
│ │ ├── smart-router/
│ │ │ ├── index.ts
│ │ │ ├── router.test.ts
│ │ │ └── router.ts
│ │ └── trie-router/
│ │ ├── index.ts
│ │ ├── node.test.ts
│ │ ├── node.ts
│ │ ├── router.test.ts
│ │ └── router.ts
│ ├── router.ts
│ ├── types.test.ts
│ ├── types.ts
│ ├── utils/
│ │ ├── accept.test.ts
│ │ ├── accept.ts
│ │ ├── basic-auth.test.ts
│ │ ├── basic-auth.ts
│ │ ├── body.test.ts
│ │ ├── body.ts
│ │ ├── buffer.test.ts
│ │ ├── buffer.ts
│ │ ├── color.test.ts
│ │ ├── color.ts
│ │ ├── compress.ts
│ │ ├── concurrent.test.ts
│ │ ├── concurrent.ts
│ │ ├── constants.ts
│ │ ├── cookie.test.ts
│ │ ├── cookie.ts
│ │ ├── crypto.test.ts
│ │ ├── crypto.ts
│ │ ├── encode.test.ts
│ │ ├── encode.ts
│ │ ├── filepath.test.ts
│ │ ├── filepath.ts
│ │ ├── handler.ts
│ │ ├── headers.ts
│ │ ├── html.test.ts
│ │ ├── html.ts
│ │ ├── http-status.ts
│ │ ├── ipaddr.test.ts
│ │ ├── ipaddr.ts
│ │ ├── jwt/
│ │ │ ├── index.ts
│ │ │ ├── jwa.test.ts
│ │ │ ├── jwa.ts
│ │ │ ├── jws.ts
│ │ │ ├── jwt.test.ts
│ │ │ ├── jwt.ts
│ │ │ ├── types.ts
│ │ │ └── utf8.ts
│ │ ├── mime.test.ts
│ │ ├── mime.ts
│ │ ├── stream.test.ts
│ │ ├── stream.ts
│ │ ├── types.test.ts
│ │ ├── types.ts
│ │ ├── url.test.ts
│ │ └── url.ts
│ └── validator/
│ ├── index.ts
│ ├── utils.test.ts
│ ├── utils.ts
│ ├── validator.test.ts
│ └── validator.ts
├── tsconfig.base.json
├── tsconfig.build.json
├── tsconfig.json
├── tsconfig.spec.json
└── vitest.config.ts
================================================
FILE CONTENTS
================================================
================================================
FILE: .devcontainer/Dockerfile
================================================
FROM mcr.microsoft.com/devcontainers/typescript-node:20
# Install Deno
ENV DENO_INSTALL=/usr/local
RUN curl -fsSL https://deno.land/install.sh | sh
# Install Bun
ENV BUN_INSTALL=/usr/local
RUN curl -fsSL https://bun.sh/install | bash
WORKDIR /hono
================================================
FILE: .devcontainer/devcontainer.json
================================================
{
"build": {
"dockerfile": "Dockerfile"
},
"containerEnv": {
"HOME": "/home/node"
},
"customizations": {
"vscode": {
"settings": {
"deno.enable": false,
"eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact"],
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
}
},
"extensions": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode"]
}
}
}
================================================
FILE: .devcontainer/docker-compose.yml
================================================
services:
hono:
build: .
container_name: hono
volumes:
- ../:/hono
networks:
- hono
command: bash
stdin_open: true
tty: true
restart: 'no'
networks:
hono:
driver: bridge
================================================
FILE: .editorconfig
================================================
root = true
[*]
charset = utf-8
insert_final_newline = true
end_of_line = lf
[*.{js,jsx,ts,tsx,json,json5,jsonc}]
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
[*.{md,yaml,yml}]
indent_style = space
indent_size = 2
trim_trailing_whitespace = false
[*.{lockb,pxm}]
charset = unset
insert_final_newline = unset
end_of_line = unset
================================================
FILE: .github/FUNDING.yml
================================================
github: ['yusukebe', 'usualoma']
================================================
FILE: .github/ISSUE_TEMPLATE/1-bug-report.yml
================================================
name: 🐛 Bug Report
description: Report an issue that should be fixed
labels: [triage]
body:
- type: markdown
attributes:
value: |
Thank you for submitting a bug report. It helps make Hono better.
If you need help or support using Hono, and are not reporting a bug, please ask questions in [our Discord](https://discord.gg/KMh2eNSdxV) or [GitHub Discussions](https://github.com/orgs/honojs/discussions).
Please try to include as much information as possible.
- type: input
attributes:
label: What version of Hono are you using?
placeholder: 0.0.0
validations:
required: true
- type: input
attributes:
label: What runtime/platform is your app running on? (with version if possible)
placeholder: Cloudflare Workers, Deno, Bun, etc.
validations:
required: true
- type: textarea
attributes:
label: What steps can reproduce the bug?
description: Explain the bug and provide a code snippet that can reproduce it.
validations:
required: true
- type: textarea
attributes:
label: What is the expected behavior?
- type: textarea
attributes:
label: What do you see instead?
- type: textarea
attributes:
label: Additional information
description: Is there anything else you think we should know?
================================================
FILE: .github/ISSUE_TEMPLATE/2-feature-request.yml
================================================
name: 🚀 Feature Request
description: Suggest an idea, feature, or enhancement
labels: [enhancement]
body:
- type: markdown
attributes:
value: |
Thank you for submitting an idea. It helps make Hono better.
- type: textarea
attributes:
label: What is the feature you are proposing?
description: A clear description of what you want to happen.
validations:
required: true
================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: true
contact_links:
- name: ❓ Questions
url: https://github.com/orgs/honojs/discussions
about: Ask your questions on the GitHub Discussions.
- name: 🗣️ Discord
url: https://discord.gg/KMh2eNSdxV
about: Join our Discord server to chat.
================================================
FILE: .github/actions/perf-measures/action.yml
================================================
name: 'Performance Measures'
description: 'Run type check and bundle size performance measurements'
inputs:
target-ref:
description: 'Target ref (main or auto). Set to "auto" to delegate ref detection to octocov.'
required: false
default: 'auto'
runs:
using: 'composite'
steps:
- uses: actions/checkout@v6
- uses: oven-sh/setup-bun@v2
with:
bun-version-file: '.tool-versions'
- run: |
bun install --frozen-lockfile
bun tsc --build
shell: bash
- name: Performance measurement of type check (tsc)
run: |
bun scripts/generate-app.ts
bun tsc -p tsconfig.build.json --diagnostics | bun scripts/process-results.ts > diagnostics-tsc.json
shell: bash
working-directory: perf-measures/type-check
env:
BENCHMARK_TS_IMPL_LABEL: tsc
- name: Performance measurement of type check (typescript-go)
run: |
bun scripts/generate-app.ts
bun tsgo -p tsconfig.build.json --diagnostics | bun scripts/process-results.ts > diagnostics-tsgo.json
shell: bash
working-directory: perf-measures/type-check
env:
BENCHMARK_TS_IMPL_LABEL: typescript-go
- name: Performance measurement of bundle check
run: |
bun run build
bun perf-measures/bundle-check/scripts/check-bundle-size.ts > perf-measures/bundle-check/size.json
shell: bash
- name: Run octocov
if: ${{ inputs.target-ref == 'auto' }}
uses: k1LoW/octocov-action@v1
with:
config: perf-measures/.octocov.consolidated.perf-measures.yml
env:
OCTOCOV_CUSTOM_METRICS_BUNDLE_SIZE_CHECK: perf-measures/bundle-check/size.json
OCTOCOV_CUSTOM_METRICS_DIAGNOSTICS_TSC: perf-measures/type-check/diagnostics-tsc.json
OCTOCOV_CUSTOM_METRICS_DIAGNOSTICS_TSGO: perf-measures/type-check/diagnostics-tsgo.json
- name: Run octocov with custom target ref
if: ${{ inputs.target-ref == 'main' }}
uses: k1LoW/octocov-action@v1
with:
config: perf-measures/.octocov.consolidated.perf-measures.main.yml
env:
OCTOCOV_GITHUB_REF: 'refs/heads/main'
OCTOCOV_CUSTOM_METRICS_BUNDLE_SIZE_CHECK: perf-measures/bundle-check/size.json
OCTOCOV_CUSTOM_METRICS_DIAGNOSTICS_TSC: perf-measures/type-check/diagnostics-tsc.json
OCTOCOV_CUSTOM_METRICS_DIAGNOSTICS_TSGO: perf-measures/type-check/diagnostics-tsgo.json
================================================
FILE: .github/pull_request_template.md
================================================
### The author should do the following, if applicable
- [ ] Add tests
- [ ] Run tests
- [ ] `bun run format:fix && bun run lint:fix` to format the code
- [ ] Add [TSDoc](https://tsdoc.org/)/[JSDoc](https://jsdoc.app/about-getting-started) to document the code
================================================
FILE: .github/workflows/autofix.yml
================================================
name: autofix.ci
on:
pull_request:
push:
branches: [main]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
autofix:
name: autofix
runs-on: ubuntu-latest
if: ${{ github.event_name == 'push' || !github.event.pull_request.draft }}
steps:
- name: Checkout
uses: actions/checkout@v6
- uses: oven-sh/setup-bun@v2
with:
bun-version-file: '.tool-versions'
- run: bun install --frozen-lockfile
- run: bun run format:fix
- run: bun run lint:fix
- name: Apply fixes
uses: autofix-ci/action@v1
with:
commit-message: 'ci: apply automated fixes'
================================================
FILE: .github/workflows/ci.yml
================================================
name: ci
on:
push:
branches: [main, next]
pull_request:
branches: ['*']
paths-ignore:
- 'docs/**'
- '.vscode/**'
- 'README.md'
- '.gitignore'
- 'LICENSE'
jobs:
coverage:
name: 'Coverage'
runs-on: ubuntu-latest
needs:
- main
- bun
- deno
steps:
- uses: actions/checkout@v6
- uses: actions/download-artifact@v6
with:
pattern: coverage-*
merge-multiple: true
path: ./coverage
- uses: codecov/codecov-action@v5
with:
fail_ci_if_error: true
directory: ./coverage
main:
name: 'Main'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version-file: '.tool-versions'
- uses: oven-sh/setup-bun@v2
with:
bun-version-file: '.tool-versions'
- run: bun install --frozen-lockfile
- run: bun run format
- run: bun run lint
- run: bun run editorconfig-checker -format github-actions
- run: bun run build
- run: bun run test
- uses: actions/upload-artifact@v5
with:
name: coverage-main
path: coverage/
jsr-dry-run:
name: "Checking if it's valid for JSR"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: denoland/setup-deno@v2
with:
deno-version-file: '.tool-versions'
- uses: oven-sh/setup-bun@v2
with:
bun-version-file: '.tool-versions'
- run: bunx jsr publish --dry-run
deno:
name: 'Deno'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: denoland/setup-deno@v2
with:
deno-version-file: '.tool-versions'
- run: env NAME=Deno deno test --coverage=coverage/raw/deno-runtime --allow-read --allow-env --allow-write --allow-net -c runtime-tests/deno/deno.json runtime-tests/deno
- run: deno test -c runtime-tests/deno-jsx/deno.precompile.json --coverage=coverage/raw/deno-precompile-jsx runtime-tests/deno-jsx
- run: deno test -c runtime-tests/deno-jsx/deno.react-jsx.json --coverage=coverage/raw/deno-react-jsx runtime-tests/deno-jsx
- run: grep -R '"url":' coverage | grep -v runtime-tests | sed -e 's/.*file:..//;s/.,//' | xargs deno cache --unstable-sloppy-imports
- run: deno coverage --lcov > coverage/deno-runtime-coverage-lcov.info
- uses: actions/upload-artifact@v5
with:
name: coverage-deno
path: coverage/
bun:
name: 'Bun'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: oven-sh/setup-bun@v2
with:
bun-version-file: '.tool-versions'
- run: bun install --frozen-lockfile
- run: bun run test:bun
- uses: actions/upload-artifact@v5
with:
name: coverage-bun
path: coverage/
bun-windows:
name: 'Bun - Windows'
runs-on: windows-latest
steps:
- uses: actions/checkout@v6
- uses: oven-sh/setup-bun@v2
with:
bun-version-file: '.tool-versions'
- run: bun run test:bun
fastly:
name: 'Fastly Compute'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: oven-sh/setup-bun@v2
with:
bun-version-file: '.tool-versions'
- run: bun install --frozen-lockfile
- run: bun run build
- run: bun run test:fastly
node:
name: 'Node.js v${{ matrix.node }}'
runs-on: ubuntu-latest
strategy:
matrix:
node: ['18.18.2', '20.x', '22.x']
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: ${{ matrix.node }}
- uses: oven-sh/setup-bun@v2
with:
bun-version-file: '.tool-versions'
- run: bun install --frozen-lockfile
- run: bun run build
- run: bun run test:node
workerd:
name: 'workerd'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version-file: '.tool-versions'
- uses: oven-sh/setup-bun@v2
with:
bun-version-file: '.tool-versions'
- run: bun install --frozen-lockfile
- run: bun run build
- run: bun run test:workerd
lambda:
name: 'AWS Lambda'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: oven-sh/setup-bun@v2
with:
bun-version-file: '.tool-versions'
- run: bun install --frozen-lockfile
- run: bun run build
- run: bun run test:lambda
lambda-edge:
name: 'Lambda@Edge'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: oven-sh/setup-bun@v2
with:
bun-version-file: '.tool-versions'
- run: bun install --frozen-lockfile
- run: bun run build
- run: bun run test:lambda-edge
perf-measures-check-on-pr:
name: 'Type & Bundle size Check on PR'
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
steps:
- uses: actions/checkout@v6
- uses: ./.github/actions/perf-measures
with:
target-ref: 'auto'
http-benchmark-on-pr:
name: 'HTTP Speed Check on PR'
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
steps:
- uses: actions/checkout@v6
- uses: oven-sh/setup-bun@v2
with:
bun-version-file: '.tool-versions'
- run: bun install --frozen-lockfile
- name: Install bombardier
run: |
wget -O bombardier https://github.com/codesenberg/bombardier/releases/download/v2.0.1/bombardier-linux-amd64
chmod +x bombardier
sudo mv bombardier /usr/local/bin/
- name: Run HTTP benchmark
run: |
cd benchmarks/http-server
bun run benchmark.ts
- name: Comment PR
uses: actions/github-script@v7
if: github.event.pull_request.head.repo.full_name == github.repository
with:
script: |
const fs = require('fs');
const results = fs.readFileSync('benchmarks/http-server/benchmark-results.md', 'utf8');
// Minimize previous benchmark comments
const comments = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number
});
for (const comment of comments.data) {
if (comment.body.includes('## HTTP Performance Benchmark')) {
await github.graphql(`
mutation {
minimizeComment(input: { subjectId: "${comment.node_id}", classifier: OUTDATED }) {
minimizedComment {
isMinimized
}
}
}
`);
}
}
// Post new comment
await github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: results
});
- name: Show benchmark results for forks
if: github.event.pull_request.head.repo.full_name != github.repository
run: |
echo "## HTTP Performance Benchmark Results"
echo "Note: Cannot post comment due to security restrictions on fork PRs"
cat benchmarks/http-server/benchmark-results.md
perf-measures-check-on-main:
name: 'Type & Bundle size Check on Main'
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v6
- uses: ./.github/actions/perf-measures
with:
target-ref: 'main'
================================================
FILE: .github/workflows/cr.yml
================================================
name: cr
on:
push:
branches: [main]
tags: ['!**'] # Avoid publishing on tags
pull_request:
types: [opened, synchronize, labeled] # Run on PR creation, updates, and when labels are added
concurrency:
group: ${{ github.workflow }}-${{ github.event.number }} # Concurrency group for each PR
cancel-in-progress: true # Cancel in progress builds for the same PR
jobs:
publish:
if: github.repository == 'honojs/hono' && (github.ref == 'refs/heads/main' || contains(github.event.pull_request.labels.*.name, 'cr-tracked'))
runs-on: ubuntu-latest
name: 'Publish: pkg.pr.new'
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- uses: actions/setup-node@v6
with:
node-version-file: '.tool-versions'
- uses: oven-sh/setup-bun@v2
with:
bun-version-file: '.tool-versions'
- name: Install Dependencies
run: bun install --frozen-lockfile
- name: Build
run: bun run build
- name: Publish to StackBlitz
run: |
bun pkg-pr-new publish --compact
================================================
FILE: .github/workflows/no-response.yml
================================================
name: Close stale issues with "not bug" label
on:
schedule:
- cron: '0 0 * * *'
permissions:
contents: write
issues: write
jobs:
stale:
runs-on: ubuntu-latest
steps:
- name: Close stale issues with "not bug" label
uses: actions/stale@v8
with:
days-before-stale: 7
days-before-close: 2
stale-issue-message: 'This issue has been marked as stale due to inactivity.'
close-issue-message: 'Closing this issue due to inactivity.'
exempt-issue-labels: ''
stale-issue-label: 'stale'
only-labels: 'not bug'
operations-per-run: 30
remove-stale-when-updated: true
================================================
FILE: .github/workflows/release.yml
================================================
name: release
on:
push:
tags:
- '*'
jobs:
jsr:
name: publish-to-jsr
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v6
- name: Install deno
uses: denoland/setup-deno@v2
with:
deno-version-file: '.tool-versions'
- run: deno install --no-lock --allow-scripts
- name: Publish to JSR
run: deno run -A jsr:@david/publish-on-tag@0.1.4
================================================
FILE: .gitignore
================================================
dist
sandbox
# Cloudflare Workers
worker
.wrangler
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
.cache
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
# IDE-specific settings
.idea
# Claude Code local files
CLAUDE.local.md
settings.local.json
================================================
FILE: .gitpod.yml
================================================
tasks:
- name: Setup
init: bun install --frozen-lockfile
image:
file: ./.devcontainer/Dockerfile
vscode:
extensions:
- oven.bun-vscode
- vitest.explorer
================================================
FILE: .prettierrc
================================================
{
"printWidth": 100,
"trailingComma": "es5",
"tabWidth": 2,
"semi": false,
"singleQuote": true,
"jsxSingleQuote": true,
"endOfLine": "lf"
}
================================================
FILE: .tool-versions
================================================
nodejs 24.7.0
bun 1.2.19
deno 2.4.5
================================================
FILE: .vitest.config/setup-vitest.ts
================================================
import * as nodeCrypto from 'node:crypto'
import { vi } from 'vitest'
/**
* crypto
*/
if (!globalThis.crypto) {
vi.stubGlobal('crypto', nodeCrypto)
vi.stubGlobal('CryptoKey', nodeCrypto.webcrypto.CryptoKey)
}
/**
* Cache API
*/
type StoreMap = Map<string | Request, Response>
class MockCache {
name: string
store: StoreMap
constructor(name: string, store: StoreMap) {
this.name = name
this.store = store
}
async match(key: Request | string): Promise<Response | null> {
return this.store.get(key) || null
}
async keys() {
return this.store.keys()
}
async put(key: Request | string, response: Response): Promise<void> {
this.store.set(key, response)
}
}
const globalStore: Map<string | Request, Response> = new Map()
const caches = {
open: (name: string) => {
return new MockCache(name, globalStore)
},
}
vi.stubGlobal('caches', caches)
================================================
FILE: .vscode/extensions.json
================================================
{
"recommendations": ["EditorConfig.EditorConfig"]
}
================================================
FILE: .vscode/settings.json
================================================
{
"deno.enable": false,
"eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact"],
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
},
"typescript.tsdk": "node_modules/typescript/lib"
}
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2021 - present, Yusuke Wada and Hono contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
<div align="center">
<a href="https://hono.dev">
<img src="https://raw.githubusercontent.com/honojs/hono/main/docs/images/hono-title.png" width="500" height="auto" alt="Hono"/>
</a>
</div>
<hr />
[](https://github.com/honojs/hono/actions)
[](https://github.com/honojs/hono/blob/main/LICENSE)
[](https://www.npmjs.com/package/hono)
[](https://www.npmjs.com/package/hono)
[](https://jsr.io/@hono/hono)
[](https://bundlephobia.com/result?p=hono)
[](https://bundlephobia.com/result?p=hono)
[](https://github.com/honojs/hono/pulse)
[](https://github.com/honojs/hono/commits/main)
[](https://codecov.io/github/honojs/hono)
[](https://discord.gg/KMh2eNSdxV)
[](https://deepwiki.com/honojs/hono)
Hono - _**means flame🔥 in Japanese**_ - is a small, simple, and ultrafast web framework built on Web Standards. It works on any JavaScript runtime: Cloudflare Workers, Fastly Compute, Deno, Bun, Vercel, AWS Lambda, Lambda@Edge, and Node.js.
Fast, but not only fast.
```ts
import { Hono } from 'hono'
const app = new Hono()
app.get('/', (c) => c.text('Hono!'))
export default app
```
## Quick Start
```bash
npm create hono@latest
```
## Features
- **Ultrafast** 🚀 - The router `RegExpRouter` is really fast. Not using linear loops. Fast.
- **Lightweight** 🪶 - The `hono/tiny` preset is under 12kB. Hono has zero dependencies and uses only the Web Standard API.
- **Multi-runtime** 🌍 - Works on Cloudflare Workers, Fastly Compute, Deno, Bun, AWS Lambda, Lambda@Edge, or Node.js. The same code runs on all platforms.
- **Batteries Included** 🔋 - Hono has built-in middleware, custom middleware, and third-party middleware. Batteries included.
- **Delightful DX** 😃 - Super clean APIs. First-class TypeScript support. Now, we've got "Types".
## Documentation
The documentation is available on [hono.dev](https://hono.dev).
## Migration
The migration guide is available on [docs/MIGRATION.md](docs/MIGRATION.md).
## Communication
[X](https://x.com/honojs) and [Discord channel](https://discord.gg/KMh2eNSdxV) are available.
## Contributing
Contributions Welcome! You can contribute in the following ways.
- Create an Issue - Propose a new feature. Report a bug.
- Pull Request - Fix a bug or typo. Refactor the code.
- Create third-party middleware - See instructions below.
- Share - Share your thoughts on the Blog, X, and others.
- Make your application - Please try to use Hono.
For more details, see [docs/CONTRIBUTING.md](docs/CONTRIBUTING.md).
## Contributors
Thanks to [all contributors](https://github.com/honojs/hono/graphs/contributors)!
## Authors
Yusuke Wada <https://github.com/yusukebe>
_RegExpRouter_, _SmartRouter_, _LinearRouter_, and _PatternRouter_ are created by Taku Amano <https://github.com/usualoma>
## License
Distributed under the MIT License. See [LICENSE](LICENSE) for more information.
================================================
FILE: benchmarks/deno/.gitignore
================================================
*.sqlite
================================================
FILE: benchmarks/deno/.vscode/settings.json
================================================
{
"eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact"],
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
"deno.enable": true
}
================================================
FILE: benchmarks/deno/fast.ts
================================================
import fast from 'https://deno.land/x/fast@4.0.0-beta.1/mod.ts'
import type { Context } from 'https://deno.land/x/fast@4.0.0-beta.1/mod.ts'
const app = fast()
app.get('/user', () => {})
app.get('/user/comments', () => {})
app.get('/user/avatar', () => {})
app.get('/user/lookup/email/:address', () => {})
app.get('/event/:id', () => {})
app.get('/event/:id/comments', () => {})
app.post('/event/:id/comments', () => {})
app.post('/status', () => {})
app.get('/very/deeply/nested/route/hello/there', () => {})
app.get('/user/lookup/username/:username', (ctx: Context) => {
return { message: `Hello ${ctx.params.username}` }
})
await app.serve({
port: 8000,
})
================================================
FILE: benchmarks/deno/faster.ts
================================================
import { res, Server } from 'https://deno.land/x/faster@v5.7/mod.ts'
const app = new Server()
app.get('/user', () => {})
app.get('/user/comments', () => {})
app.get('/user/avatar', () => {})
app.get('/user/lookup/email/:address', () => {})
app.get('/event/:id', () => {})
app.get('/event/:id/comments', () => {})
app.post('/event/:id/comments', () => {})
app.post('/status', () => {})
app.get('/very/deeply/nested/route/hello/there', () => {})
app.get('/user/lookup/username/:username', res('json'), async (ctx: any, next: any) => {
ctx.res.body = { message: `Hello ${ctx.params.username}` }
await next()
})
await app.listen({ port: 8000 })
================================================
FILE: benchmarks/deno/hono.ts
================================================
import { Hono } from '../../src/index.ts'
import { RegExpRouter } from '../../src/router/reg-exp-router/index.ts'
const app = new Hono({ router: new RegExpRouter() })
app.get('/user', (c) => c.text('User'))
app.get('/user/comments', (c) => c.text('User Comments'))
app.get('/user/avatar', (c) => c.text('User Avatar'))
app.get('/user/lookup/email/:address', (c) => c.text('User Lookup Email Address'))
app.get('/event/:id', (c) => c.text('Event'))
app.get('/event/:id/comments', (c) => c.text('Event Comments'))
app.post('/event/:id/comments', (c) => c.text('POST Event Comments'))
app.post('/status', (c) => c.text('Status'))
app.get('/very/deeply/nested/route/hello/there', (c) => c.text('Very Deeply Nested Route'))
app.get('/user/lookup/username/:username', (c) => {
return c.json({ message: `Hello ${c.req.param('username')}` })
})
Deno.serve(app.fetch, {
port: 8000,
})
================================================
FILE: benchmarks/deno/magalo.ts
================================================
import { Megalo } from 'https://deno.land/x/megalo@v0.3.0/mod.ts'
const app = new Megalo()
app.get('/user', () => {})
app.get('/user/comments', () => {})
app.get('/user/avatar', () => {})
app.get('/user/lookup/email/:address', () => {})
app.get('/event/:id', () => {})
app.get('/event/:id/comments', () => {})
app.post('/event/:id/comments', () => {})
app.post('/status', () => {})
app.get('/very/deeply/nested/route/hello/there', () => {})
app.get('/user/lookup/username/:username', ({ params }, res) => {
res.json({
message: `Hello ${params.username}`,
})
})
app.listen({ port: 8000 })
================================================
FILE: benchmarks/deno/oak.ts
================================================
import { Application, Router } from 'https://deno.land/x/oak@v10.5.1/mod.ts'
const router = new Router()
router.get('/user', () => {})
router.get('/user/comments', () => {})
router.get('/user/avatar', () => {})
router.get('/user/lookup/email/:address', () => {})
router.get('/event/:id', () => {})
router.get('/event/:id/comments', () => {})
router.post('/event/:id/comments', () => {})
router.post('/status', () => {})
router.get('/very/deeply/nested/route/hello/there', () => {})
router.get('/user/lookup/username/:username', (ctx) => {
ctx.response.body = {
message: `Hello ${ctx.params.username}`,
}
})
const app = new Application()
app.use(router.routes())
app.use(router.allowedMethods())
await app.listen({ port: 8000 })
================================================
FILE: benchmarks/deno/opine.ts
================================================
import { opine } from 'https://deno.land/x/opine@2.2.0/mod.ts'
const app = opine()
app.get('/user', () => {})
app.get('/user/comments', () => {})
app.get('/user/avatar', () => {})
app.get('/user/lookup/email/:address', () => {})
app.get('/event/:id', () => {})
app.get('/event/:id/comments', () => {})
app.post('/event/:id/comments', () => {})
app.post('/status', () => {})
app.get('/very/deeply/nested/route/hello/there', () => {})
app.get('/user/lookup/username/:username', (req, res) => {
res.send({ message: `Hello ${req.params.username}` })
})
app.listen(8000)
================================================
FILE: benchmarks/handle-event/index.js
================================================
import Benchmark from 'benchmark'
import { makeEdgeEnv } from 'edge-mock'
import { Router as IttyRouter } from 'itty-router'
import { Request, Response } from 'node-fetch'
import { Router as SunderRouter, Sunder } from 'sunder'
import { Router as WorktopRouter } from 'worktop'
import { Hono } from '../../dist/hono'
import { RegExpRouter } from '../../dist/router/reg-exp-router'
globalThis.Request = Request
globalThis.Response = Response
const initHono = (hono) => {
hono.get('/user', (c) => c.text('User'))
hono.get('/user/comments', (c) => c.text('User Comments'))
hono.get('/user/avatar', (c) => c.text('User Avatar'))
hono.get('/user/lookup/email/:address', (c) => c.text('User Lookup Email Address'))
hono.get('/event/:id', (c) => c.text('Event'))
hono.get('/event/:id/comments', (c) => c.text('Event Comments'))
hono.post('/event/:id/comments', (c) => c.text('POST Event Comments'))
hono.post('/status', (c) => c.text('Status'))
hono.get('/very/deeply/nested/route/hello/there', (c) => c.text('Very Deeply Nested Route'))
hono.get('/user/lookup/username/:username', (c) => {
return c.text(`Hello ${c.req.param('username')}`)
})
return hono
}
const hono = initHono(new Hono({ router: new RegExpRouter() }))
// itty-router
const ittyRouter = IttyRouter()
ittyRouter.get('/user', () => new Response('User'))
ittyRouter.get('/user/comments', () => new Response('User Comments'))
ittyRouter.get('/user/avatar', () => new Response('User Avatar'))
ittyRouter.get('/user/lookup/email/:address', () => new Response('User Lookup Email Address'))
ittyRouter.get('/event/:id', () => new Response('Event'))
ittyRouter.get('/event/:id/comments', () => new Response('Event Comments'))
ittyRouter.post('/event/:id/comments', () => new Response('POST Event Comments'))
ittyRouter.post('/status', () => new Response('Status'))
ittyRouter.get(
'/very/deeply/nested/route/hello/there',
() => new Response('Very Deeply Nested Route')
)
ittyRouter.get('/user/lookup/username/:username', ({ params }) => {
return new Response(`Hello ${params.username}`, {
status: 200,
headers: {
'Content-Type': 'text/plain;charset=UTF-8',
},
})
})
// Sunder
const sunderRouter = new SunderRouter()
sunderRouter.get('/user', (ctx) => {
ctx.response.body = 'User'
})
sunderRouter.get('/user/comments', (ctx) => {
ctx.response.body = 'User Comments'
})
sunderRouter.get('/user/avatar', (ctx) => {
ctx.response.body = 'User Avatar'
})
sunderRouter.get('/user/lookup/email/:address', (ctx) => {
ctx.response.body = 'User Lookup Email Address'
})
sunderRouter.get('/event/:id', (ctx) => {
ctx.response.body = 'Event'
})
sunderRouter.get('/event/:id/comments', (ctx) => {
ctx.response.body = 'Event Comments'
})
sunderRouter.post('/event/:id/comments', (ctx) => {
ctx.response.body = 'POST Event Comments'
})
sunderRouter.post('/status', (ctx) => {
ctx.response.body = 'Status'
})
sunderRouter.get('/very/deeply/nested/route/hello/there', (ctx) => {
ctx.response.body = 'Very Deeply Nested Route'
})
//sunderRouter.get('/static/*', () => {})
sunderRouter.get('/user/lookup/username/:username', (ctx) => {
ctx.response.body = `Hello ${ctx.params.username}`
})
const sunderApp = new Sunder()
sunderApp.use(sunderRouter.middleware)
// worktop
const worktopRouter = new WorktopRouter()
worktopRouter.add('GET', '/user', async (_req, res) => res.send(200, 'User'))
worktopRouter.add('GET', '/user/comments', (_req, res) => res.send(200, 'User Comments'))
worktopRouter.add('GET', '/user/avatar', (_req, res) => res.send(200, 'User Avatar'))
worktopRouter.add('GET', '/user/lookup/email/:address', (_req, res) =>
res.send(200, 'User Lookup Email Address')
)
worktopRouter.add('GET', '/event/:id', (_req, res) => res.send(200, 'Event'))
worktopRouter.add('POST', '/event/:id/comments', (_req, res) =>
res.send(200, 'POST Event Comments')
)
worktopRouter.add('POST', '/status', (_req, res) => res.send(200, 'Status'))
worktopRouter.add('GET', '/very/deeply/nested/route/hello/there', (_req, res) =>
res.send(200, 'Very Deeply Nested Route')
)
worktopRouter.add('GET', '/user/lookup/username/:username', (req, res) =>
res.send(200, `Hello ${req.params.username}`)
)
// Request Object
const request = new Request('http://localhost/user/lookup/username/hey', { method: 'GET' })
makeEdgeEnv()
// FetchEvent Object
// eslint-disable-next-line no-undef
const event = new FetchEvent('fetch', { request })
const fn = async () => {
let res = await hono.fetch(event.request)
console.log(await res.text())
res = await ittyRouter.handle(event.request)
console.log(await res.text())
res = await sunderApp.handle(event)
console.log(await res.text())
res = await worktopRouter.run(event)
console.log(await res.text())
}
fn()
const suite = new Benchmark.Suite()
suite
.add('Hono', async () => {
await hono.fetch(event.request)
})
.add('itty-router', async () => {
await ittyRouter.handle(event.request)
})
.add('sunder', async () => {
await sunderApp.handle(event)
})
.add('worktop', async () => {
await worktopRouter.run(event)
})
.on('cycle', (event) => {
console.log(String(event.target))
})
.on('complete', function () {
console.log(`Fastest is ${this.filter('fastest').map('name')}`)
})
.run({ async: true })
================================================
FILE: benchmarks/handle-event/package.json
================================================
{
"name": "hono-benchmark",
"version": "0.0.1",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node --experimental-specifier-resolution=node index.js"
},
"type": "module",
"author": "Yusuke Wada <yusuke@kamawada.com> (https://github.com/yusukebe)",
"license": "MIT",
"devDependencies": {
"benchmark": "^2.1.4",
"edge-mock": "^0.0.15",
"itty-router": "^3.0.11",
"node-fetch": "^3.2.10",
"sunder": "^0.10.1",
"worktop": "^0.7.3"
}
}
================================================
FILE: benchmarks/http-server/.gitignore
================================================
.benchmark-temp/
benchmark-results.md
================================================
FILE: benchmarks/http-server/README.md
================================================
# Hono HTTP Benchmark
HTTP performance benchmarking tool that compares main vs current versions.
## Usage
### In Pull Requests
HTTP benchmarks are automatically run for each pull request and results are commented on the PR.
### Local Development
```bash
cd benchmarks/http-server
bun run benchmark.ts
```
## Prerequisites
- Bun v1.0+
- bombardier (`brew install bombardier` on macOS)
================================================
FILE: benchmarks/http-server/benchmark.ts
================================================
/**
* Hono HTTP Performance Benchmark
*
* Inspired by https://github.com/SaltyAom/bun-http-framework-benchmark
*
* Usage:
* bun run benchmark.ts [options]
*
* Options:
* --baseline=<ref> Git reference for baseline (default: main)
* --target=<ref> Git reference for target (default: current)
* --runs=<number> Number of benchmark runs (default: 1)
* --duration=<number> Duration of each test in seconds (default: 10)
* --skip-tests Skip endpoint validation tests
*/
import { spawn } from 'node:child_process'
import { existsSync, mkdirSync, writeFileSync, rmSync } from 'node:fs'
import { join } from 'node:path'
// Configuration from command line arguments
const baseline =
process.argv.find((arg) => arg.startsWith('--baseline='))?.split('=')[1] || 'origin/main'
const target = process.argv.find((arg) => arg.startsWith('--target='))?.split('=')[1] || 'current'
const runs = parseInt(process.argv.find((arg) => arg.startsWith('--runs='))?.split('=')[1] || '1')
const duration = parseInt(
process.argv.find((arg) => arg.startsWith('--duration='))?.split('=')[1] || '10'
)
const concurrency = 500
const skipTests = process.argv.includes('--skip-tests')
const SCRIPT_DIR = import.meta.dirname
const TEMP_DIR = join(SCRIPT_DIR, '.benchmark-temp')
const HONO_ROOT = join(SCRIPT_DIR, '../..')
// Test app template (embedded to avoid file dependency issues)
const getAppTemplate = () => `import { Hono } from './src/index.ts'
import { RegExpRouter } from './src/router/reg-exp-router/index.ts'
const app = new Hono({ router: new RegExpRouter() })
app
.get('/', (c) => c.text('Hi'))
.post('/json', (c) => c.req.json().then(c.json))
.get('/id/:id', (c) => {
const id = c.req.param('id')
const name = c.req.query('name')
c.header('x-powered-by', 'benchmark')
return c.text(\`\${id} \${name}\`)
})
export default app`
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))
const runCommand = async (command: string, cwd: string) => {
const parts = command.split(' ')
const proc = spawn(parts[0], parts.slice(1), { cwd })
let stdout = ''
let stderr = ''
proc.stdout.on('data', (data) => {
stdout += data
})
proc.stderr.on('data', (data) => {
stderr += data
})
const exitCode = await new Promise<number>((resolve) => {
proc.on('close', resolve)
})
if (exitCode !== 0) {
console.error(`Command failed: ${command}`)
console.error(`Exit code: ${exitCode}`)
console.error(`Stdout: ${stdout}`)
console.error(`Stderr: ${stderr}`)
throw new Error(`Command failed: ${command}`)
}
return { stdout, stderr }
}
const setupTemp = () => {
if (existsSync(TEMP_DIR)) {
rmSync(TEMP_DIR, { recursive: true })
}
mkdirSync(TEMP_DIR, { recursive: true })
writeFileSync(join(TEMP_DIR, 'body.json'), '{"hello":"world"}')
}
const buildVersion = async (version: string, name: string) => {
console.log(`📦 Preparing ${name} (${version})...`)
let needsRestore = false
let stashRef = ''
if (version === 'current') {
// No build needed - use src directly
} else {
// Ensure we have the latest remote refs
await runCommand('git fetch origin', HONO_ROOT)
try {
const stashResult = await runCommand('git stash push -m "benchmark-temp"', HONO_ROOT)
needsRestore = stashResult.stdout.includes('Saved working directory')
if (needsRestore) {
stashRef = 'stash@{0}'
}
} catch {
// No changes to stash
}
await runCommand(`git checkout ${version}`, HONO_ROOT)
await runCommand('bun install --frozen-lockfile', HONO_ROOT)
// No build needed - use src directly
}
const versionDir = join(TEMP_DIR, name)
mkdirSync(versionDir, { recursive: true })
await runCommand(`cp -r ${HONO_ROOT}/src ${versionDir}/src`, process.cwd())
const appPath = join(versionDir, 'app.ts')
writeFileSync(appPath, getAppTemplate())
// Test endpoints (optional)
if (!skipTests) {
console.log(`🧪 Testing endpoints for ${name}...`)
const server = spawn('bun', [appPath], {
cwd: TEMP_DIR,
env: { ...process.env, NODE_ENV: 'production' },
})
await sleep(2000)
try {
const res1 = await fetch('http://127.0.0.1:3000/')
if ((await res1.text()) !== 'Hi') {
throw new Error('[GET /] test failed')
}
const res2 = await fetch('http://127.0.0.1:3000/id/1?name=bun')
if (res2.headers.get('x-powered-by') !== 'benchmark' || (await res2.text()) !== '1 bun') {
throw new Error('[GET /id/:id] test failed')
}
const body = JSON.stringify({ hello: 'world' })
const res3 = await fetch('http://127.0.0.1:3000/json', {
method: 'POST',
body,
headers: { 'content-type': 'application/json', 'content-length': body.length.toString() },
})
if (
!res3.headers.get('content-type')?.includes('application/json') ||
(await res3.text()) !== body
) {
throw new Error('[POST /json] test failed')
}
console.log(` ✅ Tests passed for ${name}`)
} finally {
server.kill()
await sleep(1000)
}
} else {
console.log(` ⏭️ Skipping endpoint tests for ${name}`)
}
// Restore git state
if (version !== 'current' && needsRestore) {
await runCommand('git checkout -', HONO_ROOT)
await runCommand(`git stash pop ${stashRef}`, HONO_ROOT)
} else if (version !== 'current') {
await runCommand('git checkout -', HONO_ROOT)
}
return appPath
}
const runBenchmark = async (appPath: string, name: string) => {
console.log(`⚡ Running HTTP benchmark for ${name}...`)
const bodyFile = join(TEMP_DIR, 'body.json')
const commands = [
`bombardier --fasthttp -c ${concurrency} -d ${duration}s http://127.0.0.1:3000/`,
`bombardier --fasthttp -c ${concurrency} -d ${duration}s http://127.0.0.1:3000/id/1?name=bun`,
`bombardier --fasthttp -c ${concurrency} -d ${duration}s -m POST -H Content-Type:application/json -f ${bodyFile} http://127.0.0.1:3000/json`,
]
const allRuns: number[][] = []
for (let run = 0; run < runs; run++) {
console.log(` Run ${run + 1}/${runs}`)
const server = spawn('bun', [appPath], {
cwd: TEMP_DIR,
env: { ...process.env, NODE_ENV: 'production' },
})
await sleep(1000)
const runResults: number[] = []
try {
for (const command of commands) {
const result = await runCommand(command, process.cwd())
console.log(result.stdout)
const match = result.stdout.match(/Reqs\/sec\s+(\d+[.|,]\d+)/)
if (match) {
runResults.push(parseFloat(match[1].replace(',', '')))
} else {
console.log('❌ Failed to parse result')
runResults.push(0)
}
}
} finally {
server.kill()
await sleep(500)
}
allRuns.push(runResults)
}
const average = (arr: number[]) => arr.reduce((a, b) => a + b, 0) / arr.length
const ping = average(allRuns.map((run) => run[0]))
const query = average(allRuns.map((run) => run[1]))
const body = average(allRuns.map((run) => run[2]))
const overall = (ping + query + body) / 3
return { name, average: overall, ping, query, body, runs: allRuns.map((run) => average(run)) }
}
const main = async () => {
console.log('🏁 Hono HTTP Benchmark')
console.log('======================')
console.log(`Baseline: ${baseline}`)
console.log(`Target: ${target}`)
console.log(`Runs: ${runs}`)
console.log(`Duration: ${duration}s`)
console.log(`Concurrency: ${concurrency}`)
console.log(`Skip Tests: ${skipTests}`)
console.log('')
setupTemp()
try {
// Compare baseline vs target
const baselinePath = await buildVersion(baseline, 'baseline')
const targetPath = await buildVersion(target, 'target')
const baselineResult = await runBenchmark(baselinePath, 'baseline')
const targetResult = await runBenchmark(targetPath, 'target')
// Calculate changes
const calculateChange = (target: number, baseline: number) =>
(((target - baseline) / baseline) * 100).toFixed(2)
const changes = {
average: calculateChange(targetResult.average, baselineResult.average),
ping: calculateChange(targetResult.ping, baselineResult.ping),
query: calculateChange(targetResult.query, baselineResult.query),
body: calculateChange(targetResult.body, baselineResult.body),
}
// Format numbers
const format = (num: number) => num.toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ',')
const formatChange = (change: string) => (Number(change) >= 0 ? '+' : '') + change + '%'
// Generate table data
const rows = [
{
framework: `hono (${baseline})`,
runtime: 'bun',
average: format(baselineResult.average),
ping: format(baselineResult.ping),
query: format(baselineResult.query),
body: format(baselineResult.body),
},
{
framework: `hono (${target})`,
runtime: 'bun',
average: format(targetResult.average),
ping: format(targetResult.ping),
query: format(targetResult.query),
body: format(targetResult.body),
},
{
framework: 'Change',
runtime: '',
average: formatChange(changes.average),
ping: formatChange(changes.ping),
query: formatChange(changes.query),
body: formatChange(changes.body),
},
]
const table = [
'| Framework | Runtime | Average | Ping | Query | Body |',
'| --- | --- | --- | --- | --- | --- |',
...rows.map(
(row) =>
`| ${row.framework} | ${row.runtime} | ${row.average} | ${row.ping} | ${row.query} | ${row.body} |`
),
]
// Console output
console.log('')
table.forEach((line) => console.log(line))
console.log('')
// Markdown output
const markdownOutput = ['## HTTP Performance Benchmark', '', ...table].join('\n')
writeFileSync(join(SCRIPT_DIR, 'benchmark-results.md'), markdownOutput)
} catch (error) {
console.error('❌ Benchmark failed:', error)
throw error
} finally {
if (existsSync(TEMP_DIR)) {
rmSync(TEMP_DIR, { recursive: true })
}
}
}
main()
================================================
FILE: benchmarks/jsx/package.json
================================================
{
"name": "jsx",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"bench:node": "esbuild --bundle src/benchmark.ts | node",
"bench:bun": "bun run src/benchmark.ts",
"bench:react-jsx:node": "esbuild --bundle src/react-jsx/benchmark.ts | node",
"compare-bundle-size": "esbuild --minify --minify-syntax --tree-shaking=true --bundle src/{hono,react,preact,nano}.ts --outdir=dist"
},
"license": "MIT",
"devDependencies": {
"esbuild": "^0.19.8",
"node-html-parser": "^6.1.11"
},
"dependencies": {
"@types/benchmark": "^2.1.5",
"@types/react": "^18.2.40",
"@types/react-dom": "^18.2.17",
"benchmark": "^2.1.4",
"hono": "^3.10.4",
"nano-jsx": "^0.1.0",
"preact": "^10.19.2",
"preact-render-to-string": "^6.3.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"typescript": "^5.3.2"
}
}
================================================
FILE: benchmarks/jsx/src/benchmark.ts
================================================
import { Suite } from 'benchmark'
import { parse } from 'node-html-parser'
import { render as renderHono } from './hono'
import { render as renderNano } from './nano'
import { render as renderPreact } from './preact'
import { render as renderReact } from './react'
const suite = new Suite()
;[renderHono, renderReact, renderPreact, renderNano].forEach((render) => {
const html = render()
const doc = parse(html)
if (doc.querySelector('p#c').textContent !== '3\nc') {
throw new Error('Invalid output')
}
})
suite
.add('Hono', () => {
renderHono()
})
.add('React', () => {
renderReact()
})
.add('Preact', () => {
renderPreact()
})
.add('Nano', () => {
renderNano()
})
.on('cycle', (ev) => {
console.log(String(ev.target))
})
.on('complete', (ev) => {
console.log(`Fastest is ${ev.currentTarget.filter('fastest').map('name')}`)
})
.run({ async: true })
================================================
FILE: benchmarks/jsx/src/hono.ts
================================================
import { jsx, Fragment } from '../../../src/jsx'
import { buildPage } from './page'
export const render = () => buildPage({ jsx, Fragment })().toString()
================================================
FILE: benchmarks/jsx/src/nano.ts
================================================
import { h, Fragment, renderSSR } from 'nano-jsx'
import { buildPage } from './page'
export const render = () => renderSSR(buildPage({ jsx: h, Fragment }))
================================================
FILE: benchmarks/jsx/src/page-react.tsx
================================================
/** @jsx jsx */
/** @jsxFrag Fragment */
export const buildPage = ({ jsx, Fragment }: { jsx: any; Fragment: any }) => {
const Content = () => (
<>
<p id='a' className='class-name'>
1<br />a
</p>
<p id='b' className='class-name'>
2<br />b
</p>
<div dangerouslySetInnerHTML={{ __html: '<p id="c" class="class-name">3<br/>c</p>' }} />
{null}
{undefined}
</>
)
const Form = () => (
<form>
<input type='text' value='1234567890 < 1234567891' readOnly tabIndex={1} />
<input type='checkbox' value='1234567890 < 1234567891' defaultChecked={true} tabIndex={2} />
<input type='checkbox' value='1234567890 < 1234567891' defaultChecked={true} tabIndex={3} />
<input type='checkbox' value='1234567890 < 1234567891' defaultChecked={false} tabIndex={4} />
<input type='checkbox' value='1234567890 < 1234567891' defaultChecked={false} tabIndex={5} />
</form>
)
return () => (
<html>
<body>
<Content />
<Form />
</body>
</html>
)
}
================================================
FILE: benchmarks/jsx/src/page.tsx
================================================
/** @jsx jsx */
/** @jsxFrag Fragment */
export const buildPage = ({ jsx, Fragment }: { jsx: any; Fragment: any }) => {
const Content = () => (
<>
<p id='a' class='class-name'>
1<br />a
</p>
<p id='b' class='class-name'>
2<br />b
</p>
<div dangerouslySetInnerHTML={{ __html: '<p id="c" class="class-name">3<br/>c</p>' }} />
{null}
{undefined}
</>
)
const Form = () => (
<form>
<input type='text' value='1234567890 < 1234567891' readonly tabindex={1} />
<input type='checkbox' value='1234567890 < 1234567891' checked={true} tabindex={2} />
<input type='checkbox' value='1234567890 < 1234567891' checked={true} tabindex={3} />
<input type='checkbox' value='1234567890 < 1234567891' checked={false} tabindex={4} />
<input type='checkbox' value='1234567890 < 1234567891' checked={false} tabindex={5} />
</form>
)
return () => (
<html>
<body>
<Content />
<Form />
</body>
</html>
)
}
================================================
FILE: benchmarks/jsx/src/preact.ts
================================================
import { h, Fragment } from 'preact'
import { renderToString } from 'preact-render-to-string'
import { buildPage } from './page'
export const render = () => renderToString(buildPage({ jsx: h, Fragment })() as any)
================================================
FILE: benchmarks/jsx/src/react-jsx/benchmark.ts
================================================
import { Suite } from 'benchmark'
import { parse } from 'node-html-parser'
import { render as renderHono } from './hono'
import { render as renderNano } from './nano'
import { render as renderPreact } from './preact'
import { render as renderReact } from './react'
const suite = new Suite()
;[renderHono, renderReact, renderPreact, renderNano].forEach((render) => {
const html = render()
const doc = parse(html)
if (doc.querySelector('p#c').textContent !== '3\nc') {
throw new Error('Invalid output')
}
})
suite
.add('Hono', () => {
renderHono()
})
.add('React', () => {
renderReact()
})
.add('Preact', () => {
renderPreact()
})
.add('Nano', () => {
renderNano()
})
.on('cycle', (ev) => {
console.log(String(ev.target))
})
.on('complete', (ev) => {
console.log(`Fastest is ${ev.currentTarget.filter('fastest').map('name')}`)
})
.run({ async: true })
================================================
FILE: benchmarks/jsx/src/react-jsx/hono.ts
================================================
import { buildPage } from './page-hono'
export const render = () => buildPage()().toString()
================================================
FILE: benchmarks/jsx/src/react-jsx/nano.ts
================================================
import { renderSSR } from 'nano-jsx'
import { buildPage } from './page-nano.tsx'
export const render = () => renderSSR(buildPage())
================================================
FILE: benchmarks/jsx/src/react-jsx/page-hono.tsx
================================================
/** @jsxImportSource ../../../../src/jsx **/
export const buildPage = () => {
const Content = () => (
<>
<p id='a' class='class-name'>
1<br />a
</p>
<p id='b' class='class-name'>
2<br />b
</p>
<div dangerouslySetInnerHTML={{ __html: '<p id="c" class="class-name">3<br/>c</p>' }} />
{null}
{undefined}
</>
)
const Form = () => (
<form>
<input type='text' value='1234567890 < 1234567891' readonly tabindex={1} />
<input type='checkbox' value='1234567890 < 1234567891' checked={true} tabindex={2} />
<input type='checkbox' value='1234567890 < 1234567891' checked={true} tabindex={3} />
<input type='checkbox' value='1234567890 < 1234567891' checked={false} tabindex={4} />
<input type='checkbox' value='1234567890 < 1234567891' checked={false} tabindex={5} />
</form>
)
return () => (
<html>
<body>
<Content />
<Form />
</body>
</html>
)
}
================================================
FILE: benchmarks/jsx/src/react-jsx/page-nano.tsx
================================================
/** @jsxImportSource nano-jsx/lib **/
export const buildPage = () => {
const Content = () => (
<>
<p id='a' class='class-name'>
1<br />a
</p>
<p id='b' class='class-name'>
2<br />b
</p>
<div dangerouslySetInnerHTML={{ __html: '<p id="c" class="class-name">3<br/>c</p>' }} />
{null}
{undefined}
</>
)
const Form = () => (
<form>
<input type='text' value='1234567890 < 1234567891' readonly tabindex={1} />
<input type='checkbox' value='1234567890 < 1234567891' checked={true} tabindex={2} />
<input type='checkbox' value='1234567890 < 1234567891' checked={true} tabindex={3} />
<input type='checkbox' value='1234567890 < 1234567891' checked={false} tabindex={4} />
<input type='checkbox' value='1234567890 < 1234567891' checked={false} tabindex={5} />
</form>
)
return () => (
<html>
<body>
<Content />
<Form />
</body>
</html>
)
}
================================================
FILE: benchmarks/jsx/src/react-jsx/page-preact.tsx
================================================
/** @jsxImportSource preact **/
export const buildPage = () => {
const Content = () => (
<>
<p id='a' class='class-name'>
1<br />a
</p>
<p id='b' class='class-name'>
2<br />b
</p>
<div dangerouslySetInnerHTML={{ __html: '<p id="c" class="class-name">3<br/>c</p>' }} />
{null}
{undefined}
</>
)
const Form = () => (
<form>
<input type='text' value='1234567890 < 1234567891' readonly tabindex={1} />
<input type='checkbox' value='1234567890 < 1234567891' checked={true} tabindex={2} />
<input type='checkbox' value='1234567890 < 1234567891' checked={true} tabindex={3} />
<input type='checkbox' value='1234567890 < 1234567891' checked={false} tabindex={4} />
<input type='checkbox' value='1234567890 < 1234567891' checked={false} tabindex={5} />
</form>
)
return () => (
<html>
<body>
<Content />
<Form />
</body>
</html>
)
}
================================================
FILE: benchmarks/jsx/src/react-jsx/page-react.tsx
================================================
/** @jsxImportSource react **/
export const buildPage = () => {
const Content = () => (
<>
<p id='a' className='class-name'>
1<br />a
</p>
<p id='b' className='class-name'>
2<br />b
</p>
<div dangerouslySetInnerHTML={{ __html: '<p id="c" class="class-name">3<br/>c</p>' }} />
{null}
{undefined}
</>
)
const Form = () => (
<form>
<input type='text' value='1234567890 < 1234567891' readOnly tabIndex={1} />
<input type='checkbox' value='1234567890 < 1234567891' defaultChecked={true} tabIndex={2} />
<input type='checkbox' value='1234567890 < 1234567891' defaultChecked={true} tabIndex={3} />
<input type='checkbox' value='1234567890 < 1234567891' defaultChecked={false} tabIndex={4} />
<input type='checkbox' value='1234567890 < 1234567891' defaultChecked={false} tabIndex={5} />
</form>
)
return () => (
<html>
<body>
<Content />
<Form />
</body>
</html>
)
}
================================================
FILE: benchmarks/jsx/src/react-jsx/preact.ts
================================================
import { renderToString } from 'preact-render-to-string'
import { buildPage } from './page-preact'
export const render = () => renderToString(buildPage()() as any)
================================================
FILE: benchmarks/jsx/src/react-jsx/react.ts
================================================
import { renderToString } from 'react-dom/server'
import { buildPage } from './page-react.tsx'
export const render = () => renderToString(buildPage()() as any)
================================================
FILE: benchmarks/jsx/src/react-jsx/tsconfig.json
================================================
{
"compilerOptions": {
"jsx": "react-jsx"
}
}
================================================
FILE: benchmarks/jsx/src/react.ts
================================================
import { createElement, Fragment } from 'react'
import { renderToString } from 'react-dom/server'
import { buildPage } from './page-react'
export const render = () => renderToString(buildPage({ jsx: createElement, Fragment })() as any)
================================================
FILE: benchmarks/jsx/tsconfig.json
================================================
{
"compilerOptions": {
"jsx": "react"
}
}
================================================
FILE: benchmarks/query-param/package.json
================================================
{
"scripts": {
"bench:node": "tsx ./src/bench.mts",
"bench:bun": "bun run ./src/bench.mts"
},
"license": "MIT",
"devDependencies": {
"@types/qs": "^6.9.17",
"tsx": "^3.12.2"
},
"dependencies": {
"fast-querystring": "^1.1.1",
"mitata": "^0.1.6",
"qs": "^6.13.0"
}
}
================================================
FILE: benchmarks/query-param/src/bench.mts
================================================
import { run, group, bench } from 'mitata'
import { getQueryStrings } from '../../../src/utils/url'
import fastQuerystring from './fast-querystring.mts'
import hono from './hono.mts'
import qs from './qs.mts'
;[
{
url: 'http://example.com/?page=1',
key: 'page',
},
{
url: 'http://example.com/?url=http://example.com&page=1',
key: 'page',
},
{
url: 'http://example.com/?page=1',
key: undefined,
},
{
url: 'http://example.com/?url=http://example.com&page=1',
key: undefined,
},
{
url: 'http://example.com/?url=http://example.com/very/very/deep/path/to/something&search=very-long-search-string',
key: undefined,
},
{
url: 'http://example.com/?search=Hono+is+a+small,+simple,+and+ultrafast+web+framework+for+the+Edge.&page=1',
key: undefined,
},
{
url: 'http://example.com/?a=1&b=2&c=3&d=4&e=5&f=6&g=7&h=8&i=9&j=10',
key: undefined,
},
].forEach((data) => {
const { url, key } = data
group(JSON.stringify(data), () => {
bench('hono', () => hono(url, key))
bench('fastQuerystring', () => fastQuerystring(url, key))
bench('qs', () => qs(url, key))
bench('URLSearchParams', () => {
const params = new URLSearchParams(getQueryStrings(url))
if (key) {
return params.get(key)
}
const obj = {}
for (const [k, v] of params) {
obj[k] = v
}
return obj
})
})
})
run()
================================================
FILE: benchmarks/query-param/src/fast-querystring.mts
================================================
import { parse } from 'fast-querystring'
const getQueryStringFromURL = (url: string): string => {
const queryIndex = url.indexOf('?', 8)
const result = queryIndex !== -1 ? url.slice(queryIndex + 1) : ''
return result
}
export default (url, key?) => {
const data = parse(getQueryStringFromURL(url))
return key !== undefined ? data[key] : data
}
================================================
FILE: benchmarks/query-param/src/hono.mts
================================================
import { getQueryParam } from '../../../src/utils/url'
export default (url, key?) => {
return getQueryParam(url, key)
}
================================================
FILE: benchmarks/query-param/src/qs.mts
================================================
import qs from 'qs'
const getQueryStringFromURL = (url: string): string => {
const queryIndex = url.indexOf('?', 8)
const result = queryIndex !== -1 ? url.slice(queryIndex + 1) : ''
return result
}
export default (url, key?) => {
const data = qs.parse(getQueryStringFromURL(url))
return key !== undefined ? data[key] : data
}
================================================
FILE: benchmarks/routers/README.md
================================================
# Router benchmarks
Benchmark of the most commonly used HTTP routers.
Tested routes:
- [find-my-way](https://github.com/delvedor/find-my-way)
- [express](https://www.npmjs.com/package/express)
- [koa-router](https://github.com/alexmingoia/koa-router)
- [koa-tree-router](https://github.com/steambap/koa-tree-router)
- [trek-router](https://www.npmjs.com/package/trek-router)
- [@medley/router](https://www.npmjs.com/package/@medley/router)
- [Hono RegExpRouter](https://github.com/honojs/hono)
- [Hono TrieRouter](https://github.com/honojs/hono)
Install:
```
bun install --frozen-lockfile
```
For Node.js:
```
bun run bench:node
```
For Bun:
```
bun run bench:bun
```
This project is heavily impaired by [delvedor/router-benchmark](https://github.com/delvedor/router-benchmark)
## License
MIT
================================================
FILE: benchmarks/routers/package.json
================================================
{
"scripts": {
"bench:node": "tsx ./src/bench.mts",
"bench:bun": "bun run ./src/bench.mts",
"bench-includes-init:node": "tsx ./src/bench-includes-init.mts",
"bench-includes-init:bun": "bun run ./src/bench-includes-init.mts"
},
"license": "MIT",
"devDependencies": {
"tsx": "^4.20.5"
},
"dependencies": {
"@medley/router": "^0.2.1",
"express": "^4.21.2",
"find-my-way": "^9.3.0",
"koa-router": "^12.0.1",
"koa-tree-router": "^0.13.1",
"memoirist": "^0.4.0",
"mitata": "^1.0.34",
"radix3": "^1.1.2",
"rou3": "^0.7.3",
"trek-router": "^1.2.0"
}
}
================================================
FILE: benchmarks/routers/src/bench-includes-init.mts
================================================
import MedleyRouter from '@medley/router'
import type { HTTPMethod } from 'find-my-way'
import findMyWay from 'find-my-way'
import KoaRouter from 'koa-tree-router'
import { run, bench, group } from 'mitata'
import TrekRouter from 'trek-router'
import { LinearRouter } from '../../../src/router/linear-router/index.ts'
import {
RegExpRouter,
PreparedRegExpRouter,
buildInitParams,
} from '../../../src/router/reg-exp-router/index.ts'
import { TrieRouter } from '../../../src/router/trie-router/index.ts'
import type { Route } from './tool.mts'
import { routes } from './tool.mts'
const preparedParams = buildInitParams({
paths: routes.map((r) => r.path),
})
const benchRoutes: (Route & { name: string })[] = [
{
name: 'short static',
method: 'GET',
path: '/user',
},
{
name: 'static with same radix',
method: 'GET',
path: '/user/comments',
},
{
name: 'dynamic route',
method: 'GET',
path: '/user/lookup/username/hey',
},
{
name: 'mixed static dynamic',
method: 'GET',
path: '/event/abcd1234/comments',
},
{
name: 'post',
method: 'POST',
path: '/event/abcd1234/comment',
},
{
name: 'long static',
method: 'GET',
path: '/very/deeply/nested/route/hello/there',
},
{
name: 'wildcard',
method: 'GET',
path: '/static/index.html',
},
]
for (const benchRoute of benchRoutes) {
group(`${benchRoute.method} ${benchRoute.path}`, () => {
bench('RegExpRouter', () => {
const router = new RegExpRouter()
for (const route of routes) {
router.add(route.method, route.path, () => {})
}
router.match(benchRoute.method, benchRoute.path)
})
bench('PreparedRegExpRouter', () => {
const router = new PreparedRegExpRouter(preparedParams[0], preparedParams[1])
for (const route of routes) {
router.add(route.method, route.path, () => {})
}
router.match(benchRoute.method, benchRoute.path)
})
bench('TrieRouter', () => {
const router = new TrieRouter()
for (const route of routes) {
router.add(route.method, route.path, () => {})
}
router.match(benchRoute.method, benchRoute.path)
})
bench('LinearRouter', () => {
const router = new LinearRouter()
for (const route of routes) {
router.add(route.method, route.path, () => {})
}
router.match(benchRoute.method, benchRoute.path)
})
bench('MedleyRouter', () => {
const router = new MedleyRouter()
for (const route of routes) {
const store = router.register(route.path)
store[route.method] = () => {}
}
const match = router.find(benchRoute.path)
match.store[benchRoute.method] // get handler
})
bench('FindMyWay', () => {
const router = findMyWay()
for (const route of routes) {
router.on(route.method as HTTPMethod, route.path, () => {})
}
router.find(benchRoute.method as HTTPMethod, benchRoute.path)
})
bench('KoaTreeRouter', () => {
const router = new KoaRouter()
for (const route of routes) {
router.on(route.method, route.path.replace('*', '*foo'), () => {})
}
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
router.find(benchRoute.method, benchRoute.path)
})
bench('TrekRouter', () => {
const router = new TrekRouter()
for (const route of routes) {
router.add(route.method, route.path, () => {})
}
router.find(benchRoute.method, benchRoute.path)
})
})
}
await run()
================================================
FILE: benchmarks/routers/src/bench.mts
================================================
import { run, bench, group, summary } from 'mitata'
import { expressRouter } from './express.mts'
import { findMyWayRouter } from './find-my-way.mts'
import { regExpRouter, trieRouter, patternRouter } from './hono.mts'
import { koaRouter } from './koa-router.mts'
import { koaTreeRouter } from './koa-tree-router.mts'
import { medleyRouter } from './medley-router.mts'
import { memoiristRouter } from './memoirist.mts'
import { radix3Router } from './radix3.mts'
import { rou3Router } from './rou3.mts'
import type { Route, RouterInterface } from './tool.mts'
import { trekRouter } from './trek-router.mts'
const routers: RouterInterface[] = [
regExpRouter,
trieRouter,
patternRouter,
medleyRouter,
findMyWayRouter,
koaTreeRouter,
trekRouter,
expressRouter,
koaRouter,
radix3Router,
memoiristRouter,
rou3Router,
]
medleyRouter.match({ method: 'GET', path: '/user' })
const routes: (Route & { name: string })[] = [
{
name: 'short static',
method: 'GET',
path: '/user',
},
{
name: 'static with same radix',
method: 'GET',
path: '/user/comments',
},
{
name: 'dynamic route',
method: 'GET',
path: '/user/lookup/username/hey',
},
{
name: 'mixed static dynamic',
method: 'GET',
path: '/event/abcd1234/comments',
},
{
name: 'post',
method: 'POST',
path: '/event/abcd1234/comment',
},
{
name: 'long static',
method: 'GET',
path: '/very/deeply/nested/route/hello/there',
},
{
name: 'wildcard',
method: 'GET',
path: '/static/index.html',
},
]
for (const route of routes) {
summary(() => {
group(`${route.name} - ${route.method} ${route.path}`, () => {
for (const router of routers) {
bench(router.name, async () => {
router.match(route)
})
}
})
})
}
group('all together', () => {
summary(() => {
for (const router of routers) {
bench(router.name, async () => {
for (const route of routes) {
router.match(route)
}
})
}
})
})
await run()
================================================
FILE: benchmarks/routers/src/express.mts
================================================
import routerFunc from 'express/lib/router/index.js'
import type { RouterInterface } from './tool.mts'
import { routes, handler } from './tool.mts'
const router = routerFunc()
const name = 'express (WARNING: includes handling)'
for (const route of routes) {
if (route.method === 'GET') {
router.route(route.path).get(handler)
} else {
router.route(route.path).post(handler)
}
}
export const expressRouter: RouterInterface = {
name,
match: (route) => {
router.handle({ method: route.method, url: route.path })
},
}
================================================
FILE: benchmarks/routers/src/find-my-way.mts
================================================
import type { HTTPMethod } from 'find-my-way'
import findMyWay from 'find-my-way'
import type { RouterInterface } from './tool.mts'
import { routes, handler } from './tool.mts'
const name = 'find-my-way'
const router = findMyWay()
for (const route of routes) {
router.on(route.method as HTTPMethod, route.path, handler)
}
export const findMyWayRouter: RouterInterface = {
name,
match: (route) => {
router.find(route.method as HTTPMethod, route.path)
},
}
================================================
FILE: benchmarks/routers/src/hono.mts
================================================
import { PatternRouter } from '../../../src/router/pattern-router/index.ts'
import { RegExpRouter } from '../../../src/router/reg-exp-router/index.ts'
import { TrieRouter } from '../../../src/router/trie-router/index.ts'
import type { Router } from '../../../src/router.ts'
import type { RouterInterface } from './tool.mts'
import { routes, handler } from './tool.mts'
const createHonoRouter = (name: string, router: Router<unknown>): RouterInterface => {
for (const route of routes) {
router.add(route.method, route.path, handler)
}
return {
name: `Hono ${name}`,
match: (route) => {
router.match(route.method, route.path)
},
}
}
export const regExpRouter = createHonoRouter('RegExpRouter', new RegExpRouter())
export const trieRouter = createHonoRouter('TrieRouter', new TrieRouter())
export const patternRouter = createHonoRouter('PatternRouter', new PatternRouter())
================================================
FILE: benchmarks/routers/src/koa-router.mts
================================================
import KoaRouter from 'koa-router'
import type { RouterInterface } from './tool.mts'
import { routes, handler } from './tool.mts'
const name = 'koa-router'
const router = new KoaRouter()
for (const route of routes) {
if (route.method === 'GET') {
router.get(route.path.replace('*', '(.*)'), handler)
} else {
router.post(route.path, handler)
}
}
export const koaRouter: RouterInterface = {
name,
match: (route) => {
router.match(route.path, route.method) // only matching
},
}
================================================
FILE: benchmarks/routers/src/koa-tree-router.mts
================================================
import KoaRouter from 'koa-tree-router'
import type { RouterInterface } from './tool.mts'
import { routes, handler } from './tool.mts'
const name = 'koa-tree-router'
const router = new KoaRouter()
for (const route of routes) {
router.on(route.method, route.path.replace('*', '*foo'), handler)
}
export const koaTreeRouter: RouterInterface = {
name,
match: (route) => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
router.find(route.method, route.path)
},
}
================================================
FILE: benchmarks/routers/src/medley-router.mts
================================================
import Router from '@medley/router'
import type { RouterInterface } from './tool.mts'
import { routes, handler } from './tool.mts'
const name = '@medley/router'
const router = new Router()
for (const route of routes) {
const store = router.register(route.path)
store[route.method] = handler
}
export const medleyRouter: RouterInterface = {
name,
match: (route) => {
const match = router.find(route.path)
match.store[route.method] // get handler
},
}
================================================
FILE: benchmarks/routers/src/memoirist.mts
================================================
import { Memoirist } from 'memoirist'
import type { RouterInterface } from './tool.mts'
import { routes, handler } from './tool.mts'
const name = 'Memoirist'
const router = new Memoirist()
for (const route of routes) {
router.add(route.method, route.path, handler)
}
export const memoiristRouter: RouterInterface = {
name,
match: (route) => {
router.find(route.method, route.path)
},
}
================================================
FILE: benchmarks/routers/src/radix3.mts
================================================
import { createRouter } from 'radix3'
import type { RouterInterface } from './tool.mts'
import { routes, handler } from './tool.mts'
const name = 'radix3'
const router = createRouter()
for (const route of routes) {
router.insert(route.path, handler)
}
export const radix3Router: RouterInterface = {
name,
match: (route) => {
router.lookup(route.path)
},
}
================================================
FILE: benchmarks/routers/src/rou3.mts
================================================
import { addRoute, createRouter, findRoute } from 'rou3'
import type { RouterInterface } from './tool.mts'
import { handler, routes } from './tool.mts'
const name = 'rou3'
const router = createRouter()
for (const route of routes) {
addRoute(router, route.path, route.method, handler)
}
export const rou3Router: RouterInterface = {
name,
match: (route) => {
findRoute(router, route.path, route.method, {
ignoreParams: false, // Don't ignore params
})
},
}
================================================
FILE: benchmarks/routers/src/tool.mts
================================================
export const handler = () => {}
export type Route = {
method: 'GET' | 'POST'
path: string
}
export interface RouterInterface {
name: string
match: (route: Route) => unknown
}
export const routes: Route[] = [
{ method: 'GET', path: '/user' },
{ method: 'GET', path: '/user/comments' },
{ method: 'GET', path: '/user/avatar' },
{ method: 'GET', path: '/user/lookup/username/:username' },
{ method: 'GET', path: '/user/lookup/email/:address' },
{ method: 'GET', path: '/event/:id' },
{ method: 'GET', path: '/event/:id/comments' },
{ method: 'POST', path: '/event/:id/comment' },
{ method: 'GET', path: '/map/:location/events' },
{ method: 'GET', path: '/status' },
{ method: 'GET', path: '/very/deeply/nested/route/hello/there' },
{ method: 'GET', path: '/static/*' },
]
================================================
FILE: benchmarks/routers/src/trek-router.mts
================================================
import TrekRouter from 'trek-router'
import type { RouterInterface } from './tool.mts'
import { routes, handler } from './tool.mts'
const name = 'trek-router'
const router = new TrekRouter()
for (const route of routes) {
router.add(route.method, route.path, handler())
}
export const trekRouter: RouterInterface = {
name,
match: (route) => {
router.find(route.method, route.path)
},
}
================================================
FILE: benchmarks/routers/tsconfig.json
================================================
{
"compilerOptions": {
"allowImportingTsExtensions": true,
"esModuleInterop": true,
"module": "NodeNext"
},
"include": ["./src"]
}
================================================
FILE: benchmarks/routers-deno/.vscode/settings.json
================================================
{
"eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact"],
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
"deno.enable": true
}
================================================
FILE: benchmarks/routers-deno/README.md
================================================
# Router benchmarks
Benchmark of the most commonly used HTTP routers.
Tested routes:
- [find-my-way](https://github.com/delvedor/find-my-way)
- [koa-router](https://github.com/alexmingoia/koa-router)
- [koa-tree-router](https://github.com/steambap/koa-tree-router)
- [trek-router](https://www.npmjs.com/package/trek-router)
- [@medley/router](https://www.npmjs.com/package/@medley/router)
- [Hono RegExpRouter](https://github.com/honojs/hono)
- [Hono TrieRouter](https://github.com/honojs/hono)
For Deno:
```
deno run --allow-read --allow-run src/bench.mts
```
This project is heavily impaired by [delvedor/router-benchmark](https://github.com/delvedor/router-benchmark)
## License
MIT
================================================
FILE: benchmarks/routers-deno/deno.json
================================================
{
"imports": {
"npm/": "https://unpkg.com/"
}
}
================================================
FILE: benchmarks/routers-deno/src/bench.mts
================================================
import { run, bench, group } from 'npm:mitata'
import { findMyWayRouter } from './find-my-way.mts'
import { regExpRouter, trieRouter, patternRouter } from './hono.mts'
import { koaRouter } from './koa-router.mts'
import { koaTreeRouter } from './koa-tree-router.mts'
import { medleyRouter } from './medley-router.mts'
import type { Route, RouterInterface } from './tool.mts'
import { trekRouter } from './trek-router.mts'
const routers: RouterInterface[] = [
regExpRouter,
trieRouter,
patternRouter,
medleyRouter,
findMyWayRouter,
koaTreeRouter,
trekRouter,
koaRouter,
]
medleyRouter.match({ method: 'GET', path: '/user' })
const routes: (Route & { name: string })[] = [
{
name: 'short static',
method: 'GET',
path: '/user',
},
{
name: 'static with same radix',
method: 'GET',
path: '/user/comments',
},
{
name: 'dynamic route',
method: 'GET',
path: '/user/lookup/username/hey',
},
{
name: 'mixed static dynamic',
method: 'GET',
path: '/event/abcd1234/comments',
},
{
name: 'post',
method: 'POST',
path: '/event/abcd1234/comment',
},
{
name: 'long static',
method: 'GET',
path: '/very/deeply/nested/route/hello/there',
},
{
name: 'wildcard',
method: 'GET',
path: '/static/index.html',
},
]
for (const route of routes) {
group(`${route.name} - ${route.method} ${route.path}`, () => {
for (const router of routers) {
bench(router.name, async () => {
router.match(route)
})
}
})
}
group('all together', () => {
for (const router of routers) {
bench(router.name, async () => {
for (const route of routes) {
router.match(route)
}
})
}
})
await run()
================================================
FILE: benchmarks/routers-deno/src/find-my-way.mts
================================================
import type { HTTPMethod } from 'npm:find-my-way'
import findMyWay from 'npm:find-my-way'
import type { RouterInterface } from './tool.mts'
import { routes, handler } from './tool.mts'
const name = 'find-my-way'
const router = findMyWay()
for (const route of routes) {
router.on(route.method as HTTPMethod, route.path, handler)
}
export const findMyWayRouter: RouterInterface = {
name,
match: (route) => {
router.find(route.method as HTTPMethod, route.path)
},
}
================================================
FILE: benchmarks/routers-deno/src/hono.mts
================================================
import { PatternRouter } from '../../../src/router/pattern-router/index.ts'
import { RegExpRouter } from '../../../src/router/reg-exp-router/index.ts'
import { TrieRouter } from '../../../src/router/trie-router/index.ts'
import type { Router } from '../../../src/router.ts'
import type { RouterInterface } from './tool.mts'
import { routes, handler } from './tool.mts'
const createHonoRouter = (name: string, router: Router<unknown>): RouterInterface => {
for (const route of routes) {
router.add(route.method, route.path, handler)
}
return {
name: `Hono ${name}`,
match: (route) => {
router.match(route.method, route.path)
},
}
}
export const regExpRouter = createHonoRouter('RegExpRouter', new RegExpRouter())
export const trieRouter = createHonoRouter('TrieRouter', new TrieRouter())
export const patternRouter = createHonoRouter('PatternRouter', new PatternRouter())
================================================
FILE: benchmarks/routers-deno/src/koa-router.mts
================================================
import KoaRouter from 'npm:koa-router'
import type { RouterInterface } from './tool.mts'
import { routes, handler } from './tool.mts'
const name = 'koa-router'
const router = new KoaRouter()
for (const route of routes) {
if (route.method === 'GET') {
router.get(route.path.replace('*', '(.*)'), handler)
} else {
router.post(route.path, handler)
}
}
export const koaRouter: RouterInterface = {
name,
match: (route) => {
router.match(route.path, route.method) // only matching
},
}
================================================
FILE: benchmarks/routers-deno/src/koa-tree-router.mts
================================================
import KoaRouter from 'npm:koa-tree-router'
import type { RouterInterface } from './tool.mts'
import { routes, handler } from './tool.mts'
const name = 'koa-tree-router'
const router = new KoaRouter()
for (const route of routes) {
router.on(route.method, route.path.replace('*', '*foo'), handler)
}
export const koaTreeRouter: RouterInterface = {
name,
match: (route) => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
router.find(route.method, route.path)
},
}
================================================
FILE: benchmarks/routers-deno/src/medley-router.mts
================================================
import Router from 'npm:@medley/router'
import type { RouterInterface } from './tool.mts'
import { routes, handler } from './tool.mts'
const name = '@medley/router'
const router = new Router()
for (const route of routes) {
const store = router.register(route.path)
store[route.method] = handler
}
export const medleyRouter: RouterInterface = {
name,
match: (route) => {
const match = router.find(route.path)
match.store[route.method] // get handler
},
}
================================================
FILE: benchmarks/routers-deno/src/tool.mts
================================================
export const handler = () => {}
export type Route = {
method: 'GET' | 'POST'
path: string
}
export interface RouterInterface {
name: string
match: (route: Route) => unknown
}
export const routes: Route[] = [
{ method: 'GET', path: '/user' },
{ method: 'GET', path: '/user/comments' },
{ method: 'GET', path: '/user/avatar' },
{ method: 'GET', path: '/user/lookup/username/:username' },
{ method: 'GET', path: '/user/lookup/email/:address' },
{ method: 'GET', path: '/event/:id' },
{ method: 'GET', path: '/event/:id/comments' },
{ method: 'POST', path: '/event/:id/comment' },
{ method: 'GET', path: '/map/:location/events' },
{ method: 'GET', path: '/status' },
{ method: 'GET', path: '/very/deeply/nested/route/hello/there' },
{ method: 'GET', path: '/static/*' },
]
================================================
FILE: benchmarks/routers-deno/src/trek-router.mts
================================================
import TrekRouter from 'npm:trek-router'
import type { RouterInterface } from './tool.mts'
import { routes, handler } from './tool.mts'
const name = 'trek-router'
const router = new TrekRouter()
for (const route of routes) {
router.add(route.method, route.path, handler())
}
export const trekRouter: RouterInterface = {
name,
match: (route) => {
router.find(route.method, route.path)
},
}
================================================
FILE: benchmarks/utils/.gitignore
================================================
yarn.lock
bun.lockb
================================================
FILE: benchmarks/utils/package.json
================================================
{
"type": "module",
"devDependencies": {
"mitata": "^0.1.11"
}
}
================================================
FILE: benchmarks/utils/src/get-path.ts
================================================
import { run, group, bench } from 'mitata'
bench('noop', () => {})
const request = new Request('http://localhost/about/me')
group('getPath', () => {
bench('slice + indexOf : w/o decodeURI', () => {
const url = request.url
const queryIndex = url.indexOf('?', 8)
return url.slice(url.indexOf('/', 8), queryIndex === -1 ? undefined : queryIndex)
})
bench('regexp : w/o decodeURI', () => {
const match = request.url.match(/^https?:\/\/[^/]+(\/[^?]*)/)
return match ? match[1] : ''
})
bench('slice + indexOf', () => {
const url = request.url
const queryIndex = url.indexOf('?', 8)
const path = url.slice(url.indexOf('/', 8), queryIndex === -1 ? undefined : queryIndex)
return path.includes('%') ? decodeURIComponent(path) : path
})
bench('slice + for-loop + flag', () => {
const url = request.url
const start = url.indexOf('/', 8)
let i = start
let hasPercentEncoding = false
for (; i < url.length; i++) {
const charCode = url.charCodeAt(i)
if (charCode === 37) {
// '%'
hasPercentEncoding = true
} else if (charCode === 63) {
// '?'
break
}
}
return hasPercentEncoding ? decodeURIComponent(url.slice(start, i)) : url.slice(start, i)
})
bench('slice + for-loop + immediate return', () => {
const url = request.url
const start = url.indexOf('/', 8)
let i = start
for (; i < url.length; i++) {
const charCode = url.charCodeAt(i)
if (charCode === 37) {
// '%'
// If the path contains percent encoding, use `indexOf()` to find '?' and return the result immediately.
// Although this is a performance disadvantage, it is acceptable since we prefer cases that do not include percent encoding.
const queryIndex = url.indexOf('?', i)
const path = url.slice(start, queryIndex === -1 ? undefined : queryIndex)
return decodeURI(path.includes('%25') ? path.replace(/%25/g, '%2525') : path)
} else if (charCode === 63) {
// '?'
break
}
}
return url.slice(start, i)
})
})
run()
================================================
FILE: benchmarks/utils/src/loop.js
================================================
import { run, group, bench } from 'mitata'
const arr = new Array(100000).fill(Math.random())
bench('noop', () => {})
group('loop', () => {
bench('map', () => {
const newArr = []
arr.map((e) => {
newArr.push(e)
})
})
bench('forEach', () => {
const newArr = []
arr.forEach((e) => {
newArr.push(e)
})
})
bench('for of', () => {
const newArr = []
for (const e of arr) {
newArr.push(e)
}
})
bench('for', () => {
const newArr = []
for (let i = 0; i < arr.length; i++) {
newArr.push(arr[i])
}
})
})
run()
================================================
FILE: benchmarks/webapp/.gitignore
================================================
yarn.lock
================================================
FILE: benchmarks/webapp/hono.js
================================================
import { Hono } from '../../dist/hono'
//import { Hono } from 'hono'
const hono = new Hono()
hono.get('/user', (c) => c.text('User'))
hono.get('/user/comments', (c) => c.text('User Comments'))
hono.get('/user/avatar', (c) => c.text('User Avatar'))
hono.get('/user/lookup/email/:address', (c) => c.text('User Lookup Email Address'))
hono.get('/event/:id', (c) => c.text('Event'))
hono.get('/event/:id/comments', (c) => c.text('Event Comments'))
hono.post('/event/:id/comments', (c) => c.text('POST Event Comments'))
hono.post('/status', (c) => c.text('Status'))
hono.get('/very/deeply/nested/route/hello/there', (c) => c.text('Very Deeply Nested Route'))
hono.get('/user/lookup/username/:username', (c) => {
return new Response(`Hello ${c.req.param('username')}`)
})
hono.fire()
================================================
FILE: benchmarks/webapp/itty-router.js
================================================
import { Router } from 'itty-router'
const ittyRouter = Router()
ittyRouter.get('/user', () => new Response('User'))
ittyRouter.get('/user/comments', () => new Response('User Comments'))
ittyRouter.get('/user/avatar', () => new Response('User Avatar'))
ittyRouter.get('/user/lookup/email/:address', () => new Response('User Lookup Email Address'))
ittyRouter.get('/event/:id', () => new Response('Event'))
ittyRouter.get('/event/:id/comments', () => new Response('Event Comments'))
ittyRouter.post('/event/:id/comments', () => new Response('POST Event Comments'))
ittyRouter.post('/status', () => new Response('Status'))
ittyRouter.get(
'/very/deeply/nested/route/hello/there',
() => new Response('Very Deeply Nested Route')
)
ittyRouter.get('/user/lookup/username/:username', ({ params }) => {
return new Response(`Hello ${params.username}`, {
status: 200,
headers: {
'Content-Type': 'text/plain;charset=UTF-8',
},
})
})
addEventListener('fetch', (event) => event.respondWith(ittyRouter.handle(event.request)))
================================================
FILE: benchmarks/webapp/package.json
================================================
{
"name": "webapp",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"start:hono": "wrangler dev hono.js --local --port 8787",
"start:itty-router": "wrangler dev itty-router.js --local --port 8788",
"start:sunder": "wrangler dev sunder.js --local --port 8789"
},
"license": "MIT",
"dependencies": {
"itty-router": "^2.6.1",
"sunder": "^0.10.1"
}
}
================================================
FILE: benchmarks/webapp/sunder.js
================================================
import { Sunder, Router } from 'sunder'
const sunderRouter = new Router()
sunderRouter.get('/user', (ctx) => {
ctx.response.body = 'User'
})
sunderRouter.get('/user/comments', (ctx) => {
ctx.response.body = 'User Comments'
})
sunderRouter.get('/user/avatar', (ctx) => {
ctx.response.body = 'User Avatar'
})
sunderRouter.get('/user/lookup/email/:address', (ctx) => {
ctx.response.body = 'User Lookup Email Address'
})
sunderRouter.get('/event/:id', (ctx) => {
ctx.response.body = 'Event'
})
sunderRouter.get('/event/:id/comments', (ctx) => {
ctx.response.body = 'Event Comments'
})
sunderRouter.post('/event/:id/comments', (ctx) => {
ctx.response.body = 'POST Event Comments'
})
sunderRouter.post('/status', (ctx) => {
ctx.response.body = 'Status'
})
sunderRouter.get('/very/deeply/nested/route/hello/there', (ctx) => {
ctx.response.body = 'Very Deeply Nested Route'
})
//sunderRouter.get('/static/*', () => {})
sunderRouter.get('/user/lookup/username/:username', (ctx) => {
ctx.response.body = `Hello ${ctx.params.username}`
})
const sunderApp = new Sunder()
sunderApp.use(sunderRouter.middleware)
addEventListener('fetch', (event) => {
event.respondWith(sunderApp.handle(event))
})
================================================
FILE: build/build.ts
================================================
/*
This script is heavily inspired by `built.ts` used in @kaze-style/react.
https://github.com/taishinaritomi/kaze-style/blob/main/scripts/build.ts
MIT License
Copyright (c) 2022 Taishi Naritomi
*/
/// <reference types="bun-types/bun" />
import arg from 'arg'
import { $ } from 'bun'
import { build, context } from 'esbuild'
import type { Plugin, PluginBuild, BuildOptions } from 'esbuild'
import * as glob from 'glob'
import fs from 'fs'
import path from 'path'
import { removePrivateFields } from './remove-private-fields'
import { validateExports } from './validate-exports'
const args = arg({
'--watch': Boolean,
})
const isWatch = args['--watch'] || false
const readJsonExports = (path: string) => JSON.parse(fs.readFileSync(path, 'utf-8')).exports
const [packageJsonExports, jsrJsonExports] = ['./package.json', './jsr.json'].map(readJsonExports)
// Validate exports of package.json and jsr.json
validateExports(packageJsonExports, jsrJsonExports, 'jsr.json')
validateExports(jsrJsonExports, packageJsonExports, 'package.json')
const entryPoints = glob.sync('./src/**/*.ts', {
ignore: ['./src/**/*.test.ts', './src/mod.ts', './src/middleware.ts', './src/deno/**/*.ts'],
})
/*
This plugin is inspired by the following.
https://github.com/evanw/esbuild/issues/622#issuecomment-769462611
*/
const addExtension = (extension: string = '.js', fileExtension: string = '.ts'): Plugin => ({
name: 'add-extension',
setup(build: PluginBuild) {
build.onResolve({ filter: /.*/ }, (args) => {
if (args.importer) {
const p = path.join(args.resolveDir, args.path)
let tsPath = `${p}${fileExtension}`
let importPath = ''
if (fs.existsSync(tsPath)) {
importPath = args.path + extension
} else {
tsPath = path.join(args.resolveDir, args.path, `index${fileExtension}`)
if (fs.existsSync(tsPath)) {
if (args.path.endsWith('/')) {
importPath = `${args.path}index${extension}`
} else {
importPath = `${args.path}/index${extension}`
}
}
}
return { path: importPath, external: true }
}
})
},
})
const commonOptions: BuildOptions = {
entryPoints,
logLevel: 'info',
platform: 'node',
}
const cjsConfig: BuildOptions = {
...commonOptions,
outbase: './src',
outdir: './dist/cjs',
format: 'cjs',
}
const esmConfig: BuildOptions = {
...commonOptions,
bundle: true,
outbase: './src',
outdir: './dist',
format: 'esm',
plugins: [addExtension('.js')],
}
const runBuild = async (config: BuildOptions) => {
if (isWatch) {
const ctx = await context(config)
await ctx.watch()
} else {
await build(config)
}
}
await Promise.all([
runBuild(esmConfig),
runBuild(cjsConfig),
$`tsc ${
isWatch ? '-w' : ''
} --emitDeclarationOnly --declaration --project tsconfig.build.json`.nothrow(),
])
// Remove #private fields
const dtsEntries = glob.globSync('./dist/types/**/*.d.ts')
await removePrivateFields(dtsEntries)
================================================
FILE: build/remove-private-fields.test.ts
================================================
/// <reference types="vitest/globals" />
import { parseSync } from 'oxc-parser'
import { removePrivateFieldFromSourceCode } from './remove-private-fields'
describe('removePrivateFields', () => {
it('should remove private fields from declarations', () => {
const sourceCode = `
import type { Result, Router } from '../../router';
export declare class PatternRouter<T> implements Router<T> {
#private;
name: string;
add(method: string, path: string, handler: T): void;
match(method: string, path: string): Result<T>;
}
`.trim()
const ast = parseSync('types.d.ts', sourceCode)
const result = removePrivateFieldFromSourceCode(ast, sourceCode)
expect(result).toBeDefined()
// expected code should be same, but the `#private;` is replaced with spaces
const expected = sourceCode.replace('#private;', ' '.repeat(9))
expect(result).toMatch(expected)
})
})
================================================
FILE: build/remove-private-fields.ts
================================================
import type { PropertyDefinition, ParseResult } from 'oxc-parser'
import { parseSync, Visitor } from 'oxc-parser'
import { readFile, writeFile } from 'fs/promises'
export async function removePrivateFields(files: string[]) {
const start = performance.now()
const parsed = await Promise.all(
files.map(async (file) => {
const sourceCode = await readFile(file, 'utf-8')
const ast = parseSync(file, sourceCode)
return { file, sourceCode, ast }
})
)
await Promise.all(
parsed.map(async ({ file, sourceCode, ast }) => {
const sourceCodeWithoutPrivateFields = removePrivateFieldFromSourceCode(ast, sourceCode)
if (sourceCodeWithoutPrivateFields) {
await writeFile(file, sourceCodeWithoutPrivateFields)
}
})
)
const end = performance.now()
console.log(`Done removing private fields in ${(end - start).toFixed(2)}ms`)
}
export function removePrivateFieldFromSourceCode(ast: ParseResult, sourceCode: string) {
const removals: PropertyDefinition[] = []
new Visitor({
ClassDeclaration: (node) => {
node.body.body.forEach((elem) => {
if (elem.type === 'PropertyDefinition' && elem.key.type === 'PrivateIdentifier') {
removals.push(elem)
}
})
},
}).visit(ast.program)
if (removals.length === 0) {
return
}
let sourceCodeWithoutPrivateFields = sourceCode
for (const elem of removals) {
sourceCodeWithoutPrivateFields = removeRange(
sourceCodeWithoutPrivateFields,
elem.start,
elem.end
)
}
return sourceCodeWithoutPrivateFields
}
function removeRange(str: string, start: number, end: number) {
return str.slice(0, start) + ' '.repeat(end - start) + str.slice(end)
}
================================================
FILE: build/validate-exports.test.ts
================================================
/// <reference types="vitest/globals" />
import { validateExports } from './validate-exports'
const mockExports1 = {
'./a': './a.ts',
'./b': './b.ts',
'./c/a': './c.ts',
'./d/*': './d/*.ts',
}
const mockExports2 = {
'./a': './a.ts',
'./b': './b.ts',
'./c/a': './c.ts',
'./d/a': './d/a.ts',
}
const mockExports3 = {
'./a': './a.ts',
'./c/a': './c.ts',
'./d/*': './d/*.ts',
}
describe('validateExports', () => {
it('Works', async () => {
expect(() => validateExports(mockExports1, mockExports1, 'package.json')).not.toThrowError()
expect(() => validateExports(mockExports1, mockExports2, 'jsr.json')).not.toThrowError()
expect(() => validateExports(mockExports1, mockExports3, 'package.json')).toThrowError()
})
})
================================================
FILE: build/validate-exports.ts
================================================
export const validateExports = (
source: Record<string, unknown>,
target: Record<string, unknown>,
fileName: string
) => {
const isEntryInTarget = (entry: string): boolean => {
if (entry in target) {
return true
}
// e.g., "./utils/*" -> "./utils"
const wildcardPrefix = entry.replace(/\/\*$/, '')
if (entry.endsWith('/*')) {
return Object.keys(target).some(
(targetEntry) =>
targetEntry.startsWith(wildcardPrefix + '/') && targetEntry !== wildcardPrefix
)
}
const separatedEntry = entry.split('/')
while (separatedEntry.length > 0) {
const pattern = `${separatedEntry.join('/')}/*`
if (pattern in target) {
return true
}
separatedEntry.pop()
}
return false
}
Object.keys(source).forEach((sourceEntry) => {
if (!isEntryInTarget(sourceEntry)) {
throw new Error(`Missing "${sourceEntry}" in '${fileName}'`)
}
})
}
================================================
FILE: bunfig.toml
================================================
[test]
coverage = true
coverageReporter = ["text", "lcov"]
coverageDir = "coverage/raw/bun"
================================================
FILE: codecov.yml
================================================
# Edit "test.coverage.exclude" option in vitest.config.ts to exclude specific files from coverage reports.
# We can also use "ignore" option in codecov.yml, but it is not recognized by vitest, so results may differ on local.
coverage:
status:
patch:
default:
target: 80%
informational: true # Don't fail the build even if coverage is below target
project:
default:
target: 75%
threshold: 1%
================================================
FILE: docs/CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
- Demonstrating empathy and kindness toward other people
- Being respectful of differing opinions, viewpoints, and experiences
- Giving and gracefully accepting constructive feedback
- Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
- Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
- The use of sexualized language or imagery, and sexual attention or
advances of any kind
- Trolling, insulting or derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or email
address, without their explicit permission
- Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, or offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
yusuke@kamawada.com.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or a series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of the community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.
================================================
FILE: docs/CONTRIBUTING.md
================================================
# Contribution Guide
Contributions Welcome! We will be glad for your help.
You can contribute in the following ways.
- Create an Issue - Propose a new feature. Report a bug.
- Pull Request - Fix a bug or typo. Refactor the code.
- Create third-party middleware - See instructions below.
- Share - Share your thoughts on the Blog, X, and others.
- Make your application - Please try to use Hono.
Note:
This project is started by Yusuke Wada ([@yusukebe](https://github.com/yusukebe)) for the hobby proposal.
It was just for fun. For now, this stance has not been changed basically.
I want to write the code as I like.
So, if you propose great ideas, but I do not appropriate them, the idea may not be accepted.
Although, don't worry!
Hono is tested well, polished by the contributors, and used by many developers. And I'll try my best to make Hono cool and hot, beautiful, and ultrafast.
## Installing dependencies
The `honojs/hono` project uses [Bun](https://bun.sh/) as its package manager. Developers should install Bun.
After that, please install the dependency environment.
```bash
bun install --frozen-lockfile
```
## PRs
Please ensure your PR passes tests with `bun run test`.
## Third-party middleware
Third-party middleware is not in the core.
It is allowed to depend on other libraries or work only in specific environments, such as Cloudflare Workers. For example:
- GraphQL Server middleware
- Firebase Auth middleware
- Sentry middleware
You can make a third-party middleware by yourself.
It may be under the "honojs organization" and distributed in the `@honojs` namespace.
The monorepo "[honojs/middleware](https://github.com/honojs/middleware)" manages these middleware.
If you want to do it, create an issue about your middleware.
## Local Development
```bash
git clone git@github.com:honojs/hono.git && cd hono/.devcontainer && bun install --frozen-lockfile
docker compose up -d --build
docker compose exec hono bash
```
================================================
FILE: docs/MIGRATION.md
================================================
# Migration Guide
## v4.3.11 to v4.4.0
### `deno.land/x` to JSR
There is no breaking change, but we no longer publish the module from `deno.land/x`. If you want to use Hono on Deno, use JSR instead of it.
If you migrate, replace the path `deno.land/x` with JSR's.
```ts
// From
import { Hono } from 'https://deno.land/x/hono/mod.ts'
// To
import { Hono } from 'jsr:@hono/hono'
```
You can see more details on our website: https://hono.dev/getting-started/deno
## v3.12.x to v4.0.0
There are some breaking changes.
### Removal of deprecated features
- AWS Lambda Adapter - `LambdaFunctionUrlRequestContext` is obsolete. Use `ApiGatewayRequestContextV2` instead.
- Next.js Adapter - `hono/nextjs` is obsolete. Use `hono/vercel` instead.
- Context - `c.jsonT()` is obsolete. Use `c.json()` instead.
- Context - `c.stream()` and `c.streamText()` are obsolete. Use `stream()` and `streamText()` in `hono/streaming` instead.
- Context - `c.env()` is obsolete. Use `getRuntimeKey()` in `hono/adapter` instead.
- Hono - `app.showRoutes()` is obsolete. Use `showRoutes()` in `hono/dev` instead.
- Hono - `app.routerName` is obsolete. Use `getRouterName()` in `hono/dev` instead.
- Hono - `app.head()` is no longer used. `app.get()` implicitly handles the HEAD method.
- Hono - `app.handleEvent()` is obsolete. Use `app.fetch()` instead.
- HonoRequest - `req.cookie()` is obsolete. Use `getCookie()` in `hono/cookie` instead.
- HonoRequest - `headers()`, `body()`, `bodyUsed()`, `integrity()`, `keepalive()`, `referrer()`, and `signal()` are obsolete. Use the methods in `req.raw` such as `req.raw.headers()`.
### `serveStatic` in Cloudflare Workers Adapter requires `manifest`
If you use the Cloudflare Workers adapter's `serve-static`, you should specify the `manifest` option.
```ts
import manifest from '__STATIC_CONTENT_MANIFEST'
// ...
app.use('/static/*', serveStatic({ root: './assets', manifest }))
```
### Others
- The default value of the `docType` option in JSX Renderer Middleware is now `true`.
- `FC` in `hono/jsx` does not pass `children`. Use `PropsWithChildren`.
- Some Mime Types are removed https://github.com/honojs/hono/pull/2119.
- Types for chaining routes with middleware matter: https://github.com/honojs/hono/pull/2046.
- Types for the validator matter: https://github.com/honojs/hono/pull/2130.
## v2.7.8 to v3.0.0
There are some breaking changes.
In addition to the following, type mismatches may occur.
### `c.req` is now `HonoRequest`
`c.req` becomes `HonoRequest`, not `Request`.
Although APIs are almost the same, if you want to access `Request`, use `c.req.raw`.
```ts
app.post('/', async (c) => {
const metadata = c.req.raw.cf?.hostMetadata?
...
})
```
### StaticRouter is obsolete
You can't use `StaticRouter`.
### Validator has been changed
Previous Validator Middleware is obsolete.
You can still use `hono/validator`, but the API has been changed.
See [the document](https://hono.dev).
### `serveStatic` is provided from the Adapter
Serve Static Middleware is obsolete. Use Adapters instead.
```ts
// For Cloudflare Workers
import { serveStatic } from 'hono/cloudflare-workers'
// For Bun
// import { serveStatic } from 'hono/bun'
// For Deno
// import { serveStatic } from 'npm:hono/deno'
// ...
app.get('/static/*', serveStatic({ root: './' }))
```
### `serveStatic` for Cloudflare Workers "Service Worker mode" is obsolete
For Cloudflare Workers, the `serveStatic` is obsolete in Service Worker mode.
Note: Service Worker mode is that using `app.fire()`.
We recommend use "Module Worker" mode with `export default app`.
### Use `type` to define the Generics for `new Hono`
You must use `type` to define the Generics for `new Hono`. Do not use `interface`.
```ts
// Should use `type`
type Bindings = {
TOKEN: string
}
const app = new Hono<{ Bindings: Bindings }>()
```
## v2.7.1 - v2.x.x
### Current Validator Middleware is deprecated
At the next major version, Validator Middleware will be changed with "breaking changes". Therefore, the current Validator Middleware will be deprecated; please use 3rd-party Validator libraries such as [Zod](https://zod.dev) or [TypeBox](https://github.com/sinclairzx81/typebox).
```ts
import * as z from 'zod'
//...
const schema = z.object({
title: z.string().max(100),
})
app.post('/posts', async (c) => {
const body = await c.req.parseBody()
const res = schema.safeParse(body)
if (!res.success) {
return c.text('Invalid!', 400)
}
return c.text('Valid!')
})
```
## v2.2.5 to v2.3.0
There is a breaking change associated with the security update.
### Basic Auth Middleware and Bearer Auth Middleware
If you are using Basic Auth and Bearer Auth in your Handler (nested), change as follows:
```ts
app.use('/auth/*', async (c, next) => {
const auth = basicAuth({ username: c.env.USERNAME, password: c.env.PASSWORD })
return auth(c, next) // Older: `await auth(c, next)`
})
```
## v2.0.9 to v2.1.0
There are two BREAKING CHANGES.
### `c.req.parseBody` does not parse JSON, text, and ArrayBuffer
**DO NOT** use `c.req.parseBody` for parsing **JSON**, **text**, or **ArrayBuffer**.
`c.req.parseBody` now only parses FormData with content type `multipart/form` or `application/x-www-form-urlencoded`. If you want to parse JSON, text, or ArrayBuffer, use `c.req.json()`, `c.req.text()`, or `c.req.arrayBuffer()`.
```ts
// `multipart/form` or `application/x-www-form-urlencoded`
const data = await c.req.parseBody()
const jsonData = await c.req.json() // for JSON body
const text = await c.req.text() // for text body
const arrayBuffer = await c.req.arrayBuffer() // for ArrayBuffer
```
### The arguments of Generics for `new Hono` have been changed
Now, the constructor of "Hono" receives `Variables` and `Bindings`.
"Bindings" is for types of environment variables for Cloudflare Workers. "Variables" is for types of `c.set`/`c.get`
```ts
type Bindings = {
KV: KVNamespace
Storage: R2Bucket
}
type WebClient = {
user: string
pass: string
}
type Variables = {
client: WebClient
}
const app = new Hono<{ Variables: Variables; Bindings: Bindings }>()
app.get('/foo', (c) => {
const client = c.get('client') // client is WebClient
const kv = c.env.KV // kv is KVNamespace
//...
})
```
## v1.6.4 to v2.0.0
There are many BREAKING CHANGES. Please follow the instructions below.
### The way to import Middleware on Deno has been changed
**DO NOT** import middleware from `hono/mod.ts`.
```ts
import { Hono, poweredBy } from 'https://deno.land/x/hono/mod.ts' // <--- NG
```
`hono/mod.ts` does not export middleware.
To import middleware, use `hono/middleware.ts`:
```ts
import { Hono } from 'https://deno.land/x/hono/mod.ts'
import { poweredBy, basicAuth } from 'https://deno.land/x/hono/middleware.ts'
```
### Cookie middleware is obsolete
**DO NOT** use `cookie` middleware.
```ts
import { cookie } from 'hono/cookie' // <--- Obsolete!
```
You do not have to use Cookie middleware to parse or set cookies.
They become default functions:
```ts
// Parse cookie
app.get('/entry/:id', (c) => {
const value = c.req.cookie('name')
...
})
```
```ts
app.get('/', (c) => {
c.cookie('delicious_cookie', 'choco')
return c.text('Do you like cookie?')
})
```
### Body parse middleware is obsolete
**DO NOT** use `body-parse` middleware.
```ts
import { bodyParse } from 'hono/body-parse' // <--- Obsolete!
```
You do not have to use Body parse middleware to parse the request body. Use `c.req.parseBody()` method instead.
```ts
// Parse Request body
app.post('', (c) => {
const body = c.req.parseBody()
...
})
```
### GraphQL Server middleware is obsolete
**DO NOT** use `graphql-server` middleware.
```ts
import { graphqlServer } from 'hono/graphql-server' // <--- Obsolete!
```
It might be distributed as third-party middleware.
### Mustache middleware is obsolete
**DO NOT** use `mustache` middleware.
```ts
import { mustache } from 'hono/mustache' // <--- Obsolete!
```
It will no longer be implemented.
================================================
FILE: eslint.config.mjs
================================================
import baseConfig from '@hono/eslint-config'
import { defineConfig, globalIgnores } from 'eslint/config'
// Disable all TypeScript rules that require type information
const typeCheckedRules = {
'@typescript-eslint/await-thenable': 'off',
'@typescript-eslint/no-base-to-string': 'off',
'@typescript-eslint/no-confusing-void-expression': 'off',
'@typescript-eslint/no-duplicate-type-constituents': 'off',
'@typescript-eslint/no-floating-promises': 'off',
'@typescript-eslint/no-for-in-array': 'off',
'@typescript-eslint/no-implied-eval': 'off',
'@typescript-eslint/no-meaningless-void-operator': 'off',
'@typescript-eslint/no-misused-promises': 'off',
'@typescript-eslint/no-mixed-enums': 'off',
'@typescript-eslint/no-redundant-type-constituents': 'off',
'@typescript-eslint/no-unnecessary-boolean-literal-compare': 'off',
'@typescript-eslint/no-unnecessary-condition': 'off',
'@typescript-eslint/no-unnecessary-template-expression': 'off',
'@typescript-eslint/no-unnecessary-type-arguments': 'off',
'@typescript-eslint/no-unnecessary-type-assertion': 'off',
"@typescript-eslint/no-unnecessary-type-conversion": 'off',
'@typescript-eslint/no-unsafe-argument': 'off',
'@typescript-eslint/no-unsafe-assignment': 'off',
'@typescript-eslint/no-unsafe-call': 'off',
'@typescript-eslint/no-unsafe-enum-comparison': 'off',
'@typescript-eslint/no-unsafe-member-access': 'off',
'@typescript-eslint/no-unsafe-return': 'off',
'@typescript-eslint/no-unsafe-unary-minus': 'off',
'@typescript-eslint/only-throw-error': 'off',
'@typescript-eslint/prefer-includes': 'off',
'@typescript-eslint/prefer-nullish-coalescing': 'off',
'@typescript-eslint/prefer-optional-chain': 'off',
'@typescript-eslint/prefer-promise-reject-errors': 'off',
'@typescript-eslint/prefer-reduce-type-parameter': 'off',
'@typescript-eslint/prefer-regexp-exec': 'off',
'@typescript-eslint/prefer-return-this-type': 'off',
'@typescript-eslint/prefer-string-starts-ends-with': 'off',
'@typescript-eslint/require-await': 'off',
'@typescript-eslint/restrict-plus-operands': 'off',
'@typescript-eslint/restrict-template-expressions': 'off',
'@typescript-eslint/return-await': 'off',
'@typescript-eslint/strict-boolean-expressions': 'off',
'@typescript-eslint/unbound-method': 'off',
'@typescript-eslint/use-unknown-in-catch-callback-variable': 'off',
'@typescript-eslint/prefer-find': 'off',
'@typescript-eslint/no-misused-spread': 'off',
'@typescript-eslint/related-getter-setter-pairs': 'off',
'@typescript-eslint/prefer-literal-enum-member': 'off',
// Stylistic rules
'@typescript-eslint/consistent-indexed-object-style': 'off',
'@typescript-eslint/consistent-type-definitions': 'off',
'@typescript-eslint/dot-notation': 'off',
'@typescript-eslint/no-array-delete': 'off',
'@typescript-eslint/no-confusing-non-null-assertion': 'off',
'@typescript-eslint/no-deprecated': 'off',
'@typescript-eslint/no-dynamic-delete': 'off',
'@typescript-eslint/no-invalid-void-type': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/no-unnecessary-type-parameters': 'off',
'@typescript-eslint/no-useless-constructor': 'off',
"@typescript-eslint/no-useless-default-assignment": 'off',
'@typescript-eslint/non-nullable-type-assertion-style': 'off',
'@typescript-eslint/prefer-for-of': 'off',
'@typescript-eslint/prefer-function-type': 'off',
'@typescript-eslint/unified-signatures': 'off',
'@typescript-eslint/consistent-generic-constructors': 'off',
'@typescript-eslint/array-type': 'off',
'@typescript-eslint/no-extraneous-class': 'off',
}
export default defineConfig(globalIgnores(['.wrangler', '**/coverage', '**/dist']), {
extends: baseConfig,
linterOptions: {
reportUnusedDisableDirectives: 'error',
reportUnusedInlineConfigs: 'error',
},
rules: typeCheckedRules,
})
================================================
FILE: jsr.json
================================================
{
"name": "@hono/hono",
"version": "0.0.0",
"compilerOptions": {
"lib": ["dom", "dom.iterable", "deno.ns"]
},
"unstable": ["sloppy-imports"],
"exports": {
".": "./src/index.ts",
"./request": "./src/request.ts",
"./types": "./src/types.ts",
"./hono-base": "./src/hono-base.ts",
"./tiny": "./src/preset/tiny.ts",
"./quick": "./src/preset/quick.ts",
"./http-exception": "./src/http-exception.ts",
"./basic-auth": "./src/middleware/basic-auth/index.ts",
"./bearer-auth": "./src/middleware/bearer-auth/index.ts",
"./body-limit": "./src/middleware/body-limit/index.ts",
"./ip-restriction": "./src/middleware/ip-restriction/index.ts",
"./cache": "./src/middleware/cache/index.ts",
"./route": "./src/helper/route/index.ts",
"./cookie": "./src/helper/cookie/index.ts",
"./accepts": "./src/helper/accepts/index.ts",
"./compress": "./src/middleware/compress/index.ts",
"./context-storage": "./src/middleware/context-storage/index.ts",
"./cors": "./src/middleware/cors/index.ts",
"./csrf": "./src/middleware/csrf/index.ts",
"./etag": "./src/middleware/etag/index.ts",
"./trailing-slash": "./src/middleware/trailing-slash/index.ts",
"./html": "./src/helper/html/index.ts",
"./css": "./src/helper/css/index.ts",
"./jsx": "./src/jsx/index.ts",
"./jsx/jsx-dev-runtime": "./src/jsx/jsx-dev-runtime.ts",
"./jsx/jsx-runtime": "./src/jsx/jsx-runtime.ts",
"./jsx/streaming": "./src/jsx/streaming.ts",
"./jsx-renderer": "./src/middleware/jsx-renderer/index.ts",
"./jsx/dom": "./src/jsx/dom/index.ts",
"./jsx/dom/jsx-dev-runtime": "./src/jsx/dom/jsx-dev-runtime.ts",
"./jsx/dom/jsx-runtime": "./src/jsx/dom/jsx-runtime.ts",
"./jsx/dom/client": "./src/jsx/dom/client.ts",
"./jsx/dom/css": "./src/jsx/dom/css.ts",
"./jsx/dom/server": "./src/jsx/dom/server.ts",
"./jwt": "./src/middleware/jwt/jwt.ts",
"./jwk": "./src/middleware/jwk/jwk.ts",
"./timeout": "./src/middleware/timeout/index.ts",
"./timing": "./src/middleware/timing/timing.ts",
"./logger": "./src/middleware/logger/index.ts",
"./method-override": "./src/middleware/method-override/index.ts",
"./powered-by": "./src/middleware/powered-by/index.ts",
"./pretty-json": "./src/middleware/pretty-json/index.ts",
"./request-id": "./src/middleware/request-id/request-id.ts",
"./language": "./src/middleware/language/language.ts",
"./secure-headers": "./src/middleware/secure-headers/secure-headers.ts",
"./combine": "./src/middleware/combine/index.ts",
"./ssg": "./src/helper/ssg/index.ts",
"./streaming": "./src/helper/streaming/index.ts",
"./validator": "./src/validator/index.ts",
"./router": "./src/router.ts",
"./router/reg-exp-router": "./src/router/reg-exp-router/index.ts",
"./router/smart-router": "./src/router/smart-router/index.ts",
"./router/trie-router": "./src/router/trie-router/index.ts",
"./router/pattern-router": "./src/router/pattern-router/index.ts",
"./router/linear-router": "./src/router/linear-router/index.ts",
"./client": "./src/client/index.ts",
"./adapter": "./src/helper/adapter/index.ts",
"./factory": "./src/helper/factory/index.ts",
"./serve-static": "./src/middleware/serve-static/index.ts",
"./cloudflare-workers": "./src/adapter/cloudflare-workers/index.ts",
"./cloudflare-pages": "./src/adapter/cloudflare-pages/index.ts",
"./deno": "./src/adapter/deno/index.ts",
"./bun": "./src/adapter/bun/index.ts",
"./aws-lambda": "./src/adapter/aws-lambda/index.ts",
"./vercel": "./src/adapter/vercel/index.ts",
"./netlify": "./src/adapter/netlify/index.ts",
"./lambda-edge": "./src/adapter/lambda-edge/index.ts",
"./service-worker": "./src/adapter/service-worker/index.ts",
"./testing": "./src/helper/testing/index.ts",
"./dev": "./src/helper/dev/index.ts",
"./ws": "./src/helper/websocket/index.ts",
"./conninfo": "./src/helper/conninfo/index.ts",
"./proxy": "./src/helper/proxy/index.ts",
"./utils/body": "./src/utils/body.ts",
"./utils/buffer": "./src/utils/buffer.ts",
"./utils/color": "./src/utils/color.ts",
"./utils/concurrent": "./src/utils/concurrent.ts",
"./utils/cookie": "./src/utils/cookie.ts",
"./utils/crypto": "./src/utils/crypto.ts",
"./utils/encode": "./src/utils/encode.ts",
"./utils/filepath": "./src/utils/filepath.ts",
"./utils/handler": "./src/utils/handler.ts",
"./utils/headers": "./src/utils/headers.ts",
"./utils/html": "./src/utils/html.ts",
"./utils/http-status": "./src/utils/http-status.ts",
"./utils/accept": "./src/utils/accept.ts",
"./utils/jwt": "./src/utils/jwt/index.ts",
"./utils/jwt/jwa": "./src/utils/jwt/jwa.ts",
"./utils/jwt/jws": "./src/utils/jwt/jws.ts",
"./utils/jwt/jwt": "./src/utils/jwt/jwt.ts",
"./utils/jwt/types": "./src/utils/jwt/types.ts",
"./utils/jwt/utf8": "./src/utils/jwt/utf8.ts",
"./utils/mime": "./src/utils/mime.ts",
"./utils/stream": "./src/utils/stream.ts",
"./utils/types": "./src/utils/types.ts",
"./utils/url": "./src/utils/url.ts",
"./utils/ipaddr": "./src/utils/ipaddr.ts"
},
"publish": {
"include": ["jsr.json", "LICENSE", "README.md", "src/**/*.ts"],
"exclude": ["src/**/*.test.ts", "src/**/*.test.tsx"]
}
}
================================================
FILE: package.cjs.json
================================================
{
"type": "commonjs"
}
================================================
FILE: package.json
================================================
{
"name": "hono",
"version": "4.12.8",
"description": "Web framework built on Web Standards",
"main": "dist/cjs/index.js",
"type": "module",
"module": "dist/index.js",
"types": "dist/types/index.d.ts",
"files": [
"dist"
],
"scripts": {
"test": "tsc --noEmit && vitest --run",
"test:watch": "vitest --watch",
"test:deno": "deno test --allow-read --allow-env --allow-write --allow-net -c runtime-tests/deno/deno.json runtime-tests/deno && deno test --no-lock -c runtime-tests/deno-jsx/deno.precompile.json runtime-tests/deno-jsx && deno test --no-lock -c runtime-tests/deno-jsx/deno.react-jsx.json runtime-tests/deno-jsx",
"test:bun": "bun test --jsx-import-source ../../src/jsx runtime-tests/bun/*",
"test:fastly": "vitest --run --project fastly",
"test:node": "vitest --run --project node",
"test:workerd": "vitest --run --project workerd",
"test:lambda": "vitest --run --project lambda",
"test:lambda-edge": "vitest --run --project lambda-edge",
"test:all": "bun run test && bun test:deno && bun test:bun",
"lint": "eslint src runtime-tests build perf-measures benchmarks",
"lint:fix": "eslint src runtime-tests build perf-measures benchmarks --fix",
"format": "prettier --check --cache \"src/**/*.{js,ts,tsx}\" \"runtime-tests/**/*.{js,ts,tsx}\" \"build/**/*.{js,ts,tsx}\" \"perf-measures/**/*.{js,ts,tsx}\" \"benchmarks/**/*.{js,ts,tsx}\"",
"format:fix": "prettier --write --cache --cache-strategy metadata \"src/**/*.{js,ts,tsx}\" \"runtime-tests/**/*.{js,ts,tsx}\" \"build/**/*.{js,ts,tsx}\" \"perf-measures/**/*.{js,ts,tsx}\" \"benchmarks/**/*.{js,ts,tsx}\"",
"editorconfig-checker": "editorconfig-checker",
"copy:package.cjs.json": "cp ./package.cjs.json ./dist/cjs/package.json && cp ./package.cjs.json ./dist/types/package.json",
"build": "bun run --shell bun remove-dist && bun ./build/build.ts && bun run copy:package.cjs.json",
"postbuild": "publint",
"watch": "bun run --shell bun remove-dist && bun ./build/build.ts --watch && bun run copy:package.cjs.json",
"coverage": "vitest --run --coverage",
"prerelease": "bun test:deno && bun run build",
"release": "np",
"remove-dist": "rm -rf dist"
},
"exports": {
".": {
"types": "./dist/types/index.d.ts",
"import": "./dist/index.js",
"require": "./dist/cjs/index.js"
},
"./request": {
"types": "./dist/types/request.d.ts",
"import": "./dist/request.js",
"require": "./dist/cjs/request.js"
},
"./types": {
"types": "./dist/types/types.d.ts",
"import": "./dist/types.js",
"require": "./dist/cjs/types.js"
},
"./hono-base": {
"types": "./dist/types/hono-base.d.ts",
"import": "./dist/hono-base.js",
"require": "./dist/cjs/hono-base.js"
},
"./tiny": {
"types": "./dist/types/preset/tiny.d.ts",
"import": "./dist/preset/tiny.js",
"require": "./dist/cjs/preset/tiny.js"
},
"./quick": {
"types": "./dist/types/preset/quick.d.ts",
"import": "./dist/preset/quick.js",
"require": "./dist/cjs/preset/quick.js"
},
"./http-exception": {
"types": "./dist/types/http-exception.d.ts",
"import": "./dist/http-exception.js",
"require": "./dist/cjs/http-exception.js"
},
"./basic-auth": {
"types": "./dist/types/middleware/basic-auth/index.d.ts",
"import": "./dist/middleware/basic-auth/index.js",
"require": "./dist/cjs/middleware/basic-auth/index.js"
},
"./bearer-auth": {
"types": "./dist/types/middleware/bearer-auth/index.d.ts",
"import": "./dist/middleware/bearer-auth/index.js",
"require": "./dist/cjs/middleware/bearer-auth/index.js"
},
"./body-limit": {
"types": "./dist/types/middleware/body-limit/index.d.ts",
"import": "./dist/middleware/body-limit/index.js",
"require": "./dist/cjs/middleware/body-limit/index.js"
},
"./ip-restriction": {
"types": "./dist/types/middleware/ip-restriction/index.d.ts",
"import": "./dist/middleware/ip-restriction/index.js",
"require": "./dist/cjs/middleware/ip-restriction/index.js"
},
"./cache": {
"types": "./dist/types/middleware/cache/index.d.ts",
"import": "./dist/middleware/cache/index.js",
"require": "./dist/cjs/middleware/cache/index.js"
},
"./route": {
"types": "./dist/types/helper/route/index.d.ts",
"import": "./dist/helper/route/index.js",
"require": "./dist/cjs/helper/route/index.js"
},
"./cookie": {
"types": "./dist/types/helper/cookie/index.d.ts",
"import": "./dist/helper/cookie/index.js",
"require": "./dist/cjs/helper/cookie/index.js"
},
"./accepts": {
"types": "./dist/types/helper/accepts/index.d.ts",
"import": "./dist/helper/accepts/index.js",
"require": "./dist/cjs/helper/accepts/index.js"
},
"./compress": {
"types": "./dist/types/middleware/compress/index.d.ts",
"import": "./dist/middleware/compress/index.js",
"require": "./dist/cjs/middleware/compress/index.js"
},
"./context-storage": {
"types": "./dist/types/middleware/context-storage/index.d.ts",
"import": "./dist/middleware/context-storage/index.js",
"require": "./dist/cjs/middleware/context-storage/index.js"
},
"./cors": {
"types": "./dist/types/middleware/cors/index.d.ts",
"import": "./dist/middleware/cors/index.js",
"require": "./dist/cjs/middleware/cors/index.js"
},
"./csrf": {
"types": "./dist/types/middleware/csrf/index.d.ts",
"import": "./dist/middleware/csrf/index.js",
"require": "./dist/cjs/middleware/csrf/index.js"
},
"./etag": {
"types": "./dist/types/middleware/etag/index.d.ts",
"import": "./dist/middleware/etag/index.js",
"require": "./dist/cjs/middleware/etag/index.js"
},
"./trailing-slash": {
"types": "./dist/types/middleware/trailing-slash/index.d.ts",
"import": "./dist/middleware/trailing-slash/index.js",
"require": "./dist/cjs/middleware/trailing-slash/index.js"
},
"./html": {
"types": "./dist/types/helper/html/index.d.ts",
"import": "./dist/helper/html/index.js",
"require": "./dist/cjs/helper/html/index.js"
},
"./css": {
"types": "./dist/types/helper/css/index.d.ts",
"import": "./dist/helper/css/index.js",
"require": "./dist/cjs/helper/css/index.js"
},
"./jsx": {
"types": "./dist/types/jsx/index.d.ts",
"import": "./dist/jsx/index.js",
"require": "./dist/cjs/jsx/index.js"
},
"./jsx/jsx-dev-runtime": {
"types": "./dist/types/jsx/jsx-dev-runtime.d.ts",
"import": "./dist/jsx/jsx-dev-runtime.js",
"require": "./dist/cjs/jsx/jsx-dev-runtime.js"
},
"./jsx/jsx-runtime": {
"types": "./dist/types/jsx/jsx-runtime.d.ts",
"import": "./dist/jsx/jsx-runtime.js",
"require": "./dist/cjs/jsx/jsx-runtime.js"
},
"./jsx/streaming": {
"types": "./dist/types/jsx/streaming.d.ts",
"import": "./dist/jsx/streaming.js",
"require": "./dist/cjs/jsx/streaming.js"
},
"./jsx-renderer": {
"types": "./dist/types/middleware/jsx-renderer/index.d.ts",
"import": "./dist/middleware/jsx-renderer/index.js",
"require": "./dist/cjs/middleware/jsx-renderer/index.js"
},
"./jsx/dom": {
"types": "./dist/types/jsx/dom/index.d.ts",
"import": "./dist/jsx/dom/index.js",
"require": "./dist/cjs/jsx/dom/index.js"
},
"./jsx/dom/jsx-dev-runtime": {
"types": "./dist/types/jsx/dom/jsx-dev-runtime.d.ts",
"import": "./dist/jsx/dom/jsx-dev-runtime.js",
"require": "./dist/cjs/jsx/dom/jsx-dev-runtime.js"
},
"./jsx/dom/jsx-runtime": {
"types": "./dist/types/jsx/dom/jsx-runtime.d.ts",
"import": "./dist/jsx/dom/jsx-runtime.js",
"require": "./dist/cjs/jsx/dom/jsx-runtime.js"
},
"./jsx/dom/client": {
"types": "./dist/types/jsx/dom/client.d.ts",
"import": "./dist/jsx/dom/client.js",
"require": "./dist/cjs/jsx/dom/client.js"
},
"./jsx/dom/css": {
"types": "./dist/types/jsx/dom/css.d.ts",
"import": "./dist/jsx/dom/css.js",
"require": "./dist/cjs/jsx/dom/css.js"
},
"./jsx/dom/server": {
"types": "./dist/types/jsx/dom/server.d.ts",
"import": "./dist/jsx/dom/server.js",
"require": "./dist/cjs/jsx/dom/server.js"
},
"./jwt": {
"types": "./dist/types/middleware/jwt/index.d.ts",
"import": "./dist/middleware/jwt/index.js",
"require": "./dist/cjs/middleware/jwt/index.js"
},
"./jwk": {
"types": "./dist/types/middleware/jwk/index.d.ts",
"import": "./dist/middleware/jwk/index.js",
"require": "./dist/cjs/middleware/jwk/index.js"
},
"./timeout": {
"types": "./dist/types/middleware/timeout/index.d.ts",
"import": "./dist/middleware/timeout/index.js",
"require": "./dist/cjs/middleware/timeout/index.js"
},
"./timing": {
"types": "./dist/types/middleware/timing/index.d.ts",
"import": "./dist/middleware/timing/index.js",
"require": "./dist/cjs/middleware/timing/index.js"
},
"./logger": {
"types": "./dist/types/middleware/logger/index.d.ts",
"import": "./dist/middleware/logger/index.js",
"require": "./dist/cjs/middleware/logger/index.js"
},
"./method-override": {
"types": "./dist/types/middleware/method-override/index.d.ts",
"import": "./dist/middleware/method-override/index.js",
"require": "./dist/cjs/middleware/method-override/index.js"
},
"./powered-by": {
"types": "./dist/types/middleware/powered-by/index.d.ts",
"import": "./dist/middleware/powered-by/index.js",
"require": "./dist/cjs/middleware/powered-by/index.js"
},
"./pretty-json": {
"types": "./dist/types/middleware/pretty-json/index.d.ts",
"import": "./dist/middleware/pretty-json/index.js",
"require": "./dist/cjs/middleware/pretty-json/index.js"
},
"./request-id": {
"types": "./dist/types/middleware/request-id/index.d.ts",
"import": "./dist/middleware/request-id/index.js",
"require": "./dist/cjs/middleware/request-id/index.js"
},
"./language": {
"types": "./dist/types/middleware/language/index.d.ts",
"import": "./dist/middleware/language/index.js",
"require": "./dist/cjs/middleware/language/index.js"
},
"./secure-headers": {
"types": "./dist/types/middleware/secure-headers/index.d.ts",
"import": "./dist/middleware/secure-headers/index.js",
"require": "./dist/cjs/middleware/secure-headers/index.js"
},
"./combine": {
"types": "./dist/types/middleware/combine/index.d.ts",
"import": "./dist/middleware/combine/index.js",
"require": "./dist/cjs/middleware/combine/index.js"
},
"./ssg": {
"types": "./dist/types/helper/ssg/index.d.ts",
"import": "./dist/helper/ssg/index.js",
"require": "./dist/cjs/helper/ssg/index.js"
},
"./streaming": {
"types": "./dist/types/helper/streaming/index.d.ts",
"import": "./dist/helper/streaming/index.js",
"require": "./dist/cjs/helper/streaming/index.js"
},
"./validator": {
"types": "./dist/types/validator/index.d.ts",
"import": "./dist/validator/index.js",
"require": "./dist/cjs/validator/index.js"
},
"./router": {
"types": "./dist/types/router.d.ts",
"import": "./dist/router.js",
"require": "./dist/cjs/router.js"
},
"./router/reg-exp-router": {
"types": "./dist/types/router/reg-exp-router/index.d.ts",
"import": "./dist/router/reg-exp-router/index.js",
"require": "./dist/cjs/router/reg-exp-router/index.js"
},
"./router/smart-router": {
"types": "./dist/types/router/smart-router/index.d.ts",
"import": "./dist/router/smart-router/index.js",
"require": "./dist/cjs/router/smart-router/index.js"
},
"./router/trie-router": {
"types": "./dist/types/router/trie-router/index.d.ts",
"import": "./dist/router/trie-router/index.js",
"require": "./dist/cjs/router/trie-router/index.js"
},
"./router/pattern-router": {
"types": "./dist/types/router/pattern-router/index.d.ts",
"import": "./dist/router/pattern-router/index.js",
"require": "./dist/cjs/router/pattern-router/index.js"
},
"./router/linear-router": {
"types": "./dist/types/router/linear-router/index.d.ts",
"import": "./dist/router/linear-router/index.js",
"require": "./dist/cjs/router/linear-router/index.js"
},
"./utils/jwt": {
"types": "./dist/types/utils/jwt/index.d.ts",
"import": "./dist/utils/jwt/index.js",
"require": "./dist/cjs/utils/jwt/index.js"
},
"./utils/*": {
"types": "./dist/types/utils/*.d.ts",
"import": "./dist/utils/*.js",
"require": "./dist/cjs/utils/*.js"
},
"./client": {
"types": "./dist/types/client/index.d.ts",
"import": "./dist/client/index.js",
"require": "./dist/cjs/client/index.js"
},
"./adapter": {
"types": "./dist/types/helper/adapter/index.d.ts",
"import": "./dist/helper/adapter/index.js",
"require": "./dist/cjs/helper/adapter/index.js"
},
"./factory": {
"types": "./dist/types/helper/factory/index.d.ts",
"import": "./dist/helper/factory/index.js",
"require": "./dist/cjs/helper/factory/index.js"
},
"./serve-static": {
"types": "./dist/types/middleware/serve-static/index.d.ts",
"import": "./dist/middleware/serve-static/index.js",
"require": "./dist/cjs/middleware/serve-static/index.js"
},
"./cloudflare-workers": {
"types": "./dist/types/adapter/cloudflare-workers/index.d.ts",
"import": "./dist/adapter/cloudflare-workers/index.js",
"require": "./dist/cjs/adapter/cloudflare-workers/index.js"
},
"./cloudflare-pages": {
"types": "./dist/types/adapter/cloudflare-pages/index.d.ts",
"import": "./dist/adapter/cloudflare-pages/index.js",
"require": "./dist/cjs/adapter/cloudflare-pages/index.js"
},
"./deno": {
"types": "./dist/types/adapter/deno/index.d.ts",
"import": "./dist/adapter/deno/index.js",
"require": "./dist/cjs/adapter/deno/index.js"
},
"./bun": {
"types": "./dist/types/adapter/bun/index.d.ts",
"import": "./dist/adapter/bun/index.js",
"require": "./dist/cjs/adapter/bun/index.js"
},
"./aws-lambda": {
"types": "./dist/types/adapter/aws-lambda/index.d.ts",
"import": "./dist/adapter/aws-lambda/index.js",
"require": "./dist/cjs/adapter/aws-lambda/index.js"
},
"./vercel": {
"types": "./dist/types/adapter/vercel/index.d.ts",
"import": "./dist/adapter/vercel/index.js",
"require": "./dist/cjs/adapter/vercel/index.js"
},
"./netlify": {
"types": "./dist/types/adapter/netlify/index.d.ts",
"import": "./dist/adapter/netlify/index.js",
"require": "./dist/cjs/adapter/netlify/index.js"
},
"./lambda-edge": {
"types": "./dist/types/adapter/lambda-edge/index.d.ts",
"import": "./dist/adapter/lambda-edge/index.js",
"require": "./dist/cjs/adapter/lambda-edge/index.js"
},
"./service-worker": {
"types": "./dist/types/adapter/service-worker/index.d.ts",
"import": "./dist/adapter/service-worker/index.js",
"require": "./dist/cjs/adapter/service-worker/index.js"
},
"./testing": {
"types": "./dist/types/helper/testing/index.d.ts",
"import": "./dist/helper/testing/index.js",
"require": "./dist/cjs/helper/testing/index.js"
},
"./dev": {
"types": "./dist/types/helper/dev/index.d.ts",
"import": "./dist/helper/dev/index.js",
"require": "./dist/cjs/helper/dev/index.js"
},
"./ws": {
"types": "./dist/types/helper/websocket/index.d.ts",
"import": "./dist/helper/websocket/index.js",
"require": "./dist/cjs/helper/websocket/index.js"
},
"./conninfo": {
"types": "./dist/types/helper/conninfo/index.d.ts",
"import": "./dist/helper/conninfo/index.js",
"require": "./dist/cjs/helper/conninfo/index.js"
},
"./proxy": {
"types": "./dist/types/helper/proxy/index.d.ts",
"import": "./dist/helper/proxy/index.js",
"require": "./dist/cjs/helper/proxy/index.js"
}
},
"typesVersions": {
"*": {
"request": [
"./dist/types/request"
],
"types": [
"./dist/types/types"
],
"hono-base": [
"./dist/types/hono-base"
],
"tiny": [
"./dist/types/preset/tiny"
],
"quick": [
"./dist/types/preset/quick"
],
"http-exception": [
"./dist/types/http-exception"
],
"basic-auth": [
"./dist/types/middleware/basic-auth"
],
"bearer-auth": [
"./dist/types/middleware/bearer-auth"
],
"body-limit": [
"./dist/types/middleware/body-limit"
],
"ip-restriction": [
"./dist/types/middleware/ip-restriction"
],
"cache": [
"./dist/types/middleware/cache"
],
"route": [
"./dist/types/helper/route"
],
"cookie": [
"./dist/types/helper/cookie"
],
"accepts": [
"./dist/types/helper/accepts"
],
"compress": [
"./dist/types/middleware/compress"
],
"context-storage": [
"./dist/types/middleware/context-storage"
],
"cors": [
"./dist/types/middleware/cors"
],
"csrf": [
"./dist/types/middleware/csrf"
],
"etag": [
"./dist/types/middleware/etag"
],
"trailing-slash": [
"./dist/types/middleware/trailing-slash"
],
"html": [
"./dist/types/helper/html"
],
"css": [
"./dist/types/helper/css"
],
"jsx": [
"./dist/types/jsx"
],
"jsx/jsx-runtime": [
"./dist/types/jsx/jsx-runtime.d.ts"
],
"jsx/jsx-dev-runtime": [
"./dist/types/jsx/jsx-dev-runtime.d.ts"
],
"jsx/streaming": [
"./dist/types/jsx/streaming.d.ts"
],
"jsx-renderer": [
"./dist/types/middleware/jsx-renderer"
],
"jsx/dom": [
"./dist/types/jsx/dom"
],
"jsx/dom/client": [
"./dist/types/jsx/dom/client.d.ts"
],
"jsx/dom/css": [
"./dist/types/jsx/dom/css.d.ts"
],
"jsx/dom/server": [
"./dist/types/jsx/dom/server.d.ts"
],
"jwt": [
"./dist/types/middleware/jwt"
],
"timeout": [
"./dist/types/middleware/timeout"
],
"timing": [
"./dist/types/middleware/timing"
],
"logger": [
"./dist/types/middleware/logger"
],
"method-override": [
"./dist/types/middleware/method-override"
],
"powered-by": [
"./dist/types/middleware/powered-by"
],
"pretty-json": [
"./dist/types/middleware/pretty-json"
],
"request-id": [
"./dist/types/middleware/request-id"
],
"language": [
"./dist/types/middleware/language"
],
"streaming": [
"./dist/types/helper/streaming"
],
"ssg": [
"./dist/types/helper/ssg"
],
"secure-headers": [
"./dist/types/middleware/secure-headers"
],
"combine": [
"./dist/types/middleware/combine"
],
"validator": [
"./dist/types/validator/index.d.ts"
],
"router": [
"./dist/types/router.d.ts"
],
"router/reg-exp-router": [
"./dist/types/router/reg-exp-router/router.d.ts"
],
"router/smart-router": [
"./dist/types/router/smart-router/router.d.ts"
],
"router/trie-router": [
"./dist/types/router/trie-router/router.d.ts"
],
"router/pattern-router": [
"./dist/types/router/pattern-router/router.d.ts"
],
"router/linear-router": [
"./dist/types/router/linear-router/router.d.ts"
],
"utils/jwt": [
"./dist/types/utils/jwt/index.d.ts"
],
"utils/*": [
"./dist/types/utils/*"
],
"client": [
"./dist/types/client/index.d.ts"
],
"adapter": [
"./dist/types/helper/adapter/index.d.ts"
],
"factory": [
"./dist/types/helper/factory/index.d.ts"
],
"serve-static": [
"./dist/types/middleware/serve-static"
],
"cloudflare-workers": [
"./dist/types/adapter/cloudflare-workers"
],
"cloudflare-pages": [
"./dist/types/adapter/cloudflare-pages"
],
"deno": [
"./dist/types/adapter/deno"
],
"bun": [
"./dist/types/adapter/bun"
],
"nextjs": [
"./dist/types/adapter/nextjs"
],
"aws-lambda": [
"./dist/types/adapter/aws-lambda"
],
"vercel": [
"./dist/types/adapter/vercel"
],
"lambda-edge": [
"./dist/types/adapter/lambda-edge"
],
"service-worker": [
"./dist/types/adapter/service-worker"
],
"testing": [
"./dist/types/helper/testing"
],
"dev": [
"./dist/types/helper/dev"
],
"ws": [
"./dist/types/helper/websocket"
],
"conninfo": [
"./dist/types/helper/conninfo"
],
"proxy": [
"./dist/types/helper/proxy"
]
}
},
"author": "Yusuke Wada <yusuke@kamawada.com> (https://github.com/yusukebe)",
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/honojs/hono.git"
},
"publishConfig": {
"registry": "https://registry.npmjs.org"
},
"homepage": "https://hono.dev",
"keywords": [
"hono",
"web",
"app",
"http",
"application",
"framework",
"router",
"cloudflare",
"workers",
"fastly",
"compute",
"deno",
"bun",
"lambda",
"nodejs"
],
"devDependencies": {
"@hono/eslint-config": "^2.1.0",
"@hono/node-server": "^1.13.5",
"@types/glob": "^9.0.0",
"@types/jsdom": "^21.1.7",
"@types/node": "^24.3.0",
"@types/ws": "^8.18.1",
"@typescript/native-preview": "7.0.0-dev.20260210.1",
"@vitest/coverage-v8": "^3.2.4",
"arg": "^5.0.2",
"bun-types": "^1.2.20",
"editorconfig-checker": "6.1.1",
"esbuild": "^0.27.1",
"eslint": "^9.39.3",
"glob": "^11.0.0",
"jsdom": "22.1.0",
"msw": "^2.6.0",
"np": "10.2.0",
"oxc-parser": "^0.96.0",
"pkg-pr-new": "^0.0.53",
"prettier": "3.7.4",
"publint": "0.3.15",
"typescript": "^5.9.2",
"undici": "^6.21.3",
"vite-plugin-fastly-js-compute": "^0.4.2",
"vitest": "^3.2.4",
"wrangler": "4.12.0",
"ws": "^8.18.0",
"zod": "^3.23.8"
},
"packageManager": "bun@1.2.20",
"engines": {
"node": ">=16.9.0"
}
}
================================================
FILE: perf-measures/.octocov.consolidated.perf-measures.main.yml
================================================
locale: 'en'
repository: ${GITHUB_REPOSITORY}/perf-measures
coverage:
if: false
codeToTestRatio:
if: false
testExecutionTime:
if: false
report:
datastores:
- artifact://${GITHUB_REPOSITORY}
summary:
if: true
customMetrics:
bundle-size-check:
key: bundle-size-check
speed-check:
key: speed-check
diagnostics-tsc:
key: diagnostics-tsc
diagnostics-typescript-go:
key: diagnostics-typescript-go
================================================
FILE: perf-measures/.octocov.consolidated.perf-measures.yml
================================================
locale: 'en'
repository: ${GITHUB_REPOSITORY}/perf-measures
coverage:
if: false
codeToTestRatio:
if: false
testExecutionTime:
if: false
diff:
datastores:
- artifact://${GITHUB_REPOSITORY}
comment:
if: is_pull_request
summary:
if: true
customMetrics:
bundle-size-check:
key: bundle-size-check
diagnostics-tsc:
key: diagnostics-tsc
diagnostics-typescript-go:
key: diagnostics-typescript-go
================================================
FILE: perf-measures/bundle-check/.gitignore
================================================
generated
!generated/.gitkeep
size.json
================================================
FILE: perf-measures/bundle-check/scripts/check-bundle-size.ts
================================================
import * as esbuild from 'esbuild'
import * as fs from 'node:fs'
import * as os from 'os'
import * as path from 'path'
async function main() {
const tempDir = os.tmpdir()
const tempFilePath = path.join(tempDir, 'bundle.tmp.js')
try {
await esbuild.build({
entryPoints: ['dist/index.js'],
bundle: true,
minify: true,
format: 'esm' as esbuild.Format,
target: 'es2022',
outfile: tempFilePath,
})
const bundleSize = fs.statSync(tempFilePath).size
const metrics = []
metrics.push({
key: 'bundle-size-b',
name: 'Bundle Size (B)',
value: bundleSize,
unit: 'B',
})
metrics.push({
key: 'bundle-size-kb',
name: 'Bundle Size (KB)',
value: parseFloat((bundleSize / 1024).toFixed(2)),
unit: 'K',
})
const benchmark = {
key: 'bundle-size-check',
name: 'Bundle size check',
metrics,
}
console.log(JSON.stringify(benchmark, null, 2))
} catch (error) {
console.error('Build failed:', error)
} finally {
if (fs.existsSync(tempFilePath)) {
fs.unlinkSync(tempFilePath)
}
}
}
main()
================================================
FILE: perf-measures/type-check/.gitignore
================================================
generated
!generated/.gitkeep
trace
*result.txt
diagnostics.json
================================================
FILE: perf-measures/type-check/client.ts
================================================
import { hc } from '../../src/client'
import type { app } from './generated/app'
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const client = hc<typeof app>('/')
================================================
FILE: perf-measures/type-check/scripts/generate-app.ts
================================================
import { writeFile } from 'node:fs'
import * as path from 'node:path'
const count = 200
const generateRoutes = (count: number) => {
let routes = `import { Hono } from '../../../src'
export const app = new Hono()`
for (let i = 1; i <= count; i++) {
routes += `
.get('/route${i}/:id', (c) => {
return c.json({
ok: true
})
})`
}
return routes
}
const routes = generateRoutes(count)
writeFile(path.join(import.meta.dirname, '../generated/app.ts'), routes, (err) => {
if (err) {
throw err
}
console.log(`${count} routes have been written to app.ts`)
})
================================================
FILE: perf-measures/type-check/scripts/process-results.ts
================================================
import * as readline from 'node:readline'
async function main() {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
terminal: false,
})
const tsImplLabel = process.env['BENCHMARK_TS_IMPL_LABEL']
if (!tsImplLabel) {
throw new Error('BENCHMARK_TS_IMPL_LABEL must be set')
}
const toKebabCase = (str: string): string => {
return str
.replace(/([a-z])([A-Z])/g, '$1-$2')
.replace(/[\s_\/]+/g, '-')
.toLowerCase()
}
const metrics = []
for await (const line of rl) {
if (!line || line.trim() === '') {
continue
}
const [name, value] = line.split(':')
const unitMatch = value?.trim().match(/^(\d+(\.\d+)?)([a-zA-Z]*)$/)
if (unitMatch) {
const [, number, , unit] = unitMatch
metrics.push({
key: toKebabCase(name?.trim()),
name: name?.trim(),
value: parseFloat(number),
unit: unit || undefined,
})
} else {
metrics.push({
key: toKebabCase(name?.trim()),
name: name?.trim(),
value: parseFloat(value?.trim()),
})
}
}
const benchmark = {
key: `diagnostics-${toKebabCase(tsImplLabel)}`,
name: `Compiler Diagnostics (${tsImplLabel})`,
metrics,
}
console.log(JSON.stringify(benchmark, null, 2))
}
main()
================================================
FILE: perf-measures/type-check/scripts/tsconfig.json
================================================
{
"extends": "../../../tsconfig.base.json",
"compilerOptions": {
"module": "esnext",
"noEmit": true
}
}
================================================
FILE: perf-measures/type-check/tsconfig.build.json
================================================
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"noEmit": true
},
"exclude": ["dist", "scripts"],
"references": [
{ "path": "../../tsconfig.build.json" }
]
}
================================================
FILE: runtime-tests/bun/.static/plain.txt
================================================
Bun!!
================================================
FILE: runtime-tests/bun/color.test.ts
================================================
import { expect, test } from 'bun:test'
test('Bun.build compatibility test', async () => {
try {
const result = await Bun.build({
entrypoints: ['./src/utils/color.ts'],
format: 'esm',
minify: true,
external: [],
})
expect(result.success).toBe(true)
expect(result.logs).toHaveLength(0)
expect(result.outputs).toHaveLength(1)
const outputContent = await result.outputs[0].text()
expect(outputContent).toBeDefined()
} catch (error) {
throw new Error(`Bun.build failed: ${error}`)
}
})
================================================
FILE: runtime-tests/bun/index.test.tsx
================================================
import { afterAll, afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import fs from 'fs/promises'
import path from 'path'
import { stream, streamSSE } from '../..//src/helper/streaming'
import { serveStatic, toSSG } from '../../src/adapter/bun'
import { createBunWebSocket } from '../../src/adapter/bun/websocket'
import type { BunWebSocketData } from '../../src/adapter/bun/websocket'
import { Context } from '../../src/context'
import { env, getRuntimeKey } from '../../src/helper/adapter'
import type { WSMessageReceive } from '../../src/helper/websocket'
import { Hono } from '../../src/index'
import type { PropsWithChildren } from '../../src/jsx'
import { basicAuth } from '../../src/middleware/basic-auth'
import { jwt } from '../../src/middleware/jwt'
declare module '../../src/index' {
interface ContextRenderer {
(content: string | Promise<string>, head: { title: string }): Response | Promise<Response>
}
}
// Test just only minimal patterns.
// Because others are tested well in Cloudflare Workers environment already.
Bun.env.NAME = 'Bun'
describe('Basic', () => {
const app = new Hono()
app.get('/a/:foo', (c) => {
c.header('x-param', c.req.param('foo'))
c.header('x-query', c.req.query('q'))
return c.text('Hello Bun!')
})
it('Should return 200 Response', async () => {
const req = new Request('http://localhost/a/foo?q=bar')
const res = await app.request(req)
expect(res.status).toBe(200)
expect(await res.text()).toBe('Hello Bun!')
expect(res.headers.get('x-param')).toBe('foo')
expect(res.headers.get('x-query')).toBe('bar')
})
it('returns current runtime (bun)', async () => {
expect(getRuntimeKey()).toBe('bun')
})
})
describe('Environment Variables', () => {
it('Should return the environment variable', async () => {
const c = new Context(new Request('http://localhost/'))
const { NAME } = env<{ NAME: string }>(c)
expect(NAME).toBe('Bun')
})
})
describe('Basic Auth Middleware', () => {
const app = new Hono()
const username = 'hono-user-a'
const password = 'hono-password-a'
app.use(
'/auth/*',
basicAuth({
username,
password,
})
)
app.get('/auth/*', () => new Response('auth'))
it('Should not authorize, return 401 Response', async () => {
const req = new Request('http://localhost/auth/a')
const res = await app.request(req)
expect(res.status).toBe(401)
expect(await res.text()).toBe('Unauthorized')
})
it('Should authorize, return 200 Response', async () => {
const credential = 'aG9uby11c2VyLWE6aG9uby1wYXNzd29yZC1h'
const req = new Request('http://localhost/auth/a')
req.headers.set('Authorization', `Basic ${credential}`)
const res = await app.request(req)
expect(res.status).toBe(200)
expect(await res.text()).toBe('auth')
})
})
describe('Serve Static Middleware', () => {
const app = new Hono()
const onNotFound = vi.fn(() => {})
app.all('/favicon.ico', serveStatic({ path: './runtime-tests/bun/favicon.ico' }))
app.all(
'/favicon-notfound.ico',
serveStatic({ path: './runtime-tests/bun/favicon-notfound.ico', onNotFound })
)
app.use('/favicon-notfound.ico', async (c, next) => {
await next()
c.header('X-Custom', 'Bun')
})
app.get(
'/static/*',
serveStatic({
root: './runtime-tests/bun/',
onNotFound,
})
)
app.get(
'/dot-static/*',
serveStatic({
root: './runtime-tests/bun/',
rewriteRequestPath: (path) => path.replace(/^\/dot-static/, './.static'),
})
)
app.all('/static-absolute-root/*', serveStatic({ root: path.dirname(__filename) }))
beforeEach(() => onNotFound.mockClear())
it('Should return static file correctly', async () => {
const res = await app.request(new Request('http://localhost/favicon.ico'))
await res.arrayBuffer()
expect(res.status).toBe(200)
expect(res.headers.get('Content-Type')).toBe('image/x-icon')
})
it('Should return 404 response', async () => {
const res = await app.request(new Request('http://localhost/favicon-notfound.ico'))
expect(res.status).toBe(404)
expect(res.headers.get('X-Custom')).toBe('Bun')
expect(onNotFound).toHaveBeenCalledWith(
process.platform === 'win32'
? 'runtime-tests\\bun\\favicon-notfound.ico'
: 'runtime-tests/bun/favicon-notfound.ico',
expect.anything()
)
})
it('Should return 200 response - /static/plain.txt', async () => {
const res = await app.request(new Request('http://localhost/static/plain.txt'))
expect(res.status).toBe(200)
expect(await res.text()).toMatch(/^Bun!(\r?\n)?$/)
expect(onNotFound).not.toHaveBeenCalled()
})
it('Should return 200 response - /static/download', async () => {
const res = await app.request(new Request('http://localhost/static/download'))
expect(res.status).toBe(200)
expect(await res.text()).toMatch(/^download(\r?\n)?$/)
expect(onNotFound).not.toHaveBeenCalled()
})
it('Should return 200 response - /dot-static/plain.txt', async () => {
const res = await app.request(new Request('http://localhost/dot-static/plain.txt'))
expect(res.status).toBe(200)
expect(await res.text()).toMatch(/^Bun!!(\r?\n)?$/)
})
it('Should return 200 response - /static/helloworld', async () => {
const res = await app.request('http://localhost/static/helloworld')
expect(res.status).toBe(200)
expect(await res.text()).toMatch(/Hi\r?\n/)
})
it('Should return 200 response - /static/hello.world', async () => {
const res = await app.request('http://localhost/static/hello.world')
expect(res.status).toBe(200)
expect(await res.text()).toMatch(/Hi\r?\n/)
})
it('Should return 200 response - /static-absolute-root/plain.txt', async () => {
const res = await app.request('http://localhost/static-absolute-root/plain.txt')
expect(res.status).toBe(200)
expect(await res.text()).toMatch(/^Bun!(\r?\n)?$/)
expect(onNotFound).not.toHaveBeenCalled()
})
})
// Bun support WebCrypto since v0.2.2
// So, JWT middleware works well.
describe('JWT Auth Middleware', () => {
const app = new Hono()
app.use('/jwt/*', jwt({ secret: 'a-secret', alg: 'HS256' }))
app.get('/jwt/a', (c) => c.text('auth'))
it('Should not authorize, return 401 Response', async () => {
const req = new Request('http://localhost/jwt/a')
const res = await app.request(req)
expect(res.status).toBe(401)
expect(await res.text()).toBe('Unauthorized')
})
it('Should authorize, return 200 Response', async () => {
const credential =
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJtZXNzYWdlIjoiaGVsbG8gd29ybGQifQ.B54pAqIiLbu170tGQ1rY06Twv__0qSHTA0ioQPIOvFE'
const req = new Request('http://localhost/jwt/a')
req.headers.set('Authorization', `Bearer ${credential}`)
const res = await app.request(req)
expect(res.status).toBe(200)
expect(await res.text()).toBe('auth')
})
})
// To enable JSX middleware,
// set "jsxImportSource": "hono/jsx" in the tsconfig.json
describe('JSX Middleware', () => {
const app = new Hono()
const Layout = (props: PropsWithChildren) => {
return <html>{props.children}</html>
}
app.get('/', (c) => {
return c.html(<h1>Hello</h1>)
})
app.get('/nest', (c) => {
return c.html(
<h1>
<a href='/top'>Hello</a>
</h1>
)
})
app.get('/layout', (c) => {
return c.html(
<Layout>
<p>hello</p>
</Layout>
)
})
it('Should return rendered HTML', async () => {
const res = await app.request(new Request('http://localhost/'))
expect(res.status).toBe(200)
expect(res.headers.get('Content-Type')).toBe('text/html; charset=UTF-8')
expect(await res.text()).toBe('<h1>Hello</h1>')
})
it('Should return rendered HTML with nest', async () => {
const res = await app.request(new Request('http://localhost/nest'))
expect(res.status).toBe(200)
expect(res.headers.get('Content-Type')).toBe('text/html; charset=UTF-8')
expect(await res.text()).toBe('<h1><a href="/top">Hello</a></h1>')
})
it('Should return rendered HTML with Layout', async () => {
const res = await app.request(new Request('http://localhost/layout'))
expect(res.status).toBe(200)
expect(res.headers.get('Content-Type')).toBe('text/html; charset=UTF-8')
expect(await res.text()).toBe('<html><p>hello</p></html>')
})
})
describe('toSSG function', () => {
let app: Hono
beforeEach(() => {
app = new Hono()
app.get('/', (c) => c.text('Hello, World!'))
app.get('/about', (c) => c.text('About Page'))
app.get('/about/some', (c) => c.text('About Page 2tier'))
app.post('/about/some/thing', (c) => c.text('About Page 3tier'))
app.get('/bravo', (c) => c.html('Bravo Page'))
app.get('/Charlie', async (c, next) => {
c.setRenderer((content, head) => {
return c.html(
<html>
<head>
<title>{head.title || ''}</title>
</head>
<body>
<p>{content}</p>
</body>
</html>
)
})
await next()
})
app.get('/Charlie', (c) => {
return c.render('Hello!', { title: 'Charlies Page' })
})
})
it('Should correctly generate static HTML files for Hono routes', async () => {
const result = await toSSG(app, { dir: './static' })
expect(result.success).toBeTruthy()
expect(result.error).toBeUndefined()
expect(result.files).toBeDefined()
afterAll(async () => {
await deleteDirectory('./static')
})
})
})
describe('WebSockets Helper', () => {
const app = new Hono()
const { websocket, upgradeWebSocket } = createBunWebSocket()
it('Should websockets is working', async () => {
const receivedMessagePromise = new Promise<WSMessageReceive>((resolve) =>
app.get(
'/ws',
upgradeWebSocket(() => ({
onMessage(evt) {
resolve(evt.data)
},
}))
)
)
const upgradedData = await new Promise<BunWebSocketData>((resolve) =>
app.fetch(new Request('http://localhost/ws'), {
upgrade: (_req: Request, data: { data: BunWebSocketData }) => {
resolve(data.data)
},
})
)
const message = Math.random().toString()
websocket.message(
{
close: () => undefined,
readyState: 3,
data: upgradedData,
send: () => undefined,
},
message
)
const receivedMessage = await receivedMessagePromise
expect(receivedMessage).toBe(message)
})
})
async function deleteDirectory(dirPath: string) {
if (
await fs
.stat(dirPath)
.then((stat) => stat.isDirectory())
.catch(() => false)
) {
for (const entry of await fs.readdir(dirPath)) {
const entryPath = path.join(dirPath, entry)
await deleteDirectory(entryPath)
}
await fs.rmdir(dirPath)
} else {
await fs.unlink(dirPath)
}
}
describe('streaming', () => {
const app = new Hono()
let server: ReturnType<typeof Bun.serve>
let aborted = false
app.get('/stream', (c) => {
return stream(c, async (stream) => {
stream.onAbort(() => {
aborted = true
})
return new Promise<void>((resolve) => {
stream.onAbort(resolve)
})
})
})
app.get('/streamHello', (c) => {
return stream(c, async (stream) => {
stream.onAbort(() => {
aborted = true
})
await stream.write('Hello')
})
})
app.get('/streamSSE', (c) => {
return streamSSE(c, async (stream) => {
stream.onAbort(() => {
aborted = true
})
return new Promise<void>((resolve) => {
stream.onAbort(resolve)
})
})
})
app.get('/streamSSEHello', (c) => {
return streamSSE(c, async (stream) => {
stream.onAbort(() => {
aborted = true
})
await stream.write('Hello')
})
})
beforeEach(() => {
aborted = false
server = Bun.serve({ port: 0, fetch: app.fetch })
})
afterEach(() => {
server.stop()
})
describe('stream', () => {
it('Should call onAbort', async () => {
const ac = new AbortController()
const req = new Request(`http://localhost:${server.port}/stream`, {
signal: ac.signal,
})
expect(aborted).toBe(false)
const res = fetch(req).catch(() => {})
await new Promise((resolve) => setTimeout(resolve, 10))
ac.abort()
await res
while (!aborted) {
await new Promise((resolve) => setTimeout(resolve))
}
expect(aborted).toBe(true)
})
it('Should not be called onAbort if already closed', async () => {
expect(aborted).toBe(false)
const res = await fetch(`http://localhost:${server.port}/streamHello`)
expect(await res.text()).toBe('Hello')
expect(aborted).toBe(false)
})
})
describe('streamSSE', () => {
it('Should call onAbort', async () => {
const ac = new AbortController()
const req = new Request(`http://localhost:${server.port}/streamSSE`, {
signal: ac.signal,
})
const res = fetch(req).catch(() => {})
await new Promise((resolve) => setTimeout(resolve, 10))
ac.abort()
await res
while (!aborted) {
await new Promise((resolve) => setTimeout(resolve))
}
expect(aborted).toBe(true)
})
it('Should not be called onAbort if already closed', async () => {
expect(aborted).toBe(false)
const res = await fetch(`http://localhost:${server.port}/streamSSEHello`)
expect(await res.text()).toBe('Hello')
expect(aborted).toBe(false)
})
})
})
describe('Buffers', () => {
const app = new Hono().get('/', async (c) => {
return c.body(Buffer.from('hello'))
})
it('should allow returning buffers', async () => {
const res = await app.request(new Request('http://localhost/'))
expect(res.status).toBe(200)
expect(await res.text()).toBe('hello')
})
})
================================================
FILE: runtime-tests/bun/static/download
================================================
download
================================================
FILE: runtime-tests/bun/static/hello.world/index.html
================================================
Hi
================================================
FILE: runtime-tests/bun/static/helloworld/index.html
================================================
Hi
================================================
FILE: runtime-tests/bun/static/plain.txt
================================================
Bun!
================================================
FILE: runtime-tests/bun/static-absolute-root/plain.txt
================================================
Bun!
================================================
FILE: runtime-tests/bun/tsconfig.json
================================================
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "hono/jsx",
"noEmit": true,
"types": ["bun-types"]
},
"references": [
{ "path": "../../tsconfig.build.json" }
]
}
================================================
FILE: runtime-tests/deno/.static/plain.txt
================================================
Deno!!
================================================
FILE: runtime-tests/deno/.vscode/settings.json
================================================
{
"eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact"],
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
},
"deno.enable": true
}
================================================
FILE: runtime-tests/deno/deno.json
================================================
{
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "hono/jsx",
"lib": ["deno.ns", "dom", "dom.iterable"]
},
"unstable": ["sloppy-imports"],
"imports": {
"@std/assert": "jsr:@std/assert@^1.0.3",
"@std/path": "jsr:@std/path@^1.0.3",
"@std/testing": "jsr:@std/testing@^1.0.1",
"hono/jsx/jsx-runtime": "../../src/jsx/jsx-runtime.ts"
}
}
================================================
FILE: runtime-tests/deno/hono.test.ts
================================================
import { assertEquals } from '@std/assert'
import { Context } from '../../src/context.ts'
import { env, getRuntimeKey } from '../../src/helper/adapter/index.ts'
import { Hono } from '../../src/hono.ts'
// Test just only minimal patterns.
// Because others are tested well in Cloudflare Workers environment already.
Deno.env.set('NAME', 'Deno')
Deno.test('Hello World', async () => {
const app = new Hono()
app.get('/:foo', (c) => {
c.header('x-param', c.req.param('foo'))
c.header('x-query', c.req.query('q') || '')
return c.text('Hello Deno!')
})
const res = await app.request('/foo?q=bar')
assertEquals(res.status, 200)
assertEquals(await res.text(), 'Hello Deno!')
assertEquals(res.headers.get('x-param'), 'foo')
assertEquals(res.headers.get('x-query'), 'bar')
})
Deno.test('runtime', () => {
assertEquals(getRuntimeKey(), 'deno')
})
Deno.test('environment variables', () => {
const c = new Context(new Request('http://localhost/'))
const { NAME } = env<{ NAME: string }>(c)
assertEquals(NAME, 'Deno')
})
================================================
FILE: runtime-tests/deno/middleware.test.tsx
================================================
import { assertEquals, assertMatch } from '@std/assert'
import { dirname, fromFileUrl } from '@std/path'
import { assertSpyCall, assertSpyCalls, spy } from '@std/testing/mock'
import { serveStatic } from '../../src/adapter/deno/index.ts'
import { Hono } from '../../src/hono.ts'
import { basicAuth } from '../../src/middleware/basic-auth/index.ts'
import { jwt } from '../../src/middleware/jwt/index.ts'
// Test just only minimal patterns.
// Because others are already tested well in Cloudflare Workers environment.
Deno.test('Basic Auth Middleware', async () => {
const app = new Hono()
const username = 'hono'
const password = 'ahotproject'
app.use(
'/auth/*',
basicAuth({
username,
password,
})
)
app.get('/auth/*', () => new Response('auth'))
const res = await app.request('http://localhost/auth/a')
assertEquals(res.status, 401)
assertEquals(await res.text(), 'Unauthorized')
const credential = 'aG9ubzphaG90cHJvamVjdA=='
const req = new Request('http://localhost/auth/a')
req.headers.set('Authorization', `Basic ${credential}`)
const resOK = await app.request(req)
assertEquals(resOK.status, 200)
assertEquals(await resOK.text(), 'auth')
const invalidCredential = 'G9ubzphY29vbHByb2plY3Q='
const req2 = new Request('http://localhost/auth/a')
req2.headers.set('Authorization', `Basic ${invalidCredential}`)
const resNG = await app.request(req2)
assertEquals(resNG.status, 401)
assertEquals(await resNG.text(), 'Unauthorized')
})
Deno.test('JSX middleware', async () => {
const app = new Hono()
app.get('/', (c) => {
return c.html(<h1>Hello</h1>)
})
const res = await app.request('http://localhost/')
assertEquals(res.status, 200)
assertEquals(res.headers.get('Content-Type'), 'text/html; charset=UTF-8')
assertEquals(await res.text(), '<h1>Hello</h1>')
// Fragment
const template = (
<>
<p>1</p>
<p>2</p>
</>
)
assertEquals(template.toString(), '<p>1</p><p>2</p>')
})
Deno.test('Serve Static middleware', async () => {
const app = new Hono()
const onNotFound = spy(() => {})
app.all('/favicon.ico', serveStatic({ path: './runtime-tests/deno/favicon.ico' }))
app.all(
'/favicon-notfound.ico',
serveStatic({ path: './runtime-tests/deno/favicon-notfound.ico', onNotFound })
)
app.use('/favicon-notfound.ico', async (c, next) => {
await next()
c.header('X-Custom', 'Deno')
})
app.get(
'/static/*',
serveStatic({
root: './runtime-tests/deno',
onNotFound,
})
)
app.get(
'/dot-static/*',
serveStatic({
root: './runtime-tests/deno',
rewriteRequestPath: (path) => path.replace(/^\/dot-static/, './.static'),
})
)
app.get('/static-absolute-root/*', serveStatic({ root: dirname(fromFileUrl(import.meta.url)) }))
let res = await app.request('http://localhost/favicon.ico')
assertEquals(res.status, 200)
assertEquals(res.headers.get('Content-Type'), 'image/x-icon')
await res.body?.cancel()
res = await app.request('http://localhost/favicon-notfound.ico')
assertEquals(res.status, 404)
assertMatch(res.headers.get('Content-Type') || '', /^text\/plain/)
assertEquals(res.headers.get('X-Custom'), 'Deno')
assertSpyCall(onNotFound, 0)
res = await app.request('http://localhost/static/plain.txt')
assertEquals(res.status, 200)
assertMatch(await res.text(), /^Deno!(\r?\n)?$/)
res = await app.request('http://localhost/static/download')
assertEquals(res.status, 200)
assertMatch(await res.text(), /^download(\r?\n)?$/)
res = await app.request('http://localhost/dot-static/plain.txt')
assertEquals(res.status, 200)
assertMatch(await res.text(), /^Deno!!(\r?\n)?$/)
assertSpyCalls(onNotFound, 1)
res = await app.fetch({
method: 'GET',
url: 'http://localhost/static/%2e%2e/static/plain.txt',
} as Request)
assertEquals(res.status, 404)
assertEquals(await res.text(), '404 Not Found')
res = await app.request('http://localhost/static/helloworld')
assertEquals(res.status, 200)
assertEquals(await res.text(), 'Hi\n')
res = await app.request('http://localhost/static/hello.world')
assertEquals(res.status, 200)
assertEquals(await res.text(), 'Hi\n')
res = await app.request('http://localhost/static-absolute-root/plain.txt')
assertEquals(res.status, 200)
assertMatch(await res.text(), /^Deno!(\r?\n)?$/)
res = await app.request('http://localhost/static')
assertEquals(res.status, 404)
assertEquals(await res.text(), '404 Not Found')
res = await app.request('http://localhost/static/dir')
assertEquals(res.status, 404)
assertEquals(await res.text(), '404 Not Found')
res = await app.request('http://localhost/static/helloworld/nested')
assertEquals(res.status, 404)
assertEquals(await res.text(), '404 Not Found')
res = await app.request('http://localhost/static/helloworld/../')
assertEquals(res.status, 404)
assertEquals(await res.text(), '404 Not Found')
})
Deno.test('JWT Authentication middleware', async () => {
const app = new Hono<{ Variables: { 'x-foo': string } }>()
app.use('/*', async (c, next) => {
await next()
c.header('x-foo', c.get('x-foo') || '')
})
app.use('/auth/*', jwt({ secret: 'a-secret', alg: 'HS256' }))
app.get('/auth/*', (c) => {
c.set('x-foo', 'bar')
return new Response('auth')
})
const req = new Request('http://localhost/auth/a')
const res = await app.request(req)
assertEquals(res.status, 401)
assertEquals(await res.text(), 'Unauthorized')
assertEquals(res.headers.get('x-foo'), '')
const credential =
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJtZXNzYWdlIjoiaGVsbG8gd29ybGQifQ.B54pAqIiLbu170tGQ1rY06Twv__0qSHTA0ioQPIOvFE'
const reqOK = new Request('http://localhost/auth/a')
reqOK.headers.set('Authorization', `Bearer ${credential}`)
const resOK = await app.request(reqOK)
assertEquals(resOK.status, 200)
assertEquals(await resOK.text(), 'auth')
assertEquals(resOK.headers.get('x-foo'), 'bar')
})
================================================
FILE: runtime-tests/deno/ssg.test.tsx
================================================
import { assertEquals } from '@std/assert'
import { toSSG } from '../../src/adapter/deno/ssg.ts'
import { Hono } from '../../src/hono.ts'
Deno.test('toSSG function', async () => {
const app = new Hono()
app.get('/', (c) => c.text('Hello, World!'))
app.get('/about', (c) => c.text('About Page'))
app.get('/about/some', (c) => c.text('About Page 2tier'))
app.post('/about/some/thing', (c) => c.text('About Page 3tier'))
app.get('/bravo', (c) => c.html('Bravo Page'))
app.get('/Charlie', async (c, next) => {
c.setRenderer((content) => {
return c.html(
<html>
<body>
<p>{content}</p>
</body>
</html>
)
})
await next()
})
app.get('/Charlie', (c) => {
return c.render('Hello!')
})
const result = await toSSG(app, { dir: './ssg-static' })
assertEquals(result.success, true)
assertEquals(result.error, undefined)
assertEquals(result.files !== undefined, true)
await deleteDirectory('./ssg-static')
})
async function deleteDirectory(dirPath: string): Promise<void> {
try {
const stat = await Deno.stat(dirPath)
if (stat.isDirectory) {
for await (const dirEntry of Deno.readDir(dirPath)) {
const entryPath = `${dirPath}/${dirEntry.name}`
await deleteDirectory(entryPath)
}
await Deno.remove(dirPath)
} else {
await Deno.remove(dirPath)
}
} catch (error) {
console.error(`Error deleting directory: ${error}`)
}
}
================================================
FILE: runtime-tests/deno/static/download
================================================
download
================================================
FILE: runtime-tests/deno/static/hello.world/index.html
================================================
Hi
================================================
FILE: runtime-tests/deno/static/helloworld/index.html
================================================
Hi
================================================
FILE: runtime-tests/deno/static/plain.txt
================================================
Deno!
================================================
FILE: runtime-tests/deno/static-absolute-root/plain.txt
================================================
Deno!
================================================
FILE: runtime-tests/deno/stream.test.ts
================================================
import { assertEquals } from '@std/assert'
import { stream, streamSSE } from '../../src/helper/streaming/index.ts'
import { Hono } from '../../src/hono.ts'
Deno.test('Should call onAbort via stream', async () => {
const app = new Hono()
let streamStarted = false
let aborted = false
app.get('/stream', (c) => {
return stream(c, (stream) => {
streamStarted = true
stream.onAbort(() => {
aborted = true
})
return new Promise<void>((resolve) => {
stream.onAbort(resolve)
})
})
})
const server = Deno.serve({ port: 0 }, app.fetch)
const ac = new AbortController()
const req = new Request(`http://localhost:${server.addr.port}/stream`, {
signal: ac.signal,
})
const res = fetch(req).catch(() => {})
assertEquals(aborted, false)
while (!streamStarted) {
await new Promise((resolve) => setTimeout(resolve, 10))
}
ac.abort()
await res
while (!aborted) {
await new Promise((resolve) => setTimeout(resolve))
}
assertEquals(aborted, true)
await server.shutdown()
})
Deno.test('Should not call onAbort via stream if already closed', async () => {
const app = new Hono()
let aborted = false
app.get('/stream', (c) => {
return stream(c, async (stream) => {
stream.onAbort(() => {
aborted = true
})
await stream.write('Hello')
})
})
const server = Deno.serve({ port: 0 }, app.fetch)
assertEquals(aborted, false)
const res = await fetch(`http://localhost:${server.addr.port}/stream`)
assertEquals(await res.text(), 'Hello')
assertEquals(aborted, false)
await server.shutdown()
})
Deno.test('Should call onAbort via streamSSE', async () => {
const app = new Hono()
let streamStarted = false
let aborted = false
app.get('/stream', (c) => {
return streamSSE(c, (stream) => {
streamStarted = true
stream.onAbort(() => {
aborted = true
})
return new Promise<void>((resolve) => {
stream.onAbort(resolve)
})
})
})
const server = Deno.serve({ port: 0 }, app.fetch)
const ac = new AbortController()
const req = new Request(`http://localhost:${server.addr.port}/stream`, {
signal: ac.signal,
})
const res = fetch(req).catch(() => {})
assertEquals(aborted, false)
while (!streamStarted) {
await new Promise((resolve) => setTimeout(resolve, 10))
}
ac.abort()
await res
while (!aborted) {
await new Promise((resolve) => setTimeout(resolve))
}
assertEquals(aborted, true)
await server.shutdown()
})
Deno.test('Should not call onAbort via streamSSE if already closed', async () => {
const app = new Hono()
let aborted = false
app.get('/stream', (c) => {
return streamSSE(c, async (stream) => {
stream.onAbort(() => {
aborted = true
})
await stream.write('Hello')
})
})
const server = Deno.serve({ port: 0 }, app.fetch)
assertEquals(aborted, false)
const res = await fetch(`http://localhost:${server.addr.port}/stream`)
assertEquals(await res.text(), 'Hello')
assertEquals(aborted, false)
await server.shutdown()
})
================================================
FILE: runtime-tests/deno-jsx/deno.precompile.json
================================================
{
"compilerOptions": {
"jsx": "precompile",
"jsxImportSource": "hono/jsx",
"lib": ["deno.ns", "dom", "dom.iterable"]
},
"unstable": ["sloppy-imports"],
"imports": {
"@std/assert": "jsr:@std/assert@^1.0.3",
"hono/jsx/jsx-runtime": "../../src/jsx/jsx-runtime.ts"
}
}
================================================
FILE: runtime-tests/deno-jsx/deno.react-jsx.json
================================================
{
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "hono/jsx",
"lib": ["deno.ns", "dom", "dom.iterable"]
},
"unstable": ["sloppy-imports"],
"imports": {
"@std/assert": "jsr:@std/assert@^1.0.3",
"hono/jsx/jsx-runtime": "../../src/jsx/jsx-runtime.ts"
}
}
================================================
FILE: runtime-tests/deno-jsx/jsx.test.tsx
================================================
/** @jsxImportSource ../../src/jsx */
import { assertEquals } from '@std/assert'
import { Style, css } from '../../src/helper/css/index.ts'
import { Suspense, renderToReadableStream } from '../../src/jsx/streaming.ts'
import type { HtmlEscapedString } from '../../src/utils/html.ts'
import { HtmlEscapedCallbackPhase, resolveCallback } from '../../src/utils/html.ts'
Deno.test('JSX', () => {
const Component = ({ name }: { name: string }) => <span>{name}</span>
const html = (
<div>
<h1 id={'<Hello>'}>
<Component name={'<Hono>'} />
</h1>
</div>
)
assertEquals(html.toString(), '<div><h1 id="<Hello>"><span><Hono></span></h1></div>')
})
Deno.test('JSX: Fragment', () => {
const fragment = (
<>
<p>1</p>
<p>2</p>
</>
)
assertEquals(fragment.toString(), '<p>1</p><p>2</p>')
})
Deno.test('JSX: Empty Fragment', () => {
const Component = () => <></>
const html = <Component />
assertEquals(html.toString(), '')
})
Deno.test('JSX: Async Component', async () => {
const Component = async ({ name }: { name: string }) =>
new Promise<HtmlEscapedString>((resolve) => setTimeout(() => resolve(<span>{name}</span>), 10))
const stream = renderToReadableStream(
<div>
<Component name={'<Hono>'} />
</div>
)
const chunks: string[] = []
const textDecoder = new TextDecoder()
// eslint-disable-next-line @typescript-eslint/no-explicit-any
for await (const chunk of stream as any) {
chunks.push(textDecoder.decode(chunk))
}
assertEquals(chunks.join(''), '<div><span><Hono></span></div>')
})
Deno.test('JSX: Suspense', async () => {
const Content = () => {
const content = new Promise<HtmlEscapedString>((resolve) =>
setTimeout(() => resolve(<h1>Hello</h1>), 10)
)
return content
}
const stream = renderToReadableStream(
<Suspense fallback={<p>Loading...</p>}>
<Content />
</Suspense>
)
const chunks: string[] = []
const textDecoder = new TextDecoder()
// eslint-disable-next-line @typescript-eslint/no-explicit-any
for await (const chunk of stream as any) {
chunks.push(textDecoder.decode(chunk))
}
assertEquals(chunks, [
'<template id="H:0"></template><p>Loading...</p><!--/$-->',
`<template data-hono-target="H:0"><h1>Hello</h1></template><script>
((d,c,n) => {
c=d.currentScript.previousSibling
d=d.getElementById('H:0')
if(!d)return
do{n=d.nextSibling;n.remove()}while(n.nodeType!=8||n.nodeValue!='/$')
d.replaceWith(c.content)
})(document)
</script>`,
])
})
Deno.test('JSX: css', async () => {
const className = css`
color: red;
`
const html = (
<html>
<head>
<Style />
</head>
<body>
<div class={className}></div>
</body>
</html>
)
const awaitedHtml = await html
const htmlEscapedString = 'callbacks' in awaitedHtml ? awaitedHtml : await awaitedHtml.toString()
assertEquals(
await resolveCallback(htmlEscapedString, HtmlEscapedCallbackPhase.Stringify, false, {}),
'<html><head><style id="hono-css">.css-3142110215{color:red}</style></head><body><div class="css-3142110215"></div></body></html>'
)
})
Deno.test('JSX: css with CSP nonce', async () => {
const className = css`
color: red;
`
const html = (
<html>
<head>
<Style nonce='1234' />
</head>
<body>
<div class={className}></div>
</body>
</html>
)
const awaitedHtml = await html
const htmlEscapedString = 'callbacks' in awaitedHtml ? awaitedHtml : await awaitedHtml.toString()
assertEquals(
await resolveCallback(htmlEscapedString, HtmlEscapedCallbackPhase.Stringify, false, {}),
'<html><head><style id="hono-css" nonce="1234">.css-3142110215{color:red}</style></head><body><div class="css-3142110215"></div></body></html>'
)
})
Deno.test('JSX: normalize key', async () => {
const className = <div className='foo'></div>
const htmlFor = <div htmlFor='foo'></div>
const crossOrigin = <div crossOrigin='foo'></div>
const httpEquiv = <div httpEquiv='foo'></div>
const itemProp = <div itemProp='foo'></div>
const fetchPriority = <div fetchPriority='foo'></div>
const noModule = <div noModule='foo'></div>
const formAction = <div formAction='foo'></div>
assertEquals(className.toString(), '<div class="foo"></div>')
assertEquals(htmlFor.toString(), '<div for="foo"></div>')
assertEquals(crossOrigin.toString(), '<div crossorigin="foo"></div>')
assertEquals(httpEquiv.toString(), '<div http-equiv="foo"></div>')
assertEquals(itemProp.toString(), '<div itemprop="foo"></div>')
assertEquals(fetchPriority.toString(), '<div fetchpriority="foo"></div>')
assertEquals(noModule.toString(), '<div nomodule="foo"></div>')
assertEquals(formAction.toString(), '<div formaction="foo"></div>')
})
Deno.test('JSX: null or undefined', async () => {
const nullHtml = <div className={null}></div>
const undefinedHtml = <div className={undefined}></div>
// react-jsx : <div>
// precompile : <div > // Extra whitespace is allowed because it is a specification.
assertEquals(nullHtml.toString().replace(/\s+/g, ''), '<div></div>')
assertEquals(undefinedHtml.toString().replace(/\s+/g, ''), '<div></div>')
})
Deno.test('JSX: boolean attributes', async () => {
const trueHtml = <div disabled={true}></div>
const falseHtml = <div disabled={false}></div>
// output is different, but semantics as HTML is the same, so both are OK
// react-jsx : <div disabled="">
// precompile : <div disabled>
assertEquals(trueHtml.toString().replace('=""', ''), '<div disabled></div>')
assertEquals(falseHtml.toString(), '<div></div>')
})
Deno.test('JSX: number', async () => {
const html = <div tabindex={1}></div>
assertEquals(html.toString(), '<div tabindex="1"></div>')
})
Deno.test('JSX: style', async () => {
const html = <div style={{ fontSize: '12px', color: null }}></div>
assertEquals(html.toString(), '<div style="font-size:12px"></div>')
})
================================================
FILE: runtime-tests/fastly/index.test.ts
================================================
import { createHash } from 'crypto'
import { getRuntimeKey } from '../../src/helper/adapter'
import { Hono } from '../../src/index'
import { basicAuth } from '../../src/middleware/basic-auth'
import { jwt } from '../../src/middleware/jwt'
declare global {
var __fastlyComputeNodeDefaultCrypto: boolean | undefined
}
beforeAll(() => {
vi.stubGlobal('fastly', true)
vi.stubGlobal('navigator', undefined)
})
afterAll(() => {
vi.unstubAllGlobals()
})
const app = new Hono()
describe('Hello World', () => {
app.get('/', (c) => c.text('Hello! Compute!'))
app.get('/runtime-name', (c) => {
return c.text(getRuntimeKey())
})
it('Should return 200', async () => {
const res = await app.request('http://localhost/')
expect(res.status).toBe(200)
expect(await res.text()).toBe('Hello! Compute!')
})
it('Should return the correct runtime name', async () => {
const res = await app.request('http://localhost/runtime-name')
expect(res.s
gitextract_1kthqqhw/ ├── .devcontainer/ │ ├── Dockerfile │ ├── devcontainer.json │ └── docker-compose.yml ├── .editorconfig ├── .github/ │ ├── FUNDING.yml │ ├── ISSUE_TEMPLATE/ │ │ ├── 1-bug-report.yml │ │ ├── 2-feature-request.yml │ │ └── config.yml │ ├── actions/ │ │ └── perf-measures/ │ │ └── action.yml │ ├── pull_request_template.md │ └── workflows/ │ ├── autofix.yml │ ├── ci.yml │ ├── cr.yml │ ├── no-response.yml │ └── release.yml ├── .gitignore ├── .gitpod.yml ├── .prettierrc ├── .tool-versions ├── .vitest.config/ │ └── setup-vitest.ts ├── .vscode/ │ ├── extensions.json │ └── settings.json ├── LICENSE ├── README.md ├── benchmarks/ │ ├── deno/ │ │ ├── .gitignore │ │ ├── .vscode/ │ │ │ └── settings.json │ │ ├── fast.ts │ │ ├── faster.ts │ │ ├── hono.ts │ │ ├── magalo.ts │ │ ├── oak.ts │ │ └── opine.ts │ ├── handle-event/ │ │ ├── index.js │ │ └── package.json │ ├── http-server/ │ │ ├── .gitignore │ │ ├── README.md │ │ └── benchmark.ts │ ├── jsx/ │ │ ├── package.json │ │ ├── src/ │ │ │ ├── benchmark.ts │ │ │ ├── hono.ts │ │ │ ├── nano.ts │ │ │ ├── page-react.tsx │ │ │ ├── page.tsx │ │ │ ├── preact.ts │ │ │ ├── react-jsx/ │ │ │ │ ├── benchmark.ts │ │ │ │ ├── hono.ts │ │ │ │ ├── nano.ts │ │ │ │ ├── page-hono.tsx │ │ │ │ ├── page-nano.tsx │ │ │ │ ├── page-preact.tsx │ │ │ │ ├── page-react.tsx │ │ │ │ ├── preact.ts │ │ │ │ ├── react.ts │ │ │ │ └── tsconfig.json │ │ │ └── react.ts │ │ └── tsconfig.json │ ├── query-param/ │ │ ├── bun.lockb │ │ ├── package.json │ │ └── src/ │ │ ├── bench.mts │ │ ├── fast-querystring.mts │ │ ├── hono.mts │ │ └── qs.mts │ ├── routers/ │ │ ├── README.md │ │ ├── package.json │ │ ├── src/ │ │ │ ├── bench-includes-init.mts │ │ │ ├── bench.mts │ │ │ ├── express.mts │ │ │ ├── find-my-way.mts │ │ │ ├── hono.mts │ │ │ ├── koa-router.mts │ │ │ ├── koa-tree-router.mts │ │ │ ├── medley-router.mts │ │ │ ├── memoirist.mts │ │ │ ├── radix3.mts │ │ │ ├── rou3.mts │ │ │ ├── tool.mts │ │ │ └── trek-router.mts │ │ └── tsconfig.json │ ├── routers-deno/ │ │ ├── .vscode/ │ │ │ └── settings.json │ │ ├── README.md │ │ ├── deno.json │ │ └── src/ │ │ ├── bench.mts │ │ ├── find-my-way.mts │ │ ├── hono.mts │ │ ├── koa-router.mts │ │ ├── koa-tree-router.mts │ │ ├── medley-router.mts │ │ ├── tool.mts │ │ └── trek-router.mts │ ├── utils/ │ │ ├── .gitignore │ │ ├── package.json │ │ └── src/ │ │ ├── get-path.ts │ │ └── loop.js │ └── webapp/ │ ├── .gitignore │ ├── hono.js │ ├── itty-router.js │ ├── package.json │ └── sunder.js ├── build/ │ ├── build.ts │ ├── remove-private-fields.test.ts │ ├── remove-private-fields.ts │ ├── validate-exports.test.ts │ └── validate-exports.ts ├── bunfig.toml ├── codecov.yml ├── docs/ │ ├── CODE_OF_CONDUCT.md │ ├── CONTRIBUTING.md │ ├── MIGRATION.md │ └── images/ │ ├── hono-logo.pxm │ └── hono-title.pxm ├── eslint.config.mjs ├── jsr.json ├── package.cjs.json ├── package.json ├── perf-measures/ │ ├── .octocov.consolidated.perf-measures.main.yml │ ├── .octocov.consolidated.perf-measures.yml │ ├── bundle-check/ │ │ ├── .gitignore │ │ └── scripts/ │ │ └── check-bundle-size.ts │ └── type-check/ │ ├── .gitignore │ ├── client.ts │ ├── scripts/ │ │ ├── generate-app.ts │ │ ├── process-results.ts │ │ └── tsconfig.json │ └── tsconfig.build.json ├── runtime-tests/ │ ├── bun/ │ │ ├── .static/ │ │ │ └── plain.txt │ │ ├── color.test.ts │ │ ├── index.test.tsx │ │ ├── static/ │ │ │ ├── download │ │ │ ├── hello.world/ │ │ │ │ └── index.html │ │ │ ├── helloworld/ │ │ │ │ └── index.html │ │ │ └── plain.txt │ │ ├── static-absolute-root/ │ │ │ └── plain.txt │ │ └── tsconfig.json │ ├── deno/ │ │ ├── .static/ │ │ │ └── plain.txt │ │ ├── .vscode/ │ │ │ └── settings.json │ │ ├── deno.json │ │ ├── hono.test.ts │ │ ├── middleware.test.tsx │ │ ├── ssg.test.tsx │ │ ├── static/ │ │ │ ├── download │ │ │ ├── hello.world/ │ │ │ │ └── index.html │ │ │ ├── helloworld/ │ │ │ │ └── index.html │ │ │ └── plain.txt │ │ ├── static-absolute-root/ │ │ │ └── plain.txt │ │ └── stream.test.ts │ ├── deno-jsx/ │ │ ├── deno.precompile.json │ │ ├── deno.react-jsx.json │ │ └── jsx.test.tsx │ ├── fastly/ │ │ ├── index.test.ts │ │ ├── tsconfig.json │ │ └── vitest.config.ts │ ├── lambda/ │ │ ├── index.test.ts │ │ ├── mock.ts │ │ ├── stream-mock.ts │ │ ├── stream.test.ts │ │ ├── tsconfig.json │ │ └── vitest.config.ts │ ├── lambda-edge/ │ │ ├── index.test.ts │ │ ├── tsconfig.json │ │ └── vitest.config.ts │ ├── node/ │ │ ├── index.test.ts │ │ ├── tsconfig.json │ │ └── vitest.config.ts │ └── workerd/ │ ├── index.test.ts │ ├── index.ts │ ├── tsconfig.json │ └── vitest.config.ts ├── src/ │ ├── adapter/ │ │ ├── aws-lambda/ │ │ │ ├── conninfo.test.ts │ │ │ ├── conninfo.ts │ │ │ ├── handler.test.ts │ │ │ ├── handler.ts │ │ │ ├── index.ts │ │ │ └── types.ts │ │ ├── bun/ │ │ │ ├── conninfo.test.ts │ │ │ ├── conninfo.ts │ │ │ ├── index.ts │ │ │ ├── serve-static.ts │ │ │ ├── server.test.ts │ │ │ ├── server.ts │ │ │ ├── ssg.ts │ │ │ ├── websocket.test.ts │ │ │ └── websocket.ts │ │ ├── cloudflare-pages/ │ │ │ ├── conninfo.test.ts │ │ │ ├── conninfo.ts │ │ │ ├── handler.test.ts │ │ │ ├── handler.ts │ │ │ └── index.ts │ │ ├── cloudflare-workers/ │ │ │ ├── conninfo.test.ts │ │ │ ├── conninfo.ts │ │ │ ├── index.ts │ │ │ ├── serve-static-module.ts │ │ │ ├── serve-static.test.ts │ │ │ ├── serve-static.ts │ │ │ ├── utils.test.ts │ │ │ ├── utils.ts │ │ │ ├── websocket.test.ts │ │ │ └── websocket.ts │ │ ├── deno/ │ │ │ ├── conninfo.test.ts │ │ │ ├── conninfo.ts │ │ │ ├── deno.d.ts │ │ │ ├── index.ts │ │ │ ├── serve-static.ts │ │ │ ├── ssg.ts │ │ │ ├── websocket.test.ts │ │ │ └── websocket.ts │ │ ├── lambda-edge/ │ │ │ ├── conninfo.test.ts │ │ │ ├── conninfo.ts │ │ │ ├── handler.test.ts │ │ │ ├── handler.ts │ │ │ └── index.ts │ │ ├── netlify/ │ │ │ ├── conninfo.test.ts │ │ │ ├── conninfo.ts │ │ │ ├── handler.ts │ │ │ ├── index.ts │ │ │ └── mod.ts │ │ ├── service-worker/ │ │ │ ├── handler.test.ts │ │ │ ├── handler.ts │ │ │ ├── index.ts │ │ │ └── types.ts │ │ └── vercel/ │ │ ├── conninfo.test.ts │ │ ├── conninfo.ts │ │ ├── handler.test.ts │ │ ├── handler.ts │ │ └── index.ts │ ├── client/ │ │ ├── client.test.ts │ │ ├── client.ts │ │ ├── fetch-result-please.ts │ │ ├── index.ts │ │ ├── types.test.ts │ │ ├── types.ts │ │ ├── utils.test.ts │ │ └── utils.ts │ ├── compose.test.ts │ ├── compose.ts │ ├── context.test.ts │ ├── context.ts │ ├── helper/ │ │ ├── accepts/ │ │ │ ├── accepts.test.ts │ │ │ ├── accepts.ts │ │ │ └── index.ts │ │ ├── adapter/ │ │ │ ├── index.test.ts │ │ │ └── index.ts │ │ ├── conninfo/ │ │ │ ├── index.ts │ │ │ └── types.ts │ │ ├── cookie/ │ │ │ ├── index.test.ts │ │ │ └── index.ts │ │ ├── css/ │ │ │ ├── common.case.test.tsx │ │ │ ├── common.ts │ │ │ ├── index.test.tsx │ │ │ └── index.ts │ │ ├── dev/ │ │ │ ├── index.test.ts │ │ │ └── index.ts │ │ ├── factory/ │ │ │ ├── index.test.ts │ │ │ └── index.ts │ │ ├── html/ │ │ │ ├── index.test.ts │ │ │ └── index.ts │ │ ├── proxy/ │ │ │ ├── index.test.ts │ │ │ └── index.ts │ │ ├── route/ │ │ │ ├── index.test.ts │ │ │ └── index.ts │ │ ├── ssg/ │ │ │ ├── index.ts │ │ │ ├── middleware.ts │ │ │ ├── plugins.test.tsx │ │ │ ├── plugins.ts │ │ │ ├── ssg.test.tsx │ │ │ ├── ssg.ts │ │ │ ├── utils.test.ts │ │ │ └── utils.ts │ │ ├── streaming/ │ │ │ ├── index.ts │ │ │ ├── sse.test.tsx │ │ │ ├── sse.ts │ │ │ ├── stream.test.ts │ │ │ ├── stream.ts │ │ │ ├── text.test.ts │ │ │ ├── text.ts │ │ │ └── utils.ts │ │ ├── testing/ │ │ │ ├── index.test.ts │ │ │ └── index.ts │ │ └── websocket/ │ │ ├── index.test.ts │ │ └── index.ts │ ├── hono-base.ts │ ├── hono.test.ts │ ├── hono.ts │ ├── http-exception.test.ts │ ├── http-exception.ts │ ├── index.ts │ ├── jsx/ │ │ ├── base.test.tsx │ │ ├── base.ts │ │ ├── children.test.ts │ │ ├── children.ts │ │ ├── components.test.tsx │ │ ├── components.ts │ │ ├── constants.ts │ │ ├── context.ts │ │ ├── dom/ │ │ │ ├── client.test.tsx │ │ │ ├── client.ts │ │ │ ├── components.test.tsx │ │ │ ├── components.ts │ │ │ ├── context.test.tsx │ │ │ ├── context.ts │ │ │ ├── css.test.tsx │ │ │ ├── css.ts │ │ │ ├── hooks/ │ │ │ │ ├── index.test.tsx │ │ │ │ └── index.ts │ │ │ ├── index.test.tsx │ │ │ ├── index.ts │ │ │ ├── intrinsic-element/ │ │ │ │ ├── components.test.tsx │ │ │ │ └── components.ts │ │ │ ├── jsx-dev-runtime.ts │ │ │ ├── jsx-runtime.ts │ │ │ ├── render.ts │ │ │ ├── server.test.tsx │ │ │ ├── server.ts │ │ │ └── utils.ts │ │ ├── hooks/ │ │ │ ├── dom.test.tsx │ │ │ ├── index.ts │ │ │ └── string.test.tsx │ │ ├── index.test.tsx │ │ ├── index.ts │ │ ├── intrinsic-element/ │ │ │ ├── common.ts │ │ │ ├── components.test.tsx │ │ │ └── components.ts │ │ ├── intrinsic-elements.ts │ │ ├── jsx-dev-runtime.ts │ │ ├── jsx-runtime.test.tsx │ │ ├── jsx-runtime.ts │ │ ├── streaming.test.tsx │ │ ├── streaming.ts │ │ ├── types.ts │ │ ├── utils.test.ts │ │ └── utils.ts │ ├── middleware/ │ │ ├── basic-auth/ │ │ │ ├── index.test.ts │ │ │ └── index.ts │ │ ├── bearer-auth/ │ │ │ ├── index.test.ts │ │ │ └── index.ts │ │ ├── body-limit/ │ │ │ ├── index.test.ts │ │ │ └── index.ts │ │ ├── cache/ │ │ │ ├── index.test.ts │ │ │ └── index.ts │ │ ├── combine/ │ │ │ ├── index.test.ts │ │ │ └── index.ts │ │ ├── compress/ │ │ │ ├── index.test.ts │ │ │ └── index.ts │ │ ├── context-storage/ │ │ │ ├── index.test.ts │ │ │ └── index.ts │ │ ├── cors/ │ │ │ ├── index.test.ts │ │ │ └── index.ts │ │ ├── csrf/ │ │ │ ├── index.test.ts │ │ │ └── index.ts │ │ ├── etag/ │ │ │ ├── digest.ts │ │ │ ├── index.test.ts │ │ │ └── index.ts │ │ ├── ip-restriction/ │ │ │ ├── index.test.ts │ │ │ └── index.ts │ │ ├── jsx-renderer/ │ │ │ ├── index.test.tsx │ │ │ └── index.ts │ │ ├── jwk/ │ │ │ ├── index.test.ts │ │ │ ├── index.ts │ │ │ ├── jwk.ts │ │ │ └── keys.test.json │ │ ├── jwt/ │ │ │ ├── index.test.ts │ │ │ ├── index.ts │ │ │ └── jwt.ts │ │ ├── language/ │ │ │ ├── index.test.ts │ │ │ ├── index.ts │ │ │ └── language.ts │ │ ├── logger/ │ │ │ ├── index.test.ts │ │ │ └── index.ts │ │ ├── method-override/ │ │ │ ├── index.test.ts │ │ │ └── index.ts │ │ ├── powered-by/ │ │ │ ├── index.test.ts │ │ │ └── index.ts │ │ ├── pretty-json/ │ │ │ ├── index.test.ts │ │ │ └── index.ts │ │ ├── request-id/ │ │ │ ├── index.test.ts │ │ │ ├── index.ts │ │ │ └── request-id.ts │ │ ├── secure-headers/ │ │ │ ├── index.test.ts │ │ │ ├── index.ts │ │ │ ├── permissions-policy.ts │ │ │ └── secure-headers.ts │ │ ├── serve-static/ │ │ │ ├── index.test.ts │ │ │ ├── index.ts │ │ │ ├── path.test.ts │ │ │ └── path.ts │ │ ├── timeout/ │ │ │ ├── index.test.ts │ │ │ └── index.ts │ │ ├── timing/ │ │ │ ├── index.test.ts │ │ │ ├── index.ts │ │ │ └── timing.ts │ │ └── trailing-slash/ │ │ ├── index.test.ts │ │ └── index.ts │ ├── preset/ │ │ ├── quick.test.ts │ │ ├── quick.ts │ │ ├── tiny.test.ts │ │ └── tiny.ts │ ├── request/ │ │ └── constants.ts │ ├── request.test.ts │ ├── request.ts │ ├── router/ │ │ ├── common.case.test.ts │ │ ├── linear-router/ │ │ │ ├── index.ts │ │ │ ├── router.test.ts │ │ │ └── router.ts │ │ ├── pattern-router/ │ │ │ ├── index.ts │ │ │ ├── router.test.ts │ │ │ └── router.ts │ │ ├── reg-exp-router/ │ │ │ ├── index.ts │ │ │ ├── matcher.ts │ │ │ ├── node.ts │ │ │ ├── prepared-router.test.ts │ │ │ ├── prepared-router.ts │ │ │ ├── router.test.ts │ │ │ ├── router.ts │ │ │ └── trie.ts │ │ ├── smart-router/ │ │ │ ├── index.ts │ │ │ ├── router.test.ts │ │ │ └── router.ts │ │ └── trie-router/ │ │ ├── index.ts │ │ ├── node.test.ts │ │ ├── node.ts │ │ ├── router.test.ts │ │ └── router.ts │ ├── router.ts │ ├── types.test.ts │ ├── types.ts │ ├── utils/ │ │ ├── accept.test.ts │ │ ├── accept.ts │ │ ├── basic-auth.test.ts │ │ ├── basic-auth.ts │ │ ├── body.test.ts │ │ ├── body.ts │ │ ├── buffer.test.ts │ │ ├── buffer.ts │ │ ├── color.test.ts │ │ ├── color.ts │ │ ├── compress.ts │ │ ├── concurrent.test.ts │ │ ├── concurrent.ts │ │ ├── constants.ts │ │ ├── cookie.test.ts │ │ ├── cookie.ts │ │ ├── crypto.test.ts │ │ ├── crypto.ts │ │ ├── encode.test.ts │ │ ├── encode.ts │ │ ├── filepath.test.ts │ │ ├── filepath.ts │ │ ├── handler.ts │ │ ├── headers.ts │ │ ├── html.test.ts │ │ ├── html.ts │ │ ├── http-status.ts │ │ ├── ipaddr.test.ts │ │ ├── ipaddr.ts │ │ ├── jwt/ │ │ │ ├── index.ts │ │ │ ├── jwa.test.ts │ │ │ ├── jwa.ts │ │ │ ├── jws.ts │ │ │ ├── jwt.test.ts │ │ │ ├── jwt.ts │ │ │ ├── types.ts │ │ │ └── utf8.ts │ │ ├── mime.test.ts │ │ ├── mime.ts │ │ ├── stream.test.ts │ │ ├── stream.ts │ │ ├── types.test.ts │ │ ├── types.ts │ │ ├── url.test.ts │ │ └── url.ts │ └── validator/ │ ├── index.ts │ ├── utils.test.ts │ ├── utils.ts │ ├── validator.test.ts │ └── validator.ts ├── tsconfig.base.json ├── tsconfig.build.json ├── tsconfig.json ├── tsconfig.spec.json └── vitest.config.ts
SYMBOL INDEX (1444 symbols across 187 files)
FILE: .vitest.config/setup-vitest.ts
type StoreMap (line 15) | type StoreMap = Map<string | Request, Response>
class MockCache (line 17) | class MockCache {
method constructor (line 21) | constructor(name: string, store: StoreMap) {
method match (line 26) | async match(key: Request | string): Promise<Response | null> {
method keys (line 30) | async keys() {
method put (line 34) | async put(key: Request | string, response: Response): Promise<void> {
FILE: benchmarks/http-server/benchmark.ts
constant SCRIPT_DIR (line 32) | const SCRIPT_DIR = import.meta.dirname
constant TEMP_DIR (line 33) | const TEMP_DIR = join(SCRIPT_DIR, '.benchmark-temp')
constant HONO_ROOT (line 34) | const HONO_ROOT = join(SCRIPT_DIR, '../..')
FILE: build/build.ts
method setup (line 44) | setup(build: PluginBuild) {
FILE: build/remove-private-fields.ts
function removePrivateFields (line 5) | async function removePrivateFields(files: string[]) {
function removePrivateFieldFromSourceCode (line 27) | function removePrivateFieldFromSourceCode(ast: ParseResult, sourceCode: ...
function removeRange (line 55) | function removeRange(str: string, start: number, end: number) {
FILE: perf-measures/bundle-check/scripts/check-bundle-size.ts
function main (line 6) | async function main() {
FILE: perf-measures/type-check/scripts/process-results.ts
function main (line 3) | async function main() {
FILE: runtime-tests/bun/index.test.tsx
type ContextRenderer (line 17) | interface ContextRenderer {
method onMessage (line 303) | onMessage(evt) {
function deleteDirectory (line 331) | async function deleteDirectory(dirPath: string) {
FILE: runtime-tests/deno/ssg.test.tsx
function deleteDirectory (line 36) | async function deleteDirectory(dirPath: string): Promise<void> {
FILE: runtime-tests/lambda-edge/index.test.ts
type Bindings (line 11) | type Bindings = {
type CloudFrontHeaders (line 883) | interface CloudFrontHeaders {
type CloudFrontHeaders (line 1029) | interface CloudFrontHeaders {
FILE: runtime-tests/lambda/index.test.ts
type Bindings (line 28) | type Bindings = {
method serialized (line 148) | get serialized() {
method serialized (line 155) | get serialized() {
method read (line 963) | read() {}
method read (line 996) | read() {}
FILE: runtime-tests/lambda/mock.ts
type StreamifyResponseHandler (line 5) | type StreamifyResponseHandler = (
method write (line 16) | write(chunk: Buffer, _encoding: string, callback: () => void) {
method final (line 20) | final(callback: () => void) {
FILE: runtime-tests/lambda/stream-mock.ts
type StreamifyResponseHandler (line 9) | type StreamifyResponseHandler = (
method write (line 21) | write(chunk, _encoding, callback) {
FILE: runtime-tests/lambda/stream.test.ts
type Bindings (line 11) | type Bindings = {
FILE: runtime-tests/node/index.test.ts
function createAgent (line 280) | function createAgent(app: Hono) {
FILE: runtime-tests/workerd/index.ts
method onMessage (line 19) | onMessage(event, ws) {
FILE: src/adapter/aws-lambda/conninfo.ts
type LambdaRequestContext (line 9) | type LambdaRequestContext =
type Env (line 14) | type Env = {
FILE: src/adapter/aws-lambda/handler.ts
function sanitizeHeaderValue (line 13) | function sanitizeHeaderValue(value: string): string {
type LambdaEvent (line 23) | type LambdaEvent =
type LatticeProxyEventV2 (line 29) | interface LatticeProxyEventV2 {
type APIGatewayProxyEventV2 (line 41) | interface APIGatewayProxyEventV2 {
type APIGatewayProxyEvent (line 64) | interface APIGatewayProxyEvent {
type ALBProxyEvent (line 85) | interface ALBProxyEvent {
type WithHeaders (line 99) | type WithHeaders = {
type WithMultiValueHeaders (line 103) | type WithMultiValueHeaders = {
type APIGatewayProxyResult (line 108) | type APIGatewayProxyResult = {
type HandleOptions (line 195) | type HandleOptions = {
method getHeaderValue (line 281) | protected getHeaderValue(headers: E['headers'], key: string): string | u...
method getDomainName (line 291) | protected getDomainName(event: E): string | undefined {
method createRequest (line 308) | createRequest(event: E): Request {
method createResult (line 330) | async createResult(
method setCookies (line 374) | setCookies(_event: E, res: Response, result: APIGatewayProxyResult) {
class EventV2Processor (line 390) | class EventV2Processor extends EventProcessor<APIGatewayProxyEventV2> {
method getPath (line 391) | protected getPath(event: APIGatewayProxyEventV2): string {
method getMethod (line 395) | protected getMethod(event: APIGatewayProxyEventV2): string {
method getQueryString (line 399) | protected getQueryString(event: APIGatewayProxyEventV2): string {
method getCookies (line 403) | protected getCookies(event: APIGatewayProxyEventV2, headers: Headers):...
method setCookiesToResult (line 409) | protected setCookiesToResult(result: APIGatewayProxyResult, cookies: s...
method getHeaders (line 413) | protected getHeaders(event: APIGatewayProxyEventV2): Headers {
class EventV1Processor (line 429) | class EventV1Processor extends EventProcessor<APIGatewayProxyEvent> {
method getPath (line 430) | protected getPath(event: APIGatewayProxyEvent): string {
method getMethod (line 434) | protected getMethod(event: APIGatewayProxyEvent): string {
method getQueryString (line 438) | protected getQueryString(event: APIGatewayProxyEvent): string {
method getCookies (line 456) | protected getCookies(_event: APIGatewayProxyEvent, _headers: Headers):...
method getHeaders (line 460) | protected getHeaders(event: APIGatewayProxyEvent): Headers {
method setCookiesToResult (line 487) | protected setCookiesToResult(result: APIGatewayProxyResult, cookies: s...
class ALBProcessor (line 496) | class ALBProcessor extends EventProcessor<ALBProxyEvent> {
method getHeaders (line 497) | protected getHeaders(event: ALBProxyEvent): Headers {
method getPath (line 519) | protected getPath(event: ALBProxyEvent): string {
method getMethod (line 523) | protected getMethod(event: ALBProxyEvent): string {
method getQueryString (line 527) | protected getQueryString(event: ALBProxyEvent): string {
method getCookies (line 553) | protected getCookies(event: ALBProxyEvent, headers: Headers): void {
method setCookiesToResult (line 565) | protected setCookiesToResult(result: APIGatewayProxyResult, cookies: s...
class LatticeV2Processor (line 578) | class LatticeV2Processor extends EventProcessor<LatticeProxyEventV2> {
method getPath (line 579) | protected getPath(event: LatticeProxyEventV2): string {
method getMethod (line 583) | protected getMethod(event: LatticeProxyEventV2): string {
method getQueryString (line 587) | protected getQueryString(): string {
method getHeaders (line 591) | protected getHeaders(event: LatticeProxyEventV2): Headers {
method getCookies (line 612) | protected getCookies(): void {
method setCookiesToResult (line 616) | protected setCookiesToResult(result: APIGatewayProxyResult, cookies: s...
FILE: src/adapter/aws-lambda/types.ts
type CognitoIdentity (line 3) | interface CognitoIdentity {
type ClientContext (line 8) | interface ClientContext {
type ClientContextClient (line 15) | interface ClientContextClient {
type ClientContextEnv (line 23) | interface ClientContextEnv {
type LambdaContext (line 35) | interface LambdaContext {
type Callback (line 50) | type Callback<TResult = any> = (error?: Error | string | null, result?: ...
type Handler (line 52) | type Handler<TEvent = any, TResult = any> = (
type ClientCert (line 58) | interface ClientCert {
type Identity (line 69) | interface Identity {
type ApiGatewayRequestContext (line 85) | interface ApiGatewayRequestContext {
type Authorizer (line 107) | interface Authorizer {
type ApiGatewayRequestContextV2 (line 119) | interface ApiGatewayRequestContextV2 {
type ALBRequestContext (line 140) | interface ALBRequestContext {
type LatticeRequestContextV2 (line 146) | interface LatticeRequestContextV2 {
FILE: src/adapter/bun/conninfo.test.ts
method requestIP (line 18) | requestIP() {
method requestIP (line 74) | requestIP() {
FILE: src/adapter/bun/websocket.test.ts
method send (line 10) | send(data) {
method close (line 13) | close(code, reason) {
method onOpen (line 77) | onOpen(evt, ws) {
method onMessage (line 81) | onMessage(evt, ws) {
method onClose (line 88) | onClose(evt, ws) {
method upgrade (line 99) | upgrade() {
FILE: src/adapter/bun/websocket.ts
type BunServerWebSocket (line 8) | interface BunServerWebSocket<T> {
type BunWebSocketHandler (line 15) | interface BunWebSocketHandler<T> {
type CreateWebSocket (line 20) | interface CreateWebSocket<T> {
type BunWebSocketData (line 24) | interface BunWebSocketData {
method close (line 42) | close(code, reason) {
method open (line 75) | open(ws) {
method close (line 81) | close(ws, code, reason) {
method message (line 93) | message(ws, message) {
FILE: src/adapter/cloudflare-pages/handler.test.ts
type Env (line 7) | type Env = {
function createEventContext (line 13) | function createEventContext(
FILE: src/adapter/cloudflare-pages/handler.ts
type Params (line 9) | type Params<P extends string = any> = Record<P, string | string[]>
type EventContext (line 12) | type EventContext<Env = {}, P extends string = any, Data = Record<string...
type PagesFunction (line 25) | type PagesFunction<
function handleMiddleware (line 49) | function handleMiddleware<E extends Env = {}, P extends string = any, I ...
FILE: src/adapter/cloudflare-workers/serve-static.test.ts
type Env (line 204) | type Env = {
FILE: src/adapter/cloudflare-workers/serve-static.ts
type ServeStaticOptions (line 6) | type ServeStaticOptions<E extends Env = Env> = BaseServeStaticOptions<E>...
FILE: src/adapter/cloudflare-workers/utils.ts
type KVAssetOptions (line 5) | type KVAssetOptions = {
FILE: src/adapter/cloudflare-workers/websocket.test.ts
method constructor (line 12) | constructor() {
method onMessage (line 24) | onMessage(evt, ws) {
FILE: src/adapter/cloudflare-workers/websocket.ts
method protocol (line 23) | get protocol() {
method readyState (line 27) | get readyState() {
FILE: src/adapter/deno/deno.d.ts
type FileHandleLike (line 2) | interface FileHandleLike {
type StatsLike (line 14) | interface StatsLike {
type UpgradeWebSocketOptions (line 60) | interface UpgradeWebSocketOptions {
FILE: src/adapter/deno/websocket.ts
method protocol (line 14) | get protocol() {
method readyState (line 18) | get readyState() {
FILE: src/adapter/lambda-edge/conninfo.ts
type Env (line 5) | type Env = {
FILE: src/adapter/lambda-edge/handler.test.ts
type Env (line 100) | type Env = { Bindings: { callback: Callback } }
FILE: src/adapter/lambda-edge/handler.ts
type CloudFrontHeader (line 10) | interface CloudFrontHeader {
type CloudFrontHeaders (line 15) | interface CloudFrontHeaders {
type CloudFrontCustomOrigin (line 19) | interface CloudFrontCustomOrigin {
type CloudFrontS3Origin (line 30) | interface CloudFrontS3Origin {
type CloudFrontOrigin (line 37) | type CloudFrontOrigin =
type CloudFrontRequest (line 41) | interface CloudFrontRequest {
type CloudFrontResponse (line 56) | interface CloudFrontResponse {
type CloudFrontConfig (line 62) | interface CloudFrontConfig {
type CloudFrontEvent (line 69) | interface CloudFrontEvent {
type CloudFrontEdgeEvent (line 77) | interface CloudFrontEdgeEvent {
type CloudFrontContext (line 81) | type CloudFrontContext = {}
type Callback (line 83) | interface Callback {
type CloudFrontResult (line 88) | interface CloudFrontResult {
FILE: src/adapter/netlify/conninfo.ts
type NetlifyContext (line 8) | type NetlifyContext = {
type Env (line 28) | type Env = {
FILE: src/adapter/service-worker/handler.test.ts
method respondWith (line 39) | respondWith(res) {
method respondWith (line 52) | respondWith(res) {
method fetch (line 62) | async fetch() {
method respondWith (line 69) | respondWith(res) {
method respondWith (line 85) | respondWith(r) {
FILE: src/adapter/service-worker/handler.ts
type Handler (line 10) | type Handler = (evt: FetchEvent) => void
type HandleOptions (line 11) | type HandleOptions = {
FILE: src/adapter/service-worker/types.ts
type ExtendableEvent (line 1) | interface ExtendableEvent extends Event {
type FetchEvent (line 6) | interface FetchEvent extends ExtendableEvent {
FILE: src/client/client.test.ts
class SafeBigInt (line 20) | class SafeBigInt {
method toJSON (line 23) | toJSON() {
type AppType (line 72) | type AppType = typeof route
type AppType (line 206) | type AppType = typeof route
type stringVerify (line 216) | type stringVerify = Expect<Equal<'a-string', typeof stringRes>>
type numberVerify (line 218) | type numberVerify = Expect<Equal<37, typeof numberRes>>
type booleanVerify (line 220) | type booleanVerify = Expect<Equal<true, typeof booleanRes>>
type genericVerify (line 222) | type genericVerify = Expect<Equal<number | boolean, typeof genericRes>>
type textTest (line 226) | type textTest = Expect<Equal<Promise<string>, ReturnType<typeof genericF...
type AppType (line 324) | type AppType = typeof route
type AppType (line 550) | type AppType = typeof route
type Actual (line 556) | type Actual = InferResponseType<typeof req>
type Expected (line 557) | type Expected = {
type verify (line 561) | type verify = Expect<Equal<Expected, Actual>>
type Actual (line 568) | type Actual = InferRequestType<typeof req>
type Expected (line 569) | type Expected = {
type verify (line 573) | type verify = Expect<Equal<Expected, Actual['query']>>
type c (line 579) | type c = typeof req
type Actual (line 581) | type Actual = InferRequestType<c>
type Expected (line 582) | type Expected = {
type verify (line 585) | type verify = Expect<Equal<Expected, Actual['header']>>
type c (line 591) | type c = typeof req
type Actual (line 593) | type Actual = InferRequestType<c>
type Expected (line 594) | type Expected = {
type verify (line 597) | type verify = Expect<Equal<Expected, Actual['cookie']>>
type AppType (line 602) | type AppType = typeof route
type Actual (line 608) | type Actual = InferResponseType<typeof req>
type Expected (line 609) | type Expected = { ok: true }
type verify (line 610) | type verify = Expect<Equal<Expected, Actual>>
type Actual (line 617) | type Actual = InferRequestType<typeof req>
type Expected (line 618) | type Expected = {}
type verify (line 619) | type verify = Expect<Equal<Expected, Actual>>
type Env (line 659) | type Env = {
type AppType (line 668) | type AppType = typeof app
type verify (line 672) | type verify = Expect<Equal<true, typeof data.ok>>
type AppType (line 679) | type AppType = typeof app
type verify (line 683) | type verify = Expect<Equal<true, typeof data.ok>>
type AppType (line 690) | type AppType = typeof app
type verify (line 694) | type verify = Expect<Equal<true, typeof data.ok>>
type Result (line 699) | interface Result {
type AppType (line 706) | type AppType = typeof app
type verify (line 710) | type verify = Expect<Equal<Result, typeof data>>
type DeepInterface (line 714) | interface DeepInterface {
type ExtraDeepInterface (line 719) | interface ExtraDeepInterface {
type verifyDeepInterface (line 722) | type verifyDeepInterface = Expect<
type verifyExtraDeepInterface (line 725) | type verifyExtraDeepInterface = Expect<
type Result (line 731) | interface Result {
type Results (line 735) | type Results = Result[]
type AppType (line 740) | type AppType = typeof app
type verify (line 744) | type verify = Expect<Equal<Results, typeof data>>
type verifyNestedArrayTyped (line 748) | type verifyNestedArrayTyped = Expect<
type verifyNestedArrayInterfaceArray (line 751) | type verifyNestedArrayInterfaceArray = Expect<
type verifyExtraNestedArrayTyped (line 754) | type verifyExtraNestedArrayTyped = Expect<
type verifyExtraNestedArrayInterfaceArray (line 757) | type verifyExtraNestedArrayInterfaceArray = Expect<
type AppType (line 765) | type AppType = typeof route
type verify (line 769) | type verify = Expect<Equal<string, typeof datetime>>
type AppType (line 777) | type AppType = typeof app
type verify (line 783) | type verify = Expect<Equal<string, typeof data.foo>>
type verify (line 789) | type verify = Expect<Equal<number, typeof data.bar>>
type AppType (line 858) | type AppType = typeof app
type AppType (line 871) | type AppType = typeof app
type AppType (line 882) | type AppType = typeof app
type AppType (line 894) | type AppType = typeof app
type Actual (line 994) | type Actual = InferResponseType<typeof req>
type Expected (line 995) | type Expected =
type verify (line 1003) | type verify = Expect<Equal<Expected, Actual>>
type Actual (line 1009) | type Actual = InferResponseType<typeof req, 200>
type Expected (line 1010) | type Expected = {
type verify (line 1013) | type verify = Expect<Equal<Expected, Actual>>
type AppType (line 1055) | type AppType = typeof app
type Actual (line 1061) | type Actual = InferResponseType<typeof req, 200>
type Expected (line 1062) | type Expected = {
type verify (line 1065) | type verify = Expect<Equal<Expected, Actual>>
type Actual (line 1071) | type Actual = InferResponseType<typeof req, 400>
type Expected (line 1072) | type Expected = {
type verify (line 1075) | type verify = Expect<Equal<Expected, Actual>>
type Actual (line 1081) | type Actual = InferResponseType<typeof req, 401>
type Expected (line 1082) | type Expected = {
type verify (line 1085) | type verify = Expect<Equal<Expected, Actual>>
type Actual (line 1095) | type Actual = typeof req.status
type Expected (line 1096) | type Expected = 200 | 400 | 401
type verify (line 1097) | type verify = Expect<Equal<Expected, Actual>>
type AppType (line 1130) | type AppType = typeof app
type AppType (line 1167) | type AppType = typeof app
type AppType (line 1206) | type AppType = typeof route
type AppType (line 1261) | type AppType = typeof route
method onMessage (line 1347) | onMessage(event, ws) {
type AppType (line 1357) | type AppType = typeof route
method onMessage (line 1402) | onMessage(event, ws) {
type AppType (line 1411) | type AppType = typeof route
type Actual (line 1517) | type Actual = ReturnType<typeof res.json>
type Expected (line 1518) | type Expected = Promise<never>
type verify (line 1519) | type verify = Expect<Equal<Expected, Actual>>
type Actual (line 1534) | type Actual = ReturnType<typeof res.text>
type Expected (line 1535) | type Expected = Promise<string>
type verify (line 1536) | type verify = Expect<Equal<Expected, Actual>>
type Actual (line 1568) | type Actual = InferResponseType<typeof req>
type Expected (line 1569) | type Expected =
type verify (line 1574) | type verify = Expect<Equal<Expected, Actual>>
type Expected (line 1584) | type Expected = ClientResponse<undefined, 301, 'redirect'>
type verify (line 1585) | type verify = Expect<Equal<Expected, typeof res>>
type Expected (line 1588) | type Expected = ClientResponse<undefined, 302, 'redirect'>
type verify (line 1589) | type verify = Expect<Equal<Expected, typeof res>>
method onMessage (line 1599) | onMessage(event, ws) {
method onClose (line 1602) | onClose() {
type AppType (line 1608) | type AppType = typeof route
method webSocket (line 1634) | webSocket(url, options) {
type AppType (line 1658) | type AppType = typeof route
type AppWithGlobalErrors (line 1748) | type AppWithGlobalErrors = ApplyGlobalResponse<
type ResponseType (line 1760) | type ResponseType = InferResponseType<typeof req>
type Expected (line 1761) | type Expected = { users: string[] } | { error: string; message: string }
type verify (line 1763) | type verify = Expect<Equal<ResponseType, Expected>>
type AppWithGlobalErrors (line 1777) | type AppWithGlobalErrors = ApplyGlobalResponse<
type AppWithOnError (line 1809) | type AppWithOnError = ApplyGlobalResponse<
type ResponseType (line 1820) | type ResponseType = InferResponseType<typeof req>
type Expected (line 1821) | type Expected = { users: string[] } | { error: string }
type verify (line 1823) | type verify = Expect<Equal<ResponseType, Expected>>
type AppWithGlobalErrors (line 1830) | type AppWithGlobalErrors = ApplyGlobalResponse<
type ResponseType (line 1840) | type ResponseType = InferResponseType<typeof req>
type Expected (line 1841) | type Expected = { users: string[] } | { error: string }
type verify (line 1843) | type verify = Expect<Equal<ResponseType, Expected>>
FILE: src/client/client.ts
method get (line 17) | get(_obj, key) {
method apply (line 23) | apply(_1, _2, args) {
class ClientRequestImpl (line 33) | class ClientRequestImpl {
method constructor (line 42) | constructor(
FILE: src/client/fetch-result-please.ts
function fetchRP (line 14) | async function fetchRP(fetchRes: Response | Promise<Response>): Promise<...
class DetailedError (line 46) | class DetailedError extends Error {
method constructor (line 64) | constructor(
function detectResponseType (line 80) | function detectResponseType(response: Response): 'json' | 'text' {
FILE: src/client/types.ts
type MethodNameAll (line 11) | type MethodNameAll = `$${typeof METHOD_NAME_ALL_LOWERCASE}`
type StandardMethods (line 17) | type StandardMethods = `$${(typeof METHODS)[number]}`
type ExpandAllMethod (line 24) | type ExpandAllMethod<S> = MethodNameAll extends keyof S
type HonoRequest (line 28) | type HonoRequest = (typeof Hono.prototype)['request']
type BuildSearchParamsFn (line 30) | type BuildSearchParamsFn = (query: Record<string, string | string[]>) =>...
type ClientRequestOptions (line 32) | type ClientRequestOptions<T = unknown> = {
type ClientRequest (line 67) | type ClientRequest<Prefix extends string, Path extends string, S extends...
type ClientResponseOfEndpoint (line 120) | type ClientResponseOfEndpoint<T extends Endpoint = Endpoint> = T extends {
type ClientResponse (line 128) | interface ClientResponse<
type BuildSearch (line 156) | type BuildSearch<Arg, Key extends 'query'> = Arg extends { [K in Key]: i...
type BuildPathname (line 162) | type BuildPathname<P extends string, Arg> = Arg extends { param: infer P...
type BuildPath (line 166) | type BuildPath<P extends string, Arg> = `${BuildPathname<P, Arg>}${Build...
type BuildTypedURL (line 168) | type BuildTypedURL<
type HonoURL (line 176) | type HonoURL<Prefix extends string, Path extends string, Arg> =
type ParseHostName (line 188) | type ParseHostName<T extends string> = T extends `${infer Host}:${infer ...
type TrimStartSlash (line 191) | type TrimStartSlash<T extends string> = T extends `/${infer R}` ? TrimSt...
type TrimEndSlash (line 192) | type TrimEndSlash<T extends string> = T extends `${infer R}/` ? TrimEndS...
type IsLiteral (line 193) | type IsLiteral<T extends string> = [T] extends [never] ? false : string ...
type ApplyParam (line 194) | type ApplyParam<
type IsEmptyObject (line 213) | type IsEmptyObject<T> = keyof T extends never ? true : false
type TypedURL (line 215) | interface TypedURL<
type Response (line 232) | interface Response extends ClientResponse<unknown> {}
type Fetch (line 234) | type Fetch<T> = (
type InferEndpointType (line 239) | type InferEndpointType<T> = T extends (
type InferResponseType (line 251) | type InferResponseType<T, U extends StatusCode = StatusCode> = InferResp...
type InferResponseTypeFromEndpoint (line 256) | type InferResponseTypeFromEndpoint<T extends Endpoint, U extends StatusC...
type InferRequestType (line 265) | type InferRequestType<T> = T extends (
type InferRequestOptionsType (line 273) | type InferRequestOptionsType<T> = T extends (
type FilterClientResponseByStatusCode (line 284) | type FilterClientResponseByStatusCode<
type PathToChain (line 294) | type PathToChain<
type Client (line 311) | type Client<T, Prefix extends string> =
type Callback (line 320) | type Callback = (opts: CallbackOptions) => unknown
type CallbackOptions (line 322) | interface CallbackOptions {
type ObjectType (line 328) | type ObjectType<T = unknown> = {
type GlobalResponseDefinition (line 332) | type GlobalResponseDefinition = {
type ToEndpoints (line 338) | type ToEndpoints<Def extends GlobalResponseDefinition, R> = {
type ModRoute (line 348) | type ModRoute<R, Def extends GlobalResponseDefinition> = R extends Endpoint
type ModSchema (line 352) | type ModSchema<D, Def extends GlobalResponseDefinition> = {
type ApplyGlobalResponse (line 358) | type ApplyGlobalResponse<App, Def extends GlobalResponseDefinition> =
FILE: src/client/utils.test.ts
type _verify (line 260) | type _verify = Expect<Equal<typeof result, 'hi'>>
type _verify (line 265) | type _verify = Expect<Equal<typeof result, 'hi'>>
type _verify (line 270) | type _verify = Expect<Equal<typeof result, 'OK'>>
type _verify (line 275) | type _verify = Expect<Equal<typeof result, { message: string }>>
type _verify (line 280) | type _verify = Expect<Equal<typeof result, { message: string }>>
type _verify (line 288) | type _verify = Expect<Equal<typeof result, 'hello'>>
type _verify (line 293) | type _verify = Expect<Equal<typeof result, string>>
type ResultType (line 314) | type ResultType = Awaited<
type _verify (line 317) | type _verify = Expect<Equal<Awaited<ResultType>, undefined>>
type ResultType (line 320) | type ResultType = Awaited<
type _verify (line 325) | type _verify = Expect<Equal<ResultType, { data: { id: number }[] }>>
type ResultType (line 328) | type ResultType = Awaited<
type _verify (line 335) | type _verify = Expect<Equal<ResultType, { message: string }>>
FILE: src/client/utils.ts
function isObject (line 62) | function isObject(item: unknown): item is ObjectType {
function deepMerge (line 66) | function deepMerge<T>(target: T, source: Record<string, unknown>): T {
function parseResponse (line 92) | async function parseResponse<T extends ClientResponse<any>>(
FILE: src/compose.test.ts
type MiddlewareTuple (line 6) | type MiddlewareTuple = [[Function, unknown], Params]
class ExpectedError (line 8) | class ExpectedError extends Error {}
function buildMiddlewareTuple (line 10) | function buildMiddlewareTuple(fn: Function, params?: Params): Middleware...
FILE: src/compose.ts
function dispatch (line 32) | async function dispatch(i: number): Promise<Context> {
FILE: src/context.test.ts
method set (line 7) | set(target, prop, value) {
method get (line 13) | get(target, prop, receiver) {
type ContextRenderer (line 569) | interface ContextRenderer {
FILE: src/context.ts
type HeaderRecord (line 18) | type HeaderRecord =
type Data (line 26) | type Data = string | ArrayBuffer | ReadableStream | Uint8Array<ArrayBuffer>
type ExecutionContext (line 31) | interface ExecutionContext {
type ContextVariableMap (line 57) | interface ContextVariableMap {}
type ContextRenderer (line 62) | interface ContextRenderer {}
type DefaultRenderer (line 71) | interface DefaultRenderer {
type Renderer (line 78) | type Renderer = ContextRenderer extends Function ? ContextRenderer : Def...
type PropsForRenderer (line 83) | type PropsForRenderer = [...Required<Parameters<Renderer>>] extends [unk...
type Layout (line 88) | type Layout<T = Record<string, any>> = (props: T) => any
type Get (line 95) | interface Get<E extends Env> {
type Set (line 105) | interface Set<E extends Env> {
type NewResponse (line 113) | interface NewResponse {
type BodyRespond (line 121) | interface BodyRespond {
type TextRespond (line 156) | interface TextRespond {
type JSONRespond (line 181) | interface JSONRespond {
type JSONRespondReturn (line 205) | type JSONRespondReturn<
type HTMLRespond (line 220) | interface HTMLRespond {
type ContextOptions (line 237) | type ContextOptions<E extends Env> = {
type SetHeadersOptions (line 254) | interface SetHeadersOptions {
type SetHeaders (line 258) | interface SetHeaders {
type ResponseHeadersInit (line 264) | type ResponseHeadersInit =
type ResponseInit (line 271) | interface ResponseInit<T extends StatusCode = StatusCode> {
type ResponseOrInit (line 277) | type ResponseOrInit<T extends StatusCode = StatusCode> = ResponseInit<T>...
constant TEXT_PLAIN (line 279) | const TEXT_PLAIN = 'text/plain; charset=UTF-8'
class Context (line 293) | class Context<
method constructor (line 352) | constructor(req: Request, options?: ContextOptions<E>) {
method req (line 366) | get req(): HonoRequest<P, I['out']> {
method event (line 377) | get event(): FetchEventLike {
method executionCtx (line 391) | get executionCtx(): ExecutionContext {
method res (line 403) | get res(): Response {
method res (line 414) | set res(_res: Response | undefined) {
method var (line 593) | get var(): Readonly<
method #newResponse (line 604) | #newResponse(
FILE: src/helper/accepts/accepts.test.ts
method start (line 126) | start(controller) {
FILE: src/helper/accepts/accepts.ts
type Accept (line 5) | interface Accept {
type acceptsConfig (line 11) | interface acceptsConfig {
type acceptsOptions (line 17) | interface acceptsOptions extends acceptsConfig {
FILE: src/helper/adapter/index.test.ts
type Env (line 14) | type Env = {
FILE: src/helper/adapter/index.ts
type Runtime (line 8) | type Runtime = 'node' | 'deno' | 'bun' | 'workerd' | 'fastly' | 'edge-li...
FILE: src/helper/conninfo/types.ts
type AddressType (line 3) | type AddressType = 'IPv6' | 'IPv4' | undefined
type NetAddrInfo (line 5) | type NetAddrInfo = {
type ConnInfo (line 35) | interface ConnInfo {
type GetConnInfo (line 45) | type GetConnInfo = (c: Context) => ConnInfo
FILE: src/helper/cookie/index.ts
type GetCookie (line 10) | interface GetCookie {
type GetSignedCookie (line 16) | interface GetSignedCookie {
FILE: src/helper/css/common.case.test.tsx
type Support (line 10) | interface Support {
FILE: src/helper/css/common.ts
constant PSEUDO_GLOBAL_SELECTOR (line 2) | const PSEUDO_GLOBAL_SELECTOR = ':-hono-global'
constant DEFAULT_STYLE_ID (line 4) | const DEFAULT_STYLE_ID = 'hono-css'
constant SELECTOR (line 6) | const SELECTOR: unique symbol = Symbol()
constant CLASS_NAME (line 7) | const CLASS_NAME: unique symbol = Symbol()
constant STYLE_STRING (line 8) | const STYLE_STRING: unique symbol = Symbol()
constant SELECTORS (line 9) | const SELECTORS: unique symbol = Symbol()
constant EXTERNAL_CLASS_NAMES (line 10) | const EXTERNAL_CLASS_NAMES: unique symbol = Symbol()
constant CSS_ESCAPED (line 11) | const CSS_ESCAPED: unique symbol = Symbol()
type CssClassName (line 13) | interface CssClassName {
constant IS_CSS_ESCAPED (line 21) | const IS_CSS_ESCAPED = Symbol()
type CssEscapedString (line 23) | interface CssEscapedString {
type CssVariableBasicType (line 81) | type CssVariableBasicType =
type CssVariableAsyncType (line 89) | type CssVariableAsyncType = Promise<CssVariableBasicType>
type CssVariableArrayType (line 90) | type CssVariableArrayType = (CssVariableBasicType | CssVariableAsyncType)[]
type CssVariableType (line 91) | type CssVariableType = CssVariableBasicType | CssVariableAsyncType | Css...
type ViewTransitionType (line 211) | type ViewTransitionType = {
FILE: src/helper/css/index.test.tsx
function toString (line 12) | async function toString(
function toCSS (line 24) | async function toCSS(
FILE: src/helper/css/index.ts
type CssClassName (line 25) | type CssClassName = HtmlEscapedString & CssClassNameCommon
type usedClassNameData (line 27) | type usedClassNameData = [
type CssType (line 32) | interface CssType {
type CxType (line 36) | interface CxType {
type KeyframesType (line 42) | interface KeyframesType {
type ViewTransitionType (line 46) | interface ViewTransitionType {
type StyleType (line 52) | interface StyleType {
type DefaultContextType (line 187) | interface DefaultContextType {
FILE: src/helper/dev/index.ts
type ShowRoutesOptions (line 11) | interface ShowRoutesOptions {
type RouteData (line 16) | interface RouteData {
FILE: src/helper/factory/index.test.ts
type Env (line 13) | type Env = { Variables: { foo: string } }
type Bindings (line 42) | type Bindings = {
type Variables (line 46) | type Variables = {
type Expected (line 71) | type Expected = MiddlewareHandler<any, string, {}, Response>
type _verify (line 72) | type _verify = Expect<Equal<Expected, typeof middleware>>
type Env (line 130) | type Env = { Variables: { foo: string } }
type Expected (line 149) | type Expected = Hono<
type Env (line 178) | type Env = { Variables: { foo: string } }
type Expected (line 209) | type Expected = Hono<
type ExtractOutput (line 350) | type ExtractOutput<R> = R extends H<any, any, any, TypedResponse<infer I...
type Handler1Output (line 352) | type Handler1Output = ExtractOutput<typeof handler1>
type Handler2Output (line 353) | type Handler2Output = ExtractOutput<typeof handler2>
type Env (line 364) | type Env = { Variables: { foo: string } }
type Actual (line 394) | type Actual = ExtractSchema<typeof routes>
type Expected (line 395) | type Expected = {}
type verify (line 396) | type verify = Expect<Equal<Expected, Actual>>
type Expected (line 405) | type Expected = MiddlewareHandler<Env, string, {}, Response>
type _verify (line 406) | type _verify = Expect<Equal<Expected, typeof middleware>>
FILE: src/helper/factory/index.ts
type InitApp (line 18) | type InitApp<E extends Env = Env> = (app: Hono<E>) => void
type CreateHandlersInterface (line 20) | interface CreateHandlersInterface<E extends Env, P extends string> {
FILE: src/helper/proxy/index.ts
constant ALLOWED_TOKEN_PATTERN (line 22) | const ALLOWED_TOKEN_PATTERN = /^[!#$%&'*+\-.0-9A-Z^_`a-z|~]+$/
type ProxyRequestInit (line 24) | interface ProxyRequestInit extends Omit<RequestInit, 'headers'> {
type ProxyFetch (line 46) | interface ProxyFetch {
FILE: src/helper/ssg/middleware.ts
constant SSG_CONTEXT (line 5) | const SSG_CONTEXT = 'HONO_SSG_CONTEXT'
constant X_HONO_DISABLE_SSG_HEADER_KEY (line 6) | const X_HONO_DISABLE_SSG_HEADER_KEY = 'x-hono-disable-ssg'
constant SSG_DISABLED_RESPONSE (line 13) | const SSG_DISABLED_RESPONSE = (() => {
type SSGParam (line 24) | interface SSGParam {
type SSGParams (line 27) | type SSGParams = SSGParam[]
type SSGParamsMiddleware (line 29) | interface SSGParamsMiddleware {
type AddedSSGDataRequest (line 36) | type AddedSSGDataRequest = Request & {
FILE: src/helper/ssg/plugins.ts
constant REDIRECT_STATUS_CODES (line 22) | const REDIRECT_STATUS_CODES = new Set([301, 302, 303, 307, 308])
FILE: src/helper/ssg/ssg.test.tsx
type Env (line 89) | type Env = {
FILE: src/helper/ssg/ssg.ts
constant DEFAULT_CONCURRENCY (line 11) | const DEFAULT_CONCURRENCY = 2 // default concurrency for ssg
constant DEFAULT_CONTENT_TYPE (line 19) | const DEFAULT_CONTENT_TYPE = 'text/plain'
constant DEFAULT_OUTPUT_DIR (line 21) | const DEFAULT_OUTPUT_DIR = './static'
type FileSystemModule (line 28) | interface FileSystemModule {
type ToSSGResult (line 38) | interface ToSSGResult {
type BeforeRequestHook (line 99) | type BeforeRequestHook = (req: Request) => Request | false | Promise<Req...
type AfterResponseHook (line 100) | type AfterResponseHook = (res: Response) => Response | false | Promise<R...
type AfterGenerateHook (line 101) | type AfterGenerateHook = (
type SSGPlugin (line 164) | interface SSGPlugin {
type ToSSGOptions (line 170) | interface ToSSGOptions {
type ToSSGInterface (line 330) | interface ToSSGInterface {
type ToSSGAdaptorInterface (line 344) | interface ToSSGAdaptorInterface<
FILE: src/helper/ssg/utils.ts
type FilterStaticGenerateRouteData (line 57) | interface FilterStaticGenerateRouteData {
FILE: src/helper/streaming/sse.ts
type SSEMessage (line 6) | interface SSEMessage {
class SSEStreamingApi (line 13) | class SSEStreamingApi extends StreamingApi {
method constructor (line 14) | constructor(writable: WritableStream, readable: ReadableStream) {
method writeSSE (line 18) | async writeSSE(message: SSEMessage) {
FILE: src/helper/testing/index.test.ts
type Bindings (line 12) | type Bindings = { hello: string }
FILE: src/helper/testing/index.ts
type ExtractEnv (line 13) | type ExtractEnv<T> = T extends Hono<infer E, Schema, string> ? E : never
FILE: src/helper/websocket/index.test.ts
type Result (line 59) | type Result = [number | undefined, string | undefined]
method close (line 63) | close(code, reason) {
method send (line 77) | send(data: string | ArrayBuffer | Uint8Array<ArrayBuffer>, _options) {
method send (line 110) | send(received, _options) {
FILE: src/helper/websocket/index.ts
type WSEvents (line 14) | interface WSEvents<T = unknown> {
type UpgradeWebSocket (line 24) | interface UpgradeWebSocket<T = unknown, U = any, _WSEvents = WSEvents<T>> {
type WSReadyState (line 45) | type WSReadyState = 0 | 1 | 2 | 3
type WSContextInit (line 50) | interface WSContextInit<T = unknown> {
type SendOptions (line 63) | interface SendOptions {
class WSContext (line 70) | class WSContext<T = unknown> {
method constructor (line 72) | constructor(init: WSContextInit<T>) {
method send (line 78) | send(source: string | ArrayBuffer | Uint8Array<ArrayBuffer>, options?:...
method readyState (line 83) | get readyState(): WSReadyState {
method close (line 88) | close(code?: number, reason?: string) {
type WSMessageReceive (line 93) | type WSMessageReceive = string | Blob | ArrayBufferLike
type WebSocketHelperDefineContext (line 101) | interface WebSocketHelperDefineContext {}
type WebSocketHelperDefineHandler (line 102) | type WebSocketHelperDefineHandler<T, U> = (
FILE: src/hono-base.ts
type GetPath (line 44) | type GetPath<E extends Env> = (request: Request, options?: { env?: E['Bi...
type HonoOptions (line 46) | type HonoOptions<E extends Env> = {
type MountOptionHandler (line 89) | type MountOptionHandler = (c: Context) => unknown
type MountReplaceRequest (line 90) | type MountReplaceRequest = (originalRequest: Request) => Request
type MountOptions (line 91) | type MountOptions =
class Hono (line 98) | class Hono<
method constructor (line 126) | constructor(options: HonoOptions<E> = {}) {
method #clone (line 175) | #clone(): Hono<E, S, BasePath, CurrentPath> {
method route (line 208) | route<
method basePath (line 247) | basePath<SubPath extends string>(
method mount (line 328) | mount(
method #addRoute (line 385) | #addRoute(method: string, path: string, handler: H): void {
method #handleError (line 393) | #handleError(err: unknown, c: Context<E>): Response | Promise<Response> {
method #dispatch (line 400) | #dispatch(
FILE: src/hono.test.ts
function throwExpression (line 18) | function throwExpression(errorMessage: string): never {
type Env (line 22) | type Env = {
method get (line 30) | get(target, prop, receiver) {
class CustomError (line 1467) | class CustomError extends Error {
method getResponse (line 1468) | getResponse() {
type Variables (line 2345) | type Variables = {
type Bindings (line 2374) | type Bindings = {
type ContextRenderer (line 2797) | interface ContextRenderer {
type key (line 3564) | type key = keyof typeof client
type verify (line 3565) | type verify = Expect<Equal<'posts', key>>
class ExtendedHono (line 3579) | class ExtendedHono extends Hono {
method route (line 3581) | route(path: string, app?: Hono) {
method basePath (line 3587) | basePath(path: string) {
type CloudflareBindings (line 3603) | interface CloudflareBindings {
FILE: src/hono.ts
class Hono (line 16) | class Hono<
method constructor (line 26) | constructor(options: HonoOptions<E> = {}) {
FILE: src/http-exception.ts
type HTTPExceptionOptions (line 14) | type HTTPExceptionOptions = {
class HTTPException (line 46) | class HTTPException extends Error {
method constructor (line 55) | constructor(status: ContentfulStatusCode = 500, options?: HTTPExceptio...
method getResponse (line 66) | getResponse(): Response {
FILE: src/jsx/base.ts
type Props (line 16) | type Props = Record<string, any>
type FC (line 17) | type FC<P = Props> = {
type DOMAttributes (line 22) | type DOMAttributes = HonoJSX.HTMLAttributes
type Element (line 26) | type Element = HtmlEscapedString | Promise<HtmlEscapedString>
type ElementChildrenAttribute (line 27) | interface ElementChildrenAttribute {
type IntrinsicElements (line 30) | interface IntrinsicElements extends IntrinsicElementsDefined {
type IntrinsicAttributes (line 33) | interface IntrinsicAttributes {
type LocalContexts (line 120) | type LocalContexts = [Context<unknown>, unknown][]
type Child (line 121) | type Child =
class JSXNode (line 130) | class JSXNode implements HtmlEscaped {
method constructor (line 137) | constructor(tag: string | Function, props: Props, children: Child[]) {
method type (line 143) | get type(): string | Function {
method ref (line 149) | get ref(): any {
method toString (line 153) | toString(): string | Promise<string> {
method toStringToBuffer (line 172) | toStringToBuffer(buffer: StringBufferWithCallbacks): void {
class JSXFunctionNode (line 244) | class JSXFunctionNode extends JSXNode {
method toStringToBuffer (line 245) | override toStringToBuffer(buffer: StringBufferWithCallbacks): void {
class JSXFragmentNode (line 288) | class JSXFragmentNode extends JSXNode {
method toStringToBuffer (line 289) | override toStringToBuffer(buffer: StringBufferWithCallbacks): void {
type MemorableFC (line 378) | type MemorableFC<T> = FC<T> & {
FILE: src/jsx/components.test.tsx
function resolveCallback (line 9) | function resolveCallback(template: string | HtmlEscapedString) {
function replacementResult (line 13) | function replacementResult(html: string) {
FILE: src/jsx/components.ts
type ErrorHandler (line 44) | type ErrorHandler = (error: Error) => void
type FallbackRender (line 45) | type FallbackRender = (error: Error) => Child
FILE: src/jsx/constants.ts
constant DOM_RENDERER (line 1) | const DOM_RENDERER = Symbol('RENDERER')
constant DOM_ERROR_HANDLER (line 2) | const DOM_ERROR_HANDLER = Symbol('ERROR_HANDLER')
constant DOM_STASH (line 3) | const DOM_STASH = Symbol('STASH')
constant DOM_INTERNAL_TAG (line 4) | const DOM_INTERNAL_TAG = Symbol('INTERNAL')
constant DOM_MEMO (line 5) | const DOM_MEMO = Symbol('MEMO')
constant PERMALINK (line 6) | const PERMALINK = Symbol('PERMALINK')
FILE: src/jsx/context.ts
type Context (line 8) | interface Context<T> extends FC<PropsWithChildren<{ value: T }>> {
FILE: src/jsx/dom/client.ts
type Root (line 11) | interface Root {
type RootOptions (line 15) | type RootOptions = Record<string, unknown>
method render (line 38) | render(jsxNode: unknown) {
method unmount (line 61) | unmount() {
FILE: src/jsx/dom/components.test.tsx
function runner (line 12) | function runner(
FILE: src/jsx/dom/context.test.tsx
function runner (line 16) | function runner(
FILE: src/jsx/dom/css.ts
type CreateCssJsxDomObjectsType (line 63) | interface CreateCssJsxDomObjectsType {
method toString (line 111) | toString(this: CssClassName): string {
type CssType (line 140) | interface CssType {
type CxType (line 144) | interface CxType {
type KeyframesType (line 148) | interface KeyframesType {
type ViewTransitionType (line 152) | interface ViewTransitionType {
type DefaultContextType (line 158) | interface DefaultContextType {
FILE: src/jsx/dom/hooks/index.ts
type FormStatus (line 11) | type FormStatus =
FILE: src/jsx/dom/intrinsic-element/components.test.tsx
method fetch (line 13) | fetch(url: string) {
type LinkSignatureInput (line 318) | type LinkSignatureInput = {
FILE: src/jsx/dom/render.ts
constant HONO_PORTAL_ELEMENT (line 17) | const HONO_PORTAL_ELEMENT = '_hp'
type HasRenderToDom (line 30) | type HasRenderToDom = FC<any> & { [DOM_RENDERER]: FC<any> }
type ErrorHandler (line 32) | type ErrorHandler = (error: any, retry: () => void) => Child | undefined
type Container (line 34) | type Container = HTMLElement | DocumentFragment
type LocalJSXContexts (line 35) | type LocalJSXContexts = [JSXContext<unknown>, unknown][] | undefined
type SupportedElement (line 36) | type SupportedElement = HTMLElement | SVGElement | MathMLElement
type PreserveNodeType (line 37) | type PreserveNodeType =
type NodeObject (line 41) | type NodeObject = {
type NodeString (line 66) | type NodeString = {
type Node (line 80) | type Node = NodeString | NodeObject
type PendingType (line 82) | type PendingType =
type UpdateHook (line 86) | type UpdateHook = (
type Context (line 91) | type Context =
type UpdateMapResolve (line 701) | type UpdateMapResolve = (node: NodeObject | undefined) => void
FILE: src/jsx/dom/server.ts
type RenderToStringOptions (line 11) | interface RenderToStringOptions {
type RenderToReadableStreamOptions (line 32) | interface RenderToReadableStreamOptions {
FILE: src/jsx/hooks/index.ts
type UpdateStateFunction (line 6) | type UpdateStateFunction<T> = (newState: T | ((currentState: T) => T)) =...
constant STASH_SATE (line 8) | const STASH_SATE = 0
constant STASH_EFFECT (line 9) | const STASH_EFFECT = 1
constant STASH_CALLBACK (line 10) | const STASH_CALLBACK = 2
constant STASH_MEMO (line 11) | const STASH_MEMO = 3
constant STASH_REF (line 12) | const STASH_REF = 4
type EffectData (line 14) | type EffectData = [
type UseDeferredValue (line 157) | type UseDeferredValue = <T>(value: T, initialValue?: T) => T
type UseStateType (line 178) | type UseStateType = {
type RefObject (line 318) | type RefObject<T> = { current: T | null }
FILE: src/jsx/index.test.tsx
type SiteData (line 9) | interface SiteData {
function Repeat (line 379) | function Repeat(props: any) {
function ListOfTenThings (line 387) | function ListOfTenThings() {
FILE: src/jsx/intrinsic-elements.ts
type CrossOrigin (line 15) | type CrossOrigin = 'anonymous' | 'use-credentials' | '' | undefined
type CSSProperties (line 16) | interface CSSProperties {
type AnyAttributes (line 19) | type AnyAttributes = { [attributeName: string]: any }
type JSXAttributes (line 21) | interface JSXAttributes {
type EventAttributes (line 27) | interface EventAttributes {
type HTMLAttributes (line 152) | interface HTMLAttributes extends JSXAttributes, EventAttributes, AnyAttr...
type HTMLAttributeReferrerPolicy (line 195) | type HTMLAttributeReferrerPolicy =
type HTMLAttributeAnchorTarget (line 206) | type HTMLAttributeAnchorTarget = StringLiteralUnion<'_self' | '_blank' |...
type AnchorHTMLAttributes (line 208) | interface AnchorHTMLAttributes extends HTMLAttributes {
type AudioHTMLAttributes (line 219) | interface AudioHTMLAttributes extends MediaHTMLAttributes {}
type AreaHTMLAttributes (line 221) | interface AreaHTMLAttributes extends HTMLAttributes {
type BaseHTMLAttributes (line 233) | interface BaseHTMLAttributes extends HTMLAttributes {
type BlockquoteHTMLAttributes (line 238) | interface BlockquoteHTMLAttributes extends HTMLAttributes {
type HTMLAttributePopoverTargetAction (line 243) | type HTMLAttributePopoverTargetAction = 'show' | 'hide' | 'toggle'
type ButtonHTMLAttributes (line 245) | interface ButtonHTMLAttributes extends HTMLAttributes {
type CanvasHTMLAttributes (line 262) | interface CanvasHTMLAttributes extends HTMLAttributes {
type ColHTMLAttributes (line 267) | interface ColHTMLAttributes extends HTMLAttributes {
type ColgroupHTMLAttributes (line 272) | interface ColgroupHTMLAttributes extends HTMLAttributes {
type DataHTMLAttributes (line 276) | interface DataHTMLAttributes extends HTMLAttributes {
type DetailsHTMLAttributes (line 280) | interface DetailsHTMLAttributes extends HTMLAttributes {
type DelHTMLAttributes (line 284) | interface DelHTMLAttributes extends HTMLAttributes {
type DialogHTMLAttributes (line 289) | interface DialogHTMLAttributes extends HTMLAttributes {
type EmbedHTMLAttributes (line 293) | interface EmbedHTMLAttributes extends HTMLAttributes {
type FieldsetHTMLAttributes (line 300) | interface FieldsetHTMLAttributes extends HTMLAttributes {
type HTMLAttributeFormMethod (line 307) | type HTMLAttributeFormMethod = 'get' | 'post' | 'dialog'
type HTMLAttributeFormEnctype (line 309) | type HTMLAttributeFormEnctype =
type HTMLAttributeFormAutocomplete (line 314) | type HTMLAttributeFormAutocomplete = 'on' | 'off'
type FormHTMLAttributes (line 316) | interface FormHTMLAttributes extends HTMLAttributes {
type HtmlHTMLAttributes (line 329) | interface HtmlHTMLAttributes extends HTMLAttributes {
type IframeHTMLAttributes (line 333) | interface IframeHTMLAttributes extends HTMLAttributes {
type ImgHTMLAttributes (line 347) | interface ImgHTMLAttributes extends HTMLAttributes {
type InsHTMLAttributes (line 361) | interface InsHTMLAttributes extends HTMLAttributes {
type HTMLInputTypeAttribute (line 366) | type HTMLInputTypeAttribute = StringLiteralUnion<
type AutoFillAddressKind (line 390) | type AutoFillAddressKind = 'billing' | 'shipping'
type AutoFillBase (line 391) | type AutoFillBase = '' | 'off' | 'on'
type AutoFillContactField (line 392) | type AutoFillContactField =
type AutoFillContactKind (line 402) | type AutoFillContactKind = 'home' | 'mobile' | 'work'
type AutoFillCredentialField (line 403) | type AutoFillCredentialField = 'webauthn'
type AutoFillNormalField (line 404) | type AutoFillNormalField =
type OptionalPrefixToken (line 441) | type OptionalPrefixToken<T extends string> = `${T} ` | ''
type OptionalPostfixToken (line 442) | type OptionalPostfixToken<T extends string> = ` ${T}` | ''
type AutoFillField (line 443) | type AutoFillField =
type AutoFillSection (line 446) | type AutoFillSection = `section-${string}`
type AutoFill (line 447) | type AutoFill =
type InputHTMLAttributes (line 451) | interface InputHTMLAttributes extends HTMLAttributes {
type KeygenHTMLAttributes (line 488) | interface KeygenHTMLAttributes extends HTMLAttributes {
type LabelHTMLAttributes (line 496) | interface LabelHTMLAttributes extends HTMLAttributes {
type LiHTMLAttributes (line 501) | interface LiHTMLAttributes extends HTMLAttributes {
type LinkHTMLAttributes (line 505) | interface LinkHTMLAttributes extends HTMLAttributes {
type MapHTMLAttributes (line 529) | interface MapHTMLAttributes extends HTMLAttributes {
type MenuHTMLAttributes (line 533) | interface MenuHTMLAttributes extends HTMLAttributes {
type MediaHTMLAttributes (line 537) | interface MediaHTMLAttributes extends HTMLAttributes {
type MetaHttpEquiv (line 553) | type MetaHttpEquiv =
type MetaName (line 562) | type MetaName =
type MetaProperty (line 579) | type MetaProperty =
type MetaHTMLAttributes (line 597) | interface MetaHTMLAttributes extends HTMLAttributes {
type MeterHTMLAttributes (line 609) | interface MeterHTMLAttributes extends HTMLAttributes {
type QuoteHTMLAttributes (line 619) | interface QuoteHTMLAttributes extends HTMLAttributes {
type ObjectHTMLAttributes (line 623) | interface ObjectHTMLAttributes extends HTMLAttributes {
type OlHTMLAttributes (line 633) | interface OlHTMLAttributes extends HTMLAttributes {
type OptgroupHTMLAttributes (line 639) | interface OptgroupHTMLAttributes extends HTMLAttributes {
type OptionHTMLAttributes (line 644) | interface OptionHTMLAttributes extends HTMLAttributes {
type OutputHTMLAttributes (line 651) | interface OutputHTMLAttributes extends HTMLAttributes {
type ParamHTMLAttributes (line 657) | interface ParamHTMLAttributes extends HTMLAttributes {
type ProgressHTMLAttributes (line 662) | interface ProgressHTMLAttributes extends HTMLAttributes {
type SlotHTMLAttributes (line 667) | interface SlotHTMLAttributes extends HTMLAttributes {
type ScriptHTMLAttributes (line 671) | interface ScriptHTMLAttributes extends HTMLAttributes {
type SelectHTMLAttributes (line 694) | interface SelectHTMLAttributes extends HTMLAttributes {
type MediaMime (line 705) | type MediaMime = BaseMime & (`image/${string}` | `audio/${string}` | `vi...
type SourceHTMLAttributes (line 706) | interface SourceHTMLAttributes extends HTMLAttributes {
type StyleHTMLAttributes (line 716) | interface StyleHTMLAttributes extends HTMLAttributes {
type TableHTMLAttributes (line 732) | interface TableHTMLAttributes extends HTMLAttributes {
type TextareaHTMLAttributes (line 744) | interface TextareaHTMLAttributes extends HTMLAttributes {
type TdHTMLAttributes (line 761) | interface TdHTMLAttributes extends HTMLAttributes {
type ThHTMLAttributes (line 773) | interface ThHTMLAttributes extends HTMLAttributes {
type TimeHTMLAttributes (line 782) | interface TimeHTMLAttributes extends HTMLAttributes {
type TrackHTMLAttributes (line 786) | interface TrackHTMLAttributes extends HTMLAttributes {
type VideoHTMLAttributes (line 794) | interface VideoHTMLAttributes extends MediaHTMLAttributes {
type IntrinsicElements (line 803) | interface IntrinsicElements {
type IntrinsicElements (line 924) | interface IntrinsicElements extends JSX.IntrinsicElements {}
FILE: src/jsx/jsx-dev-runtime.ts
function jsxDEV (line 12) | function jsxDEV(
FILE: src/jsx/streaming.test.tsx
function replacementResult (line 10) | function replacementResult(html: string) {
FILE: src/jsx/streaming.ts
method start (line 148) | async start(controller) {
method cancel (line 211) | cancel() {
FILE: src/jsx/types.ts
type PropsWithChildren (line 11) | type PropsWithChildren<P = unknown> = P & { children?: Child | undefined }
type CSSProperties (line 12) | type CSSProperties = JSX.CSSProperties
type ReactElement (line 19) | type ReactElement<P = any, T = string | Function> = JSXNode & {
type ReactNode (line 24) | type ReactNode = ReactElement | string | number | boolean | null | undef...
type ComponentClass (line 25) | type ComponentClass<_P = {}, _S = {}> = unknown
type Event (line 29) | type Event = globalThis.Event
type MouseEvent (line 30) | type MouseEvent = globalThis.MouseEvent
type KeyboardEvent (line 31) | type KeyboardEvent = globalThis.KeyboardEvent
type FocusEvent (line 32) | type FocusEvent = globalThis.FocusEvent
type ClipboardEvent (line 33) | type ClipboardEvent = globalThis.ClipboardEvent
type InputEvent (line 34) | type InputEvent = globalThis.InputEvent
type PointerEvent (line 35) | type PointerEvent = globalThis.PointerEvent
type TouchEvent (line 36) | type TouchEvent = globalThis.TouchEvent
type WheelEvent (line 37) | type WheelEvent = globalThis.WheelEvent
type AnimationEvent (line 38) | type AnimationEvent = globalThis.AnimationEvent
type TransitionEvent (line 39) | type TransitionEvent = globalThis.TransitionEvent
type DragEvent (line 40) | type DragEvent = globalThis.DragEvent
FILE: src/middleware/basic-auth/index.test.ts
type Env (line 328) | type Env = { Variables: { custom: string } }
type Env (line 359) | type Env = { Variables: { asyncValue: string } }
type Env (line 409) | type Env = { Variables: { verified: string } }
FILE: src/middleware/basic-auth/index.ts
type MessageFunction (line 12) | type MessageFunction = (c: Context) => string | object | Promise<string ...
type BasicAuthOptions (line 14) | type BasicAuthOptions =
FILE: src/middleware/bearer-auth/index.ts
constant TOKEN_STRINGS (line 12) | const TOKEN_STRINGS = '[A-Za-z0-9._~+/-]+=*'
constant PREFIX (line 13) | const PREFIX = 'Bearer'
constant HEADER (line 14) | const HEADER = 'Authorization'
type MessageFunction (line 16) | type MessageFunction = (c: Context) => string | object | Promise<string ...
type CustomizedErrorResponseOptions (line 17) | type CustomizedErrorResponseOptions = {
type BearerAuthOptions (line 22) | type BearerAuthOptions =
FILE: src/middleware/body-limit/index.test.ts
method start (line 70) | start(controller) {
method start (line 149) | start(controller) {
method start (line 182) | start(controller) {
FILE: src/middleware/body-limit/index.ts
constant ERROR_MESSAGE (line 10) | const ERROR_MESSAGE = 'Payload Too Large'
type OnError (line 12) | type OnError = (c: Context) => Response | Promise<Response>
type BodyLimitOptions (line 13) | type BodyLimitOptions = {
class BodyLimitError (line 18) | class BodyLimitError extends Error {
method constructor (line 19) | constructor(message: string) {
method start (line 95) | async start(controller) {
FILE: src/middleware/cache/index.test.ts
class Context (line 6) | class Context implements ExecutionContext {
method passThroughOnException (line 7) | passThroughOnException(): void {
method waitUntil (line 10) | async waitUntil(promise: Promise<unknown>): Promise<void> {
FILE: src/middleware/combine/index.ts
type Condition (line 12) | type Condition = (c: Context) => boolean
FILE: src/middleware/compress/index.test.ts
function decompressResponse (line 219) | async function decompressResponse(res: Response): Promise<string> {
FILE: src/middleware/compress/index.ts
constant ENCODING_TYPES (line 9) | const ENCODING_TYPES = ['gzip', 'deflate'] as const
type CompressionOptions (line 12) | interface CompressionOptions {
FILE: src/middleware/context-storage/index.test.ts
type Env (line 5) | type Env = {
FILE: src/middleware/cors/index.ts
type CORSOptions (line 9) | type CORSOptions = {
function set (line 100) | function set(key: string, value: string) {
FILE: src/middleware/csrf/index.ts
type IsAllowedOriginHandler (line 10) | type IsAllowedOriginHandler = (origin: string, context: Context) => bool...
type SecFetchSite (line 13) | type SecFetchSite = (typeof secFetchSiteValues)[number]
type IsAllowedSecFetchSiteHandler (line 18) | type IsAllowedSecFetchSiteHandler = (
type CSRFOptions (line 23) | interface CSRFOptions {
FILE: src/middleware/etag/index.test.ts
method start (line 107) | start(controller) {
method start (line 118) | start(controller) {
method start (line 138) | start(controller) {
FILE: src/middleware/etag/index.ts
type ETagOptions (line 9) | type ETagOptions = {
constant RETAINED_304_HEADERS (line 21) | const RETAINED_304_HEADERS = [
function etagMatches (line 32) | function etagMatches(etag: string, ifNoneMatch: string | null) {
function initializeGenerator (line 38) | function initializeGenerator(
FILE: src/middleware/ip-restriction/index.ts
type GetIPAddr (line 19) | type GetIPAddr = GetConnInfo | ((c: Context) => string)
type IPRestrictionRuleFunction (line 33) | type IPRestrictionRuleFunction = (addr: { addr: string; type: AddressTyp...
type IPRestrictionRule (line 34) | type IPRestrictionRule = string | ((addr: { addr: string; type: AddressT...
constant IS_CIDR_NOTATION_REGEX (line 36) | const IS_CIDR_NOTATION_REGEX = /\/[0-9]{0,3}$/
type IPRestrictionRules (line 118) | interface IPRestrictionRules {
FILE: src/middleware/jsx-renderer/index.test.tsx
type JSXRendererEnv (line 358) | type JSXRendererEnv = {
type Env (line 411) | type Env = { Bindings: { HONO_STREAMING?: boolean } }
FILE: src/middleware/jsx-renderer/index.ts
type RendererOptions (line 18) | type RendererOptions = {
type Component (line 23) | type Component = (
type ComponentWithChildren (line 28) | type ComponentWithChildren = (
FILE: src/middleware/jwk/index.test.ts
function inFuture (line 778) | function inFuture() {
function inPast (line 782) | function inPast() {
FILE: src/middleware/jwk/jwk.ts
function unauthorizedResponse (line 170) | function unauthorizedResponse(opts: {
FILE: src/middleware/jwt/index.test.ts
type User (line 664) | type User = {
FILE: src/middleware/jwt/index.ts
type ContextVariableMap (line 8) | interface ContextVariableMap extends JwtVariables<unknown> {}
FILE: src/middleware/jwt/jwt.ts
type JwtVariables (line 18) | type JwtVariables<T = any> = {
function unauthorizedResponse (line 160) | function unauthorizedResponse(opts: {
FILE: src/middleware/language/index.ts
type ContextVariableMap (line 11) | interface ContextVariableMap extends LanguageVariables {}
FILE: src/middleware/language/language.ts
type DetectorType (line 10) | type DetectorType = 'path' | 'querystring' | 'cookie' | 'header'
type CacheType (line 11) | type CacheType = 'cookie'
type DetectorOptions (line 13) | interface DetectorOptions {
type LanguageVariables (line 47) | interface LanguageVariables {
constant DEFAULT_OPTIONS (line 51) | const DEFAULT_OPTIONS: DetectorOptions = {
function parseAcceptLanguage (line 74) | function parseAcceptLanguage(header: string): Array<{ lang: string; q: n...
function detectFromHeader (line 152) | function detectFromHeader(c: Context, options: DetectorOptions): string ...
function detectFromPath (line 175) | function detectFromPath(c: Context, options: DetectorOptions): string | ...
type DetectorFunction (line 197) | type DetectorFunction = (c: Context, options: DetectorOptions) => string...
type Detectors (line 200) | type Detectors = Record<keyof typeof detectors, DetectorFunction>
function validateOptions (line 207) | function validateOptions(options: DetectorOptions): void {
function cacheLanguage (line 224) | function cacheLanguage(c: Context, language: string, options: DetectorOp...
FILE: src/middleware/logger/index.test.ts
function sleep (line 9) | function sleep(time: number) {
function sleep (line 135) | function sleep(time: number) {
FILE: src/middleware/logger/index.ts
type LogPrefix (line 9) | enum LogPrefix {
type PrintFunc (line 48) | type PrintFunc = (str: string, ...rest: string[]) => void
function log (line 50) | async function log(
FILE: src/middleware/method-override/index.ts
type MethodOverrideOptions (line 11) | type MethodOverrideOptions = {
constant DEFAULT_METHOD_FORM_NAME (line 33) | const DEFAULT_METHOD_FORM_NAME = '_method'
FILE: src/middleware/powered-by/index.ts
type PoweredByOptions (line 7) | type PoweredByOptions = {
FILE: src/middleware/pretty-json/index.ts
type PrettyOptions (line 8) | interface PrettyOptions {
FILE: src/middleware/request-id/index.test.ts
function generateWord (line 59) | function generateWord() {
function generateDoubleRequestId (line 62) | function generateDoubleRequestId(c: Context) {
FILE: src/middleware/request-id/index.ts
type ContextVariableMap (line 6) | interface ContextVariableMap extends RequestIdVariables {}
FILE: src/middleware/request-id/request-id.ts
type RequestIdVariables (line 9) | type RequestIdVariables = {
type RequestIdOptions (line 13) | type RequestIdOptions = {
FILE: src/middleware/secure-headers/index.test.ts
type ContextVariableMap (line 7) | interface ContextVariableMap {
FILE: src/middleware/secure-headers/index.ts
type ContextVariableMap (line 7) | interface ContextVariableMap extends SecureHeadersVariables {}
FILE: src/middleware/secure-headers/permissions-policy.ts
type PermissionsPolicyDirective (line 3) | type PermissionsPolicyDirective =
type StandardizedFeatures (line 11) | type StandardizedFeatures =
type ProposedFeatures (line 62) | type ProposedFeatures =
type ExperimentalFeatures (line 72) | type ExperimentalFeatures =
FILE: src/middleware/secure-headers/secure-headers.ts
type SecureHeadersVariables (line 11) | type SecureHeadersVariables = {
type ContentSecurityPolicyOptionHandler (line 15) | type ContentSecurityPolicyOptionHandler = (ctx: Context, directive: stri...
type ContentSecurityPolicyOptionValue (line 16) | type ContentSecurityPolicyOptionValue = (string | ContentSecurityPolicyO...
type ContentSecurityPolicyOptions (line 18) | interface ContentSecurityPolicyOptions {
type ReportToOptions (line 46) | interface ReportToOptions {
type ReportToEndpoint (line 52) | interface ReportToEndpoint {
type ReportingEndpointOptions (line 56) | interface ReportingEndpointOptions {
type PermissionsPolicyValue (line 61) | type PermissionsPolicyValue = '*' | 'self' | 'src' | 'none' | string
type PermissionsPolicyOptions (line 63) | type PermissionsPolicyOptions = Partial<
type overridableHeader (line 67) | type overridableHeader = boolean | string
type SecureHeadersOptions (line 69) | interface SecureHeadersOptions {
type HeadersMap (line 90) | type HeadersMap = {
constant HEADERS_MAP (line 94) | const HEADERS_MAP: HeadersMap = {
constant DEFAULT_OPTIONS (line 109) | const DEFAULT_OPTIONS: SecureHeadersOptions = {
type SecureHeadersCallback (line 126) | type SecureHeadersCallback = (
function getFilteredHeaders (line 231) | function getFilteredHeaders(options: SecureHeadersOptions): [string, str...
function getCSPDirectives (line 240) | function getCSPDirectives(
function getPermissionsPolicyDirectives (line 290) | function getPermissionsPolicyDirectives(policy: PermissionsPolicyOptions...
function camelToKebab (line 316) | function camelToKebab(str: string): string {
function getReportingEndpoints (line 320) | function getReportingEndpoints(
function getReportToOptions (line 326) | function getReportToOptions(reportTo: SecureHeadersOptions['reportTo'] =...
function setHeaders (line 330) | function setHeaders(ctx: Context, headersToSet: [string, string][]) {
FILE: src/middleware/serve-static/index.ts
type ServeStaticOptions (line 13) | type ServeStaticOptions<E extends Env = Env> = {
constant ENCODINGS (line 23) | const ENCODINGS = {
constant ENCODINGS_ORDERED_KEYS (line 28) | const ENCODINGS_ORDERED_KEYS = Object.keys(ENCODINGS) as (keyof typeof E...
constant DEFAULT_DOCUMENT (line 30) | const DEFAULT_DOCUMENT = 'index.html'
FILE: src/middleware/timeout/index.ts
type HTTPExceptionFunction (line 10) | type HTTPExceptionFunction = (context: Context) => HTTPException
FILE: src/middleware/timing/index.ts
type ContextVariableMap (line 7) | interface ContextVariableMap extends TimingVariables {}
FILE: src/middleware/timing/timing.ts
type TimingVariables (line 10) | type TimingVariables = {
type Timer (line 17) | interface Timer {
type TimingOptions (line 22) | interface TimingOptions {
type SetMetric (line 126) | interface SetMetric {
function wrapTime (line 242) | async function wrapTime<T>(
FILE: src/middleware/trailing-slash/index.ts
type TrimTrailingSlashOptions (line 8) | type TrimTrailingSlashOptions = {
type AppendTrailingSlashOptions (line 76) | type AppendTrailingSlashOptions = {
FILE: src/preset/quick.test.ts
type CloudflareBindings (line 12) | interface CloudflareBindings {
FILE: src/preset/quick.ts
class Hono (line 13) | class Hono<
method constructor (line 18) | constructor(options: HonoOptions<E> = {}) {
FILE: src/preset/tiny.test.ts
type CloudflareBindings (line 12) | interface CloudflareBindings {
FILE: src/preset/tiny.ts
class Hono (line 11) | class Hono<
method constructor (line 16) | constructor(options: HonoOptions<E> = {}) {
FILE: src/request.test.ts
type RecursiveRecord (line 5) | type RecursiveRecord<K extends string, T> = {
FILE: src/request.ts
type Body (line 20) | type Body = {
type BodyCache (line 27) | type BodyCache = Partial<Body & { parsedBody: BodyData }>
type OptionalRequestInitProperties (line 29) | type OptionalRequestInitProperties = 'window' | 'priority'
type RequiredRequestInit (line 30) | type RequiredRequestInit = Required<Omit<RequestInit, OptionalRequestIni...
class HonoRequest (line 36) | class HonoRequest<P extends string = '/', I extends Input['out'] = {}> {
method constructor (line 71) | constructor(
method param (line 102) | param(key?: string): unknown {
method #getDecodedParam (line 106) | #getDecodedParam(key: string): string | undefined {
method #getAllDecodedParams (line 112) | #getAllDecodedParams(): Record<string, string> {
method #getParamValue (line 126) | #getParamValue(paramKey: any): string | undefined {
method query (line 150) | query(key?: string) {
method queries (line 169) | queries(key?: string) {
method header (line 188) | header(name?: string) {
method parseBody (line 216) | async parseBody(options?: Partial<ParseBodyOptions>) {
method json (line 253) | json<T = any>(): Promise<T> {
method text (line 269) | text(): Promise<string> {
method arrayBuffer (line 285) | arrayBuffer(): Promise<ArrayBuffer> {
method blob (line 299) | blob(): Promise<Blob> {
method formData (line 313) | formData(): Promise<FormData> {
method addValidatedData (line 323) | addValidatedData(target: keyof ValidationTargets, data: {}) {
method valid (line 336) | valid(target: keyof ValidationTargets) {
method url (line 353) | get url(): string {
method method (line 369) | get method(): string {
method [GET_MATCH_RESULT] (line 373) | get [GET_MATCH_RESULT](): Result<[unknown, RouterRoute]> {
method matchedRoutes (line 404) | get matchedRoutes(): RouterRoute[] {
method routePath (line 424) | get routePath(): string {
FILE: src/request/constants.ts
constant GET_MATCH_RESULT (line 1) | const GET_MATCH_RESULT: unique symbol = Symbol()
FILE: src/router.ts
constant METHOD_NAME_ALL (line 9) | const METHOD_NAME_ALL = 'ALL' as const
constant METHOD_NAME_ALL_LOWERCASE (line 13) | const METHOD_NAME_ALL_LOWERCASE = 'all' as const
constant METHODS (line 17) | const METHODS = ['get', 'post', 'put', 'delete', 'options', 'patch'] as ...
constant MESSAGE_MATCHER_IS_ALREADY_BUILT (line 21) | const MESSAGE_MATCHER_IS_ALREADY_BUILT =
type Router (line 29) | interface Router<T> {
type ParamIndexMap (line 57) | type ParamIndexMap = Record<string, number>
type ParamStash (line 61) | type ParamStash = string[]
type Params (line 65) | type Params = Record<string, string>
type Result (line 98) | type Result<T> = [[T, ParamIndexMap][], ParamStash] | [[T, Params][]]
class UnsupportedPathError (line 103) | class UnsupportedPathError extends Error {}
FILE: src/router/common.case.test.ts
type Match (line 25) | type Match = (method: string, path: string) => { handler: string; params...
FILE: src/router/linear-router/router.ts
type RegExpMatchArrayWithIndices (line 5) | type RegExpMatchArrayWithIndices = RegExpMatchArray & { indices: [number...
class LinearRouter (line 11) | class LinearRouter<T> implements Router<T> {
method add (line 15) | add(method: string, path: string, handler: T) {
method match (line 25) | match(method: string, path: string): Result<T> {
FILE: src/router/pattern-router/router.ts
type Route (line 4) | type Route<T> = [RegExp, string, T] // [pattern, method, handler]
class PatternRouter (line 8) | class PatternRouter<T> implements Router<T> {
method add (line 12) | add(method: string, path: string, handler: T) {
method match (line 44) | match(method: string, path: string): Result<T> {
FILE: src/router/reg-exp-router/matcher.ts
type HandlerData (line 4) | type HandlerData<T> = [T, ParamIndexMap][]
type StaticMap (line 5) | type StaticMap<T> = Record<string, Result<T>>
type Matcher (line 6) | type Matcher<T> = [RegExp, HandlerData<T>[], StaticMap<T>]
type MatcherMap (line 7) | type MatcherMap<T> = Record<string, Matcher<T> | null>
function match (line 10) | function match<R extends Router<T>, T>(this: R, method: string, path: st...
FILE: src/router/reg-exp-router/node.ts
constant LABEL_REG_EXP_STR (line 1) | const LABEL_REG_EXP_STR = '[^/]+'
constant ONLY_WILDCARD_REG_EXP_STR (line 2) | const ONLY_WILDCARD_REG_EXP_STR = '.*'
constant TAIL_WILDCARD_REG_EXP_STR (line 3) | const TAIL_WILDCARD_REG_EXP_STR = '(?:|/.*)'
constant PATH_ERROR (line 4) | const PATH_ERROR = Symbol()
type ParamAssocArray (line 6) | type ParamAssocArray = [string, number][]
type Context (line 7) | interface Context {
function compareKey (line 20) | function compareKey(a: string, b: string): number {
class Node (line 45) | class Node {
method insert (line 50) | insert(
method buildRegExpStr (line 135) | buildRegExpStr(): string {
FILE: src/router/reg-exp-router/prepared-router.ts
type RelocateMap (line 7) | type RelocateMap = Record<string, ([(number | string)[], ParamIndexMap] ...
class PreparedRegExpRouter (line 9) | class PreparedRegExpRouter<T> implements Router<T> {
method constructor (line 14) | constructor(matchers: MatcherMap<T>, relocateMap: RelocateMap) {
method #addWildcard (line 19) | #addWildcard(method: string, handlerData: [T, ParamIndexMap]) {
method #addPath (line 25) | #addPath(
method add (line 47) | add(method: string, path: string, handler: T) {
method buildAllMatchers (line 88) | protected buildAllMatchers(): MatcherMap<T> {
method buildAndExportAllMatchers (line 99) | buildAndExportAllMatchers() {
FILE: src/router/reg-exp-router/router.ts
type HandlerWithMetadata (line 14) | type HandlerWithMetadata<T> = [T, number] // [handler, paramCount]
function buildWildcardRegExp (line 20) | function buildWildcardRegExp(path: string): RegExp {
function clearWildcardRegExpCache (line 30) | function clearWildcardRegExpCache() {
function buildMatcherFromPreprocessedRoutes (line 34) | function buildMatcherFromPreprocessedRoutes<T>(
function findMiddleware (line 105) | function findMiddleware<T>(
class RegExpRouter (line 122) | class RegExpRouter<T> implements Router<T> {
method constructor (line 127) | constructor() {
method add (line 132) | add(method: string, path: string, handler: T) {
method buildAllMatchers (line 208) | protected buildAllMatchers(): MatcherMap<T> {
method #buildMatcher (line 224) | #buildMatcher(method: string): Matcher<T> | null {
FILE: src/router/reg-exp-router/trie.ts
type ReplacementMap (line 4) | type ReplacementMap = number[]
class Trie (line 6) | class Trie {
method insert (line 10) | insert(path: string, index: number, pathErrorCheckOnly: boolean): Para...
method buildRegExp (line 49) | buildRegExp(): [RegExp, ReplacementMap, ReplacementMap] {
FILE: src/router/smart-router/router.ts
class SmartRouter (line 4) | class SmartRouter<T> implements Router<T> {
method constructor (line 9) | constructor(init: { routers: Router<T>[] }) {
method add (line 13) | add(method: string, path: string, handler: T) {
method match (line 21) | match(method: string, path: string): Result<T> {
method activeRouter (line 63) | get activeRouter(): Router<T> {
FILE: src/router/trie-router/node.ts
type HandlerSet (line 6) | type HandlerSet<T> = {
type HandlerParamsSet (line 12) | type HandlerParamsSet<T> = HandlerSet<T> & {
class Node (line 25) | class Node<T> {
method constructor (line 33) | constructor(method?: string, handler?: T, children?: Record<string, No...
method insert (line 44) | insert(method: string, path: string, handler: T): Node<T> {
method #pushHandlerSets (line 87) | #pushHandlerSets(
method search (line 114) | search(method: string, path: string): [[T, Params][]] {
FILE: src/router/trie-router/router.ts
class TrieRouter (line 5) | class TrieRouter<T> implements Router<T> {
method constructor (line 9) | constructor() {
method add (line 13) | add(method: string, path: string, handler: T) {
method match (line 25) | match(method: string, path: string): Result<T> {
FILE: src/types.test.ts
type E (line 31) | type E = {
type Env (line 52) | type Env = {}
type Payload (line 54) | type Payload = { foo: string; bar: boolean }
type Expected (line 70) | type Expected = Context<
type Actual (line 90) | type Actual = ExtractSchema<typeof route>
type Expected (line 91) | type Expected = {
type verify (line 105) | type verify = Expect<Equal<Expected, Actual>>
type Expected (line 122) | type Expected = Context<Env, '/foo', { in: { json: Payload }; out: { jso...
type Actual (line 128) | type Actual = ExtractSchema<typeof route>
type Expected (line 129) | type Expected = {
type verify (line 146) | type verify = Expect<Equal<Expected, Actual>>
type Actual (line 159) | type Actual = ExtractSchema<typeof route>
type Expected (line 160) | type Expected = {
type verify (line 174) | type verify = Expect<Equal<Expected, Actual>>
type Actual (line 187) | type Actual = ExtractSchema<typeof route>
type Expected (line 189) | type Expected = {
type verify (line 203) | type verify = Expect<Equal<Expected, Actual>>
type Actual (line 211) | type Actual = ExtractSchema<typeof route>
type Expected (line 212) | type Expected = {
type verify (line 243) | type verify = Expect<Equal<Expected, Actual>>
type Actual (line 252) | type Actual = ExtractSchema<typeof app>
type Expected (line 253) | type Expected = {
type verify (line 281) | type verify = Expect<Equal<Expected, Actual>>
type Actual (line 303) | type Actual = ExtractSchema<typeof route>
type Expected (line 304) | type Expected = {
type verify (line 320) | type verify = Expect<Equal<Expected, Actual>>
type Actual (line 328) | type Actual = ExtractSchema<typeof route>
type Expected (line 329) | type Expected = {
type verify (line 349) | type verify = Expect<Equal<Expected, Actual>>
type Actual (line 368) | type Actual = ExtractSchema<typeof route>
type Expected (line 369) | type Expected = {
type verify (line 402) | type verify = Expect<Equal<Expected, Actual>>
type Actual (line 408) | type Actual = TypedResponse
type Expected (line 409) | type Expected = {
type verify (line 414) | type verify = Expect<Equal<Expected, Actual>>
type Actual (line 418) | type Actual = TypedResponse<string>
type Expected (line 419) | type Expected = {
type verify (line 424) | type verify = Expect<Equal<Expected, Actual>>
type Actual (line 428) | type Actual = TypedResponse<{ ok: true }>
type Expected (line 429) | type Expected = {
type verify (line 434) | type verify = Expect<Equal<Expected, Actual>>
type AppType (line 440) | type AppType = Hono<
type Actual (line 464) | type Actual = ExtractSchema<AppType>
type Expected (line 465) | type Expected = {
type verify (line 487) | type verify = Expect<Equal<Expected, Actual>>
type Actual (line 496) | type Actual = ExtractSchema<typeof app>
type Expected (line 497) | type Expected = {
type verify (line 507) | type verify = Expect<Equal<Expected, Actual>>
type E (line 512) | type E = {
type User (line 547) | type User = {
type Actual (line 578) | type Actual = ExtractSchema<typeof route>
type Expected (line 579) | type Expected = {
type verify (line 591) | type verify = Expect<Equal<Expected, Actual>>
type Actual (line 603) | type Actual = ExtractSchema<typeof route>
type Expected (line 604) | type Expected = {
type verify (line 616) | type verify = Expect<Equal<Expected, Actual>>
type Actual (line 622) | type Actual = ParamKeys<'/posts/:postId/comment/:commentId'>
type Expected (line 623) | type Expected = 'postId' | 'commentId'
type verify (line 624) | type verify = Expect<Equal<Expected, Actual>>
type Actual (line 629) | type Actual = ParamKeyToRecord<'/animal/type?'>
type Expected (line 630) | type Expected = { [K in '/animal/type']: string | undefined }
type verify (line 631) | type verify = Expect<Equal<Expected, Actual>>
type Actual (line 634) | type Actual = ParamKeyToRecord<'/animal/type'>
type Expected (line 635) | type Expected = { [K in '/animal/type']: string }
type verify (line 636) | type verify = Expect<Equal<Expected, Actual>>
type T (line 650) | type T = ExtractSchema<typeof routes>
type Output (line 651) | type Output = T['/api/:a/:b?']['$get']['output']
type Expected (line 652) | type Expected = {
type verify (line 656) | type verify = Expect<Equal<Expected, Output>>
type Input (line 662) | type Input = {
type Actual (line 673) | type Actual = InputToDataByTarget<Input, 'json'>
type Expected (line 674) | type Expected = {
type verify (line 678) | type verify = Expect<Equal<Expected, Actual>>
type Actual (line 682) | type Actual = InputToDataByTarget<Input, 'form'>
type verify (line 683) | type verify = Expect<Equal<never, Actual>>
type Actual (line 687) | type Actual = RemoveQuestion<'/animal/type?'>
type verify (line 688) | type verify = Expect<Equal<'/animal/type', Actual>>
type Actual (line 694) | type Actual = AddParam<
type Expected (line 706) | type Expected = {
type verify (line 715) | type verify = Expect<Equal<Expected, Actual>>
type Actual (line 721) | type Actual = ToSchema<
type Expected (line 727) | type Expected = {
type verify (line 744) | type verify = Expect<Equal<Expected, Actual>>
type path1 (line 750) | type path1 = MergePath<'/api', '/book'>
type verify1 (line 751) | type verify1 = Expect<Equal<'/api/book', path1>>
type path2 (line 752) | type path2 = MergePath<'/api/', '/book'>
type verify2 (line 753) | type verify2 = Expect<Equal<'/api/book', path2>>
type path3 (line 754) | type path3 = MergePath<'/api/', '/'>
type verify3 (line 755) | type verify3 = Expect<Equal<'/api/', path3>>
type path4 (line 756) | type path4 = MergePath<'/api', '/'>
type verify4 (line 757) | type verify4 = Expect<Equal<'/api', path4>>
type path5 (line 758) | type path5 = MergePath<'/', ''>
type verify5 (line 759) | type verify5 = Expect<Equal<'/', path5>>
type path6 (line 760) | type path6 = MergePath<'', '/'>
type verify6 (line 761) | type verify6 = Expect<Equal<'/', path6>>
type path7 (line 762) | type path7 = MergePath<'/', '/'>
type verify7 (line 763) | type verify7 = Expect<Equal<'/', path7>>
type path8 (line 764) | type path8 = MergePath<'', ''>
type verify8 (line 765) | type verify8 = Expect<Equal<'/', path8>>
type Sub (line 771) | type Sub = ToSchema<
type Actual (line 795) | type Actual = MergeSchemaPath<Sub, '/api'>
type Expected (line 797) | type Expected = {
type verify (line 823) | type verify = Expect<Equal<Expected, Actual>>
type Actual (line 827) | type Actual = MergeSchemaPath<
type Expected (line 847) | type Expected = {
type verify (line 864) | type verify = Expect<Equal<Expected, Actual>>
type GetKey (line 867) | type GetKey<T> = T extends Record<infer K, unknown> ? K : never
type Sub (line 870) | type Sub = ToSchema<'get', '/', {}, TypedResponse<{}>>
type Actual (line 871) | type Actual = MergeSchemaPath<Sub, '/'>
type verify (line 872) | type verify = Expect<Equal<'/', GetKey<Actual>>>
type Sub (line 876) | type Sub = ToSchema<'get', '/tags', {}, TypedResponse<{}>>
type Actual (line 877) | type Actual = MergeSchemaPath<Sub, '/'>
type verify (line 878) | type verify = Expect<Equal<'/tags', GetKey<Actual>>>
type Sub (line 882) | type Sub = ToSchema<'get', '/', {}, TypedResponse<{}>>
type Actual (line 883) | type Actual = MergeSchemaPath<Sub, '/tags'>
type verify (line 884) | type verify = Expect<Equal<'/tags', GetKey<Actual>>>
type Actual (line 888) | type Actual = MergeSchemaPath<ToSchema<'get', '/', {}, TypedResponse>, '...
type Expected (line 889) | type Expected = {
type verify (line 903) | type verify = Expect<Equal<Expected, Actual>>
type Actual (line 907) | type Actual = MergeSchemaPath<ToSchema<'get', '/c/:d', {}, TypedResponse...
type Expected (line 908) | type Expected = {
type verify (line 924) | type verify = Expect<Equal<Expected, Actual>>
type Actual (line 928) | type Actual = MergeSchemaPath<ToSchema<'get', '/c/:d{.+}', {}, TypedResp...
type Expected (line 929) | type Expected = {
type verify (line 945) | type verify = Expect<Equal<Expected, Actual>>
type Actual (line 949) | type Actual = MergeSchemaPath<
type Expected (line 973) | type Expected = {
type verify (line 994) | type verify = Expect<Equal<Expected, Actual>>
type Actual (line 1019) | type Actual = ExtractSchema<typeof route>
type Expected (line 1020) | type Expected = {
type verify (line 1049) | type verify = Expect<Equal<Expected, Actual>>
type Actual (line 1075) | type Actual = ExtractSchema<typeof route>
type Expected (line 1076) | type Expected = {
type verify (line 1105) | type verify = Expect<Equal<Expected, Actual>>
type Actual (line 1129) | type Actual = ExtractSchema<typeof route>
type Expected (line 1130) | type Expected = {
type verify (line 1159) | type verify = Expect<Equal<Expected, Actual>>
type Actual (line 1185) | type Actual = ExtractSchema<typeof route>
type Expected (line 1186) | type Expected = {
type verify (line 1215) | type verify = Expect<Equal<Expected, Actual>>
type Actual (line 1229) | type Actual = ExtractSchema<typeof route>
type Expected (line 1230) | type Expected = {
type verify (line 1242) | type verify = Expect<Equal<Expected, Actual>>
type Actual (line 1254) | type Actual = ExtractSchema<typeof route>
type Expected (line 1255) | type Expected = {
type verify (line 1267) | type verify = Expect<Equal<Expected, Actual>>
type Env (line 1502) | type Env = {
type ContextVariableMap (line 1887) | interface ContextVariableMap {
type Env (line 2045) | type Env = {
type Actual (line 2437) | type Actual = ExtractSchema<typeof app>
type Expected (line 2438) | type Expected = {}
type verify (line 2439) | type verify = Expect<Equal<Expected, Actual>>
type Variables (line 2447) | type Variables = {
type Actual (line 2462) | type Actual = ExtractSchema<typeof route>['/']['$get']['output']
type Expected (line 2463) | type Expected = { data: string }
type Actual (line 2473) | type Actual = ExtractSchema<typeof route>['/']['$get']['status']
type Actual (line 2479) | type Actual = ExtractSchema<typeof route>['/']['$get']['status']
type Actual (line 2485) | type Actual = ExtractSchema<typeof route>['/']['$get']['status']
type Actual (line 2537) | type Actual = ExtractSchema<typeof router>['/']['$get']['status']
type Expected (line 3231) | type Expected = {
type Actual (line 3246) | type Actual = ExtractSchema<typeof routes>
type verify (line 3247) | type verify = Expect<Equal<Expected, Actual>>
type Actual (line 3252) | type Actual = ExtractSchema<typeof routes>
type verify (line 3253) | type verify = Expect<Equal<Expected, Actual>>
type Actual (line 3258) | type Actual = ExtractSchema<typeof routes>
type verify (line 3259) | type verify = Expect<Equal<Expected, Actual>>
type Actual (line 3264) | type Actual = ExtractSchema<typeof routes>
type verify (line 3265) | type verify = Expect<Equal<Expected, Actual>>
type Actual (line 3270) | type Actual = ExtractSchema<typeof routes>
type verify (line 3271) | type verify = Expect<Equal<Expected, Actual>>
type Actual (line 3284) | type Actual = ExtractSchema<typeof routes>
type verify (line 3285) | type verify = Expect<Equal<Expected, Actual>>
type Actual (line 3299) | type Actual = ExtractSchema<typeof routes>
type verify (line 3300) | type verify = Expect<Equal<Expected, Actual>>
type Actual (line 3315) | type Actual = ExtractSchema<typeof routes>
type verify (line 3316) | type verify = Expect<Equal<Expected, Actual>>
type Actual (line 3332) | type Actual = ExtractSchema<typeof routes>
type verify (line 3333) | type verify = Expect<Equal<Expected, Actual>>
type Actual (line 3350) | type Actual = ExtractSchema<typeof routes>
type verify (line 3351) | type verify = Expect<Equal<Expected, Actual>>
type Actual (line 3372) | type Actual = ExtractSchema<typeof routes>
type Expected (line 3373) | type Expected = {
type verify (line 3394) | type verify = Expect<Equal<Expected, Actual>>
type Actual (line 3421) | type Actual = ExtractSchema<typeof routes>
type Expected (line 3422) | type Expected = {
type verify (line 3451) | type verify = Expect<Equal<Expected, Actual>>
type Actual (line 3491) | type Actual = ExtractSchema<typeof app>
type Expected (line 3492) | type Expected = {
type verify (line 3506) | type verify = Expect<Equal<Expected, Actual>>
type Actual (line 3520) | type Actual = ExtractSchema<typeof app>
type Expected (line 3521) | type Expected = {
type verify (line 3544) | type verify = Expect<Equal<Expected, Actual>>
type Actual (line 3558) | type Actual = ExtractSchema<typeof app>
type Expected (line 3559) | type Expected = {
type verify (line 3582) | type verify = Expect<Equal<Expected, Actual>>
type Actual (line 3602) | type Actual = ExtractSchema<typeof app>
type Expected (line 3603) | type Expected = {
type verify (line 3626) | type verify = Expect<Equal<Expected, Actual>>
type Actual (line 3639) | type Actual = ExtractSchema<typeof app>
type Expected (line 3640) | type Expected = {
type verify (line 3650) | type verify = Expect<Equal<Expected, Actual>>
type Actual (line 3661) | type Actual = ExtractSchema<typeof app>
type Expected (line 3662) | type Expected = {
type verify (line 3681) | type verify = Expect<Equal<Expected, Actual>>
type Actual (line 3696) | type Actual = ExtractSchema<typeof app>
type Expected (line 3697) | type Expected = {
type verify (line 3707) | type verify = Expect<Equal<Expected, Actual>>
type Actual (line 3720) | type Actual = ExtractSchema<typeof app>
type Expected (line 3721) | type Expected = {
type verify (line 3731) | type verify = Expect<Equal<Expected, Actual>>
type Actual (line 3742) | type Actual = ExtractSchema<typeof app>
type Expected (line 3743) | type Expected = {
type verify (line 3766) | type verify = Expect<Equal<Expected, Actual>>
type Actual (line 3794) | type Actual = ExtractSchema<typeof app>
type Expected (line 3795) | type Expected = {
type verify (line 3814) | type verify = Expect<Equal<Expected, Actual>>
FILE: src/types.ts
type Bindings (line 26) | type Bindings = object
type Variables (line 27) | type Variables = object
type BlankEnv (line 29) | type BlankEnv = {}
type Env (line 30) | type Env = {
type Next (line 35) | type Next = () => Promise<void>
type ExtractInput (line 37) | type ExtractInput<I extends Input | Input['in']> = I extends Input
type Input (line 42) | type Input = {
type BlankSchema (line 48) | type BlankSchema = {}
type BlankInput (line 49) | type BlankInput = {}
type RouterRoute (line 57) | interface RouterRoute {
type HandlerResponse (line 70) | type HandlerResponse<O> =
type Handler (line 76) | type Handler<
type MiddlewareHandler (line 83) | type MiddlewareHandler<
type H (line 90) | type H<
type NotFoundResponse (line 105) | interface NotFoundResponse {}
type NotFoundHandler (line 107) | type NotFoundHandler<E extends Env = any> = (
type HTTPResponseError (line 113) | interface HTTPResponseError extends Error {
type ErrorHandler (line 116) | type ErrorHandler<E extends Env = any> = (
type HandlerInterface (line 127) | interface HandlerInterface<
FILE: src/utils/accept.ts
type Accept (line 1) | interface Accept {
FILE: src/utils/basic-auth.ts
constant CREDENTIALS_REGEXP (line 3) | const CREDENTIALS_REGEXP = /^ *(?:[Bb][Aa][Ss][Ii][Cc]) +([A-Za-z0-9._~+...
constant USER_PASS_REGEXP (line 4) | const USER_PASS_REGEXP = /^([^:]*):(.*)$/
type Auth (line 7) | type Auth = (req: Request) => { username: string; password: string } | u...
FILE: src/utils/body.test.ts
type RecursiveRecord (line 4) | type RecursiveRecord<K extends string, T> = {
FILE: src/utils/body.ts
type BodyDataValueDot (line 8) | type BodyDataValueDot = { [x: string]: string | File | BodyDataValueDot }
type BodyDataValueDotAll (line 9) | type BodyDataValueDotAll = {
type SimplifyBodyData (line 12) | type SimplifyBodyData<T> = {
type BodyDataValueComponent (line 22) | type BodyDataValueComponent<T> =
type BodyDataValueObject (line 30) | type BodyDataValueObject<T> = { [key: string]: BodyDataValueComponent<T>...
type BodyDataValue (line 31) | type BodyDataValue<T> =
type BodyData (line 38) | type BodyData<T extends Partial<ParseBodyOptions> = {}> = SimplifyBodyData<
type ParseBodyOptions (line 42) | type ParseBodyOptions = {
type ParseBody (line 84) | interface ParseBody {
function parseFormData (line 121) | async function parseFormData<T extends BodyData>(
function convertFormDataToBodyData (line 142) | function convertFormDataToBodyData<T extends BodyData = BodyData>(
FILE: src/utils/buffer.ts
type StringHashFunction (line 42) | type StringHashFunction = (input: string) => string | null | Promise<str...
type TimingSafeEqual (line 65) | type TimingSafeEqual = {
FILE: src/utils/color.ts
function getColorEnabled (line 14) | function getColorEnabled(): boolean {
function getColorEnabledAsync (line 36) | async function getColorEnabledAsync(): Promise<boolean> {
FILE: src/utils/compress.ts
constant COMPRESSIBLE_CONTENT_TYPE_REGEX (line 9) | const COMPRESSIBLE_CONTENT_TYPE_REGEX =
FILE: src/utils/concurrent.ts
constant DEFAULT_CONCURRENCY (line 6) | const DEFAULT_CONCURRENCY = 1024
type Pool (line 8) | interface Pool {
FILE: src/utils/constants.ts
constant COMPOSED_HANDLER (line 4) | const COMPOSED_HANDLER = '__COMPOSED_HANDLER'
FILE: src/utils/cookie.ts
type Cookie (line 8) | type Cookie = Record<string, string>
type SignedCookie (line 9) | type SignedCookie = Record<string, string | false>
type PartitionedCookieConstraint (line 11) | type PartitionedCookieConstraint =
type SecureCookieConstraint (line 14) | type SecureCookieConstraint = { secure: true }
type HostCookieConstraint (line 15) | type HostCookieConstraint = { secure: true; path: '/'; domain?: undefined }
type CookieOptions (line 17) | type CookieOptions = {
type CookiePrefixOptions (line 29) | type CookiePrefixOptions = 'host' | 'secure'
type CookieConstraint (line 31) | type CookieConstraint<Name> = Name extends `__Secure-${string}`
FILE: src/utils/crypto.ts
type Algorithm (line 8) | type Algorithm = {
type Data (line 13) | type Data = string | boolean | number | JSONValue | ArrayBufferView | Ar...
FILE: src/utils/filepath.test.ts
function slashToBackslash (line 65) | function slashToBackslash(filename: string) {
FILE: src/utils/filepath.ts
type FilePathOptions (line 6) | type FilePathOptions = {
FILE: src/utils/headers.ts
type RequestHeader (line 8) | type RequestHeader =
type ResponseHeader (line 252) | type ResponseHeader =
type AcceptHeader (line 323) | type AcceptHeader =
type CustomHeader (line 333) | type CustomHeader = string & {}
FILE: src/utils/html.ts
type HtmlEscapedCallbackOpts (line 11) | type HtmlEscapedCallbackOpts = {
type HtmlEscapedCallback (line 16) | type HtmlEscapedCallback = (opts: HtmlEscapedCallbackOpts) => Promise<st...
type HtmlEscaped (line 17) | type HtmlEscaped = {
type HtmlEscapedString (line 21) | type HtmlEscapedString = string & HtmlEscaped
type StringBuffer (line 37) | type StringBuffer = (string | Promise<string>)[]
type StringBufferWithCallbacks (line 38) | type StringBufferWithCallbacks = StringBuffer & { callbacks: HtmlEscaped...
FILE: src/utils/http-status.ts
type InfoStatusCode (line 6) | type InfoStatusCode = 100 | 101 | 102 | 103
type SuccessStatusCode (line 7) | type SuccessStatusCode = 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 |...
type DeprecatedStatusCode (line 8) | type DeprecatedStatusCode = 305 | 306
type RedirectStatusCode (line 9) | type RedirectStatusCode = 300 | 301 | 302 | 303 | 304 | DeprecatedStatus...
type ClientErrorStatusCode (line 10) | type ClientErrorStatusCode =
type ServerErrorStatusCode (line 40) | type ServerErrorStatusCode = 500 | 501 | 502 | 503 | 504 | 505 | 506 | 5...
type UnofficialStatusCode (line 52) | type UnofficialStatusCode = -1
type UnOfficalStatusCode (line 58) | type UnOfficalStatusCode = UnofficialStatusCode
type StatusCode (line 63) | type StatusCode =
type ContentlessStatusCode (line 71) | type ContentlessStatusCode = 101 | 204 | 205 | 304
type ContentfulStatusCode (line 72) | type ContentfulStatusCode = Exclude<StatusCode, ContentlessStatusCode>
FILE: src/utils/ipaddr.ts
constant IPV4_OCTET_PART (line 36) | const IPV4_OCTET_PART = '(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])'
constant IPV4_REGEX (line 37) | const IPV4_REGEX = new RegExp(`^(?:${IPV4_OCTET_PART}\\.){3}${IPV4_OCTET...
FILE: src/utils/jwt/jwa.ts
type AlgorithmTypes (line 7) | enum AlgorithmTypes {
type SignatureAlgorithm (line 23) | type SignatureAlgorithm = keyof typeof AlgorithmTypes
type SymmetricAlgorithm (line 25) | type SymmetricAlgorithm = 'HS256' | 'HS384' | 'HS512'
type AsymmetricAlgorithm (line 27) | type AsymmetricAlgorithm =
FILE: src/utils/jwt/jws.ts
type KeyImporterAlgorithm (line 13) | type KeyImporterAlgorithm = Parameters<typeof crypto.subtle.importKey>[2]
type KeyAlgorithm (line 14) | type KeyAlgorithm =
type HonoJsonWebKey (line 23) | interface HonoJsonWebKey extends JsonWebKey {
type SignatureKey (line 27) | type SignatureKey = string | HonoJsonWebKey | CryptoKey
function signing (line 29) | async function signing(
function verifying (line 39) | async function verifying(
function pemToBinary (line 50) | function pemToBinary(pem: string): Uint8Array<ArrayBuffer> {
function importPrivateKey (line 54) | async function importPrivateKey(key: SignatureKey, alg: KeyImporterAlgor...
function importPublicKey (line 78) | async function importPublicKey(key: SignatureKey, alg: KeyImporterAlgori...
function exportPublicJwkFrom (line 108) | async function exportPublicJwkFrom(privateKey: CryptoKey): Promise<JsonW...
function getKeyAlgorithm (line 122) | function getKeyAlgorithm(name: SignatureAlgorithm): KeyAlgorithm {
function isCryptoKey (line 226) | function isCryptoKey(key: SignatureKey): key is CryptoKey {
FILE: src/utils/jwt/jwt.test.ts
function exportPEMPrivateKey (line 1353) | async function exportPEMPrivateKey(key: CryptoKey): Promise<string> {
function exportPEMPublicKey (line 1359) | async function exportPEMPublicKey(key: CryptoKey): Promise<string> {
function exportJWK (line 1365) | async function exportJWK(key: CryptoKey): Promise<JsonWebKey> {
function generateRSAKey (line 1369) | async function generateRSAKey(hash: string): Promise<CryptoKeyPair> {
function generateRSAPSSKey (line 1382) | async function generateRSAPSSKey(hash: string): Promise<CryptoKeyPair> {
function generateECDSAKey (line 1395) | async function generateECDSAKey(namedCurve: string): Promise<CryptoKeyPa...
function generateEd25519Key (line 1406) | async function generateEd25519Key(): Promise<CryptoKeyPair> {
FILE: src/utils/jwt/jwt.ts
type TokenHeader (line 38) | interface TokenHeader {
function isTokenHeader (line 44) | function isTokenHeader(obj: unknown): obj is TokenHeader {
type VerifyOptions (line 78) | type VerifyOptions = {
type VerifyOptionsWithAlg (line 91) | type VerifyOptionsWithAlg = {
FILE: src/utils/jwt/types.ts
class JwtAlgorithmNotImplemented (line 6) | class JwtAlgorithmNotImplemented extends Error {
method constructor (line 7) | constructor(alg: string) {
class JwtAlgorithmRequired (line 13) | class JwtAlgorithmRequired extends Error {
method constructor (line 14) | constructor() {
class JwtAlgorithmMismatch (line 20) | class JwtAlgorithmMismatch extends Error {
method constructor (line 21) | constructor(expected: string, actual: string) {
class JwtTokenInvalid (line 27) | class JwtTokenInvalid extends Error {
method constructor (line 28) | constructor(token: string) {
class JwtTokenNotBefore (line 34) | class JwtTokenNotBefore extends Error {
method constructor (line 35) | constructor(token: string) {
class JwtTokenExpired (line 41) | class JwtTokenExpired extends Error {
method constructor (line 42) | constructor(token: string) {
class JwtTokenIssuedAt (line 48) | class JwtTokenIssuedAt extends Error {
method constructor (line 49) | constructor(currentTimestamp: number, iat: number) {
class JwtTokenIssuer (line 57) | class JwtTokenIssuer extends Error {
method constructor (line 58) | constructor(expected: string | RegExp, iss: string | null) {
class JwtHeaderInvalid (line 64) | class JwtHeaderInvalid extends Error {
method constructor (line 65) | constructor(header: object) {
class JwtHeaderRequiresKid (line 71) | class JwtHeaderRequiresKid extends Error {
method constructor (line 72) | constructor(header: object) {
class JwtSymmetricAlgorithmNotAllowed (line 78) | class JwtSymmetricAlgorithmNotAllowed extends Error {
method constructor (line 79) | constructor(alg: string) {
class JwtAlgorithmNotAllowed (line 85) | class JwtAlgorithmNotAllowed extends Error {
method constructor (line 86) | constructor(alg: string, allowedAlgorithms: readonly string[]) {
class JwtTokenSignatureMismatched (line 92) | class JwtTokenSignatureMismatched extends Error {
method constructor (line 93) | constructor(token: string) {
class JwtPayloadRequiresAud (line 99) | class JwtPayloadRequiresAud extends Error {
method constructor (line 100) | constructor(payload: object) {
class JwtTokenAudience (line 106) | class JwtTokenAudience extends Error {
method constructor (line 107) | constructor(expected: string | string[] | RegExp, aud: string | string...
type CryptoKeyUsage (line 117) | enum CryptoKeyUsage {
type JWTPayload (line 131) | type JWTPayload = {
FILE: src/utils/mime.ts
type BaseMime (line 35) | type BaseMime = (typeof _baseMimes)[keyof typeof _baseMimes]
FILE: src/utils/stream.ts
class StreamingApi (line 6) | class StreamingApi {
method constructor (line 21) | constructor(writable: WritableStream, _readable: ReadableStream) {
method write (line 46) | async write(input: Uint8Array | string): Promise<StreamingApi> {
method writeln (line 58) | async writeln(input: string): Promise<StreamingApi> {
method sleep (line 63) | sleep(ms: number): Promise<unknown> {
method close (line 67) | async close() {
method pipe (line 76) | async pipe(body: ReadableStream) {
method onAbort (line 82) | onAbort(listener: () => void | Promise<void>) {
method abort (line 90) | abort() {
FILE: src/utils/types.test.ts
type SampleEnum (line 4) | enum SampleEnum {
type Meta (line 9) | interface Meta {
type SampleInterface (line 16) | interface SampleInterface {
type SampleType (line 20) | type SampleType = {
type Actual (line 26) | type Actual = JSONParsed<number>
type Expected (line 27) | type Expected = number
type Actual (line 31) | type Actual = JSONParsed<string>
type Expected (line 32) | type Expected = string
type Actual (line 36) | type Actual = JSONParsed<boolean>
type Expected (line 37) | type Expected = boolean
type Actual (line 41) | type Actual = JSONParsed<null>
type Expected (line 42) | type Expected = null
type Actual (line 49) | type Actual = JSONParsed<{ toJSON(): number }>
type Expected (line 50) | type Expected = number
type Actual (line 54) | type Actual = JSONParsed<{ toJSON(): { toJSON(): number } }>
type Expected (line 55) | type Expected = {}
type Actual (line 59) | type Actual = JSONParsed<{ a: { toJSON(): number } }>
type Expected (line 60) | type Expected = { a: number }
type Actual (line 64) | type Actual = JSONParsed<bigint & { toJSON(): string }>
type Expected (line 65) | type Expected = string
type Actual (line 72) | type Actual = JSONParsed<undefined>
type Expected (line 73) | type Expected = never
type Actual (line 77) | type Actual = JSONParsed<symbol>
type Expected (line 78) | type Expected = never
type Actual (line 82) | type Actual = JSONParsed<() => void>
type Expected (line 83) | type Expected = never
type Actual (line 87) | type Actual = JSONParsed<bigint>
type Expected (line 88) | type Expected = never
type Actual (line 95) | type Actual = JSONParsed<undefined[]>
type Expected (line 96) | type Expected = null[]
type Actual (line 100) | type Actual = JSONParsed<(() => void)[]>
type Expected (line 101) | type Expected = null[]
type Actual (line 105) | type Actual = JSONParsed<symbol[]>
type Expected (line 106) | type Expected = null[]
type Actual (line 110) | type Actual = JSONParsed<(number | undefined)[]>
type Expected (line 111) | type Expected = (number | null)[]
type Actual (line 115) | type Actual = JSONParsed<{ key: readonly number[] }>
type Expected (line 116) | type Expected = { key: readonly number[] }
type Actual (line 123) | type Actual = JSONParsed<[number, string]>
type Expected (line 124) | type Expected = [number, string]
type Actual (line 128) | type Actual = JSONParsed<[number, undefined]>
type Expected (line 129) | type Expected = [number, null]
type Actual (line 136) | type Actual = JSONParsed<{ a: number; b: undefined }>
type Expected (line 137) | type Expected = { a: number }
type Actual (line 141) | type Actual = JSONParsed<{ a: number; b: symbol }>
type Expected (line 142) | type Expected = { a: number }
type Actual (line 146) | type Actual = JSONParsed<{ a: number; b: () => void }>
type Expected (line 147) | type Expected = { a: number }
type Actual (line 151) | type Actual = JSONParsed<{ a: number; [x: symbol]: number }>
type Expected (line 152) | type Expected = { a: number }
type Actual (line 156) | type Actual = JSONParsed<{ a: number; b: number | undefined }>
type Expected (line 157) | type Expected = { a: number; b: number | undefined }
type Actual (line 161) | type Actual = JSONParsed<{ a: number; b: undefined | symbol }>
type Expected (line 162) | type Expected = { a: number }
type Actual (line 169) | type Actual = JSONParsed<unknown>
type Expected (line 170) | type Expected = JSONValue
type Actual (line 175) | type Actual = JSONParsed<{ value: unknown }>
type Expected (line 176) | type Expected = { value: JSONValue }
type Actual (line 183) | type Actual = JSONParsed<Set<number>>
type Expected (line 184) | type Expected = {}
type Actual (line 188) | type Actual = JSONParsed<Map<number, number>>
type Expected (line 189) | type Expected = {}
type Post (line 219) | type Post = {
type Expected (line 222) | type Expected = {
type Actual (line 225) | type Actual = JSONParsed<Post>
type Post (line 230) | type Post = {
type Expected (line 233) | type Expected = never
type Actual (line 234) | type Actual = JSONParsed<Post>
type Post (line 239) | type Post = {
type Expected (line 242) | type Expected = {
type Actual (line 245) | type Actual = JSONParsed<Post, never>
type Post (line 250) | type Post = {
type Expected (line 253) | type Expected = never
type Actual (line 254) | type Actual = JSONParsed<Post>
type Post (line 259) | type Post = {
type Expected (line 262) | type Expected = {
type Actual (line 265) | type Actual = JSONParsed<Post, never>
class SafeBigInt (line 270) | class SafeBigInt {
method toJSON (line 273) | toJSON() {
type Actual (line 280) | type Actual = JSONParsed<SafeBigInt>
type Expected (line 281) | type Expected = { unsafe: string }
FILE: src/utils/types.ts
type Expect (line 7) | type Expect<T extends true> = T
type Equal (line 8) | type Equal<X, Y> =
type NotEqual (line 10) | type NotEqual<X, Y> = true extends Equal<X, Y> ? false : true
type UnionToIntersection (line 12) | type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) e...
type RemoveBlankRecord (line 18) | type RemoveBlankRecord<T> =
type IfAnyThenEmptyObject (line 21) | type IfAnyThenEmptyObject<T> = 0 extends 1 & T ? {} : T
type JSONPrimitive (line 23) | type JSONPrimitive = string | boolean | number | null
type JSONArray (line 24) | type JSONArray = (JSONPrimitive | JSONObject | JSONArray)[]
type JSONObject (line 25) | type JSONObject = {
type InvalidJSONValue (line 28) | type InvalidJSONValue = undefined | symbol | ((...args: unknown[]) => un...
type InvalidToNull (line 30) | type InvalidToNull<T> = T extends InvalidJSONValue ? null : T
type IsInvalid (line 32) | type IsInvalid<T> = T extends InvalidJSONValue ? true : false
type OmitSymbolKeys (line 37) | type OmitSymbolKeys<T> = { [K in keyof T as K extends symbol ? never : K...
type JSONValue (line 39) | type JSONValue = JSONObject | JSONArray | JSONPrimitive
type JSONParsed (line 53) | type JSONParsed<T, TError = bigint | ReadonlyArray<bigint>> = T extends {
type Simplify (line 89) | type Simplify<T> = { [KeyType in keyof T]: T[KeyType] } & {}
type SimplifyDeepArray (line 94) | type SimplifyDeepArray<T> = T extends any[]
type InterfaceToType (line 98) | type InterfaceToType<T> = T extends Function ? T : { [K in keyof T]: Int...
type RequiredKeysOf (line 100) | type RequiredKeysOf<BaseType extends object> = Exclude<
type HasRequiredKeys (line 107) | type HasRequiredKeys<BaseType extends object> =
type IsAny (line 110) | type IsAny<T> = boolean extends (T extends never ? true : false) ? true ...
type StringLiteralUnion (line 116) | type StringLiteralUnion<T> = T | (string & Record<never, never>)
FILE: src/utils/url.ts
type Pattern (line 6) | type Pattern = readonly [string, string, RegExp | true] | '*'
type Decoder (line 80) | type Decoder = (str: string) => string
FILE: src/validator/utils.ts
type IsLiteralUnion (line 9) | type IsLiteralUnion<T, Base> = [Exclude<T, undefined>] extends [Base]
type IsOptionalUnion (line 16) | type IsOptionalUnion<T> = [unknown] extends [T]
type SimplifyDeep (line 23) | type SimplifyDeep<T> = { [K in keyof T]: T[K] } & {}
type InferInputInner (line 25) | type InferInputInner<
type InferInput (line 59) | type InferInput<
FILE: src/validator/validator.test.ts
type InferValidatorResponse (line 23) | type InferValidatorResponse<VF> = VF extends (value: any, c: any) => inf...
type ResponseType (line 53) | type ResponseType = InferValidatorResponse<typeof validationFunc>
type verify (line 72) | type verify = Expect<Equal<Record<string, string | string[]>, typeof val...
type Expected (line 82) | type Expected = {
type Actual (line 104) | type Actual = ExtractSchema<typeof route>
type verify (line 106) | type verify = Expect<Equal<Expected, Actual>>
type Expected (line 488) | type Expected = {
type verify (line 491) | type verify = Expect<Equal<Expected, typeof post>>
type Expected (line 497) | type Expected = {
type Actual (line 527) | type Actual = ExtractSchema<typeof route>
type verify2 (line 528) | type verify2 = Expect<Equal<Expected, Actual>>
type Expected (line 559) | type Expected = {
type verify (line 563) | type verify = Expect<Equal<Expected, typeof post>>
type Expected (line 569) | type Expected = {
type Actual (line 602) | type Actual = ExtractSchema<typeof route>
type verify2 (line 604) | type verify2 = Expect<Equal<Expected, Actual>>
type verify (line 858) | type verify = Expect<Equal<number, typeof id>>
type verify2 (line 860) | type verify2 = Expect<Equal<{ title: string }, typeof formValidatedData>>
type Actual (line 867) | type Actual = ExtractSchema<typeof route>
type Expected (line 869) | type Expected = {
type verify (line 906) | type verify = Expect<Equal<Expected, Actual>>
type Expected (line 951) | type Expected = {
type Actual (line 970) | type Actual = ExtractSchema<typeof route>
type verify (line 971) | type verify = Expect<Equal<Expected, Actual>>
type Expected (line 997) | type Expected = {
type Actual (line 1018) | type Actual = ExtractSchema<typeof route>
type verify (line 1019) | type verify = Expect<Equal<Expected, Actual>>
type Actual (line 1061) | type Actual = ExtractSchema<typeof route>
type Expected (line 1277) | type Expected = {
type Actual (line 1294) | type Actual = ExtractSchema<typeof route>
type verify (line 1295) | type verify = Expect<Equal<Expected, Actual>>
type TestActual (line 1298) | type TestActual = Actual
type Expected (line 1324) | type Expected = {
type Actual (line 1345) | type Actual = ExtractSchema<typeof route>
type verify (line 1346) | type verify = Expect<Equal<Expected, Actual>>
type TestActual (line 1349) | type TestActual = Actual
FILE: src/validator/validator.ts
type ValidationTargetKeysWithBody (line 9) | type ValidationTargetKeysWithBody = 'form' | 'json'
type ValidationTargetByMethod (line 10) | type ValidationTargetByMethod<M> = M extends 'get' | 'head' // GET and H...
type ValidationFunction (line 14) | type ValidationFunction<
type ExtractValidationResponse (line 28) | type ExtractValidationResponse<VF> = VF extends (value: any, c: any) => ...
type ExtractValidatorOutput (line 175) | type ExtractValidatorOutput<VF> = VF extends (value: any, c: any) => inf...
Condensed preview — 479 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (2,492K chars).
[
{
"path": ".devcontainer/Dockerfile",
"chars": 251,
"preview": "FROM mcr.microsoft.com/devcontainers/typescript-node:20\n\n# Install Deno\nENV DENO_INSTALL=/usr/local\nRUN curl -fsSL https"
},
{
"path": ".devcontainer/devcontainer.json",
"chars": 471,
"preview": "{\n \"build\": {\n \"dockerfile\": \"Dockerfile\"\n },\n \"containerEnv\": {\n \"HOME\": \"/home/node\"\n },\n \"customizations\":"
},
{
"path": ".devcontainer/docker-compose.yml",
"chars": 223,
"preview": "services:\n hono:\n build: .\n container_name: hono\n volumes:\n - ../:/hono\n networks:\n - hono\n co"
},
{
"path": ".editorconfig",
"chars": 356,
"preview": "root = true\n\n[*]\ncharset = utf-8\ninsert_final_newline = true\nend_of_line = lf\n\n[*.{js,jsx,ts,tsx,json,json5,jsonc}]\ninde"
},
{
"path": ".github/FUNDING.yml",
"chars": 33,
"preview": "github: ['yusukebe', 'usualoma']\n"
},
{
"path": ".github/ISSUE_TEMPLATE/1-bug-report.yml",
"chars": 1347,
"preview": "name: 🐛 Bug Report\ndescription: Report an issue that should be fixed\nlabels: [triage]\nbody:\n - type: markdown\n attri"
},
{
"path": ".github/ISSUE_TEMPLATE/2-feature-request.yml",
"chars": 418,
"preview": "name: 🚀 Feature Request\ndescription: Suggest an idea, feature, or enhancement\nlabels: [enhancement]\nbody:\n - type: mark"
},
{
"path": ".github/ISSUE_TEMPLATE/config.yml",
"chars": 277,
"preview": "blank_issues_enabled: true\ncontact_links:\n - name: ❓ Questions\n url: https://github.com/orgs/honojs/discussions\n "
},
{
"path": ".github/actions/perf-measures/action.yml",
"chars": 2434,
"preview": "name: 'Performance Measures'\ndescription: 'Run type check and bundle size performance measurements'\n\ninputs:\n target-re"
},
{
"path": ".github/pull_request_template.md",
"chars": 261,
"preview": "### The author should do the following, if applicable\n\n- [ ] Add tests\n- [ ] Run tests\n- [ ] `bun run format:fix && bun "
},
{
"path": ".github/workflows/autofix.yml",
"chars": 727,
"preview": "name: autofix.ci\n\non:\n pull_request:\n push:\n branches: [main]\n\nconcurrency:\n group: ${{ github.workflow }}-${{ git"
},
{
"path": ".github/workflows/ci.yml",
"chars": 7880,
"preview": "name: ci\non:\n push:\n branches: [main, next]\n pull_request:\n branches: ['*']\n paths-ignore:\n - 'docs/**'\n"
},
{
"path": ".github/workflows/cr.yml",
"chars": 1100,
"preview": "name: cr\non:\n push:\n branches: [main]\n tags: ['!**'] # Avoid publishing on tags\n pull_request:\n types: [opene"
},
{
"path": ".github/workflows/no-response.yml",
"chars": 688,
"preview": "name: Close stale issues with \"not bug\" label\n\non:\n schedule:\n - cron: '0 0 * * *'\n\npermissions:\n contents: write\n "
},
{
"path": ".github/workflows/release.yml",
"chars": 484,
"preview": "name: release\n\non:\n push:\n tags:\n - '*'\n\njobs:\n jsr:\n name: publish-to-jsr\n runs-on: ubuntu-latest\n\n "
},
{
"path": ".gitignore",
"chars": 2137,
"preview": "dist\nsandbox\n\n# Cloudflare Workers\nworker\n.wrangler\n\n# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nle"
},
{
"path": ".gitpod.yml",
"chars": 171,
"preview": "tasks:\n - name: Setup\n init: bun install --frozen-lockfile\nimage:\n file: ./.devcontainer/Dockerfile\nvscode:\n exten"
},
{
"path": ".prettierrc",
"chars": 154,
"preview": "{\n \"printWidth\": 100,\n \"trailingComma\": \"es5\",\n \"tabWidth\": 2,\n \"semi\": false,\n \"singleQuote\": true,\n \"jsxSingleQu"
},
{
"path": ".tool-versions",
"chars": 37,
"preview": "nodejs 24.7.0\nbun 1.2.19\ndeno 2.4.5\n"
},
{
"path": ".vitest.config/setup-vitest.ts",
"chars": 901,
"preview": "import * as nodeCrypto from 'node:crypto'\nimport { vi } from 'vitest'\n\n/**\n * crypto\n */\nif (!globalThis.crypto) {\n vi."
},
{
"path": ".vscode/extensions.json",
"chars": 55,
"preview": "{\n \"recommendations\": [\"EditorConfig.EditorConfig\"]\n}\n"
},
{
"path": ".vscode/settings.json",
"chars": 244,
"preview": "{\n \"deno.enable\": false,\n \"eslint.validate\": [\"javascript\", \"javascriptreact\", \"typescript\", \"typescriptreact\"],\n \"ed"
},
{
"path": "LICENSE",
"chars": 1101,
"preview": "MIT License\n\nCopyright (c) 2021 - present, Yusuke Wada and Hono contributors\n\nPermission is hereby granted, free of char"
},
{
"path": "README.md",
"chars": 3621,
"preview": "<div align=\"center\">\n <a href=\"https://hono.dev\">\n <img src=\"https://raw.githubusercontent.com/honojs/hono/main/docs"
},
{
"path": "benchmarks/deno/.gitignore",
"chars": 9,
"preview": "*.sqlite\n"
},
{
"path": "benchmarks/deno/.vscode/settings.json",
"chars": 185,
"preview": "{\n \"eslint.validate\": [\"javascript\", \"javascriptreact\", \"typescript\", \"typescriptreact\"],\n \"editor.codeActionsOnSave\":"
},
{
"path": "benchmarks/deno/fast.ts",
"chars": 666,
"preview": "import fast from 'https://deno.land/x/fast@4.0.0-beta.1/mod.ts'\nimport type { Context } from 'https://deno.land/x/fast@4"
},
{
"path": "benchmarks/deno/faster.ts",
"chars": 647,
"preview": "import { res, Server } from 'https://deno.land/x/faster@v5.7/mod.ts'\nconst app = new Server()\n\napp.get('/user', () => {}"
},
{
"path": "benchmarks/deno/hono.ts",
"chars": 883,
"preview": "import { Hono } from '../../src/index.ts'\nimport { RegExpRouter } from '../../src/router/reg-exp-router/index.ts'\n\nconst"
},
{
"path": "benchmarks/deno/magalo.ts",
"chars": 599,
"preview": "import { Megalo } from 'https://deno.land/x/megalo@v0.3.0/mod.ts'\n\nconst app = new Megalo()\n\napp.get('/user', () => {})\n"
},
{
"path": "benchmarks/deno/oak.ts",
"chars": 740,
"preview": "import { Application, Router } from 'https://deno.land/x/oak@v10.5.1/mod.ts'\n\nconst router = new Router()\n\nrouter.get('/"
},
{
"path": "benchmarks/deno/opine.ts",
"chars": 571,
"preview": "import { opine } from 'https://deno.land/x/opine@2.2.0/mod.ts'\n\nconst app = opine()\n\napp.get('/user', () => {})\napp.get("
},
{
"path": "benchmarks/handle-event/index.js",
"chars": 5314,
"preview": "import Benchmark from 'benchmark'\nimport { makeEdgeEnv } from 'edge-mock'\nimport { Router as IttyRouter } from 'itty-rou"
},
{
"path": "benchmarks/handle-event/package.json",
"chars": 555,
"preview": "{\n \"name\": \"hono-benchmark\",\n \"version\": \"0.0.1\",\n \"description\": \"\",\n \"main\": \"index.js\",\n \"scripts\": {\n \"test\""
},
{
"path": "benchmarks/http-server/.gitignore",
"chars": 38,
"preview": ".benchmark-temp/\nbenchmark-results.md\n"
},
{
"path": "benchmarks/http-server/README.md",
"chars": 392,
"preview": "# Hono HTTP Benchmark\n\nHTTP performance benchmarking tool that compares main vs current versions.\n\n## Usage\n\n### In Pull"
},
{
"path": "benchmarks/http-server/benchmark.ts",
"chars": 10221,
"preview": "/**\n * Hono HTTP Performance Benchmark\n *\n * Inspired by https://github.com/SaltyAom/bun-http-framework-benchmark\n *\n * "
},
{
"path": "benchmarks/jsx/package.json",
"chars": 867,
"preview": "{\n \"name\": \"jsx\",\n \"version\": \"1.0.0\",\n \"main\": \"index.js\",\n \"scripts\": {\n \"bench:node\": \"esbuild --bundle src/be"
},
{
"path": "benchmarks/jsx/src/benchmark.ts",
"chars": 916,
"preview": "import { Suite } from 'benchmark'\nimport { parse } from 'node-html-parser'\n\nimport { render as renderHono } from './hono"
},
{
"path": "benchmarks/jsx/src/hono.ts",
"chars": 155,
"preview": "import { jsx, Fragment } from '../../../src/jsx'\nimport { buildPage } from './page'\n\nexport const render = () => buildPa"
},
{
"path": "benchmarks/jsx/src/nano.ts",
"chars": 157,
"preview": "import { h, Fragment, renderSSR } from 'nano-jsx'\nimport { buildPage } from './page'\n\nexport const render = () => render"
},
{
"path": "benchmarks/jsx/src/page-react.tsx",
"chars": 1070,
"preview": "/** @jsx jsx */\n/** @jsxFrag Fragment */\n\nexport const buildPage = ({ jsx, Fragment }: { jsx: any; Fragment: any }) => {"
},
{
"path": "benchmarks/jsx/src/page.tsx",
"chars": 1034,
"preview": "/** @jsx jsx */\n/** @jsxFrag Fragment */\n\nexport const buildPage = ({ jsx, Fragment }: { jsx: any; Fragment: any }) => {"
},
{
"path": "benchmarks/jsx/src/preact.ts",
"chars": 215,
"preview": "import { h, Fragment } from 'preact'\nimport { renderToString } from 'preact-render-to-string'\nimport { buildPage } from "
},
{
"path": "benchmarks/jsx/src/react-jsx/benchmark.ts",
"chars": 916,
"preview": "import { Suite } from 'benchmark'\nimport { parse } from 'node-html-parser'\n\nimport { render as renderHono } from './hono"
},
{
"path": "benchmarks/jsx/src/react-jsx/hono.ts",
"chars": 94,
"preview": "import { buildPage } from './page-hono'\n\nexport const render = () => buildPage()().toString()\n"
},
{
"path": "benchmarks/jsx/src/react-jsx/nano.ts",
"chars": 133,
"preview": "import { renderSSR } from 'nano-jsx'\nimport { buildPage } from './page-nano.tsx'\n\nexport const render = () => renderSSR("
},
{
"path": "benchmarks/jsx/src/react-jsx/page-hono.tsx",
"chars": 992,
"preview": "/** @jsxImportSource ../../../../src/jsx **/\n\nexport const buildPage = () => {\n const Content = () => (\n <>\n <p"
},
{
"path": "benchmarks/jsx/src/react-jsx/page-nano.tsx",
"chars": 985,
"preview": "/** @jsxImportSource nano-jsx/lib **/\n\nexport const buildPage = () => {\n const Content = () => (\n <>\n <p id='a'"
},
{
"path": "benchmarks/jsx/src/react-jsx/page-preact.tsx",
"chars": 979,
"preview": "/** @jsxImportSource preact **/\n\nexport const buildPage = () => {\n const Content = () => (\n <>\n <p id='a' class"
},
{
"path": "benchmarks/jsx/src/react-jsx/page-react.tsx",
"chars": 1014,
"preview": "/** @jsxImportSource react **/\n\nexport const buildPage = () => {\n const Content = () => (\n <>\n <p id='a' classN"
},
{
"path": "benchmarks/jsx/src/react-jsx/preact.ts",
"chars": 165,
"preview": "import { renderToString } from 'preact-render-to-string'\nimport { buildPage } from './page-preact'\n\nexport const render "
},
{
"path": "benchmarks/jsx/src/react-jsx/react.ts",
"chars": 161,
"preview": "import { renderToString } from 'react-dom/server'\nimport { buildPage } from './page-react.tsx'\n\nexport const render = ()"
},
{
"path": "benchmarks/jsx/src/react-jsx/tsconfig.json",
"chars": 54,
"preview": "{\n \"compilerOptions\": {\n \"jsx\": \"react-jsx\"\n }\n}\n"
},
{
"path": "benchmarks/jsx/src/react.ts",
"chars": 237,
"preview": "import { createElement, Fragment } from 'react'\nimport { renderToString } from 'react-dom/server'\nimport { buildPage } f"
},
{
"path": "benchmarks/jsx/tsconfig.json",
"chars": 50,
"preview": "{\n \"compilerOptions\": {\n \"jsx\": \"react\"\n }\n}\n"
},
{
"path": "benchmarks/query-param/package.json",
"chars": 307,
"preview": "{\n \"scripts\": {\n \"bench:node\": \"tsx ./src/bench.mts\",\n \"bench:bun\": \"bun run ./src/bench.mts\"\n },\n \"license\": \""
},
{
"path": "benchmarks/query-param/src/bench.mts",
"chars": 1423,
"preview": "import { run, group, bench } from 'mitata'\nimport { getQueryStrings } from '../../../src/utils/url'\nimport fastQuerystri"
},
{
"path": "benchmarks/query-param/src/fast-querystring.mts",
"chars": 357,
"preview": "import { parse } from 'fast-querystring'\n\nconst getQueryStringFromURL = (url: string): string => {\n const queryIndex = "
},
{
"path": "benchmarks/query-param/src/hono.mts",
"chars": 123,
"preview": "import { getQueryParam } from '../../../src/utils/url'\n\nexport default (url, key?) => {\n return getQueryParam(url, key)"
},
{
"path": "benchmarks/query-param/src/qs.mts",
"chars": 338,
"preview": "import qs from 'qs'\n\nconst getQueryStringFromURL = (url: string): string => {\n const queryIndex = url.indexOf('?', 8)\n "
},
{
"path": "benchmarks/routers/README.md",
"chars": 805,
"preview": "# Router benchmarks\n\nBenchmark of the most commonly used HTTP routers.\n\nTested routes:\n\n- [find-my-way](https://github.c"
},
{
"path": "benchmarks/routers/package.json",
"chars": 618,
"preview": "{\n \"scripts\": {\n \"bench:node\": \"tsx ./src/bench.mts\",\n \"bench:bun\": \"bun run ./src/bench.mts\",\n \"bench-include"
},
{
"path": "benchmarks/routers/src/bench-includes-init.mts",
"chars": 3593,
"preview": "import MedleyRouter from '@medley/router'\nimport type { HTTPMethod } from 'find-my-way'\nimport findMyWay from 'find-my-w"
},
{
"path": "benchmarks/routers/src/bench.mts",
"chars": 2068,
"preview": "import { run, bench, group, summary } from 'mitata'\nimport { expressRouter } from './express.mts'\nimport { findMyWayRout"
},
{
"path": "benchmarks/routers/src/express.mts",
"chars": 541,
"preview": "import routerFunc from 'express/lib/router/index.js'\nimport type { RouterInterface } from './tool.mts'\nimport { routes, "
},
{
"path": "benchmarks/routers/src/find-my-way.mts",
"chars": 470,
"preview": "import type { HTTPMethod } from 'find-my-way'\nimport findMyWay from 'find-my-way'\nimport type { RouterInterface } from '"
},
{
"path": "benchmarks/routers/src/hono.mts",
"chars": 903,
"preview": "import { PatternRouter } from '../../../src/router/pattern-router/index.ts'\nimport { RegExpRouter } from '../../../src/r"
},
{
"path": "benchmarks/routers/src/koa-router.mts",
"chars": 504,
"preview": "import KoaRouter from 'koa-router'\nimport type { RouterInterface } from './tool.mts'\nimport { routes, handler } from './"
},
{
"path": "benchmarks/routers/src/koa-tree-router.mts",
"chars": 511,
"preview": "import KoaRouter from 'koa-tree-router'\nimport type { RouterInterface } from './tool.mts'\nimport { routes, handler } fro"
},
{
"path": "benchmarks/routers/src/medley-router.mts",
"chars": 471,
"preview": "import Router from '@medley/router'\nimport type { RouterInterface } from './tool.mts'\nimport { routes, handler } from '."
},
{
"path": "benchmarks/routers/src/memoirist.mts",
"chars": 401,
"preview": "import { Memoirist } from 'memoirist'\nimport type { RouterInterface } from './tool.mts'\nimport { routes, handler } from "
},
{
"path": "benchmarks/routers/src/radix3.mts",
"chars": 371,
"preview": "import { createRouter } from 'radix3'\nimport type { RouterInterface } from './tool.mts'\nimport { routes, handler } from "
},
{
"path": "benchmarks/routers/src/rou3.mts",
"chars": 480,
"preview": "import { addRoute, createRouter, findRoute } from 'rou3'\nimport type { RouterInterface } from './tool.mts'\nimport { hand"
},
{
"path": "benchmarks/routers/src/tool.mts",
"chars": 804,
"preview": "export const handler = () => {}\n\nexport type Route = {\n method: 'GET' | 'POST'\n path: string\n}\n\nexport interface Route"
},
{
"path": "benchmarks/routers/src/trek-router.mts",
"chars": 400,
"preview": "import TrekRouter from 'trek-router'\nimport type { RouterInterface } from './tool.mts'\nimport { routes, handler } from '"
},
{
"path": "benchmarks/routers/tsconfig.json",
"chars": 149,
"preview": "{\n \"compilerOptions\": {\n \"allowImportingTsExtensions\": true,\n \"esModuleInterop\": true,\n \"module\": \"NodeNext\"\n "
},
{
"path": "benchmarks/routers-deno/.vscode/settings.json",
"chars": 185,
"preview": "{\n \"eslint.validate\": [\"javascript\", \"javascriptreact\", \"typescript\", \"typescriptreact\"],\n \"editor.codeActionsOnSave\":"
},
{
"path": "benchmarks/routers-deno/README.md",
"chars": 694,
"preview": "# Router benchmarks\n\nBenchmark of the most commonly used HTTP routers.\n\nTested routes:\n\n- [find-my-way](https://github.c"
},
{
"path": "benchmarks/routers-deno/deno.json",
"chars": 56,
"preview": "{\n \"imports\": {\n \"npm/\": \"https://unpkg.com/\"\n }\n}\n"
},
{
"path": "benchmarks/routers-deno/src/bench.mts",
"chars": 1743,
"preview": "import { run, bench, group } from 'npm:mitata'\nimport { findMyWayRouter } from './find-my-way.mts'\nimport { regExpRouter"
},
{
"path": "benchmarks/routers-deno/src/find-my-way.mts",
"chars": 478,
"preview": "import type { HTTPMethod } from 'npm:find-my-way'\nimport findMyWay from 'npm:find-my-way'\nimport type { RouterInterface "
},
{
"path": "benchmarks/routers-deno/src/hono.mts",
"chars": 903,
"preview": "import { PatternRouter } from '../../../src/router/pattern-router/index.ts'\nimport { RegExpRouter } from '../../../src/r"
},
{
"path": "benchmarks/routers-deno/src/koa-router.mts",
"chars": 508,
"preview": "import KoaRouter from 'npm:koa-router'\nimport type { RouterInterface } from './tool.mts'\nimport { routes, handler } from"
},
{
"path": "benchmarks/routers-deno/src/koa-tree-router.mts",
"chars": 515,
"preview": "import KoaRouter from 'npm:koa-tree-router'\nimport type { RouterInterface } from './tool.mts'\nimport { routes, handler }"
},
{
"path": "benchmarks/routers-deno/src/medley-router.mts",
"chars": 475,
"preview": "import Router from 'npm:@medley/router'\nimport type { RouterInterface } from './tool.mts'\nimport { routes, handler } fro"
},
{
"path": "benchmarks/routers-deno/src/tool.mts",
"chars": 804,
"preview": "export const handler = () => {}\n\nexport type Route = {\n method: 'GET' | 'POST'\n path: string\n}\n\nexport interface Route"
},
{
"path": "benchmarks/routers-deno/src/trek-router.mts",
"chars": 404,
"preview": "import TrekRouter from 'npm:trek-router'\nimport type { RouterInterface } from './tool.mts'\nimport { routes, handler } fr"
},
{
"path": "benchmarks/utils/.gitignore",
"chars": 20,
"preview": "yarn.lock\nbun.lockb\n"
},
{
"path": "benchmarks/utils/package.json",
"chars": 75,
"preview": "{\n \"type\": \"module\",\n \"devDependencies\": {\n \"mitata\": \"^0.1.11\"\n }\n}\n"
},
{
"path": "benchmarks/utils/src/get-path.ts",
"chars": 2119,
"preview": "import { run, group, bench } from 'mitata'\n\nbench('noop', () => {})\n\nconst request = new Request('http://localhost/about"
},
{
"path": "benchmarks/utils/src/loop.js",
"chars": 593,
"preview": "import { run, group, bench } from 'mitata'\n\nconst arr = new Array(100000).fill(Math.random())\n\nbench('noop', () => {})\n\n"
},
{
"path": "benchmarks/webapp/.gitignore",
"chars": 10,
"preview": "yarn.lock\n"
},
{
"path": "benchmarks/webapp/hono.js",
"chars": 782,
"preview": "import { Hono } from '../../dist/hono'\n//import { Hono } from 'hono'\n\nconst hono = new Hono()\nhono.get('/user', (c) => c"
},
{
"path": "benchmarks/webapp/itty-router.js",
"chars": 1040,
"preview": "import { Router } from 'itty-router'\n\nconst ittyRouter = Router()\nittyRouter.get('/user', () => new Response('User'))\nit"
},
{
"path": "benchmarks/webapp/package.json",
"chars": 388,
"preview": "{\n \"name\": \"webapp\",\n \"version\": \"1.0.0\",\n \"main\": \"index.js\",\n \"scripts\": {\n \"start:hono\": \"wrangler dev hono.js"
},
{
"path": "benchmarks/webapp/sunder.js",
"chars": 1207,
"preview": "import { Sunder, Router } from 'sunder'\n\nconst sunderRouter = new Router()\nsunderRouter.get('/user', (ctx) => {\n ctx.re"
},
{
"path": "build/build.ts",
"chars": 3047,
"preview": "/*\n This script is heavily inspired by `built.ts` used in @kaze-style/react.\n https://github.com/taishinaritomi/kaze-s"
},
{
"path": "build/remove-private-fields.test.ts",
"chars": 932,
"preview": "/// <reference types=\"vitest/globals\" />\n\nimport { parseSync } from 'oxc-parser'\nimport { removePrivateFieldFromSourceCo"
},
{
"path": "build/remove-private-fields.ts",
"chars": 1728,
"preview": "import type { PropertyDefinition, ParseResult } from 'oxc-parser'\nimport { parseSync, Visitor } from 'oxc-parser'\nimport"
},
{
"path": "build/validate-exports.test.ts",
"chars": 756,
"preview": "/// <reference types=\"vitest/globals\" />\n\nimport { validateExports } from './validate-exports'\n\nconst mockExports1 = {\n "
},
{
"path": "build/validate-exports.ts",
"chars": 952,
"preview": "export const validateExports = (\n source: Record<string, unknown>,\n target: Record<string, unknown>,\n fileName: strin"
},
{
"path": "bunfig.toml",
"chars": 92,
"preview": "[test]\ncoverage = true\ncoverageReporter = [\"text\", \"lcov\"]\ncoverageDir = \"coverage/raw/bun\"\n"
},
{
"path": "codecov.yml",
"chars": 446,
"preview": "# Edit \"test.coverage.exclude\" option in vitest.config.ts to exclude specific files from coverage reports.\n# We can also"
},
{
"path": "docs/CODE_OF_CONDUCT.md",
"chars": 5229,
"preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to make participa"
},
{
"path": "docs/CONTRIBUTING.md",
"chars": 1955,
"preview": "# Contribution Guide\n\nContributions Welcome! We will be glad for your help.\nYou can contribute in the following ways.\n\n-"
},
{
"path": "docs/MIGRATION.md",
"chars": 7995,
"preview": "# Migration Guide\n\n## v4.3.11 to v4.4.0\n\n### `deno.land/x` to JSR\n\nThere is no breaking change, but we no longer publish"
},
{
"path": "eslint.config.mjs",
"chars": 3885,
"preview": "import baseConfig from '@hono/eslint-config'\nimport { defineConfig, globalIgnores } from 'eslint/config'\n\n// Disable all"
},
{
"path": "jsr.json",
"chars": 5354,
"preview": "{\n \"name\": \"@hono/hono\",\n \"version\": \"0.0.0\",\n \"compilerOptions\": {\n \"lib\": [\"dom\", \"dom.iterable\", \"deno.ns\"]\n }"
},
{
"path": "package.cjs.json",
"chars": 25,
"preview": "{\n \"type\": \"commonjs\"\n}\n"
},
{
"path": "package.json",
"chars": 23131,
"preview": "{\n \"name\": \"hono\",\n \"version\": \"4.12.8\",\n \"description\": \"Web framework built on Web Standards\",\n \"main\": \"dist/cjs/"
},
{
"path": "perf-measures/.octocov.consolidated.perf-measures.main.yml",
"chars": 429,
"preview": "locale: 'en'\nrepository: ${GITHUB_REPOSITORY}/perf-measures\ncoverage:\n if: false\ncodeToTestRatio:\n if: false\ntestExecu"
},
{
"path": "perf-measures/.octocov.consolidated.perf-measures.yml",
"chars": 422,
"preview": "locale: 'en'\nrepository: ${GITHUB_REPOSITORY}/perf-measures\ncoverage:\n if: false\ncodeToTestRatio:\n if: false\ntestExecu"
},
{
"path": "perf-measures/bundle-check/.gitignore",
"chars": 40,
"preview": "generated\n!generated/.gitkeep\nsize.json\n"
},
{
"path": "perf-measures/bundle-check/scripts/check-bundle-size.ts",
"chars": 1145,
"preview": "import * as esbuild from 'esbuild'\nimport * as fs from 'node:fs'\nimport * as os from 'os'\nimport * as path from 'path'\n\n"
},
{
"path": "perf-measures/type-check/.gitignore",
"chars": 65,
"preview": "generated\n!generated/.gitkeep\ntrace\n*result.txt\ndiagnostics.json\n"
},
{
"path": "perf-measures/type-check/client.ts",
"chars": 179,
"preview": "import { hc } from '../../src/client'\nimport type { app } from './generated/app'\n\n// eslint-disable-next-line @typescrip"
},
{
"path": "perf-measures/type-check/scripts/generate-app.ts",
"chars": 592,
"preview": "import { writeFile } from 'node:fs'\nimport * as path from 'node:path'\n\nconst count = 200\n\nconst generateRoutes = (count:"
},
{
"path": "perf-measures/type-check/scripts/process-results.ts",
"chars": 1322,
"preview": "import * as readline from 'node:readline'\n\nasync function main() {\n const rl = readline.createInterface({\n input: pr"
},
{
"path": "perf-measures/type-check/scripts/tsconfig.json",
"chars": 118,
"preview": "{\n \"extends\": \"../../../tsconfig.base.json\",\n \"compilerOptions\": {\n \"module\": \"esnext\",\n \"noEmit\": true\n }\n}\n"
},
{
"path": "perf-measures/type-check/tsconfig.build.json",
"chars": 192,
"preview": "{\n \"extends\": \"../../tsconfig.base.json\",\n \"compilerOptions\": {\n \"noEmit\": true\n },\n \"exclude\": [\"dist\", \"scripts"
},
{
"path": "runtime-tests/bun/.static/plain.txt",
"chars": 6,
"preview": "Bun!!\n"
},
{
"path": "runtime-tests/bun/color.test.ts",
"chars": 547,
"preview": "import { expect, test } from 'bun:test'\n\ntest('Bun.build compatibility test', async () => {\n try {\n const result = a"
},
{
"path": "runtime-tests/bun/index.test.tsx",
"chars": 13990,
"preview": "import { afterAll, afterEach, beforeEach, describe, expect, it, vi } from 'vitest'\nimport fs from 'fs/promises'\nimport p"
},
{
"path": "runtime-tests/bun/static/download",
"chars": 9,
"preview": "download\n"
},
{
"path": "runtime-tests/bun/static/hello.world/index.html",
"chars": 3,
"preview": "Hi\n"
},
{
"path": "runtime-tests/bun/static/helloworld/index.html",
"chars": 3,
"preview": "Hi\n"
},
{
"path": "runtime-tests/bun/static/plain.txt",
"chars": 5,
"preview": "Bun!\n"
},
{
"path": "runtime-tests/bun/static-absolute-root/plain.txt",
"chars": 5,
"preview": "Bun!\n"
},
{
"path": "runtime-tests/bun/tsconfig.json",
"chars": 245,
"preview": "{\n \"extends\": \"../../tsconfig.base.json\",\n \"compilerOptions\": {\n \"jsx\": \"react-jsx\",\n \"jsxImportSource\": \"hono/j"
},
{
"path": "runtime-tests/deno/.static/plain.txt",
"chars": 7,
"preview": "Deno!!\n"
},
{
"path": "runtime-tests/deno/.vscode/settings.json",
"chars": 191,
"preview": "{\n \"eslint.validate\": [\"javascript\", \"javascriptreact\", \"typescript\", \"typescriptreact\"],\n \"editor.codeActionsOnSave\":"
},
{
"path": "runtime-tests/deno/deno.json",
"chars": 382,
"preview": "{\n \"compilerOptions\": {\n \"jsx\": \"react-jsx\",\n \"jsxImportSource\": \"hono/jsx\",\n \"lib\": [\"deno.ns\", \"dom\", \"dom.i"
},
{
"path": "runtime-tests/deno/hono.test.ts",
"chars": 1051,
"preview": "import { assertEquals } from '@std/assert'\n\nimport { Context } from '../../src/context.ts'\nimport { env, getRuntimeKey }"
},
{
"path": "runtime-tests/deno/middleware.test.tsx",
"chars": 5994,
"preview": "import { assertEquals, assertMatch } from '@std/assert'\nimport { dirname, fromFileUrl } from '@std/path'\nimport { assert"
},
{
"path": "runtime-tests/deno/ssg.test.tsx",
"chars": 1480,
"preview": "import { assertEquals } from '@std/assert'\nimport { toSSG } from '../../src/adapter/deno/ssg.ts'\nimport { Hono } from '."
},
{
"path": "runtime-tests/deno/static/download",
"chars": 9,
"preview": "download\n"
},
{
"path": "runtime-tests/deno/static/hello.world/index.html",
"chars": 3,
"preview": "Hi\n"
},
{
"path": "runtime-tests/deno/static/helloworld/index.html",
"chars": 3,
"preview": "Hi\n"
},
{
"path": "runtime-tests/deno/static/plain.txt",
"chars": 6,
"preview": "Deno!\n"
},
{
"path": "runtime-tests/deno/static-absolute-root/plain.txt",
"chars": 6,
"preview": "Deno!\n"
},
{
"path": "runtime-tests/deno/stream.test.ts",
"chars": 3110,
"preview": "import { assertEquals } from '@std/assert'\nimport { stream, streamSSE } from '../../src/helper/streaming/index.ts'\nimpor"
},
{
"path": "runtime-tests/deno-jsx/deno.precompile.json",
"chars": 295,
"preview": "{\n \"compilerOptions\": {\n \"jsx\": \"precompile\",\n \"jsxImportSource\": \"hono/jsx\",\n \"lib\": [\"deno.ns\", \"dom\", \"dom."
},
{
"path": "runtime-tests/deno-jsx/deno.react-jsx.json",
"chars": 294,
"preview": "{\n \"compilerOptions\": {\n \"jsx\": \"react-jsx\",\n \"jsxImportSource\": \"hono/jsx\",\n \"lib\": [\"deno.ns\", \"dom\", \"dom.i"
},
{
"path": "runtime-tests/deno-jsx/jsx.test.tsx",
"chars": 5988,
"preview": "/** @jsxImportSource ../../src/jsx */\nimport { assertEquals } from '@std/assert'\nimport { Style, css } from '../../src/h"
},
{
"path": "runtime-tests/fastly/index.test.ts",
"chars": 3287,
"preview": "import { createHash } from 'crypto'\nimport { getRuntimeKey } from '../../src/helper/adapter'\nimport { Hono } from '../.."
},
{
"path": "runtime-tests/fastly/tsconfig.json",
"chars": 191,
"preview": "{\n \"extends\": \"../../tsconfig.base.json\",\n \"compilerOptions\": {\n \"noEmit\": true,\n \"types\": [\"vitest/globals\"]\n "
},
{
"path": "runtime-tests/fastly/vitest.config.ts",
"chars": 223,
"preview": "import fastlyCompute from 'vite-plugin-fastly-js-compute'\nimport { defineProject } from 'vitest/config'\n\nexport default "
},
{
"path": "runtime-tests/lambda/index.test.ts",
"chars": 33953,
"preview": "import { Readable } from 'stream'\nimport {\n ALBProcessor,\n EventV1Processor,\n EventV2Processor,\n getProcessor,\n han"
},
{
"path": "runtime-tests/lambda/mock.ts",
"chars": 1213,
"preview": "import { vi } from 'vitest'\nimport type { LambdaEvent } from '../../src/adapter/aws-lambda/handler'\nimport type { Lambda"
},
{
"path": "runtime-tests/lambda/stream-mock.ts",
"chars": 1347,
"preview": "import { vi } from 'vitest'\nimport { Writable } from 'node:stream'\nimport type {\n APIGatewayProxyEvent,\n APIGatewayPro"
},
{
"path": "runtime-tests/lambda/stream.test.ts",
"chars": 2055,
"preview": "import { streamHandle } from '../../src/adapter/aws-lambda/handler'\nimport type { LambdaEvent } from '../../src/adapter/"
},
{
"path": "runtime-tests/lambda/tsconfig.json",
"chars": 191,
"preview": "{\n \"extends\": \"../../tsconfig.base.json\",\n \"compilerOptions\": {\n \"noEmit\": true,\n \"types\": [\"vitest/globals\"]\n "
},
{
"path": "runtime-tests/lambda/vitest.config.ts",
"chars": 173,
"preview": "import { defineProject } from 'vitest/config'\n\nexport default defineProject({\n test: {\n env: {\n NAME: 'Node',\n "
},
{
"path": "runtime-tests/lambda-edge/index.test.ts",
"chars": 32217,
"preview": "import type {\n Callback,\n CloudFrontConfig,\n CloudFrontRequest,\n CloudFrontResponse,\n} from '../../src/adapter/lambd"
},
{
"path": "runtime-tests/lambda-edge/tsconfig.json",
"chars": 191,
"preview": "{\n \"extends\": \"../../tsconfig.base.json\",\n \"compilerOptions\": {\n \"noEmit\": true,\n \"types\": [\"vitest/globals\"]\n "
},
{
"path": "runtime-tests/lambda-edge/vitest.config.ts",
"chars": 178,
"preview": "import { defineProject } from 'vitest/config'\n\nexport default defineProject({\n test: {\n env: {\n NAME: 'Node',\n "
},
{
"path": "runtime-tests/node/index.test.ts",
"chars": 8220,
"preview": "import { createAdaptorServer, serve } from '@hono/node-server'\nimport * as undici from 'undici'\nimport { once } from 'no"
},
{
"path": "runtime-tests/node/tsconfig.json",
"chars": 191,
"preview": "{\n \"extends\": \"../../tsconfig.base.json\",\n \"compilerOptions\": {\n \"noEmit\": true,\n \"types\": [\"vitest/globals\"]\n "
},
{
"path": "runtime-tests/node/vitest.config.ts",
"chars": 171,
"preview": "import { defineProject } from 'vitest/config'\n\nexport default defineProject({\n test: {\n env: {\n NAME: 'Node',\n "
},
{
"path": "runtime-tests/workerd/index.test.ts",
"chars": 2749,
"preview": "import { unstable_dev } from 'wrangler'\nimport type { Unstable_DevWorker } from 'wrangler'\nimport { WebSocket } from 'ws"
},
{
"path": "runtime-tests/workerd/index.ts",
"chars": 686,
"preview": "import { upgradeWebSocket } from '../../src/adapter/cloudflare-workers'\nimport { env, getRuntimeKey } from '../../src/he"
},
{
"path": "runtime-tests/workerd/tsconfig.json",
"chars": 191,
"preview": "{\n \"extends\": \"../../tsconfig.base.json\",\n \"compilerOptions\": {\n \"noEmit\": true,\n \"types\": [\"vitest/globals\"]\n "
},
{
"path": "runtime-tests/workerd/vitest.config.ts",
"chars": 136,
"preview": "import { defineProject } from 'vitest/config'\n\nexport default defineProject({\n test: {\n globals: true,\n name: 'wo"
},
{
"path": "src/adapter/aws-lambda/conninfo.test.ts",
"chars": 3142,
"preview": "import { Context } from '../../context'\nimport { getConnInfo } from './conninfo'\n\ndescribe('getConnInfo', () => {\n desc"
},
{
"path": "src/adapter/aws-lambda/conninfo.ts",
"chars": 1877,
"preview": "import type { Context } from '../../context'\nimport type { GetConnInfo } from '../../helper/conninfo'\nimport type {\n Ap"
},
{
"path": "src/adapter/aws-lambda/handler.test.ts",
"chars": 12015,
"preview": "import type { LambdaEvent, LatticeProxyEventV2 } from './handler'\nimport { getProcessor, isContentEncodingBinary, defaul"
},
{
"path": "src/adapter/aws-lambda/handler.ts",
"chars": 20196,
"preview": "import type { Hono } from '../../hono'\nimport type { Env, Schema } from '../../types'\nimport { decodeBase64, encodeBase6"
},
{
"path": "src/adapter/aws-lambda/index.ts",
"chars": 365,
"preview": "/**\n * @module\n * AWS Lambda Adapter for Hono.\n */\n\nexport { handle, streamHandle, defaultIsContentTypeBinary } from './"
},
{
"path": "src/adapter/aws-lambda/types.ts",
"chars": 3355,
"preview": "/* eslint-disable @typescript-eslint/no-explicit-any */\n\nexport interface CognitoIdentity {\n cognitoIdentityId: string\n"
},
{
"path": "src/adapter/bun/conninfo.test.ts",
"chars": 2456,
"preview": "import { Context } from '../../context'\nimport type { AddressType } from '../../helper/conninfo'\nimport { getConnInfo } "
},
{
"path": "src/adapter/bun/conninfo.ts",
"chars": 1047,
"preview": "import type { Context } from '../..'\nimport type { GetConnInfo } from '../../helper/conninfo'\nimport { getBunServer } fr"
},
{
"path": "src/adapter/bun/index.ts",
"chars": 373,
"preview": "/**\n * @module\n * Bun Adapter for Hono.\n */\n\nexport { serveStatic } from './serve-static'\nexport { bunFileSystemModule, "
},
{
"path": "src/adapter/bun/serve-static.ts",
"chars": 953,
"preview": "/* eslint-disable @typescript-eslint/ban-ts-comment */\nimport { stat } from 'node:fs/promises'\nimport { join } from 'nod"
},
{
"path": "src/adapter/bun/server.test.ts",
"chars": 423,
"preview": "import { Context } from '../../context'\nimport { getBunServer } from './server'\n\ndescribe('getBunServer', () => {\n it('"
},
{
"path": "src/adapter/bun/server.ts",
"chars": 364,
"preview": "/**\n * Getting Bun Server Object for Bun adapters\n * @module\n */\nimport type { Context } from '../../context'\n\n/**\n * Ge"
},
{
"path": "src/adapter/bun/ssg.ts",
"chars": 706,
"preview": "/* eslint-disable @typescript-eslint/ban-ts-comment */\nimport { toSSG as baseToSSG } from '../../helper/ssg'\nimport type"
},
{
"path": "src/adapter/bun/websocket.test.ts",
"chars": 3525,
"preview": "import { Context } from '../../context'\nimport type { BunWebSocketData, BunServerWebSocket } from './websocket'\nimport {"
},
{
"path": "src/adapter/bun/websocket.ts",
"chars": 2984,
"preview": "import type { UpgradeWebSocket, WSEvents, WSMessageReceive } from '../../helper/websocket'\nimport { createWSMessageEvent"
},
{
"path": "src/adapter/cloudflare-pages/conninfo.test.ts",
"chars": 756,
"preview": "import { Context } from '../../context'\nimport { getConnInfo } from './conninfo'\n\ndescribe('getConnInfo', () => {\n it('"
},
{
"path": "src/adapter/cloudflare-pages/conninfo.ts",
"chars": 632,
"preview": "import type { GetConnInfo } from '../../helper/conninfo'\n\n/**\n * Get connection information from Cloudflare Pages\n * @pa"
},
{
"path": "src/adapter/cloudflare-pages/handler.test.ts",
"chars": 9271,
"preview": "import { getCookie } from '../../helper/cookie'\nimport { Hono } from '../../hono'\nimport { HTTPException } from '../../h"
},
{
"path": "src/adapter/cloudflare-pages/handler.ts",
"chars": 3459,
"preview": "import { Context } from '../../context'\nimport type { Hono } from '../../hono'\nimport { HTTPException } from '../../http"
},
{
"path": "src/adapter/cloudflare-pages/index.ts",
"chars": 211,
"preview": "/**\n * @module\n * Cloudflare Pages Adapter for Hono.\n */\n\nexport { handle, handleMiddleware, serveStatic } from './handl"
},
{
"path": "src/adapter/cloudflare-workers/conninfo.test.ts",
"chars": 492,
"preview": "import { Context } from '../../context'\nimport { getConnInfo } from './conninfo'\n\ndescribe('getConnInfo', () => {\n it('"
},
{
"path": "src/adapter/cloudflare-workers/conninfo.ts",
"chars": 175,
"preview": "import type { GetConnInfo } from '../../helper/conninfo'\n\nexport const getConnInfo: GetConnInfo = (c) => ({\n remote: {\n"
},
{
"path": "src/adapter/cloudflare-workers/index.ts",
"chars": 200,
"preview": "/**\n * @module\n * Cloudflare Workers Adapter for Hono.\n */\n\nexport { serveStatic } from './serve-static-module'\nexport {"
},
{
"path": "src/adapter/cloudflare-workers/serve-static-module.ts",
"chars": 368,
"preview": "// For ES module mode\nimport type { Env, MiddlewareHandler } from '../../types'\nimport type { ServeStaticOptions } from "
},
{
"path": "src/adapter/cloudflare-workers/serve-static.test.ts",
"chars": 8003,
"preview": "import type { Context } from '../../context'\nimport { Hono } from '../../hono'\nimport type { Next } from '../../types'\ni"
},
{
"path": "src/adapter/cloudflare-workers/serve-static.ts",
"chars": 1532,
"preview": "import { serveStatic as baseServeStatic } from '../../middleware/serve-static'\nimport type { ServeStaticOptions as BaseS"
},
{
"path": "src/adapter/cloudflare-workers/utils.test.ts",
"chars": 977,
"preview": "import { getContentFromKVAsset } from './utils'\n\n// Mock\nconst store: { [key: string]: string } = {\n 'index.abcdef.html"
},
{
"path": "src/adapter/cloudflare-workers/utils.ts",
"chars": 1312,
"preview": "// __STATIC_CONTENT is KVNamespace\ndeclare const __STATIC_CONTENT: unknown\ndeclare const __STATIC_CONTENT_MANIFEST: stri"
},
{
"path": "src/adapter/cloudflare-workers/websocket.test.ts",
"chars": 1381,
"preview": "import { Hono } from '../..'\nimport { Context } from '../../context'\nimport { upgradeWebSocket } from '.'\n\ndescribe('upg"
},
{
"path": "src/adapter/cloudflare-workers/websocket.ts",
"chars": 1725,
"preview": "import { WSContext, defineWebSocketHelper } from '../../helper/websocket'\nimport type { UpgradeWebSocket, WSEvents, WSRe"
},
{
"path": "src/adapter/deno/conninfo.test.ts",
"chars": 700,
"preview": "import { Context } from '../../context'\nimport { getConnInfo } from './conninfo'\n\ndescribe('getConnInfo', () => {\n it('"
},
{
"path": "src/adapter/deno/conninfo.ts",
"chars": 355,
"preview": "import type { GetConnInfo } from '../../helper/conninfo'\n\n/**\n * Get conninfo with Deno\n * @param c Context\n * @returns "
},
{
"path": "src/adapter/deno/deno.d.ts",
"chars": 2230,
"preview": "declare namespace Deno {\n interface FileHandleLike {\n readonly readable: ReadableStream<Uint8Array>\n }\n\n /**\n * "
},
{
"path": "src/adapter/deno/index.ts",
"chars": 231,
"preview": "/**\n * @module\n * Deno Adapter for Hono.\n */\n\nexport { serveStatic } from './serve-static'\nexport { toSSG, denoFileSyste"
},
{
"path": "src/adapter/deno/serve-static.ts",
"chars": 1061,
"preview": "import { join } from 'node:path'\nimport type { ServeStaticOptions } from '../../middleware/serve-static'\nimport { serveS"
},
{
"path": "src/adapter/deno/ssg.ts",
"chars": 840,
"preview": "import { toSSG as baseToSSG } from '../../helper/ssg/index'\nimport type { FileSystemModule, ToSSGAdaptorInterface } from"
}
]
// ... and 279 more files (download for full content)
About this extraction
This page contains the full source code of the honojs/hono GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 479 files (2.3 MB), approximately 617.9k tokens, and a symbol index with 1444 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.