Skip to content

feat(router): typed c.res response view on both surfaces (issue #57) - #79

Merged
devin-ai-integration[bot] merged 1 commit into
mainfrom
devin/1792346815-ctx-res-status
Oct 18, 2026
Merged

devin-ai-integration[bot] merged 1 commit into
mainfrom
devin/1792346815-ctx-res-status

Conversation

@espetro

@espetro espetro commented Oct 18, 2026

Copy link
Copy Markdown
Contributor

Summary

Implements #57 — c.res is now a typed, read-only response view that works identically on both surfaces, so hono/logger's outbound --> line (and the compress/etag/prettyJSON Hono middlewares that read c.res.status after await next()) get a real status code.

CtxRes view (CtxResCore + CtxRes interface/const, same pattern as Ctx/ReqView):

get res(): CtxResCore;             // on CtxCore — was ServerResponse | null

class CtxResCore {
  get status(): number;            // sink.statusOf()
  get statusCode(): number;        // Node-shaped alias
  header(name): string | null;     // sink.headerOf() — case-insensitive
  get headers(): { [k]: string };  // sink.headersMap()
  nodeRes(): ServerResponse | null; // raw escape hatch on the Node surface
}

ResponseSink read-back accessors — statusOf(), headerOf(), headersMap() added beside the existing write-side members; NodeResponseSink serves them from _status + _set, FetchResponseSink from status + hdrKeys/hdrVals (last-wins dedup). No instanceof/as — same discrimination style as sink.nodeRes().

Two adjacent gaps the unskipped tests surfaced, fixed here:

  • use() middleware never ran on the default 404 chain — _nfchain was only recomposed inside notFound(). use() now keeps _nfchain = compose(copyMw(this._mw), this._nf) in step (Hono semantics: app.use('*', mw) observes misses too). Per-route middleware snapshots in _mws are untouched.
  • c.req.url on the fetch surface is now the absolute Request URL (http://host/path), matching Hono — hono/logger derives the path via url.slice(url.indexOf("/", 8)), which misread the previous path-only value ("g" for /missing). Node surface stays path+query-only (IncomingMessage.url carries no host); c.req.path/queries() unchanged.

Downstream compat: @chttp/static and @chttp/stream already read c._nodeRes (unchanged); otlp-traces/prometheus-metrics examples keep working via the statusCode alias; docs/middleware.md raw-write snippets now go through c.res.nodeRes().

Tests

  • Unskips both outbound --> tests in examples/integrations/hono-middleware/test/logger.test.ts (200 and 404 — assertions strip ANSI status colors).
  • New coverage in router.test.js (Node surface) and fetch-passthrough.test.js (fetch surface): c.res.status after a 404 and c.res.header('content-type') after a c.json() write, observed from middleware after await next().

Gates

  • pnpm -r test: all green (83 router tests + workspace incl. the 3 hono-middleware tests).
  • bash scripts/scriptc-gate.sh: 100% static on all gates incl. bench/apps/chttp-fetch.ts.
  • pnpm lint / pnpm fmt: clean.

Link to Devin session: https://app.devin.ai/sessions/7b9534a797564587b9d517a904dfd268
Open in Devin Desktop: https://app.devin.ai/desktop/session/7b9534a797564587b9d517a904dfd268?variant=devin
Requested by: @espetro

@devin-ai-integration

Copy link
Copy Markdown
Contributor

I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".

  • Disable automatic comment, CI, and merge conflict monitoring

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

Deploying chttpjs with  Cloudflare Pages  Cloudflare Pages

Latest commit: d30e750
Status:⚡️  Build in progress...

View logs

- ResponseSink gains statusOf()/headerOf()/headersMap() read-back
  accessors, implemented on NodeResponseSink (_status + _set) and
  FetchResponseSink (status + hdrKeys/hdrVals).
- Ctx.res is now a CtxRes view (status, header(name), headers) backed by
  the sink — same shape under app.handler and app.fetch, so hono/logger's
  outbound --> line prints the real status after await next(). statusCode
  is kept as a Node-shaped alias; nodeRes() exposes the raw
  ServerResponse escape hatch (c._nodeRes unchanged for @chttp/static
  and @chttp/stream).
- use() middleware now also wraps the notFound chain (Hono semantics:
  app.use('*', mw) must observe 404s).
- c.req.url reports the absolute Request URL on the app.fetch surface —
  hono/logger derives the path via url.slice(url.indexOf('/', 8)).
- Unskips the two outbound --> logger tests; adds c.res.status (404) and
  c.res.header('content-type') (json) coverage on both surfaces.
- Updates otlp-traces/prometheus-metrics examples and docs/middleware.md
  to the new accessor.
@devin-ai-integration
devin-ai-integration Bot force-pushed the devin/1792346815-ctx-res-status branch from 92bf70e to d30e750 Compare October 18, 2026 18:19
@devin-ai-integration
devin-ai-integration Bot merged commit 46eff51 into main Oct 18, 2026
2 of 3 checks passed
@devin-ai-integration
devin-ai-integration Bot deleted the devin/1792346815-ctx-res-status branch October 18, 2026 18:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant