Skip to content

feat!: migrate to h3 v2#4

Merged
antfu merged 1 commit into
mainfrom
antfu/create-pr
May 12, 2026
Merged

feat!: migrate to h3 v2#4
antfu merged 1 commit into
mainfrom
antfu/create-pr

Conversation

@antfu
Copy link
Copy Markdown
Contributor

@antfu antfu commented May 12, 2026

Summary

Migrates devframe's HTTP plumbing from h3 v1 to h3 v2 (2.0.1-rc.22, pinned), rewriting handlers onto v2's web-standard Request / Response / Headers primitives. h3 v2 no longer exports the App type, so devframe's public surface (CreateDevServerOptions.app, StartHttpAndWsOptions.app, StartedServer.app, onReady({ app })) renames AppH3; devframe bumps to 0.2.0 to reflect the typed-API break.

Adds a new mountStaticHandler(app, base, dir, options?) export in devframe/utils/serve-static to encapsulate h3 v2's route-matching change: app.use(base, handler) now matches base exactly (not subpaths) and does not strip the prefix from event.url.pathname. The helper bundles the ${base}/** route and withBase(base, ...) prefix stripping so the four static-mount call sites (production + tests) stay clean. serveStaticHandler and serveStaticNodeMiddleware are kept; the connection-meta handler collapses to () => ({...}) thanks to v2's auto JSON serialization.

Test plan

  • pnpm typecheck — clean
  • pnpm exec vitest run — 283/283 tests pass across 27 files (incl. the static-serve deployed-SPA contract test at both root and sub-path mounts)
  • pnpm lint — clean
  • Live dev-server smoke (examples/devframe-files-inspector): /__devframe-files-inspector/ → 200 HTML; /__connection.json{"backend":"websocket","websocket":...}; asset miss → 404; SPA fallback route → 200; POST → 405
  • tsnapi public-API snapshots regenerated to capture the AppH3 rename and the new mountStaticHandler export

Bump the h3 catalog pin to 2.0.1-rc.22 and rewrite devframe's HTTP
plumbing onto h3 v2's web-standard primitives. The h3 v1 `App` type is
no longer exported in v2, so devframe's public surface — the optional
`app` on `CreateDevServerOptions` / `StartHttpAndWsOptions`, the `app`
field on `StartedServer`, and the `onReady({ app })` callback — switches
from `App` to `H3`. Devframe bumps to `0.2.0` to reflect the breaking
type rename.

Internally: `createApp()` → `new H3()`, `toNodeListener` →
`toNodeHandler`, `defineEventHandler` → `defineHandler`,
`setResponseStatus`/`setResponseHeader` → `event.res.status` /
`event.res.headers.set(...)`, and the static file stream returns via
`Readable.toWeb(...)` instead of `sendStream`. The connection-meta
handler in `adapters/dev.ts` collapses to a plain `() => ({...})` now
that h3 auto-serializes object returns.

The trickiest v2 behavior change is route matching: `app.use(base, h)`
in v2 only matches the exact `base` path (not subpaths) and does not
strip the prefix from `event.url.pathname`. Static-dir mounts need
both. To avoid spreading that quirk across every call site, add a new
`mountStaticHandler(app, base, dir, options?)` export in
`devframe/utils/serve-static` that bundles the `${base}/**` route and
`withBase(base, ...)` prefix stripping. All four production/test mount
sites switch to it; the existing `serveStaticHandler` (h3 event
handler) and `serveStaticNodeMiddleware` (Connect middleware) are
kept unchanged.

Snapshots in `tests/__snapshots__/tsnapi/` are regenerated to reflect
the renamed types and the new `mountStaticHandler` export.
@antfu antfu merged commit f994c4b into main May 12, 2026
7 checks passed
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