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