Context
srvx/body-limit's limitRequestBody(request, max) rebuilds the request via new Request(request, { body, duplex }):
https://github.com/h3js/srvx/blob/main/src/body-limit.ts
That's fine for a plain web Request, but it doesn't work on srvx's own ServerRequest — which is exactly the object srvx hands to handlers (event.req in h3). We hit this while wiring per-handler body limits in h3 (h3js/h3#1500).
Problem 1 — throws on the Node ServerRequest
Passing srvx's Node ServerRequest into new Request(request, ...) throws:
TypeError: Cannot read private member #state from an object whose class did not declare it
i.e. the Node adapter's request isn't a native Request under the hood, so new Request(serverRequest, init) can't re-wrap it. So limitRequestBody currently can't be applied to event.req on Node at all.
Problem 2 — drops runtime augmentation
Even where the rebuild succeeds (web/Deno), the result is a plain Request — it loses srvx's augmentation (runtime, waitUntil, ip, context). A downstream layer that swaps the request back in then has to re-copy those props one-by-one, which is fragile (a newly-added augmentation prop silently drops off).
Repro
import { limitRequestBody } from "srvx/body-limit";
export default {
fetch(req) { // req is srvx ServerRequest
const limited = limitRequestBody(req, 1024); // throws on Node
return new Response("ok");
},
};
Proposed fix
Make limitRequestBody accept and return a ServerRequest intact. Two options:
-
Proxy-wrap instead of rebuild — route body reads (body / text / json / formData / arrayBuffer / blob / bytes) through a single lazy limitBodyStream, and pass everything else through with Reflect.get. No new Request, no duplex hack, and augmentation is preserved for free. This is what we ended up doing in h3 as a local workaround:
https://github.com/h3js/h3/blob/perf/body-limit/src/utils/internal/body.ts (limitRequestBody)
-
Reconstruct through srvx's ServerRequest constructor, carrying augmentation, so the Node #state path is handled internally.
If limitRequestBody preserved ServerRequest, h3 could drop its copy and just call the srvx export directly. Happy to send a PR (option 1) if you'd like.
Context
srvx/body-limit'slimitRequestBody(request, max)rebuilds the request vianew Request(request, { body, duplex }):https://github.com/h3js/srvx/blob/main/src/body-limit.ts
That's fine for a plain web
Request, but it doesn't work on srvx's ownServerRequest— which is exactly the object srvx hands to handlers (event.reqin h3). We hit this while wiring per-handler body limits in h3 (h3js/h3#1500).Problem 1 — throws on the Node
ServerRequestPassing srvx's Node
ServerRequestintonew Request(request, ...)throws:i.e. the Node adapter's request isn't a native
Requestunder the hood, sonew Request(serverRequest, init)can't re-wrap it. SolimitRequestBodycurrently can't be applied toevent.reqon Node at all.Problem 2 — drops runtime augmentation
Even where the rebuild succeeds (web/Deno), the result is a plain
Request— it loses srvx's augmentation (runtime,waitUntil,ip,context). A downstream layer that swaps the request back in then has to re-copy those props one-by-one, which is fragile (a newly-added augmentation prop silently drops off).Repro
Proposed fix
Make
limitRequestBodyaccept and return aServerRequestintact. Two options:Proxy-wrap instead of rebuild — route body reads (
body/text/json/formData/arrayBuffer/blob/bytes) through a single lazylimitBodyStream, and pass everything else through withReflect.get. Nonew Request, noduplexhack, and augmentation is preserved for free. This is what we ended up doing in h3 as a local workaround:https://github.com/h3js/h3/blob/perf/body-limit/src/utils/internal/body.ts (
limitRequestBody)Reconstruct through srvx's ServerRequest constructor, carrying augmentation, so the Node
#statepath is handled internally.If
limitRequestBodypreservedServerRequest, h3 could drop its copy and just call the srvx export directly. Happy to send a PR (option 1) if you'd like.