lacis
Version:
Zero-dependency TypeScript web framework
3 lines • 10.1 kB
JavaScript
import {i,b as b$1,f,g,c,G as G$1,e,l,t,a}from'./chunk-CNL4FQE2.js';export{j as HTTP_STATUS,m as createBadRequestError,r as createConflictError,d as createCorsMiddleware,o as createForbiddenError,w as createGatewayTimeoutError,k as createHttpError,u as createInternalServerError,q as createMethodNotAllowedError,p as createNotFoundError,t as createRateLimitError,z as createSSEClient,v as createServiceUnavailableError,n as createUnauthorizedError,s as createValidationError,h as getRoutesDir,A as initSSE,y as isHttpError,x as normalizeError,l as sendError}from'./chunk-CNL4FQE2.js';import V from'cluster';var O={port:3e3,isDev:process.env.NODE_ENV==="development",timeout:3e4,cluster:{enabled:false,workers:void 0},platform:"node"};function ue(e={}){return {...O,...e}}async function C(e){let r=e?.["~standard"]?.vendor;if(!r)return null;try{if(r==="zod"){let t=await import('zod');if(typeof t.toJSONSchema=="function")return t.toJSONSchema(e);let n=await import('zod-to-json-schema');return (n.zodToJsonSchema??n.default)(e,{target:"openApi3"})}if(r==="valibot"){let t=await import('@valibot/to-json-schema');return (t.toJsonSchema??t.default)(e)}if(r==="arktype")return e.toJsonSchema()}catch{c(`[openapi] no converter found for "${r}" \u2014 install the matching json-schema package`);}return null}function ce(e){return e.replace(/:(\w+)\??/g,"{$1}")}var pe={200:"OK",201:"Created",204:"No Content",400:"Bad Request",401:"Unauthorized",403:"Forbidden",404:"Not Found",409:"Conflict",422:"Unprocessable Entity",500:"Internal Server Error"};function U(e){return pe[e]??"Response"}function B(e,r){let t=r.split("/").filter(Boolean),n=t.filter(i=>!i.startsWith("{")),s=t.filter(i=>i.startsWith("{")).map(i=>i.slice(1,-1)),o=e.toLowerCase()+n.map(i=>i.charAt(0).toUpperCase()+i.slice(1)).join(""),a=s.length>0?"By"+s.map(i=>i.charAt(0).toUpperCase()+i.slice(1)).join("And"):"";return o+a}function N(e,r,t,n){let s={name:e,in:r,required:t,schema:n};return n.description&&(s.description=n.description),n.example!==void 0&&(s.example=n.example),s}async function le(e,r,t){let n={operationId:t.meta?.operationId??B(e,r),responses:{200:{description:"Success"}}};t.meta?.summary&&(n.summary=t.meta.summary),t.meta?.description&&(n.description=t.meta.description),t.meta?.tags&&(n.tags=t.meta.tags),t.meta?.deprecated&&(n.deprecated=t.meta.deprecated);let s=[];if(t.params){let o=await C(t.params);if(o?.properties)for(let[a,i]of Object.entries(o.properties))s.push(N(a,"path",true,i));}if(t.query){let o=await C(t.query);if(o?.properties){let a=o.required??[];for(let[i,d]of Object.entries(o.properties))s.push(N(i,"query",a.includes(i),d));}}if(s.length>0&&(n.parameters=s),t.body){let o=await C(t.body);o&&(n.requestBody={required:true,content:{"application/json":{schema:o}}});}if(t.responses){let o={};for(let[a,i]of Object.entries(t.responses)){let d=await C(i);o[a]=d?{description:U(Number(a)),content:{"application/json":{schema:d}}}:{description:U(Number(a))};}n.responses=o;}return n}async function _(e$1){let r=e.getRoutes(),t={};for(let{method:s,path:o,handler:a}of r){let i=a._defineHandler,d=ce(o);t[d]||(t[d]={}),t[d][s.toLowerCase()]=i?await le(s,d,i):{operationId:B(s,d),responses:{200:{description:"Success"}}};}let n={openapi:"3.1.0",info:e$1.info,paths:t};return e$1.servers?.length&&(n.servers=e$1.servers),n}var y=null,G=false,z=false,b=new Set;async function fe(e$1,r=O){let{platform:t="node"}=r,n=r.isDev&&V.isPrimary&&!process.env.LACIS_BUN_WORKER;try{r.routes?(i(),b$1(),f(r.routes)):await g(e$1);let s=null,o=null;r.openapi&&(s=await _(r.openapi),o=r.openapi.path??"/openapi.json"),n&&c(`\u{1F4C2} Routes loaded from: ${e$1}`);let i$1=G$1(t).createHandler(e$1),d;switch(t){case "node":d=await i$1(r),y=d,y.on("connection",u=>{b.add(u),u.on("close",()=>b.delete(u));});break;case "bun":d=i$1(r);break;case "vercel":case "netlify":d=i$1;break;default:throw new Error(`Unsupported platform: "${t}"`)}return s&&o&&(e.addRoute("GET",o,(u,c)=>c.json(s)),n&&c(`OpenAPI doc available at ${o}`)),me(),d}catch(s){throw n&&c("\u274C Failed to create server:",s),s}}function me(){if(!V.isPrimary||z)return;z=true;let e=async r=>{if(!G){if(G=true,c(`
${r} received, shutting down...`),await a(),y&&typeof y.close=="function"){for(let t of b)t.destroy();b.clear(),await new Promise(t=>{y.close(()=>t()),setTimeout(()=>t(),3e3);});}process.exit(0);}};process.on("SIGINT",()=>e("SIGINT")),process.on("SIGTERM",()=>e("SIGTERM")),process.on("SIGHUP",()=>e("SIGHUP"));}function w(e){let r=new Map;function t(s){let o=r.get(s);if(o){if(Date.now()>=o.expiresAt){r.delete(s);return}return r.delete(s),r.set(s,o),o}}function n(s,o){if(r.size>=e){let a=0;for(let i of r.keys())if(r.delete(i),++a>=50)break}r.set(s,o);}return {get:t,set:n}}function x(e,r){let t="",n=false,s=false,o=e.setHeader.bind(e);if(e.setHeader=(i,d)=>{let u=i.toLowerCase();return u==="content-type"&&(t=String(d)),u==="set-cookie"&&(n=true),o(i,d)},typeof e.writeHead=="function"){let i=e.writeHead.bind(e);e.writeHead=(d,u)=>{if(u)for(let[c,l]of Object.entries(u)){let p=c.toLowerCase();p==="content-type"&&(t=String(l)),p==="set-cookie"&&(n=true);}return i(d,u)};}let a=e.end.bind(e);e.end=i=>(s||(s=true,!n&&!t.includes("text/event-stream")&&r({body:i,status:e.statusCode,contentType:t})),a(i));}function v(e,r){r.contentType&&e.setHeader("Content-Type",r.contentType),e.statusCode=r.status,e.end(r.body);}function S(e){return (e.method??"GET")+":"+(e.url??"/")}function ye(e){let{ttl:r,methods:t=["GET","HEAD"],maxSize:n=500,keyGenerator:s=S,match:o,exclude:a,shouldCache:i=(p,m)=>m.statusCode>=200&&m.statusCode<300,store:d}=e,u=new Set(t.map(p=>p.toUpperCase())),c=a?Array.isArray(a)?a:[a]:[],l=d??w(n);return async(p,m)=>{if(!u.has((p.method??"GET").toUpperCase()))return;let h=p.url??"/",F=h.indexOf("?")===-1?h:h.slice(0,h.indexOf("?"));if(c.some(T=>F.startsWith(T))||o&&!o(p))return;let H=s(p),P=await l.get(H);if(P)return v(m,P),false;x(m,T=>{i(p,m)&&Promise.resolve(l.set(H,{...T,expiresAt:Date.now()+r*1e3})).catch(()=>{});});}}function Se(e,r){let{ttl:t,maxSize:n=500,key:s,store:o}=e,a=o??w(n);return async(i,d)=>{let u=s?s(i):S(i),c=await a.get(u);if(c){v(d,c);return}x(d,l=>{d.statusCode>=200&&d.statusCode<300&&Promise.resolve(a.set(u,{...l,expiresAt:Date.now()+t*1e3})).catch(()=>{});}),await r(i,d);}}async function A(e,r){let t=await e["~standard"].validate(r);return t.issues?{success:false,issues:t.issues}:{success:true,data:t.value}}function R(e){return Array.from(e).map(r=>({message:r.message,path:r.path?Array.from(r.path).map(t=>typeof t=="object"&&"key"in t?t.key:t):void 0}))}function Re(e,r){let t=n=>{let s=r[e.statusCode];if(!s)return;let o=s["~standard"].validate(n);if(o instanceof Promise){o.then(a=>{a.issues&&console.error(`[lacis] Response body for status ${e.statusCode} does not match its declared schema:`,R(a.issues));});return}if(o.issues)throw Object.assign(new Error(`Response body for status ${e.statusCode} does not match its declared schema`),{issues:R(o.issues),status:500})};if(typeof e.json=="function"){let n=e.json.bind(e);e.json=(s=>(t(s),n(s)));}if(typeof e.send=="function"){let n=e.send.bind(e);e.send=(s=>(t(s),n(s)));}}function je(e){let r=e.cache?w(e.cache.maxSize??500):null,t=async(n,s)=>{if(e.responses&&(s.raw=s,(e.validateResponses??process.env.NODE_ENV!=="production")&&Re(s,e.responses)),e.use)for(let o of e.use){let a=await o(n,s);if(a===false||s.headersSent)return;a&&typeof a=="object"&&Object.assign(n.locals??={},a);}if(r&&e.cache){let o=e.cache.key?e.cache.key(n):S(n),a=await r.get(o);if(a){v(s,a);return}x(s,i=>{s.statusCode>=200&&s.statusCode<300&&Promise.resolve(r.set(o,{...i,expiresAt:Date.now()+e.cache.ttl*1e3})).catch(()=>{});});}if(e.params){let o=await A(e.params,n.params??{});if(!o.success){s.status(400).json({error:"Validation failed",issues:R(o.issues)});return}n.params=o.data;}if(e.query){let o=await A(e.query,n.query??{});if(!o.success){s.status(400).json({error:"Validation failed",issues:R(o.issues)});return}n.query=o.data;}if(e.body){let o;try{o=await n.json();}catch{s.status(400).json({error:"Invalid JSON body"});return}let a=await A(e.body,o);if(!a.success){s.status(400).json({error:"Validation failed",issues:R(a.issues)});return}n.body=a.data;}await e.handler(n,s);};return t._defineHandler=e,t}function he(e){let r=new Map;return setInterval(()=>{let n=Date.now();for(let[s,o]of r)n>=o.resetAt&&r.delete(s);},e).unref(),{get:n=>r.get(n),set:(n,s)=>{r.set(n,s);},delete:n=>{r.delete(n);},increment:(n,s)=>{let o=Date.now(),a=r.get(n);return (!a||o>=a.resetAt)&&(a={count:0,resetAt:o+s}),a.count++,r.set(n,a),{count:a.count,resetAt:a.resetAt}}}}function ge(e){return r=>{if(e){let t=r.headers["x-forwarded-for"],n=typeof t=="string"?t.split(",")[0].trim():"";if(n)return n}return r.socket?.remoteAddress??"unknown"}}function Ne(e={}){let r=e.windowMs??6e4,t$1=e.max??100,n=e.message??"Too Many Requests",s=e.keyGenerator??ge(e.trustProxy??false),o=e.store??he(r);return async(a,i)=>{let d=s(a),u=Date.now(),c;o.increment?c=await o.increment(d,r):(c=await o.get(d)??{count:0,resetAt:u+r},u>=c.resetAt&&(c={count:0,resetAt:u+r}),c.count++,await o.set(d,c));let l$1=Math.max(0,t$1-c.count),p=Math.ceil(c.resetAt/1e3);if(i.setHeader("X-RateLimit-Limit",String(t$1)),i.setHeader("X-RateLimit-Remaining",String(l$1)),i.setHeader("X-RateLimit-Reset",String(p)),c.count>t$1)return i.setHeader("Retry-After",String(Math.ceil((c.resetAt-u)/1e3))),l(t(n),i),false}}function Ce(e){return {[Symbol.asyncIterator](){let r=e.getReader(),t=new TextDecoder,n="",s=false;return {async next(){for(;;){let o=n.indexOf(`
`);if(o!==-1){let d=n.slice(0,o).trim();if(n=n.slice(o+1),d)return {value:JSON.parse(d),done:false};continue}if(s){let d=n.trim();return n="",d?{value:JSON.parse(d),done:false}:{value:void 0,done:true}}let{value:a,done:i}=await r.read();i?s=true:n+=t.decode(a,{stream:true});}},return(){return r.cancel(),Promise.resolve({value:void 0,done:true})}}}}}export{he as createInMemoryRateLimitStore,Ne as createRateLimit,ye as createResponseCache,fe as createServer,S as defaultCacheKey,je as defineHandler,ue as getConfig,Ce as parseNDJSON,Se as withCache};