Scope
Under Perry-native compile, new Request(url, { headers }).headers returns a number (the internal FFI handle) instead of a Headers instance. Any code that calls req.headers.get(...), forEach, iteration, etc. immediately throws TypeError: (number).get is not a function. This is the root cause of Hono's app.fetch crashing on the first request — Hono's Context constructor reads request headers via the standard API.
Why
Hono is runtime-agnostic by design: the same (request: Request) => Promise<Response> contract runs on Workers, Bun, Deno, Node, Lambda — and (we want) Perry. The whole pattern depends on Request.headers being a real Headers object so middleware / context / routing can read headers uniformly. Without this, @hono/node-server, @hono/perry-server, any custom Fastify→Hono bridge, and Hono itself all break at the first header access.
This is the precondition for Hono on Perry working at all — every other piece in the stack reaches for req.headers.get(...) somewhere.
Repro (Perry 0.5.1026, perry-src commit f46c4c3)
```typescript
// repro.ts
const req = new Request('http://localhost/x', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: '{"k":1}',
});
console.log('method:', req.method, 'url:', req.url);
console.log('typeof headers:', typeof req.headers);
console.log('headers value:', String(req.headers));
console.log('content-type:', req.headers.get('content-type'));
```
```bash
perry compile repro.ts -o repro && ./repro
```
Observed:
```
method: POST url: http://localhost/x
typeof headers: number
headers value: 0
TypeError: (number).get is not a function
```
Expected (Node / Bun / Deno / Workers):
```
method: POST url: http://localhost/x
typeof headers: object
headers value: [object Headers]
content-type: application/json
```
Acceptance
typeof (new Request(url, { headers })).headers === 'object'
.headers.get(name), .headers.has(name), .headers.set(name, value), .headers.append(name, value), .headers.delete(name), .headers.forEach(cb), and the entries/keys/values iterators all work per Web Fetch spec
- A 10-line repro that constructs a Hono app with one route, calls
app.fetch(new Request(url, { headers: {...} })), awaits the result and reads it via .text() returns the expected body — no throws, no hangs.
Related
Scope
Under Perry-native compile,
new Request(url, { headers }).headersreturns a number (the internal FFI handle) instead of aHeadersinstance. Any code that callsreq.headers.get(...),forEach, iteration, etc. immediately throwsTypeError: (number).get is not a function. This is the root cause of Hono'sapp.fetchcrashing on the first request — Hono's Context constructor reads request headers via the standard API.Why
Hono is runtime-agnostic by design: the same
(request: Request) => Promise<Response>contract runs on Workers, Bun, Deno, Node, Lambda — and (we want) Perry. The whole pattern depends onRequest.headersbeing a realHeadersobject so middleware / context / routing can read headers uniformly. Without this,@hono/node-server,@hono/perry-server, any custom Fastify→Hono bridge, and Hono itself all break at the first header access.This is the precondition for Hono on Perry working at all — every other piece in the stack reaches for
req.headers.get(...)somewhere.Repro (Perry 0.5.1026, perry-src commit f46c4c3)
```typescript
// repro.ts
const req = new Request('http://localhost/x', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: '{"k":1}',
});
console.log('method:', req.method, 'url:', req.url);
console.log('typeof headers:', typeof req.headers);
console.log('headers value:', String(req.headers));
console.log('content-type:', req.headers.get('content-type'));
```
```bash
perry compile repro.ts -o repro && ./repro
```
Observed:
```
method: POST url: http://localhost/x
typeof headers: number
headers value: 0
TypeError: (number).get is not a function
```
Expected (Node / Bun / Deno / Workers):
```
method: POST url: http://localhost/x
typeof headers: object
headers value: [object Headers]
content-type: application/json
```
Acceptance
typeof (new Request(url, { headers })).headers === 'object'.headers.get(name),.headers.has(name),.headers.set(name, value),.headers.append(name, value),.headers.delete(name),.headers.forEach(cb), and the entries/keys/values iterators all work per Web Fetch specapp.fetch(new Request(url, { headers: {...} })), awaits the result and reads it via.text()returns the expected body — no throws, no hangs.Related