lacis 0.2.0 → 0.2.2
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/dist/adapters/index.js +1 -1
- package/dist/chunk-GWLL6W5O.js +26 -0
- package/dist/cli/index.js +7 -148
- package/dist/index.js +2 -457
- package/package.json +1 -1
- package/dist/chunk-NVNSYLVY.js +0 -2075
package/dist/adapters/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export{I as bunAdapter,J as getAdapter,H as netlifyAdapter,F as nodeAdapter,G as vercelAdapter}from'../chunk-GWLL6W5O.js';
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import F from'path';import Le from'fs/promises';import L from'cluster';import ue,{IncomingMessage,ServerResponse}from'http';import Ve from'https';import*as q from'os';import q__default from'os';import {EventEmitter}from'events';import {Socket}from'net';var $={beforeRequest:[],afterRequest:[],onError:[]},T=new Map;function re(t,e){return $[t].push(e),{remove:()=>{let r=$[t].indexOf(e);r!==-1&&$[t].splice(r,1);}}}function dt(t,e,r){let n=t==="/"?"/":t.replace(/\/+$/,"");return T.has(n)||T.set(n,{beforeRequest:[],afterRequest:[],onError:[]}),T.get(n)[e].push(r),{remove:()=>{if(T.has(n)){let s=T.get(n),i=s[e].indexOf(r);i!==-1&&s[e].splice(i,1);}}}}function je(t){let r=(t==="/"?"/":t.replace(/\/+$/,"")).split("/").filter(Boolean),n={beforeRequest:[...$.beforeRequest],afterRequest:[...$.afterRequest],onError:[...$.onError]},o="";if(T.has("/")){let s=T.get("/");n.beforeRequest.push(...s.beforeRequest),n.afterRequest.push(...s.afterRequest),n.onError.push(...s.onError);}for(let s of r)if(o+="/"+s,T.has(o)){let i=T.get(o);n.beforeRequest.push(...i.beforeRequest),n.afterRequest.push(...i.afterRequest),n.onError.push(...i.onError);}return n}function _(){return $.beforeRequest.length>0||$.afterRequest.length>0||$.onError.length>0?true:T.size>0}async function k(t,e,r,n){let o=e.url?.split("?")[0]||"/",s=je(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}async function ye(t){T.clear();async function e(r,n=""){try{let o=await Le.readdir(r,{withFileTypes:!0}),s=o.find(i=>i.name==="+middleware.ts"||i.name==="+middleware.js");if(s){let i=F.join(r,s.name);try{let a=await import(`${F.resolve(i)}?update=${Date.now()}`);if(T.has(n)||T.set(n,{beforeRequest:[],afterRequest:[],onError:[]}),a.beforeRequest){let d=Array.isArray(a.beforeRequest)?a.beforeRequest:[a.beforeRequest];T.get(n).beforeRequest.push(...d);}if(a.afterRequest){let d=Array.isArray(a.afterRequest)?a.afterRequest:[a.afterRequest];T.get(n).afterRequest.push(...d);}if(a.onError){let d=Array.isArray(a.onError)?a.onError:[a.onError];T.get(n).onError.push(...d);}}catch(l){console.error(`Error loading middleware for ${n}:`,l);}}for(let i of o)i.isDirectory()&&await e(F.join(r,i.name),`${n==="/"?"":n}/${i.name}`);}catch(o){console.error(`Error scanning directory ${r}:`,o);}}await e(t,"/");}function ut(){return T}function ct(){$.beforeRequest=[],$.afterRequest=[],$.onError=[],T.clear();}function O(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)re(e,o);}}}function Ue(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 De(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||!Ue(a,t.origin))return;let d=s&&!t.credentials;if(l.setHeader("Access-Control-Allow-Origin",d?"*":a),d||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 P(t){t&&re("beforeRequest",De(t));}function R(...t){L.isPrimary&&console.log(...t);}function We(t){let e=t.match(/^\[(\w+)(\??)]/);return e?{name:e[1],isParam:true,isOptional:e[2]==="?"}:{name:t,isParam:false,isOptional:false}}var Fe=1e3,ze=100,se=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=We(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 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(this.cachedRoutes.size>=Fe){let d=0;for(let u of this.cachedRoutes.keys())if(this.cachedRoutes.delete(u),++d>=ze)break}return this.cachedRoutes.set(o,a),a}traverse(e,r,n,o,s){if(s===r.length){if(e.isEndpoint){let d=e.handlers[n]??(n==="HEAD"?e.handlers.GET:void 0)??e.handlers[""];if(d)return {handler:d,params:{...o}};let u=Object.keys(e.handlers).filter(p=>p!=="");return {handler:null,params:{},allowedMethods:u}}let a=e.paramChild;if(a?.isOptional&&a.node.isEndpoint){let d=a.node.handlers[n]??(n==="HEAD"?a.node.handlers.GET:void 0)??a.node.handlers[""];if(d)return {handler:d,params:{...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:d}=e.paramChild;o[a]=i;let u=this.traverse(d,r,n,o,s+1);if(u)return u;delete o[a];}if(e.wildcardHandler){let a=e.wildcardHandler[n]??e.wildcardHandler[""],d=r.slice(s).join("/");if(a)return {handler:a,params:{...o,"*":d}};let u=Object.keys(e.wildcardHandler).filter(p=>p!=="");if(u.length>0)return {handler:null,params:{...o,"*":d},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 Le.readdir(o,{withFileTypes:!0}),l=i.find(a=>!a.isDirectory()&&(a.name==="index.ts"||a.name==="index.js"));if(l)try{let d=await import(`${F.resolve(F.join(o,l.name))}?update=${Date.now()}`),u=["GET","POST","PUT","DELETE","PATCH"],p="/"+s.join("/"),g=!1;for(let C of u)typeof d[C]=="function"&&(r.addRoute(C,p,d[C]),g=!0);!g&&typeof d.default=="function"&&r.addRoute("GET",p,d.default),r.verbose&&R(`Route loaded: ${p}`);}catch(a){console.error(`Error loading index in ${o}:`,a);}for(let a of i)if(a.isDirectory()){let d=a.name.match(/^\[(\w+)(\??)]/),u=d?`[${d[1]}${d[2]}]`:a.name;await n(F.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&&R(`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;}},U=new se;function z(t){return t&&typeof t=="object"&&"error"in t}function J(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"&&U.addRoute(o,n,s);}}async function V(t){return U.loadRoutes(t)}function I(t,e="GET"){let r=U.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 St(t){return F.resolve(process.cwd(),t||process.env.ROUTES_DIR||"routes")}function vt(){return U.getStats()}function bt(t){U.setVerbose(t);}function Et(){U.reset();}function Re(t,e={},r={},n){let o=false,s=0,i={},l=[],a=[],d=[],u=null;r.onMessage&&l.push(r.onMessage),r.onClose&&a.push(r.onClose),r.onError&&d.push(r.onError),r.onEvent&&Object.entries(r.onEvent).forEach(([c,m])=>{i[c]||(i[c]=[]),i[c].push(m);});let p=e.reconnectInterval||3e3,g=e.maxRetries||3,C=e.disableReconnect||false,y=e.body,h=e.contentType||(y?"application/json":void 0),f=e.method||(y?"POST":"GET");if(typeof t!="string"){let c=t;return o=true,c.on("data",m=>{b(m.toString());}),c.on("end",()=>{o=false;}),c.on("error",m=>{o=false,console.error("SSE Error:",m);}),v()}if(typeof t=="string")return new Promise((c,m)=>{x().then(()=>c(v())).catch(m);});function b(c){let m=c,S=m.split(`
|
|
2
|
+
|
|
3
|
+
`);m=S.pop()||"";for(let M of S){let A=M.split(`
|
|
4
|
+
`),w="",E="";for(let H of A)H.startsWith("event:")?E=H.slice(6).trim():H.startsWith("data:")&&(w=H.slice(5).trim());if(w)try{let H=JSON.parse(w);E&&i[E]?i[E].forEach(B=>B(H)):l.forEach(B=>B(H));}catch{E&&i[E]?i[E].forEach(H=>H(w)):l.forEach(H=>H(w));}}}function v(){return {onMessage(c){return l.push(c),this},onEvent(c,m){return i[c]||(i[c]=[]),i[c].push(m),this},onClose(c){return a.push(c),this},close(){u&&(u.destroy(),u=null),o=false,a.forEach(c=>c());}}}async function x(){if(!o){o=true;try{await D();}catch(c){!C&&s<g?(s++,setTimeout(()=>{x();},p)):d.forEach(m=>m(c));}}}async function D(){return new Promise((c,m)=>{let S=new URL(t);e.params&&(typeof e.params=="object"?Object.entries(e.params).forEach(([w,E])=>{S.searchParams.append(w,String(E));}):typeof e.params=="string"&&new URLSearchParams(e.params).forEach((E,H)=>{S.searchParams.append(H,E);}));let M={hostname:S.hostname,port:S.port||(S.protocol==="https:"?443:80),path:S.pathname+S.search,method:f,headers:{Accept:"text/event-stream",...n?.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:"?Ve:ue).request(M,w=>{if(w.statusCode!==200){o=false,m(new Error(`Server responded with status: ${w.statusCode}`));return}w.setEncoding("utf8"),w.on("data",b),w.on("end",()=>{o=false,u?.destroy(),u=null,a.forEach(E=>E()),c(null);}),w.on("close",()=>{o=false,u?.destroy(),u=null,a.forEach(E=>E()),c(null);}),w.on("error",E=>{o=false,u?.destroy(),u=null,a.forEach(H=>H()),m(E);});}),u.on("error",w=>{o=false,u?.destroy(),u=null,m(w);}),y&&(typeof y=="string"?u.write(y):u.write(JSON.stringify(y))),u.end();})}return v()}function we(t,e){let r=e?.headers?.["Cache-Control"]||"no-cache",n=e?.headers?.Connection||"keep-alive",o=e?.timeout||3e5;t.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":r,Connection:n,...e?.headers||{}});let s=setTimeout(()=>{t.end();},o);return t.on("close",()=>{clearTimeout(s);}),s}function Ce(t,e){return t.writableEnded?false:t.write(`data: ${e}
|
|
5
|
+
|
|
6
|
+
`)}function Se(t,e){return t.writableEnded?false:t.write(`data: ${JSON.stringify(e)}
|
|
7
|
+
|
|
8
|
+
`)}function ve(t,e,r){return t.writableEnded?false:(t.write(`event: ${e}
|
|
9
|
+
`),t.write(`data: ${JSON.stringify(r)}
|
|
10
|
+
|
|
11
|
+
`))}function be(t,e){return t.writableEnded?false:t.write(`: ${e}
|
|
12
|
+
|
|
13
|
+
`)}function Ee(t,e){return t.writableEnded?false:t.write(`id: ${e}
|
|
14
|
+
|
|
15
|
+
`)}function Me(t,e){return t.writableEnded?false:t.write(`retry: ${e}
|
|
16
|
+
|
|
17
|
+
`)}function xe(t,e="Connection closed"){t.write(`: ${e}
|
|
18
|
+
|
|
19
|
+
`),t.end();}function Te(t,e,r,n=500,o){t.writeHead(n,{"Content-Type":"text/event-stream"}),t.write(`event: ${e}
|
|
20
|
+
`);let s={message:r,code:n,details:o||null};t.write(`data: ${JSON.stringify(s)}
|
|
21
|
+
|
|
22
|
+
`),t.end();}var Ge=10485760,K=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()}}},X=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 _e(t){if(typeof t.get=="function")return t.get("cookie")??void 0;let e=t.cookie;return Array.isArray(e)?e.join("; "):e}function N(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 K(_e(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={},d=Buffer.from(`--${i}`),u=[],p=0,g;for(;(g=l.indexOf(d,p))!==-1;)u.push(l.subarray(p,g)),p=g+d.length;u.push(l.subarray(p));for(let C=1;C<u.length-1;C++){let y=u[C].subarray(2),h=y.indexOf(`\r
|
|
23
|
+
\r
|
|
24
|
+
`);if(h===-1)continue;let f=y.subarray(0,h).toString(),b=y.subarray(h+4,y.length-2),v=f.match(/Content-Disposition: form-data; name="([^"]+)"(?:; filename="([^"]+)")?/i);if(v)if(v[2]){let x=f.match(/Content-Type:\s*([^\r\n]+)/i);a[v[1]]={filename:v[2],mimetype:x?x[1].trim():"application/octet-stream",data:b,size:b.length};}else a[v[1]]=b.toString("utf-8");}e(a);}).catch(r);})}createSSEClient(e,r){return Re(this,e,r)}}}function Y(){let t=[],e=0,r=false;return new Promise((n,o)=>{this.on("data",s=>{if(e+=s.length,e>Ge){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 ke(t,e){let r=t.serialize();r.length>0&&!e.headersSent&&e.setHeader("Set-Cookie",r);}function W(t){return class extends t{_zresCookies;get cookies(){return this._zresCookies||(this._zresCookies=new X),this._zresCookies}end(e){return this._zresCookies&&ke(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}initSSE(e){return we(this,e)}sseSend(e){Ce(this,e);}sseJson(e){Se(this,e);}sseEvent(e,r){ve(this,e,r);}sseComment(e){be(this,e);}sseId(e){Ee(this,e);}sseRetry(e){Me(this,e);}sseClose(e){xe(this,e);}sseError(e,r,n=500,o){Te(this,e,r,n,o);}}}var ne=class{params={};headers={};body(){return Y.call(this)}},G=N(ne).prototype,oe=class{statusCode=200;headersSent=false;setHeader(e,r){}end(e){}write(e){}},Ke=W(oe).prototype;function Z(t){t.body=Y,t.json=G.json,t.form=G.form,t.getHeader=G.getHeader,t.createSSEClient=G.createSSEClient,t.cookies=new K(_e(t.headers));}function Q(t){let e=Ke;t.status=e.status,t.send=e.send,t.json=e.json,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 X;t.cookies=r;let n=t.end.bind(t);t.end=function(...o){return ke(r,this),n(...o)};}var ee=null;function Ye(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=d(),i=[],l=process.cpuUsage(),a={cpu:false,memory:false,responseTime:false,errorRate:false};function d(){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:q.loadavg(),systemMemory:{total:q.totalmem(),free:q.freemem(),used:q.totalmem()-q.freemem()}}}function u(){let c=o.responseTimes;if(c.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 c)m=Math.min(m,E),S=Math.max(S,E),M+=E;let A=M/c.length,w={min:m,max:S,avg:A,count:c.length,sum:M};if(r.enableHistogram){let E=[...c].sort((H,B)=>H-B);w.p50=E[Math.floor(E.length*.5)],w.p90=E[Math.floor(E.length*.9)],w.p99=E[Math.floor(E.length*.99)];}return w}function p(){let c=Date.now(),m=process.uptime(),S=process.memoryUsage(),M=process.cpuUsage(l);l=process.cpuUsage();let w=(M.user+M.system)/1e3/r.sampleInterval*100;s={timestamp:c,uptime:m,requestCount:o.requestCount,activeRequests:o.activeRequests,errorCount:o.errorCount,responseTimes:u(),statusCodes:{...o.statusCodes},memory:S,cpu:{usage:w,system:M.system,user:M.user},systemLoad:q.loadavg(),systemMemory:{total:q.totalmem(),free:q.freemem(),used:q.totalmem()-q.freemem()}},i.push({...s}),i.length>100&&i.shift(),y(),L.isWorker&&process.send&&process.send({type:"metrics",metrics:s});}function g(){if(!r.logToConsole||L.isPrimary===false)return;let c={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,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: ${c.rss}MB (RSS), ${c.heapUsed}MB / ${c.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(([A],[w])=>parseInt(A)-parseInt(w)).forEach(([A,w])=>{R(` ${A}: ${w}`);})),R(""),o.lastReport=Date.now();}function C(){let c=Date.now();o.requestCount=0,o.activeRequests=0,o.errorCount=0,o.responseTimes=[],o.responseTimesBucket=Array(100).fill(0),o.statusCodes={},o.lastReport=c,o.lastReset=c,R("\u{1F504} Performance metrics have been reset");}function y(){let c=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)}%`)),c>r.thresholds.memory&&!a.memory?(a.memory=true,e.emit("alarm","memory",`High memory usage: ${c.toFixed(2)}%`)):c<=r.thresholds.memory&&a.memory&&(a.memory=false,e.emit("alarm-clear","memory",`Memory usage returned to normal: ${c.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(c){let m=Math.floor(c/86400),S=Math.floor(c%86400/3600),M=Math.floor(c%3600/60),A=Math.floor(c%60),w=[];return m>0&&w.push(`${m}d`),S>0&&w.push(`${S}h`),M>0&&w.push(`${M}m`),(A>0||w.length===0)&&w.push(`${A}s`),w.join(" ")}function f(){let c=setInterval(p,r.sampleInterval),m=setInterval(g,r.reportInterval),S=setInterval(C,r.resetInterval);return {stop:()=>{clearInterval(c),clearInterval(m),clearInterval(S);}}}function b(){let c=Date.now();return o.activeRequests++,{end:(m,S=false)=>{let M=Date.now()-c;return o.activeRequests--,o.requestCount++,o.responseTimes.push(M),o.responseTimes.length>1e3&&o.responseTimes.shift(),o.statusCodes[m]=(o.statusCodes[m]||0)+1,(S||m>=500)&&o.errorCount++,M}}}function v(){return !Object.values(a).some(c=>c)}function x(){let c=s.memory.heapUsed/s.memory.heapTotal*100,m=s.requestCount?s.errorCount/s.requestCount*100:0;return {status:v()?"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:c.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 D=f();return {trackRequest:b,getMetrics:()=>({...s}),getMetricsHistory:()=>[...i],getHealthMetrics:x,isHealthy:v,stop:D.stop,on:e.on.bind(e),once:e.once.bind(e),off:e.off.bind(e)}}function qe(t){if(!ee){let e=Ye(t),r=e.stop;e.stop=()=>{r(),ee=null;},ee=e;}return ee}function ae(t={}){let e=t.reportInterval??5e3,r={workers:new Map,workerIds:[]};function n(){if(!L.isWorker)return;let d=process.cpuUsage(),u=()=>{if(!process.send)return;let p=process.memoryUsage(),g=process.cpuUsage(d);d=process.cpuUsage();let C=(g.user+g.system)/(e*1e3),y={type:"stats",stats:{pid:process.pid,load:C,lastUsed:Date.now(),memoryUsage:p}};process.send(y);};u(),setInterval(u,e).unref();}function o(){let d=L.fork();r.workers.set(d.id,{pid:d.process.pid,load:0,lastUsed:Date.now(),memoryUsage:{rss:0,heapTotal:0,heapUsed:0,external:0,arrayBuffers:0}}),r.workerIds.push(d.id),R(`Worker ${d.process.pid} started`);}let s=false,i=[];function l(d=q.cpus().length){if(!L.isPrimary)return n();R(`\u{1F9F5} Launching ${d} workers...`);for(let u=0;u<d;u++)o();L.on("message",(u,p)=>{if(p.type==="stats"&&"stats"in p){let g=r.workers.get(u.id);g&&Object.assign(g,p.stats);}}),L.on("exit",(u,p,g)=>{if(r.workers.delete(u.id),r.workerIds=r.workerIds.filter(C=>C!==u.id),s){i.forEach(C=>C());return}R(`Worker ${u.process.pid} died (${g||p}). Restarting...`),setTimeout(()=>o(),1e3);});}function a(d){s=true;let u=Object.keys(L.workers??{});if(u.length===0){d?.();return}let p=u.length;i.push(()=>{p--,p===0&&d?.();});for(let g of u)L.workers?.[g]?.kill();}return {start:l,shutdown:a,getWorkerStats:()=>Array.from(r.workers.entries()),getActiveWorkerCount:()=>r.workerIds.length}}var ie=class extends ue.IncomingMessage{params={};body=Y},le=class extends N(ie){},de=class extends W(ue.ServerResponse){},$e={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=qe({sampleInterval:l.sampleInterval||5e3,reportInterval:l.reportInterval||6e4,thresholds:l.thresholds,logToConsole:true}),L.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 d=o?Object.entries(o):[];if(i?.enabled&&L.isPrimary){let u=i.workers??q__default.cpus().length,p=r.httpsOptions?"https":"http";if(R(`\u{1F9F5} Starting server with ${u} workers`),L.schedulingPolicy!==void 0)try{L.schedulingPolicy=L.SCHED_RR;}catch{}let g=ae();return g.start(u),R(`\u{1F680} Server running at ${p}://localhost:${n}/`+(s?" (dev)":"")),s&&a&&R(`\u{1F4CA} Performance monitoring available at http://localhost:${n}/health`),{close:C=>{a&&a.stop(),g.shutdown(C);}}}if(L.isWorker||!i?.enabled){await V(e),P(r.cors),O(r.middleware);let u=async(h,f,b)=>{try{if(s&&a&&h.url==="/health"){f.setHeader("Content-Type","application/json"),f.end(JSON.stringify(a.getHealthMetrics())),b?.end(200);return}let v=h.url||"/",x=v.indexOf("?"),D=x===-1?v:v.slice(0,x);if(_()&&(await k("beforeRequest",h,f)===!1||f.headersSent)){b?.end(f.statusCode||204);return}let c=I(D,h.method||"GET");if(!c){_()&&await k("onError",h,f),f.status(404).json({error:"Route not found"}),b?.end(404);return}if("error"in c){let m=c.status||500;f.status(m).json({error:c.error}),b?.end(m,!0);return}h.params=c.params,await c.handler(h,f),_()&&await k("afterRequest",h,f),f.headersSent||f.end(),b?.end(f.statusCode||200);}catch(v){s&&console.error("Error:",v),f.headersSent||f.status(500).json({error:"Internal Server Error"}),_()&&await k("onError",h,f),b?.end(f.statusCode||500,true);}},p=(h,f)=>{let b=s&&a?a.trackRequest():null;if(d.length>0)for(let v=0;v<d.length;v++)f.setHeader(d[v][0],d[v][1]);u(h,f,b).catch(v=>{s&&console.error("Fatal error:",v),f.headersSent||(f.statusCode=500,f.end("Server Error")),b?.end(500,true);});},g={IncomingMessage:le,ServerResponse:de},C=r.httpsOptions?Ve.createServer({...g,...r.httpsOptions},p):ue.createServer(g,p);C.on("clientError",(h,f)=>{f.destroyed||f.destroy();});let y=r.httpsOptions?"https":"http";return L.isWorker&&ae().start(),C.listen(n,()=>{i?.enabled?s&&R(`Worker ${process.pid} is listening on port ${n}`):(R(`\u{1F680} Server running at ${y}://localhost:${n}/`+(s?" (dev)":"")),s&&a&&R(`\u{1F4CA} Performance monitoring available at http://localhost:${n}/health`));}),C}return null}}};var Oe={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()=>{J(t.routes),P(t.cors),O(t.middleware);})(),e);return async(n,o)=>{await r(),Z(n),Q(o);let s=n,i=o;try{if(_()&&(await k("beforeRequest",s,i)===!1||i.headersSent))return;let l=I(s.url??"/",s.method??"GET");if(!l){i.status(404).json({error:"Route not found"});return}if(z(l)){i.status(l.status??500).json({error:l.error});return}s.params=l.params,await l.handler(s,i),_()&&await k("afterRequest",s,i);}catch(l){console.error("[lacis/vercel] Handler error:",l),_()&&await k("onError",s,i,{error:l}),i.headersSent||i.status(500).json({error:"Internal server error"});}}}};function ce(t,e,r,n){let o=Object.keys(r).length>0;return {statusCode:t,headers:e,...o?{multiValueHeaders:r}:{},body:n}}var Pe={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()=>{J(t.routes),P(t.cors),O(t.middleware);})(),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);l.url=i,l.method=n.httpMethod,l.headers=n.headers,n.body&&l.push(Buffer.from(n.body,"utf-8")),l.push(null);let a="",d={},u={},p=false,g=new ServerResponse(l);g.writeHead=function(f,b){return g.statusCode=f,b&&(d={...d,...b}),this},g.setHeader=function(f,b){let v=f.toLowerCase();return Array.isArray(b)?(u[v]=b.map(String),d[v]=String(b[0])):d[v]=String(b),this},g.getHeader=function(f){return d[f.toLowerCase()]},g.end=function(f){return p=true,f!==void 0&&(a=typeof f=="string"?f:f.toString()),this},Object.defineProperty(g,"headersSent",{get:()=>p}),Z(l),Q(g);let C=l,y=g,h=I(n.path,n.httpMethod);if(!h)return {statusCode:404,body:JSON.stringify({error:"Route not found"})};if(z(h))return {statusCode:h.status??500,body:JSON.stringify({error:h.error})};try{return _()&&(await k("beforeRequest",C,y)===!1||p)||(C.params=h.params,await h.handler(C,y),_()&&await k("afterRequest",C,y)),ce(y.statusCode,d,u,a)}catch(f){return console.error("[lacis/netlify] Handler error:",f),_()&&await k("onError",C,y,{error:f}),p?ce(y.statusCode,d,u,a):{statusCode:500,body:JSON.stringify({error:"Internal server error"})}}}}};var nt=10485760,ot=new TextEncoder,pe=class{params={};url;method;headers;socket={setTimeout:e=>{}};connection;_req;constructor(e,r,n){this._req=e,this.url=r+n,this.method=e.method,this.headers=e.headers,this.connection={remoteAddress:e.headers.get("x-forwarded-for")??"127.0.0.1"};}setTimeout(e){}text(){return this._req.text()}body(){return this._req.arrayBuffer().then(e=>{if(e.byteLength>nt)throw Object.assign(new Error("Payload Too Large"),{code:413});return Buffer.from(e)})}},he=class extends N(pe){json(){return this._req.json()}},me=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(ot.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;}},ge=class extends W(me){initSSE(e){return this._initSseStream(),super.initSSE(e)}},Ie={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 p=i.workers??q__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:${o}/`),{close:C=>{for(let y of g)y.kill();C?.();}}}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"),await V(e),P(r.cors),O(r.middleware);let d=s?Object.entries(s):[],u=Bun.serve({port:o,reusePort:a,async fetch(p){let g=new URL(p.url),C=g.pathname,y=new he(p,C,g.search),h=new ge;try{for(let x=0;x<d.length;x++)h.setHeader(d[x][0],d[x][1]);if(_()&&(!await k("beforeRequest",y,h)||h.headersSent))return fe(h);let f=I(C,p.method);if(!f)return _()&&await k("onError",y,h),new Response(JSON.stringify({error:"Route not found"}),{status:404,headers:{"Content-Type":"application/json"}});if("error"in f)return _()&&await k("onError",y,h),new Response(JSON.stringify({error:f.error}),{status:f.status||500,headers:{"Content-Type":"application/json"}});y.params=f.params;let b=null,v=(async()=>{await f.handler(y,h),_()&&await k("afterRequest",y,h),h.headersSent||h.end();})().catch(x=>{b=x,n&&console.error("Server error:",x),h._sseReadable&&!h.headersSent&&h.end();});return h._closeSseWindow(),h._sseReadable?fe(h,h._sseReadable):(await v,b&&!h.headersSent?new Response(JSON.stringify({error:"Internal Server Error"}),{status:500,headers:{"Content-Type":"application/json"}}):fe(h))}catch(f){return n&&console.error("Server error:",f),new Response(JSON.stringify({error:"Internal Server Error"}),{status:500,headers:{"Content-Type":"application/json"}})}}});return R(`\u{1F680} Server started on http://localhost:${o}${n?" (dev)":""}`),{close:()=>{u.stop();}}}}};function fe(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 at={node:$e,vercel:Oe,netlify:Pe,bun:Ie};function br(t="node"){let e=at[t];if(!e)throw new Error(`Platform "${t}" not supported`);return e}
|
|
26
|
+
export{be as A,Ee as B,Me as C,xe as D,Te as E,$e as F,Oe as G,Pe as H,Ie as I,br as J,re as a,dt as b,je as c,_ as d,k as e,ye as f,ut as g,ct as h,O as i,R as j,U as k,z as l,J as m,V as n,I as o,St as p,vt as q,bt as r,Et as s,De as t,P as u,Re as v,we as w,Ce as x,Se as y,ve as z};
|
package/dist/cli/index.js
CHANGED
|
@@ -1,158 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
async function discoverRoutes(routesDir) {
|
|
8
|
-
const routes = [];
|
|
9
|
-
async function scan(dir, pathSegments = []) {
|
|
10
|
-
let entries;
|
|
11
|
-
try {
|
|
12
|
-
entries = await readdir(dir, { withFileTypes: true });
|
|
13
|
-
} catch {
|
|
14
|
-
return;
|
|
15
|
-
}
|
|
16
|
-
const indexFile = entries.find(
|
|
17
|
-
(e) => !e.isDirectory() && (e.name === "index.ts" || e.name === "index.js")
|
|
18
|
-
);
|
|
19
|
-
if (indexFile) {
|
|
20
|
-
const importPath = "./" + relative(routesDir, join(dir, indexFile.name)).replace(/\\/g, "/").replace(/\.ts$/, ".js");
|
|
21
|
-
const routePath = "/" + pathSegments.map((s) => s.replace(/^\[(\w+)\??\]$/, ":$1")).join("/");
|
|
22
|
-
routes.push({ importPath, routePath: routePath === "//" ? "/" : routePath });
|
|
23
|
-
}
|
|
24
|
-
for (const entry of entries) {
|
|
25
|
-
if (entry.isDirectory() && !entry.name.startsWith("+")) {
|
|
26
|
-
await scan(join(dir, entry.name), [...pathSegments, entry.name]);
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
await scan(routesDir);
|
|
31
|
-
return routes;
|
|
32
|
-
}
|
|
33
|
-
async function generateManifest(routesDir) {
|
|
34
|
-
const routes = await discoverRoutes(routesDir);
|
|
35
|
-
const imports = routes.map((r, i) => `import * as _route_${i} from '${r.importPath}'`).join("\n");
|
|
36
|
-
const entries = routes.map((r, i) => ` { path: '${r.routePath}', handlers: _route_${i} }`).join(",\n");
|
|
37
|
-
const content = [
|
|
38
|
-
"// AUTO-GENERATED by zeno build \u2014 do not edit",
|
|
39
|
-
imports,
|
|
40
|
-
"",
|
|
41
|
-
"export const routes = [",
|
|
42
|
-
entries,
|
|
43
|
-
"]",
|
|
44
|
-
""
|
|
45
|
-
].join("\n");
|
|
46
|
-
await writeFile(join(routesDir, "_manifest.ts"), content, "utf-8");
|
|
47
|
-
console.log(`[zeno] Generated manifest with ${routes.length} route(s)`);
|
|
48
|
-
}
|
|
49
|
-
async function watchRoutes(routesDir) {
|
|
50
|
-
await generateManifest(routesDir);
|
|
51
|
-
let debounceTimer = null;
|
|
52
|
-
watch(routesDir, { recursive: true }, (_event, filename) => {
|
|
53
|
-
if (!filename || filename === "_manifest.ts") return;
|
|
54
|
-
if (debounceTimer) clearTimeout(debounceTimer);
|
|
55
|
-
debounceTimer = setTimeout(async () => {
|
|
56
|
-
console.log(`[zeno] Route changed: ${filename}, regenerating manifest...`);
|
|
57
|
-
try {
|
|
58
|
-
await generateManifest(routesDir);
|
|
59
|
-
} catch (err) {
|
|
60
|
-
console.error("[zeno] Failed to regenerate manifest:", err);
|
|
61
|
-
}
|
|
62
|
-
}, 100);
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
function detectPlatform(cwd) {
|
|
66
|
-
if (existsSync(join(cwd, "netlify.toml"))) return "netlify";
|
|
67
|
-
if (existsSync(join(cwd, "vercel.json"))) return "vercel";
|
|
68
|
-
try {
|
|
69
|
-
const pkg = JSON.parse(readFileSync(join(cwd, "package.json"), "utf-8"));
|
|
70
|
-
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
71
|
-
if (allDeps["@netlify/functions"] || allDeps["netlify-cli"]) return "netlify";
|
|
72
|
-
if (allDeps["vercel"] || allDeps["@vercel/node"]) return "vercel";
|
|
73
|
-
} catch {
|
|
74
|
-
}
|
|
75
|
-
return "node";
|
|
76
|
-
}
|
|
77
|
-
async function dev(routesDir) {
|
|
78
|
-
const cwd = process.cwd();
|
|
79
|
-
if (process.env.VERCEL === "1") {
|
|
80
|
-
await generateManifest(routesDir);
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
if (process.env.NETLIFY === "true") {
|
|
84
|
-
await watchRoutes(routesDir);
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
const platform = detectPlatform(cwd);
|
|
88
|
-
console.log(`[zeno] Detected platform: ${platform}`);
|
|
89
|
-
await watchRoutes(routesDir);
|
|
90
|
-
if (platform === "node") {
|
|
91
|
-
console.log("[zeno] Node mode: watching routes for changes...");
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
const [cmd, args] = platform === "netlify" ? ["netlify", ["dev"]] : ["vercel", ["dev"]];
|
|
95
|
-
const child = spawn(cmd, args, { stdio: "inherit", shell: true, cwd });
|
|
96
|
-
child.on("error", (err) => {
|
|
97
|
-
if (err.code === "ENOENT") {
|
|
98
|
-
const install = platform === "netlify" ? "npm i -g netlify-cli" : "npm i -g vercel";
|
|
99
|
-
console.error(`[zeno] ${cmd} CLI not found. Install it with: ${install}`);
|
|
100
|
-
} else {
|
|
101
|
-
console.error(`[zeno] Failed to start ${cmd} dev:`, err.message);
|
|
102
|
-
}
|
|
103
|
-
});
|
|
104
|
-
const forward = (signal) => {
|
|
105
|
-
process.on(signal, () => child.kill(signal));
|
|
106
|
-
};
|
|
107
|
-
forward("SIGINT");
|
|
108
|
-
forward("SIGTERM");
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// src/cli/index.ts
|
|
112
|
-
function parseArgs(argv) {
|
|
113
|
-
const args = argv.slice(2);
|
|
114
|
-
const command = args[0] ?? "";
|
|
115
|
-
const routesFlagIndex = args.indexOf("--routes");
|
|
116
|
-
const routesDirArg = routesFlagIndex !== -1 ? args[routesFlagIndex + 1] : void 0;
|
|
117
|
-
const routesDir = routesDirArg ? resolve(process.cwd(), routesDirArg) : resolve(process.cwd(), "routes");
|
|
118
|
-
return { command, routesDir };
|
|
119
|
-
}
|
|
120
|
-
function printHelp() {
|
|
121
|
-
console.log(`
|
|
122
|
-
Usage: zeno <command> [options]
|
|
2
|
+
import {resolve,join,relative}from'path';import {writeFile,readdir}from'fs/promises';import {watch,existsSync,readFileSync}from'fs';import {spawn}from'child_process';async function $(e){let t=[];async function o(r,i=[]){let s;try{s=await readdir(r,{withFileTypes:!0});}catch{return}let a=s.find(n=>!n.isDirectory()&&(n.name==="index.ts"||n.name==="index.js"));if(a){let n="./"+relative(e,join(r,a.name)).replace(/\\/g,"/").replace(/\.ts$/,".js"),f="/"+i.map(h=>h.replace(/^\[(\w+)\??\]$/,":$1")).join("/");t.push({importPath:n,routePath:f==="//"?"/":f});}for(let n of s)n.isDirectory()&&!n.name.startsWith("+")&&await o(join(r,n.name),[...i,n.name]);}return await o(e),t}async function c(e){let t=await $(e),o=t.map((s,a)=>`import * as _route_${a} from '${s.importPath}'`).join(`
|
|
3
|
+
`),r=t.map((s,a)=>` { path: '${s.routePath}', handlers: _route_${a} }`).join(`,
|
|
4
|
+
`),i=["// AUTO-GENERATED by lacis build \u2014 do not edit",o,"","export const routes = [",r,"]",""].join(`
|
|
5
|
+
`);await writeFile(join(e,"_manifest.ts"),i,"utf-8"),console.log(`[lacis] Generated manifest with ${t.length} route(s)`);}async function l(e){await c(e);let t=null;watch(e,{recursive:true},(o,r)=>{!r||r==="_manifest.ts"||(t&&clearTimeout(t),t=setTimeout(async()=>{console.log(`[lacis] Route changed: ${r}, regenerating manifest...`);try{await c(e);}catch(i){console.error("[lacis] Failed to regenerate manifest:",i);}},100));});}function R(e){if(existsSync(join(e,"netlify.toml")))return "netlify";if(existsSync(join(e,"vercel.json")))return "vercel";try{let t=JSON.parse(readFileSync(join(e,"package.json"),"utf-8")),o={...t.dependencies,...t.devDependencies};if(o["@netlify/functions"]||o["netlify-cli"])return "netlify";if(o.vercel||o["@vercel/node"])return "vercel"}catch{}return "node"}async function p(e){let t=process.cwd();if(process.env.VERCEL==="1"){await c(e);return}if(process.env.NETLIFY==="true"){await l(e);return}let o=R(t);if(console.log(`[lacis] Detected platform: ${o}`),await l(e),o==="node"){console.log("[lacis] Node mode: watching routes for changes...");return}let[r,i]=o==="netlify"?["netlify",["dev"]]:["vercel",["dev"]],s=spawn(r,i,{stdio:"inherit",shell:true,cwd:t});s.on("error",n=>{n.code==="ENOENT"?console.error(`[lacis] ${r} CLI not found. Install it with: ${o==="netlify"?"npm i -g netlify-cli":"npm i -g vercel"}`):console.error(`[lacis] Failed to start ${r} dev:`,n.message);});let a=n=>{process.on(n,()=>s.kill(n));};a("SIGINT"),a("SIGTERM");}function j(e){let t=e.slice(2),o=t[0]??"",r=t.indexOf("--routes"),i=r!==-1?t[r+1]:void 0,s=i?resolve(process.cwd(),i):resolve(process.cwd(),"routes");return {command:o,routesDir:s}}function E(){console.log(`
|
|
6
|
+
Usage: lacis <command> [options]
|
|
123
7
|
|
|
124
8
|
Commands:
|
|
125
9
|
build Generate routes/_manifest.ts
|
|
126
10
|
watch Watch routes and regenerate manifest on changes
|
|
127
11
|
dev Auto-detect platform and start dev server
|
|
128
12
|
|
|
129
|
-
To scaffold a new project: npm create
|
|
13
|
+
To scaffold a new project: npm create lacis@latest
|
|
130
14
|
|
|
131
15
|
Options:
|
|
132
16
|
--routes <dir> Path to routes directory (default: ./routes)
|
|
133
|
-
`);
|
|
134
|
-
}
|
|
135
|
-
async function main() {
|
|
136
|
-
const { command, routesDir } = parseArgs(process.argv);
|
|
137
|
-
switch (command) {
|
|
138
|
-
case "build":
|
|
139
|
-
await generateManifest(routesDir);
|
|
140
|
-
break;
|
|
141
|
-
case "watch":
|
|
142
|
-
await watchRoutes(routesDir);
|
|
143
|
-
break;
|
|
144
|
-
case "dev":
|
|
145
|
-
await dev(routesDir);
|
|
146
|
-
break;
|
|
147
|
-
default:
|
|
148
|
-
printHelp();
|
|
149
|
-
if (command) {
|
|
150
|
-
console.error(`Unknown command: ${command}`);
|
|
151
|
-
process.exit(1);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
main().catch((err) => {
|
|
156
|
-
console.error("[zeno]", err);
|
|
157
|
-
process.exit(1);
|
|
158
|
-
});
|
|
17
|
+
`);}async function N(){let{command:e,routesDir:t}=j(process.argv);switch(e){case "build":await c(t);break;case "watch":await l(t);break;case "dev":await p(t);break;default:E(),e&&(console.error(`Unknown command: ${e}`),process.exit(1));}}N().catch(e=>{console.error("[lacis]",e),process.exit(1);});
|