lacis
Version:
Zero-dependency TypeScript web framework
2 lines • 7.87 kB
JavaScript
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(`
${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};