Take @hono/node-server to 2.1.1 #323

Closed
opened 2026-09-10 15:28:20 +00:00 by jcoffey-dev · 0 comments
Owner

Supersedes #317, which is the same bump without the runtime checking below.

The API we actually use is unchanged

Three entry points, all intact across the major:

Import v2
serve from @hono/node-server same (options, listeningListener) signature; fetch, hostname, port all still valid
RESPONSE_ALREADY_SENT from @hono/node-server/utils/response still exported
getConnInfo from @hono/node-server/conninfo still exported

The peer is hono: ^4 (we are on 4.13.7) and the engine >=20 (CI and the image are on 22).

What v2 actually changes

Two new defaults rather than removals:

  • overrideGlobalObjects (default true) — swaps the global Request/Response for a lighter implementation.
  • autoCleanupIncoming (default true) — destroys an incoming request the application never finished consuming. The README is explicit that this is the behaviour you want when accepting connections from arbitrary clients; leaving the default alone is the safe choice for us.

Which is why this was run, not reasoned about

Neither default is something 168 unit tests or a docker build would notice, and the risky surface is the SSE proxy at app.ts:742 — it writes to the raw ServerResponse and returns RESPONSE_ALREADY_SENT, exactly the mechanism a lightweight Response could break.

So I drove a real server against the mock backend on both versions with the same script:

Check 1.19.17 2.1.1
/api/health 200 200
/api/auth/login 200 + session 200 + session
/api/events (SSE) 200, text/event-stream, chunked, 0 bytes in 12s identical
POST /api/jmap with a body 200, body reached upstream and answered

The SSE stream carries no bytes on either version because the mock emits no events on that endpoint — worth stating plainly, since a lone "0 bytes" reads like a failure. It is the same 0 bytes before and after, with the same headers and the same chunked encoding, which is the comparison that matters.

The POST confirms request bodies still proxy through with autoCleanupIncoming on.

Verification

npm run typecheck, npm test (1153 web, 168 server) and npm run build all pass. The diff is the version range and 10 lines of lockfile.

Merged 2026-09-10 as coffey-labs/ihasmail@7d9d5b005c

Rebuilt from: git history, session transcript.

Supersedes #317, which is the same bump without the runtime checking below. ## The API we actually use is unchanged Three entry points, all intact across the major: | Import | v2 | |---|---| | `serve` from `@hono/node-server` | same `(options, listeningListener)` signature; `fetch`, `hostname`, `port` all still valid | | `RESPONSE_ALREADY_SENT` from `@hono/node-server/utils/response` | still exported | | `getConnInfo` from `@hono/node-server/conninfo` | still exported | The peer is `hono: ^4` (we are on 4.13.7) and the engine `>=20` (CI and the image are on 22). ## What v2 actually changes Two new defaults rather than removals: - **`overrideGlobalObjects`** (default `true`) — swaps the global Request/Response for a lighter implementation. - **`autoCleanupIncoming`** (default `true`) — destroys an incoming request the application never finished consuming. The README is explicit that this is the behaviour you want when accepting connections from arbitrary clients; leaving the default alone is the safe choice for us. ## Which is why this was run, not reasoned about Neither default is something 168 unit tests or a `docker build` would notice, and the risky surface is the SSE proxy at `app.ts:742` — it writes to the raw `ServerResponse` and returns `RESPONSE_ALREADY_SENT`, exactly the mechanism a lightweight Response could break. So I drove a real server against the mock backend on both versions with the same script: | Check | 1.19.17 | 2.1.1 | |---|---|---| | `/api/health` | 200 | 200 | | `/api/auth/login` | 200 + session | 200 + session | | `/api/events` (SSE) | `200`, `text/event-stream`, `chunked`, 0 bytes in 12s | identical | | `POST /api/jmap` with a body | — | 200, body reached upstream and answered | The SSE stream carries no bytes on either version because the mock emits no events on that endpoint — worth stating plainly, since a lone "0 bytes" reads like a failure. It is the same 0 bytes before and after, with the same headers and the same chunked encoding, which is the comparison that matters. The POST confirms request bodies still proxy through with `autoCleanupIncoming` on. ## Verification `npm run typecheck`, `npm test` (1153 web, 168 server) and `npm run build` all pass. The diff is the version range and 10 lines of lockfile. **Merged** 2026-09-10 as coffey-labs/ihasmail@7d9d5b005c86 <sub>Rebuilt from: git history, session transcript.</sub>
This repo is archived. You cannot comment on issues.