@sourceregistry/node-webserver 1.2.3 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -10,6 +10,7 @@ It provides:
10
10
 
11
11
  - A typed router with path params
12
12
  - Middleware support
13
+ - Route enhancers for typed request-scoped context
13
14
  - Router lifecycle hooks with `pre()` and `post()`
14
15
  - WebSocket routing
15
16
  - Cookie helpers
@@ -201,6 +202,7 @@ Available fields include:
201
202
 
202
203
  - `event.request`
203
204
  - `event.url`
205
+ - `event.fetch(...)`
204
206
  - `event.params`
205
207
  - `event.locals`
206
208
  - `event.platform`
@@ -208,6 +210,23 @@ Available fields include:
208
210
  - `event.getClientAddress()`
209
211
  - `event.setHeaders(...)`
210
212
 
213
+ `event.fetch(...)` is a server-aware variant of the native Fetch API:
214
+
215
+ - it resolves relative URLs against the current request URL
216
+ - it forwards `cookie` and `authorization` headers by default
217
+ - it dispatches same-origin requests internally through the router when possible
218
+
219
+ ```ts
220
+ app.GET("/posts", async (event) => {
221
+ const response = await event.fetch("/api/posts");
222
+ return new Response(await response.text(), {
223
+ headers: {
224
+ "content-type": response.headers.get("content-type") ?? "text/plain"
225
+ }
226
+ });
227
+ });
228
+ ```
229
+
211
230
  ## App Typings
212
231
 
213
232
  You can extend the request-local and platform typings by adding your own `app.d.ts` file in your project:
@@ -264,6 +283,49 @@ const requireApiKey = async (event, next) => {
264
283
  app.GET("/admin", () => new Response("secret"), requireApiKey);
265
284
  ```
266
285
 
286
+ ## Route Enhancers
287
+
288
+ Use `enhance()` when you want to derive typed request-scoped data for a single handler without putting everything on `event.locals`.
289
+
290
+ Each enhancer receives the normal request event and can:
291
+
292
+ - return an object to merge into `event.context`
293
+ - return `undefined` to contribute nothing
294
+ - return a `Response` to short-circuit the route early
295
+ - throw `error(...)`, `redirect(...)`, or `new Response(...)` for the same control flow used elsewhere in the router
296
+
297
+ ```ts
298
+ import { enhance, error } from "@sourceregistry/node-webserver";
299
+
300
+ app.GET("/admin", enhance(
301
+ async (event) => {
302
+ return new Response(JSON.stringify({
303
+ userId: event.context.user.id,
304
+ requestId: event.context.requestId
305
+ }), {
306
+ headers: {
307
+ "content-type": "application/json"
308
+ }
309
+ });
310
+ },
311
+ async (event) => {
312
+ const token = event.request.headers.get("authorization");
313
+ if (!token) {
314
+ error(401, { message: "Unauthorized" });
315
+ }
316
+
317
+ return {
318
+ user: { id: "u_1", role: "admin" }
319
+ };
320
+ },
321
+ async (event) => {
322
+ return {
323
+ requestId: event.locals.requestId
324
+ };
325
+ }
326
+ ));
327
+ ```
328
+
267
329
  ## Router Lifecycle Hooks
268
330
 
269
331
  Use `pre()` for logic that should run before route resolution, and `post()` for logic that should run after a response has been produced.
@@ -0,0 +1,11 @@
1
+ import { RequestEvent } from './types';
2
+ import { MaybePromise } from './types/MaybePromise';
3
+ type AnyFn = (...args: any[]) => any;
4
+ type ConcatReturnTypes<T extends AnyFn[]> = T extends [] ? {} : T extends [infer First, ...infer Rest] ? First extends AnyFn ? Awaited<ReturnType<First>> & ConcatReturnTypes<Rest extends AnyFn[] ? Rest : []> : {} : {};
5
+ export type EventEnhancer<Params extends Partial<Record<string, string>> = Partial<Record<string, string>>, RouteId extends string | null = string | null, Locals extends App.Locals = App.Locals, Context extends Record<string, any> = Record<string, any>> = (event: RequestEvent<Params, RouteId, Locals>) => MaybePromise<Context | void | undefined | Response>;
6
+ export type EnhancedRequestEvent<Params extends Partial<Record<string, string>> = Partial<Record<string, string>>, RouteId extends string | null = string | null, Locals extends App.Locals = App.Locals, Context extends Record<string, any> = Record<string, any>> = RequestEvent<Params, RouteId, Locals> & {
7
+ context: Context;
8
+ };
9
+ export type EnhancedRouteHandler<Params extends Partial<Record<string, string>> = Partial<Record<string, string>>, RouteId extends string | null = string | null, Locals extends App.Locals = App.Locals, Context extends Record<string, any> = Record<string, any>> = (event: EnhancedRequestEvent<Params, RouteId, Locals, Context>) => MaybePromise<Response>;
10
+ export declare const enhance: <Params extends Partial<Record<string, string>> = Partial<Record<string, string>>, RouteId extends string | null = string | null, Locals extends App.Locals = App.Locals, Enhancers extends EventEnhancer<Params, RouteId, Locals, any>[] = EventEnhancer<Params, RouteId, Locals, any>[], Context extends Awaited<ConcatReturnTypes<Enhancers>> = Awaited<ConcatReturnTypes<Enhancers>>>(handler: EnhancedRouteHandler<Params, RouteId, Locals, Context>, ...enhancers: Enhancers) => (event: RequestEvent<Params, RouteId, Locals>) => Promise<Response>;
11
+ export {};
package/dist/index.cjs.js CHANGED
@@ -1,5 +1,5 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const D=require("http"),$=require("https"),U=require("tls"),b=require("stream"),C=require("ws"),W=require("cookie"),F=require("node:fs"),A=require("node:fs/promises"),S=require("node:path"),z=require("node:stream");function L(a){return H(a)&&a.status>=400&&a.status<600}function v(a){return H(a)&&a.status>=300&&a.status<400}function H(a){return a instanceof Response}class B{constructor(e){this.data=new Map,this.windowMs=e.windowMs,this.startCleanup()}async incr(e){const t=Date.now();let s=this.data.get(e);return!s||t>=s.reset?(s={count:1,reset:t+this.windowMs},this.data.set(e,s)):s.count++,{current:s.count,reset:s.reset}}startCleanup(){this.cleanupInterval=setInterval(()=>{const e=Date.now();for(const[t,{reset:s}]of this.data)e>=s&&this.data.delete(t)},Math.min(this.windowMs,3e5))}stop(){this.cleanupInterval&&clearInterval(this.cleanupInterval)}async resetAll(){this.data.clear()}}function X(a){const{windowMs:e=6e4,max:t,key:s=c=>c.getClientAddress(),message:r="Too many requests, please try again later.",statusCode:n=429,headers:o="include",onRateLimit:i,store:u=new B({windowMs:e})}=a;return async(c,l)=>{const d=`rl:${s(c)}`,{current:h,reset:f}=await u.incr(d),y=Math.ceil((f-Date.now())/1e3);if(h>t){i&&i(c,{current:h,max:t,key:d});const g={status:n,headers:new Headers},p=g.headers;o==="include"&&(p.set("X-RateLimit-Limit",String(t)),p.set("X-RateLimit-Remaining","0"),p.set("X-RateLimit-Reset",String(Math.floor(f/1e3))),p.set("Retry-After",String(y)));let m;return typeof r=="string"?(m=r,p.set("Content-Type","text/plain")):(m=JSON.stringify(r),p.set("Content-Type","application/json")),new Response(m,g)}if(c.rateLimit={current:h,limit:t,reset:new Date(f),remaining:t-h},o==="include"){const g={"X-RateLimit-Limit":String(t),"X-RateLimit-Remaining":String(t-h),"X-RateLimit-Reset":String(Math.floor(f/1e3))},p=c.setHeaders;c.setHeaders=m=>{p({...g,...m})},p(g)}return l()}}const J=Object.freeze(Object.defineProperty({__proto__:null,fixedWindowLimit:X},Symbol.toStringTag,{value:"Module"})),G=["GET","POST","PUT","DELETE","PATCH","HEAD","OPTIONS"],V=["Accept","Accept-Language","Content-Language","Content-Type","Range"],K=["Authorization","X-Auth-Token","X-Requested-With","X-CSRF-Token","X-HTTP-Method-Override","X-Forwarded-For","X-Real-IP","X-Custom-Header"];function j(a,e){return!a||!e?!1:e==="*"?!0:e==="null"?a==="null":Array.isArray(e)?e.some(t=>j(a,t)):typeof e=="function"?e(a):e instanceof RegExp?e.test(a):a===e}function Y(a,e){const{origin:t="*"}=e;return a?t==="*"?e.credentials?a:"*":j(a,t)?a:null:t==="*"?"*":null}function Q(a={}){const{methods:e=G,allowedHeaders:t=K,exposedHeaders:s,credentials:r=!1,maxAge:n=86400,onResponse:o}=a,i="Origin,Access-Control-Request-Method,Access-Control-Request-Headers",u=e.join(","),c=[...V,...t].join(","),l=[["Vary",i],["Access-Control-Allow-Methods",u],["Access-Control-Allow-Headers",c]];return s&&l.push(["Access-Control-Expose-Headers",s.join(",")]),r&&l.push(["Access-Control-Allow-Credentials","true"]),n&&l.push(["Access-Control-Max-Age",n.toString()]),async(d,h)=>{const f=d.request,y=f.headers.get("Origin"),g=f.method==="OPTIONS"&&y!==null&&f.headers.has("Access-Control-Request-Method"),p=Y(y,a);if(g){if(!p)return new Response(null,{status:403});const w=new Response(null,{status:204});for(const[T,I]of l)w.headers.set(T,I);return w.headers.set("Access-Control-Allow-Origin",p),w}const m=await h();if(!m)return;if(!p)return m;const E=new Response(m.body,m);for(const[w,T]of l)E.headers.set(w,T);E.headers.set("Access-Control-Allow-Origin",p);let P=E;if(o){const w=o(P);w&&(P=w)}return P}}const Z=Object.freeze(Object.defineProperty({__proto__:null,policy:Q},Symbol.toStringTag,{value:"Module"})),k=["GET","PUT","POST","DELETE","PATCH","HEAD","OPTIONS"];class O{static{this.cache=new Map}static get(e){return this.cache.get(e)}static set(e,t){this.cache.set(e,t)}}class x{constructor(){this._routes=[],this._wsRoutes=[],this._nestedRouters=[],this._middlewares=[],this._preHandlers=[],this._postHandlers=[],this.routesSorted=!1,this.wsRoutesSorted=!1}get routes(){return this._routes}get nestedRouters(){return this._nestedRouters}GET(e,t,...s){return this.addHandler("GET",e,t,s)}POST(e,t,...s){return this.addHandler("POST",e,t,s)}PUT(e,t,...s){return this.addHandler("PUT",e,t,s)}PATCH(e,t,...s){return this.addHandler("PATCH",e,t,s)}DELETE(e,t,...s){return this.addHandler("DELETE",e,t,s)}HEAD(e,t,...s){return this.addHandler("HEAD",e,t,s)}OPTIONS(e,t,...s){return this.addHandler("OPTIONS",e,t,s)}USE(e,t,...s){return k.forEach(r=>this.addHandler(r,e,t,s)),this}action(e,t,...s){const r=async n=>{try{const o=await t(n);return this.formatActionResult(o)}catch(o){return this.handleActionError(o)}};return this.addHandler("POST",e,r,s)}use(e,t,...s){let r,n,o=s;Array.isArray(e)?([r,n]=e,o=e.length>2?e.slice(2):[]):(r=e,n=t);const i=this.normalizePrefix(r),{regex:u,paramNames:c,isCatchAll:l,priority:d}=this.createPrefixRegex(i);return this._nestedRouters.push({prefix:i,router:n,regex:u,paramNames:c,isCatchAll:l,priority:d,middlewares:o}),this}useMiddleware(...e){return this._middlewares.push(...e),this}pre(...e){return this._preHandlers.push(...e),this}post(...e){return this._postHandlers.push(...e),this}discard(e,t){return this._nestedRouters=this._nestedRouters.filter(s=>s.prefix!==e),this._routes=this._routes.filter(s=>s.path!==e||t&&s.method!==t),this}WS(e,t,...s){const{regex:r,paramNames:n,isCatchAll:o,priority:i}=this.createPathRegex(e);return this._wsRoutes.push({path:e,regex:r,paramNames:n,isCatchAll:o,priority:i,handler:t,middlewares:s}),this.wsRoutesSorted=!1,this}async canHandleWebSocket(e){return this.canHandleWebSocketAtPath(e,e.url.pathname)}async canHandleWebSocketAtPath(e,t){this.wsRoutesSorted||this.sortWsRoutes();for(const s of[...this._nestedRouters].sort((r,n)=>n.priority-r.priority)){const r=t.match(s.regex);if(!r||r.index!==0)continue;const n=r[0],o=t.slice(n.length)||"/",i={...e,params:{...e.params,...this.extractPrefixParams(s,n)}};if(await s.router.canHandleWebSocketAtPath(i,o))return!0}for(const s of this._wsRoutes)if(s.regex.test(t))return!0;return!1}async handleWebSocket(e,t){return this.handleWebSocketAtPath(e,t,e.url.pathname)}async handleWebSocketAtPath(e,t,s){this.wsRoutesSorted||this.sortWsRoutes();for(const r of[...this._nestedRouters].sort((n,o)=>o.priority-n.priority)){const n=s.match(r.regex);if(!n||n.index!==0)continue;const o=n[0],i=s.slice(o.length)||"/",u=this.extractPrefixParams(r,o),c={...e,params:{...e.params,...u}},l=[...this._middlewares,...r.middlewares],d=()=>r.router.handleWebSocketAtPath(c,t,i);if(await this.applyMiddlewaresWithList(c,l,d))return!0}for(const r of this._wsRoutes){if(!r.regex.test(s))continue;const n=s.match(r.regex);if(!n)continue;const o=Object.fromEntries(r.paramNames.map((l,d)=>[l,n[d+1]||""])),i={...e,params:{...e.params,...o},route:{...e.route,id:r.path},websocket:t},u=[...this._middlewares,...r.middlewares];if(await this.applyMiddlewaresWithList(i,u,()=>r.handler(i))===void 0)return!0}return!1}async handle(e){return this.handleAtPath(e,e.url.pathname)}async handleAtPath(e,t){try{const s=e.request.method;let r=await this.runPreHandlers(e);if(!r){const o=async()=>{const i=await this.handleNestedRouters(e,t);return i||(this.routesSorted||this.sortRoutes(),this.handleLocalRoutes(e,s,t))};r=await this.applyMiddlewaresWithList(e,this._middlewares,o)}const n=r||new Response("No Content",{status:204});return await this.runPostHandlers(e,n)}catch(s){if(H(s))return s;throw s}}async applyMiddlewaresWithList(e,t,s){const r=[...t],n=async o=>o>=r.length?s():r[o](e,()=>n(o+1));return n(0)}async runPreHandlers(e){for(const t of this._preHandlers){const s=await t(e);if(s instanceof Response)return s}}async runPostHandlers(e,t){let s=t;for(const r of this._postHandlers){const n=await r(e,s);n instanceof Response&&(s=n)}return s}addHandler(e,t,s,r=[]){const{regex:n,paramNames:o,isCatchAll:i,priority:u}=this.createPathRegex(t);return this._routes.push({method:e,path:t,regex:n,paramNames:o,isCatchAll:i,priority:u,handler:s,middlewares:r}),this.routesSorted=!1,this}createPathRegex(e){const t=O.get(e);if(t)return t;const s=[];let r=!1,n;const o=e.split("/").filter(Boolean);n=o.reduce((c,l)=>l.startsWith("[...")?c-10:l.startsWith("[[")?c-5:l.startsWith("[")?c-1:c+1,0);let i="^";for(const c of o)if(c.startsWith("[...")&&c.endsWith("]")){r=!0;const l=c.slice(4,-1);s.push(l),i+="/(.+)"}else if(c.startsWith("[[")&&c.endsWith("]]")){const l=c.slice(2,-2);s.push(l),i+="(?:/([^/]+))?"}else if(c.startsWith("[")&&c.endsWith("]")){const l=c.slice(1,-1);s.push(l),i+="/([^/]+)"}else i+="/"+c.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&");i+="/?$";const u={regex:new RegExp(i),paramNames:s,isCatchAll:r,priority:n};return O.set(e,u),u}createPrefixRegex(e){const t=[];let s=!1,r;const n=e.split("/").filter(Boolean);r=n.reduce((i,u)=>u.startsWith("[...")?i-10:u.startsWith("[[")?i-5:u.startsWith("[")?i-1:i+1,0);let o="^";for(const i of n)if(i.startsWith("[...")&&i.endsWith("]")){s=!0;const u=i.slice(4,-1);t.push(u),o+="/(.+)"}else if(i.startsWith("[[")&&i.endsWith("]]")){const u=i.slice(2,-2);t.push(u),o+="(?:/([^/]+))?"}else if(i.startsWith("[")&&i.endsWith("]")){const u=i.slice(1,-1);t.push(u),o+="/([^/]+)"}else o+="/"+i.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&");return o+="(?=/|$)",{regex:new RegExp(o),paramNames:t,isCatchAll:s,priority:r}}normalizePrefix(e){return e.startsWith("/")?e.replace(/\/$/,""):`/${e.replace(/\/$/,"")}`}extractPrefixParams(e,t){const s=t.match(e.regex);if(!s)return{};const r={};return e.isCatchAll&&e.paramNames.length===1?r[e.paramNames[0]]=s[1]?.replace(/^\//,"")||"":e.paramNames.forEach((n,o)=>{r[n]=s[o+1]||""}),r}async handleNestedRouters(e,t){const s=[...this._nestedRouters].sort((r,n)=>n.priority-r.priority);for(const r of s){const n=t.match(r.regex);if(!n||n.index!==0)continue;const o=n[0],i=t.slice(o.length)||"/",u=this.extractPrefixParams(r,o),c={...e,params:{...e.params,...u}},l=async()=>await r.router.handleAtPath(c,i),d=await this.applyMiddlewaresWithList(c,r.middlewares,l);if(d)return d}return null}async handleLocalRoutes(e,t,s){const r=new Set;let n=!1;for(const o of this._routes){if(!o.regex.test(s)||(r.add(o.method),o.method==="GET"&&(n=!0,r.add("HEAD")),!(o.method===t||t==="HEAD"&&o.method==="GET")))continue;const u=s.match(o.regex);if(!u)continue;const c=Object.fromEntries(o.paramNames.map((d,h)=>[d,u[h+1]||""]));e.params={...e.params,...c},e.route={...e.route,id:o.path};const l=()=>o.handler(e);return await this.applyMiddlewaresWithList(e,o.middlewares,l)}if(r.size>0||t==="HEAD"&&n){const o=[...r].join(", ");return t==="OPTIONS"?new Response(null,{status:200,headers:{Allow:o}}):new Response("Method Not Allowed",{status:405,headers:{Allow:o}})}return new Response("Not Found",{status:404})}sortRoutes(){this._routes.sort((e,t)=>t.priority-e.priority),this.routesSorted=!0}sortWsRoutes(){this._wsRoutes.sort((e,t)=>t.priority-e.priority),this.wsRoutesSorted=!0}formatActionResult(e){return e instanceof Response?e:e?.type==="failure"&&"status"in e?R.fail(e.status,e.data):R.success(200,e??void 0)}handleActionError(e){if(L(e))return R.error(e.status,{message:e.statusText||"Error"});if(v(e)){const t=e.headers.get("Location")||"/";return R.redirect(e.status,t)}return console.error(e),R.error(500,{message:"Internal Server Error"})}static New(){return new x}}const R={success:(a=200,e)=>new Response(JSON.stringify({data:e,type:"success",status:a}),{status:a,headers:{"Content-Type":"application/json"}}),redirect:(a=302,e)=>new Response(JSON.stringify({location:e,type:"redirect",status:a}),{status:a,headers:{"Content-Type":"application/json"}}),error:(a=500,e)=>new Response(JSON.stringify({error:e,type:"error",status:a}),{status:a,headers:{"Content-Type":"application/json"}}),fail:(a=400,e)=>new Response(JSON.stringify({data:e,type:"failure",status:a}),{status:a,headers:{"Content-Type":"application/json"}})};class ee{constructor(e,t){this.raw=e.headers.get("cookie")??"",this.setCookieHeader=t}get(e,t){return W.parse(this.raw,t)[e]}getAll(e){return Object.entries(W.parse(this.raw,e)).filter(([,t])=>t!==void 0).map(([t,s])=>({name:t,value:s}))}set(e,t,s){this.setCookieHeader(W.serialize(e,t,s))}delete(e,t){this.set(e,"",{...t,maxAge:0})}}class _ extends Error{constructor(e="Payload Too Large"){super(e),this.status=413,this.name="PayloadTooLargeError"}}class te extends x{constructor(e){super(),this.upgradeHandlerInstalled=!1,this.config=e??{type:"http",options:{}},this.wss=new C.WebSocketServer({noServer:!0,maxPayload:this.config.security?.maxWebSocketPayload??1024*1024})}get server(){if(!this._server){const e=(t,s)=>{this.handleRequest(t,s).catch(r=>{console.error("Unhandled request error:",r),s.statusCode=500,s.end("Internal Server Error")})};this._server=this.config.type==="https"?$.createServer(this.config.options,e):D.createServer(this.config.options,e)}return this._server}listen(...e){return this.upgradeHandlerInstalled||(this.installUpgradeHandler(),this.upgradeHandlerInstalled=!0),this.server.listen(...e),this}installUpgradeHandler(){this.server.on("upgrade",(e,t,s)=>{if(e.headers.upgrade?.toLowerCase()!=="websocket"){t.destroy();return}let r,n;try{r=this.toURL(e,!0),n=this.toRequest(e,r,!0)}catch{t.destroy();return}const o=this.toRequestEvent(n,r,{getClientAddress:()=>e.socket.remoteAddress??"127.0.0.1",setHeader:()=>{},pushSetCookie:()=>{}});this.canHandleWebSocket(o).then(i=>{if(!i||!this.isAllowedWebSocketOrigin(e)){t.destroy();return}this.wss.handleUpgrade(e,t,s,u=>{this.handleWebSocket(o,u).then(c=>{!c&&u.readyState===C.WebSocket.OPEN&&u.close(1008,"Route not found")}).catch(c=>{console.error("WebSocket routing error:",c),u.readyState===C.WebSocket.OPEN&&u.close(1011,"Internal error")})})}).catch(()=>t.destroy())})}close(e){this.wss.close(()=>{this.server.close(e)})}address(){return this.server.address()}get listening(){return this.server.listening}async handleRequest(e,t){if(!this.isRequestBodyAllowed(e)){t.statusCode=413,t.end("Payload Too Large");return}const s=this.toWebRequest(e),r=new URL(s.url),n={},o=[],i=this.toRequestEvent(s,r,{getClientAddress:()=>e.socket.remoteAddress??"127.0.0.1",setHeader:(c,l)=>{n[c.toLowerCase()]=l},pushSetCookie:c=>{o.push(c)}});let u;try{u=await this.handle(i)}catch(c){u=this.handleError(c)}for(const[c,l]of Object.entries(n))t.setHeader(c,l);o.length>0&&t.setHeader("Set-Cookie",o),await this.sendWebResponse(t,u)}toWebRequest(e){const t=this.toURL(e,!1);return this.toRequest(e,t,!1)}toRequest(e,t,s){const r={method:s?"GET":e.method,headers:this.toHeaders(e.headers),duplex:"half"};return!s&&e.method!=="GET"&&e.method!=="HEAD"&&(r.body=b.Readable.toWeb(this.wrapRequestBody(e))),new Request(t,r)}wrapRequestBody(e){const t=this.config.security?.maxRequestBodySize;if(!t)return e;let s=0;const r=new b.Transform({transform(n,o,i){if(s+=Buffer.byteLength(n),s>t){i(new _);return}i(null,n)}});return e.on("aborted",()=>r.destroy(new Error("Request aborted"))),e.on("error",n=>r.destroy(n)),e.pipe(r),r}toURL(e,t){const s=e.socket instanceof U.TLSSocket?t?"wss":"https":t?"ws":"http",r=this.resolveAuthority(e);return new URL(e.url??"/",`${s}://${r}`)}resolveAuthority(e){const t=this.config.security?.trustHostHeader?this.normalizeTrustedHost(e.headers.host):null;if(t)return t;const s=this.server.address();return s&&typeof s=="object"?`${s.address.includes(":")?`[${s.address}]`:s.address}:${s.port}`:e.socket.localPort?`127.0.0.1:${e.socket.localPort}`:"localhost"}normalizeTrustedHost(e){if(!e)return null;let t;try{t=new URL(`http://${e}`)}catch{return null}if(t.username||t.password||t.pathname!=="/"||t.search||t.hash)return null;const s=t.port?`${t.hostname}:${t.port}`:t.hostname,r=this.config.security?.allowedHosts;return!r||this.matchesValue(s,r)?s:null}matchesValue(e,t){return(Array.isArray(t)?t:[t]).some(r=>typeof r=="string"?r===e:r instanceof RegExp?r.test(e):r(e))}toHeaders(e){const t=new Headers;for(const[s,r]of Object.entries(e))if(r!==void 0){if(Array.isArray(r)){const n=s.toLowerCase()==="cookie"?r.join("; "):r.join(", ");t.set(s,n);continue}t.set(s,r)}return t}isRequestBodyAllowed(e){const t=this.config.security?.maxRequestBodySize;if(!t)return!0;const s=e.headers["content-length"];if(!s)return!0;const r=Number.parseInt(Array.isArray(s)?s[0]:s,10);return Number.isFinite(r)&&r<=t}handleError(e){if(e instanceof _)return new Response(e.message,{status:e.status});if(L(e))return new Response(JSON.stringify({error:e.statusText||"Error",status:e.status}),{status:e.status,headers:{"Content-Type":"application/json"}});if(v(e)){const t=e.headers.get("Location")||"/";return new Response(null,{status:e.status,headers:{Location:t}})}return console.error("Unhandled error:",e),new Response("Internal Server Error",{status:500})}async sendWebResponse(e,t){if(e.statusCode=t.status,t.headers.forEach((n,o)=>{e.setHeader(o,n)}),e.hasHeader("Server")||e.setHeader("Server","WebHTTPServer"),!t.body||this.shouldOmitResponseBody(t,e.req?.method)){e.end();return}const s=t.body.getReader(),r=b.Writable.toWeb(e).getWriter();try{for(;;){const{done:n,value:o}=await s.read();if(n)break;await r.write(o)}}finally{await r.close().catch(()=>{})}}shouldOmitResponseBody(e,t){return t==="HEAD"?!0:e.status===204||e.status===205||e.status===304}isAllowedWebSocketOrigin(e){const t=e.headers.origin;if(!t)return!0;const s=this.config.security?.allowedWebSocketOrigins;return s?this.matchesValue(t,s):!0}toRequestEvent(e,t,s){const r=new ee(e,s.pushSetCookie),n=this.config,o={},i={name:"WebHTTPServer"},u=new Set,c={request:e,url:t,cookies:r,getClientAddress:s.getClientAddress,get locals(){return o},get platform(){return i},params:{},route:{id:""},setHeaders:l=>{for(const[d,h]of Object.entries(l)){const f=d.toLowerCase();if(f==="set-cookie")throw new TypeError("Use event.cookies for Set-Cookie headers");if(u.has(f))throw new TypeError(`Header "${d}" has already been set`);u.add(f),s.setHeader(d,h)}}};return n.locals&&Object.assign(o,n.locals(c)),n.platform&&Object.assign(i,n.platform(c)),c}}const se={".avif":"image/avif",".css":"text/css; charset=utf-8",".gif":"image/gif",".html":"text/html; charset=utf-8",".ico":"image/x-icon",".jpg":"image/jpeg",".jpeg":"image/jpeg",".js":"text/javascript; charset=utf-8",".json":"application/json; charset=utf-8",".mjs":"text/javascript; charset=utf-8",".pdf":"application/pdf",".png":"image/png",".svg":"image/svg+xml; charset=utf-8",".txt":"text/plain; charset=utf-8",".wasm":"application/wasm",".webp":"image/webp",".xml":"application/xml; charset=utf-8"},re={index:"index.html",cacheControl:"public, max-age=0",dotFiles:"ignore"};async function M(a,e,t={}){const s=ce(e),r={...re,...t},n=await ne(a),o=oe(s,r.dotFiles);if(o instanceof Response)return o;const i=o.length>0?o.join(S.sep):"",u=S.resolve(n,i);if(!q(n,u))return new Response("Forbidden",{status:403});const c=await ae(u,n,r.index);if(c instanceof Response)return c;const l=await A.stat(c),d=new Headers({"content-length":String(l.size),"content-type":ie(c),"cache-control":r.cacheControl,"last-modified":l.mtime.toUTCString(),"x-content-type-options":"nosniff"});if(t.headers){const h=typeof t.headers=="function"?t.headers(c,l):t.headers;new Headers(h).forEach((f,y)=>{d.set(y,f)})}return new Response(z.Readable.toWeb(F.createReadStream(c)),{status:200,headers:d})}async function ne(a){return A.realpath(a)}function oe(a,e){if(a.includes("\0"))return new Response("Bad Request",{status:400});const t=a.replace(/\\/g,"/").split("/").filter(Boolean),s=[];for(const r of t){let n;try{n=decodeURIComponent(r)}catch{return new Response("Bad Request",{status:400})}if(!(!n||n===".")){if(n===".."||n.includes("/")||n.includes("\\")||n.includes("\0"))return new Response("Forbidden",{status:403});if(n.startsWith(".")){if(e==="deny")return new Response("Forbidden",{status:403});if(e!=="allow")return new Response("Not Found",{status:404})}s.push(n)}}return s}async function ae(a,e,t){try{if((await A.lstat(a)).isDirectory()){const r=S.resolve(a,t);return N(r,e)}return N(a,e)}catch{return new Response("Not Found",{status:404})}}async function N(a,e){try{const t=await A.realpath(a);return q(e,t)?(await A.stat(t)).isFile()?t:new Response("Not Found",{status:404}):new Response("Forbidden",{status:403})}catch{return new Response("Not Found",{status:404})}}function q(a,e){const t=S.relative(a,e);return t===""||!t.startsWith("..")&&!S.isAbsolute(t)}function ie(a){return se[S.extname(a).toLowerCase()]??"application/octet-stream"}function ce(a){return typeof a.params.path=="string"?a.params.path:a.url.pathname.replace(/^\/+/,"")}const ue=(a,e={})=>t=>M(a,t,e);function le(a,e={}){const t=[];if(e.comment)for(const s of e.comment.split(/\r?\n/))t.push(`: ${s}`);if(e.event&&t.push(`event: ${e.event}`),e.id&&t.push(`id: ${e.id}`),e.retry!==void 0&&t.push(`retry: ${e.retry}`),a!==void 0){const s=typeof a=="string"?a:JSON.stringify(a);for(const r of s.split(/\r?\n/))t.push(`data: ${r}`)}return`${t.join(`
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const F=require("http"),D=require("https"),U=require("tls"),T=require("stream"),C=require("ws"),v=require("cookie"),$=require("node:fs"),H=require("node:fs/promises"),S=require("node:path"),z=require("node:stream");function L(a){return A(a)&&a.status>=400&&a.status<600}function W(a){return A(a)&&a.status>=300&&a.status<400}function A(a){return a instanceof Response}class B{constructor(e){this.data=new Map,this.windowMs=e.windowMs,this.startCleanup()}async incr(e){const t=Date.now();let s=this.data.get(e);return!s||t>=s.reset?(s={count:1,reset:t+this.windowMs},this.data.set(e,s)):s.count++,{current:s.count,reset:s.reset}}startCleanup(){this.cleanupInterval=setInterval(()=>{const e=Date.now();for(const[t,{reset:s}]of this.data)e>=s&&this.data.delete(t)},Math.min(this.windowMs,3e5))}stop(){this.cleanupInterval&&clearInterval(this.cleanupInterval)}async resetAll(){this.data.clear()}}function X(a){const{windowMs:e=6e4,max:t,key:s=c=>c.getClientAddress(),message:r="Too many requests, please try again later.",statusCode:n=429,headers:o="include",onRateLimit:i,store:u=new B({windowMs:e})}=a;return async(c,l)=>{const d=`rl:${s(c)}`,{current:h,reset:p}=await u.incr(d),m=Math.ceil((p-Date.now())/1e3);if(h>t){i&&i(c,{current:h,max:t,key:d});const y={status:n,headers:new Headers},f=y.headers;o==="include"&&(f.set("X-RateLimit-Limit",String(t)),f.set("X-RateLimit-Remaining","0"),f.set("X-RateLimit-Reset",String(Math.floor(p/1e3))),f.set("Retry-After",String(m)));let w;return typeof r=="string"?(w=r,f.set("Content-Type","text/plain")):(w=JSON.stringify(r),f.set("Content-Type","application/json")),new Response(w,y)}if(c.rateLimit={current:h,limit:t,reset:new Date(p),remaining:t-h},o==="include"){const y={"X-RateLimit-Limit":String(t),"X-RateLimit-Remaining":String(t-h),"X-RateLimit-Reset":String(Math.floor(p/1e3))},f=c.setHeaders;c.setHeaders=w=>{f({...y,...w})},f(y)}return l()}}const J=Object.freeze(Object.defineProperty({__proto__:null,fixedWindowLimit:X},Symbol.toStringTag,{value:"Module"})),G=["GET","POST","PUT","DELETE","PATCH","HEAD","OPTIONS"],V=["Accept","Accept-Language","Content-Language","Content-Type","Range"],K=["Authorization","X-Auth-Token","X-Requested-With","X-CSRF-Token","X-HTTP-Method-Override","X-Forwarded-For","X-Real-IP","X-Custom-Header"];function N(a,e){return!a||!e?!1:e==="*"?!0:e==="null"?a==="null":Array.isArray(e)?e.some(t=>N(a,t)):typeof e=="function"?e(a):e instanceof RegExp?e.test(a):a===e}function Y(a,e){const{origin:t="*"}=e;return a?t==="*"?e.credentials?a:"*":N(a,t)?a:null:t==="*"?"*":null}function Q(a={}){const{methods:e=G,allowedHeaders:t=K,exposedHeaders:s,credentials:r=!1,maxAge:n=86400,onResponse:o}=a,i="Origin,Access-Control-Request-Method,Access-Control-Request-Headers",u=e.join(","),c=[...V,...t].join(","),l=[["Vary",i],["Access-Control-Allow-Methods",u],["Access-Control-Allow-Headers",c]];return s&&l.push(["Access-Control-Expose-Headers",s.join(",")]),r&&l.push(["Access-Control-Allow-Credentials","true"]),n&&l.push(["Access-Control-Max-Age",n.toString()]),async(d,h)=>{const p=d.request,m=p.headers.get("Origin"),y=p.method==="OPTIONS"&&m!==null&&p.headers.has("Access-Control-Request-Method"),f=Y(m,a);if(y){if(!f)return new Response(null,{status:403});const g=new Response(null,{status:204});for(const[b,I]of l)g.headers.set(b,I);return g.headers.set("Access-Control-Allow-Origin",f),g}const w=await h();if(!w)return;if(!f)return w;const E=new Response(w.body,w);for(const[g,b]of l)E.headers.set(g,b);E.headers.set("Access-Control-Allow-Origin",f);let P=E;if(o){const g=o(P);g&&(P=g)}return P}}const Z=Object.freeze(Object.defineProperty({__proto__:null,policy:Q},Symbol.toStringTag,{value:"Module"})),j=["GET","PUT","POST","DELETE","PATCH","HEAD","OPTIONS"];class O{static{this.cache=new Map}static get(e){return this.cache.get(e)}static set(e,t){this.cache.set(e,t)}}class x{constructor(){this._routes=[],this._wsRoutes=[],this._nestedRouters=[],this._middlewares=[],this._preHandlers=[],this._postHandlers=[],this.routesSorted=!1,this.wsRoutesSorted=!1}get routes(){return this._routes}get nestedRouters(){return this._nestedRouters}GET(e,t,...s){return this.addHandler("GET",e,t,s)}POST(e,t,...s){return this.addHandler("POST",e,t,s)}PUT(e,t,...s){return this.addHandler("PUT",e,t,s)}PATCH(e,t,...s){return this.addHandler("PATCH",e,t,s)}DELETE(e,t,...s){return this.addHandler("DELETE",e,t,s)}HEAD(e,t,...s){return this.addHandler("HEAD",e,t,s)}OPTIONS(e,t,...s){return this.addHandler("OPTIONS",e,t,s)}USE(e,t,...s){return j.forEach(r=>this.addHandler(r,e,t,s)),this}action(e,t,...s){const r=async n=>{try{const o=await t(n);return this.formatActionResult(o)}catch(o){return this.handleActionError(o)}};return this.addHandler("POST",e,r,s)}use(e,t,...s){let r,n,o=s;Array.isArray(e)?([r,n]=e,o=e.length>2?e.slice(2):[]):(r=e,n=t);const i=this.normalizePrefix(r),{regex:u,paramNames:c,isCatchAll:l,priority:d}=this.createPrefixRegex(i);return this._nestedRouters.push({prefix:i,router:n,regex:u,paramNames:c,isCatchAll:l,priority:d,middlewares:o}),this}useMiddleware(...e){return this._middlewares.push(...e),this}pre(...e){return this._preHandlers.push(...e),this}post(...e){return this._postHandlers.push(...e),this}discard(e,t){return this._nestedRouters=this._nestedRouters.filter(s=>s.prefix!==e),this._routes=this._routes.filter(s=>s.path!==e||t&&s.method!==t),this}WS(e,t,...s){const{regex:r,paramNames:n,isCatchAll:o,priority:i}=this.createPathRegex(e);return this._wsRoutes.push({path:e,regex:r,paramNames:n,isCatchAll:o,priority:i,handler:t,middlewares:s}),this.wsRoutesSorted=!1,this}async canHandleWebSocket(e){return this.canHandleWebSocketAtPath(e,e.url.pathname)}async canHandleWebSocketAtPath(e,t){this.wsRoutesSorted||this.sortWsRoutes();for(const s of[...this._nestedRouters].sort((r,n)=>n.priority-r.priority)){const r=t.match(s.regex);if(!r||r.index!==0)continue;const n=r[0],o=t.slice(n.length)||"/",i={...e,params:{...e.params,...this.extractPrefixParams(s,n)}};if(await s.router.canHandleWebSocketAtPath(i,o))return!0}for(const s of this._wsRoutes)if(s.regex.test(t))return!0;return!1}async handleWebSocket(e,t){return this.handleWebSocketAtPath(e,t,e.url.pathname)}async handleWebSocketAtPath(e,t,s){this.wsRoutesSorted||this.sortWsRoutes();for(const r of[...this._nestedRouters].sort((n,o)=>o.priority-n.priority)){const n=s.match(r.regex);if(!n||n.index!==0)continue;const o=n[0],i=s.slice(o.length)||"/",u=this.extractPrefixParams(r,o),c={...e,params:{...e.params,...u}},l=[...this._middlewares,...r.middlewares],d=()=>r.router.handleWebSocketAtPath(c,t,i);if(await this.applyMiddlewaresWithList(c,l,d))return!0}for(const r of this._wsRoutes){if(!r.regex.test(s))continue;const n=s.match(r.regex);if(!n)continue;const o=Object.fromEntries(r.paramNames.map((l,d)=>[l,n[d+1]||""])),i={...e,params:{...e.params,...o},route:{...e.route,id:r.path},websocket:t},u=[...this._middlewares,...r.middlewares];if(await this.applyMiddlewaresWithList(i,u,()=>r.handler(i))===void 0)return!0}return!1}async handle(e){return this.handleAtPath(e,e.url.pathname)}async handleAtPath(e,t){try{const s=e.request.method;let r=await this.runPreHandlers(e);if(!r){const o=async()=>{const i=await this.handleNestedRouters(e,t);return i||(this.routesSorted||this.sortRoutes(),this.handleLocalRoutes(e,s,t))};r=await this.applyMiddlewaresWithList(e,this._middlewares,o)}const n=r||new Response("No Content",{status:204});return await this.runPostHandlers(e,n)}catch(s){if(A(s))return s;throw s}}async applyMiddlewaresWithList(e,t,s){const r=[...t],n=async o=>o>=r.length?s():r[o](e,()=>n(o+1));return n(0)}async runPreHandlers(e){for(const t of this._preHandlers){const s=await t(e);if(s instanceof Response)return s}}async runPostHandlers(e,t){let s=t;for(const r of this._postHandlers){const n=await r(e,s);n instanceof Response&&(s=n)}return s}addHandler(e,t,s,r=[]){const{regex:n,paramNames:o,isCatchAll:i,priority:u}=this.createPathRegex(t);return this._routes.push({method:e,path:t,regex:n,paramNames:o,isCatchAll:i,priority:u,handler:s,middlewares:r}),this.routesSorted=!1,this}createPathRegex(e){const t=O.get(e);if(t)return t;const s=[];let r=!1,n;const o=e.split("/").filter(Boolean);n=o.reduce((c,l)=>l.startsWith("[...")?c-10:l.startsWith("[[")?c-5:l.startsWith("[")?c-1:c+1,0);let i="^";for(const c of o)if(c.startsWith("[...")&&c.endsWith("]")){r=!0;const l=c.slice(4,-1);s.push(l),i+="/(.+)"}else if(c.startsWith("[[")&&c.endsWith("]]")){const l=c.slice(2,-2);s.push(l),i+="(?:/([^/]+))?"}else if(c.startsWith("[")&&c.endsWith("]")){const l=c.slice(1,-1);s.push(l),i+="/([^/]+)"}else i+="/"+c.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&");i+="/?$";const u={regex:new RegExp(i),paramNames:s,isCatchAll:r,priority:n};return O.set(e,u),u}createPrefixRegex(e){const t=[];let s=!1,r;const n=e.split("/").filter(Boolean);r=n.reduce((i,u)=>u.startsWith("[...")?i-10:u.startsWith("[[")?i-5:u.startsWith("[")?i-1:i+1,0);let o="^";for(const i of n)if(i.startsWith("[...")&&i.endsWith("]")){s=!0;const u=i.slice(4,-1);t.push(u),o+="/(.+)"}else if(i.startsWith("[[")&&i.endsWith("]]")){const u=i.slice(2,-2);t.push(u),o+="(?:/([^/]+))?"}else if(i.startsWith("[")&&i.endsWith("]")){const u=i.slice(1,-1);t.push(u),o+="/([^/]+)"}else o+="/"+i.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&");return o+="(?=/|$)",{regex:new RegExp(o),paramNames:t,isCatchAll:s,priority:r}}normalizePrefix(e){return e.startsWith("/")?e.replace(/\/$/,""):`/${e.replace(/\/$/,"")}`}extractPrefixParams(e,t){const s=t.match(e.regex);if(!s)return{};const r={};return e.isCatchAll&&e.paramNames.length===1?r[e.paramNames[0]]=s[1]?.replace(/^\//,"")||"":e.paramNames.forEach((n,o)=>{r[n]=s[o+1]||""}),r}async handleNestedRouters(e,t){const s=[...this._nestedRouters].sort((r,n)=>n.priority-r.priority);for(const r of s){const n=t.match(r.regex);if(!n||n.index!==0)continue;const o=n[0],i=t.slice(o.length)||"/",u=this.extractPrefixParams(r,o),c={...e,params:{...e.params,...u}},l=async()=>await r.router.handleAtPath(c,i),d=await this.applyMiddlewaresWithList(c,r.middlewares,l);if(d)return d}return null}async handleLocalRoutes(e,t,s){const r=new Set;let n=!1;for(const o of this._routes){if(!o.regex.test(s)||(r.add(o.method),o.method==="GET"&&(n=!0,r.add("HEAD")),!(o.method===t||t==="HEAD"&&o.method==="GET")))continue;const u=s.match(o.regex);if(!u)continue;const c=Object.fromEntries(o.paramNames.map((d,h)=>[d,u[h+1]||""]));e.params={...e.params,...c},e.route={...e.route,id:o.path};const l=()=>o.handler(e);return await this.applyMiddlewaresWithList(e,o.middlewares,l)}if(r.size>0||t==="HEAD"&&n){const o=[...r].join(", ");return t==="OPTIONS"?new Response(null,{status:200,headers:{Allow:o}}):new Response("Method Not Allowed",{status:405,headers:{Allow:o}})}return new Response("Not Found",{status:404})}sortRoutes(){this._routes.sort((e,t)=>t.priority-e.priority),this.routesSorted=!0}sortWsRoutes(){this._wsRoutes.sort((e,t)=>t.priority-e.priority),this.wsRoutesSorted=!0}formatActionResult(e){return e instanceof Response?e:e?.type==="failure"&&"status"in e?R.fail(e.status,e.data):R.success(200,e??void 0)}handleActionError(e){if(L(e))return R.error(e.status,{message:e.statusText||"Error"});if(W(e)){const t=e.headers.get("Location")||"/";return R.redirect(e.status,t)}return console.error(e),R.error(500,{message:"Internal Server Error"})}static New(){return new x}}const R={success:(a=200,e)=>new Response(JSON.stringify({data:e,type:"success",status:a}),{status:a,headers:{"Content-Type":"application/json"}}),redirect:(a=302,e)=>new Response(JSON.stringify({location:e,type:"redirect",status:a}),{status:a,headers:{"Content-Type":"application/json"}}),error:(a=500,e)=>new Response(JSON.stringify({error:e,type:"error",status:a}),{status:a,headers:{"Content-Type":"application/json"}}),fail:(a=400,e)=>new Response(JSON.stringify({data:e,type:"failure",status:a}),{status:a,headers:{"Content-Type":"application/json"}})};class ee{constructor(e,t){this.raw=e.headers.get("cookie")??"",this.setCookieHeader=t}get(e,t){return v.parse(this.raw,t)[e]}getAll(e){return Object.entries(v.parse(this.raw,e)).filter(([,t])=>t!==void 0).map(([t,s])=>({name:t,value:s}))}set(e,t,s){this.setCookieHeader(v.serialize(e,t,s))}delete(e,t){this.set(e,"",{...t,maxAge:0})}}class q extends Error{constructor(e="Payload Too Large"){super(e),this.status=413,this.name="PayloadTooLargeError"}}class te extends x{constructor(e){super(),this.upgradeHandlerInstalled=!1,this.config=e??{type:"http",options:{}},this.wss=new C.WebSocketServer({noServer:!0,maxPayload:this.config.security?.maxWebSocketPayload??1024*1024})}get server(){if(!this._server){const e=(t,s)=>{this.handleRequest(t,s).catch(r=>{console.error("Unhandled request error:",r),s.statusCode=500,s.end("Internal Server Error")})};this._server=this.config.type==="https"?D.createServer(this.config.options,e):F.createServer(this.config.options,e)}return this._server}listen(...e){return this.upgradeHandlerInstalled||(this.installUpgradeHandler(),this.upgradeHandlerInstalled=!0),this.server.listen(...e),this}installUpgradeHandler(){this.server.on("upgrade",(e,t,s)=>{if(e.headers.upgrade?.toLowerCase()!=="websocket"){t.destroy();return}let r,n;try{r=this.toURL(e,!0),n=this.toRequest(e,r,!0)}catch{t.destroy();return}const o=this.toRequestEvent(n,r,{getClientAddress:()=>e.socket.remoteAddress??"127.0.0.1",setHeader:()=>{},pushSetCookie:()=>{}});this.canHandleWebSocket(o).then(i=>{if(!i||!this.isAllowedWebSocketOrigin(e)){t.destroy();return}this.wss.handleUpgrade(e,t,s,u=>{this.handleWebSocket(o,u).then(c=>{!c&&u.readyState===C.WebSocket.OPEN&&u.close(1008,"Route not found")}).catch(c=>{console.error("WebSocket routing error:",c),u.readyState===C.WebSocket.OPEN&&u.close(1011,"Internal error")})})}).catch(()=>t.destroy())})}close(e){this.wss.close(()=>{this.server.close(e)})}address(){return this.server.address()}get listening(){return this.server.listening}async handleRequest(e,t){if(!this.isRequestBodyAllowed(e)){t.statusCode=413,t.end("Payload Too Large");return}const s=this.toWebRequest(e),r=new URL(s.url),n={},o=[],i=this.toRequestEvent(s,r,{getClientAddress:()=>e.socket.remoteAddress??"127.0.0.1",setHeader:(c,l)=>{n[c.toLowerCase()]=l},pushSetCookie:c=>{o.push(c)}});let u;try{u=await this.handle(i)}catch(c){u=this.handleError(c)}for(const[c,l]of Object.entries(n))t.setHeader(c,l);o.length>0&&t.setHeader("Set-Cookie",o),await this.sendWebResponse(t,u)}toWebRequest(e){const t=this.toURL(e,!1);return this.toRequest(e,t,!1)}toRequest(e,t,s){const r={method:s?"GET":e.method,headers:this.toHeaders(e.headers),duplex:"half"};return!s&&e.method!=="GET"&&e.method!=="HEAD"&&(r.body=T.Readable.toWeb(this.wrapRequestBody(e))),new Request(t,r)}wrapRequestBody(e){const t=this.config.security?.maxRequestBodySize;if(!t)return e;let s=0;const r=new T.Transform({transform(n,o,i){if(s+=Buffer.byteLength(n),s>t){i(new q);return}i(null,n)}});return e.on("aborted",()=>r.destroy(new Error("Request aborted"))),e.on("error",n=>r.destroy(n)),e.pipe(r),r}toURL(e,t){const s=e.socket instanceof U.TLSSocket?t?"wss":"https":t?"ws":"http",r=this.resolveAuthority(e);return new URL(e.url??"/",`${s}://${r}`)}resolveAuthority(e){const t=this.config.security?.trustHostHeader?this.normalizeTrustedHost(e.headers.host):null;if(t)return t;const s=this.server.address();return s&&typeof s=="object"?`${s.address.includes(":")?`[${s.address}]`:s.address}:${s.port}`:e.socket.localPort?`127.0.0.1:${e.socket.localPort}`:"localhost"}normalizeTrustedHost(e){if(!e)return null;let t;try{t=new URL(`http://${e}`)}catch{return null}if(t.username||t.password||t.pathname!=="/"||t.search||t.hash)return null;const s=t.port?`${t.hostname}:${t.port}`:t.hostname,r=this.config.security?.allowedHosts;return!r||this.matchesValue(s,r)?s:null}matchesValue(e,t){return(Array.isArray(t)?t:[t]).some(r=>typeof r=="string"?r===e:r instanceof RegExp?r.test(e):r(e))}toHeaders(e){const t=new Headers;for(const[s,r]of Object.entries(e))if(r!==void 0){if(Array.isArray(r)){const n=s.toLowerCase()==="cookie"?r.join("; "):r.join(", ");t.set(s,n);continue}t.set(s,r)}return t}isRequestBodyAllowed(e){const t=this.config.security?.maxRequestBodySize;if(!t)return!0;const s=e.headers["content-length"];if(!s)return!0;const r=Number.parseInt(Array.isArray(s)?s[0]:s,10);return Number.isFinite(r)&&r<=t}handleError(e){if(e instanceof q)return new Response(e.message,{status:e.status});if(L(e))return new Response(JSON.stringify({error:e.statusText||"Error",status:e.status}),{status:e.status,headers:{"Content-Type":"application/json"}});if(W(e)){const t=e.headers.get("Location")||"/";return new Response(null,{status:e.status,headers:{Location:t}})}return console.error("Unhandled error:",e),new Response("Internal Server Error",{status:500})}async sendWebResponse(e,t){if(e.statusCode=t.status,t.headers.forEach((n,o)=>{e.setHeader(o,n)}),e.hasHeader("Server")||e.setHeader("Server","WebHTTPServer"),!t.body||this.shouldOmitResponseBody(t,e.req?.method)){e.end();return}const s=t.body.getReader(),r=T.Writable.toWeb(e).getWriter();try{for(;;){const{done:n,value:o}=await s.read();if(n)break;await r.write(o)}}finally{await r.close().catch(()=>{})}}shouldOmitResponseBody(e,t){return t==="HEAD"?!0:e.status===204||e.status===205||e.status===304}isAllowedWebSocketOrigin(e){const t=e.headers.origin;if(!t)return!0;const s=this.config.security?.allowedWebSocketOrigins;return s?this.matchesValue(t,s):!0}createEventFetch(e,t){return async(s,r)=>{const n=this.toEventFetchRequest(e,s,r);if(n.url.startsWith(`${e.url.origin}/`)){const o=this.toRequestEvent(n,new URL(n.url),t);try{return await this.handle(o)}catch(i){return this.handleError(i)}}return fetch(n)}}toEventFetchRequest(e,t,s){const r=t instanceof URL?t.toString():t,n=r instanceof Request?r:new Request(new URL(String(r),e.url),s);if(r instanceof Request&&!s)return this.withInheritedRequestHeaders(e,r);const o=new Headers(r instanceof Request?r.headers:s?.headers),i=s?.method??(r instanceof Request?r.method:void 0),u=s?.body??(r instanceof Request?r.body:void 0),c=u?"half":void 0;return this.inheritRequestHeader(e.request.headers,o,"cookie"),this.inheritRequestHeader(e.request.headers,o,"authorization"),new Request(n.url,{...s,method:i,headers:o,body:u,duplex:c})}withInheritedRequestHeaders(e,t){const s=new Headers(t.headers);return this.inheritRequestHeader(e.request.headers,s,"cookie"),this.inheritRequestHeader(e.request.headers,s,"authorization"),new Request(t,{headers:s,duplex:t.body?"half":void 0})}inheritRequestHeader(e,t,s){if(t.has(s))return;const r=e.get(s);r&&t.set(s,r)}toRequestEvent(e,t,s){const r=new ee(e,s.pushSetCookie),n=this.config,o={},i={name:"WebHTTPServer"},u=new Set,c={request:e,url:t,cookies:r,getClientAddress:s.getClientAddress,get locals(){return o},get platform(){return i},fetch:async(d,h)=>l(d,h),params:{},route:{id:null},setHeaders:d=>{for(const[h,p]of Object.entries(d)){const m=h.toLowerCase();if(m==="set-cookie")throw new TypeError("Use event.cookies for Set-Cookie headers");if(u.has(m))throw new TypeError(`Header "${h}" has already been set`);u.add(m),s.setHeader(h,p)}}},l=this.createEventFetch(c,s);return n.locals&&Object.assign(o,n.locals(c)),n.platform&&Object.assign(i,n.platform(c)),c}}const se={".avif":"image/avif",".css":"text/css; charset=utf-8",".gif":"image/gif",".html":"text/html; charset=utf-8",".ico":"image/x-icon",".jpg":"image/jpeg",".jpeg":"image/jpeg",".js":"text/javascript; charset=utf-8",".json":"application/json; charset=utf-8",".mjs":"text/javascript; charset=utf-8",".pdf":"application/pdf",".png":"image/png",".svg":"image/svg+xml; charset=utf-8",".txt":"text/plain; charset=utf-8",".wasm":"application/wasm",".webp":"image/webp",".xml":"application/xml; charset=utf-8"},re={index:"index.html",cacheControl:"public, max-age=0",dotFiles:"ignore"};async function k(a,e,t={}){const s=ce(e),r={...re,...t},n=await ne(a),o=oe(s,r.dotFiles);if(o instanceof Response)return o;const i=o.length>0?o.join(S.sep):"",u=S.resolve(n,i);if(!M(n,u))return new Response("Forbidden",{status:403});const c=await ae(u,n,r.index);if(c instanceof Response)return c;const l=await H.stat(c),d=new Headers({"content-length":String(l.size),"content-type":ie(c),"cache-control":r.cacheControl,"last-modified":l.mtime.toUTCString(),"x-content-type-options":"nosniff"});if(t.headers){const h=typeof t.headers=="function"?t.headers(c,l):t.headers;new Headers(h).forEach((p,m)=>{d.set(m,p)})}return new Response(z.Readable.toWeb($.createReadStream(c)),{status:200,headers:d})}async function ne(a){return H.realpath(a)}function oe(a,e){if(a.includes("\0"))return new Response("Bad Request",{status:400});const t=a.replace(/\\/g,"/").split("/").filter(Boolean),s=[];for(const r of t){let n;try{n=decodeURIComponent(r)}catch{return new Response("Bad Request",{status:400})}if(!(!n||n===".")){if(n===".."||n.includes("/")||n.includes("\\")||n.includes("\0"))return new Response("Forbidden",{status:403});if(n.startsWith(".")){if(e==="deny")return new Response("Forbidden",{status:403});if(e!=="allow")return new Response("Not Found",{status:404})}s.push(n)}}return s}async function ae(a,e,t){try{if((await H.lstat(a)).isDirectory()){const r=S.resolve(a,t);return _(r,e)}return _(a,e)}catch{return new Response("Not Found",{status:404})}}async function _(a,e){try{const t=await H.realpath(a);return M(e,t)?(await H.stat(t)).isFile()?t:new Response("Not Found",{status:404}):new Response("Forbidden",{status:403})}catch{return new Response("Not Found",{status:404})}}function M(a,e){const t=S.relative(a,e);return t===""||!t.startsWith("..")&&!S.isAbsolute(t)}function ie(a){return se[S.extname(a).toLowerCase()]??"application/octet-stream"}function ce(a){return typeof a.params.path=="string"?a.params.path:a.url.pathname.replace(/^\/+/,"")}const ue=(a,e={})=>t=>k(a,t,e),le=(a,...e)=>async t=>{const s={};for(const r of e){const n=await r(t);if(A(n))return n;n&&typeof n=="object"&&Object.assign(s,n)}return a(Object.assign(t,{context:s}))};function de(a,e={}){const t=[];if(e.comment)for(const s of e.comment.split(/\r?\n/))t.push(`: ${s}`);if(e.event&&t.push(`event: ${e.event}`),e.id&&t.push(`id: ${e.id}`),e.retry!==void 0&&t.push(`retry: ${e.retry}`),a!==void 0){const s=typeof a=="string"?a:JSON.stringify(a);for(const r of s.split(/\r?\n/))t.push(`data: ${r}`)}return`${t.join(`
2
2
  `)}
3
3
 
4
- `}const de=(a,e={})=>t=>{const s=new TextEncoder;let r=null,n=!1,o;const i=async()=>{if(!n){n=!0;try{await o?.()}finally{r?.close()}}},u=new ReadableStream({async start(c){r=c;const l=(d,h={})=>{n||c.enqueue(s.encode(le(d,h)))};t.request.signal.addEventListener("abort",()=>{i()},{once:!0});try{if(o=await a(t,l),!t.request.signal.aborted&&o===void 0)return;t.request.signal.aborted&&await i()}catch(d){n||c.error(d)}},async cancel(){await i()}});return new Response(u,{...e,headers:{"content-type":"text/event-stream","cache-control":"no-cache",connection:"keep-alive",...e.headers}})},he=async(a,e)=>{const t=JSON.stringify(await a);return new Response(t,{...e,headers:{"content-type":"application/json","content-length":Buffer.byteLength(t).toString(),...e?.headers}})};function fe(a,e){throw new Response(null,{status:a,headers:{location:e.toString()}})}function pe(a,e){throw new Response(JSON.stringify(typeof e=="string"?{message:e}:e),{status:a,headers:{"content-type":"application/json"}})}const me=async(a,e)=>{const t=await a;return new Response(t,{...e,headers:{"content-type":"text/plain","content-length":Buffer.byteLength(t).toString(),...e?.headers}})},we=async(a,e)=>{const t=await a;return new Response(t,{...e,headers:{"content-type":"text/html","content-length":Buffer.byteLength(t).toString(),...e?.headers}})};exports.Action=R;exports.CORS=Z;exports.RateLimiter=J;exports.RequestMethods=k;exports.Router=x;exports.WebServer=te;exports.dir=ue;exports.error=pe;exports.html=we;exports.isHttpError=L;exports.isRedirect=v;exports.isResponse=H;exports.json=he;exports.redirect=fe;exports.serveStatic=M;exports.sse=de;exports.text=me;
4
+ `}const he=(a,e={})=>t=>{const s=new TextEncoder;let r=null,n=!1,o;const i=async()=>{if(!n){n=!0;try{await o?.()}finally{r?.close()}}},u=new ReadableStream({async start(c){r=c;const l=(d,h={})=>{n||c.enqueue(s.encode(de(d,h)))};t.request.signal.addEventListener("abort",()=>{i()},{once:!0});try{if(o=await a(t,l),!t.request.signal.aborted&&o===void 0)return;t.request.signal.aborted&&await i()}catch(d){n||c.error(d)}},async cancel(){await i()}});return new Response(u,{...e,headers:{"content-type":"text/event-stream","cache-control":"no-cache",connection:"keep-alive",...e.headers}})},fe=async(a,e)=>{const t=JSON.stringify(await a);return new Response(t,{...e,headers:{"content-type":"application/json","content-length":Buffer.byteLength(t).toString(),...e?.headers}})};function pe(a,e){throw new Response(null,{status:a,headers:{location:e.toString()}})}function me(a,e){throw new Response(JSON.stringify(typeof e=="string"?{message:e}:e),{status:a,headers:{"content-type":"application/json"}})}const we=async(a,e)=>{const t=await a;return new Response(t,{...e,headers:{"content-type":"text/plain","content-length":Buffer.byteLength(t).toString(),...e?.headers}})},ge=async(a,e)=>{const t=await a;return new Response(t,{...e,headers:{"content-type":"text/html","content-length":Buffer.byteLength(t).toString(),...e?.headers}})};exports.Action=R;exports.CORS=Z;exports.RateLimiter=J;exports.RequestMethods=j;exports.Router=x;exports.WebServer=te;exports.dir=ue;exports.enhance=le;exports.error=me;exports.html=ge;exports.isHttpError=L;exports.isRedirect=W;exports.isResponse=A;exports.json=fe;exports.redirect=pe;exports.serveStatic=k;exports.sse=he;exports.text=we;
5
5
  //# sourceMappingURL=index.cjs.js.map