lacis 0.3.1 → 0.3.3

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
@@ -2,6 +2,8 @@
2
2
 
3
3
  Zero-dependency TypeScript web framework with file-based routing.
4
4
 
5
+ **Documentation:** [lacis.lycia.dev](https://lacis.lycia.dev)
6
+
5
7
  ## Features
6
8
 
7
9
  - **File-based routing** — routes generated automatically from your `routes/` folder
@@ -34,14 +36,15 @@ All commands accept `--routes <dir>` to override the default `./routes` director
34
36
  ```
35
37
  my-app/
36
38
  routes/
37
- +middleware.ts # global middleware
38
- index.ts # GET /
39
+ +middleware.global.ts # cascades to all routes
40
+ index.ts # GET /
39
41
  users/
40
- index.ts # GET /users, POST /users
42
+ index.ts # GET /users, POST /users
41
43
  [id]/
42
- index.ts # GET /users/:id
44
+ index.ts # GET /users/:id
43
45
  api/
44
- +middleware.ts # middleware scoped to /api/*
46
+ +middleware.ts # exact to /api — does NOT cascade
47
+ +middleware.global.ts # cascades to /api/* and below
45
48
  items/
46
49
  index.ts
47
50
  server.ts
@@ -199,10 +202,14 @@ The spec is built from all `defineHandler` routes. Routes without `defineHandler
199
202
 
200
203
  ## Middleware
201
204
 
202
- Create a `+middleware.ts` file in any route directory. It applies to all routes at and below that path.
205
+ There are two middleware file conventions with different scoping behaviors.
206
+
207
+ **`+middleware.global.ts` — cascading**
208
+
209
+ Applies to the current directory and all subdirectories.
203
210
 
204
211
  ```ts
205
- // routes/api/+middleware.ts
212
+ // routes/api/+middleware.global.ts — runs for /api, /api/users, /api/users/:id, etc.
206
213
  import type { Request, Response } from 'lacis'
207
214
 
208
215
  export const beforeRequest = async (req: Request, res: Response) => {
@@ -221,6 +228,19 @@ export const onError = async (req: Request, res: Response, context: any) => {
221
228
  }
222
229
  ```
223
230
 
231
+ **`+middleware.ts` — exact path only**
232
+
233
+ Applies only to routes at that directory level. Does **not** cascade into subdirectories.
234
+
235
+ ```ts
236
+ // routes/api/+middleware.ts — runs for /api only, NOT /api/users
237
+ import type { Request, Response } from 'lacis'
238
+
239
+ export const beforeRequest = async (req: Request, res: Response) => {
240
+ // ...
241
+ }
242
+ ```
243
+
224
244
  Returning `false` from `beforeRequest` stops the request pipeline.
225
245
 
226
246
  **Global middleware via server config**
@@ -235,6 +255,26 @@ createServer(routesDir, {
235
255
  })
236
256
  ```
237
257
 
258
+ ## Lifecycle hooks
259
+
260
+ ```ts
261
+ createServer(routesDir, {
262
+ hooks: {
263
+ onNotFound: async (req, res) => {
264
+ res.status(404).json({ error: 'Not found', path: req.url })
265
+ },
266
+
267
+ onShutdown: async () => {
268
+ // close DB connections, flush logs, etc.
269
+ },
270
+ },
271
+ })
272
+ ```
273
+
274
+ `onNotFound` is called when no route matches. If it sends a response, the default `404` is skipped. If it returns without sending, the default `{ error: "Not Found", code: 404 }` is used.
275
+
276
+ `onShutdown` is called during graceful shutdown (SIGINT / SIGTERM / SIGHUP), before the server closes.
277
+
238
278
  ## CORS
239
279
 
240
280
  ```ts
@@ -288,24 +328,54 @@ Sets `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` headers o
288
328
  import type { Request, Response } from 'lacis'
289
329
 
290
330
  export async function GET(req: Request, res: Response) {
291
- res.initSSE()
331
+ const sse = res.initSSE()
292
332
 
293
- res.sseJson({ status: 'connected' })
294
- res.sseEvent('update', { id: 1, value: 42 })
295
- res.sseClose()
333
+ sse.json({ status: 'connected' })
334
+ sse.event('update', { id: 1, value: 42 })
335
+ sse.close()
296
336
  }
297
337
  ```
298
338
 
339
+ `res.initSSE(options?)` returns an `SSEContext` object:
340
+
299
341
  | Method | Description |
300
342
  |---|---|
301
- | `res.initSSE(options?)` | Initialize SSE response |
302
- | `res.sseSend(data)` | Send raw string data |
303
- | `res.sseJson(data)` | Send JSON data |
304
- | `res.sseEvent(event, data)` | Send named event with JSON data |
305
- | `res.sseComment(comment)` | Send a comment (keepalive) |
306
- | `res.sseId(id)` | Set event ID |
307
- | `res.sseRetry(ms)` | Set client retry interval |
308
- | `res.sseClose(comment?)` | Close the connection |
343
+ | `sse.send(data)` | Send raw string data |
344
+ | `sse.json(data)` | Send JSON data |
345
+ | `sse.event(event, data)` | Send named event with JSON data |
346
+ | `sse.comment(text)` | Send a comment (keepalive) |
347
+ | `sse.id(id)` | Set event ID |
348
+ | `sse.retry(ms)` | Set client retry interval |
349
+ | `sse.close(comment?)` | Close the connection |
350
+ | `sse.error(event, message, code?, details?)` | Send error event and close |
351
+
352
+ **Bun: call `initSSE()` before any `await`**
353
+
354
+ On Bun, the response type (streaming vs buffered) must be decided synchronously before the first `await`. Calling `initSSE()` after an `await` throws at runtime.
355
+
356
+ ```ts
357
+ // ✗ throws on Bun
358
+ export async function GET(req: Request, res: Response) {
359
+ const data = await fetchData()
360
+ const sse = res.initSSE() // too late
361
+ }
362
+
363
+ // ✓ fetch data first, then init
364
+ export async function GET(req: Request, res: Response) {
365
+ const data = await fetchData()
366
+ const sse = res.initSSE() // works on Node/Netlify/Vercel but not Bun
367
+ }
368
+
369
+ // ✓ workaround: initSSE before any await
370
+ export async function GET(req: Request, res: Response) {
371
+ const sse = res.initSSE()
372
+ const data = await fetchData()
373
+ sse.json(data)
374
+ sse.close()
375
+ }
376
+ ```
377
+
378
+ This is a constraint of Bun's HTTP model. Once the handler's first `await` resolves, response headers are committed and the streaming decision is final. Node.js, Vercel, and Netlify do not have this constraint.
309
379
 
310
380
  **Client**
311
381
 
@@ -1,4 +1,4 @@
1
- import { k as Adapter } from '../index-BPqzMBG-.js';
1
+ import { k as Adapter } from '../index-pGWdfH82.js';
2
2
  import 'http';
3
3
 
4
4
  declare const nodeAdapter: Adapter;
@@ -1 +1 @@
1
- export{O as bunAdapter,P as getAdapter,N as netlifyAdapter,L as nodeAdapter,M as vercelAdapter}from'../chunk-PV6YEQ6J.js';
1
+ export{X as bunAdapter,Y as getAdapter,W as netlifyAdapter,U as nodeAdapter,V as vercelAdapter}from'../chunk-XWERCAXC.js';
@@ -0,0 +1,26 @@
1
+ import Q,{join}from'path';import Je from'fs/promises';import {existsSync}from'fs';import J from'cluster';import Te,{IncomingMessage,ServerResponse}from'http';import pt from'https';import*as x from'os';import x__default from'os';import {EventEmitter}from'events';import {Socket}from'net';var N={beforeRequest:[],afterRequest:[],onError:[]},z={onNotFound:[],onShutdown:[]},$=new Map,L=new Map;function ge(t,e){return N[t].push(e),{remove:()=>{let r=N[t].indexOf(e);r!==-1&&N[t].splice(r,1);}}}function xe(t,e,r,n){return t.has(e)||t.set(e,{beforeRequest:[],afterRequest:[],onError:[]}),t.get(e)[r].push(n),{remove:()=>{let o=t.get(e);if(!o)return;let s=o[r].indexOf(n);s!==-1&&o[r].splice(s,1);}}}function Ge(t,e,r){return xe($,t==="/"?"/":t.replace(/\/+$/,""),e,r)}function Ye(t,e,r){return xe(L,t==="/"?"/":t.replace(/\/+$/,""),e,r)}function re(t,e,r){let n=t.get(e);n&&(r.beforeRequest.push(...n.beforeRequest),r.afterRequest.push(...n.afterRequest),r.onError.push(...n.onError));}function Qe(t){let e=t==="/"?"/":t.replace(/\/+$/,""),r=e.split("/").filter(Boolean),n={beforeRequest:[...N.beforeRequest],afterRequest:[...N.afterRequest],onError:[...N.onError]};re($,"/",n),e==="/"&&re(L,"/",n);let o="";for(let s of r)o+="/"+s,re($,o,n),o===e&&re(L,o,n);return n}function H(){return N.beforeRequest.length>0||N.afterRequest.length>0||N.onError.length>0||$.size>0||L.size>0}async function k(t,e,r,n){let o=e.url?.split("?")[0]||"/",s=Qe(o);if(s[t].length===0)return true;for(let i of s[t])try{if(await i(e,r,n)===!1)return !1}catch(l){if(t!=="onError")try{for(let a of s.onError)await a(e,r,{error:l,phase:t});}catch(a){console.error("Error in error handler:",a);}return false}return true}function Ke(t,e,r){e.has(r)||e.set(r,{beforeRequest:[],afterRequest:[],onError:[]});let n=e.get(r);for(let o of ["beforeRequest","afterRequest","onError"])if(t[o]){let s=Array.isArray(t[o])?t[o]:[t[o]];n[o].push(...s);}}async function ye(t){$.clear(),L.clear();async function e(r,n=""){try{let o=await Je.readdir(r,{withFileTypes:!0});for(let[s,i]of [["+middleware.global.ts",$],["+middleware.global.js",$],["+middleware.ts",L],["+middleware.js",L]]){let l=o.find(a=>a.name===s);if(l)try{let c=await import(`${Q.resolve(Q.join(r,l.name))}?update=${Date.now()}`);Ke(c,i,n);}catch(a){console.error(`Error loading middleware for ${n}:`,a);}}for(let s of o)s.isDirectory()&&await e(Q.join(r,s.name),`${n==="/"?"":n}/${s.name}`);}catch(o){console.error(`Error scanning directory ${r}:`,o);}}await e(t,"/");}function kt(){return $}function Y(t){if(t?.length)for(let e of t){let r=e.type==="cascade"?Ge:Ye;for(let n of ["beforeRequest","afterRequest","onError"]){let o=e.module[n];if(!o)continue;let s=Array.isArray(o)?o:[o];for(let i of s)r(e.path,n,i);}}}function U(t){t&&(t.onNotFound&&z.onNotFound.push(t.onNotFound),t.onShutdown&&z.onShutdown.push(t.onShutdown));}function j(){return z.onNotFound.length>0}async function F(t,e){for(let r of z.onNotFound)if(await r(t,e),e.headersSent)return}async function Ot(){for(let t of z.onShutdown)try{await t();}catch(e){console.error("Error in onShutdown hook:",e);}}function xt(){N.beforeRequest=[],N.afterRequest=[],N.onError=[],$.clear(),L.clear(),z.onNotFound=[],z.onShutdown=[];}function D(t){if(t)for(let e of ["beforeRequest","afterRequest","onError"]){let r=t[e];if(r){let n=Array.isArray(r)?r:[r];for(let o of n)ge(e,o);}}}function Ze(t,e){return !e||e==="*"?true:typeof e=="string"?t===e:Array.isArray(e)?e.includes(t):e instanceof RegExp?e.test(t):typeof e=="function"?e(t):false}function Xe(t){let e=(t.methods??["GET","POST","PUT","DELETE","PATCH","OPTIONS"]).join(", "),r=(t.allowedHeaders??["Content-Type","Authorization"]).join(", "),n=t.exposedHeaders?.join(", "),o=t.maxAge!=null?String(t.maxAge):null,s=!t.origin||t.origin==="*";return async(i,l)=>{let a=i.getHeader("origin");if(!a||!Ze(a,t.origin))return;let c=s&&!t.credentials;if(l.setHeader("Access-Control-Allow-Origin",c?"*":a),c||l.setHeader("Vary","Origin"),t.credentials&&l.setHeader("Access-Control-Allow-Credentials","true"),n&&l.setHeader("Access-Control-Expose-Headers",n),i.method==="OPTIONS")return l.setHeader("Access-Control-Allow-Methods",e),l.setHeader("Access-Control-Allow-Headers",r),o&&l.setHeader("Access-Control-Max-Age",o),l.status(204),l.end(),false}}function B(t){t&&ge("beforeRequest",Xe(t));}function g(...t){J.isPrimary&&console.log(...t);}function nt(t){let e=t.match(/^\[(\w+)(\??)]/);return e?{name:e[1],isParam:true,isOptional:e[2]==="?"}:{name:t,isParam:false,isOptional:false}}var ot=1e3,at=100,we=class{rootNode;cachedRoutes;routeCount;lastLoaded;verbose;constructor(){this.rootNode=this.createNode(),this.cachedRoutes=new Map,this.routeCount=0,this.lastLoaded=0,this.verbose=false;}createNode(){return {handlers:Object.create(null),staticChildren:new Map,paramChild:null,wildcardHandler:null,isEndpoint:false}}addRoute(e,r,n){let o=r.split("/").filter(Boolean),s=this.rootNode;for(let i of o){let l=nt(i);if(l.isParam){if(!s.paramChild)s.paramChild={name:l.name,node:this.createNode(),isOptional:l.isOptional};else if(s.paramChild.name!==l.name)throw new Error(`Route conflict: param name "[${l.name}]" conflicts with existing "[${s.paramChild.name}]" at the same path position. Use the same param name for all methods at this level.`);s=s.paramChild.node;}else {if(i==="*")return s.wildcardHandler||(s.wildcardHandler=Object.create(null)),s.wildcardHandler[e]=n,this.cachedRoutes.clear(),this;s.staticChildren.has(i)||s.staticChildren.set(i,this.createNode()),s=s.staticChildren.get(i);}}return s.isEndpoint=true,s.handlers[e]||this.routeCount++,s.handlers[e]=n,this.cachedRoutes.clear(),this}findRoute(e,r){let n=r==="/"?"/":r.replace(/\/+$/,""),o=e+":"+n,s=this.cachedRoutes.get(o);if(s)return this.cachedRoutes.delete(o),this.cachedRoutes.set(o,s),s;let i=n==="/"?[]:n.split("/").filter(Boolean),l=Object.create(null),a=this.traverse(this.rootNode,i,e,l,0)??{handler:null,params:Object.create(null)};if(a.handler!==null||a.allowedMethods?.length){if(this.cachedRoutes.size>=ot){let c=0;for(let u of this.cachedRoutes.keys())if(this.cachedRoutes.delete(u),++c>=at)break}this.cachedRoutes.set(o,a);}return a}traverse(e,r,n,o,s){if(s===r.length){if(e.isEndpoint){let c=e.handlers[n]??(n==="HEAD"?e.handlers.GET:void 0)??e.handlers[""];if(c)return {handler:c,params:Object.assign(Object.create(null),o)};let u=Object.keys(e.handlers).filter(h=>h!=="");return {handler:null,params:Object.create(null),allowedMethods:u}}let a=e.paramChild;if(a?.isOptional&&a.node.isEndpoint){let c=a.node.handlers[n]??(n==="HEAD"?a.node.handlers.GET:void 0)??a.node.handlers[""];if(c)return {handler:c,params:Object.assign(Object.create(null),o)}}return null}let i=r[s],l=e.staticChildren.get(i);if(l){let a=this.traverse(l,r,n,o,s+1);if(a)return a}if(e.paramChild){let{name:a,node:c}=e.paramChild;o[a]=i;let u=this.traverse(c,r,n,o,s+1);if(u)return u;delete o[a];}if(e.wildcardHandler){let a=e.wildcardHandler[n]??e.wildcardHandler[""],c=r.slice(s).join("/");if(a)return {handler:a,params:Object.assign(Object.create(null),o,{"*":c})};let u=Object.keys(e.wildcardHandler).filter(h=>h!=="");if(u.length>0)return {handler:null,params:Object.assign(Object.create(null),o,{"*":c}),allowedMethods:u}}return null}async loadRoutes(e){this.rootNode=this.createNode(),this.cachedRoutes.clear(),this.routeCount=0,await ye(e);let r=this;async function n(o,s=[]){try{let i=await Je.readdir(o,{withFileTypes:!0}),l=i.find(a=>!a.isDirectory()&&(a.name==="index.ts"||a.name==="index.js"));if(l)try{let c=await import(`${Q.resolve(Q.join(o,l.name))}?update=${Date.now()}`),u=["GET","POST","PUT","DELETE","PATCH"],h="/"+s.join("/"),y=!1;for(let R of u)typeof c[R]=="function"&&(r.addRoute(R,h,c[R]),y=!0);!y&&typeof c.default=="function"&&r.addRoute("GET",h,c.default),r.verbose&&g(`Route loaded: ${h}`);}catch(a){console.error(`Error loading index in ${o}:`,a);}for(let a of i)if(a.isDirectory()){let c=a.name.match(/^\[(\w+)(\??)]/),u=c?`[${c[1]}${c[2]}]`:a.name;await n(Q.join(o,a.name),[...s,u]);}}catch(i){console.error(`Error scanning directory ${o}:`,i);}}return await n(e),this.lastLoaded=Date.now(),this.verbose&&g(`Loading completed: ${this.routeCount} routes`),true}getRoutes(){let e=[];return this.collectRoutes(this.rootNode,[],e),e}collectRoutes(e,r,n){if(e.isEndpoint){let o=r.length===0?"/":"/"+r.join("/");for(let[s,i]of Object.entries(e.handlers))s!==""&&n.push({method:s,path:o,handler:i});}for(let[o,s]of e.staticChildren)this.collectRoutes(s,[...r,o],n);if(e.paramChild){let{name:o,isOptional:s,node:i}=e.paramChild;this.collectRoutes(i,[...r,s?`:${o}?`:`:${o}`],n);}if(e.wildcardHandler){let o="/"+[...r,"*"].join("/");for(let[s,i]of Object.entries(e.wildcardHandler))s!==""&&n.push({method:s,path:o,handler:i});}}getStats(){return {routeCount:this.routeCount,lastLoaded:this.lastLoaded,uptime:Date.now()-this.lastLoaded,cacheSize:this.cachedRoutes.size}}setVerbose(e){return this.verbose=e,this}reset(){this.rootNode=this.createNode(),this.cachedRoutes=new Map,this.routeCount=0;}},V=new we;function se(t){return t&&typeof t=="object"&&"error"in t}function Z(t){for(let{path:e,handlers:r}of t){let n=e.replace(/:(\w+\??)/g,(o,s)=>s.endsWith("?")?`[${s.slice(0,-1)}?]`:`[${s}]`);for(let[o,s]of Object.entries(r))typeof s=="function"&&V.addRoute(o,n,s);}}async function ne(t){let e=join(t,"_manifest.js");if(existsSync(e)){V.reset();let r=await import(e);Z(r.routes),r.middlewares?Y(r.middlewares):await ye(t);return}return V.loadRoutes(t)}function W(t,e="GET"){let r=V.findRoute(e,t);return r.handler?{handler:r.handler,params:r.params}:r.allowedMethods?.length?{error:"Method Not Allowed",status:405,allowedMethods:r.allowedMethods}:null}function Bt(t){let e=process.env.NODE_ENV==="production",r=process.env.ROUTES_DIR??t??(e?"dist/routes":"routes"),n=e&&!Q.isAbsolute(r)&&!r.startsWith("dist/")?Q.join("dist",r):r;return Q.resolve(process.cwd(),n)}function Wt(){return V.getStats()}function zt(t){V.setVerbose(t);}function Vt(){V.reset();}var q={OK:200,CREATED:201,NO_CONTENT:204,BAD_REQUEST:400,UNAUTHORIZED:401,FORBIDDEN:403,NOT_FOUND:404,METHOD_NOT_ALLOWED:405,CONFLICT:409,UNPROCESSABLE_ENTITY:422,TOO_MANY_REQUESTS:429,INTERNAL_SERVER_ERROR:500,SERVICE_UNAVAILABLE:503,GATEWAY_TIMEOUT:504},Ae={400:"Bad Request",401:"Unauthorized",403:"Forbidden",404:"Not Found",405:"Method Not Allowed",409:"Conflict",422:"Unprocessable Entity",429:"Too Many Requests",500:"Internal Server Error",503:"Service Unavailable",504:"Gateway Timeout"};function O(t){let e=t.code||q.INTERNAL_SERVER_ERROR,r=t.message||Ae[e]||"Unknown Error",n=t.name||`HttpError${e}`,o=new Error(r);return Error.captureStackTrace?.(o,O),{name:n,code:e,message:r,details:t.details,expose:t.expose!==void 0?t.expose:e<500,log:t.log!==void 0?t.log:e>=500,stack:o.stack}}function it(t){let r={error:t.expose?t.message:Ae[t.code]||"Internal Server Error",code:t.code};return t.expose&&t.details?{...r,details:t.details}:r}function Ne(t,e){e.headersSent||(e.statusCode=t.code,e.setHeader("Content-Type","application/json"),e.end(JSON.stringify(it(t)))),t.log&&dt(t);}function dt(t){let e={name:t.name,message:t.message,code:t.code,details:t.details,stack:t.stack};t.code>=500?g("\u274C ERROR:",JSON.stringify(e,null,2)):g("\u26A0\uFE0F WARNING:",JSON.stringify(e,null,2));}function Yt(t,e){return O({name:"BadRequestError",code:q.BAD_REQUEST,message:t,details:e})}function Qt(t,e){return O({name:"UnauthorizedError",code:q.UNAUTHORIZED,message:t,details:e})}function Kt(t,e){return O({name:"ForbiddenError",code:q.FORBIDDEN,message:t,details:e})}function Zt(t,e){return O({name:"NotFoundError",code:q.NOT_FOUND,message:t,details:e})}function Xt(t,e){return O({name:"MethodNotAllowedError",code:q.METHOD_NOT_ALLOWED,message:t,details:e})}function er(t,e){return O({name:"ConflictError",code:q.CONFLICT,message:t,details:e})}function tr(t,e){return O({name:"ValidationError",code:q.UNPROCESSABLE_ENTITY,message:t,details:e,expose:true})}function rr(t,e){return O({name:"RateLimitError",code:q.TOO_MANY_REQUESTS,message:t,details:e})}function lt(t,e){return O({name:"InternalServerError",code:q.INTERNAL_SERVER_ERROR,message:t,details:e})}function ut(t,e){return O({name:"ServiceUnavailableError",code:q.SERVICE_UNAVAILABLE,message:t,details:e})}function ct(t,e){return O({name:"GatewayTimeoutError",code:q.GATEWAY_TIMEOUT,message:t,details:e})}function qe(t){if(t&&typeof t=="object"&&typeof t.code=="number"&&"message"in t)return t;let e=t?.message||"Unknown error occurred",r=t?.stack?{stack:t.stack}:void 0,n=t?.code||t?.statusCode;return n==="ECONNREFUSED"||n==="ENOTFOUND"?ut(`Service connection failed: ${e}`,r):n==="ETIMEDOUT"?ct(`Request timed out: ${e}`,r):typeof n=="number"&&n>=400&&n<600?O({code:n,message:e,details:r}):lt(e,r)}function Ie(t){return !!(t&&typeof t=="object"&&typeof t.code=="number"&&"message"in t&&"name"in t)}function $e(t,e={},r={},n){let o=false,s=0,i={},l=[],a=[],c=[],u=null;r.onMessage&&l.push(r.onMessage),r.onClose&&a.push(r.onClose),r.onError&&c.push(r.onError),r.onEvent&&Object.entries(r.onEvent).forEach(([f,m])=>{i[f]||(i[f]=[]),i[f].push(m);});let h=e.reconnectInterval||3e3,y=e.maxRetries||3,R=e.disableReconnect||false,w=e.body,p=e.contentType||(w?"application/json":void 0),d=e.method||(w?"POST":"GET");if(typeof t!="string"){let f=t;return o=true,f.on("data",m=>{S(m.toString());}),f.on("end",()=>{o=false;}),f.on("error",m=>{o=false,console.error("SSE Error:",m);}),b()}if(typeof t=="string")return new Promise((f,m)=>{I().then(()=>f(b())).catch(m);});function S(f){let m=f,C=m.split(`
2
+
3
+ `);m=C.pop()||"";for(let M of C){let A=M.split(`
4
+ `),E="",v="";for(let _ of A)_.startsWith("event:")?v=_.slice(6).trim():_.startsWith("data:")&&(E=_.slice(5).trim());if(E)try{let _=JSON.parse(E);v&&i[v]?i[v].forEach(K=>K(_)):l.forEach(K=>K(_));}catch{v&&i[v]?i[v].forEach(_=>_(E)):l.forEach(_=>_(E));}}}function b(){return {onMessage(f){return l.push(f),this},onEvent(f,m){return i[f]||(i[f]=[]),i[f].push(m),this},onClose(f){return a.push(f),this},close(){u&&(u.destroy(),u=null),o=false,a.forEach(f=>f());}}}async function I(){if(!o){o=true;try{await T();}catch(f){!R&&s<y?(s++,setTimeout(()=>{I();},h)):c.forEach(m=>m(f));}}}async function T(){return new Promise((f,m)=>{let C=new URL(t);e.params&&(typeof e.params=="object"?Object.entries(e.params).forEach(([E,v])=>{C.searchParams.append(E,String(v));}):typeof e.params=="string"&&new URLSearchParams(e.params).forEach((v,_)=>{C.searchParams.append(_,v);}));let M={hostname:C.hostname,port:C.port||(C.protocol==="https:"?443:80),path:C.pathname+C.search,method:d,headers:{Accept:"text/event-stream",...n?.headers||{},"Cache-Control":"no-cache",Connection:"keep-alive",...p&&{"Content-Type":p},...w&&typeof w=="string"&&{"Content-Length":Buffer.byteLength(w).toString()},...w&&typeof w!="string"&&{"Content-Length":Buffer.byteLength(JSON.stringify(w)).toString()}}};u=(C.protocol==="https:"?pt:Te).request(M,E=>{if(E.statusCode!==200){o=false,m(new Error(`Server responded with status: ${E.statusCode}`));return}E.setEncoding("utf8"),E.on("data",S),E.on("end",()=>{o=false,u?.destroy(),u=null,a.forEach(v=>v()),f(null);}),E.on("close",()=>{o=false,u?.destroy(),u=null,a.forEach(v=>v()),f(null);}),E.on("error",v=>{o=false,u?.destroy(),u=null,a.forEach(_=>_()),m(v);});}),u.on("error",E=>{o=false,u?.destroy(),u=null,m(E);}),w&&(typeof w=="string"?u.write(w):u.write(JSON.stringify(w))),u.end();})}return b()}var Re=class{constructor(e,r){this._res=e;this._timeoutId=setTimeout(()=>e.end(),r),e.on("close",()=>clearTimeout(this._timeoutId));}_res;_timeoutId;send(e){return this._res.writableEnded?false:this._res.write(`data: ${e}
5
+
6
+ `)}json(e){return this._res.writableEnded?false:this._res.write(`data: ${JSON.stringify(e)}
7
+
8
+ `)}event(e,r){return this._res.writableEnded?false:(this._res.write(`event: ${e}
9
+ `),this._res.write(`data: ${JSON.stringify(r)}
10
+
11
+ `))}comment(e){return this._res.writableEnded?false:this._res.write(`: ${e}
12
+
13
+ `)}id(e){return this._res.writableEnded?false:this._res.write(`id: ${e}
14
+
15
+ `)}retry(e){return this._res.writableEnded?false:this._res.write(`retry: ${e}
16
+
17
+ `)}close(e="Connection closed"){clearTimeout(this._timeoutId),this._res.write(`: ${e}
18
+
19
+ `),this._res.end();}error(e,r,n=500,o){clearTimeout(this._timeoutId),this._res.write(`event: ${e}
20
+ `),this._res.write(`data: ${JSON.stringify({message:r,code:n,details:o??null})}
21
+
22
+ `),this._res.end();}};function Pe(t,e){let r=e?.headers?.["Cache-Control"]??"no-cache",n=e?.headers?.Connection??"keep-alive",o=e?.timeout??3e5;return t.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":r,Connection:n,...e?.headers??{}}),new Re(t,o)}var ht=10485760,ae=class{_parsed=null;_raw;constructor(e){this._raw=e;}_parse(){if(this._parsed!==null)return this._parsed;if(this._parsed={},!this._raw)return this._parsed;for(let e of this._raw.split(";")){let r=e.indexOf("=");if(r===-1)continue;let n=e.slice(0,r).trim(),o=e.slice(r+1).trim();if(n){let s=o.startsWith('"')&&o.endsWith('"')?o.slice(1,-1):o;try{this._parsed[n]=decodeURIComponent(s);}catch{this._parsed[n]=s;}}}return this._parsed}get(e){return this._parse()[e]}all(){return {...this._parse()}}},ie=class{_pending=[];set(e,r,n={}){return this._pending.push({name:e,value:r,opts:n}),this}delete(e,r={}){return this.set(e,"",{...r,maxAge:0,expires:new Date(0)})}serialize(){return this._pending.map(({name:e,value:r,opts:n})=>{let o=`${e}=${encodeURIComponent(r)}`,s=n.path!==void 0?n.path:"/";return s&&(o+=`; Path=${s}`),n.domain&&(o+=`; Domain=${n.domain}`),n.maxAge!=null&&(o+=`; Max-Age=${n.maxAge}`),n.expires&&(o+=`; Expires=${n.expires.toUTCString()}`),n.httpOnly&&(o+="; HttpOnly"),n.secure&&(o+="; Secure"),n.sameSite&&(o+=`; SameSite=${n.sameSite}`),o})}};function Le(t){if(typeof t.get=="function")return t.get("cookie")??void 0;let e=t.cookie;return Array.isArray(e)?e.join("; "):e}function X(t){return class extends t{_zreqCookies;getHeader(e){let r=this.headers;if(typeof r.get=="function")return r.get(e)??void 0;let n=r[e.toLowerCase()];return Array.isArray(n)?n[0]:n}get cookies(){return this._zreqCookies||(this._zreqCookies=new ae(Le(this.headers))),this._zreqCookies}json(){return this.body().then(e=>JSON.parse(e.toString()))}form(){return new Promise((e,r)=>{let n=this.headers,o=typeof n.get=="function"?n.get("content-type")??"":n["content-type"]??"";if(!o.startsWith("multipart/form-data")){r(new Error("Content-Type is not multipart/form-data"));return}let s=o.match(/boundary=(.+)$/);if(!s){r(new Error("Boundary not found"));return}let i=s[1].trim();this.body().then(l=>{let a={},c=Buffer.from(`--${i}`),u=[],h=0,y;for(;(y=l.indexOf(c,h))!==-1;)u.push(l.subarray(h,y)),h=y+c.length;u.push(l.subarray(h));for(let R=1;R<u.length-1;R++){let w=u[R].subarray(2),p=w.indexOf(`\r
23
+ \r
24
+ `);if(p===-1)continue;let d=w.subarray(0,p).toString(),S=w.subarray(p+4,w.length-2),b=d.match(/Content-Disposition: form-data; name="([^"]+)"(?:; filename="([^"]+)")?/i);if(b)if(b[2]){let I=d.match(/Content-Type:\s*([^\r\n]+)/i);a[b[1]]={filename:b[2],mimetype:I?I[1].trim():"application/octet-stream",data:S,size:S.length};}else a[b[1]]=S.toString("utf-8");}e(a);}).catch(r);})}createSSEClient(e,r){return $e(this,e,r)}}}function de(){let t=[],e=0,r=false;return new Promise((n,o)=>{this.on("data",s=>{if(e+=s.length,e>ht){r=true,this.destroy(),o(Object.assign(new Error("Payload Too Large"),{code:413}));return}t.push(s);}).on("end",()=>{r||(r=true,n(Buffer.concat(t)));}).on("error",s=>{r||(r=true,o(s));});})}function Ue(t,e){let r=t.serialize();r.length>0&&!e.headersSent&&e.setHeader("Set-Cookie",r);}function ee(t){return class extends t{_zresCookies;get cookies(){return this._zresCookies||(this._zresCookies=new ie),this._zresCookies}end(e){return this._zresCookies&&Ue(this._zresCookies,this),super.end(e)}status(e){return this.statusCode=e,this}send(e){return typeof e=="string"?(this.setHeader("Content-Type","text/plain"),this.end(e)):this.json(e),this}json(e){return this.setHeader("Content-Type","application/json"),this.end(JSON.stringify(e)),this}html(e){return this.setHeader("Content-Type","text/html; charset=utf-8"),this.end(e),this}redirect(e,r=302){return this.statusCode=r,this.setHeader("Location",e),this.end(),this}initSSE(e){return Pe(this,e)}}}var Ee=class{params={};headers={};body(){return de.call(this)}},oe=X(Ee).prototype,Ce=class{statusCode=200;headersSent=false;setHeader(e,r){}end(e){}write(e){}},mt=ee(Ce).prototype;function le(t){t.body=de,t.json=oe.json,t.form=oe.form,t.getHeader=oe.getHeader,t.createSSEClient=oe.createSSEClient,t.cookies=new ae(Le(t.headers));}function ue(t){let e=t.indexOf("?");return e===-1?t:t.slice(0,e)}function ce(t){let e=t.indexOf("?");return e===-1?{}:Object.fromEntries(new URLSearchParams(t.slice(e+1)).entries())}async function P(t,e,r){let n=Ie(r)?r:qe(r);n.log&&console.error("[lacis] Error:",r),H()&&await k("onError",t,e,{error:n}),e.headersSent||Ne(n,e);}function fe(t){let e=mt;t.status=e.status,t.send=e.send,t.json=e.json,t.html=e.html,t.redirect=e.redirect,t.initSSE=e.initSSE;let r=new ie;t.cookies=r;let n=t.end.bind(t);t.end=function(...o){return Ue(r,this),n(...o)};}var pe=null;function yt(t={}){let e=new EventEmitter,r={sampleInterval:t.sampleInterval||5e3,reportInterval:t.reportInterval||6e4,resetInterval:t.resetInterval||1440*60*1e3,enableHistogram:t.enableHistogram!==void 0?t.enableHistogram:true,logToConsole:t.logToConsole!==void 0?t.logToConsole:true,thresholds:{cpu:t.thresholds?.cpu||80,memory:t.thresholds?.memory||80,responseTime:t.thresholds?.responseTime||1e3,errorRate:t.thresholds?.errorRate||5}},n=Date.now(),o={requestCount:0,activeRequests:0,errorCount:0,responseTimes:[],responseTimesBucket:Array(100).fill(0),statusCodes:{},lastReport:n,lastReset:n},s=c(),i=[],l=process.cpuUsage(),a={cpu:false,memory:false,responseTime:false,errorRate:false};function c(){return {timestamp:Date.now(),uptime:process.uptime(),requestCount:0,activeRequests:0,errorCount:0,responseTimes:{min:0,max:0,avg:0,count:0,sum:0},statusCodes:{},memory:process.memoryUsage(),cpu:{usage:0,system:0,user:0},systemLoad:x.loadavg(),systemMemory:{total:x.totalmem(),free:x.freemem(),used:x.totalmem()-x.freemem()}}}function u(){let f=o.responseTimes;if(f.length===0)return {min:0,max:0,avg:0,count:0,sum:0};let m=1/0,C=-1/0,M=0;for(let v of f)m=Math.min(m,v),C=Math.max(C,v),M+=v;let A=M/f.length,E={min:m,max:C,avg:A,count:f.length,sum:M};if(r.enableHistogram){let v=[...f].sort((_,K)=>_-K);E.p50=v[Math.floor(v.length*.5)],E.p90=v[Math.floor(v.length*.9)],E.p99=v[Math.floor(v.length*.99)];}return E}function h(){let f=Date.now(),m=process.uptime(),C=process.memoryUsage(),M=process.cpuUsage(l);l=process.cpuUsage();let E=(M.user+M.system)/1e3/r.sampleInterval*100;s={timestamp:f,uptime:m,requestCount:o.requestCount,activeRequests:o.activeRequests,errorCount:o.errorCount,responseTimes:u(),statusCodes:{...o.statusCodes},memory:C,cpu:{usage:E,system:M.system,user:M.user},systemLoad:x.loadavg(),systemMemory:{total:x.totalmem(),free:x.freemem(),used:x.totalmem()-x.freemem()}},i.push({...s}),i.length>100&&i.shift(),w(),J.isWorker&&process.send&&process.send({type:"metrics",metrics:s});}function y(){if(!r.logToConsole||J.isPrimary===false)return;let f={rss:(s.memory.rss/1024/1024).toFixed(2),heapTotal:(s.memory.heapTotal/1024/1024).toFixed(2),heapUsed:(s.memory.heapUsed/1024/1024).toFixed(2)},m=Date.now()-o.lastReport,C=(s.requestCount/(m/1e3)).toFixed(2),M=s.requestCount?(s.errorCount/s.requestCount*100).toFixed(2):"0.00";g(`
25
+ \u{1F4CA} Server Performance Metrics \u{1F4CA}`),g(`Uptime: ${p(s.uptime)}`),g(`Load: ${s.systemLoad[0].toFixed(2)}, ${s.systemLoad[1].toFixed(2)}, ${s.systemLoad[2].toFixed(2)}`),g(`Requests: ${s.requestCount} total, ${C} req/sec`),g(`Active Requests: ${s.activeRequests}`),g(`Errors: ${s.errorCount} (${M}%)`),s.responseTimes.count>0&&(g(`Response Times: avg ${s.responseTimes.avg.toFixed(2)}ms, min ${s.responseTimes.min}ms, max ${s.responseTimes.max}ms`),s.responseTimes.p50&&g(`Response Time Percentiles: p50 ${s.responseTimes.p50}ms, p90 ${s.responseTimes.p90}ms, p99 ${s.responseTimes.p99}ms`)),g(`Memory: ${f.rss}MB (RSS), ${f.heapUsed}MB / ${f.heapTotal}MB (Heap)`),g(`CPU Usage: ${s.cpu.usage.toFixed(2)}%`),Object.keys(s.statusCodes).length>0&&(g("Status Codes:"),Object.entries(s.statusCodes).sort(([A],[E])=>parseInt(A)-parseInt(E)).forEach(([A,E])=>{g(` ${A}: ${E}`);})),g(""),o.lastReport=Date.now();}function R(){let f=Date.now();o.requestCount=0,o.activeRequests=0,o.errorCount=0,o.responseTimes=[],o.responseTimesBucket=Array(100).fill(0),o.statusCodes={},o.lastReport=f,o.lastReset=f,g("\u{1F504} Performance metrics have been reset");}function w(){let f=s.memory.heapUsed/s.memory.heapTotal*100,m=s.requestCount?s.errorCount/s.requestCount*100:0,C=s.responseTimes.avg;s.cpu.usage>r.thresholds.cpu&&!a.cpu?(a.cpu=true,e.emit("alarm","cpu",`High CPU usage: ${s.cpu.usage.toFixed(2)}%`)):s.cpu.usage<=r.thresholds.cpu&&a.cpu&&(a.cpu=false,e.emit("alarm-clear","cpu",`CPU usage returned to normal: ${s.cpu.usage.toFixed(2)}%`)),f>r.thresholds.memory&&!a.memory?(a.memory=true,e.emit("alarm","memory",`High memory usage: ${f.toFixed(2)}%`)):f<=r.thresholds.memory&&a.memory&&(a.memory=false,e.emit("alarm-clear","memory",`Memory usage returned to normal: ${f.toFixed(2)}%`)),C>r.thresholds.responseTime&&!a.responseTime?(a.responseTime=true,e.emit("alarm","responseTime",`High response time: ${C.toFixed(2)}ms`)):C<=r.thresholds.responseTime&&a.responseTime&&(a.responseTime=false,e.emit("alarm-clear","responseTime",`Response time returned to normal: ${C.toFixed(2)}ms`)),m>r.thresholds.errorRate&&!a.errorRate?(a.errorRate=true,e.emit("alarm","errorRate",`High error rate: ${m.toFixed(2)}%`)):m<=r.thresholds.errorRate&&a.errorRate&&(a.errorRate=false,e.emit("alarm-clear","errorRate",`Error rate returned to normal: ${m.toFixed(2)}%`));}function p(f){let m=Math.floor(f/86400),C=Math.floor(f%86400/3600),M=Math.floor(f%3600/60),A=Math.floor(f%60),E=[];return m>0&&E.push(`${m}d`),C>0&&E.push(`${C}h`),M>0&&E.push(`${M}m`),(A>0||E.length===0)&&E.push(`${A}s`),E.join(" ")}function d(){let f=setInterval(h,r.sampleInterval),m=setInterval(y,r.reportInterval),C=setInterval(R,r.resetInterval);return {stop:()=>{clearInterval(f),clearInterval(m),clearInterval(C);}}}function S(){let f=Date.now();return o.activeRequests++,{end:(m,C=false)=>{let M=Date.now()-f;return o.activeRequests--,o.requestCount++,o.responseTimes.push(M),o.responseTimes.length>1e3&&o.responseTimes.shift(),o.statusCodes[m]=(o.statusCodes[m]||0)+1,(C||m>=500)&&o.errorCount++,M}}}function b(){return !Object.values(a).some(f=>f)}function I(){let f=s.memory.heapUsed/s.memory.heapTotal*100,m=s.requestCount?s.errorCount/s.requestCount*100:0;return {status:b()?"healthy":"unhealthy",uptime:s.uptime,responseTimes:{avg:s.responseTimes.avg,p90:s.responseTimes.p90||null,p99:s.responseTimes.p99||null},memory:{usedMB:Math.round(s.memory.heapUsed/1024/1024),totalMB:Math.round(s.memory.heapTotal/1024/1024),percent:f.toFixed(2)},cpu:s.cpu.usage.toFixed(2),requests:{total:s.requestCount,active:s.activeRequests,errors:s.errorCount,errorRate:m.toFixed(2)},alerts:Object.entries(a).filter(([C,M])=>M).map(([C])=>C)}}let T=d();return {trackRequest:S,getMetrics:()=>({...s}),getMetricsHistory:()=>[...i],getHealthMetrics:I,isHealthy:b,stop:T.stop,on:e.on.bind(e),once:e.once.bind(e),off:e.off.bind(e)}}function Fe(t){if(!pe){let e=yt(t),r=e.stop;e.stop=()=>{r(),pe=null;},pe=e;}return pe}function Se(t={}){let e=t.reportInterval??5e3,r={workers:new Map,workerIds:[]};function n(){if(!J.isWorker)return;let c=process.cpuUsage(),u=()=>{if(!process.send)return;let h=process.memoryUsage(),y=process.cpuUsage(c);c=process.cpuUsage();let R=(y.user+y.system)/(e*1e3),w={type:"stats",stats:{pid:process.pid,load:R,lastUsed:Date.now(),memoryUsage:h}};process.send(w);};u(),setInterval(u,e).unref();}function o(){let c=J.fork();r.workers.set(c.id,{pid:c.process.pid,load:0,lastUsed:Date.now(),memoryUsage:{rss:0,heapTotal:0,heapUsed:0,external:0,arrayBuffers:0}}),r.workerIds.push(c.id),g(`Worker ${c.process.pid} started`);}let s=false,i=[];function l(c=x.cpus().length){if(!J.isPrimary)return n();g(`\u{1F9F5} Launching ${c} workers...`);for(let u=0;u<c;u++)o();J.on("message",(u,h)=>{if(h.type==="stats"&&"stats"in h){let y=r.workers.get(u.id);y&&Object.assign(y,h.stats);}}),J.on("exit",(u,h,y)=>{if(r.workers.delete(u.id),r.workerIds=r.workerIds.filter(R=>R!==u.id),s){i.forEach(R=>R());return}g(`Worker ${u.process.pid} died (${y||h}). Restarting...`),setTimeout(()=>o(),1e3);});}function a(c){s=true;let u=Object.keys(J.workers??{});if(u.length===0){c?.();return}let h=u.length;i.push(()=>{h--,h===0&&c?.();});for(let y of u)J.workers?.[y]?.kill();}return {start:l,shutdown:a,getWorkerStats:()=>Array.from(r.workers.entries()),getActiveWorkerCount:()=>r.workerIds.length}}var be=class extends Te.IncomingMessage{params={};body=de},ve=class extends X(be){},Me=class extends ee(Te.ServerResponse){},Be={name:"node",createHandler:t=>{if(typeof t!="string")throw new Error("nodeAdapter requires a routesDir string, not a ServerlessConfig.");let e=t;return async(r={})=>{let{port:n=3e3,defaultHeaders:o,isDev:s,cluster:i,monitoring:l={enabled:false}}=r,a=null;s&&l.enabled&&(a=Fe({sampleInterval:l.sampleInterval||5e3,reportInterval:l.reportInterval||6e4,thresholds:l.thresholds,logToConsole:true}),J.isPrimary&&(g("\u{1F4CA} Development performance monitoring enabled"),a.on("alarm",(u,h)=>{g(`\u26A0\uFE0F ALERT: ${h}`);}),a.on("alarm-clear",(u,h)=>{g(`\u2705 RESOLVED: ${h}`);})));let c=o?Object.entries(o):[];if(i?.enabled&&J.isPrimary){let u=i.workers??x__default.cpus().length,h=r.httpsOptions?"https":"http";if(g(`\u{1F9F5} Starting server with ${u} workers`),J.schedulingPolicy!==void 0)try{J.schedulingPolicy=J.SCHED_RR;}catch{}let y=Se();return y.start(u),g(`\u{1F680} Server running at ${h}://localhost:${n}/`+(s?" (dev)":"")),s&&a&&g(`\u{1F4CA} Performance monitoring available at http://localhost:${n}/health`),{close:R=>{a&&a.stop(),y.shutdown(R);}}}if(J.isWorker||!i?.enabled){r.routes||await ne(e),B(r.cors),D(r.middleware),U(r.hooks);let u=async(p,d,S)=>{try{if(s&&a&&p.url==="/health"){d.setHeader("Content-Type","application/json"),d.end(JSON.stringify(a.getHealthMetrics())),S?.end(200);return}let b=p.url||"/",I=ue(b);if(p.query=ce(b),H()&&(await k("beforeRequest",p,d)===!1||d.headersSent)){S?.end(d.statusCode||204);return}let T=W(I,p.method||"GET");if(!T){if(j()&&(await F(p,d),d.headersSent)){S?.end(d.statusCode);return}d.status(404).json({error:"Route not found"}),S?.end(404);return}if("error"in T){let f=T.status||500;d.status(f).json({error:T.error}),S?.end(f,!0);return}p.params=T.params,await T.handler(p,d),H()&&await k("afterRequest",p,d),d.headersSent||d.end(),S?.end(d.statusCode||200);}catch(b){await P(p,d,b),d.headersSent||d.status(500).json({error:"Internal Server Error"}),S?.end(d.statusCode||500,true);}},h=(p,d)=>{let S=s&&a?a.trackRequest():null;if(c.length>0)for(let b=0;b<c.length;b++)d.setHeader(c[b][0],c[b][1]);u(p,d,S).catch(b=>{s&&console.error("Fatal error:",b),d.headersSent||(d.statusCode=500,d.end("Server Error")),S?.end(500,true);});},y={IncomingMessage:ve,ServerResponse:Me},R=r.httpsOptions?pt.createServer({...y,...r.httpsOptions},h):Te.createServer(y,h);R.on("clientError",(p,d)=>{d.destroyed||d.destroy();});let w=r.httpsOptions?"https":"http";return J.isWorker&&Se().start(),R.listen(n,()=>{i?.enabled?s&&g(`Worker ${process.pid} is listening on port ${n}`):(g(`\u{1F680} Server running at ${w}://localhost:${n}/`+(s?" (dev)":"")),s&&a&&g(`\u{1F4CA} Performance monitoring available at http://localhost:${n}/health`));}),R}return null}}};var We={name:"vercel",createHandler:t=>{if(typeof t=="string")throw new Error("vercelAdapter.createHandler() requires a ServerlessConfig object, not a routesDir string. Import your routes manifest and pass { routes } instead.");let e=null,r=()=>e||(e=(async()=>{Z(t.routes),B(t.cors),D(t.middleware),Y(t.middlewares),U(t.hooks);})(),e);return async(n,o)=>{await r(),le(n),fe(o);let s=n,i=o;try{if(H()&&(await k("beforeRequest",s,i)===!1||i.headersSent))return;let l=W(ue(s.url??"/"),s.method??"GET");if(!l){if(j()&&(await F(s,i),i.headersSent))return;i.status(404).json({error:"Route not found"});return}if(se(l)){i.status(l.status??500).json({error:l.error});return}s.params=l.params,await l.handler(s,i),H()&&await k("afterRequest",s,i);}catch(l){await P(s,i,l),i.headersSent||i.status(500).json({error:"Internal server error"});}}}};function he(t,e,r,n){let o=Object.keys(r).length>0;return {statusCode:t,headers:e,...o?{multiValueHeaders:r}:{},body:n}}var ze={name:"netlify",createHandler:t=>{if(typeof t=="string")throw new Error("netlifyAdapter.createHandler() requires a ServerlessConfig object. Run `lacis build` to generate routes/_manifest.ts and pass { routes } instead.");let e=null,r=()=>e||(e=(async()=>{Z(t.routes),B(t.cors),D(t.middleware),Y(t.middlewares),U(t.hooks);})(),e);return async(n,o)=>{await r();let s=n.queryStringParameters?"?"+new URLSearchParams(n.queryStringParameters).toString():"",i=n.path+s,l=new IncomingMessage(new Socket);if(l.url=i,l.method=n.httpMethod,l.headers=n.headers,n.body){let p=n.isBase64Encoded?"base64":"utf-8";l.push(Buffer.from(n.body,p));}l.push(null);let a="",c={},u={},h=false,y=new ServerResponse(l);y.writeHead=function(p,d){return y.statusCode=p,d&&(c={...c,...d}),this},y.setHeader=function(p,d){let S=p.toLowerCase();return Array.isArray(d)?(u[S]=d.map(String),c[S]=String(d[0])):c[S]=String(d),this},y.getHeader=function(p){return c[p.toLowerCase()]},y.end=function(p){return h=true,p!==void 0&&(a=typeof p=="string"?p:p.toString()),this},Object.defineProperty(y,"headersSent",{get:()=>h}),le(l),fe(y);let R=l,w=y;R.query=n.queryStringParameters??{};try{if(H()&&(await k("beforeRequest",R,w)===!1||h))return he(w.statusCode,c,u,a);let p=W(n.path,n.httpMethod);return p?se(p)?{statusCode:p.status??500,body:JSON.stringify({error:p.error})}:(R.params=p.params,await p.handler(R,w),H()&&await k("afterRequest",R,w),he(w.statusCode,c,u,a)):j()&&(await F(R,w),h)?he(w.statusCode,c,u,a):{statusCode:404,body:JSON.stringify({error:"Route not found"})}}catch(p){return await P(R,w,p),h?he(w.statusCode,c,u,a):{statusCode:500,body:JSON.stringify({error:"Internal server error"})}}}}};var vt=10485760,Mt=new TextEncoder,_e=class{params={};url;method;headers;socket={setTimeout:e=>{}};connection;_req;constructor(e,r,n,o){this._req=e,this.url=r+n,this.method=e.method,this.headers=e.headers,this.connection={remoteAddress:o};}setTimeout(e){}text(){return this._req.text()}body(){return this._req.arrayBuffer().then(e=>{if(e.byteLength>vt)throw Object.assign(new Error("Payload Too Large"),{code:413});return Buffer.from(e)})}},He=class extends X(_e){json(){return this._req.json()}},ke=class{statusCode=200;headersSent=false;get finished(){return this.headersSent}get writableEnded(){return this.headersSent}_body=null;_headers=null;_sseReadable=null;_sseWriter=null;_sseWindowClosed=false;_listeners=null;on(e,r){return (e==="finish"||e==="close")&&(this._listeners||(this._listeners=[]),this._listeners.push(r)),this}once(e,r){return this.on(e,r)}emit(e){if((e==="finish"||e==="close")&&this._listeners)for(let r=0;r<this._listeners.length;r++)this._listeners[r]();return true}setHeader(e,r){if(this._headers||(this._headers=[]),Array.isArray(r))for(let n of r)this._headers.push(e,n);else this._headers.push(e,r);return this}getHeader(e){if(!this._headers)return;let r=e.toLowerCase();for(let n=0;n<this._headers.length;n+=2)if(this._headers[n].toLowerCase()===r)return this._headers[n+1]}removeHeader(e){if(!this._headers)return this;let r=e.toLowerCase();for(let n=0;n<this._headers.length;n+=2)if(this._headers[n].toLowerCase()===r){this._headers.splice(n,2);break}return this}hasHeader(e){if(!this._headers)return false;let r=e.toLowerCase();for(let n=0;n<this._headers.length;n+=2)if(this._headers[n].toLowerCase()===r)return true;return false}writeHead(e,r){if(this.statusCode=e,r)for(let[n,o]of Object.entries(r))this.setHeader(n,o);return this}write(e){return this._sseWriter?(this._sseWriter.write(Mt.encode(String(e))),true):(this._body=(this._body??"")+e,true)}end(e){if(e!==void 0&&this.write(e),this._sseWriter&&this._sseWriter.close(),this.headersSent=true,this._listeners)for(let r=0;r<this._listeners.length;r++)this._listeners[r]();return this}_initSseStream(){if(this._sseWindowClosed)throw new Error("[lacis/bun] initSSE() must be called synchronously before any `await` in your handler.");let{readable:e,writable:r}=new TransformStream;this._sseReadable=e,this._sseWriter=r.getWriter();}_closeSseWindow(){this._sseWindowClosed=true;}},Oe=class extends ee(ke){initSSE(e){return this._initSseStream(),super.initSSE(e)}},Ve={name:"bun",createHandler:t=>{if(typeof t!="string")throw new Error("bunAdapter requires a routesDir string, not a ServerlessConfig.");let e=t;return async(r={})=>{let{isDev:n,port:o=3e3,defaultHeaders:s,cluster:i}=r,l=parseInt(process.env.LACIS_BUN_WORKER??"0"),a=l>0;if(i?.enabled&&!a){let h=i.workers??x__default.cpus().length;g(`\u{1F9F5} Starting Bun server with ${h} workers (reusePort)`);let y=Array.from({length:h},()=>Bun.spawn(process.argv,{env:{...process.env,LACIS_BUN_WORKER:String(process.pid)},stdout:"ignore",stderr:"inherit"}));return g(`\u{1F680} Server running at http://localhost:${o}/`),{close:R=>{for(let w of y)w.kill();R?.();}}}if(a){let h=setInterval(()=>{try{process.kill(l,0);}catch{clearInterval(h),process.exit(0);}},2e3);h.unref();}g("\u{1F680} Bun high-performance mode enabled"),r.routes||await ne(e),B(r.cors),D(r.middleware),U(r.hooks);let c=s?Object.entries(s):[],u=Bun.serve({port:o,reusePort:a,async fetch(h,y){let R=new URL(h.url),w=R.pathname,p=new He(h,w,R.search,y?.requestIP(h)?.address??"");p.query=ce(R.search);let d=new Oe;try{for(let T=0;T<c.length;T++)d.setHeader(c[T][0],c[T][1]);if(H()&&(!await k("beforeRequest",p,d)||d.headersSent))return te(d);let S=W(w,h.method);if(!S)return j()&&(await F(p,d),d.headersSent)?te(d):new Response(JSON.stringify({error:"Route not found"}),{status:404,headers:{"Content-Type":"application/json"}});if("error"in S)return H()&&await k("onError",p,d),new Response(JSON.stringify({error:S.error}),{status:S.status||500,headers:{"Content-Type":"application/json"}});p.params=S.params;let b=null,I=(async()=>{await S.handler(p,d),H()&&await k("afterRequest",p,d),d.headersSent||d.end();})().catch(T=>{b=T,n&&console.error("Server error:",T),d._sseReadable&&!d.headersSent&&d.end();});return d._closeSseWindow(),d._sseReadable?te(d,d._sseReadable):(await I,b&&(await P(p,d,b),!d.headersSent)?new Response(JSON.stringify({error:"Internal Server Error"}),{status:500,headers:{"Content-Type":"application/json"}}):te(d))}catch(S){return await P(p,d,S),d.headersSent?te(d):new Response(JSON.stringify({error:"Internal Server Error"}),{status:500,headers:{"Content-Type":"application/json"}})}}});return g(`\u{1F680} Server started on http://localhost:${o}${n?" (dev)":""}`),{close:()=>{u.stop();}}}}};function te(t,e){let r=e??t._body;if(!t._headers)return new Response(r,{status:t.statusCode});let n=new Headers;for(let o=0;o<t._headers.length;o+=2){let s=t._headers[o],i=t._headers[o+1];s.toLowerCase()==="set-cookie"?n.append(s,i):n.set(s,i);}return new Response(r,{status:t.statusCode,headers:n})}var Tt={node:Be,vercel:We,netlify:ze,bun:Ve};function ss(t="node"){let e=Tt[t];if(!e)throw new Error(`Platform "${t}" not supported`);return e}
26
+ export{B as A,q as B,O as C,Ne as D,dt as E,Yt as F,Qt as G,Kt as H,Zt as I,Xt as J,er as K,tr as L,rr as M,lt as N,ut as O,ct as P,qe as Q,Ie as R,$e as S,Pe as T,Be as U,We as V,ze as W,Ve as X,ss as Y,ge as a,Ge as b,Ye as c,Qe as d,H as e,k as f,ye as g,kt as h,Y as i,U as j,j as k,F as l,Ot as m,xt as n,D as o,g as p,V as q,se as r,Z as s,ne as t,W as u,Bt as v,Wt as w,zt as x,Vt as y,Xe as z};
@@ -42,15 +42,7 @@ interface Response extends ServerResponse {
42
42
  redirect(url: string, status?: number): void;
43
43
  send(data: any): void;
44
44
  status(code: number): Response;
45
- initSSE(options?: SSEOptions): void;
46
- sseSend(data: string): void;
47
- sseJson(data: any): void;
48
- sseEvent(event: string, data: any): void;
49
- sseComment(comment: string): void;
50
- sseId(id: string): void;
51
- sseRetry(ms: number): void;
52
- sseClose(comment?: string): void;
53
- sseError(event: string, error: string, code?: number, details?: string): void;
45
+ initSSE(options?: SSEOptions): SSEContext;
54
46
  }
55
47
 
56
48
  type MiddlewareType = 'beforeRequest' | 'afterRequest' | 'onError';
@@ -114,6 +106,16 @@ interface Adapter {
114
106
  createHandler: (config: string | ServerlessConfig) => unknown;
115
107
  }
116
108
 
109
+ interface SSEContext {
110
+ send(data: string): boolean;
111
+ json(data: any): boolean;
112
+ event(event: string, data: any): boolean;
113
+ comment(text: string): boolean;
114
+ id(id: string): boolean;
115
+ retry(ms: number): boolean;
116
+ close(comment?: string): void;
117
+ error(event: string, message: string, code?: number, details?: string): void;
118
+ }
117
119
  interface SSEOptions {
118
120
  headers?: Record<string, string>;
119
121
  timeout?: number;
@@ -212,4 +214,4 @@ interface ClusterConfig {
212
214
  workers?: number;
213
215
  }
214
216
 
215
- export type { AdapterRequest as A, CorsConfig as C, Handler as H, MiddlewareType as M, NotFoundHook as N, PathMiddlewares as P, Request as R, ServerConfig as S, UploadedFile as U, AdapterResponse as a, Response as b, ServerlessRoute as c, MiddlewareCallback as d, ShutdownHook as e, ServerlessMiddleware as f, SSEOptions as g, SSEClientOptions as h, SSEEventHandlers as i, SSEClient as j, Adapter as k, AdapterContext as l, ClusterConfig as m, CookieOptions as n, MiddlewareModule as o, RequestCookies as p, ResponseCookies as q, Route as r, RouteHandlers as s, ServerlessConfig as t };
217
+ export type { AdapterRequest as A, CorsConfig as C, Handler as H, MiddlewareType as M, NotFoundHook as N, PathMiddlewares as P, Request as R, ServerConfig as S, UploadedFile as U, AdapterResponse as a, Response as b, ServerlessRoute as c, MiddlewareCallback as d, ShutdownHook as e, ServerlessMiddleware as f, SSEOptions as g, SSEClientOptions as h, SSEEventHandlers as i, SSEClient as j, Adapter as k, AdapterContext as l, ClusterConfig as m, CookieOptions as n, MiddlewareModule as o, RequestCookies as p, ResponseCookies as q, Route as r, RouteHandlers as s, SSEContext as t, ServerlessConfig as u };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { S as ServerConfig, A as AdapterRequest, a as AdapterResponse, R as Request, b as Response, c as ServerlessRoute, M as MiddlewareType, d as MiddlewareCallback, P as PathMiddlewares, N as NotFoundHook, e as ShutdownHook, f as ServerlessMiddleware, C as CorsConfig, g as SSEOptions, h as SSEClientOptions, i as SSEEventHandlers, j as SSEClient } from './index-BPqzMBG-.js';
2
- export { k as Adapter, l as AdapterContext, m as ClusterConfig, n as CookieOptions, H as Handler, o as MiddlewareModule, p as RequestCookies, q as ResponseCookies, r as Route, s as RouteHandlers, t as ServerlessConfig, U as UploadedFile } from './index-BPqzMBG-.js';
1
+ import { S as ServerConfig, A as AdapterRequest, a as AdapterResponse, R as Request, b as Response, c as ServerlessRoute, M as MiddlewareType, d as MiddlewareCallback, P as PathMiddlewares, N as NotFoundHook, e as ShutdownHook, f as ServerlessMiddleware, C as CorsConfig, g as SSEOptions, h as SSEClientOptions, i as SSEEventHandlers, j as SSEClient } from './index-pGWdfH82.js';
2
+ export { k as Adapter, l as AdapterContext, m as ClusterConfig, n as CookieOptions, H as Handler, o as MiddlewareModule, p as RequestCookies, q as ResponseCookies, r as Route, s as RouteHandlers, t as SSEContext, u as ServerlessConfig, U as UploadedFile } from './index-pGWdfH82.js';
3
3
  import { Server, ServerResponse, IncomingMessage } from 'http';
4
4
 
5
5
  interface VercelRequest {
@@ -388,15 +388,27 @@ declare function withCache(options: WithCacheOptions, handler: (req: Request, re
388
388
  declare function createCorsMiddleware(config: CorsConfig): MiddlewareCallback;
389
389
  declare function registerCorsConfig(cors?: CorsConfig): void;
390
390
 
391
- declare function initSSE(res: ServerResponse, options?: SSEOptions): ReturnType<typeof setTimeout>;
392
- declare function send(res: ServerResponse, data: any): boolean;
393
- declare function sendJson(res: ServerResponse, data: any): boolean;
394
- declare function sendEvent(res: ServerResponse, event: string, data: any): boolean;
395
- declare function sseComment(res: ServerResponse, comment: string): boolean;
396
- declare function sseId(res: ServerResponse, id: string): boolean;
397
- declare function sseRetry(res: ServerResponse, ms: number): boolean;
398
- declare function sseClose(res: ServerResponse, comment?: string): void;
399
- declare function sseEventError(res: ServerResponse, event: string, error: string, code?: number, details?: string): void;
391
+ interface ResLike {
392
+ readonly writableEnded: boolean;
393
+ write(chunk: any): any;
394
+ end(data?: any): any;
395
+ writeHead(code: number, headers?: Record<string, string>): any;
396
+ on(event: string, cb: () => void): any;
397
+ }
398
+ declare class SSEContext {
399
+ private _res;
400
+ private _timeoutId;
401
+ constructor(_res: ResLike, timeout: number);
402
+ send(data: string): boolean;
403
+ json(data: any): boolean;
404
+ event(event: string, data: any): boolean;
405
+ comment(text: string): boolean;
406
+ id(id: string): boolean;
407
+ retry(ms: number): boolean;
408
+ close(comment?: string): void;
409
+ error(event: string, message: string, code?: number, details?: string): void;
410
+ }
411
+ declare function initSSE(res: ServerResponse, options?: SSEOptions): SSEContext;
400
412
 
401
413
  /**
402
414
  * Creates a Server-Sent Events (SSE) client that can handle both URL connections and existing SSE streams
@@ -408,4 +420,4 @@ declare function sseEventError(res: ServerResponse, event: string, error: string
408
420
  */
409
421
  declare function createSSEClient(urlOrReq: string | IncomingMessage, options?: SSEClientOptions, handlers?: SSEEventHandlers, req?: IncomingMessage): Promise<SSEClient> | SSEClient;
410
422
 
411
- export { AdapterRequest, AdapterResponse, type AlarmStatus, type BalancerOptions, type CacheEntry, CorsConfig, type DefineHandlerConfig, type DefinedHandler, HTTP_STATUS, type HandlerCacheOptions, type HandlerMeta, type InferOutput, type MetricValue, type Metrics, MiddlewareCallback, MiddlewareType, type MonitorOptions, type NetlifyContext, type NetlifyEvent, type NetlifyHandler, type NetlifyResponse, NotFoundHook, type OpenApiConfig, type OpenApiInfo, PathMiddlewares, type PerformanceData, type PlatformHandler, type RateLimitOptions, Request, Response, type ResponseCacheOptions, SSEClient, SSEClientOptions, SSEEventHandlers, SSEOptions, ServerConfig, ServerlessMiddleware, ServerlessRoute, ShutdownHook, type StandardIssue, type StandardSchema, type StatsMessage, type Store, type ValidatedRequest, type VercelRequest, type VercelResponse, type WithCacheOptions, type WorkerStats, addExactPathMiddleware, addMiddleware, addPathMiddleware, buildOpenApiDoc, collectMiddleware, createBadRequestError, createConflictError, createCorsMiddleware, createForbiddenError, createGatewayTimeoutError, createHttpError, createInternalServerError, createMethodNotAllowedError, createNotFoundError, createRateLimit, createRateLimitError, createResponseCache, createSSEClient, createServer, createServiceUnavailableError, createStore, createUnauthorizedError, createValidationError, defaultCacheKey, defaultConfig, defineHandler, findRoute, getConfig, getPathMiddlewares, getRouterStats, getRoutesDir, hasMiddlewares, hasNotFoundHook, initSSE, interceptResponse, isHttpError, isRouteError, loadMiddlewares, loadRoutes, logError, normalizeError, registerCorsConfig, registerHooksConfig, registerMiddlewareConfig, registerMiddlewares, registerRoutes, replayEntry, resetMiddlewares, resetRouter, router, runMiddlewares, runNotFoundHook, runShutdownHook, send, sendError, sendEvent, sendJson, setVerboseLogging, sseClose, sseComment, sseEventError, sseId, sseRetry, withCache };
423
+ export { AdapterRequest, AdapterResponse, type AlarmStatus, type BalancerOptions, type CacheEntry, CorsConfig, type DefineHandlerConfig, type DefinedHandler, HTTP_STATUS, type HandlerCacheOptions, type HandlerMeta, type InferOutput, type MetricValue, type Metrics, MiddlewareCallback, MiddlewareType, type MonitorOptions, type NetlifyContext, type NetlifyEvent, type NetlifyHandler, type NetlifyResponse, NotFoundHook, type OpenApiConfig, type OpenApiInfo, PathMiddlewares, type PerformanceData, type PlatformHandler, type RateLimitOptions, Request, Response, type ResponseCacheOptions, SSEClient, SSEClientOptions, SSEEventHandlers, SSEOptions, ServerConfig, ServerlessMiddleware, ServerlessRoute, ShutdownHook, type StandardIssue, type StandardSchema, type StatsMessage, type Store, type ValidatedRequest, type VercelRequest, type VercelResponse, type WithCacheOptions, type WorkerStats, addExactPathMiddleware, addMiddleware, addPathMiddleware, buildOpenApiDoc, collectMiddleware, createBadRequestError, createConflictError, createCorsMiddleware, createForbiddenError, createGatewayTimeoutError, createHttpError, createInternalServerError, createMethodNotAllowedError, createNotFoundError, createRateLimit, createRateLimitError, createResponseCache, createSSEClient, createServer, createServiceUnavailableError, createStore, createUnauthorizedError, createValidationError, defaultCacheKey, defaultConfig, defineHandler, findRoute, getConfig, getPathMiddlewares, getRouterStats, getRoutesDir, hasMiddlewares, hasNotFoundHook, initSSE, interceptResponse, isHttpError, isRouteError, loadMiddlewares, loadRoutes, logError, normalizeError, registerCorsConfig, registerHooksConfig, registerMiddlewareConfig, registerMiddlewares, registerRoutes, replayEntry, resetMiddlewares, resetRouter, router, runMiddlewares, runNotFoundHook, runShutdownHook, sendError, setVerboseLogging, withCache };
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import {q,y,n,s,t,p as p$1,P,m as m$1}from'./chunk-PV6YEQ6J.js';export{c as addExactPathMiddleware,a as addMiddleware,b as addPathMiddleware,d as collectMiddleware,z as createCorsMiddleware,B as createSSEClient,u as findRoute,h as getPathMiddlewares,w as getRouterStats,v as getRoutesDir,e as hasMiddlewares,k as hasNotFoundHook,C as initSSE,r as isRouteError,g as loadMiddlewares,t as loadRoutes,A as registerCorsConfig,j as registerHooksConfig,o as registerMiddlewareConfig,i as registerMiddlewares,s as registerRoutes,n as resetMiddlewares,y as resetRouter,q as router,f as runMiddlewares,l as runNotFoundHook,m as runShutdownHook,D as send,F as sendEvent,E as sendJson,x as setVerboseLogging,J as sseClose,G as sseComment,K as sseEventError,H as sseId,I as sseRetry}from'./chunk-PV6YEQ6J.js';import B from'cluster';var v={port:3e3,isDev:process.env.NODE_ENV==="development",timeout:3e4,cluster:{enabled:false,workers:void 0},platform:"node"};function ke(e={}){return {...v,...e}}function R(e){let t=new Map;function r(n){let a=t.get(n);if(a){if(Date.now()>=a.expiresAt){t.delete(n);return}return t.delete(n),t.set(n,a),a}}function o(n,a){if(t.size>=e){let i=0;for(let s of t.keys())if(t.delete(s),++i>=50)break}t.set(n,a);}return {get:r,set:o}}function h(e,t){let r="",o=false,n=false,a=e.setHeader.bind(e);e.setHeader=(s,d)=>{let c=s.toLowerCase();return c==="content-type"&&(r=String(d)),c==="set-cookie"&&(o=true),a(s,d)};let i=e.end.bind(e);e.end=s=>(n||(n=true,!o&&!r.includes("text/event-stream")&&t({body:s,status:e.statusCode,contentType:r})),i(s));}function C(e,t){t.contentType&&e.setHeader("Content-Type",t.contentType),e.statusCode=t.status,e.end(t.body);}function T(e){return (e.method??"GET")+":"+(e.url??"/")}function Ne(e){let{ttl:t,methods:r=["GET","HEAD"],maxSize:o=500,keyGenerator:n=T,match:a,exclude:i,shouldCache:s=(u,y)=>y.statusCode>=200&&y.statusCode<300}=e,d=new Set(r.map(u=>u.toUpperCase())),c=i?Array.isArray(i)?i:[i]:[],l=R(o);return (u,y)=>{if(!d.has((u.method??"GET").toUpperCase()))return;let g=u.url??"/",j=g.indexOf("?")===-1?g:g.slice(0,g.indexOf("?"));if(c.some(x=>j.startsWith(x))||a&&!a(u))return;let k=n(u),A=l.get(k);if(A)return C(y,A),false;h(y,x=>{s(u,y)&&l.set(k,{...x,expiresAt:Date.now()+t*1e3});});}}function Ie(e,t){let{ttl:r,maxSize:o=500,key:n}=e,a=R(o);return async(i,s)=>{let d=n?n(i):T(i),c=a.get(d);if(c){C(s,c);return}h(s,l=>{s.statusCode>=200&&s.statusCode<300&&a.set(d,{...l,expiresAt:Date.now()+r*1e3});}),await t(i,s);}}async function w(e,t){let r=await e["~standard"].validate(t);return r.issues?{success:false,issues:r.issues}:{success:true,data:r.value}}function O(e){return Array.from(e).map(t=>({message:t.message,path:t.path?Array.from(t.path).map(r=>typeof r=="object"&&"key"in r?r.key:r):void 0}))}function _e(e){let t=e.cache?R(e.cache.maxSize??500):null,r=async(o,n)=>{if(t&&e.cache){let a=e.cache.key?e.cache.key(o):T(o),i=t.get(a);if(i){C(n,i);return}h(n,s=>{n.statusCode>=200&&n.statusCode<300&&t.set(a,{...s,expiresAt:Date.now()+e.cache.ttl*1e3});});}if(e.params){let a=await w(e.params,o.params??{});if(!a.success){n.status(400).json({error:"Validation failed",issues:O(a.issues)});return}o.params=a.data;}if(e.query){let a=await w(e.query,o.query??{});if(!a.success){n.status(400).json({error:"Validation failed",issues:O(a.issues)});return}o.query=a.data;}if(e.body){let a;try{a=await o.json();}catch{n.status(400).json({error:"Invalid JSON body"});return}let i=await w(e.body,a);if(!i.success){n.status(400).json({error:"Validation failed",issues:O(i.issues)});return}o.body=i.data;}await e.handler(o,n);};return r._defineHandler=e,r}async function H(e){let t=e?.["~standard"]?.vendor;if(!t)return null;try{if(t==="zod"){let r=await import('zod');if(typeof r.toJSONSchema=="function")return r.toJSONSchema(e);let o=await import('zod-to-json-schema');return (o.zodToJsonSchema??o.default)(e,{target:"openApi3"})}if(t==="valibot"){let r=await import('@valibot/to-json-schema');return (r.toJsonSchema??r.default)(e)}if(t==="arktype")return e.toJsonSchema()}catch{p$1(`[openapi] no converter found for "${t}" \u2014 install the matching json-schema package`);}return null}function V(e){return e.replace(/:(\w+)\??/g,"{$1}")}async function z(e,t){let r={responses:{200:{description:"Success"}}};t.meta?.summary&&(r.summary=t.meta.summary),t.meta?.description&&(r.description=t.meta.description),t.meta?.tags&&(r.tags=t.meta.tags),t.meta?.deprecated&&(r.deprecated=t.meta.deprecated);let o=[];if(t.params){let n=await H(t.params);if(n?.properties)for(let[a,i]of Object.entries(n.properties))o.push({name:a,in:"path",required:true,schema:i});}if(t.query){let n=await H(t.query);if(n?.properties){let a=n.required??[];for(let[i,s]of Object.entries(n.properties))o.push({name:i,in:"query",required:a.includes(i),schema:s});}}if(o.length>0&&(r.parameters=o),t.body){let n=await H(t.body);n&&(r.requestBody={required:true,content:{"application/json":{schema:n}}});}return r}async function L(e){let t=q.getRoutes(),r={};for(let{method:o,path:n,handler:a}of t){let i=a._defineHandler,s=V(n);r[s]||(r[s]={}),r[s][o.toLowerCase()]=i?await z(o,i):{responses:{200:{description:"Success"}}};}return {openapi:"3.1.0",info:e.info,paths:r}}var S=null,M=false,U=false,b=new Set;async function Je(e,t$1=v){let{platform:r="node"}=t$1,o=t$1.isDev&&B.isPrimary&&!process.env.LACIS_BUN_WORKER;try{t$1.routes?(y(),n(),s(t$1.routes)):await t(e);let n$1=null,a=null;t$1.openapi&&(n$1=await L(t$1.openapi),a=t$1.openapi.path??"/openapi.json"),o&&p$1(`\u{1F4C2} Routes loaded from: ${e}`);let s$1=P(r).createHandler(e),d;switch(r){case "node":d=await s$1(t$1),S=d,S.on("connection",c=>{b.add(c),c.on("close",()=>b.delete(c));});break;case "bun":d=s$1(t$1);break;case "vercel":case "netlify":d=s$1;break;default:throw new Error(`Unsupported platform: "${r}"`)}return n$1&&a&&(q.addRoute("GET",a,(c,l)=>l.json(n$1)),o&&p$1(`OpenAPI doc available at ${a}`)),Q(),d}catch(n){throw o&&p$1("\u274C Failed to create server:",n),n}}function Q(){if(!B.isPrimary||U)return;U=true;let e=async t=>{if(!M){if(M=true,p$1(`
2
- ${t} received, shutting down...`),await m$1(),S&&typeof S.close=="function"){for(let r of b)r.destroy();b.clear(),await new Promise(r=>{S.close(()=>r()),setTimeout(()=>r(),3e3);});}process.exit(0);}};process.on("SIGINT",()=>e("SIGINT")),process.on("SIGTERM",()=>e("SIGTERM")),process.on("SIGHUP",()=>e("SIGHUP"));}var m={OK:200,CREATED:201,NO_CONTENT:204,BAD_REQUEST:400,UNAUTHORIZED:401,FORBIDDEN:403,NOT_FOUND:404,METHOD_NOT_ALLOWED:405,CONFLICT:409,UNPROCESSABLE_ENTITY:422,TOO_MANY_REQUESTS:429,INTERNAL_SERVER_ERROR:500,SERVICE_UNAVAILABLE:503,GATEWAY_TIMEOUT:504},J={400:"Bad Request",401:"Unauthorized",403:"Forbidden",404:"Not Found",405:"Method Not Allowed",409:"Conflict",422:"Unprocessable Entity",429:"Too Many Requests",500:"Internal Server Error",503:"Service Unavailable",504:"Gateway Timeout"};function p(e){let t=e.code||m.INTERNAL_SERVER_ERROR,r=e.message||J[t]||"Unknown Error",o=e.name||`HttpError${t}`,n=new Error(r);return Error.captureStackTrace?.(n,p),{name:o,code:t,message:r,details:e.details,expose:e.expose!==void 0?e.expose:t<500,log:e.log!==void 0?e.log:t>=500,stack:n.stack}}function W(e){let t={error:e.message,code:e.code};return e.expose&&e.details?{...t,details:e.details}:t}function F(e,t){t.headersSent||(t.statusCode=e.code,t.setHeader("Content-Type","application/json"),t.end(JSON.stringify(W(e)))),e.log&&$(e);}function $(e){let t={name:e.name,message:e.message,code:e.code,details:e.details,stack:e.stack};e.code>=500?p$1("\u274C ERROR:",JSON.stringify(t,null,2)):p$1("\u26A0\uFE0F WARNING:",JSON.stringify(t,null,2));}function Ke(e,t){return p({name:"BadRequestError",code:m.BAD_REQUEST,message:e,details:t})}function Xe(e,t){return p({name:"UnauthorizedError",code:m.UNAUTHORIZED,message:e,details:t})}function Ye(e,t){return p({name:"ForbiddenError",code:m.FORBIDDEN,message:e,details:t})}function Ze(e,t){return p({name:"NotFoundError",code:m.NOT_FOUND,message:e,details:t})}function et(e,t){return p({name:"MethodNotAllowedError",code:m.METHOD_NOT_ALLOWED,message:e,details:t})}function tt(e,t){return p({name:"ConflictError",code:m.CONFLICT,message:e,details:t})}function rt(e,t){return p({name:"ValidationError",code:m.UNPROCESSABLE_ENTITY,message:e,details:t,expose:true})}function G(e,t){return p({name:"RateLimitError",code:m.TOO_MANY_REQUESTS,message:e,details:t})}function K(e,t){return p({name:"InternalServerError",code:m.INTERNAL_SERVER_ERROR,message:e,details:t})}function X(e,t){return p({name:"ServiceUnavailableError",code:m.SERVICE_UNAVAILABLE,message:e,details:t})}function Y(e,t){return p({name:"GatewayTimeoutError",code:m.GATEWAY_TIMEOUT,message:e,details:t})}function nt(e){if(e&&typeof e=="object"&&typeof e.code=="number"&&"message"in e)return e;let t=e?.message||"Unknown error occurred",r=e?.stack?{stack:e.stack}:void 0,o=e?.code||e?.statusCode;return o==="ECONNREFUSED"||o==="ENOTFOUND"?X(`Service connection failed: ${t}`,r):o==="ETIMEDOUT"?Y(`Request timed out: ${t}`,r):typeof o=="number"&&o>=400&&o<600?p({code:o,message:t,details:r}):K(t,r)}function ot(e){return !!(e&&typeof e=="object"&&typeof e.code=="number"&&"message"in e&&"name"in e)}function it(e={}){let t=e.windowMs??6e4,r=e.max??100,o=e.message??"Too Many Requests",n=e.keyGenerator??(s=>{let d=s.headers["x-forwarded-for"];return (typeof d=="string"?d.split(",")[0].trim():s.socket?.remoteAddress)??"unknown"}),a=new Map;return setInterval(()=>{let s=Date.now();for(let[d,c]of a)s>=c.resetAt&&a.delete(d);},t).unref(),(s,d)=>{let c=n(s),l=Date.now(),u=a.get(c);(!u||l>=u.resetAt)&&(u={count:0,resetAt:l+t},a.set(c,u)),u.count++;let y=Math.max(0,r-u.count),g=Math.ceil(u.resetAt/1e3);if(d.setHeader("X-RateLimit-Limit",String(r)),d.setHeader("X-RateLimit-Remaining",String(y)),d.setHeader("X-RateLimit-Reset",String(g)),u.count>r)return d.setHeader("Retry-After",String(Math.ceil((u.resetAt-l)/1e3))),F(G(o),d),false}}export{m as HTTP_STATUS,L as buildOpenApiDoc,Ke as createBadRequestError,tt as createConflictError,Ye as createForbiddenError,Y as createGatewayTimeoutError,p as createHttpError,K as createInternalServerError,et as createMethodNotAllowedError,Ze as createNotFoundError,it as createRateLimit,G as createRateLimitError,Ne as createResponseCache,Je as createServer,X as createServiceUnavailableError,R as createStore,Xe as createUnauthorizedError,rt as createValidationError,T as defaultCacheKey,v as defaultConfig,_e as defineHandler,ke as getConfig,h as interceptResponse,ot as isHttpError,$ as logError,nt as normalizeError,C as replayEntry,F as sendError,Ie as withCache};
1
+ import {q,y as y$1,n,s,t,p,Y,D,M,m}from'./chunk-XWERCAXC.js';export{B as HTTP_STATUS,c as addExactPathMiddleware,a as addMiddleware,b as addPathMiddleware,d as collectMiddleware,F as createBadRequestError,K as createConflictError,z as createCorsMiddleware,H as createForbiddenError,P as createGatewayTimeoutError,C as createHttpError,N as createInternalServerError,J as createMethodNotAllowedError,I as createNotFoundError,M as createRateLimitError,S as createSSEClient,O as createServiceUnavailableError,G as createUnauthorizedError,L as createValidationError,u as findRoute,h as getPathMiddlewares,w as getRouterStats,v as getRoutesDir,e as hasMiddlewares,k as hasNotFoundHook,T as initSSE,R as isHttpError,r as isRouteError,g as loadMiddlewares,t as loadRoutes,E as logError,Q as normalizeError,A as registerCorsConfig,j as registerHooksConfig,o as registerMiddlewareConfig,i as registerMiddlewares,s as registerRoutes,n as resetMiddlewares,y as resetRouter,q as router,f as runMiddlewares,l as runNotFoundHook,m as runShutdownHook,D as sendError,x as setVerboseLogging}from'./chunk-XWERCAXC.js';import G from'cluster';var w={port:3e3,isDev:process.env.NODE_ENV==="development",timeout:3e4,cluster:{enabled:false,workers:void 0},platform:"node"};function Ee(e={}){return {...w,...e}}function g(e){let t=new Map;function r(n){let o=t.get(n);if(o){if(Date.now()>=o.expiresAt){t.delete(n);return}return t.delete(n),t.set(n,o),o}}function a(n,o){if(t.size>=e){let i=0;for(let s of t.keys())if(t.delete(s),++i>=50)break}t.set(n,o);}return {get:r,set:a}}function h(e,t){let r="",a=false,n=false,o=e.setHeader.bind(e);if(e.setHeader=(s,d)=>{let u=s.toLowerCase();return u==="content-type"&&(r=String(d)),u==="set-cookie"&&(a=true),o(s,d)},typeof e.writeHead=="function"){let s=e.writeHead.bind(e);e.writeHead=(d,u)=>{if(u)for(let[c,p]of Object.entries(u)){let m=c.toLowerCase();m==="content-type"&&(r=String(p)),m==="set-cookie"&&(a=true);}return s(d,u)};}let i=e.end.bind(e);e.end=s=>(n||(n=true,!a&&!r.includes("text/event-stream")&&t({body:s,status:e.statusCode,contentType:r})),i(s));}function R(e,t){t.contentType&&e.setHeader("Content-Type",t.contentType),e.statusCode=t.status,e.end(t.body);}function C(e){return (e.method??"GET")+":"+(e.url??"/")}function Oe(e){let{ttl:t,methods:r=["GET","HEAD"],maxSize:a=500,keyGenerator:n=C,match:o,exclude:i,shouldCache:s=(p,m)=>m.statusCode>=200&&m.statusCode<300}=e,d=new Set(r.map(p=>p.toUpperCase())),u=i?Array.isArray(i)?i:[i]:[],c=g(a);return (p,m)=>{if(!d.has((p.method??"GET").toUpperCase()))return;let l=p.url??"/",N=l.indexOf("?")===-1?l:l.slice(0,l.indexOf("?"));if(u.some(x=>N.startsWith(x))||o&&!o(p))return;let H=n(p),E=c.get(H);if(E)return R(m,E),false;h(m,x=>{s(p,m)&&c.set(H,{...x,expiresAt:Date.now()+t*1e3});});}}function qe(e,t){let{ttl:r,maxSize:a=500,key:n}=e,o=g(a);return async(i,s)=>{let d=n?n(i):C(i),u=o.get(d);if(u){R(s,u);return}h(s,c=>{s.statusCode>=200&&s.statusCode<300&&o.set(d,{...c,expiresAt:Date.now()+r*1e3});}),await t(i,s);}}async function k(e,t){let r=await e["~standard"].validate(t);return r.issues?{success:false,issues:r.issues}:{success:true,data:r.value}}function v(e){return Array.from(e).map(t=>({message:t.message,path:t.path?Array.from(t.path).map(r=>typeof r=="object"&&"key"in r?r.key:r):void 0}))}function Me(e){let t=e.cache?g(e.cache.maxSize??500):null,r=async(a,n)=>{if(t&&e.cache){let o=e.cache.key?e.cache.key(a):C(a),i=t.get(o);if(i){R(n,i);return}h(n,s=>{n.statusCode>=200&&n.statusCode<300&&t.set(o,{...s,expiresAt:Date.now()+e.cache.ttl*1e3});});}if(e.params){let o=await k(e.params,a.params??{});if(!o.success){n.status(400).json({error:"Validation failed",issues:v(o.issues)});return}a.params=o.data;}if(e.query){let o=await k(e.query,a.query??{});if(!o.success){n.status(400).json({error:"Validation failed",issues:v(o.issues)});return}a.query=o.data;}if(e.body){let o;try{o=await a.json();}catch{n.status(400).json({error:"Invalid JSON body"});return}let i=await k(e.body,o);if(!i.success){n.status(400).json({error:"Validation failed",issues:v(i.issues)});return}a.body=i.data;}await e.handler(a,n);};return r._defineHandler=e,r}async function T(e){let t=e?.["~standard"]?.vendor;if(!t)return null;try{if(t==="zod"){let r=await import('zod');if(typeof r.toJSONSchema=="function")return r.toJSONSchema(e);let a=await import('zod-to-json-schema');return (a.zodToJsonSchema??a.default)(e,{target:"openApi3"})}if(t==="valibot"){let r=await import('@valibot/to-json-schema');return (r.toJsonSchema??r.default)(e)}if(t==="arktype")return e.toJsonSchema()}catch{p(`[openapi] no converter found for "${t}" \u2014 install the matching json-schema package`);}return null}function z(e){return e.replace(/:(\w+)\??/g,"{$1}")}async function V(e,t){let r={responses:{200:{description:"Success"}}};t.meta?.summary&&(r.summary=t.meta.summary),t.meta?.description&&(r.description=t.meta.description),t.meta?.tags&&(r.tags=t.meta.tags),t.meta?.deprecated&&(r.deprecated=t.meta.deprecated);let a=[];if(t.params){let n=await T(t.params);if(n?.properties)for(let[o,i]of Object.entries(n.properties))a.push({name:o,in:"path",required:true,schema:i});}if(t.query){let n=await T(t.query);if(n?.properties){let o=n.required??[];for(let[i,s]of Object.entries(n.properties))a.push({name:i,in:"query",required:o.includes(i),schema:s});}}if(a.length>0&&(r.parameters=a),t.body){let n=await T(t.body);n&&(r.requestBody={required:true,content:{"application/json":{schema:n}}});}return r}async function B(e){let t=q.getRoutes(),r={};for(let{method:a,path:n,handler:o}of t){let i=o._defineHandler,s=z(n);r[s]||(r[s]={}),r[s][a.toLowerCase()]=i?await V(a,i):{responses:{200:{description:"Success"}}};}return {openapi:"3.1.0",info:e.info,paths:r}}var y=null,j=false,_=false,b=new Set;async function Qe(e,t$1=w){let{platform:r="node"}=t$1,a=t$1.isDev&&G.isPrimary&&!process.env.LACIS_BUN_WORKER;try{t$1.routes?(y$1(),n(),s(t$1.routes)):await t(e);let n$1=null,o=null;t$1.openapi&&(n$1=await B(t$1.openapi),o=t$1.openapi.path??"/openapi.json"),a&&p(`\u{1F4C2} Routes loaded from: ${e}`);let s$1=Y(r).createHandler(e),d;switch(r){case "node":d=await s$1(t$1),y=d,y.on("connection",u=>{b.add(u),u.on("close",()=>b.delete(u));});break;case "bun":d=s$1(t$1);break;case "vercel":case "netlify":d=s$1;break;default:throw new Error(`Unsupported platform: "${r}"`)}return n$1&&o&&(q.addRoute("GET",o,(u,c)=>c.json(n$1)),a&&p(`OpenAPI doc available at ${o}`)),F(),d}catch(n){throw a&&p("\u274C Failed to create server:",n),n}}function F(){if(!G.isPrimary||_)return;_=true;let e=async t=>{if(!j){if(j=true,p(`
2
+ ${t} received, shutting down...`),await m(),y&&typeof y.close=="function"){for(let r of b)r.destroy();b.clear(),await new Promise(r=>{y.close(()=>r()),setTimeout(()=>r(),3e3);});}process.exit(0);}};process.on("SIGINT",()=>e("SIGINT")),process.on("SIGTERM",()=>e("SIGTERM")),process.on("SIGHUP",()=>e("SIGHUP"));}function $e(e={}){let t=e.windowMs??6e4,r=e.max??100,a=e.message??"Too Many Requests",n=e.keyGenerator??(s=>{let d=s.headers["x-forwarded-for"];return (typeof d=="string"?d.split(",")[0].trim():s.socket?.remoteAddress)??"unknown"}),o=new Map;return setInterval(()=>{let s=Date.now();for(let[d,u]of o)s>=u.resetAt&&o.delete(d);},t).unref(),(s,d)=>{let u=n(s),c=Date.now(),p=o.get(u);(!p||c>=p.resetAt)&&(p={count:0,resetAt:c+t},o.set(u,p)),p.count++;let m=Math.max(0,r-p.count),l=Math.ceil(p.resetAt/1e3);if(d.setHeader("X-RateLimit-Limit",String(r)),d.setHeader("X-RateLimit-Remaining",String(m)),d.setHeader("X-RateLimit-Reset",String(l)),p.count>r)return d.setHeader("Retry-After",String(Math.ceil((p.resetAt-c)/1e3))),D(M(a),d),false}}export{B as buildOpenApiDoc,$e as createRateLimit,Oe as createResponseCache,Qe as createServer,g as createStore,C as defaultCacheKey,w as defaultConfig,Me as defineHandler,Ee as getConfig,h as interceptResponse,R as replayEntry,qe as withCache};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lacis",
3
- "version": "0.3.1",
3
+ "version": "0.3.3",
4
4
  "description": "Zero-dependency TypeScript web framework",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -1,26 +0,0 @@
1
- import G,{join}from'path';import Ge from'fs/promises';import {existsSync}from'fs';import J from'cluster';import be,{IncomingMessage,ServerResponse}from'http';import lt from'https';import*as _ from'os';import ___default from'os';import {EventEmitter}from'events';import {Socket}from'net';var A={beforeRequest:[],afterRequest:[],onError:[]},B={onNotFound:[],onShutdown:[]},P=new Map,I=new Map;function pe(t,e){return A[t].push(e),{remove:()=>{let r=A[t].indexOf(e);r!==-1&&A[t].splice(r,1);}}}function He(t,e,r,o){return t.has(e)||t.set(e,{beforeRequest:[],afterRequest:[],onError:[]}),t.get(e)[r].push(o),{remove:()=>{let n=t.get(e);if(!n)return;let s=n[r].indexOf(o);s!==-1&&n[r].splice(s,1);}}}function Ke(t,e,r){return He(P,t==="/"?"/":t.replace(/\/+$/,""),e,r)}function Qe(t,e,r){return He(I,t==="/"?"/":t.replace(/\/+$/,""),e,r)}function ee(t,e,r){let o=t.get(e);o&&(r.beforeRequest.push(...o.beforeRequest),r.afterRequest.push(...o.afterRequest),r.onError.push(...o.onError));}function Xe(t){let e=t==="/"?"/":t.replace(/\/+$/,""),r=e.split("/").filter(Boolean),o={beforeRequest:[...A.beforeRequest],afterRequest:[...A.afterRequest],onError:[...A.onError]};ee(P,"/",o),e==="/"&&ee(I,"/",o);let n="";for(let s of r)n+="/"+s,ee(P,n,o),n===e&&ee(I,n,o);return o}function H(){return A.beforeRequest.length>0||A.afterRequest.length>0||A.onError.length>0||P.size>0||I.size>0}async function T(t,e,r,o){let n=e.url?.split("?")[0]||"/",s=Xe(n);if(s[t].length===0)return true;for(let i of s[t])try{if(await i(e,r,o)===!1)return !1}catch(l){if(t!=="onError")try{for(let a of s.onError)await a(e,r,{error:l,phase:t});}catch(a){console.error("Error in error handler:",a);}return false}return true}function Ye(t,e,r){e.has(r)||e.set(r,{beforeRequest:[],afterRequest:[],onError:[]});let o=e.get(r);for(let n of ["beforeRequest","afterRequest","onError"])if(t[n]){let s=Array.isArray(t[n])?t[n]:[t[n]];o[n].push(...s);}}async function me(t){P.clear(),I.clear();async function e(r,o=""){try{let n=await Ge.readdir(r,{withFileTypes:!0});for(let[s,i]of [["+middleware.global.ts",P],["+middleware.global.js",P],["+middleware.ts",I],["+middleware.js",I]]){let l=n.find(a=>a.name===s);if(l)try{let c=await import(`${G.resolve(G.join(r,l.name))}?update=${Date.now()}`);Ye(c,i,o);}catch(a){console.error(`Error loading middleware for ${o}:`,a);}}for(let s of n)s.isDirectory()&&await e(G.join(r,s.name),`${o==="/"?"":o}/${s.name}`);}catch(n){console.error(`Error scanning directory ${r}:`,n);}}await e(t,"/");}function Mt(){return P}function V(t){if(t?.length)for(let e of t){let r=e.type==="cascade"?Ke:Qe;for(let o of ["beforeRequest","afterRequest","onError"]){let n=e.module[o];if(!n)continue;let s=Array.isArray(n)?n:[n];for(let i of s)r(e.path,o,i);}}}function j(t){t&&(t.onNotFound&&B.onNotFound.push(t.onNotFound),t.onShutdown&&B.onShutdown.push(t.onShutdown));}function L(){return B.onNotFound.length>0}async function N(t,e){for(let r of B.onNotFound)if(await r(t,e),e.headersSent)return}async function kt(){for(let t of B.onShutdown)try{await t();}catch(e){console.error("Error in onShutdown hook:",e);}}function xt(){A.beforeRequest=[],A.afterRequest=[],A.onError=[],P.clear(),I.clear(),B.onNotFound=[],B.onShutdown=[];}function F(t){if(t)for(let e of ["beforeRequest","afterRequest","onError"]){let r=t[e];if(r){let o=Array.isArray(r)?r:[r];for(let n of o)pe(e,n);}}}function Ze(t,e){return !e||e==="*"?true:typeof e=="string"?t===e:Array.isArray(e)?e.includes(t):e instanceof RegExp?e.test(t):typeof e=="function"?e(t):false}function et(t){let e=(t.methods??["GET","POST","PUT","DELETE","PATCH","OPTIONS"]).join(", "),r=(t.allowedHeaders??["Content-Type","Authorization"]).join(", "),o=t.exposedHeaders?.join(", "),n=t.maxAge!=null?String(t.maxAge):null,s=!t.origin||t.origin==="*";return async(i,l)=>{let a=i.getHeader("origin");if(!a||!Ze(a,t.origin))return;let c=s&&!t.credentials;if(l.setHeader("Access-Control-Allow-Origin",c?"*":a),c||l.setHeader("Vary","Origin"),t.credentials&&l.setHeader("Access-Control-Allow-Credentials","true"),o&&l.setHeader("Access-Control-Expose-Headers",o),i.method==="OPTIONS")return l.setHeader("Access-Control-Allow-Methods",e),l.setHeader("Access-Control-Allow-Headers",r),n&&l.setHeader("Access-Control-Max-Age",n),l.status(204),l.end(),false}}function U(t){t&&pe("beforeRequest",et(t));}function R(...t){J.isPrimary&&console.log(...t);}function ot(t){let e=t.match(/^\[(\w+)(\??)]/);return e?{name:e[1],isParam:true,isOptional:e[2]==="?"}:{name:t,isParam:false,isOptional:false}}var at=1e3,it=100,ge=class{rootNode;cachedRoutes;routeCount;lastLoaded;verbose;constructor(){this.rootNode=this.createNode(),this.cachedRoutes=new Map,this.routeCount=0,this.lastLoaded=0,this.verbose=false;}createNode(){return {handlers:Object.create(null),staticChildren:new Map,paramChild:null,wildcardHandler:null,isEndpoint:false}}addRoute(e,r,o){let n=r.split("/").filter(Boolean),s=this.rootNode;for(let i of n){let l=ot(i);if(l.isParam){if(!s.paramChild)s.paramChild={name:l.name,node:this.createNode(),isOptional:l.isOptional};else if(s.paramChild.name!==l.name)throw new Error(`Route conflict: param name "[${l.name}]" conflicts with existing "[${s.paramChild.name}]" at the same path position. Use the same param name for all methods at this level.`);s=s.paramChild.node;}else {if(i==="*")return s.wildcardHandler||(s.wildcardHandler=Object.create(null)),s.wildcardHandler[e]=o,this.cachedRoutes.clear(),this;s.staticChildren.has(i)||s.staticChildren.set(i,this.createNode()),s=s.staticChildren.get(i);}}return s.isEndpoint=true,s.handlers[e]||this.routeCount++,s.handlers[e]=o,this.cachedRoutes.clear(),this}findRoute(e,r){let o=r==="/"?"/":r.replace(/\/+$/,""),n=e+":"+o,s=this.cachedRoutes.get(n);if(s)return this.cachedRoutes.delete(n),this.cachedRoutes.set(n,s),s;let i=o==="/"?[]:o.split("/").filter(Boolean),l=Object.create(null),a=this.traverse(this.rootNode,i,e,l,0)??{handler:null,params:Object.create(null)};if(a.handler!==null||a.allowedMethods?.length){if(this.cachedRoutes.size>=at){let c=0;for(let u of this.cachedRoutes.keys())if(this.cachedRoutes.delete(u),++c>=it)break}this.cachedRoutes.set(n,a);}return a}traverse(e,r,o,n,s){if(s===r.length){if(e.isEndpoint){let c=e.handlers[o]??(o==="HEAD"?e.handlers.GET:void 0)??e.handlers[""];if(c)return {handler:c,params:Object.assign(Object.create(null),n)};let u=Object.keys(e.handlers).filter(p=>p!=="");return {handler:null,params:Object.create(null),allowedMethods:u}}let a=e.paramChild;if(a?.isOptional&&a.node.isEndpoint){let c=a.node.handlers[o]??(o==="HEAD"?a.node.handlers.GET:void 0)??a.node.handlers[""];if(c)return {handler:c,params:Object.assign(Object.create(null),n)}}return null}let i=r[s],l=e.staticChildren.get(i);if(l){let a=this.traverse(l,r,o,n,s+1);if(a)return a}if(e.paramChild){let{name:a,node:c}=e.paramChild;n[a]=i;let u=this.traverse(c,r,o,n,s+1);if(u)return u;delete n[a];}if(e.wildcardHandler){let a=e.wildcardHandler[o]??e.wildcardHandler[""],c=r.slice(s).join("/");if(a)return {handler:a,params:Object.assign(Object.create(null),n,{"*":c})};let u=Object.keys(e.wildcardHandler).filter(p=>p!=="");if(u.length>0)return {handler:null,params:Object.assign(Object.create(null),n,{"*":c}),allowedMethods:u}}return null}async loadRoutes(e){this.rootNode=this.createNode(),this.cachedRoutes.clear(),this.routeCount=0,await me(e);let r=this;async function o(n,s=[]){try{let i=await Ge.readdir(n,{withFileTypes:!0}),l=i.find(a=>!a.isDirectory()&&(a.name==="index.ts"||a.name==="index.js"));if(l)try{let c=await import(`${G.resolve(G.join(n,l.name))}?update=${Date.now()}`),u=["GET","POST","PUT","DELETE","PATCH"],p="/"+s.join("/"),g=!1;for(let w of u)typeof c[w]=="function"&&(r.addRoute(w,p,c[w]),g=!0);!g&&typeof c.default=="function"&&r.addRoute("GET",p,c.default),r.verbose&&R(`Route loaded: ${p}`);}catch(a){console.error(`Error loading index in ${n}:`,a);}for(let a of i)if(a.isDirectory()){let c=a.name.match(/^\[(\w+)(\??)]/),u=c?`[${c[1]}${c[2]}]`:a.name;await o(G.join(n,a.name),[...s,u]);}}catch(i){console.error(`Error scanning directory ${n}:`,i);}}return await o(e),this.lastLoaded=Date.now(),this.verbose&&R(`Loading completed: ${this.routeCount} routes`),true}getRoutes(){let e=[];return this.collectRoutes(this.rootNode,[],e),e}collectRoutes(e,r,o){if(e.isEndpoint){let n=r.length===0?"/":"/"+r.join("/");for(let[s,i]of Object.entries(e.handlers))s!==""&&o.push({method:s,path:n,handler:i});}for(let[n,s]of e.staticChildren)this.collectRoutes(s,[...r,n],o);if(e.paramChild){let{name:n,isOptional:s,node:i}=e.paramChild;this.collectRoutes(i,[...r,s?`:${n}?`:`:${n}`],o);}if(e.wildcardHandler){let n="/"+[...r,"*"].join("/");for(let[s,i]of Object.entries(e.wildcardHandler))s!==""&&o.push({method:s,path:n,handler:i});}}getStats(){return {routeCount:this.routeCount,lastLoaded:this.lastLoaded,uptime:Date.now()-this.lastLoaded,cacheSize:this.cachedRoutes.size}}setVerbose(e){return this.verbose=e,this}reset(){this.rootNode=this.createNode(),this.cachedRoutes=new Map,this.routeCount=0;}},W=new ge;function te(t){return t&&typeof t=="object"&&"error"in t}function Q(t){for(let{path:e,handlers:r}of t){let o=e.replace(/:(\w+\??)/g,(n,s)=>s.endsWith("?")?`[${s.slice(0,-1)}?]`:`[${s}]`);for(let[n,s]of Object.entries(r))typeof s=="function"&&W.addRoute(n,o,s);}}async function re(t){let e=join(t,"_manifest.js");if(existsSync(e)){W.reset();let r=await import(e);Q(r.routes),r.middlewares?V(r.middlewares):await me(t);return}return W.loadRoutes(t)}function D(t,e="GET"){let r=W.findRoute(e,t);return r.handler?{handler:r.handler,params:r.params}:r.allowedMethods?.length?{error:"Method Not Allowed",status:405,allowedMethods:r.allowedMethods}:null}function Nt(t){let e=process.env.NODE_ENV==="production",r=process.env.ROUTES_DIR??t??(e?"dist/routes":"routes"),o=e&&!G.isAbsolute(r)&&!r.startsWith("dist/")?G.join("dist",r):r;return G.resolve(process.cwd(),o)}function Ft(){return W.getStats()}function Ut(t){W.setVerbose(t);}function Dt(){W.reset();}function Te(t,e={},r={},o){let n=false,s=0,i={},l=[],a=[],c=[],u=null;r.onMessage&&l.push(r.onMessage),r.onClose&&a.push(r.onClose),r.onError&&c.push(r.onError),r.onEvent&&Object.entries(r.onEvent).forEach(([f,m])=>{i[f]||(i[f]=[]),i[f].push(m);});let p=e.reconnectInterval||3e3,g=e.maxRetries||3,w=e.disableReconnect||false,y=e.body,h=e.contentType||(y?"application/json":void 0),d=e.method||(y?"POST":"GET");if(typeof t!="string"){let f=t;return n=true,f.on("data",m=>{v(m.toString());}),f.on("end",()=>{n=false;}),f.on("error",m=>{n=false,console.error("SSE Error:",m);}),b()}if(typeof t=="string")return new Promise((f,m)=>{$().then(()=>f(b())).catch(m);});function v(f){let m=f,S=m.split(`
2
-
3
- `);m=S.pop()||"";for(let M of S){let O=M.split(`
4
- `),C="",E="";for(let x of O)x.startsWith("event:")?E=x.slice(6).trim():x.startsWith("data:")&&(C=x.slice(5).trim());if(C)try{let x=JSON.parse(C);E&&i[E]?i[E].forEach(K=>K(x)):l.forEach(K=>K(x));}catch{E&&i[E]?i[E].forEach(x=>x(C)):l.forEach(x=>x(C));}}}function b(){return {onMessage(f){return l.push(f),this},onEvent(f,m){return i[f]||(i[f]=[]),i[f].push(m),this},onClose(f){return a.push(f),this},close(){u&&(u.destroy(),u=null),n=false,a.forEach(f=>f());}}}async function $(){if(!n){n=true;try{await k();}catch(f){!w&&s<g?(s++,setTimeout(()=>{$();},p)):c.forEach(m=>m(f));}}}async function k(){return new Promise((f,m)=>{let S=new URL(t);e.params&&(typeof e.params=="object"?Object.entries(e.params).forEach(([C,E])=>{S.searchParams.append(C,String(E));}):typeof e.params=="string"&&new URLSearchParams(e.params).forEach((E,x)=>{S.searchParams.append(x,E);}));let M={hostname:S.hostname,port:S.port||(S.protocol==="https:"?443:80),path:S.pathname+S.search,method:d,headers:{Accept:"text/event-stream",...o?.headers||{},"Cache-Control":"no-cache",Connection:"keep-alive",...h&&{"Content-Type":h},...y&&typeof y=="string"&&{"Content-Length":Buffer.byteLength(y).toString()},...y&&typeof y!="string"&&{"Content-Length":Buffer.byteLength(JSON.stringify(y)).toString()}}};u=(S.protocol==="https:"?lt:be).request(M,C=>{if(C.statusCode!==200){n=false,m(new Error(`Server responded with status: ${C.statusCode}`));return}C.setEncoding("utf8"),C.on("data",v),C.on("end",()=>{n=false,u?.destroy(),u=null,a.forEach(E=>E()),f(null);}),C.on("close",()=>{n=false,u?.destroy(),u=null,a.forEach(E=>E()),f(null);}),C.on("error",E=>{n=false,u?.destroy(),u=null,a.forEach(x=>x()),m(E);});}),u.on("error",C=>{n=false,u?.destroy(),u=null,m(C);}),y&&(typeof y=="string"?u.write(y):u.write(JSON.stringify(y))),u.end();})}return b()}function _e(t,e){let r=e?.headers?.["Cache-Control"]||"no-cache",o=e?.headers?.Connection||"keep-alive",n=e?.timeout||3e5;t.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":r,Connection:o,...e?.headers||{}});let s=setTimeout(()=>{t.end();},n);return t.on("close",()=>{clearTimeout(s);}),s}function Oe(t,e){return t.writableEnded?false:t.write(`data: ${e}
5
-
6
- `)}function Ae(t,e){return t.writableEnded?false:t.write(`data: ${JSON.stringify(e)}
7
-
8
- `)}function $e(t,e,r){return t.writableEnded?false:(t.write(`event: ${e}
9
- `),t.write(`data: ${JSON.stringify(r)}
10
-
11
- `))}function Pe(t,e){return t.writableEnded?false:t.write(`: ${e}
12
-
13
- `)}function qe(t,e){return t.writableEnded?false:t.write(`id: ${e}
14
-
15
- `)}function Ie(t,e){return t.writableEnded?false:t.write(`retry: ${e}
16
-
17
- `)}function je(t,e="Connection closed"){t.write(`: ${e}
18
-
19
- `),t.end();}function Le(t,e,r,o=500,n){t.writeHead(o,{"Content-Type":"text/event-stream"}),t.write(`event: ${e}
20
- `);let s={message:r,code:o,details:n||null};t.write(`data: ${JSON.stringify(s)}
21
-
22
- `),t.end();}var ut=10485760,ne=class{_parsed=null;_raw;constructor(e){this._raw=e;}_parse(){if(this._parsed!==null)return this._parsed;if(this._parsed={},!this._raw)return this._parsed;for(let e of this._raw.split(";")){let r=e.indexOf("=");if(r===-1)continue;let o=e.slice(0,r).trim(),n=e.slice(r+1).trim();if(o){let s=n.startsWith('"')&&n.endsWith('"')?n.slice(1,-1):n;try{this._parsed[o]=decodeURIComponent(s);}catch{this._parsed[o]=s;}}}return this._parsed}get(e){return this._parse()[e]}all(){return {...this._parse()}}},oe=class{_pending=[];set(e,r,o={}){return this._pending.push({name:e,value:r,opts:o}),this}delete(e,r={}){return this.set(e,"",{...r,maxAge:0,expires:new Date(0)})}serialize(){return this._pending.map(({name:e,value:r,opts:o})=>{let n=`${e}=${encodeURIComponent(r)}`,s=o.path!==void 0?o.path:"/";return s&&(n+=`; Path=${s}`),o.domain&&(n+=`; Domain=${o.domain}`),o.maxAge!=null&&(n+=`; Max-Age=${o.maxAge}`),o.expires&&(n+=`; Expires=${o.expires.toUTCString()}`),o.httpOnly&&(n+="; HttpOnly"),o.secure&&(n+="; Secure"),o.sameSite&&(n+=`; SameSite=${o.sameSite}`),n})}};function Ne(t){if(typeof t.get=="function")return t.get("cookie")??void 0;let e=t.cookie;return Array.isArray(e)?e.join("; "):e}function X(t){return class extends t{_zreqCookies;getHeader(e){let r=this.headers;if(typeof r.get=="function")return r.get(e)??void 0;let o=r[e.toLowerCase()];return Array.isArray(o)?o[0]:o}get cookies(){return this._zreqCookies||(this._zreqCookies=new ne(Ne(this.headers))),this._zreqCookies}json(){return this.body().then(e=>JSON.parse(e.toString()))}form(){return new Promise((e,r)=>{let o=this.headers,n=typeof o.get=="function"?o.get("content-type")??"":o["content-type"]??"";if(!n.startsWith("multipart/form-data")){r(new Error("Content-Type is not multipart/form-data"));return}let s=n.match(/boundary=(.+)$/);if(!s){r(new Error("Boundary not found"));return}let i=s[1].trim();this.body().then(l=>{let a={},c=Buffer.from(`--${i}`),u=[],p=0,g;for(;(g=l.indexOf(c,p))!==-1;)u.push(l.subarray(p,g)),p=g+c.length;u.push(l.subarray(p));for(let w=1;w<u.length-1;w++){let y=u[w].subarray(2),h=y.indexOf(`\r
23
- \r
24
- `);if(h===-1)continue;let d=y.subarray(0,h).toString(),v=y.subarray(h+4,y.length-2),b=d.match(/Content-Disposition: form-data; name="([^"]+)"(?:; filename="([^"]+)")?/i);if(b)if(b[2]){let $=d.match(/Content-Type:\s*([^\r\n]+)/i);a[b[1]]={filename:b[2],mimetype:$?$[1].trim():"application/octet-stream",data:v,size:v.length};}else a[b[1]]=v.toString("utf-8");}e(a);}).catch(r);})}createSSEClient(e,r){return Te(this,e,r)}}}function ae(){let t=[],e=0,r=false;return new Promise((o,n)=>{this.on("data",s=>{if(e+=s.length,e>ut){r=true,this.destroy(),n(Object.assign(new Error("Payload Too Large"),{code:413}));return}t.push(s);}).on("end",()=>{r||(r=true,o(Buffer.concat(t)));}).on("error",s=>{r||(r=true,n(s));});})}function Fe(t,e){let r=t.serialize();r.length>0&&!e.headersSent&&e.setHeader("Set-Cookie",r);}function Y(t){return class extends t{_zresCookies;get cookies(){return this._zresCookies||(this._zresCookies=new oe),this._zresCookies}end(e){return this._zresCookies&&Fe(this._zresCookies,this),super.end(e)}status(e){return this.statusCode=e,this}send(e){return typeof e=="string"?(this.setHeader("Content-Type","text/plain"),this.end(e)):this.json(e),this}json(e){return this.setHeader("Content-Type","application/json"),this.end(JSON.stringify(e)),this}html(e){return this.setHeader("Content-Type","text/html; charset=utf-8"),this.end(e),this}redirect(e,r=302){return this.statusCode=r,this.setHeader("Location",e),this.end(),this}initSSE(e){return _e(this,e)}sseSend(e){Oe(this,e);}sseJson(e){Ae(this,e);}sseEvent(e,r){$e(this,e,r);}sseComment(e){Pe(this,e);}sseId(e){qe(this,e);}sseRetry(e){Ie(this,e);}sseClose(e){je(this,e);}sseError(e,r,o=500,n){Le(this,e,r,o,n);}}}var ye=class{params={};headers={};body(){return ae.call(this)}},se=X(ye).prototype,we=class{statusCode=200;headersSent=false;setHeader(e,r){}end(e){}write(e){}},ct=Y(we).prototype;function ie(t){t.body=ae,t.json=se.json,t.form=se.form,t.getHeader=se.getHeader,t.createSSEClient=se.createSSEClient,t.cookies=new ne(Ne(t.headers));}function de(t){let e=t.indexOf("?");return e===-1?t:t.slice(0,e)}function le(t){let e=t.indexOf("?");return e===-1?{}:Object.fromEntries(new URLSearchParams(t.slice(e+1)).entries())}async function q(t,e,r){console.error("[lacis] Unhandled error:",r),H()&&await T("onError",t,e,{error:r});}function ue(t){let e=ct;t.status=e.status,t.send=e.send,t.json=e.json,t.html=e.html,t.redirect=e.redirect,t.initSSE=e.initSSE,t.sseSend=e.sseSend,t.sseJson=e.sseJson,t.sseEvent=e.sseEvent,t.sseComment=e.sseComment,t.sseId=e.sseId,t.sseRetry=e.sseRetry,t.sseClose=e.sseClose,t.sseError=e.sseError;let r=new oe;t.cookies=r;let o=t.end.bind(t);t.end=function(...n){return Fe(r,this),o(...n)};}var ce=null;function ht(t={}){let e=new EventEmitter,r={sampleInterval:t.sampleInterval||5e3,reportInterval:t.reportInterval||6e4,resetInterval:t.resetInterval||1440*60*1e3,enableHistogram:t.enableHistogram!==void 0?t.enableHistogram:true,logToConsole:t.logToConsole!==void 0?t.logToConsole:true,thresholds:{cpu:t.thresholds?.cpu||80,memory:t.thresholds?.memory||80,responseTime:t.thresholds?.responseTime||1e3,errorRate:t.thresholds?.errorRate||5}},o=Date.now(),n={requestCount:0,activeRequests:0,errorCount:0,responseTimes:[],responseTimesBucket:Array(100).fill(0),statusCodes:{},lastReport:o,lastReset:o},s=c(),i=[],l=process.cpuUsage(),a={cpu:false,memory:false,responseTime:false,errorRate:false};function c(){return {timestamp:Date.now(),uptime:process.uptime(),requestCount:0,activeRequests:0,errorCount:0,responseTimes:{min:0,max:0,avg:0,count:0,sum:0},statusCodes:{},memory:process.memoryUsage(),cpu:{usage:0,system:0,user:0},systemLoad:_.loadavg(),systemMemory:{total:_.totalmem(),free:_.freemem(),used:_.totalmem()-_.freemem()}}}function u(){let f=n.responseTimes;if(f.length===0)return {min:0,max:0,avg:0,count:0,sum:0};let m=1/0,S=-1/0,M=0;for(let E of f)m=Math.min(m,E),S=Math.max(S,E),M+=E;let O=M/f.length,C={min:m,max:S,avg:O,count:f.length,sum:M};if(r.enableHistogram){let E=[...f].sort((x,K)=>x-K);C.p50=E[Math.floor(E.length*.5)],C.p90=E[Math.floor(E.length*.9)],C.p99=E[Math.floor(E.length*.99)];}return C}function p(){let f=Date.now(),m=process.uptime(),S=process.memoryUsage(),M=process.cpuUsage(l);l=process.cpuUsage();let C=(M.user+M.system)/1e3/r.sampleInterval*100;s={timestamp:f,uptime:m,requestCount:n.requestCount,activeRequests:n.activeRequests,errorCount:n.errorCount,responseTimes:u(),statusCodes:{...n.statusCodes},memory:S,cpu:{usage:C,system:M.system,user:M.user},systemLoad:_.loadavg(),systemMemory:{total:_.totalmem(),free:_.freemem(),used:_.totalmem()-_.freemem()}},i.push({...s}),i.length>100&&i.shift(),y(),J.isWorker&&process.send&&process.send({type:"metrics",metrics:s});}function g(){if(!r.logToConsole||J.isPrimary===false)return;let f={rss:(s.memory.rss/1024/1024).toFixed(2),heapTotal:(s.memory.heapTotal/1024/1024).toFixed(2),heapUsed:(s.memory.heapUsed/1024/1024).toFixed(2)},m=Date.now()-n.lastReport,S=(s.requestCount/(m/1e3)).toFixed(2),M=s.requestCount?(s.errorCount/s.requestCount*100).toFixed(2):"0.00";R(`
25
- \u{1F4CA} Server Performance Metrics \u{1F4CA}`),R(`Uptime: ${h(s.uptime)}`),R(`Load: ${s.systemLoad[0].toFixed(2)}, ${s.systemLoad[1].toFixed(2)}, ${s.systemLoad[2].toFixed(2)}`),R(`Requests: ${s.requestCount} total, ${S} req/sec`),R(`Active Requests: ${s.activeRequests}`),R(`Errors: ${s.errorCount} (${M}%)`),s.responseTimes.count>0&&(R(`Response Times: avg ${s.responseTimes.avg.toFixed(2)}ms, min ${s.responseTimes.min}ms, max ${s.responseTimes.max}ms`),s.responseTimes.p50&&R(`Response Time Percentiles: p50 ${s.responseTimes.p50}ms, p90 ${s.responseTimes.p90}ms, p99 ${s.responseTimes.p99}ms`)),R(`Memory: ${f.rss}MB (RSS), ${f.heapUsed}MB / ${f.heapTotal}MB (Heap)`),R(`CPU Usage: ${s.cpu.usage.toFixed(2)}%`),Object.keys(s.statusCodes).length>0&&(R("Status Codes:"),Object.entries(s.statusCodes).sort(([O],[C])=>parseInt(O)-parseInt(C)).forEach(([O,C])=>{R(` ${O}: ${C}`);})),R(""),n.lastReport=Date.now();}function w(){let f=Date.now();n.requestCount=0,n.activeRequests=0,n.errorCount=0,n.responseTimes=[],n.responseTimesBucket=Array(100).fill(0),n.statusCodes={},n.lastReport=f,n.lastReset=f,R("\u{1F504} Performance metrics have been reset");}function y(){let f=s.memory.heapUsed/s.memory.heapTotal*100,m=s.requestCount?s.errorCount/s.requestCount*100:0,S=s.responseTimes.avg;s.cpu.usage>r.thresholds.cpu&&!a.cpu?(a.cpu=true,e.emit("alarm","cpu",`High CPU usage: ${s.cpu.usage.toFixed(2)}%`)):s.cpu.usage<=r.thresholds.cpu&&a.cpu&&(a.cpu=false,e.emit("alarm-clear","cpu",`CPU usage returned to normal: ${s.cpu.usage.toFixed(2)}%`)),f>r.thresholds.memory&&!a.memory?(a.memory=true,e.emit("alarm","memory",`High memory usage: ${f.toFixed(2)}%`)):f<=r.thresholds.memory&&a.memory&&(a.memory=false,e.emit("alarm-clear","memory",`Memory usage returned to normal: ${f.toFixed(2)}%`)),S>r.thresholds.responseTime&&!a.responseTime?(a.responseTime=true,e.emit("alarm","responseTime",`High response time: ${S.toFixed(2)}ms`)):S<=r.thresholds.responseTime&&a.responseTime&&(a.responseTime=false,e.emit("alarm-clear","responseTime",`Response time returned to normal: ${S.toFixed(2)}ms`)),m>r.thresholds.errorRate&&!a.errorRate?(a.errorRate=true,e.emit("alarm","errorRate",`High error rate: ${m.toFixed(2)}%`)):m<=r.thresholds.errorRate&&a.errorRate&&(a.errorRate=false,e.emit("alarm-clear","errorRate",`Error rate returned to normal: ${m.toFixed(2)}%`));}function h(f){let m=Math.floor(f/86400),S=Math.floor(f%86400/3600),M=Math.floor(f%3600/60),O=Math.floor(f%60),C=[];return m>0&&C.push(`${m}d`),S>0&&C.push(`${S}h`),M>0&&C.push(`${M}m`),(O>0||C.length===0)&&C.push(`${O}s`),C.join(" ")}function d(){let f=setInterval(p,r.sampleInterval),m=setInterval(g,r.reportInterval),S=setInterval(w,r.resetInterval);return {stop:()=>{clearInterval(f),clearInterval(m),clearInterval(S);}}}function v(){let f=Date.now();return n.activeRequests++,{end:(m,S=false)=>{let M=Date.now()-f;return n.activeRequests--,n.requestCount++,n.responseTimes.push(M),n.responseTimes.length>1e3&&n.responseTimes.shift(),n.statusCodes[m]=(n.statusCodes[m]||0)+1,(S||m>=500)&&n.errorCount++,M}}}function b(){return !Object.values(a).some(f=>f)}function $(){let f=s.memory.heapUsed/s.memory.heapTotal*100,m=s.requestCount?s.errorCount/s.requestCount*100:0;return {status:b()?"healthy":"unhealthy",uptime:s.uptime,responseTimes:{avg:s.responseTimes.avg,p90:s.responseTimes.p90||null,p99:s.responseTimes.p99||null},memory:{usedMB:Math.round(s.memory.heapUsed/1024/1024),totalMB:Math.round(s.memory.heapTotal/1024/1024),percent:f.toFixed(2)},cpu:s.cpu.usage.toFixed(2),requests:{total:s.requestCount,active:s.activeRequests,errors:s.errorCount,errorRate:m.toFixed(2)},alerts:Object.entries(a).filter(([S,M])=>M).map(([S])=>S)}}let k=d();return {trackRequest:v,getMetrics:()=>({...s}),getMetricsHistory:()=>[...i],getHealthMetrics:$,isHealthy:b,stop:k.stop,on:e.on.bind(e),once:e.once.bind(e),off:e.off.bind(e)}}function De(t){if(!ce){let e=ht(t),r=e.stop;e.stop=()=>{r(),ce=null;},ce=e;}return ce}function Re(t={}){let e=t.reportInterval??5e3,r={workers:new Map,workerIds:[]};function o(){if(!J.isWorker)return;let c=process.cpuUsage(),u=()=>{if(!process.send)return;let p=process.memoryUsage(),g=process.cpuUsage(c);c=process.cpuUsage();let w=(g.user+g.system)/(e*1e3),y={type:"stats",stats:{pid:process.pid,load:w,lastUsed:Date.now(),memoryUsage:p}};process.send(y);};u(),setInterval(u,e).unref();}function n(){let c=J.fork();r.workers.set(c.id,{pid:c.process.pid,load:0,lastUsed:Date.now(),memoryUsage:{rss:0,heapTotal:0,heapUsed:0,external:0,arrayBuffers:0}}),r.workerIds.push(c.id),R(`Worker ${c.process.pid} started`);}let s=false,i=[];function l(c=_.cpus().length){if(!J.isPrimary)return o();R(`\u{1F9F5} Launching ${c} workers...`);for(let u=0;u<c;u++)n();J.on("message",(u,p)=>{if(p.type==="stats"&&"stats"in p){let g=r.workers.get(u.id);g&&Object.assign(g,p.stats);}}),J.on("exit",(u,p,g)=>{if(r.workers.delete(u.id),r.workerIds=r.workerIds.filter(w=>w!==u.id),s){i.forEach(w=>w());return}R(`Worker ${u.process.pid} died (${g||p}). Restarting...`),setTimeout(()=>n(),1e3);});}function a(c){s=true;let u=Object.keys(J.workers??{});if(u.length===0){c?.();return}let p=u.length;i.push(()=>{p--,p===0&&c?.();});for(let g of u)J.workers?.[g]?.kill();}return {start:l,shutdown:a,getWorkerStats:()=>Array.from(r.workers.entries()),getActiveWorkerCount:()=>r.workerIds.length}}var Ce=class extends be.IncomingMessage{params={};body=ae},Se=class extends X(Ce){},ve=class extends Y(be.ServerResponse){},We={name:"node",createHandler:t=>{if(typeof t!="string")throw new Error("nodeAdapter requires a routesDir string, not a ServerlessConfig.");let e=t;return async(r={})=>{let{port:o=3e3,defaultHeaders:n,isDev:s,cluster:i,monitoring:l={enabled:false}}=r,a=null;s&&l.enabled&&(a=De({sampleInterval:l.sampleInterval||5e3,reportInterval:l.reportInterval||6e4,thresholds:l.thresholds,logToConsole:true}),J.isPrimary&&(R("\u{1F4CA} Development performance monitoring enabled"),a.on("alarm",(u,p)=>{R(`\u26A0\uFE0F ALERT: ${p}`);}),a.on("alarm-clear",(u,p)=>{R(`\u2705 RESOLVED: ${p}`);})));let c=n?Object.entries(n):[];if(i?.enabled&&J.isPrimary){let u=i.workers??___default.cpus().length,p=r.httpsOptions?"https":"http";if(R(`\u{1F9F5} Starting server with ${u} workers`),J.schedulingPolicy!==void 0)try{J.schedulingPolicy=J.SCHED_RR;}catch{}let g=Re();return g.start(u),R(`\u{1F680} Server running at ${p}://localhost:${o}/`+(s?" (dev)":"")),s&&a&&R(`\u{1F4CA} Performance monitoring available at http://localhost:${o}/health`),{close:w=>{a&&a.stop(),g.shutdown(w);}}}if(J.isWorker||!i?.enabled){r.routes||await re(e),U(r.cors),F(r.middleware),j(r.hooks);let u=async(h,d,v)=>{try{if(s&&a&&h.url==="/health"){d.setHeader("Content-Type","application/json"),d.end(JSON.stringify(a.getHealthMetrics())),v?.end(200);return}let b=h.url||"/",$=de(b);if(h.query=le(b),H()&&(await T("beforeRequest",h,d)===!1||d.headersSent)){v?.end(d.statusCode||204);return}let k=D($,h.method||"GET");if(!k){if(L()&&(await N(h,d),d.headersSent)){v?.end(d.statusCode);return}d.status(404).json({error:"Route not found"}),v?.end(404);return}if("error"in k){let f=k.status||500;d.status(f).json({error:k.error}),v?.end(f,!0);return}h.params=k.params,await k.handler(h,d),H()&&await T("afterRequest",h,d),d.headersSent||d.end(),v?.end(d.statusCode||200);}catch(b){await q(h,d,b),d.headersSent||d.status(500).json({error:"Internal Server Error"}),v?.end(d.statusCode||500,true);}},p=(h,d)=>{let v=s&&a?a.trackRequest():null;if(c.length>0)for(let b=0;b<c.length;b++)d.setHeader(c[b][0],c[b][1]);u(h,d,v).catch(b=>{s&&console.error("Fatal error:",b),d.headersSent||(d.statusCode=500,d.end("Server Error")),v?.end(500,true);});},g={IncomingMessage:Se,ServerResponse:ve},w=r.httpsOptions?lt.createServer({...g,...r.httpsOptions},p):be.createServer(g,p);w.on("clientError",(h,d)=>{d.destroyed||d.destroy();});let y=r.httpsOptions?"https":"http";return J.isWorker&&Re().start(),w.listen(o,()=>{i?.enabled?s&&R(`Worker ${process.pid} is listening on port ${o}`):(R(`\u{1F680} Server running at ${y}://localhost:${o}/`+(s?" (dev)":"")),s&&a&&R(`\u{1F4CA} Performance monitoring available at http://localhost:${o}/health`));}),w}return null}}};var Je={name:"vercel",createHandler:t=>{if(typeof t=="string")throw new Error("vercelAdapter.createHandler() requires a ServerlessConfig object, not a routesDir string. Import your routes manifest and pass { routes } instead.");let e=null,r=()=>e||(e=(async()=>{Q(t.routes),U(t.cors),F(t.middleware),V(t.middlewares),j(t.hooks);})(),e);return async(o,n)=>{await r(),ie(o),ue(n);let s=o,i=n;try{if(H()&&(await T("beforeRequest",s,i)===!1||i.headersSent))return;let l=D(de(s.url??"/"),s.method??"GET");if(!l){if(L()&&(await N(s,i),i.headersSent))return;i.status(404).json({error:"Route not found"});return}if(te(l)){i.status(l.status??500).json({error:l.error});return}s.params=l.params,await l.handler(s,i),H()&&await T("afterRequest",s,i);}catch(l){await q(s,i,l),i.headersSent||i.status(500).json({error:"Internal server error"});}}}};function fe(t,e,r,o){let n=Object.keys(r).length>0;return {statusCode:t,headers:e,...n?{multiValueHeaders:r}:{},body:o}}var ze={name:"netlify",createHandler:t=>{if(typeof t=="string")throw new Error("netlifyAdapter.createHandler() requires a ServerlessConfig object. Run `lacis build` to generate routes/_manifest.ts and pass { routes } instead.");let e=null,r=()=>e||(e=(async()=>{Q(t.routes),U(t.cors),F(t.middleware),V(t.middlewares),j(t.hooks);})(),e);return async(o,n)=>{await r();let s=o.queryStringParameters?"?"+new URLSearchParams(o.queryStringParameters).toString():"",i=o.path+s,l=new IncomingMessage(new Socket);if(l.url=i,l.method=o.httpMethod,l.headers=o.headers,o.body){let h=o.isBase64Encoded?"base64":"utf-8";l.push(Buffer.from(o.body,h));}l.push(null);let a="",c={},u={},p=false,g=new ServerResponse(l);g.writeHead=function(h,d){return g.statusCode=h,d&&(c={...c,...d}),this},g.setHeader=function(h,d){let v=h.toLowerCase();return Array.isArray(d)?(u[v]=d.map(String),c[v]=String(d[0])):c[v]=String(d),this},g.getHeader=function(h){return c[h.toLowerCase()]},g.end=function(h){return p=true,h!==void 0&&(a=typeof h=="string"?h:h.toString()),this},Object.defineProperty(g,"headersSent",{get:()=>p}),ie(l),ue(g);let w=l,y=g;w.query=o.queryStringParameters??{};try{if(H()&&(await T("beforeRequest",w,y)===!1||p))return fe(y.statusCode,c,u,a);let h=D(o.path,o.httpMethod);return h?te(h)?{statusCode:h.status??500,body:JSON.stringify({error:h.error})}:(w.params=h.params,await h.handler(w,y),H()&&await T("afterRequest",w,y),fe(y.statusCode,c,u,a)):L()&&(await N(w,y),p)?fe(y.statusCode,c,u,a):{statusCode:404,body:JSON.stringify({error:"Route not found"})}}catch(h){return await q(w,y,h),p?fe(y.statusCode,c,u,a):{statusCode:500,body:JSON.stringify({error:"Internal server error"})}}}}};var Ct=10485760,St=new TextEncoder,Ee=class{params={};url;method;headers;socket={setTimeout:e=>{}};connection;_req;constructor(e,r,o,n){this._req=e,this.url=r+o,this.method=e.method,this.headers=e.headers,this.connection={remoteAddress:n};}setTimeout(e){}text(){return this._req.text()}body(){return this._req.arrayBuffer().then(e=>{if(e.byteLength>Ct)throw Object.assign(new Error("Payload Too Large"),{code:413});return Buffer.from(e)})}},Me=class extends X(Ee){json(){return this._req.json()}},ke=class{statusCode=200;headersSent=false;get finished(){return this.headersSent}get writableEnded(){return this.headersSent}_body=null;_headers=null;_sseReadable=null;_sseWriter=null;_sseWindowClosed=false;_listeners=null;on(e,r){return (e==="finish"||e==="close")&&(this._listeners||(this._listeners=[]),this._listeners.push(r)),this}once(e,r){return this.on(e,r)}emit(e){if((e==="finish"||e==="close")&&this._listeners)for(let r=0;r<this._listeners.length;r++)this._listeners[r]();return true}setHeader(e,r){if(this._headers||(this._headers=[]),Array.isArray(r))for(let o of r)this._headers.push(e,o);else this._headers.push(e,r);return this}getHeader(e){if(!this._headers)return;let r=e.toLowerCase();for(let o=0;o<this._headers.length;o+=2)if(this._headers[o].toLowerCase()===r)return this._headers[o+1]}removeHeader(e){if(!this._headers)return this;let r=e.toLowerCase();for(let o=0;o<this._headers.length;o+=2)if(this._headers[o].toLowerCase()===r){this._headers.splice(o,2);break}return this}hasHeader(e){if(!this._headers)return false;let r=e.toLowerCase();for(let o=0;o<this._headers.length;o+=2)if(this._headers[o].toLowerCase()===r)return true;return false}writeHead(e,r){if(this.statusCode=e,r)for(let[o,n]of Object.entries(r))this.setHeader(o,n);return this}write(e){return this._sseWriter?(this._sseWriter.write(St.encode(String(e))),true):(this._body=(this._body??"")+e,true)}end(e){if(e!==void 0&&this.write(e),this._sseWriter&&this._sseWriter.close(),this.headersSent=true,this._listeners)for(let r=0;r<this._listeners.length;r++)this._listeners[r]();return this}_initSseStream(){if(this._sseWindowClosed)throw new Error("[lacis/bun] initSSE() must be called synchronously before any `await` in your handler.");let{readable:e,writable:r}=new TransformStream;this._sseReadable=e,this._sseWriter=r.getWriter();}_closeSseWindow(){this._sseWindowClosed=true;}},xe=class extends Y(ke){initSSE(e){return this._initSseStream(),super.initSSE(e)}},Ve={name:"bun",createHandler:t=>{if(typeof t!="string")throw new Error("bunAdapter requires a routesDir string, not a ServerlessConfig.");let e=t;return async(r={})=>{let{isDev:o,port:n=3e3,defaultHeaders:s,cluster:i}=r,l=parseInt(process.env.LACIS_BUN_WORKER??"0"),a=l>0;if(i?.enabled&&!a){let p=i.workers??___default.cpus().length;R(`\u{1F9F5} Starting Bun server with ${p} workers (reusePort)`);let g=Array.from({length:p},()=>Bun.spawn(process.argv,{env:{...process.env,LACIS_BUN_WORKER:String(process.pid)},stdout:"ignore",stderr:"inherit"}));return R(`\u{1F680} Server running at http://localhost:${n}/`),{close:w=>{for(let y of g)y.kill();w?.();}}}if(a){let p=setInterval(()=>{try{process.kill(l,0);}catch{clearInterval(p),process.exit(0);}},2e3);p.unref();}R("\u{1F680} Bun high-performance mode enabled"),r.routes||await re(e),U(r.cors),F(r.middleware),j(r.hooks);let c=s?Object.entries(s):[],u=Bun.serve({port:n,reusePort:a,async fetch(p,g){let w=new URL(p.url),y=w.pathname,h=new Me(p,y,w.search,g?.requestIP(p)?.address??"");h.query=le(w.search);let d=new xe;try{for(let k=0;k<c.length;k++)d.setHeader(c[k][0],c[k][1]);if(H()&&(!await T("beforeRequest",h,d)||d.headersSent))return Z(d);let v=D(y,p.method);if(!v)return L()&&(await N(h,d),d.headersSent)?Z(d):new Response(JSON.stringify({error:"Route not found"}),{status:404,headers:{"Content-Type":"application/json"}});if("error"in v)return H()&&await T("onError",h,d),new Response(JSON.stringify({error:v.error}),{status:v.status||500,headers:{"Content-Type":"application/json"}});h.params=v.params;let b=null,$=(async()=>{await v.handler(h,d),H()&&await T("afterRequest",h,d),d.headersSent||d.end();})().catch(k=>{b=k,o&&console.error("Server error:",k),d._sseReadable&&!d.headersSent&&d.end();});return d._closeSseWindow(),d._sseReadable?Z(d,d._sseReadable):(await $,b&&(await q(h,d,b),!d.headersSent)?new Response(JSON.stringify({error:"Internal Server Error"}),{status:500,headers:{"Content-Type":"application/json"}}):Z(d))}catch(v){return await q(h,d,v),d.headersSent?Z(d):new Response(JSON.stringify({error:"Internal Server Error"}),{status:500,headers:{"Content-Type":"application/json"}})}}});return R(`\u{1F680} Server started on http://localhost:${n}${o?" (dev)":""}`),{close:()=>{u.stop();}}}}};function Z(t,e){let r=e??t._body;if(!t._headers)return new Response(r,{status:t.statusCode});let o=new Headers;for(let n=0;n<t._headers.length;n+=2){let s=t._headers[n],i=t._headers[n+1];s.toLowerCase()==="set-cookie"?o.append(s,i):o.set(s,i);}return new Response(r,{status:t.statusCode,headers:o})}var vt={node:We,vercel:Je,netlify:ze,bun:Ve};function Dr(t="node"){let e=vt[t];if(!e)throw new Error(`Platform "${t}" not supported`);return e}
26
- export{U as A,Te as B,_e as C,Oe as D,Ae as E,$e as F,Pe as G,qe as H,Ie as I,je as J,Le as K,We as L,Je as M,ze as N,Ve as O,Dr as P,pe as a,Ke as b,Qe as c,Xe as d,H as e,T as f,me as g,Mt as h,V as i,j,L as k,N as l,kt as m,xt as n,F as o,R as p,W as q,te as r,Q as s,re as t,D as u,Nt as v,Ft as w,Ut as x,Dt as y,et as z};