Honoのコミット記録
What is this?
ただのHonoにコミットされたものを記録していくためのメモ
![ame-x@amex2189](https://res.cloudinary.com/zenn/image/fetch/s--7E6BGTlX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_70/https://storage.googleapis.com/zenn-user-upload/avatar/43b4b572a7.jpeg)
凄い・・・笑
4.3.7以降の変更
内部的な型のリファクタリング
型関係のリファクタリングのようです。特に表立ってのインターフェースが変更されたわけではありません。
Set Cookieが正常生成されない不具合修正
import { setCookie } from "hono/cookie";
const app = new Hono();
app.get("/does-not-work", (c) => {
setCookie(c, "hello", "world");
setCookie(c, "hello", "world2");
const res = new Response("does not work");
return c.body(res.body, res); // <-- this
});
上記のサンプルコード c.body(res.body, res)
のようにレスポンスオブジェクトの初期値を渡してレスポンスを生成する場合にレスポンスヘッダーの Set-Cookie
が正常に返却されないという不具合の修正です。
forwardRef
を使用したコンポーネントで ref
を必ず渡す必要がある不具合修正
hono/jsxの
import { forwardRef } from 'hono/jsx'
const DivComponent = forwardRef((props, ref) => {
return <div {...props} ref={ref} />
})
function Render() {
return <DivComponent /> // <-- type error(ref not specified)
}
上記のサンプルコードのように forwardRef
を使用すると ref
のpropsを渡す必要があるという型になっているが、optionalでも問題ないように型の修正が行われています。
上記の変更で 4.3.8
がリリースされています
4.3.8以降の変更
CreateHandlersInterface
の型定義をexportするように変更
import type { CreateHandlersInterface } from 'hono/factory'
上記のコードで factory.createHandlers
の生成する型を使えるようになったようです。
ALBのマルチバリューヘッダー設定に対応
同キーのクエリパラメータ(http://www.example.com?&myKey=val1&myKey=val2
など)がある場合に multiValueQueryStringParameters
としてAWS Lambdaに送信します。それの対応が行われました。
上記の変更で 4.3.9 がリリースされています
4.3.9以降の変更
Secure Headers Middleware の Node.js依存の解消
Secure Headers Middlewareは nonce
属性に設定する際のbase64値をNode.jsのAPIに依存していましたが、Node.jsに依存しない変更が行われました。この変更でCloudflare WorkersでSecure Headers Middlewareを使用する場合は nodejs_compat
の compatibility_flags
の設定が必要でしたが、不要になります。
4.3.9以降の変更 その2
Status Codeが指定のコードで返らない場合がある不具合の修正
ちょっと特殊ですが HTTPException
を使用した例外をスローした際のStatus Codeが返らず、例外となった元のResponseオブジェクトのStatus Codeで返ってしまうという不具合の修正です。具体的には以下のコード例で400が返らず、200が返ってしまうということの修正が行われました。
app.get("/example-a", (c) => {
throw new HTTPException(400, {
res: new Response("An exception", { status: 200 }),
});
});
304を返す場合のヘッダー設定をHonoから提供
304を返す場合に設定するヘッダー設定(ETag)をHonoからPublicな設定として提供されるようになりました。これにより304のヘッダーを設定するのが用意になります。
import { etag, RETAINED_304_HEADERS } from 'hono/etag'
app.use(
'/etag/*',
etag({
retainedHeaders: ['x-message-retain', ...RETAINED_304_HEADERS],
})
)
上記の変更で 4.3.10 がリリースされています
4.3.10以降の変更
secret
に設定漏れがあった場合に正しいエラーメッセージを表示するように修正
JWT Auth Middlewareの
JWT Auth Middlewareを使用する場合に secret
を設定が必要ですが、secret
が undefined
など定義されていない場合はJWTの検証エラーとなりエラー理由がわかりにくいため、secret
が設定されていない場合にはその旨のエラーを表示するように修正されました。
上記の変更で 4.3.11 がリリースされています
4.3.11以降の変更
JWT Auth Middlewareの secret に設定漏れがあった場合に正しいエラーメッセージを正しいメッセージに修正
メッセージに "secret
ではなく "secret"
とダブルクォーテーションが抜けていたメッセージの修正です。
4.3.11以降の変更 その2
4.4.0の内容がマージされてるので昨日の内容とコミット順が前後します。
JSDoc によるドキュメントの追加
JSDoc によるドキュメントが大量に追加されています。コミットを1つ1つ書くと多くなってしまうので纏めます。
JSRのサポート
4.4.0からJSRがサポートされました。JSRからHonoがインストールできるようになっています。
req.paramsの型を見やすい型に変更
今までRequest Handlerの引数から取得する req.params
で複数のURLパラメータがある場合は
type params {
version: string
} & {
id: string
}
となっていましたが、これが1つに纏まって
type params {
version: string
id: string
}
と表示されるようになるリファクタリングです。
convertFormDataToBodyData
のリファクタリング
内部関数である
この convertFormDataToBodyData
の役割を詳しく追ってないのでなんのための関数かわかりませんが、内部的なこの関数の FormData
生成のために2重で生成されていたが FormData
を1つの変数で生成できるようにリファクタリングが行われています。
上記の変更で 4.4.0-rc.1 がリリースされています
この4.4.0の正式版へのコミットがされていますが、多いのでまた後日。
4.4.0-rc.1以降の修正
4.4.0まで別ブランチで開発してたものが多く数が多いので特に重要ではないものは軽くしか触れません。
READMEへJSRバッジの修正
DenoからJSRにバッジの表示を変更しています。
JSRへPublishするためのCIのトリガーの修正
tagがPushされたら実行するように変更
Deno環境でのベンチマークプログラムの import パスの変更
Denoで実行しているベンチマークプログラムはsloppy importsという機能を使ってimportを解釈されるパスに変更されました。
HonoのESLintルール変更
@hono/eslint-config
が 0.0.6
にUpdateされ、そのルールに沿ったコードに修正されています。
JWT Auth Middlewareの型定義のexportを追加
JWT Auth Middlewareの JwtVariables
SecureHeadersVariables
TimingVariables
の型がexportされて使用できるようになりました。
JSDocの追加
module系のドキュメントの追加です。
Honoで使用されるReactのバージョンの取得が可能になるように修正
Honoに依存したReactのライブラリを作成する場合にReactのバージョンにより処理を分岐できるようにHonoが使用するReactのバージョンを取得できるようにする修正がされました。
CODE_OF_CONDUCT.md
などのリンク追加
READMEに
各種ドキュメントのリンクがREADMEに追加されました。
上記の変更で 4.4.0 がリリースされています
この4.4.0のリリース以降もコミットがされていますが、多いのでまた後日。
![ame-x@amex2189](https://res.cloudinary.com/zenn/image/fetch/s--7E6BGTlX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_70/https://storage.googleapis.com/zenn-user-upload/avatar/43b4b572a7.jpeg)
細かいですが、
途中のparamsをparasにtypoしてますね…
ありがとうございます!修正しました。
4.4.0以降の修正
pretty
が存在するかどうかの条件式から不要な条件式を削除
Pretty JSON Middlewareの クエリパラメータに
全体のコードとは若干異なりますが、上記のような三項演算子の条件までは不要なため削除されました。
- c.req.query('pretty') === '' ? true : false
+ c.req.query('pretty') === ''
multiValueQueryStringParameters
より queryStringParameters
が優先されてしまう不具合の修正
ALBのマルチバリューヘッダー設定の対応で
ALBのマルチヘッダー設定の対応がされましたが、 multiValueQueryStringParameters
と queryStringParameters
の両方が送信される時に queryStringParameters
を優先してパラメータを取得してしまい、パラメータが送信されてくるパラメータが欠損してしまうという不具合が修正されました。
if (event.multiValueQueryStringParameters) {
...
} else {
...
}
となっているので multiValueQueryStringParameters
がある場合はそちらを優先して取得するという変更です。
Object.hasOwn()
を使用するリファクタリング
Objectに指定したプロパティが存在するかどうかの判定に
HonoのAWS LambdaアダプターでALBのProxy判定に Object.hasOwnProperty()
が使用されていましが、 Object.hasOwn()
を使用するようにリファクタリングが行われました。
Object.hasOwn()
はドキュメントにある通り Object.hasOwnProperty()
の代わりに使用できるように設計されおり、 Object.hasOwn()
の使用を推奨されています。また、HonoはNode.js環境ではversion 16以上が動作保証環境になっており、Node.jsでも Object.hasOwn()
が使用できるので問題ないそうです。
hono/client
で FormDataに配列を設定できるように修正
HonoでRPC機能 を使って hono/client
からサーバに送信するする際のFormDataに同一キーのデータつまり配列を送信できるように修正されました。
const res = await client.posts.$post({
form: {
title: 'Hello',
body: 'Hono is a cool project',
list: ['listA', 'listB'] // <-- It has never been sent correctly
},
})
これが可能になっています。
../../
)が正常に生成されない不具合修正
SSGを行う際のパス生成で2階層以上の親のパス(
SSGを行う際は内部的に joinPaths
という関数を呼びますが、この関数を以下のように呼び出すと正常にファイルパスが生成されない不具合が修正されました。
ただし、これは saveContentToFile
という experimentalなAPI に影響する不具合の修正です。
import { saveContentToFile } from 'hono/ssg'
await saveContentToFile({
data: new Promise((resolve) => resolve({ routePath: '..', content: 'test', mimeType: 'text/plain'})),
fsModule: fs,
outDir: '..',
})
一旦28日分までを纏めて記載。29日分は後で追記する予定。
4.4.0以降の修正 その2
ちょっと量が多いのはmainブランチの最新に追いつくためです。
Pull Request templateにドキュメント追加確認のチェックボックスを追加
4.4.0でJSDocによるドキュメントの追加がされましたが、以降はドキュメントをメンテナンスしていく上で修正や追加、削除などがある場合にドキュメントを合わせて修正する必要があるということになりそうです。
request
する場合に duplex
が設定されずにエラーになる場合がある不具合の修正
Honoに定義しているRoutingに
Body Limit Middlewareを使用していてかつ、@hono/node-server
を使用していない場合に以下のようなコードで内部ルーティングを呼び出すとエラーになります。
import {Hono} from 'hono'
import {bodyLimit} from 'hono/body-limit'
const app = new Hono()
app.use(bodyLimit({maxSize: 100 * 1024})) // 100kb
app.post('/hello', async (context) => {
return context.json({message: 'Hello, World!'})
})
const response = await app.request('/hello', { // <-- This is where the error occurs
method: 'POST',
body: JSON.stringify({foo: 'bar'}),
})
これは内部ルーティングではReadable StreamをBodyに設定して送信しますが、Readable Streamを送信する場合は duplex
を設定する必要がありますが、設定されていないことでエラーになるそうです。
@hono/node-server
を使用する場合にエラーにならないのは @hono/node-server
の内部にてReadable Streamを送信する場合に duplex
を付与するロジックが含まれているためです。
onOpen
など呼び出す際にオプショナルチェーンを使って呼び出すようにするリファクタリング
WebSocketのEvent型から
今までは events.onOpen && events.onOpen(evt, wsContext))
のようにEvent型の変数に onOpen
の存在確認を行ってから呼び出していたが、それをオプショナルチェーンを使うようにリファクタリングされました。
- events.onOpen && events.onOpen(evt, wsContext))
+ events.onOpen?.(evt, wsContext))
Context
に server
を渡せるように修正
BunのWebSocket Helperを使用する場合に BunでWebSocketを使用する場合は、下記のコードでいう req
からServerオブジェクトを取り出していたが、Context
に server
を渡せるようになり、指定できるようになる変更がされました。
(ちょっとこれは説明があってるか自信がないです)
import { createBunWebSocket } from 'hono/bun'
const { websocket } = createBunWebSocket()
Bun.serve({
fetch(req, server) {
return app.fetch(req, { ip: server.requestIP(req), server }) <-- `server` can be passed
},
websocket,
})
ドキュメント系のファイルが変更されてもCIでテストが動かないように修正
paths-ignore
を設定して特定ファイルの変更はCIが動作しないように修正されています。
CIのテスト環境にNode.jsの22が対象に追加
Node.jsの22は10月からLTSに入るので先んじてテスト環境に追加されました。
src/*
のTSDocの追加
src/compose.ts
や src/context.ts
の src/
直下のファイルにTSDocが追加されました。
hono/testing
の testClient
で型エラーを起こす不具合の修正
hono/testing
の testClient
で basePath
を使用した以下ようなテストを書くと型エラーが発生する型修正が行われました。
import { Hono } from 'hono';
import { testClient } from 'hono/testing';
const app = new Hono().basePath('/mybasepath');
const route = app.get('/items', async c => c.json({ id: 2 }));
type AppType = typeof route;
const api = testClient<AppType>(app); // <-- If app is specified as an argument here, a type error will occur.
v4向けのブランチをCIの対象外とする修正
v4はすでにリリースされているため不要なため削除されました。
navigator.userAgent
を使用するように修正
一部のランタイム判定に
Deno, Bun, Cloudflare Workers, Nodeは navigator.userAgent
から取得した値でどの環境下なのかを判定するように修正されました。
これらの4環境は navigator.userAgent
をWinterCGで策定された要件通りに実装しているようです。
JSXのスタイルに数値を渡すと正しく反映されない不具合修正
以下ような数値のスタイルを渡すとスタイルに px
が付与されてしまい正しくスタイルとして認識されない不具合が修正されました。
<div style={{ "--hue": 10, "--text": '#000' }}>test</div>
{/* output: <div style="--hue: 10px; --text: #000"> */}
setHeadersToResult
が未使用なので削除される
以前ALB対応で追加された
ALBの対応で追加されたものが未使用のコードがあったため削除されました。
Content-Type
をサポート
JSON APIの
JSON APIの仕様で Content-Type
に application/vnd.api+json
のようなドットを含んだ文字列が入ることがありますが、今までドットを受け入れてなかったので HTTPException
が発生していましたが、それを発生しないようにする修正がされました。
hono/testing
の testClient
の型を修正
testClient
内で any
が使用されていますが、その一部を使用しないで済むようにリファクタリングが行われました。
以上が29日までにmainブランチに行われたコミットです。
4.4.0以降の修正 その3
CONTRIBUTING.md
のコードブロックのシンタックスハイライトの形式を変更
マークダウンのコードブロックのシンタックスハイライトの言語指定が txt
になっていましたが、コマンドなので bash
に変更されました。
重複する型定義の修正
手前味噌ですいません。先日の src/
直下にTSDocが追加されたコミットのdiffを見たら気づいたのですが、同一の型定義が作成されてしまっていたので1つ削除されました。
以上が30日までにmainブランチに行われたコミットです。
後日 上記の変更で 4.4.1 がリリースされています
4.4.1以降の修正
戻り値の型指定を行うようにリファクタリングを実施
どうやら4.4.1のリリースを行ったが、JSRのリリースが正常に行えていなかったようです。その原因がこの戻り値の型指定がなかったためとあります。JSRのリリースに戻り値の型指定が必要ということでしょうか?私もJSRにはまだリリースしてないのでなんとも言えないのですが、そういう風なコミットのようです。
- function test() { console.log('test') }
+ function test(): void { console.log('test') }
上記の変更で 4.4.1 改め 4.4.2 がリリースされています
4.4.2以降の修正
release
に変更
リリースするためのワークフロー名を
GitHub Actionsでリリースを行っていますが、リリースするためのワークフロー名が ci
だったので release
という名称に変更されました。
letによる変数宣言を無くすリファクタリング
意図があってるか不明ですが、 let
で宣言されていた変数を const
で定義するためのリファクタリングが実施されました。
ただ、コードを見るに try {} catch {}
の try
内の最後に return
つまり返り値はあるのですが、 catch
で例外を潰しつつ、返り値がないのはどういうことを意味するのかちょっとわからなかったです。
4.4.2以降の修正 その2
リリースCIのJSRへのpublishジョブ名の変更
JSRに対応したため、ジョブ名もdenoからJSRに変更されました。
いくつかモジュールについてJSDocの追加
JSDocでのドキュメント管理にするために更にいくつかのモジュールについてドキュメントが追加されました。
CodeCovによるカバレッジレポートの追加
CIで各ランタイムのテストなどが実行された後にテストのカバレッジが計測されるようになりました。
JSRに移行した旨の記載がREADMEに追加
4.4.0以降はJSRにHonoがリリースされるので以前の deno.land/x
にはリリースされない旨と deno.land/x
から JSR
への移行方法が追記されました。
また、4.3.11から4.4.0へは特にbraking changeが発生していない旨も合わせて追記されています。(このスクラップを読んでる人にはくどい説明かもしれません)
4.4.2以降の修正 その3
カバレッジバッジをREADMEに追加
先日カバレッジレポートの対応が行われましたが、カバレッジのパーセンテージがREADMEに表示される対応がされました。
DenoのWebSocketのonOpenイベントがトリガーされない不具合を修正
DenoでWebSocketへの接続を行う upgradeWebSocket
内で遅延処理つまりPromise処理があると onOpen
のイベントがトリガーされないという不具合が修正されました。
const app = new Hono()
app.get(
'/ws',
upgradeWebSocket(async (c) => {
await new Promise(resolve => setTimeout(resolve, 5e2)) // Simulate time-consuming operation
return {
onOpen: () => console.log('opened') // can't be trigger
}
})
)
Pull Requestにあったサンプルのままですが、上記のようにPromiseがあると動作しないという不具合が修正されています。原因としてはEventの生成処理前に upgradeWebSocket
を呼んでること原因のようです。
Cloudflare WorkersでWebSocketの接続を行う場合にhttp statusに101が返らない不具合の修正
Cloudflare WorkersでWebSocketを接続する場合にはhttp statusが101が返っていないため接続できない不具合が修正されました。
wrangler
というランタイム名称を workerd
という名称に変更
CIでの
細かい話ですが wranlger
はあくまでCLIであり、Cloudflare Workersのランタイムは workerd
ということでランタイム名が正しい名称に変更されました。
ランタイム名の変更に伴いテストなどのファイル名の配置ディレクトリ名やテストの describe
なども同じく修正されています。
workerd
環境下でのWebSocketのテスト追加
その名の通りCloudflare Workers環境下でのWebSocketのテストが追加されました。
今まではWebSocketの接続ルーティングをもったHonoアプリケーションに対して、WebSocketではないルーティングへのアクセスのみがテストとして定義されていましたが、WebSocketの接続ルーティングへ接続を行い open
message
close
と一通りのイベントが動作するかのテストが追加されました。
またこのコミットにもコメントにもありますが、 workerd
環境下の unstable_dev
を使ってWebSocketの接続を行うには ws
のパッケージが必要になるようです。
AWS Lambda adapterの型定義ファイルを1つに纏めるリファクタリング
AWS Lambdaのadapterには2つのカスタムの型ファイルが存在していました。2つ存在するとそれぞれの使用用途が明確でなく、型ファイルの追加を行う場合にどちらに追加するか迷うことが発生するため1つに纏められました。
カバレッジから型定義のみのファイルは除外するように修正
カバレッジを計測しますが、型定義のみのファイルはカバレッジの対象に含める必要はないので、その修正が行われました。
4.4.2以降の修正 その4
hono/quick
と hono/tiny
のテスト追加
hono/quick
もしくは hono/tiny
のテストかどうかというのは表現的に怪しいですが、Honoはv4で getRouterName
というヘルパーが追加されましたが、hono/quick
か hono/tiny
で作成されたHono
インスタンスに対して getRouterName
で名称が取得できるかどうかというテストが追加されています。
非公式のhttp statusの型名のタイプミスを修正
http statusには200や404といった公式なステータスと218や520といった非公式なステータスが存在します。
Honoでhttp statusを指定する場合に非公式なステータスを使用する場合は今まで UnOfficalStatusCode
と型名がつけられていましたが、正しくは UnofficialStatusCode
(3文字目のオーが小文字) という名称に変更されました。ただ、今すぐ以前の UnOfficalStatusCode
の型名が使用できるのではなく deprecated ですが使用することは可能です。
app.mount
時にアクセスのあったルーティングパスのRequestオブジェクトを指定できるように修正
Honoには mount
という機能が存在します。 mount
はHonoやHono以外のルーティングをHonoのルーティングで上書きするというような機能です。
import { Router as IttyRouter } from 'itty-router'
import { Hono } from 'hono'
// Create itty-router application
const ittyRouter = IttyRouter()
// GET /itty-router/hello
ittyRouter.get('/hello', (request) => new Response(request.url))
const app = new Hono()
app.mount('/itty-router', ittyRouter.handle)
この例では IttyRouter
では /hello
を定義していますが、それをHonoがマウントして /itty-router/hello
で元の IttyRouter
のルーティングにアクセスできるようにするというものです。
しかし、この例で書いてるように IttyRouter
側では本来 /hello
で受けているので本来であればレスポンスの中の request.url
は /hello
のはずですが、HonoがマウントしているためHono側で受けたRequestつまり /itty-router/hello
となります。もちろんこれはそもそも受けているRequestがそうなので間違いではないですが、 IttyRouter
側からすればマウントする親がどうなっているかというのは考える必要はない場合もあります(リクエストパスで何か処理をしたいのであれば自分が定義したパスだけで考えたい場合など)。
そこで、本来の /hello
のルーティングアクセスのRequestオブジェクトを渡せるように mount
の受け入れれる引数に新たな指定方法が増えました。
import { Router as IttyRouter } from 'itty-router'
import { Hono } from 'hono'
// Create itty-router application
const ittyRouter = IttyRouter()
// GET /itty-router/hello
ittyRouter.get('/hello', (request) => new Response(request.url))
const app = new Hono()
app.mount(
'/itty-router',
ittyRouter.fetch,
{
replaceRequest: (req) => req
}
)
replaceRequest
オプションを渡すことでHonoで受けたRequestではなく、IttyRouter
側 /hello
のRequestをそのまま渡せるようになりました。 replaceRequest
だけでなく optionHandler
というものも渡せて、これは従来の ittyRouter.handle
と同等のハンドラーも指定できるようになっています。
上記の変更で 4.4.3 がリリースされています
4.4.3以降の修正
src/request.test.ts
内のテスト内容のTypoを修正
テスト内容のTypoが修正されました。
HTTPException
クラスを使用しないように修正
Honoのエラーハンドリングで受け取る引数で
HTTPException
というものが、Honoのエラーハンドリングに渡された場合は HTTPException
からエラーのResponseを取得してクライアントに返す処理というものがあります。
しかし、この HTTPException
の独自クラスかどうかという判定のためにimportされており、HTTPException
をimportすることで常に HTTPException
分のファイルサイズ (260B) がバンドルサイズに含まれることを懸念して、importせずに同等のことができるようにという修正がされました。(涙ぐましいがこういう努力が嬉しいですね)
requestハンドラー内でResponseオブジェクトを返さず終了した場合のエラーメッセージをわかりやすメッセージに修正
ニュアンスとしては「たぶんResponseオブジェクトのreturnを忘れてるよ」から「Responseオブジェクトのreturnを忘れない?」って感じのエラーメッセージに修正されています。
.gitpod.yml
を追加
Honoの開発環境をGitpodで使用できるように
GitpodというCodespacesみたいなもの?で開発環境が即座に作成できるように .gitpod.yml
が追加されました。
4.4.3以降の修正 その2
getConnInfo
がexportされていない不具合修正
Honoには ConnInfo Helper というリクエスト元の情報を取得できるヘルパーがあります。ドキュメントの記載どおりに hono/cloudflare-workers
からimportするとエラーになるという不具合が修正されました。
4.4.3以降の修正 その3
ミドルウェアを使用しないハンドラーで Response を返さない場合は404 Responseを返すように修正
ミドルウェアを使用しないしないルーティングで非同期のハンドラーでResponseを返さないと404なるという動きがあるようですが、同期のハンドラーの場合はレスポンスが返されずエラーになっていました。
そこで同期のハンドラーでもResponseを返さないと404 Responseを返すという修正がされました。
const app = new Hono()
app.get('/async', async () => {
return // <-- 404 Response
})
app.get('/sync', () => {
return // <-- error
})
同一型定義の削除
Hono内部のユーティリティの型に Prettify
と Simplify
という同じ型定義ありましたが、同一なので Prettify
が削除されました。
never
になる不具合修正
配列を返すJSONをRPCのインターフェースとして使用すると型が
JSONはオブジェクト( { "foo": "bar" }
)から始まる形式と配列( [{ "foo": "bar" }]
)から始まる形式の両方が可能ですが、配列から始まる形式をJSONとして返すハンドラーをRPCの型として読み込むとハンドラーの返り値の型が配列の型ではなく never
になる不具合が修正されました。
import { Hono } from "hono";
// server
const app = new Hono()
.get(
'/test',
async c => {
return c.json([{ foo: 'bar' }])
}
)
export type APP = typeof app
// client
import { hc } from "hono/client";
const client = hc<APP>('http://localhost')
const resp = await client.test.$get()
const result = await resp.json() <-- type of result becomes `never`
HonoのRPCにリダイレクトによる型のサポートを追加
HonoのRPCに使用するハンドラーの中でリダイレクトを行う分岐などがあると型情報が正常に取得できない問題がありました。この修正でハンドラー内でリダイレクトを行った場合でも型情報が取得されるようになりました。リダイレクトがされる場合を考慮してクライアント側は記述する必要があるということです。
const routes = app.get('/profile', (c) => {
if (!flag) {
return c.redirect('/')
}
return c.json({ name: 'abc' }, 200)
})
const client = hc<typeof routes>('/')
const res = await client.profile.$get()
if (res.status === 302) {
console.log(res.headers.get('Location'))
} else {
const data = await res.json()
// ^ { name: string }
}
for
属性のサポートが追加
hono/jsxで
label
タグなどでよく用いられる for
属性が新たに使用できるようになりました。ただし、for
をそのまま指定するのではなく、Reactと同様に htmlFor
として属性を指定すると for
として出力されるようです。
アンダーバーやハイフンを含む拡張子のファイルを静的ファイルとして配信できない不具合の修正
そのままなのですが、ファイル拡張子にアンダーバーやハイフンを含むファイルが404になってしまう不具合が修正されました。
RPC内部で型のために内部で使用するプロパティ名と同名のプロパティを持つオブジェクトが返却できるように修正
HonoのRPCでは型情報のためのプロパティ(data
, status
, format
)が存在しますが、それと同名のプロパティを持つオブジェクトをハンドラーで返してしまうとRPCの型情報が正しくなるということがありました。そこでHonoのためのプロパティについてはアンダーバーから始まる名称に変更されました。
never
になる不具合修正(の追加修正)
配列を返すJSONをRPCのインターフェースとして使用すると型が
先ほど配列を返すハンドラーをRPCとして使用すると型が never
になる不具合の修正を記載しましたが、それの追加修正です。
発生条件を詳しく見ていないですが、配列にネストした配列を入れると never
になる不具合が追加で修正されました。
静的ファイル配信で404の場合にファイル検索が2回動かなくていいように修正
静的ファイル配信を行う処理で404の場合に配信ファイルの検索が2回動いていた処理ロジックが1回で済むようにリファクタリングされました。
カバレッジのレポート出力にテキストファイルとHTMLファイルの出力を追加
カバレッジレポートの出力は今まではJSONだけでしたが、ローカルなどでの確認が行いやすいようにテキストとHTMLファイルの出力も追加されました。
ConnInfo Helper の型定義ファイルの配置場所を変更
今までは src/helper/conninfo/index.ts
に直接定義されていましたが、型定義は src/helper/conninfo/types.ts
に移して export する形にリファクタリングされました。
上記の変更で 4.4.4 がリリースされています
4.4.4以降の修正
カスタムのVery Headerを設定できるように修正
今まではVery Headerは無条件で Origin
が設定されていましたが、カスタムの値を設定できるようになりました。PRでも書かれていますが、CloudflareのPolishを使用する際にこの変更が必要な場合あるための修正です。
typed-htmx
による型定義が行えない不具合の修正
HonoでhtmxとTypeScriptを使用する際のライブラリの
Honoとhtmxを組み合わせて使用する際に typed-htmx
というライブラリを使用することでJSXの属性に型定義を行えるようになるらしいです。
これが4.4.0のリリースから動作しない不具合が修正されました。原因としてはJSRの対応でGlobalを使用しての定義を削除したことで動作しなくなったらしいです。JSRの対応でGlobalは使用できないので、新しくJSXという名前空間で定義してそれを使用することになったようです。
app.route
の第二引数を必須化
4.0以前は任意の引数だったのですが、4.0以降は指定しないことは非推奨とされていました。そこで今回、その非推奨だった第二引数を必須化する変更です。
(ただ、v4のリリースノートやマイグレーションのドキュメント見ても非推奨になりますという記述が見つけられなかった)
app.on
の第一引数の必須チェックを削除するリファクタリング
これだけ見るとなぜこの行がそもそも存在していたのか不明ですが、元々第一引数は文字もしくは文字の配列が型として定義されていますが、それが無い( if (!method)
)場合は自身を返すという無駄なチェックは必要ないということでチェック処理が削除されました。
4.4.4以降の修正 その2
Context
内のプライベート変数の不要な初期化を削除
Context
内に持つプライベートな変数( _var
)が {}
で初期化されていましたが、そもそも初期化が不要なので undefined
を許容するという変更がされました。 _var
は Variables
を保持する内部変数ですが、値の格納や取得時に _var
自体が存在しているかのチェックがあるために不要という判断をしたようです。
また、このコミットで Context
のカバレッジをあげるためにテストも追加されています。
Honoのベース処理保持ファイルのカバレッジを100%にするためにテストを追加
Honoの基本機能( ErrorHandler
や MiddlewareHandler
など )を保持している src/hono.ts
とそれに付随する src/hono-base.ts
のカバレッジを100%にするためにテストが追加されました。
ResponseのJSONまたはテキストの型をジェネリックで指定する場合にステータスコードの型は省略できるように型を修正
HonoではJSONもしくはテキストを返す場合に c.json
や c.text
といったRequestHeaderの Context
の引数からレスポンスを生成できますが、これをジェネリックで指定することも可能です。しかし、ジェネリックで指定する場合は返すJSONやテキストの型とステータスコードを指定する必要がありました。c.json
などはそもそもステータスコードを指定せずともデフォルトのステータスコードでResponseが生成されるのでこれをジェネリックでも可能とする型修正が行われました。
app.get('/', (c) => {
return c.json<Array<{sample: string}>>([{ sample: 'test1' }, { sample: 'test2' }]) // <-- You do not need to specify the second StatusCode in the json generic type specification.
})
リクエストのBodyをparseする内部処理の中で条件にマッチしないチェック処理を削除するリファクタリングを実施
Hono内部でRequestのBodyをparseする parseBody
という処理の中に存在する特定の if
文は必ずマッチしないため不要な条件チェック処理ということで削除するリファクタリングが実施されました。
リファクタリングを行っただけなので、特に振る舞いなどが変わることはありません。
4.4.4以降の修正 その3
JSXに指定された属性をHTMLタグの属性に変換する処理をリファクタリングし、コードサイズを小さくする修正を実施
HonoのJSXはReactと同様に className
や htmlFor
といった通常の属性とは異なる属性を指定することで通常の class
や for
属性に変換する処理があります。このリファクタリングではJSXの属性をまずはHTMLタグの属性としてすべて扱ってから、JSX独自の属性をHTMLタグ属性に変換したものを足した後にJSXで指定された独自の属性を消すという処理になっていましたが、そもそもJSXの属性からHTMLタグ属性に変換する際にJSXの属性をそのまま追加するか、変換してから追加するかという単に属性を追加していくロジックにリファクタリングされました。
このリファクタリングによりバンドルサイズ(minifyあり)ベースで77bほどサイズが小さくなるようです。
上記の変更で 4.4.5 がリリースされています
4.4.5以降の修正
AWS Lambdaの環境でStream Responseを返す際にSet Cookieが正常に行えない場合がある不具合の修正
AWS LambdaのランタイムでStream Responseを返す際にResponseの headers
に set-cookie
を複数設定したとしても1つしか設定されないという不具合が修正されました。
上記の変更で 4.4.6 がリリースされています
4.4.6以降の修正
c.html
の返り値の型は引数に応じて Reponse
もしくは Promise<Response>
となるように修正
今までは c.html
の返り値の型が常に Response
もしくは Promise<Response>
となっていました。しかし、c.html
の第一引数が非Promise(例えば文字列)の場合は Promise<Response>
の返り値とならないように型修正が行われました。
4.4.6以降の修正 その2
hono/client
内で使用されているObjectのマージに関するテストをケース通り行うようにテストを修正
HonoにはRPC機能がありますが、その中で使用する hc
に関するリクエストの処理でクライアント側からオプションを追加で渡すことができます。その渡したオプションをデフォルトのオプションにマージしてリクエストを行うという deepMerge
という関数が内部に存在しますが、そのテストに関する修正です。
振る舞いとしては Object ではないオプションが渡された場合はそのオプションをそのまま投げるという処理になっていますが、テストケースが正常に実行されるようにテストが修正されました。
同一のテストケースの削除
Honoが独自に定義する Request(HonoRequest)
で .text()
や .json()
などRequestのBodyから指定の形式でデータを取得する処理のテストが重複して定義されていたのが削除されました。
4.4.6以降の修正 その3
ContextVariableMap
と Env(Variables)
と同名のキー定義がある場合に Env
の型を ContextVariableMap
の型で上書きしてしまう不具合修正
HonoのContextには ContextVariableMap
という型定義があります。これはmiddlewareなどで Context に値を設定する場合に定義するものですが、これが middleware に定義した Variables
の型定義を壊してしまうというものの修正です。文字を読んだだけではわからないので、PRにあったコードを拝借して説明します。
declare module 'hono' {
interface ContextVariableMap {
test: 'root'
}
}
const middleware = createMiddleware<{
Variables: {
test: 'override'
}
}>(async (c, next) => {
c.set('test', 'override')
await next()
})
まず ContextVariableMap
で test
というキーに root
という文字列が入るという型定義をしています。次に createMiddleware
は Variables
つまり Env
の型定義に同名の test
というキーで override
という文字列を設定しているという型定義の2つを作成します。
const router = new Hono()
.get(
'/test',
middleware,
async c => {
const testValue = c.get('test') // test type is 'root'
return c.text(testValue) // However, the testValue variable contains `override`.
},
)
c.get
で test
というキーでデータを testValue
の変数に格納しています。この時 testValue
の型は root
という文字列の型になるが、実際には override
という文字列が入っているというのが今回の不具合の内容です。
上記の変更で 4.4.7 がリリースされています
4.4.7以降の修正
変数に明示的に型を設定
hono/jsx
内部で使用する変数に明示的に型を設定する修正がされました。これはJSRがslow typesになっている警告を修正するものです。要は型を推論に任せるよりも明示的に設定した方が型の決定が早くなるために行ってほしいというものらしいです。
Codecovを動作させるためのトークンの環境変数の設定方法を変更
HonoにPull Requestを作成した場合に Codecov
によるカバレッジの計測が行われていましたが、トークンが正常に設定されていないというエラーが発生していました。正しくトークンを設定するように修正されました。
JSDocのタイポ修正
ドキュメントのタイポが修正されました。
DenoのWebSocket接続時のOptionを設定できるように修正
DenoでWebSocket接続時にオプションを指定することができますが、DenoでHonoを使用する場合にWebSocket Helperではそれが対応されていませんでしたが、このコミットにより使用できるようになりました。
使用するには upgradeWebSocket
の第二引数に指定することで使用できるようになります。
app.get(
'/ws',
upgradeWebSocket((c) => {
return {
onMessage(event, ws) {
console.log(`Message from client: ${event.data}`)
ws.send('Hello from server!')
},
onClose: () => {
console.log('Connection closed')
},
}
}, { idleTimeout: 60 })
)
4.4.7以降の修正 その2
Accepts Helper内で行うリクエストヘッダーのパース処理速度を更に向上させる修正を実施
HonoにはAcceptsヘッダーを処理するためのヘルパーが存在します。内部の処理ではRequest Headerに設定されているAcceptsの内容を読み取る時に、スプレッド構文を用いて、以下のようにAcceptをパースしています。(実際のコードとは少し異なるが似たようなサンプルです)
const acceptHeader = c.req.header('Accept') // Accept: text/html, application/xhtml+xml, application/xml;q=0.9, image/webp, */*;q=0.8
const accepts = acceptHeader.split(',') // ['text/html', 'application/xhtml+xml', 'application/xml;q=0.9', 'image/webp', '*/*;q=0.8']
return accepts.map((accept) => {
- const [type, ...params] = accept.trim().split(';') // ['application/xml', 'q=0.9']
+ const parts = accept.trim().split(';') // ['application/xml', 'q=0.9']
+ const type = parts[0]
...
}
この処理の中で重み値である ;
の除いた実際の値を取得するための処理をスプレッド構文を使うより1つの配列に一旦格納してから実際の値を取得するといったコードの方がパフォーマンスが向上するということで変更が行われました。
4.4.7以降の修正 その3
utils/buffer.ts
のテストカバレッジをあげるためにテストを追加
utils/buffer.ts
の中に equal
という関数がありますが、これはHonoの中では使用されていません。しかし、ユーザが使用するかもしれないということで残す決定をしましたが、それならば振る舞いを保証する必要があるためテストコードが追加されました。
Bunのテストでは標準機能を用いてLCOV形式のカバレッジレポートを生成するように修正
Bunは1.1.16からテスト実行におけるカバレッジのレポートをLCOV形式で出力できるようになったようです。そこでBunの環境では1.1.16にバージョンをあげて、テストによるカバレッジレポートをBun標準の機能を用いて出力するように変更されました。
テストコードにおけるVitestの不要なimportを削除
HonoのテストではVitestが使用されていますが、Vitestの describe
や it
といったAPIはglobalsの設定がされているので、 import
は特に必要ありません。そこで import
してしまっているテストから不要なVitest import
を削除する修正されました。
4.4.7以降の修正 その4
指定された2つの値をそれぞれハッシュ化する関数を並行処理するようにしてパフォーマンスを改善
Basic Auth MiddlewareやBearer Auth Middlewareでは渡されたユーザ名やパスワード、トークンといったものが設定値と同等かどうかを調べるために timingSafeEqual
というHono内部で使用する検証関数が存在します。その timingSafeEqual
の関数では渡された値とハッシュ化する関数を通して値が2つの値が同値になるかという処理がありますが、それぞれ直列処理していたコードを並行処理に変えてパフォーマンスをあげるという変更が行われました。コミット内容そのままですが以下の変更です。
- const sa = await hashFunction(a)
- const sb = await hashFunction(b)
+ const [sa, sb] = await Promise.all([hashFunction(a), hashFunction(b)])
上記の変更で 4.4.8 がリリースされています
4.4.8以降の修正
Context生成時にHonoRequestオブジェクトを事前に生成して保持するのではなく、必要時に生成するように修正
Request毎にHandlerで使用するContextを生成しますが、生成時にオリジナルのRequestオブジェクトからHono独自のRequestオブジェクトつまり、 HonoRequest
というオブジェクトを生成してそれをContextに設定していました。しかし、これは使用しなれば無駄な生成コストであるため、処理の開始速度を早めるためにContextの初期では HonoRequest
を生成せず、 HonoRequest
を取り出す時に生成するようにしました。これにより HonoRequest
を使用しないリクエスト処理時間が早くなります。
よくある手法ですが、以下のようにプライベートな変数に生成した結果を保持しておき、2回目以降は変数にすでに生成済みの結果を返すようになっています。
get req(): HonoRequest<P, I['out']> {
this.#req ??= new HonoRequest(this.#rawRequest, this.#path, this.#matchResult)
return this.#req
}
ミドルウェアを使用してルートをChainしていくとContextの型が正しくなくなる不具合を修正
Issueにあったコードを拝借して説明します。
import { Hono, MiddlewareHandler } from 'hono'
const mw1: MiddlewareHandler<{ Variables: { foo1: string } }> = async (c, next) => {
c.set('foo1', 'foo1')
await next()
}
const mw2: MiddlewareHandler<{ Variables: { foo2: string } }> = async (c, next) => {
c.set('foo2', 'foo2')
await next()
}
const app = new Hono()
.get('/1', mw1, async (c) => {
// this is correctly typed.
return c.json({ foo1: c.var.foo1 })
})
.get('/1-then-2', mw1, mw2, async (c) => {
// this is also correctly typed.
return c.json({ foo1: c.var.foo1, foo2: c.var.foo2 })
})
.get('/nothing', async (c) => {
// In this scope, c.var should be empty because no middlewares are applied.
// However, c.var is typed as { foo1: string; foo2: string; }.
// In runtime, v is {}.
const v: { foo1: string, foo2: string } = c.var;
return c.json(v);
});
コメントそのままですが、上記のコードでいう /nothing
にアクセスすると返ってくる値は {}
というカラのJSONが返ってきます。しかし、コード上の c.var
はカラではなく、しっかり型が入っているというバグが以前ありました。
このバグはバージョン4のリリースと同時に修正されましたが、別に型の修正がされたことで再度同じバグが引き起こされたようです。その修正が行われました。
Web Standardsで動作する説明文に変更
ここ最近「Edge」というのは私も使用しなくなってきたのですが、Honoも「Edge」で動作するという説明ではなく、Web StandardsのAPIで動作するフレームワークですよという説明文にREADMEなどが修正されました。
Server-Timing Middlewareが重複して動作するとエラーとなってしまう不具合の修正
HonoにはServer-Timingのヘッダーを設定してくれるServer-Timing Middlewareが存在します。
しかし、このミドルウェアを app.route
などの機能を用いて、2つのルーティングで重複して使用するとエラーになるという不具合が修正されました。
import { timing } from 'hono/timing'
import { Hono } from 'hono'
const app = new Hono()
app.use(timing())
const a = new Hono()
a.use(timing())
a.get('/', (c) => c.text('a'))
app.route('/a', a)
export default app
上記の変更で 4.4.9 がリリースされています
4.4.9以降の修正
JSRでのJWTにあるユーティリティの型が読み込めない不具合修正
HonoのJWTには JWTPayload
や SignatureAlgorithm
といった型が存在しますが、JSRを使ったHonoではその型が正常に取得できないという不具合が修正されました。
4.4.9以降の修正 その2
ReadableStream
の接続が切断された場合に明示的に abort
を呼び出すように修正
Bunで
Bunでは SSE などの ReadableStream
で接続している場合にクライアントで画面を閉じるなどといった切断処理起こった場合に ReadableStream
のキャンセル関数が呼び出されないようです。そのため、キャンセル関数でユーザ側で定義した abort
処理が動作しないということが発生するようです。
そこで AbortSignal
の abort
イベントでもユーザが定義した abort
を呼び出すように修正されました。
先頭にスラッシュから始まらないルーティングを定義した場合にHono RPCクライアントが正しい型を返さない不具合の修正
HonoにはRPCのようにレスポンスのルーティングとレスポンスの型を明示的に行える機能があります。その機能を用いる際に、ルーティングに設定するパスをスラッシュから始めないと型が正常に動作しないという不具合が修正されました。
const route = app.get('posts', (c) => {
return c.json({
test: 'test'
})
})
export type AppType = typeof route
import { hc } from 'hono/client'
const client = hc<AppType>('/')
client.posts.$get // <-- Incorrect type
上記の変更で 4.4.10 がリリースされています
4.4.10以降の修正
routerのテスト関数が非同期である必要のないものは通常の関数定義に修正
テストの it
に渡すテスト関数が非同期関数として定義されているものの中で非同期である必要の無いものから async
を消すという修正がされました。
1つのルーティングに2つ以上のハンドラーを定義すると 2つ目以降はFormDataをRecord型として値を受け取れない不具合の修正
hono/validator
を使用すると form
として指定するとFormDataをRecord側で受け取ることができます。しかし、2つ以上定義すると2つ目以降はFormDataがそのままFormDataとして渡されてRecord型として値が受け取れない不具合が修正されました。
原因はリクエストから受け取ったFormDataをRecord型に変換して返す処理の中で2回目以降のリクエストからFormDataを取り出す処理を高速化するためにFormDataをキャッシュしているようですが、そのキャッシュの設定に設定したFormDataをそのまま返してしまうロジックが存在したため発生するようです。
import assert from 'assert'
import { Hono } from 'hono'
import { validator } from 'hono/validator'
const app = new Hono().post(
'/',
async (c, next) => {
await c.req.parseBody()
await next()
},
validator('form', (value) => {
console.log('validator - value:', value)
assert(!(value instanceof FormData), 'Value has to be a Record, not FormData')
return value
}),
(c) => {
return c.text('Hello Hono!')
}
)
const form = new FormData()
form.append('foo', 'bar')
await app.request('/', {
method: 'POST',
body: form
})
hono/client
を使用してWebSocketを接続する際にQuery Parametersをサポートするように修正
hono/client
を使用してエンドポイントに対してWebSocketのリクエストを投げる際にQuery Parametersを渡して接続できるように変更が加えられました。これによりサーバ側はエンドポイントだけでなく、渡されたパラメータにより処理を振り分けることが可能となりました。
const client = hc<AppType>('http://localhost')
client.index.$ws({
query: {
id: '123',
type: 'test',
},
})
// => ws://localhost/index?id=123&type=test
ルーティングに定義するパスとハンドラーの組み合わせの全パターン型指定の修正
ちょっと正直これだけ見ると正直難解な型定義の修正なのですが、つまるところHonoには app.get
の引数にパスと複数のハンドラーを渡すことができますが、その渡せるハンドラーの個数9個分の型定義を直すリファクタリングが行われたようです。
テスト対象の名称のタイポを修正
getRouterName
を対象としたテストの description
が誤っていたため修正されました。
4.4.10以降の修正 その2
ReadableStream
が正常終了した場合に onAbort
が呼び出されてしまう不具合修正
denoで
この記事の「4.4.9以降の修正 その2」でも触れたBunで ReadableStream
が切断された場合に onAbort
が呼び出す修正を触れたと思いますが、その修正でBunは確かに問題なくなったのですが、denoの環境下では正常終了した場合も onAbort
が呼ばれてしまうという不具合が発生したようです。
そこで明示的に close()
を呼んでいない場合は onAbort
を呼び出すという修正がされました。
上記の変更で 4.4.11 がリリースされています
4.4.11以降の修正
Set-Cookie
にカンマが入るような設定を行った場合に正しく Set-Cookie
が返らない不具合の修正
AWS-LambdaのAdapterで
PRにかかれていますが、例えば Set-Cookie
の値に Expires
を設定すると以下のような内容になります。
Set-Cookie: key=value; path=/; expires=Wed, 10-Oct-12 07:12:20 GMT
AWS-LambdaのAdapterではHonoで設定した Set-Cookie
をAWS LambdaのResponseとして返す処理でCookieの値を再設定しています。そこでHonoで設定した Set-Cookie
にカンマが入るような設定をすると正常に Set-Cookie
の値が返らないという不具合が修正されました。
またこの修正でHeaderから Set-Cookie
の値を取得するために、 getSetCookie
というAPIを使用できる場合は使用するという風にも変更されています。
use
指定後のHttp Method定義にパスを指定しない場合にURL Parameterの型が正しく取得できない型定義の修正
同一のパスに対して、POST
と GET
の両方のHttp Methodを受け取るルーティングを定義する場合はチェインして記述することが可能です。更にチェインした側はパスを省略することでチェインされた側のパスを自動的に引き継ぎます。
new Hono()
.post("/:id", async (c) => {
const id = c.req.param("id");
return c.text(id);
})
.get((c) => { // path is "/:id"
const id = c.req.param("id");
return c.text(id);
});
しかし、use
を使用するとpathは引き継ぐのは引き継ぐのですが、型が正しく認識できないという型の不具合が修正されました。
new Hono()
.use("/:id", async (c, next) => {
await next();
})
.get((c) => { // path is "/:id"
const id = c.req.param("id");
return c.text(id); // TypeScript complains that `id` might be `undefined`.
});
4.4.11以降の修正 その2
JSDocの hono.dev へのドキュメントページリンクを修正
JSDocには hono.dev へのドキュメントページのリンクが記載されています。各機能のドキュメントページのURLが /docs
変更されたため、JSDoc内のリンクも /docs
へのリンクへと変更されました。
4.4.11以降の修正 その3
download
属性にbooleanの値を設定した場合に属性自体の出力有無となるように修正
アンカータグの
アンカータグには download
という属性値があり、この属性値はリンク先を download
属性値で指定した名前で保存するというものです。 hono/jsx
で download
属性値にbooleanの値を設定するとそれがそのまま文字列として出力されていましが、この変更により true
の場合はただ download
属性値を出力し、 false
の場合は download
属性値を出力しないという風に変更されました。
triage
ラベルとするように変更
HonoへのIssue作成の際にBug Reportを作成された場合は
今まではBug ReportのIssueを作成された場合は即座に bug
のラベルが付与されていましが、まずは triage
ラベルが付与されるように運用が変更されるようです。その後に調査した結果でバグだった場合は bug
ラベルを付与する運用に変更されます。
c.json
で返る型を JSON.stringify()
で返る型に限りなく近くする変更
Honoでは c.json
をハンドラーで返すことでJSONのデータをクライアントに返すことができます。またHonoではRPCで処理するための hono/client
が存在します。 hono/client
で使用する hc
はハンドラーが返す c.json
の返り値から型を決めますが、JSON.stringfy
にはObjectではなく、Functionを渡したり Symbol
を渡したりと無茶なことをすると undefined
や カラのオブジェクト( {}
)を返しますが、その動作に近くなるように型変更が行われました。
slow types
となる変数に明示的に型を指定するように修正
JSRリリースのための
JSRリリースするために slow types
となる変数に明示的に型を指定するように修正がされました。
utils/jwt
にJSDocを追加
JSRのスコアを向上させるために
JSRにはパッケージのスコアリングに「ドキュメント」「ベストプラクティス」「発見可能性」「互換性」の4つの項目がありますが、HonoのJSRのスコアを上げるために utils/jwt
にJSDocが追加されました。
上記の変更で 4.4.12 がリリースされています
ベンチマーク計測を行うため、他Routerライブラリバージョンを更新
Honoと他のRouterライブラリとの速度比較を行うために最新版で速度を測るように express
や koa-router
などなどのライブラリを最新バージョンで速度を測るように変更されています。
4.4.12以降の修正
HonoのビルドをBunで行うように修正
Honoのビルドは一部Bunを使用してビルドされていました。しかし、一部はNode.jsでTypeScriptを実行するために tsx
も併用されていました。Bunでのビルドが統一されるならば tsx
の使用や2つのRuntimeは必要ないので、Bunに寄せるように修正されたようです。
4.4.12以降の修正 その2
UnOfficalStatusCode
の型を使用しないようにするリファクタリング
deprecatedな
「4.4.2以降の修正 その4」でも触れていますが、 UnOfficalStatusCode
は deprecatedです。(予定ではv5で削除されるようです)内部で UnOfficalStatusCode
の型が使用されていますが、結局のところ型としては UnofficalStatusCode
と同様なので、 UnOfficalStatusCode
は UnofficalStatusCode
と定義するように修正されました。書いてて伝わりにくいのでコードで書くとこうです。
- export type UnOfficalStatusCode = -1 // same UnofficialStatusCode
+ export type UnOfficalStatusCode = UnofficialStatusCode
4.4.12以降の修正 その3
th
タグの scope
属性の指定できる正確な属性値をUnion型の定義を追加
hono/jsx
で作成する th
タグの scope
の属性値は String
が指定可能でした。ただ、scope
に指定する属性値は row
や col
といった決まった値を指定します。そこで指定する属性値の型に row
や col
といった指定できる4つの属性値のUnion型が追加されました。
ただし、この4つの属性値以外にも String
での指定は可能です。これは下位互換もあるかもしれませんが、4つの属性値以外も一応ブラウザでの動作があるので今まで通りの値も指定が可能になっています。
RPCのFormの属性値に配列を指定するとエラーになる型情報の修正
hono/client
を使用してRPCによるFormを送信する際に、本来Formは属性値に配列データを指定することが出来ますが、HonoのRPCでは配列を使用すると型エラーが発生する型情報の修正が行われました。
Issueにあったコードを拝借すると以下のような場合にエラーになるようです。
import { Hono } from "hono";
import { hc } from "hono/client";
import { validator } from "hono/validator";
const route = new Hono().post(
"/",
validator("form", (value: { body?: string[] }, c) => {
const body = value["body"];
if (!body || !Array.isArray(body)) {
return c.text("Invalid!", 400);
}
return { body };
}),
(ctx) => {
return ctx.text(`Body is [${ctx.req.valid("form").body}]`)
}
);
const client = hc<typeof route>("");
client.index.$post({
form: {
body: ["1", "2", "3"], // this is type error
},
});
4.4.12以降の修正 その4
Blob
を指定できるように型を変更
RPCのFromに
昨日の 4.4.12以降の修正 その3 で行われた「RPCのFormの属性値に配列を指定するとエラーになる型情報の修正」に付随して、Formに指定できるのは File
型ではなく Blob
型が良いということで Blob
型を指定できるように変更されました。
ちなみに File
と Blob
の違いは Blob
の型に対して name
と lastModified
のプロパティを持つことで File
型になります。(他に2つのプロパティがありますが、非推奨だったりします)
余談ですが、この File
から Blob
への変更の方がいいのではないかという議論は作者はもとより中学生たちで議論されてて関心するばかりです。
use
のパスにワイルドカードのパスを指定したミドルウェアを設定するとRPCの型が正常に生成されない型の不具合を修正
Honoには use
というミドルウェアを設定するものがありますが、この use
にワイルドカード、つまり *
を指定するとRPCの型が正常に生成されないという型の不具合がありましたが、それが修正されました。
const app = new Hono();
app.use('*', middleware).route('/users', usersRouter);
const client = hc<typeof app>('');
console.log(client.users.$get()); // type error (Property '$get' does not exist on type 'never'.)
上記の変更で 4.4.13 がリリースされています
4.4.13以降の修正
Honoのv4.5がリリースされたので、その修正およびコミットが以下のPRに纏まっています。
数日はここからコミットの内容を書きつつ、mainブランチに追いついていく記載とします。
@hono/react-compat
パッケージのために createRoot
と hydrateRoot
をデフォルトエクスポートに変更
HonoもPreactのように @hono/react-compat
というライブラリを使用すると import xx from 'React'
がReactを使用するのではなく、 hono/jsx
を使用するということが出来るようになるための修正が行われました。
@hono/react-compat
パッケージのために React互換のrenderToString
と renderToReadableStream
を追加
上記の修正と同じように @hono/react-compat
を指定するということはReact互換のAPIが必要になってきます。そこで hono/jsx
にもReactと同じ振る舞いを行う renderToString
と renderToReadableStream
が実装されました。
useCallback
の引数の関数に引数を取る関数を指定すると型エラーとなるため型を修正
これもReactとの互換性向上の1つだと思いますが、 useCallback
の第一引数に関数を渡しますが、その関数が引数を取るような関数を渡すと型エラーとなっていたようです。そこで型エラーが発生しないように修正されました。
また、Reactの StrictMode が動作するようにJSXの記述ができるように修正も同時に行われています。
JWT Auth Middleware で署名付き Cookieを使用できるように修正
HonoのJWT Auth Middlewareでは署名付き Cookieを扱うことができず、ミドルウェアが401で応答を返していました。この変更によりJWT Auth Middlewareでも署名付き Cookieを使用できるように修正がされました。
余談ですが、この修正自体は元は別の方が行っていたようですが、PRが停滞していたため別の方が引き取って続きを作成したようです。停滞しているPRをこのように引き取る形もありですね。
hono/jsx
バージョン番号を更新
v4.5で hono/jsx
のバージョン判定文字列が 19.0.0-hono-jsx
に変更されました。
createContext
をReact同様にProviderとするように修正
これも @hono/react-compat
を実現するための変更です。Reactのエイリアスとして動作するようにReact同様の動作をする必要があるので、作成したContextはProviderとして動作するように修正されました。
React 19の新機能と互換性を持つための大幅な変更
これはかなり大作なコミットです。
React 19では色々と新機能が入っています。中でもこのコミットで対応されたのは数多くあります。
- headタグ内の
title
やmeta
などのタグの巻き上げレンダリングをサポートの追加 - refsのクリーンアップ関数対応のサポートの追加
- 非同期スクリプトのサポートの追加
-
useActionState
などのFormに関するHooks関数のサポートの追加 -
use
関数のサポートの追加
書いてる私もお腹いっぱいになってきます。これも最終的には @hono/react-compat
を実現するためのものにもなると思います。まだ私自身も試したわけではありませんが、 use
がサポートされているということは非同期コンポーネントも hono/jsx
で使用できるということになるのでしょうか。
続きは別途記載していきます。
4.4.13以降の修正 その2
undefined
を返すように修正
ConnInfo HelperのIPアドレスタイプの判定が行えない場合は
今まではIPアドレスのタイプが IPv4
もしくは IPv6
のどちらか、もしくは判断できない場合は unknown
という文字列を返していました。(IPアドレスの取得ができないというわけではなく、IPアドレスのタイプということ)
しかし、Cloudflare Workersで動作させると常に unknown
という文字列を返すようです。そこで、IPアドレスのタイプが不明な場合は取得できたIPアドレスからタイプを逆引きするという提案もありましたが、IPアドレスのタイプが取得できないのあれば無理に作成せず undefined
を返すという方向で修正がされました。
ちなみにIPアドレスのタイプはどのように取得しているかというとBunだけは送られてきたRequestオブジェクトからIPアドレスやIPアドレスタイプが取得できるようです。逆にBun以外はIPアドレスのタイプは取得できないので、IPアドレスから逆引きするしか無いと思います。
4.4.9で修正された影響により4.5のブランチでテストが失敗してるテストコードの修正
ちょっとコミットの解釈は自信が無いですが、たぶん合ってると思います。(PRなど何も説明がないので自信の記憶だけが頼り)
4.4.9のリリースに入ってる「Context生成時にHonoRequestオブジェクトを事前に生成して保持するのではなく、必要時に生成するように修正」というのがあります。この記事の上の方に解説しているので見て頂ければわかると思います。
これの影響で4.5リリースのブランチにあるテストが失敗してしまっていたので、4.4.9リリースと同様の修正がテストコードにて行われました。
React Compilerによる再レンダリングの抑制するための修正
なんかタイトルが雑ですが、雰囲気で感じとってください。
hono/jsx
は(特にHono v4.5)ではReact 19互換に力が入っています。そこでReact互換であるならばReact Compilerで実行することより同一のPropsであれば子の再計算処理が行われない(つまり、自動的にmemo化された状態)になるはずが、 hono/jsx
では再計算されてしまうという事象がありました。そこでその事象を修正したのがこのコミットです。
修正としては、元からのJSXのProps数やPropsの値を現在のPropsの設定を見比べて、同一の場合は比較処理をスキップするという処理が入ったようです。
Cloudflare Pages用のmiddlewareの追加
HonoにはCloudflare PagesのAdapterはありますが、middlewareはありませんでした。Issueで提案された面白い例ではmiddlewareではCloudflareのHTMLRewriterでのHTML書き換え処理をmiddlewareで共通的に定義するというような使用用途が提案され承諾されて追加されました。
// functions/_middleware.ts
import { getCookie } from 'hono/cookie'
import { handleMiddleware } from 'hono/cloudflare-pages'
export const onRequest = handleMiddleware(async (c, next) => {
const myCookie = getCookie(c, 'my_cookie')
const response = await next()
// Alternative API similar to Pages middleware
const response = await c.next()
return new HTMLRewriter()
.on('head', new InjectScriptHandler(myCookie))
.transform(response)
})
Cloudflare Pagesのhandlerの型を追加したCloudflare Pages用のmiddlewareのhandlerと同様に修正
追加されたCloudflare Pages用のmiddlewareではCloudflare Pages用のhandlerの型が定義されていましたが、元からあったCloudflare PagesのAdpaterではhandlerの型定義が少し異なっていました。そこで追加されたmiddlewareのhandlerの型定義をAdpater側でも使用するように修正されました。
続きは別途記載していきます。
4.4.13以降の修正 その3
undefined
を追加
ConnInfo Helperが返すIPアドレスタイプの型に
前回の「4.4.13以降の修正 その2」でも説明したConnInfo HelperのIPアドレスタイプですが、型自体に undefined
が追加されました。これは値が undefined
ということではなく、そもそもオブジェクトに addressType
というものがオプショナルで返ってくるものなので undefined
という型が追加された形になります。
IP Restriction Middlewareを追加
そのままですが、要はIP制限を行ってくれるIP Restriction Middlewareというミドルウェアが追加されました。
ドキュメントの通りですが、このミドルウェアでは拒否するリストを denyList
に、許可するリストを allowList
に登録することで指定のエンドポイントをIP制限をかけることができるようになります。まだまだこういったIP制限を行うアクセス制限はまだまだあると思うので十分需要はあると思います。(昔自分もFastifyのプラグインとして作った記憶があります)
import { Hono } from 'hono'
import { getConnInfo } from 'hono/bun'
import { ipRestriction } from 'hono/ip-restriction'
const app = new Hono()
app.use(
'*',
ipRestriction(getConnInfo, {
denyList: [],
allowList: ['127.0.0.1', '::1'],
})
)
app.get('/', (c) => c.text('Hello Hono!'))
察しのいい方は気づいていると思いますが、HonoでIPアドレスを扱うにはConnInfo Helperが必要になりますが、このIP制限を行うミドルウェアももちろんIPアドレスを扱うのでConnInfo Helperに依存しています。
IP Restriction Middlewareが返す無名関数に関数名を追加
IP Restriction Middlewareが返す関数が無名関数だったため、関数名を定義して返すように修正されました。これでミドルウェアからの返り値を名称をもって取得できるようになります。
ConnInfo HelperがVercel上で動作するように修正
ConnInfo Helperは様々な実行環境でのIPアドレスの取得を吸収するものですが、VercelはVercelでまたIPアドレスの取得方法が異なります。このコミットではVercel AdapterでのIPアドレスの取得が行えるに修正がされました。
VercelはこのHeaderからIPアドレスが取得できますが、これを見ると確かにそこからですよねって感じのわかる人はわかるヘッダーの値です。
undefined
に修正
Vercel Adapterで返すIPアドレスタイプを
追加されたVercel AdapterでのConnInfo HelperでIPアドレスタイプが旧の unkwon
の文字列を返すようになっていたので、直近変更された undefined
で返すように修正されました。
ConnInfo HelperがLambda Edge上で動作するように修正
これもConnInfo Helperシリーズの修正ですが、Lambda EdgeのAdapterでIPアドレスが取得できるように修正されました。
Service Worker Adapterの追加
いわずもがなHonoはバックエンドサーバとして使用されることが多いですが、HonoをService Workerとして動作させることを可能とするAdapterが追加されました。これは一種のHonoがWeb Standards APIで作成されているのでこういうことが可能ということです。
このService Worker Adapterを使用するとオフラインで動作するWebアプリケーションの構築が行えるということでこれはこれで面白いものが追加されたと思います。
リクエストごとに一意のIDを採番するRequest ID Middlewareの追加
追加したものを見ていてRuby on Railsにもあったなと懐かしい気持ちになってきました。
Honoでリクエストごとに一意にIDを採番してくれるミドルウェアが追加されました。パッと聞いただけでは使用用途がわからないかもしれませんが、よくある使用例でいうとログに仕込むことで、そのリクエストがどのような処理を通ってリクエストが処理されたかというのがわかるようになります。それがこのミドルウェアを使用すると簡単に採番されたIDが取得できるようになります。
ちなみにドキュメントにも書いていますが、IDの採番はデフォルトで crypto.randomUUID()
を使用します。そのため、環境によっては動作しない可能性もありますが、変更することも可能になっています。
複数のミドルウェアの実行結果を制御するCombine Middlewareの追加
ミドルウェアは色々と存在しますが、複数使用することも想定されます。そこで使用するミドルウェアの全部を実行する必要がない場合などにはこの新しく追加されたCombine Middlewareが大いに役立ちそうです。
このミドルウェアは some
, every
, except
の3つの機能を持つようで、ドキュメントにも書いていますが、some
でIP制限のミドルウェアとベーシック認証にミドルウェアでどちらかが正常に通ればアクセスできるエンドポイントをこのCombine Middlewareを使用することで構築できるようになります。現実世界では例えば社内や特定のプライベートネックワーク内からであれば無条件でアクセスを許可しつつ、パブリックネットワークからはベーシック認証がかかるということができるということです。
import { Hono } from 'hono'
import { bearerAuth } from 'hono/bearer-auth'
import { getConnInfo } from 'hono/cloudflare-workers'
import { some } from 'hono/combine'
import { ipRestriction } from 'hono/ip-restriction'
const app = new Hono()
app.use(
'*',
some(
ipRestriction(getConnInfo, { allowList: ['192.168.0.2'] }),
bearerAuth({ token })
)
)
app.get('/', (c) => c.text('Hello Hono!'))
Bindings
に interface
を指定できるように型を修正
Honoのジェネリックである
HonoにはBindingというContextつまりDIを設定するジェネリックを指定できる型に Bindings
というものがあります。これは今まではTypeScriptの type
しか指定できませんでしたが、 interface
も指定できるようにするというのがこの変更です。
PRでも触れられていますが、この変更の起点となったのはCloudflare Workersでは Env
の型を取得するcliコマンドがありますが、このコマンドを使用して生成した型は interface
で定義されているためそのまま使用することができるようになるというのが利点として書かれています。Cloudflareユーザには嬉しい変更だと思います。
追加されたIP制限やミドルウェアの組み合わせミドルウェアの使用例などに関するコメントを正しいコメントに修正
exampleなどに書かれているコメントに from
先が正しくないものがあったりJSDoc自体の追加だったりとコード内のコメントに関する修正がされました。
ContextVariableMapに配列型のものを指定すると型が正しく取得できない場合がある不具合の修正
発生条件まで詳しく見ていないですが、ContextVariableMap
の値に配列の型を指定すると c.get
して取り出したものの値の型が any
になってしまうという不具合が修正されました。
hono/jsx
でHTMLのグローバル属性の型のサポートを追加
HTMLにはグローバル属性というすべてのHTML要素で共通の属性があります。その属性を指定する場合の型のサポートが追加されました。
JSRリリースのための slow types となる変数に明示的に型を指定するように修正
毎度お馴染みのJSRのslow typesとなる警告の修正です。
上記の変更で 4.5.0 がリリースされています
これでv4.5のリリースの変更がすべて大体mainブランチの変更に追い付いたと思います。(今日1つだけマージされていますが、コードの変更ではないので明日の解説に回します)
4.5.0以降の修正
ビルド時のファイル削除をライブラリを使用しないように修正
ビルド時に前回のビルドをクリーンアップするために rimraf
を使用して rm -rf
と同等の処理を実施していました。ビルドはBunで行っていますので、Bun Shellにそれと同等のことができるcliが存在するのでそれを使用して依存関係を減らすように修正されました。
src/
配下から移動する修正
テスト用のコンフィグファイルを
テスト用コンフィグファイルは src/
配下に配置しない方がよいということで、vitestの設定ファイルである setup-vitest.ts
ファイルが移動されました。
4.5.0以降の修正 その2
secret
プロパティに JsonWebKey
や CryptoKey
の型の値も指定できるように型を修正
JWT Auth Middlewareの引数の
jwt
の引数に secret
というプロパティには今まで文字列のものしか指定できませんでしたが、その先の内部でJWTを verify
するメソッドは文字列だけでなく、 JsonWebKey
や CryptoKey
も渡すことができるので、ミドルウェアのそもそもの jwt
の secret
も JsonWebKey
や CryptoKey
を渡すようにしてもよいという修正です。何か動作自体が変わることはありません。
Bearer Authの値のprefixが無い状態も動作するようにする
Bearer Authの値は特に指定しなれば Bearer {token}
という風に Bearer
というprefixがある前提で動作します。このprefixを変更することはできますが、prefix自体を無しにするということを可能にするというのがこの修正です。
この変更により以下のような動作が可能です。
app.use('/apiKey/*', bearerAuth({ token, prefix: '', headerName: 'X-Api-Key' }))
app.get('/apiKey/*', (c) => {
handlerExecuted = true
return c.text('auth apiKey')
})
const req = new Request('http://localhost/apiKey/a')
req.headers.set('X-Api-Key', 'hoge')
const res = await app.request(req)
console.log(await res.text()) // output auth apiKey
factory.createApp()
をStableに変更
factory.createApp()
は今まで experimental でしたが、stableに変更されました。
WebSocketのクエリパラメータの配列をサポートするに修正
「4.4.10以降の修正」であったWebSocketのクエリパラメータをサポートしましたが、配列のパラメータがサポートされていなかったため、この修正で配列もサポートされるように修正されました。
Content-Type
と validator
のタイプが一致しなくてもエラーをスローしないように修正
Requestの
今までの validator
の振る舞いとして第一引数に json
という文字列を指定した場合はRequestの Content-Type
が application/json
でない場合はエラーとしていました。しかしRequestの Content-Type
が ultipart/form-data
やそもそも Content-Type
が指定されていない場合もエラーになるのは使い勝手としてあまり良くないためvalidatorは Content-Type
が一致しない場合は何も行わないという変更がされました。
上記の変更で 4.5.1 がリリースされています
4.5.1以降の修正
getRuntimeKey()
が other
になってしまわないように修正
Cloudflare Pagesで
どうやらCloudflare Pages(のProduction?)では getRuntimeKey()
の値取得のロジックに使用している navigator.userAgent
もっというと navigator
が使用出来ないために、ランタイムキーの判定が正しくないということが発生するようです。そこでエラーにならないように修正されました。
ただしこの修正の元となっているのはCloudflare Pagesで c.env
が正しく取得出来ない場合があるというIssueが元になっているようで、この修正でなぜ直るかまでは理解出来ていません。また c.env
が正しく取得出来ないのは Cloudflare Workersのバージョンである compatibility_date
を更新することで直るということも報告されているので、ちょっと問題としてはややこしそうです。
4.5.1以降の修正 その2
c.json
で readonly
が設定されたプロパティの型が正常に返らない不具合の修正
4.4.11以降の修正 その3に記載した「c.json
で返る型を JSON.stringify()
で返る型に限りなく近くする変更」の修正を行いましたが、readonly
の型が正しく返らない不具合が修正されました。
昨日の「Cloudflare Pagesで getRuntimeKey() が other になってしまわないように修正」をリバート
やはり修正の起因は c.env
の値が正常に取得出来ないということらしいですが、内容を読む限りCloudflare Workers上の問題であるように感じ取れます。昨日も書きましたが compatibility_date
で正常に動作するCloudflare Workersのバージョンを指定すると良いということです。
TypedResponse
を使用した回帰するジェネリックの型指定を行うと c.set
の型が正しく行えないための修正
このスクラップを書いていて今まで1,2位を争うくらい意味がわからない修正です。そもそもこの修正を行う起因となったIssueで TypedResponse
というResponseの型をジェネリックで指定する型があるのですが、これを contextVariables
の型に使用して回帰するというかなりトリッキーな型指定をすることで c.set
の型がおかしくなるという報告の元に修正されています。
が、そもそもなんでこの修正で直るかというのすら理解できてないです。どちらかというと元の使用方法が使用想定にない型指定なのかなと思います。
4.5.1以降の修正 その3
CSRF Protectionのmiddlewareの返り値として返す関数名のTypoを修正
CSRF Protection というmiddlewareがHonoにはありますが、この import { csrf } from 'hono/csrf'
の csrf
が返す関数名が誤っているので修正されました。
4.5.1以降の修正 その3
X-Powered-By
を削除しないオプションを追加
Secure Headers MiddlewareでResponse Headersの
フレームワークでよく付与されるResponse Headersに X-Powered-By
というどのフレームワークで動作しているかを返す値がありますが、Secure Headers Middlewareを使用するとその値が自動的に削除されていました。そこで残すために removePoweredBy
というオプションに false
を渡すとReponse Headersの値を削除しないというオプションが追加されました。
上記の変更で 4.5.2 がリリースされています
4.5.2以降の修正
Content-Typeの文字列チェックにダブルクォーテーションも許すように修正
Bunなどでは boundary
の部分にダブルクォーテーションが入ることがあるようです。現状はContent-Typeの文字列チェックにダブルクォーテーションを入ることを許していないためエラーとなるそうです。そこでContent-Typeにダブルクォーテーションを許すように修正されました。
application/json
を指定しつつ、 charset
を指定するとvalidationされない不具合を修正
Content-Typeに
Content-Typeには application/json
という値の後ろに charaset
を指定できるそうです。(もちろん application/json
だけではありません。)
Content-Type: application/json; charset=UTF-8
hono/validator
でJSONのチェックを行う場合はContent-Typeの値が application/json
という値である必要があるのですが、 charaset
の値が入ってくる想定がされておらず、 application/json
という値で終わることつまり、後には文字列が入らない想定となっており、validationがスキップされる動作となっていたことが原因のようです。
そこで、application/json
の後ろに値が入ってきても問題ないように正規表現のチェック処理が修正されました。
![ame-x@amex2189](https://res.cloudinary.com/zenn/image/fetch/s--7E6BGTlX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_70/https://storage.googleapis.com/zenn-user-upload/avatar/43b4b572a7.jpeg)
Content-Typeをtypoしてるかと
恥ずかしい…修正しました!
4.5.2以降の修正 その2
hono/jsx
でSVGに関する不具合の修正
このコミットには2つの内容が含まれています。
- SVGの中の
title
タグを使用するとhead
タグのtitle
が追加される不具合の修正 - SVGの属性の中で、ケバブケースで出力されない不具合の修正
まず、1つ目はReact 19と同様の動作をするために4.5系で処理が追加されましたが、SVGの中に使用する title
タグを使用してしまうと同様の動作をする不具合が修正されています。またSVGの中にはキャメルケースとケバブケースの属性がありますが、 hono/jsx
やReactはそうですが、属性は基本的にキャメルケースで指定します。その指定した属性がケバブケースで出力されないという不具合が修正されています。
hono/jsx
のDOMのレンダリング速度の改善
hono/jsx
で1コンポーネント内でそれなりのDOMの数(Issueで報告があったのは <div><span></span></div>
が、ただ28個あるコンポーネント)をレンダリングする速度が遅くなるようです。どうやらDOMが増える度に指数関数的に速度が遅くなる。
原因としてはDOMを生成する処理にどこにDOMを差し込むかを判定する処理が再帰的かつループ処理があり、それにより計算コストが増大していくようです。無駄なループ処理が実行されないように修正されています。
Service Worker Adapterを使用してルーティングが存在しないURLにアクセスするとエラーが発生する不具合の修正
基本的にはルーティングに存在しないURLへのアクセスは404だったり、何かしらのエラーのレンダリング処理が行われますが、Service Workers Adapterでは存在しないルーティングにアクセスするとHonoでエラーが起きるようです。
Failed to execute 'fetch' on 'WorkerGlobalScope': Illegal invocation`
というようなエラーが発生して、Service Workerでの fetch
が動作しないというエラーが起きるようです。それについて暫定ではありますが、 globalThis.self
から fetch
が取得できるならその fetch
を使用するという処理で修正されています。
上記の変更で 4.5.3 がリリースされています
4.5.3以降の修正
hono/jsx
で draggable
属性値に string(true)
を設定すると型エラーとならないように修正
hono/jsx
では draggable
は boolean
または undefined
で受け入れる型定義になっています。そのため "true"
という文字列の設定が出来ないため、受け入れる型が変更されました。
ただ、変更された型が以下です。
- draggable?: boolean | undefined
+ draggable?: 'true' | 'false' | undefined
ちなみにReactの場合は boolean
でも受け入れます。
<div draggable={true}>react dragable</div>
// => output dom: <div draggable="true">react dragable</div>
このようにReactの場合は draggable
属性は Booleanish
という型つまり、 boolean
でも string
でも受け入れて文字列で出力するという処理がされています。なのでちょっと修正方法が微妙なようは気もします。
4.5.3以降の修正 その2
CSSProperties
という style
属性に指定できる型をオーバーライドできるように修正
hono/jsx
では属性の型をオーバーライドして独自の型を加えることが出来ます。 style
属性には CSSProperties
という型があるので、それをオーバーライドしても型が反映されないという不具合が修正されました。
合わせて、 style
属性には {}
のような構造化のデータを渡す型となっていましたが、文字列を渡すことができるようにも型が修正されています。
WebSocket再接続時の初期化処理を独自に定義できるように修正
今まではWebSocketの再接続時はHonoの内部で new WebSocket
という風に新しいWebSocketクラスを生成します。また、生成するWebSocketの接続URLはオプションで渡すクエリパラメータも含める必要があるので、元のWebSocketと異なる事ができ、型の安全性が失われることがあるということです。
そこで、この修正ではWebSocketの再接続するための関数を渡すことが出来るようになり、オプションも合わせて定義できることから上記の問題を解消するという変更がされました。
const client = hc<AppType>(url, {
webSocket(url, options) {
return new ReconnectingWebSocket(url, options)
},
})
validator
の 'param'
を指定しつつ、必須のパラメータが1つの定義と指定しているにも関わらず、オプションとなってしまう型不具合の修正
ちょっとタイトルからわかりにくいですが、元となったIssueから参考のコードを拝借します。
const schema = z.object({ id: z.string() })
const paramValidator = zValidator("param", schema)
// in: {
// param: {
// id: string | undefined; < -- The type undefined is wrong
// }
}
このように必ず入ってくる値にも関わらずMiddlewareでは undefined
という誤った型が入ってきてしまい、わざわざ undefined
の処理が必要になるということです。
上記の変更で 4.5.4 がリリースされています
4.5.4以降の修正
hono/jsx
で Fragment だけや null
を返した場合にカラ文字列のHTMLを生成するように修正
よくあるレンダリングしたくない場合にFragmentだけ( <></>
)や null
を返してカラのHTML文字列を生成することがありますが、hono/jsx
ではそれを行うとエラーになってしまっていたようです。そこでそれがエラーにならずカラのHTML文字列を生成するように修正されました。
c.header
に設定できる値を型でサポートするように修正
以下のようにHonoではResponseのHeadersに値を設定できます。
app.get('/', c => {
c.header('Content-Type', 'text/plain')
c.text('ok')
})
このResponseのHeadersのキーは標準でいくつかあり、例えばCORSを設定するならば Access-Control-Allow-Headers
などがあります。この設定できるキーの入力をある程度型でサポートするというのがこの修正です。確かにHeadersは色々あるので、こういう地味なのは嬉しいですね。
4.5.4以降の修正 その2
hono/jsx
の draggable
属性について boolean
型も指定できるように修正
これは以前で「4.5.3以降の修正」で私が書いたReactでは draggable
属性には boolean
が指定できるのでちょっと互換性が異なるなというものが修正されたようです。
要は 'true'
'false'
の文字列に加えて boolean
型も指定できるようになったようです。
4.5.4以降の修正 その3
c.header
に Content-Type
を指定した場合の値を型でサポートするように修正
「4.5.4以降の修正」の修正の c.header
の修正に更に追加されたものと思って大丈夫です。前回の修正はReponse Headersのキーに関する型のサポートでしたが、今回はそのキーに Content-Type
を指定した場合の値の型がサポートされました。ですので、Content-Type
に application/json
というのをフル入力せず「json」と入力するだけで、 application/json
が候補として表示されるようになります。
serveStatic
を使用する際にディレクトリ名にドットを含ディレクトリ名を指定した場合に静的ファイルが返せない場合がある動作の修正
app.get('/favicon.ico', serveStatic({ path: './satic/hello.world' }))
上記のようの例の場合、 hello.world
はファイルなのか、ディレクトリなのかフレームワーク側では現在ファイルとして扱うぽいのですが、実際はディレクトリの場合に hello.world/index.html
を返してほしいが動作しないという振る舞いに対する動作に対応する修正されました。
フレームワーク側ではそれが判断できない場合にディレクトリとして、認識するために isDir
というオプションを渡してディレクトリとして認識させるということが可能になりました。
app.get('/favicon.ico', serveStatic({
path: './satic/hello.world',
isDir: (path) => {
return path === 'static/hello.world'
}
}))
これでディレクトリとして認識されて static/hello.world/index.html
を返してくれるというものになります。
上記の変更で 4.5.5 がリリースされています
4.5.5以降の修正
Uncaught (in promise)
)が発生するとクラッシュする可能性の不具合を修正
非同期コンポーネントでエラー(
PRに書いていますが、クラッシュする条件としては
- 非同期コンポーネントが複数ある
- 最初のPromiseが解決される前に後続のPromiseでエラーが発生する
テストコードをほぼそのまま転載しますが、こんな感じのコードで発生するようです。
const AsyncComponent = async () => {
await new Promise((resolve) => setTimeout(resolve, 10))
return <h1>Hello from async component</h1>
}
const AsyncErrorComponent = async () => {
await new Promise((resolve) => setTimeout(resolve, 0))
throw componentError
}
app.get('/', (c) => {
return c.html(
<>
<AsyncComponent />
<AsyncErrorComponent />
</>
)
})
というものです。非同期コンポーネント自体はReact 19と同様にHonoでも使えるようになったのですが、エラーとなった場合は onError
でハンドリングされないという不具合が修正されました。
4.5.5以降の修正 その2
multipart/form-data
のデータを送信した際にFormDataのvalidationが必ず失敗してしまう不具合の修正
const route = app.post(
'/posts',
zValidator(
'form',
z.object({
body: z.string(),
})
),
(c) => {
const validated = c.req.valid('form')
// ... use your validated data
}
)
HonoではこのようにFormDataのvalidationを行う際に引数に 'form'
という指定をしてvalidationを行うことが出来ますが、 Content-Type
に multipart/form-data
を指定した場合に必ずvalidationが失敗する不具合が修正されました。どうやら原因は Content-Type
のmultipartから始まる正規表現のチェックで空白の指定が誤っているため、Content-Type
が正常でないとみなされて、必ず失敗するといったことが発生したようです。
FormDataの同一キーにappendした際のvalidationを行う値は配列として保持するように修正
要は const form = new FormData()
を行った後に form.append('key', ...)
として同一キーに値を append
していくとそれは配列として扱うのですが、 validation時にFormデータからObjectとして値を復元する際に配列としてみなすように修正されました。(余談ですが、HonoはこのFormDataの配列として扱う修正が多いような気が…)
hono/jsx
metaタグの name
属性に補完が効くように型を追加
これは以前あった c.header
のキーおよび値をある程度、型で補完することができる機能のmetaタグ版です。これによりmetaタグの代表的なものは name
を指定時に補完が効くようになります。
上記の変更で 4.5.6 がリリースされています
4.5.6以降の修正
hono/jsx
にて script
タグが style
タグとして出力される不具合を修正
タイトルのままなんですが、 script
タグを指定しても style
タグとして出力されてしまう不具合が修正されました。
hono/jsx
のレンダリング速度を改善
hono/jsx
と他のライブラリとクライアントでのレンダリング速度を調べてくれた方の報告では、速度の早い順に Solid.js > Preact > React > Hono という順になったようです。それで一番遅いのであれば問題ないのですが、コンポーネント数が多いとブラウザでレンダリングが出来ずハングするようです。
そこで usualoma さんによる速度の改善が行われました。
大きい変更としては「再帰のロジックをやめ、ループで処理するように変更」「Object.definePropertiesの速度が遅いため使用しないよう変更」ということでReact並へのレンダリング速度に修正されました。
a
タグの target
属性に補完が効くように型を追加
以前に書いた c.header
や meta
タグと同様のシリーズの修正です。
a
の target
属性には _blank
などを指定することが出来ますが、これも入力補完が効くように修正されました。
Content-Type
に application/x-www-form-urlencoded
に追加して charset=UTF-8
を指定した場合にvalidationが正しく動作しない不具合修正
「4.5.2以降の修正」で application/json
で同じことがありましたが、 application/x-www-form-urlencoded
でも同様の不具合があり、修正されました。原因は以前と同じ正規表現的に文字コードが入ることがない条件指定となっていたことが原因です。
4.5.6以降の修正 その2
area
タグの target
属性や button
タグの formtarget
に補完が効くように型を追加
「4.5.6以降の修正」で書いた a
タグの target
属性と同じように他のタグでも target
および formtarget
属性の入力に補完が効くように型が修正されました。修正されたタグおよび属性は以下です。
-
area
タグのtarget
属性 -
base
タグのtarget
属性 -
button
タグのformtarget
属性 -
form
タグのformtarget
属性 -
input
タグのformtarget
属性
このシリーズまだまだありそうなのでコントリビュートチャンスではないでしょうか。
x.com
に変更
HonoのXアカウントのURLを
READMEにHonoの公式であるXのアカウントがありますが、そのURLが旧ドメインである twitter.com
であったため、 x.com
に変更されました。
4.5.6以降の修正 その3
hono/client
を使用すると正常なパスで呼び出されない不具合を修正
オプショナルのURLパラメータを定義したルーティングが
HonoではオプショナルのURLパラメータを持ったルーティングを定義することができます。
const app = new Hono()
app.get('/something/:firstId/:secondId/:version?', (c) => c.json({ ok: true }))
上記の例では firstId
および secondId
部分は必須ですが、 version
部分は無くてもこのルーティングに該当するという定義です。つまり /something/1/2
も /something/1/2/3
もこのルーティングで処理されます。
しかし、 hono/client
を使用したRPCを使用して、オプショナルな部分を渡さず呼び出すと呼び出されたURLは /something/1/2/undefined
という風に正常なURLとして渡って来ないという不具合が修正されました。
input
タグの type
および autocomplete
属性について補完が効くように型を修正
最近恒例のシリーズです。今度は input
タグについて type
と autocomplete
属性について入力が補完されるように修正されました。ただ、そこまで詳しく見ていないのですが、 autocomplete
に関してはまだまだ種類は多いようですが、これはあくまでReactと同等の補完ということで承認されています。
上記の変更で 4.5.7 がリリースされています
4.5.7以降の修正
Content-Type
の大文字小文字を考慮しないように修正
CSRF Protection middlewareでチェックする
CSRF対策を行うミドルウェアがありますが、チェック時に Content-Type
の大文字小文字を区別してチェックしていたため、CSRF対策として機能しない不具合が修正されました。
上記の変更で 4.5.8 がリリースされています
4.5.8以降の修正
TypeScript 5.6にした場合にテストが正常に動作しなくなるテストコードの修正
端的に表題の通りなんですが、じゃあなんで次期バージョンの5.6ではこれが通らなくなくなるの?っていうのはちょっと難しい内容です。
そもそもこのテストは型が指定の通りに型となっているかテストを書いています。で、5.6では5.5で発生しているジェネリックによる型評価に不具合があり、それが修正されてリリースされるようです。で、その不具合を対応したTypeScriptのバージョンを使用すると、元々はTypeScriptのバグのせいでテストが通っていたコードが失敗するようになったという経緯です。
4.5.8以降の修正 その2
NO_COLOR
という環境変数へのアクセス権を求められる動作の修正
denoの場合に 標準出力のカラーコード出力の確認ロジックが動作すると
Honoは内部的で標準出力する際に NO_COLOR
の環境変数を見て、標準出力にカラーコードを付与するかどうかの判定を行っているようです。ただ、コードを見るとすべての標準出力がそうではなく、現状は showRoutes
というすべてのルーティングを出力できる開発用の関数があるのですが、その関数で出力する標準出力に NO_COLOR
の環境変数でのカラーコードを出力するかどうかを判定していました。
しかし、Denoの場合は環境変数にアクセスするためには --allow-env
というパーミッションが必要になります。そこでDenoの場合は NO_COLOR
の環境変数を見るのではなく、 Deno.noColor
という --allow-env
パーミッションがなくても NO_COLOR
の環境変数を参照する値を見るように修正されました。
embed
タグや link
の type
属性について補完が効くように型を修正
今度は以下のタグの type
属性について入力が補完されるように修正されました。
- embed
- link
- object
- source
- script
- style
Pretty JSON Middlewareに整形を行うクエリパラメータを指定できるよう修正
Pretty JSON Middlewareを使えば、JSONのレスポンスが整形されたJSON(JSON.stringify({ sample }, null, 2)
)で表示することが出来ましたが、今まではスペースの数値しかオプションで指定できませんでした。なので、Pretty JSON Middlewareを使用すると使用するルーティングは prettye
というクエリパラメータを付与すると整形されるようになっていました。
今回は整形するための特定のクエリパラメータ(デフォルトでは pretty
というパラメータ)を指定することが出来るように変更がされました。オプションなので、以下のように space
と同様に指定することで使用することが出来ます。
app.use(
'*',
prettyJSON({
query: 'format',
})
)
上記の変更で 4.5.9 がリリースされています
4.5.9以降の修正
Compress Middleware
の圧縮を行わないようにする処理の追加
レスポンスBodyを圧縮して返すミドルウェアがありますが、いくつかの条件で圧縮を行わない条件が追加されました。
- エンコード済みの場合
- リクエストメソッドが
HEAD
の場合 -
Content-Length
が1024もしくは指定のサイズ以下の場合 - 圧縮を行わない
Content-Type
(application/jsonやx-www-form-urlencodedなど)の場合 - レスポンスHeadersの
Cache-Control
にno-transform
が付与されている場合
上記の条件のどれかに当てはまるとミドルウェアは圧縮を行わないようにするという処理に修正されました。
popover
属性や popovertargetaction
属性について補完が効くように型を修正
Popover APIの
新しめのAPIに Popover API というものがあるのですが(ざっくり書くとHTMLだけでポップオーバーを表示できるもので、JSでも書ける)、そのAPIを使用するための属性に popover
や popovertargetaction
という属性がありますが、これもある程度決まりのある値を指定します。
それが恒例のシリーズと化していますが、入力を補完するように型が修正されました。
4.5.9以降の修正 その2
button
タグと input
タグの formenctype
属性や formmethod
属性について補完が効くように型を修正
これをほぼ毎日書いてるとコントリビュータの方の名前を見るだけで、次はなんのシリーズと思えるくらいの安心感です。
今回は表題の通り input
タグなどに使用される formenctype
属性や formmethod
属性とその大本の form
タグの enctype
属性と method
に補完が効くように型が修正されました。
また、 form
タグの autocomplete
も一部補完が効くように修正されています。
Vitest をv2へバージョンアップ
Honoで使用されているテストフレームワークのVitestがv2へバージョンアップされました。v2のバージョンアップに伴い一部だけテストが書き換えられていますが、難なく変更されているようです。
4.5.9以降の修正 その3
Vitest v2へのアップデートに伴い deprecated な型の修正
Suite
という型が deprecated になっており、移行先の型である RunnerTestSuite
に修正されました。
logger middlewareで出力されるログの先頭からスペースを削除
logger middlewareを使用する <--
や -->
の先頭にスペース3つが先頭について状態で出力されていました。ただし、ここだけがスペースが挿入されているので他のログを合わせると位置がズレているということなので削除されました。
上記の変更で 4.5.10 がリリースされています
ErrorBoundary
によるエラーをキャッチした場合にクラッシュしてしまう不具合の修正
非同期コンポーネントを複数レンダリングする際に
表題を見てもわからないと思うので、Issueのコードをそのまま記載します。
const app = new Hono();
export const SlowComponent: FC = async () => {
await new Promise((resolve) => setTimeout(resolve, 100));
return <div></div>;
};
const BrokenComponent: FC = async () => {
await new Promise((resolve) => setTimeout(resolve, 10));
throw new Error("Out of service");
};
app.get("/", (ctx) => {
return ctx.html(
<>
<SlowComponent />
<ErrorBoundary fallback="An error occurred">
<BrokenComponent />
</ErrorBoundary>
</>,
);
});
上記の例では BrokenComponent
というコンポーネントはエラーを起こします。しかし ErrorBoundary
が存在するので一見問題なさそうに見えますが、その横にそれよりもレンダリングが遅い SlowComponent
というものが存在します。この場合エラーが起こった後よりもレンダリングを行おうとして処理が正常に行えないということが発生するようです。
この場合でも正常にレンダリング処理が行われるように修正がされました。修正は至ってシンプルで全レンダリングで1つでもエラーがあった場合にエラーを認識するように修正されています。
4.5.10以降の修正
head
タグ内のタグ要素の巻き上げ処理を改善しレンダリング速度を改善
hono/jsx
もReact19同様に title
や meta
タグなどの head
タグに配置するタグをどこに記述しても head
タグに巻き上げる仕様が入っています。しかし、その仕様の動作によりレンダリング速度が遅くなっていたということで、 title
や meta
タグなどが head
タグ内にある場合は処理しないようにして速度が改善されています。
Content-Typeの判定処理にオプショナルチェーンを使用するようにリファクタリング
HonoでBodyをパースする際にContent-Typeが multipart/form-data
か application/x-www-form-urlencoded
の場合にパースするという判定がありますが、その判定にContent-Typeが習得できない場合の判定条件があったので、それをオプショナルチェーンで書くようにリファクタリングされました。
Twitter
という文言を X
という文言に修正
README.md 内に
Twitterのサービス名が変わりましたが、HonoのREADME.mdに一部Twitterというサービス名が残っていたのでXに変更されました。
4.5.10以降の修正 その2
JSX RendererのJSDocへのドキュメントへのURLの指定方法を修正
JSDocにJSX RendererのドキュメントURLが記載されていますが、そのURLの指定方法を正しい記述に修正されました。
Basic認証の認証ロジックを切り出すリファクタリングを実施
Basic認証の認証部分に関する関数が独立して切り出されました。この切り出しには別で提案されているCasbinを使用した認可のミドルウェアを取り込むためのようです。
Casbinはあくまで認可を制御するのであって、認証は別で賄う必要があります。また、認可なので「誰が」というのを把握する必要があり、このリファクタリングでは認証部分で「誰が」というのを習得しやすくするためにリファクタリングが行われたという背景がありそうです。
上記の変更で 4.5.11 がリリースされています
4.5.11以降の修正
JSX Renderer MiddlewareのJSDoc内の名称を正しい名称に変更
"JSX" Renderer Middlewareですが、"JSR"と記載されているJSDocがあり、"JSX"という正しいJSDocドキュメントに修正されました。
@std/assert
をJSRから取得して使用するように修正
Denoのテストに使用する
Denoのランタイムで行うテストコードでassetを行うライブラリがJSRから取得して使用するように修正されました。
これにはどうやら背景があるようで、Denoにて先日リリースされたCache APIをサポートするための機能を開発のようですが、キャッシュの有効期限をHono側で制御するための処理が組み込まれる(Denoのキャッシュにはexpireを設定できないぽい?)ようで、その過程でテストライブラリを更新する必要があるようです。