fluxion-ts 0.9.5 → 0.9.6
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/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -1
- package/dist/index.d.mts +2 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require("node:fs");c=s(c);let l=require("node:path");l=s(l);let u=require("node:cluster");u=s(u);let d=require("node:os");d=s(d);let f=require("node:http");f=s(f);let p=require("node:https");p=s(p);let m=require("chokidar");m=s(m);let h=require("minimatch"),g=require("type-narrow");function _(e=new Date){return`${e.getFullYear()}.${String(e.getMonth()+1).padStart(2,`0`)}.${String(e.getDate()).padStart(2,`0`)} ${String(e.getHours()).padStart(2,`0`)}:${String(e.getMinutes()).padStart(2,`0`)}:${String(e.getSeconds()).padStart(2,`0`)}.${String(e.getMilliseconds()).padStart(3,`0`)}`}const v=JSON.stringify,y=Object.keys,b=process.env.FLUXION_COLORS!==`0`;let x;(function(e){e.reset=b?`\x1B[0m`:``,e.bold=b?`\x1B[1m`:``,e.dim=b?`\x1B[2m`:``,e.italic=b?`\x1B[3m`:``,e.underline=b?`\x1B[4m`:``,e.blink=b?`\x1B[5m`:``,e.inverse=b?`\x1B[7m`:``,e.black=b?`\x1B[30m`:``,e.red=b?`\x1B[31m`:``,e.green=b?`\x1B[32m`:``,e.yellow=b?`\x1B[33m`:``,e.blue=b?`\x1B[34m`:``,e.magenta=b?`\x1B[35m`:``,e.cyan=b?`\x1B[36m`:``,e.white=b?`\x1B[37m`:``,e.brightBlack=b?`\x1B[90m`:``,e.brightRed=b?`\x1B[91m`:``,e.brightGreen=b?`\x1B[92m`:``,e.brightYellow=b?`\x1B[93m`:``,e.brightBlue=b?`\x1B[94m`:``,e.brightMagenta=b?`\x1B[95m`:``,e.brightCyan=b?`\x1B[96m`:``,e.brightWhite=b?`\x1B[97m`:``,e.bgBlack=b?`\x1B[40m`:``,e.bgRed=b?`\x1B[41m`:``,e.bgGreen=b?`\x1B[42m`:``,e.bgYellow=b?`\x1B[43m`:``,e.bgBlue=b?`\x1B[44m`:``,e.bgMagenta=b?`\x1B[45m`:``,e.bgCyan=b?`\x1B[46m`:``,e.bgWhite=b?`\x1B[47m`:``,e.bgBrightBlack=b?`\x1B[100m`:``,e.bgBrightRed=b?`\x1B[101m`:``,e.bgBrightGreen=b?`\x1B[102m`:``,e.bgBrightYellow=b?`\x1B[103m`:``,e.bgBrightBlue=b?`\x1B[104m`:``,e.bgBrightMagenta=b?`\x1B[105m`:``,e.bgBrightCyan=b?`\x1B[106m`:``,e.bgBrightWhite=b?`\x1B[107m`:``,e.purple=b?`\x1B[38;2;225;16;248m`:``,e.orange=b?`\x1B[38;2;248;147;16m`:``,e.darkGreen=b?`\x1B[38;2;22;101;52m`:``,e.claude=b?`\x1B[38;2;217;119;87m`:``,e.deepseek=b?`\x1B[38;2;57;100;254m`:``,e.gpt=b?`\x1B[38;2;41;60;77m`:``})(x||={});const S=e=>{try{return v(e)}catch{return`[unserializable]`}},ee={INFO:`${x.cyan}INFO${x.reset}`,WARN:`${x.orange}WARN${x.reset}`,ERROR:`${x.red}ERROR${x.reset}`,SUCC:`${x.green}SUCC${x.reset}`,DEBUG:`${x.blue}DEBUG${x.reset}`,VERBOSE:`${x.purple}VERBOSE${x.reset}`},C=e=>{let{level:t,timestamp:n,event:r,message:i,...a}=e,o=`${x.darkGreen}[${n}]${x.reset}`,s=ee[t]??t,c=i??r,l=y(a).length>0?`${x.dim}${S(a)}${x.reset}`:``;console.log(`${o} ${s} ${c}${l}`)};function w(e){let t=e.options.logger;return t===void 0||t===`one-line`?C:t===`json-line`?e=>console.log(S(e)):t}function te(e){let t=w(e);return{write(e,n,r={}){let i={...r,timestamp:_(),level:e,event:n};try{t(i)}catch{}},info(e,t){this.write(`INFO`,e,t)},warn(e,t){this.write(`WARN`,e,t)},error(e,t){this.write(`ERROR`,e,t)},succ(e,t){this.write(`SUCC`,e,t)},debug(e,t){this.write(`DEBUG`,e,t)},verbose(e,t){this.write(`VERBOSE`,e,t)}}}function ne(e,t){let n=`[${t}]`;return{write(t,r,i){e.write(t,`${n} ${r}`,i)},info(t,r){e.info(`${n} ${t}`,r)},warn(t,r){e.warn(`${n} ${t}`,r)},error(t,r){e.error(`${n} ${t}`,r)},succ(t,r){e.succ(`${n} ${t}`,r)},debug(t,r){e.debug(`${n} ${t}`,r)},verbose(t,r){e.verbose(`${n} ${t}`,r)}}}const T=typeof Error.isError==`function`?e=>Error.isError(e)?e.message:String(e):e=>e?.message||String(e);function re(e={}){let t=e.restartWhen??{},n=t.healthzTimeout??3e4;return n!==1/0&&(!Number.isFinite(n)||n<1e4)&&$throw(`workerOptions.restartWhen.healthzTimeout must be a finite number >= 10000 (ms) or Infinity`),{maxWorkerCount:e.maxWorkerCount??4,restartWhen:{memoryUsageGreaterThan:t.memoryUsageGreaterThan??1/0,healthzTimeout:n,uptimeGreaterThan:t.uptimeGreaterThan??1/0}}}function E(e,t){if(Buffer.isBuffer(e))return e;if(typeof e==`string`){if(!e.startsWith(`-----BEGIN`)){let n=l.default.isAbsolute(e)?e:l.default.join(t,e);if(c.default.existsSync(n))return c.default.readFileSync(n)}return Buffer.from(e)}$throw(`Certificate content must be a string or Buffer`)}function D(e,t){if(!e)return;(typeof e!=`object`||!e||Array.isArray(e))&&$throw(`FluxionOptions.https must be an object`),typeof e.key!=`string`&&$throw(`FluxionOptions.https.key must be a string`),typeof e.cert!=`string`&&$throw(`FluxionOptions.https.cert must be a string`);let n={key:E(e.key,t),cert:E(e.cert,t)};return e.ca!==void 0&&(Array.isArray(e.ca)?n.ca=e.ca.map(e=>E(e,t)):n.ca=E(e.ca,t)),n}function O(e){(typeof e!=`object`||!e||Array.isArray(e))&&$throw(`FluxionOptions must be an object`);let{dir:t=l.default.isAbsolute(e.dir)?e.dir:l.default.join(process.cwd(),e.dir),host:n,port:r,handlerTimeoutMs:i=5e3,middlewareTimeoutMs:a=3e3,staticResourceTimeoutMs:o=10*6e5,metaPort:s=r+1,moduleDir:u=process.cwd(),workerOptions:d={},maxRequestBytes:f=8e6,reloadDelay:p=500,include:m=[`**/*`],apiInclude:h=[`**/*.ts`],exclude:g=[`**/node_modules/**`,`**/.git/**`,`**/dist/**`,`**/build/**`,`**/.vscode/**`,`**/.idea/**`,`**/*.log`,`**/.DS_Store`,`**/coverage/**`,`**/.nyc_output/**`,`**/*.tmp`,`**/*.temp`],https:_,nativeWatcher:v=!1}=e,y=e.logger??`one-line`;return y!==`one-line`&&y!==`json-line`&&typeof y!=`function`&&$throw(`Invalid logger option, Must be 'one-line', 'json-line' or a custom logger function`),typeof t!=`string`&&$throw(`FluxionOptions.dir must be a string`),typeof u!=`string`&&$throw(`FluxionOptions.moduleDir must be a string`),typeof n!=`string`&&$throw(`FluxionOptions.host must be a string`),(!Number.isSafeInteger(i)||i<=100)&&$throw(`FluxionOptions.handlerTimeoutMs must be an integer greater than 100`),(!Number.isSafeInteger(a)||a<=100)&&$throw(`FluxionOptions.middlewareTimeoutMs must be an integer greater than 100`),(typeof p!=`number`||p<=0||!Number.isSafeInteger(p))&&$throw(`FluxionOptions.reloadDelay must be a positive integer`),p<50&&$throw(`FluxionOptions.reloadDelay must be greater than or equal to 50`),(typeof r!=`number`||!Number.isSafeInteger(r))&&$throw(`FluxionOptions.port must be a positive integer`),(r<=1||r>65535)&&$throw(`FluxionOptions.port must be 1 ~ 65535`),(typeof s!=`number`||!Number.isSafeInteger(s))&&$throw(`FluxionOptions.metaPort must be a positive integer`),(s<=1||s>65535)&&$throw(`FluxionOptions.metaPort must be 1 ~ 65535`),s===r&&$throw(`FluxionOptions.metaPort must be different from FluxionOptions.port`),(typeof d!=`object`||!d||Array.isArray(d))&&$throw(`FluxionOptions.workerOptions must be an object`),(typeof f!=`number`||f<=0||!Number.isSafeInteger(f))&&$throw(`FluxionOptions.maxRequestBytes must be a positive integer`),c.default.existsSync(t)||c.default.mkdirSync(t,{recursive:!0}),{dir:t,host:n,port:r,handlerTimeoutMs:i,middlewareTimeoutMs:a,staticResourceTimeoutMs:o,reloadDelay:p,metaPort:s,moduleDir:u,workerOptions:re(d),maxRequestBytes:f,logger:y,include:m,apiInclude:h,exclude:g,nativeWatcher:v,https:D(_,u)}}const k=e=>[100].includes(e?.type),A=e=>[202,200,201,203].includes(e?.type),j=e=>process.send?.(e),M=(e,t)=>e.send(t),N=Symbol.for(`fluxion.router.StaticHandled`),P=Symbol.for(`fluxion.handlerTimeout`),F=Symbol.for(`fluxion.middlewareTimeout`),ie={".css":`text/css; charset=utf-8`,".html":`text/html; charset=utf-8`,".ico":`image/x-icon`,".js":`text/javascript; charset=utf-8`,".json":`application/json; charset=utf-8`,".map":`application/json; charset=utf-8`,".png":`image/png`,".jpg":`image/jpeg`,".jpeg":`image/jpeg`,".svg":`image/svg+xml`,".txt":`text/plain; charset=utf-8`,".webp":`image/webp`},ae=()=>{};function I(e,t,n=200){e.statusCode=n,e.setHeader(`Content-Type`,`application/json; charset=utf-8`),e.end(JSON.stringify(t))}function L(e,t,n=200){if(!e.writableEnded){if(e.headersSent){e.end();return}I(e,t,n)}}function R(e,t){let n=f.default.createServer((e,n)=>{let r=e.method??`GET`,i=`/`;try{i=new URL(e.url??`/`,`http://fluxion.local`).pathname}catch{I(n,{message:`Bad Request: invalid url`},400);return}if(r===`GET`&&i===`/_fluxion/healthz`){I(n,{ok:!0,role:`primary`,pid:process.pid,now:Date.now(),uptimeSeconds:Number(process.uptime().toFixed(3))});return}if(r===`GET`&&i===`/_fluxion/workers`){I(n,{ok:!0,now:Date.now(),workers:t()});return}I(n,{message:`Not Found`},404)});return n.on(`listening`,()=>{e.logger.info(`MetaApiStarted`,{pid:process.pid,host:e.options.host,port:e.options.metaPort,prefix:`/_fluxion`})}),n.on(`error`,t=>{e.logger.error(`MetaApiError`,{host:e.options.host,port:e.options.metaPort,error:T(t)}),process.exit(1)}),n.listen(e.options.metaPort,e.options.host),n}const z=e=>Number((e/1024/1024).toFixed(2)),B=6e4;function V(e){u.default.isPrimary||$throw(`createPrimary should only be called in primary process`);let{workerOptions:t}=e.options,n=t.restartWhen,r=Math.max(1,d.default.cpus().length),i=Math.max(1,Math.min(t.maxWorkerCount??Math.min(2,r),r));e.logger.info(`PrimaryStarted`,{pid:process.pid,workers:i,host:e.options.host,port:e.options.port,metaPort:e.options.metaPort});let a=new Map,o=new Map,s=e=>{let t=Date.now(),n=(o.get(e)??[]).filter(e=>t-e<B);return o.set(e,n),n.length},c=e=>{let t=Date.now(),n=(o.get(e)??[]).filter(e=>t-e<B);n.push(t),o.set(e,n)},l=e=>s(e)>=3;R(e,()=>({primaryPid:process.pid,host:e.options.host,port:e.options.port,metaPort:e.options.metaPort,uptimeSeconds:Number(process.uptime().toFixed(3)),workers:Array.from(a.entries()).map(([e,t])=>{let{instance:n}=t,r=t.lastStats;return{workerId:e,slot:t.slot,pid:t.pid??n.process.pid??null,state:t.state,restartReason:t.restartReason??null,createdAt:t.createdAt,readyAt:t.readyAt??null,connected:n.isConnected(),dead:n.isDead(),exitedAfterDisconnect:n.exitedAfterDisconnect,lastPongAt:t.lastPongAt??null,lastRttMs:t.lastRttMs??null,stats:r===void 0?null:{at:r.at,uptimeSeconds:r.uptimeSeconds,cpu:r.cpu,memory:{...r.memory,rssMb:z(r.memory.rss),heapTotalMb:z(r.memory.heapTotal),heapUsedMb:z(r.memory.heapUsed),externalMb:z(r.memory.external),arrayBuffersMb:z(r.memory.arrayBuffers)}}}})}));let f=(t,n)=>{for(let e of a.values())if(e.state===`restarting`)return;if(l(t.slot)){e.logger.warn(`WorkerRecycleSuppressed`,{slot:t.slot,pid:t.pid,reason:n,windowMs:B,max:3});return}c(t.slot),t.state=`restarting`,t.restartReason=n,e.logger.warn(`WorkerRecycling`,{slot:t.slot,pid:t.pid,reason:n}),t.instance.kill()},p=(e,t)=>{let r=z(t.memory.rss);if(r>n.memoryUsageGreaterThan){f(e,`memoryUsageGreaterThan: rss ${r}MB > ${n.memoryUsageGreaterThan}MB`);return}let i=t.uptimeSeconds*1e3;i>n.uptimeGreaterThan&&f(e,`uptimeGreaterThan: ${Math.round(i/1e3)}s > ${Math.round(n.uptimeGreaterThan/1e3)}s`)},m=e=>{for(let t of a.values()){if(t.state!==`ready`||t.lastPongAt===void 0)continue;let r=e-t.lastPongAt;r>n.healthzTimeout&&f(t,`healthzTimeout: no pong for ${Math.round(r/1e3)}s > ${Math.round(n.healthzTimeout/1e3)}s`)}},h=e=>{g(u.default.fork({WORKER_ID:String(e)}),e)},g=(t,n)=>{let r={state:`creating`,pid:t.process.pid,slot:n,createdAt:Date.now(),instance:t};a.set(t.id,r),t.on(`message`,i=>{if(A(i)){if(i.type===202){let e=Date.now()-i.sentAt;r.pid=i.pid,r.lastPongAt=Date.now(),r.lastRttMs=e;return}if(i.type===201){r.state=`ready`,r.pid=i.pid,r.readyAt=Date.now(),e.logger.info(`WorkerReady`,{workerId:t.id,slot:n,pid:i.pid});return}if(i.type===200){r.state=`created`,r.pid=i.pid,e.logger.info(`WorkerCreated`,{workerId:t.id,slot:n,pid:i.pid});return}i.type===203&&(r.pid=i.pid,r.lastStats=i.stats,r.state===`ready`&&p(r,i.stats))}}),t.on(`exit`,(n,r)=>{let i=a.get(t.id);a.delete(t.id);let o=i?.slot,s=i?.state===`restarting`,u=i?.restartReason??null;if(e.logger.warn(`WorkerExited`,{workerId:t.id,slot:o??null,pid:t.process.pid??`unknown`,code:n,signal:r??`none`,expected:s,reason:u}),o!==void 0){if(s){h(o);return}if(c(o),l(o)){e.logger.error(`WorkerRespawnSuppressed`,{slot:o,windowMs:B,max:3});return}h(o)}})};for(let e=0;e<i;e++)h(e+1);setInterval(()=>{let e=Date.now();for(let t of a.values())if(t.instance.isConnected())try{M(t.instance,{type:100,sentAt:e})}catch{}m(Date.now())},5e3).unref()}function H(e,...t){return new Promise((n,r)=>{try{let i=e(...t);i instanceof Promise?i.then(n).catch(r):n(i)}catch(e){r(e)}})}function U(e){let t=e.headersDistinct[`x-forwarded-for`];if(t){let e=t[0]?.split(`,`)[0]?.trim();if(e&&e.length>0)return e}let n=e.headersDistinct[`x-real-ip`]?.[0].trim();return n===void 0?e.socket.remoteAddress??`unknown`:n}function W(e){if(e===void 0)return!1;let t=e.toLowerCase();return t.startsWith(`text/`)||t.includes(`json`)||t.includes(`xml`)||t.includes(`x-www-form-urlencoded`)||t.includes(`javascript`)}function G(e){if(e!==void 0)try{return new URL(e,`http://fluxion.local`)}catch{return}}function K(e){let t={};for(let[n,r]of e.entries()){let e=t[n];if(e===void 0){t[n]=r;continue}if(Array.isArray(e)){e.push(r);continue}t[n]=[e,r]}return t}function q(e,t){let n=Error(`request body too large: ${e.toString()} bytes exceeds ${t.toString()} bytes`);return n.code=`REQUEST_BODY_TOO_LARGE`,n}function J(e){return Array.isArray(e)?e[0]:e}function Y(){return{exists:!1,bytes:0,truncated:!1}}function oe(e,t,n,r){return t===0?Y():W(n)?{exists:!0,value:e.toString(`utf8`),bytes:t,truncated:r}:{exists:!0,value:`<binary body: ${t} bytes>`,bytes:t,truncated:r}}async function se(e,t,n,r=8192){if(t===`GET`||t===`HEAD`||e.readableEnded)return{rawBody:void 0,preview:Y()};let i=J(e.headers[`content-length`]),a=i===void 0?NaN:Number.parseInt(i,10);if(Number.isFinite(a)&&a>n)throw q(a,n);return new Promise((t,i)=>{let a=[],o=[],s=0,c=0,l=!1,u=!1,d=()=>{e.off(`data`,p),e.off(`end`,m),e.off(`error`,h),e.off(`aborted`,g)},f=e=>{u||(u=!0,e())},p=t=>{let u=Buffer.isBuffer(t)?t:Buffer.from(t);if(s+=u.byteLength,s>n){d(),e.resume(),f(()=>{i(q(s,n))});return}if(a.push(u),c<r){let e=r-c,t=u.subarray(0,e);o.push(t),c+=t.length,t.length<u.length&&(l=!0)}else l=!0},m=()=>{d(),f(()=>{let n=a.length>0?Buffer.concat(a):void 0;t({rawBody:n,preview:oe(o.length>0?Buffer.concat(o):Buffer.alloc(0),n?.byteLength??0,J(e.headers[`content-type`]),l)})})},h=e=>{d(),f(()=>{i(e)})},g=()=>{d(),f(()=>{i(Error(`request aborted while reading body`))})};e.on(`data`,p),e.once(`end`,m),e.once(`error`,h),e.once(`aborted`,g)})}async function ce(e,t,n){let{rawBody:r,preview:i}=await se(e,t,n);if(r===void 0||r.byteLength===0)return{body:{},preview:i};let a=J(e.headers[`content-type`])?.toLowerCase()??``;if(a.includes(`json`)){let e=r.toString(`utf8`).trim();if(e.length===0)return{body:{},preview:i};try{let t=JSON.parse(e);return typeof t==`object`&&t&&!Array.isArray(t)?{body:t,preview:i}:{body:{value:t},preview:i}}catch{return{body:{raw:e},preview:i}}}return a.includes(`x-www-form-urlencoded`)?{body:K(new URLSearchParams(r.toString(`utf8`))),preview:i}:W(a)?{body:{raw:r.toString(`utf8`)},preview:i}:{body:{},preview:i}}function le(e){if(!e)return{};let t={},n=e.split(`;`);for(let e of n){let[n,...r]=e.split(`=`);if(!n)continue;let i=n.trim(),a=r.join(`=`).trim();t[i]=decodeURIComponent(a)}return t}const X=(e,t,n)=>Promise.race([e,new Promise(e=>setTimeout(()=>e(n),t))]);function ue(e){let t=async(t,n)=>{let r=t.method??`GET`,i=U(t),a=G(t.url);if(a===void 0){L(n,{message:`Bad Request: req.url is undefined`},400);return}let o={method:r,ip:i,url:a,query:K(a.searchParams),body:{},headers:t.headers,cookie:le(t.headers.cookie)},s={exists:!1,bytes:0,truncated:!1};e.logger.info(`Req`,{method:r,ip:i,path:a.pathname});let c=performance.now();n.once(`finish`,()=>{let t={workerId:process.env.WORKER_ID??`[primary]`,method:r,ip:i,path:a.pathname,status:n.statusCode,duration:(performance.now()-c).toFixed(4)};y(o.query).length>0&&(t.query=o.query),s.exists&&(t.body=s.value,t.bodyBytes=s.bytes,t.bodyTruncated=s.truncated),e.logger.info(`Res`,t)});try{if(o.url.pathname.startsWith(`/_fluxion/`)){L(n,{message:`Not Found`},404);return}let r=await ce(t,o.method,e.options.maxRequestBytes);o.body=r.body,s=r.preview;let i=await e.router.getModule(a);if(!i){L(n,{message:`Not Found`},404);return}if(t.method&&i.methods&&!i.methods.includes(t.method)){L(n,{message:`Method Not Allowed`},405);return}let c=i.type===0?i.handlerTimeoutMs??e.options.handlerTimeoutMs:e.options.staticResourceTimeoutMs;if(i.middlewares)for(let r=0;r<i.middlewares.length;r++){if(await X(H(i.middlewares[r],o,t,n),e.options.middlewareTimeoutMs,F)===F){e.logger.warn(`MiddlewareTimeout`,{method:o.method,ip:o.ip}),L(n,{message:`Internal Server Error`},500);return}if(n.writableEnded)return;if(n.headersSent){n.end();return}}let l=await X(H(i.handler,o,t,n),c,P);if(l===P){e.logger.warn(`HandlerTimeout`,{method:o.method,ip:o.ip}),L(n,{message:`Handler timed out`},500);return}l!==N&&L(n,l)}catch(t){e.logger.error(`RequestFailed`,{method:o.method,ip:o.ip,path:o.url.pathname,error:T(t)}),t.code===`REQUEST_BODY_TOO_LARGE`?L(n,{message:T(t)},413):L(n,{message:T(t)},500)}},n=e.options.https?p.default.createServer({key:e.options.https.key,cert:e.options.https.cert,ca:e.options.https.ca},t):f.default.createServer(t);return n.on(`close`,()=>{e.logger.info(`ServerClosed`,{host:e.options.host,port:e.options.port})}),n.listen(e.options.port,e.options.host,()=>{e.logger.info(`ServerStarted`,{pid:process.pid,protocol:e.options.https?`https`:`http`,host:e.options.host,port:e.options.port}),e.logger.info(`DynamicDirectory`,{directory:e.options.dir})}),n.on(`error`,t=>{e.logger.error(`ServerError`,{error:T(t)})}),n}const de=()=>{let e=process.cpuUsage(),t=Date.now();setInterval(()=>{let n=Date.now(),r=Math.max(1,(n-t)*1e3),i=process.cpuUsage(e),a=Number(((i.user+i.system)/r*100).toFixed(2));e=process.cpuUsage(),t=n;let o=process.memoryUsage();j({type:203,pid:process.pid,stats:{at:n,pid:process.pid,uptimeSeconds:Number(process.uptime().toFixed(3)),cpu:{userMicros:i.user,systemMicros:i.system,percent:a},memory:{rss:o.rss,heapTotal:o.heapTotal,heapUsed:o.heapUsed,external:o.external,arrayBuffers:o.arrayBuffers}}})},2e3).unref()};function fe(e){u.default.isPrimary&&$throw(`createWorker should only be called in worker process`),process.on(`message`,e=>{if(k(e)&&e.type===100){j({type:202,pid:process.pid,sentAt:e.sentAt,receivedAt:Date.now()});return}}),j({type:200,pid:process.pid}),de();try{ue(e),j({type:201,pid:process.pid})}catch(t){e.logger.error(`WorkerBootstrapFailed`,{pid:process.pid,error:T(t)}),process.exit(1)}}var Z=class{cx;timer=null;filesChanged=new Map;constructor(e){this.cx=e}async init(){let e=this.cx.options.dir;if(!c.default.existsSync(e))return this.cx.logger.warn(`Directory does not exist: ${e}`),this;let t=[],n=(e,r)=>{let i=c.default.readdirSync(e,{withFileTypes:!0});for(let a=0;a<i.length;a++){let o=i[a],s=l.default.join(e,o.name),c=l.default.join(r,o.name);if(o.isDirectory())n(s,c);else if(o.isFile()){let e=this.cx.router.register(s,c).catch(e=>{this.cx.logger.error(`Error registering file ${c}: ${e.message}`)});t.push(e)}}};return n(e,``),await Promise.all(t),this.cx.logger.info(`Initial registration complete for directory: ${e}`),this}queueUp(e,t){this.filesChanged.set(e,t),!this.timer&&(this.timer=setTimeout(async()=>{let e=[...this.filesChanged].map(([e,t])=>this.cx.router.register(e,t).catch(e=>this.cx.logger.error(`Error refreshing handlers: ${e.message}`)).finally(()=>this.filesChanged.delete(e)));await Promise.all(e),this.timer=null},this.cx.options.reloadDelay))}stopCore(){this.timer&&=(clearTimeout(this.timer),null),this.filesChanged.clear()}},pe=class extends Z{watcher=null;constructor(e){super(e)}async start(){this.stop(),await this.init();let e=this.cx.options.dir;return this.watcher=m.default.watch(e,{persistent:!0,ignoreInitial:!0,usePolling:!1,awaitWriteFinish:{stabilityThreshold:100,pollInterval:50}}).on(`all`,(t,n)=>{n&&this.queueUp(n,l.default.relative(e,n))}).on(`error`,e=>{let t=e instanceof Error?e:Error(String(e));this.cx.logger.error(`Watcher error: ${t.message}`),this.cx.logger.error(`Restarting watcher...`),this.start()}).on(`ready`,()=>{this.cx.logger.info(`Watcher ready and watching directory: ${e}`)}),this.cx.logger.info(`Watcher started on directory: ${e}`),this}stop(){return this.watcher&&=(this.watcher.close(),null),this.stopCore(),this}},me=class extends Z{watcher=null;constructor(e){super(e)}async start(){this.stop(),await this.init();let e=this.cx.options.dir;return this.watcher=c.default.watch(e,{recursive:!0},(t,n)=>{n&&this.queueUp(l.default.join(e,n),n)}).on(`error`,e=>{this.cx.logger.error(`Watcher error: ${e.message}`),this.cx.logger.error(`Restarting watcher...`),this.start()}),this.cx.logger.info(`Watcher started on directory: ${e}`),this}stop(){return this.watcher&&=(this.watcher.close(),null),this.stopCore(),this}};function Q(e,t){if(typeof t!=`object`||!t)return!1;if((0,g.static_cast)(t),typeof t.handler!=`function`)return e.logger.error(`handler must be a function`),!1;if(t.disposer!==void 0&&typeof t.disposer!=`function`)return e.logger.error(`disposer must be a function if provided`),!1;let n=t.handlerTimeoutMs;return n!==void 0&&(!Number.isSafeInteger(n)||n<100)?(e.logger.error(`handlerTimeoutMs must be an integer >= 100 if provided`),!1):t.type===0?!0:(e.logger.error(`You must use defineFluxionModule to create module`),!1)}function he(e,t){delete require.cache[t];let n=require(t);return Q(e,n)||(Q(e,n.default)?n=n.default:$throw(`Invalid handler module '${t}', make sure it satisfies defineFluxionModule(...) helper`)),n}var ge=class{cx;handlers=new Map;constructor(e){this.cx=e}makeStaticResource(e){return{type:1,handler:async(t,n,r)=>{if(t.method!==`GET`&&t.method!==`HEAD`){r.statusCode=405,r.setHeader(`Allow`,`GET, HEAD`),r.end();return}if(!c.default.existsSync(e)){r.statusCode=404,r.end(`Not Found`);return}let i=c.default.statSync(e);if(!i.isFile()){r.statusCode=404,r.end(`Not Found`);return}let a=ie[l.default.extname(e).toLowerCase()]??`application/octet-stream`;if(r.statusCode=200,r.setHeader(`Content-Type`,a),r.setHeader(`Content-Length`,String(i.size)),t.method===`HEAD`){r.end();return}return new Promise((t,n)=>{let i=c.default.createReadStream(e);i.on(`error`,n),i.on(`end`,()=>t(N)),i.pipe(r)})}}}async register(e,t){let n=this.handlers.get(t)?.disposer;if(n&&await H(n),!c.default.existsSync(e)){this.handlers.delete(t),this.cx.logger.info(`${x.red}Deleted ${x.reset} - ${t}`);return}if(!this.cx.options.include.some(e=>(0,h.minimatch)(t,e))){this.handlers.delete(t),this.cx.logger.info(`${x.yellow}Skipped ${x.reset} - ${t}`);return}if(this.cx.options.exclude.some(e=>(0,h.minimatch)(t,e))){this.handlers.delete(t),this.cx.logger.info(`${x.orange}Excluded${x.reset} - ${t}`);return}if(this.cx.options.apiInclude.some(e=>(0,h.minimatch)(t,e))){let n=he(this.cx,e);this.handlers.set(t,n),this.cx.logger.info(`${x.green}Api ${x.reset} - ${t}`);return}this.handlers.set(t,this.makeStaticResource(t)),this.cx.logger.info(`${x.brightBlue}Static ${x.reset} - ${t}`)}getModule(e){let t=e.pathname.replace(/^[/]+/,``).replace(/[/]+$/,``);return this.handlers.get(t)}remove(e){this.handlers.has(e)&&(this.handlers.delete(e),this.cx.logger.info(`${x.red}Deleted ${x.reset} - ${e}`));let t=e.endsWith(`/`)?e:e+`/`;for(let e of this.handlers.keys())e.startsWith(t)&&(this.handlers.delete(e),this.cx.logger.info(`${x.red}Deleted ${x.reset} - ${e}`))}};async function $(e){let t={options:O(e)};t.logger=te(t),t.router=new ge(t),u.default.isPrimary?V(t):(t.logger=ne(t.logger,process.pid),t.watcher=await new(t.options.nativeWatcher?me:pe)(t).start(),fe(t))}function _e(e,t=ae){return typeof e==`function`?(typeof t!=`function`&&$throw(`Invalid disposer, expected a function but got ${typeof t}`),{handler:e,disposer:t,type:0}):((typeof e!=`object`||!e)&&$throw(`Invalid argument, expected a FluxionModule object or a handler function, but got ${typeof e}`),typeof e.handler!=`function`&&$throw(`Invalid FluxionModule, "handler" must be a function`),e.disposer!==void 0&&typeof e.disposer!=`function`&&$throw(`Invalid FluxionModule, "disposer" must be a function if provided`),e.methods!==void 0&&(!Array.isArray(e.methods)||e.methods.some(e=>typeof e!=`string`))&&$throw(`Invalid FluxionModule, "methods" must be an array of strings if provided`),e.middlewares!==void 0&&(!Array.isArray(e.middlewares)||e.middlewares.some(e=>typeof e!=`function`))&&$throw(`Invalid FluxionModule, "middlewares" must be an array of functions if provided`),{...e,type:0})}exports.defineFluxionModule=_e,exports.fluxion=$;
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require("node:fs");c=s(c);let l=require("node:path");l=s(l);let u=require("node:cluster");u=s(u);let d=require("node:os");d=s(d);let f=require("node:http");f=s(f);let p=require("node:https");p=s(p);let m=require("chokidar");m=s(m);let h=require("minimatch"),g=require("type-narrow");function _(e=new Date){return`${e.getFullYear()}.${String(e.getMonth()+1).padStart(2,`0`)}.${String(e.getDate()).padStart(2,`0`)} ${String(e.getHours()).padStart(2,`0`)}:${String(e.getMinutes()).padStart(2,`0`)}:${String(e.getSeconds()).padStart(2,`0`)}.${String(e.getMilliseconds()).padStart(3,`0`)}`}const v=JSON.stringify,y=Object.keys,b=process.env.FLUXION_COLORS!==`0`;let x;(function(e){e.reset=b?`\x1B[0m`:``,e.bold=b?`\x1B[1m`:``,e.dim=b?`\x1B[2m`:``,e.italic=b?`\x1B[3m`:``,e.underline=b?`\x1B[4m`:``,e.blink=b?`\x1B[5m`:``,e.inverse=b?`\x1B[7m`:``,e.black=b?`\x1B[30m`:``,e.red=b?`\x1B[31m`:``,e.green=b?`\x1B[32m`:``,e.yellow=b?`\x1B[33m`:``,e.blue=b?`\x1B[34m`:``,e.magenta=b?`\x1B[35m`:``,e.cyan=b?`\x1B[36m`:``,e.white=b?`\x1B[37m`:``,e.brightBlack=b?`\x1B[90m`:``,e.brightRed=b?`\x1B[91m`:``,e.brightGreen=b?`\x1B[92m`:``,e.brightYellow=b?`\x1B[93m`:``,e.brightBlue=b?`\x1B[94m`:``,e.brightMagenta=b?`\x1B[95m`:``,e.brightCyan=b?`\x1B[96m`:``,e.brightWhite=b?`\x1B[97m`:``,e.bgBlack=b?`\x1B[40m`:``,e.bgRed=b?`\x1B[41m`:``,e.bgGreen=b?`\x1B[42m`:``,e.bgYellow=b?`\x1B[43m`:``,e.bgBlue=b?`\x1B[44m`:``,e.bgMagenta=b?`\x1B[45m`:``,e.bgCyan=b?`\x1B[46m`:``,e.bgWhite=b?`\x1B[47m`:``,e.bgBrightBlack=b?`\x1B[100m`:``,e.bgBrightRed=b?`\x1B[101m`:``,e.bgBrightGreen=b?`\x1B[102m`:``,e.bgBrightYellow=b?`\x1B[103m`:``,e.bgBrightBlue=b?`\x1B[104m`:``,e.bgBrightMagenta=b?`\x1B[105m`:``,e.bgBrightCyan=b?`\x1B[106m`:``,e.bgBrightWhite=b?`\x1B[107m`:``,e.purple=b?`\x1B[38;2;225;16;248m`:``,e.orange=b?`\x1B[38;2;248;147;16m`:``,e.darkGreen=b?`\x1B[38;2;22;101;52m`:``,e.claude=b?`\x1B[38;2;217;119;87m`:``,e.deepseek=b?`\x1B[38;2;57;100;254m`:``,e.gpt=b?`\x1B[38;2;41;60;77m`:``})(x||={});const S=e=>{try{return v(e)}catch{return`[unserializable]`}},ee={INFO:`${x.cyan}INFO${x.reset}`,WARN:`${x.orange}WARN${x.reset}`,ERROR:`${x.red}ERROR${x.reset}`,SUCC:`${x.green}SUCC${x.reset}`,DEBUG:`${x.blue}DEBUG${x.reset}`,VERBOSE:`${x.purple}VERBOSE${x.reset}`},C=e=>{let{level:t,timestamp:n,event:r,message:i,...a}=e,o=`${x.darkGreen}[${n}]${x.reset}`,s=ee[t]??t,c=i??r,l=y(a).length>0?`${x.dim}${S(a)}${x.reset}`:``;console.log(`${o} ${s} ${c}${l}`)};function w(e){let t=e.options.logger;return t===void 0||t===`one-line`?C:t===`json-line`?e=>console.log(S(e)):t}function te(e){let t=w(e);return{write(e,n,r={}){let i={...r,timestamp:_(),level:e,event:n};try{t(i)}catch{}},info(e,t){this.write(`INFO`,e,t)},warn(e,t){this.write(`WARN`,e,t)},error(e,t){this.write(`ERROR`,e,t)},succ(e,t){this.write(`SUCC`,e,t)},debug(e,t){this.write(`DEBUG`,e,t)},verbose(e,t){this.write(`VERBOSE`,e,t)}}}function ne(e,t){let n=`[${t}]`;return{write(t,r,i){e.write(t,`${n} ${r}`,i)},info(t,r){e.info(`${n} ${t}`,r)},warn(t,r){e.warn(`${n} ${t}`,r)},error(t,r){e.error(`${n} ${t}`,r)},succ(t,r){e.succ(`${n} ${t}`,r)},debug(t,r){e.debug(`${n} ${t}`,r)},verbose(t,r){e.verbose(`${n} ${t}`,r)}}}const T=typeof Error.isError==`function`?e=>Error.isError(e)?e.message:String(e):e=>e?.message||String(e);function re(e={}){let t=e.restartWhen??{},n=t.healthzTimeout??3e4;if(n!==1/0&&(!Number.isFinite(n)||n<1e4))throw Error(`[fluxion error] workerOptions.restartWhen.healthzTimeout must be a finite number >= 10000 (ms) or Infinity`);return{maxWorkerCount:e.maxWorkerCount??4,restartWhen:{memoryUsageGreaterThan:t.memoryUsageGreaterThan??1/0,healthzTimeout:n,uptimeGreaterThan:t.uptimeGreaterThan??1/0}}}function E(e,t){if(Buffer.isBuffer(e))return e;if(typeof e==`string`){if(!e.startsWith(`-----BEGIN`)){let n=l.default.isAbsolute(e)?e:l.default.join(t,e);if(c.default.existsSync(n))return c.default.readFileSync(n)}return Buffer.from(e)}throw Error(`[fluxion error] Certificate content must be a string or Buffer`)}function D(e,t){if(!e)return;if(typeof e!=`object`||!e||Array.isArray(e))throw Error(`[fluxion error] FluxionOptions.https must be an object`);if(typeof e.key!=`string`)throw Error(`[fluxion error] FluxionOptions.https.key must be a string`);if(typeof e.cert!=`string`)throw Error(`[fluxion error] FluxionOptions.https.cert must be a string`);let n={key:E(e.key,t),cert:E(e.cert,t)};return e.ca!==void 0&&(Array.isArray(e.ca)?n.ca=e.ca.map(e=>E(e,t)):n.ca=E(e.ca,t)),n}function O(e){if(typeof e!=`object`||!e||Array.isArray(e))throw Error(`[fluxion error] FluxionOptions must be an object`);let{dir:t=l.default.isAbsolute(e.dir)?e.dir:l.default.join(process.cwd(),e.dir),host:n,port:r,handlerTimeoutMs:i=5e3,middlewareTimeoutMs:a=3e3,staticResourceTimeoutMs:o=10*6e5,metaPort:s=r+1,moduleDir:u=process.cwd(),workerOptions:d={},maxRequestBytes:f=8e6,reloadDelay:p=500,include:m=[`**/*`],apiInclude:h=[`**/*.ts`],exclude:g=[`**/node_modules/**`,`**/.git/**`,`**/dist/**`,`**/build/**`,`**/.vscode/**`,`**/.idea/**`,`**/*.log`,`**/.DS_Store`,`**/coverage/**`,`**/.nyc_output/**`,`**/*.tmp`,`**/*.temp`],https:_,nativeWatcher:v=!1}=e,y=e.logger??`one-line`;if(y!==`one-line`&&y!==`json-line`&&typeof y!=`function`)throw Error(`[fluxion error] Invalid logger option, Must be 'one-line', 'json-line' or a custom logger function`);if(typeof t!=`string`)throw Error(`[fluxion error] FluxionOptions.dir must be a string`);if(typeof u!=`string`)throw Error(`[fluxion error] FluxionOptions.moduleDir must be a string`);if(typeof n!=`string`)throw Error(`[fluxion error] FluxionOptions.host must be a string`);if(!Number.isSafeInteger(i)||i<=100)throw Error(`[fluxion error] FluxionOptions.handlerTimeoutMs must be an integer greater than 100`);if(!Number.isSafeInteger(a)||a<=100)throw Error(`[fluxion error] FluxionOptions.middlewareTimeoutMs must be an integer greater than 100`);if(typeof p!=`number`||p<=0||!Number.isSafeInteger(p))throw Error(`[fluxion error] FluxionOptions.reloadDelay must be a positive integer`);if(p<50)throw Error(`[fluxion error] FluxionOptions.reloadDelay must be greater than or equal to 50`);if(typeof r!=`number`||!Number.isSafeInteger(r))throw Error(`[fluxion error] FluxionOptions.port must be a positive integer`);if(r<=1||r>65535)throw Error(`[fluxion error] FluxionOptions.port must be 1 ~ 65535`);if(typeof s!=`number`||!Number.isSafeInteger(s))throw Error(`[fluxion error] FluxionOptions.metaPort must be a positive integer`);if(s<=1||s>65535)throw Error(`[fluxion error] FluxionOptions.metaPort must be 1 ~ 65535`);if(s===r)throw Error(`[fluxion error] FluxionOptions.metaPort must be different from FluxionOptions.port`);if(typeof d!=`object`||!d||Array.isArray(d))throw Error(`[fluxion error] FluxionOptions.workerOptions must be an object`);if(typeof f!=`number`||f<=0||!Number.isSafeInteger(f))throw Error(`[fluxion error] FluxionOptions.maxRequestBytes must be a positive integer`);return c.default.existsSync(t)||c.default.mkdirSync(t,{recursive:!0}),{dir:t,host:n,port:r,handlerTimeoutMs:i,middlewareTimeoutMs:a,staticResourceTimeoutMs:o,reloadDelay:p,metaPort:s,moduleDir:u,workerOptions:re(d),maxRequestBytes:f,logger:y,include:m,apiInclude:h,exclude:g,nativeWatcher:v,https:D(_,u)}}const k=e=>[100].includes(e?.type),A=e=>[202,200,201,203].includes(e?.type),j=e=>process.send?.(e),M=(e,t)=>e.send(t),N=Symbol.for(`fluxion.router.StaticHandled`),P=Symbol.for(`fluxion.handlerTimeout`),F=Symbol.for(`fluxion.middlewareTimeout`),ie={".css":`text/css; charset=utf-8`,".html":`text/html; charset=utf-8`,".ico":`image/x-icon`,".js":`text/javascript; charset=utf-8`,".json":`application/json; charset=utf-8`,".map":`application/json; charset=utf-8`,".png":`image/png`,".jpg":`image/jpeg`,".jpeg":`image/jpeg`,".svg":`image/svg+xml`,".txt":`text/plain; charset=utf-8`,".webp":`image/webp`},ae=()=>{};function I(e,t,n=200){e.statusCode=n,e.setHeader(`Content-Type`,`application/json; charset=utf-8`),e.end(JSON.stringify(t))}function L(e,t,n=200){if(!e.writableEnded){if(e.headersSent){e.end();return}I(e,t,n)}}function R(e,t){let n=f.default.createServer((e,n)=>{let r=e.method??`GET`,i=`/`;try{i=new URL(e.url??`/`,`http://fluxion.local`).pathname}catch{I(n,{message:`Bad Request: invalid url`},400);return}if(r===`GET`&&i===`/_fluxion/healthz`){I(n,{ok:!0,role:`primary`,pid:process.pid,now:Date.now(),uptimeSeconds:Number(process.uptime().toFixed(3))});return}if(r===`GET`&&i===`/_fluxion/workers`){I(n,{ok:!0,now:Date.now(),workers:t()});return}I(n,{message:`Not Found`},404)});return n.on(`listening`,()=>{e.logger.info(`MetaApiStarted`,{pid:process.pid,host:e.options.host,port:e.options.metaPort,prefix:`/_fluxion`})}),n.on(`error`,t=>{e.logger.error(`MetaApiError`,{host:e.options.host,port:e.options.metaPort,error:T(t)}),process.exit(1)}),n.listen(e.options.metaPort,e.options.host),n}const z=e=>Number((e/1024/1024).toFixed(2)),B=6e4;function V(e){if(!u.default.isPrimary)throw Error(`[fluxion error] createPrimary should only be called in primary process`);let{workerOptions:t}=e.options,n=t.restartWhen,r=Math.max(1,d.default.cpus().length),i=Math.max(1,Math.min(t.maxWorkerCount??Math.min(2,r),r));e.logger.info(`PrimaryStarted`,{pid:process.pid,workers:i,host:e.options.host,port:e.options.port,metaPort:e.options.metaPort});let a=new Map,o=new Map,s=e=>{let t=Date.now(),n=(o.get(e)??[]).filter(e=>t-e<B);return o.set(e,n),n.length},c=e=>{let t=Date.now(),n=(o.get(e)??[]).filter(e=>t-e<B);n.push(t),o.set(e,n)},l=e=>s(e)>=3;R(e,()=>({primaryPid:process.pid,host:e.options.host,port:e.options.port,metaPort:e.options.metaPort,uptimeSeconds:Number(process.uptime().toFixed(3)),workers:Array.from(a.entries()).map(([e,t])=>{let{instance:n}=t,r=t.lastStats;return{workerId:e,slot:t.slot,pid:t.pid??n.process.pid??null,state:t.state,restartReason:t.restartReason??null,createdAt:t.createdAt,readyAt:t.readyAt??null,connected:n.isConnected(),dead:n.isDead(),exitedAfterDisconnect:n.exitedAfterDisconnect,lastPongAt:t.lastPongAt??null,lastRttMs:t.lastRttMs??null,stats:r===void 0?null:{at:r.at,uptimeSeconds:r.uptimeSeconds,cpu:r.cpu,memory:{...r.memory,rssMb:z(r.memory.rss),heapTotalMb:z(r.memory.heapTotal),heapUsedMb:z(r.memory.heapUsed),externalMb:z(r.memory.external),arrayBuffersMb:z(r.memory.arrayBuffers)}}}})}));let f=(t,n)=>{for(let e of a.values())if(e.state===`restarting`)return;if(l(t.slot)){e.logger.warn(`WorkerRecycleSuppressed`,{slot:t.slot,pid:t.pid,reason:n,windowMs:B,max:3});return}c(t.slot),t.state=`restarting`,t.restartReason=n,e.logger.warn(`WorkerRecycling`,{slot:t.slot,pid:t.pid,reason:n}),t.instance.kill()},p=(e,t)=>{let r=z(t.memory.rss);if(r>n.memoryUsageGreaterThan){f(e,`memoryUsageGreaterThan: rss ${r}MB > ${n.memoryUsageGreaterThan}MB`);return}let i=t.uptimeSeconds*1e3;i>n.uptimeGreaterThan&&f(e,`uptimeGreaterThan: ${Math.round(i/1e3)}s > ${Math.round(n.uptimeGreaterThan/1e3)}s`)},m=e=>{for(let t of a.values()){if(t.state!==`ready`||t.lastPongAt===void 0)continue;let r=e-t.lastPongAt;r>n.healthzTimeout&&f(t,`healthzTimeout: no pong for ${Math.round(r/1e3)}s > ${Math.round(n.healthzTimeout/1e3)}s`)}},h=e=>{g(u.default.fork({WORKER_ID:String(e)}),e)},g=(t,n)=>{let r={state:`creating`,pid:t.process.pid,slot:n,createdAt:Date.now(),instance:t};a.set(t.id,r),t.on(`message`,i=>{if(A(i)){if(i.type===202){let e=Date.now()-i.sentAt;r.pid=i.pid,r.lastPongAt=Date.now(),r.lastRttMs=e;return}if(i.type===201){r.state=`ready`,r.pid=i.pid,r.readyAt=Date.now(),e.logger.info(`WorkerReady`,{workerId:t.id,slot:n,pid:i.pid});return}if(i.type===200){r.state=`created`,r.pid=i.pid,e.logger.info(`WorkerCreated`,{workerId:t.id,slot:n,pid:i.pid});return}i.type===203&&(r.pid=i.pid,r.lastStats=i.stats,r.state===`ready`&&p(r,i.stats))}}),t.on(`exit`,(n,r)=>{let i=a.get(t.id);a.delete(t.id);let o=i?.slot,s=i?.state===`restarting`,u=i?.restartReason??null;if(e.logger.warn(`WorkerExited`,{workerId:t.id,slot:o??null,pid:t.process.pid??`unknown`,code:n,signal:r??`none`,expected:s,reason:u}),o!==void 0){if(s){h(o);return}if(c(o),l(o)){e.logger.error(`WorkerRespawnSuppressed`,{slot:o,windowMs:B,max:3});return}h(o)}})};for(let e=0;e<i;e++)h(e+1);setInterval(()=>{let e=Date.now();for(let t of a.values())if(t.instance.isConnected())try{M(t.instance,{type:100,sentAt:e})}catch{}m(Date.now())},5e3).unref()}function H(e,...t){return new Promise((n,r)=>{try{let i=e(...t);i instanceof Promise?i.then(n).catch(r):n(i)}catch(e){r(e)}})}function U(e){let t=e.headersDistinct[`x-forwarded-for`];if(t){let e=t[0]?.split(`,`)[0]?.trim();if(e&&e.length>0)return e}let n=e.headersDistinct[`x-real-ip`]?.[0].trim();return n===void 0?e.socket.remoteAddress??`unknown`:n}function W(e){if(e===void 0)return!1;let t=e.toLowerCase();return t.startsWith(`text/`)||t.includes(`json`)||t.includes(`xml`)||t.includes(`x-www-form-urlencoded`)||t.includes(`javascript`)}function G(e){if(e!==void 0)try{return new URL(e,`http://fluxion.local`)}catch{return}}function K(e){let t={};for(let[n,r]of e.entries()){let e=t[n];if(e===void 0){t[n]=r;continue}if(Array.isArray(e)){e.push(r);continue}t[n]=[e,r]}return t}function q(e,t){let n=Error(`request body too large: ${e.toString()} bytes exceeds ${t.toString()} bytes`);return n.code=`REQUEST_BODY_TOO_LARGE`,n}function J(e){return Array.isArray(e)?e[0]:e}function Y(){return{exists:!1,bytes:0,truncated:!1}}function oe(e,t,n,r){return t===0?Y():W(n)?{exists:!0,value:e.toString(`utf8`),bytes:t,truncated:r}:{exists:!0,value:`<binary body: ${t} bytes>`,bytes:t,truncated:r}}async function se(e,t,n,r=8192){if(t===`GET`||t===`HEAD`||e.readableEnded)return{rawBody:void 0,preview:Y()};let i=J(e.headers[`content-length`]),a=i===void 0?NaN:Number.parseInt(i,10);if(Number.isFinite(a)&&a>n)throw q(a,n);return new Promise((t,i)=>{let a=[],o=[],s=0,c=0,l=!1,u=!1,d=()=>{e.off(`data`,p),e.off(`end`,m),e.off(`error`,h),e.off(`aborted`,g)},f=e=>{u||(u=!0,e())},p=t=>{let u=Buffer.isBuffer(t)?t:Buffer.from(t);if(s+=u.byteLength,s>n){d(),e.resume(),f(()=>{i(q(s,n))});return}if(a.push(u),c<r){let e=r-c,t=u.subarray(0,e);o.push(t),c+=t.length,t.length<u.length&&(l=!0)}else l=!0},m=()=>{d(),f(()=>{let n=a.length>0?Buffer.concat(a):void 0;t({rawBody:n,preview:oe(o.length>0?Buffer.concat(o):Buffer.alloc(0),n?.byteLength??0,J(e.headers[`content-type`]),l)})})},h=e=>{d(),f(()=>{i(e)})},g=()=>{d(),f(()=>{i(Error(`request aborted while reading body`))})};e.on(`data`,p),e.once(`end`,m),e.once(`error`,h),e.once(`aborted`,g)})}async function ce(e,t,n){let{rawBody:r,preview:i}=await se(e,t,n);if(r===void 0||r.byteLength===0)return{body:{},preview:i};let a=J(e.headers[`content-type`])?.toLowerCase()??``;if(a.includes(`json`)){let e=r.toString(`utf8`).trim();if(e.length===0)return{body:{},preview:i};try{let t=JSON.parse(e);return typeof t==`object`&&t&&!Array.isArray(t)?{body:t,preview:i}:{body:{value:t},preview:i}}catch{return{body:{raw:e},preview:i}}}return a.includes(`x-www-form-urlencoded`)?{body:K(new URLSearchParams(r.toString(`utf8`))),preview:i}:W(a)?{body:{raw:r.toString(`utf8`)},preview:i}:{body:{},preview:i}}function le(e){if(!e)return{};let t={},n=e.split(`;`);for(let e of n){let[n,...r]=e.split(`=`);if(!n)continue;let i=n.trim(),a=r.join(`=`).trim();t[i]=decodeURIComponent(a)}return t}const X=(e,t,n)=>Promise.race([e,new Promise(e=>setTimeout(()=>e(n),t))]);function ue(e){let t=async(t,n)=>{let r=t.method??`GET`,i=U(t),a=G(t.url);if(a===void 0){L(n,{message:`Bad Request: req.url is undefined`},400);return}let o={method:r,ip:i,url:a,query:K(a.searchParams),body:{},headers:t.headers,cookie:le(t.headers.cookie)},s={exists:!1,bytes:0,truncated:!1};e.logger.info(`Req`,{method:r,ip:i,path:a.pathname});let c=performance.now();n.once(`finish`,()=>{let t={workerId:process.env.WORKER_ID??`[primary]`,method:r,ip:i,path:a.pathname,status:n.statusCode,duration:(performance.now()-c).toFixed(4)};y(o.query).length>0&&(t.query=o.query),s.exists&&(t.body=s.value,t.bodyBytes=s.bytes,t.bodyTruncated=s.truncated),e.logger.info(`Res`,t)});try{if(o.url.pathname.startsWith(`/_fluxion/`)){L(n,{message:`Not Found`},404);return}let r=await ce(t,o.method,e.options.maxRequestBytes);o.body=r.body,s=r.preview;let i=await e.router.getModule(a);if(!i){L(n,{message:`Not Found`},404);return}if(t.method&&i.methods&&!i.methods.includes(t.method)){L(n,{message:`Method Not Allowed`},405);return}let c=i.type===0?i.handlerTimeoutMs??e.options.handlerTimeoutMs:e.options.staticResourceTimeoutMs;if(i.middlewares)for(let r=0;r<i.middlewares.length;r++){if(await X(H(i.middlewares[r],o,t,n),e.options.middlewareTimeoutMs,F)===F){e.logger.warn(`MiddlewareTimeout`,{method:o.method,ip:o.ip}),L(n,{message:`Internal Server Error`},500);return}if(n.writableEnded)return;if(n.headersSent){n.end();return}}let l=await X(H(i.handler,o,t,n),c,P);if(l===P){e.logger.warn(`HandlerTimeout`,{method:o.method,ip:o.ip}),L(n,{message:`Handler timed out`},500);return}l!==N&&L(n,l)}catch(t){e.logger.error(`RequestFailed`,{method:o.method,ip:o.ip,path:o.url.pathname,error:T(t)}),t.code===`REQUEST_BODY_TOO_LARGE`?L(n,{message:T(t)},413):L(n,{message:T(t)},500)}},n=e.options.https?p.default.createServer({key:e.options.https.key,cert:e.options.https.cert,ca:e.options.https.ca},t):f.default.createServer(t);return n.on(`close`,()=>{e.logger.info(`ServerClosed`,{host:e.options.host,port:e.options.port})}),n.listen(e.options.port,e.options.host,()=>{e.logger.info(`ServerStarted`,{pid:process.pid,protocol:e.options.https?`https`:`http`,host:e.options.host,port:e.options.port}),e.logger.info(`DynamicDirectory`,{directory:e.options.dir})}),n.on(`error`,t=>{e.logger.error(`ServerError`,{error:T(t)})}),n}const de=()=>{let e=process.cpuUsage(),t=Date.now();setInterval(()=>{let n=Date.now(),r=Math.max(1,(n-t)*1e3),i=process.cpuUsage(e),a=Number(((i.user+i.system)/r*100).toFixed(2));e=process.cpuUsage(),t=n;let o=process.memoryUsage();j({type:203,pid:process.pid,stats:{at:n,pid:process.pid,uptimeSeconds:Number(process.uptime().toFixed(3)),cpu:{userMicros:i.user,systemMicros:i.system,percent:a},memory:{rss:o.rss,heapTotal:o.heapTotal,heapUsed:o.heapUsed,external:o.external,arrayBuffers:o.arrayBuffers}}})},2e3).unref()};function fe(e){if(u.default.isPrimary)throw Error(`[fluxion error] createWorker should only be called in worker process`);process.on(`message`,e=>{if(k(e)&&e.type===100){j({type:202,pid:process.pid,sentAt:e.sentAt,receivedAt:Date.now()});return}}),j({type:200,pid:process.pid}),de();try{ue(e),j({type:201,pid:process.pid})}catch(t){e.logger.error(`WorkerBootstrapFailed`,{pid:process.pid,error:T(t)}),process.exit(1)}}var Z=class{cx;timer=null;filesChanged=new Map;constructor(e){this.cx=e}async init(){let e=this.cx.options.dir;if(!c.default.existsSync(e))return this.cx.logger.warn(`Directory does not exist: ${e}`),this;let t=[],n=(e,r)=>{let i=c.default.readdirSync(e,{withFileTypes:!0});for(let a=0;a<i.length;a++){let o=i[a],s=l.default.join(e,o.name),c=l.default.join(r,o.name);if(o.isDirectory())n(s,c);else if(o.isFile()){let e=this.cx.router.register(s,c).catch(e=>{this.cx.logger.error(`Error registering file ${c}: ${e.message}`)});t.push(e)}}};return n(e,``),await Promise.all(t),this.cx.logger.info(`Initial registration complete for directory: ${e}`),this}queueUp(e,t){this.filesChanged.set(e,t),!this.timer&&(this.timer=setTimeout(async()=>{let e=[...this.filesChanged].map(([e,t])=>this.cx.router.register(e,t).catch(e=>this.cx.logger.error(`Error refreshing handlers: ${e.message}`)).finally(()=>this.filesChanged.delete(e)));await Promise.all(e),this.timer=null},this.cx.options.reloadDelay))}stopCore(){this.timer&&=(clearTimeout(this.timer),null),this.filesChanged.clear()}},pe=class extends Z{watcher=null;constructor(e){super(e)}async start(){this.stop(),await this.init();let e=this.cx.options.dir;return this.watcher=m.default.watch(e,{persistent:!0,ignoreInitial:!0,usePolling:!1,awaitWriteFinish:{stabilityThreshold:100,pollInterval:50}}).on(`all`,(t,n)=>{n&&this.queueUp(n,l.default.relative(e,n))}).on(`error`,e=>{let t=e instanceof Error?e:Error(String(e));this.cx.logger.error(`Watcher error: ${t.message}`),this.cx.logger.error(`Restarting watcher...`),this.start()}).on(`ready`,()=>{this.cx.logger.info(`Watcher ready and watching directory: ${e}`)}),this.cx.logger.info(`Watcher started on directory: ${e}`),this}stop(){return this.watcher&&=(this.watcher.close(),null),this.stopCore(),this}},me=class extends Z{watcher=null;constructor(e){super(e)}async start(){this.stop(),await this.init();let e=this.cx.options.dir;return this.watcher=c.default.watch(e,{recursive:!0},(t,n)=>{n&&this.queueUp(l.default.join(e,n),n)}).on(`error`,e=>{this.cx.logger.error(`Watcher error: ${e.message}`),this.cx.logger.error(`Restarting watcher...`),this.start()}),this.cx.logger.info(`Watcher started on directory: ${e}`),this}stop(){return this.watcher&&=(this.watcher.close(),null),this.stopCore(),this}};function Q(e,t){if(typeof t!=`object`||!t)return!1;if((0,g.static_cast)(t),typeof t.handler!=`function`)return e.logger.error(`handler must be a function`),!1;if(t.disposer!==void 0&&typeof t.disposer!=`function`)return e.logger.error(`disposer must be a function if provided`),!1;let n=t.handlerTimeoutMs;return n!==void 0&&(!Number.isSafeInteger(n)||n<100)?(e.logger.error(`handlerTimeoutMs must be an integer >= 100 if provided`),!1):t.type===0?!0:(e.logger.error(`You must use defineFluxionModule to create module`),!1)}function he(e,t){delete require.cache[t];let n=require(t);if(!Q(e,n))if(Q(e,n.default))n=n.default;else throw Error(`[fluxion error] Invalid handler module '${t}', make sure it satisfies defineFluxionModule(...) helper`);return n}var ge=class{cx;handlers=new Map;constructor(e){this.cx=e}makeStaticResource(e){return{type:1,handler:async(t,n,r)=>{if(t.method!==`GET`&&t.method!==`HEAD`){r.statusCode=405,r.setHeader(`Allow`,`GET, HEAD`),r.end();return}if(!c.default.existsSync(e)){r.statusCode=404,r.end(`Not Found`);return}let i=c.default.statSync(e);if(!i.isFile()){r.statusCode=404,r.end(`Not Found`);return}let a=ie[l.default.extname(e).toLowerCase()]??`application/octet-stream`;if(r.statusCode=200,r.setHeader(`Content-Type`,a),r.setHeader(`Content-Length`,String(i.size)),t.method===`HEAD`){r.end();return}return new Promise((t,n)=>{let i=c.default.createReadStream(e);i.on(`error`,n),i.on(`end`,()=>t(N)),i.pipe(r)})}}}async register(e,t){let n=this.handlers.get(t)?.disposer;if(n&&await H(n),!c.default.existsSync(e)){this.handlers.delete(t),this.cx.logger.info(`${x.red}Deleted ${x.reset} - ${t}`);return}if(!this.cx.options.include.some(e=>(0,h.minimatch)(t,e))){this.handlers.delete(t),this.cx.logger.info(`${x.yellow}Skipped ${x.reset} - ${t}`);return}if(this.cx.options.exclude.some(e=>(0,h.minimatch)(t,e))){this.handlers.delete(t),this.cx.logger.info(`${x.orange}Excluded${x.reset} - ${t}`);return}if(this.cx.options.apiInclude.some(e=>(0,h.minimatch)(t,e))){let n=he(this.cx,e);this.handlers.set(t,n),this.cx.logger.info(`${x.green}Api ${x.reset} - ${t}`);return}this.handlers.set(t,this.makeStaticResource(t)),this.cx.logger.info(`${x.brightBlue}Static ${x.reset} - ${t}`)}getModule(e){let t=e.pathname.replace(/^[/]+/,``).replace(/[/]+$/,``);return this.handlers.get(t)}remove(e){this.handlers.has(e)&&(this.handlers.delete(e),this.cx.logger.info(`${x.red}Deleted ${x.reset} - ${e}`));let t=e.endsWith(`/`)?e:e+`/`;for(let e of this.handlers.keys())e.startsWith(t)&&(this.handlers.delete(e),this.cx.logger.info(`${x.red}Deleted ${x.reset} - ${e}`))}};async function $(e){let t={options:O(e)};t.logger=te(t),t.router=new ge(t),u.default.isPrimary?V(t):(t.logger=ne(t.logger,process.pid),t.watcher=await new(t.options.nativeWatcher?me:pe)(t).start(),fe(t))}function _e(e,t=ae){if(typeof e==`function`){if(typeof t!=`function`)throw Error(`[fluxion error] Invalid disposer, expected a function but got ${typeof t}`);return{handler:e,disposer:t,type:0}}if(typeof e!=`object`||!e)throw Error(`[fluxion error] Invalid argument, expected a FluxionModule object or a handler function, but got ${typeof e}`);if(typeof e.handler!=`function`)throw Error(`[fluxion error] Invalid FluxionModule, "handler" must be a function`);if(e.disposer!==void 0&&typeof e.disposer!=`function`)throw Error(`[fluxion error] Invalid FluxionModule, "disposer" must be a function if provided`);if(e.methods!==void 0&&(!Array.isArray(e.methods)||e.methods.some(e=>typeof e!=`string`)))throw Error(`[fluxion error] Invalid FluxionModule, "methods" must be an array of strings if provided`);if(e.middlewares!==void 0&&(!Array.isArray(e.middlewares)||e.middlewares.some(e=>typeof e!=`function`)))throw Error(`[fluxion error] Invalid FluxionModule, "middlewares" must be an array of functions if provided`);return{...e,type:0}}function ve(e){if(typeof e!=`function`)throw Error(`[fluxion error] Invalid FluxionMiddleware, expected a function but got ${typeof e}`);return e}exports.defineFluxionMiddleware=ve,exports.defineFluxionModule=_e,exports.fluxion=$;
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["path","fs","http","cluster","os","https","http","cluster","fs","path","path","fs","path","fs","path","cluster"],"sources":["../src/common/dtm.ts","../src/common/native.ts","../src/common/color.ts","../src/common/logger.ts","../src/http/options.ts","../src/cluster/consts.ts","../src/cluster/communicate.ts","../src/common/consts.ts","../src/http/respond.ts","../src/cluster/meta-api.ts","../src/cluster/primary.ts","../src/common/promise-try.ts","../src/http/headers.ts","../src/http/request.ts","../src/http/query.ts","../src/http/body.ts","../src/http/cookie.ts","../src/cluster/server.ts","../src/cluster/worker.ts","../src/watcher/base.ts","../src/watcher/chokidar.ts","../src/watcher/native.ts","../src/common/injector.ts","../src/router/index.ts","../src/fluxion.ts","../src/index.ts"],"sourcesContent":["export function dtm(dt = new Date()) {\n const y = dt.getFullYear();\n const m = String(dt.getMonth() + 1).padStart(2, '0');\n const d = String(dt.getDate()).padStart(2, '0');\n const hh = String(dt.getHours()).padStart(2, '0');\n const mm = String(dt.getMinutes()).padStart(2, '0');\n const ss = String(dt.getSeconds()).padStart(2, '0');\n const ms = String(dt.getMilliseconds()).padStart(3, '0');\n return `${y}.${m}.${d} ${hh}:${mm}:${ss}.${ms}`;\n}\n","export const $stringify = JSON.stringify;\n\nexport const $keys = Object.keys;\n","const useColor = process.env.FLUXION_COLORS !== '0';\n\n/**\n * Color Control Characters for Terminal (cctl)\n */\nexport namespace cctl {\n export const reset = useColor ? '\\x1b[0m' : '';\n export const bold = useColor ? '\\x1b[1m' : '';\n export const dim = useColor ? '\\x1b[2m' : '';\n export const italic = useColor ? '\\x1b[3m' : '';\n export const underline = useColor ? '\\x1b[4m' : '';\n export const blink = useColor ? '\\x1b[5m' : '';\n export const inverse = useColor ? '\\x1b[7m' : '';\n\n export const black = useColor ? '\\x1b[30m' : '';\n export const red = useColor ? '\\x1b[31m' : '';\n export const green = useColor ? '\\x1b[32m' : '';\n export const yellow = useColor ? '\\x1b[33m' : '';\n export const blue = useColor ? '\\x1b[34m' : '';\n export const magenta = useColor ? '\\x1b[35m' : '';\n export const cyan = useColor ? '\\x1b[36m' : '';\n export const white = useColor ? '\\x1b[37m' : '';\n\n export const brightBlack = useColor ? '\\x1b[90m' : '';\n export const brightRed = useColor ? '\\x1b[91m' : '';\n export const brightGreen = useColor ? '\\x1b[92m' : '';\n export const brightYellow = useColor ? '\\x1b[93m' : '';\n export const brightBlue = useColor ? '\\x1b[94m' : '';\n export const brightMagenta = useColor ? '\\x1b[95m' : '';\n export const brightCyan = useColor ? '\\x1b[96m' : '';\n export const brightWhite = useColor ? '\\x1b[97m' : '';\n\n export const bgBlack = useColor ? '\\x1b[40m' : '';\n export const bgRed = useColor ? '\\x1b[41m' : '';\n export const bgGreen = useColor ? '\\x1b[42m' : '';\n export const bgYellow = useColor ? '\\x1b[43m' : '';\n export const bgBlue = useColor ? '\\x1b[44m' : '';\n export const bgMagenta = useColor ? '\\x1b[45m' : '';\n export const bgCyan = useColor ? '\\x1b[46m' : '';\n export const bgWhite = useColor ? '\\x1b[47m' : '';\n\n export const bgBrightBlack = useColor ? '\\x1b[100m' : '';\n export const bgBrightRed = useColor ? '\\x1b[101m' : '';\n export const bgBrightGreen = useColor ? '\\x1b[102m' : '';\n export const bgBrightYellow = useColor ? '\\x1b[103m' : '';\n export const bgBrightBlue = useColor ? '\\x1b[104m' : '';\n export const bgBrightMagenta = useColor ? '\\x1b[105m' : '';\n export const bgBrightCyan = useColor ? '\\x1b[106m' : '';\n export const bgBrightWhite = useColor ? '\\x1b[107m' : '';\n\n // 'rgb(225, 16, 248)';\n export const purple = useColor ? '\\x1b[38;2;225;16;248m' : '';\n // 'rgb(248, 147, 16)';\n export const orange = useColor ? '\\x1b[38;2;248;147;16m' : '';\n export const darkGreen = useColor ? '\\x1b[38;2;22;101;52m' : '';\n export const claude = useColor ? '\\x1b[38;2;217;119;87m' : '';\n export const deepseek = useColor ? '\\x1b[38;2;57;100;254m' : '';\n export const gpt = useColor ? '\\x1b[38;2;41;60;77m' : '';\n}\n","import type { FluxionContext } from '@/types.js';\nimport type { otherstring } from '@/global.js';\n\nimport { dtm } from './dtm.js';\nimport { $keys, $stringify } from './native.js';\nimport { cctl } from './color.js';\n\ntype LogLevel = 'INFO' | 'WARN' | 'ERROR' | 'SUCC' | 'DEBUG' | 'VERBOSE' | otherstring;\n\ninterface LogEntry {\n timestamp: string;\n level: LogLevel;\n event: string;\n message?: string;\n [key: string]: unknown;\n}\n\nexport type LoggerOption = 'one-line' | 'json-line' | FluxionLoggerFn;\n\nexport type FluxionLoggerFn = (entry: LogEntry) => void;\n\nexport interface FluxionLogger {\n /**\n * [WARN] We assert that `fields` is an object or undefined.\n */\n write(level: LogLevel, event: string, fields?: object): void;\n info(event: string, fields?: object): void;\n warn(event: string, fields?: object): void;\n error(event: string, fields?: object): void;\n succ(event: string, fields?: object): void;\n debug(event: string, fields?: object): void;\n verbose(event: string, fields?: object): void;\n}\n\nconst safeStringify = (value: unknown): string => {\n try {\n return $stringify(value);\n } catch {\n return '[unserializable]';\n }\n};\n\nconst ColoredLevels: Record<LogLevel, string> = {\n INFO: `${cctl.cyan}INFO${cctl.reset}`,\n WARN: `${cctl.orange}WARN${cctl.reset}`,\n ERROR: `${cctl.red}ERROR${cctl.reset}`,\n SUCC: `${cctl.green}SUCC${cctl.reset}`,\n DEBUG: `${cctl.blue}DEBUG${cctl.reset}`,\n VERBOSE: `${cctl.purple}VERBOSE${cctl.reset}`,\n};\n\nexport const oneLineLogger: FluxionLoggerFn = (entry: LogEntry) => {\n const { level: rawLevel, timestamp: rawTimestamp, event: rawEvent, message: rawMessage, ...fields } = entry;\n\n const timestamp = `${cctl.darkGreen}[${rawTimestamp}]${cctl.reset}`;\n const level = ColoredLevels[rawLevel] ?? rawLevel;\n const body = rawMessage ?? rawEvent;\n const fieldsText = $keys(fields).length > 0 ? `${cctl.dim}${safeStringify(fields)}${cctl.reset}` : '';\n\n // eslint-disable-next-line @typescript-eslint/no-console\n console.log(`${timestamp} ${level} ${body}${fieldsText}`);\n};\n\n/**\n * & Logger Options here is checked by normalizeOptions function.\n */\nfunction resolveLoggerSink(cx: Pick<FluxionContext, 'options'>): FluxionLoggerFn {\n const loggerOption = cx.options.logger;\n if (loggerOption === undefined || loggerOption === 'one-line') {\n return oneLineLogger;\n }\n\n if (loggerOption === 'json-line') {\n // eslint-disable-next-line @typescript-eslint/no-console\n return (entry: LogEntry) => console.log(safeStringify(entry));\n }\n\n return loggerOption;\n}\n\nexport function createLogger(cx: Pick<FluxionContext, 'options'>): FluxionLogger {\n const sink = resolveLoggerSink(cx);\n\n const logger: FluxionLogger = {\n write(level: LogLevel, event: string, fields: Record<string, unknown> = {}): void {\n const entry: LogEntry = {\n ...fields,\n timestamp: dtm(),\n level,\n event,\n };\n\n try {\n sink(entry);\n } catch {\n // Ignore logger sink failures to avoid breaking request handling.\n }\n },\n info(event: string, fields?: Record<string, unknown>): void {\n this.write('INFO', event, fields);\n },\n warn(event: string, fields?: Record<string, unknown>): void {\n this.write('WARN', event, fields);\n },\n error(event: string, fields?: Record<string, unknown>): void {\n this.write('ERROR', event, fields);\n },\n succ(event: string, fields?: Record<string, unknown>): void {\n this.write('SUCC', event, fields);\n },\n debug(event: string, fields?: Record<string, unknown>): void {\n this.write('DEBUG', event, fields);\n },\n verbose(event: string, fields?: Record<string, unknown>): void {\n this.write('VERBOSE', event, fields);\n },\n };\n\n return logger;\n}\n\n/**\n * Create a worker logger that prefixes all log messages with the worker PID.\n */\nexport function createWorkerLogger(baseLogger: FluxionLogger, pid: number): FluxionLogger {\n const pidPrefix = `[${pid}]`;\n\n return {\n write(level: LogLevel, event: string, fields?: object): void {\n baseLogger.write(level, `${pidPrefix} ${event}`, fields);\n },\n info(event: string, fields?: object): void {\n baseLogger.info(`${pidPrefix} ${event}`, fields);\n },\n warn(event: string, fields?: object): void {\n baseLogger.warn(`${pidPrefix} ${event}`, fields);\n },\n error(event: string, fields?: object): void {\n baseLogger.error(`${pidPrefix} ${event}`, fields);\n },\n succ(event: string, fields?: object): void {\n baseLogger.succ(`${pidPrefix} ${event}`, fields);\n },\n debug(event: string, fields?: object): void {\n baseLogger.debug(`${pidPrefix} ${event}`, fields);\n },\n verbose(event: string, fields?: object): void {\n baseLogger.verbose(`${pidPrefix} ${event}`, fields);\n },\n };\n}\n\n/**\n * ! Error.isError needs Node.js 24\n */\nexport const getErrorMessage =\n typeof Error.isError === 'function'\n ? (e: unknown): string => (Error.isError(e) ? e.message : String(e))\n : (e: unknown): string => (e as any)?.message || String(e);\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport type { WorkerOptions, NormalizedWorkerOptions, FluxionOptions, NormalizedFluxionOptions } from '../types.js';\n\n/**\n * Resolves worker options with framework defaults. All thresholds become\n * concrete numbers (`Infinity` disables a check) so the primary can evaluate\n * them without null-handling.\n */\nfunction resolveWorkerOptions(options: WorkerOptions = {}): NormalizedWorkerOptions {\n const rw = options.restartWhen ?? {};\n const healthzTimeout = rw.healthzTimeout ?? 30_000;\n // Ping runs every 5s; a threshold below 2x that would recycle healthy workers\n // (a ready worker's lastPongAt is normally ~5s old). Infinity disables.\n if (healthzTimeout !== Infinity && (!Number.isFinite(healthzTimeout) || healthzTimeout < 10_000)) {\n $throw('workerOptions.restartWhen.healthzTimeout must be a finite number >= 10000 (ms) or Infinity');\n }\n return {\n maxWorkerCount: options.maxWorkerCount ?? 4,\n restartWhen: {\n memoryUsageGreaterThan: rw.memoryUsageGreaterThan ?? Infinity,\n healthzTimeout,\n uptimeGreaterThan: rw.uptimeGreaterThan ?? Infinity,\n },\n };\n}\n\n/**\n * Read certificate content from a file path or return the content directly.\n */\nfunction readCertificateContent(content: string | Buffer, moduleDir: string): Buffer {\n if (Buffer.isBuffer(content)) {\n return content;\n }\n if (typeof content === 'string') {\n // Check if it looks like a file path (not a PEM certificate)\n // PEM certificates start with \"-----BEGIN\"\n if (!content.startsWith('-----BEGIN')) {\n const filePath = path.isAbsolute(content) ? content : path.join(moduleDir, content);\n if (fs.existsSync(filePath)) {\n return fs.readFileSync(filePath);\n }\n }\n return Buffer.from(content);\n }\n $throw('Certificate content must be a string or Buffer');\n}\n\n/**\n * Normalize HTTPS options.\n */\nfunction normalizeHttpsOptions(\n https: FluxionOptions['https'],\n moduleDir: string,\n): NormalizedFluxionOptions['https'] | undefined {\n if (!https) {\n return undefined;\n }\n\n if (typeof https !== 'object' || https === null || Array.isArray(https)) {\n $throw('FluxionOptions.https must be an object');\n }\n if (typeof https.key !== 'string') {\n $throw('FluxionOptions.https.key must be a string');\n }\n if (typeof https.cert !== 'string') {\n $throw('FluxionOptions.https.cert must be a string');\n }\n\n const result: NormalizedFluxionOptions['https'] = {\n key: readCertificateContent(https.key, moduleDir),\n cert: readCertificateContent(https.cert, moduleDir),\n };\n\n if (https.ca !== undefined) {\n if (Array.isArray(https.ca)) {\n result.ca = https.ca.map((item) => readCertificateContent(item, moduleDir));\n } else {\n result.ca = readCertificateContent(https.ca, moduleDir);\n }\n }\n\n return result;\n}\n\n/**\n * Normalize options and create necessary resources like the dynamic directory and logger.\n */\nexport function normalizeOptions(o: FluxionOptions): NormalizedFluxionOptions {\n if (typeof o !== 'object' || o === null || Array.isArray(o)) {\n $throw('FluxionOptions must be an object');\n }\n\n const {\n dir = path.isAbsolute(o.dir) ? o.dir : path.join(process.cwd(), o.dir),\n host,\n port,\n handlerTimeoutMs = 5000,\n middlewareTimeoutMs = 3000,\n staticResourceTimeoutMs = 10 * 600000,\n metaPort = port + 1,\n moduleDir = process.cwd(),\n workerOptions = {},\n maxRequestBytes = 8_000_000,\n reloadDelay = 500,\n include = ['**/*'],\n apiInclude = ['**/*.ts'],\n exclude = [\n '**/node_modules/**',\n '**/.git/**',\n '**/dist/**',\n '**/build/**',\n '**/.vscode/**',\n '**/.idea/**',\n '**/*.log',\n '**/.DS_Store',\n '**/coverage/**',\n '**/.nyc_output/**',\n '**/*.tmp',\n '**/*.temp',\n ],\n https,\n nativeWatcher = false,\n } = o as FluxionOptions;\n\n const logger = o.logger ?? 'one-line';\n if (logger !== 'one-line' && logger !== 'json-line' && typeof logger !== 'function') {\n $throw(`Invalid logger option, Must be 'one-line', 'json-line' or a custom logger function`);\n }\n\n if (typeof dir !== 'string') {\n $throw('FluxionOptions.dir must be a string');\n }\n\n if (typeof moduleDir !== 'string') {\n $throw('FluxionOptions.moduleDir must be a string');\n }\n\n if (typeof host !== 'string') {\n $throw('FluxionOptions.host must be a string');\n }\n\n if (!Number.isSafeInteger(handlerTimeoutMs) || handlerTimeoutMs <= 100) {\n $throw('FluxionOptions.handlerTimeoutMs must be an integer greater than 100');\n }\n\n if (!Number.isSafeInteger(middlewareTimeoutMs) || middlewareTimeoutMs <= 100) {\n $throw('FluxionOptions.middlewareTimeoutMs must be an integer greater than 100');\n }\n\n if (typeof reloadDelay !== 'number' || reloadDelay <= 0 || !Number.isSafeInteger(reloadDelay)) {\n $throw('FluxionOptions.reloadDelay must be a positive integer');\n }\n\n if (reloadDelay < 50) {\n $throw('FluxionOptions.reloadDelay must be greater than or equal to 50');\n }\n\n if (typeof port !== 'number' || !Number.isSafeInteger(port)) {\n $throw('FluxionOptions.port must be a positive integer');\n }\n\n if (port <= 1 || port > 65535) {\n $throw('FluxionOptions.port must be 1 ~ 65535');\n }\n\n if (typeof metaPort !== 'number' || !Number.isSafeInteger(metaPort)) {\n $throw('FluxionOptions.metaPort must be a positive integer');\n }\n\n if (metaPort <= 1 || metaPort > 65535) {\n $throw('FluxionOptions.metaPort must be 1 ~ 65535');\n }\n\n if (metaPort === port) {\n $throw('FluxionOptions.metaPort must be different from FluxionOptions.port');\n }\n\n if (typeof workerOptions !== 'object' || workerOptions === null || Array.isArray(workerOptions)) {\n $throw('FluxionOptions.workerOptions must be an object');\n }\n\n if (typeof maxRequestBytes !== 'number' || maxRequestBytes <= 0 || !Number.isSafeInteger(maxRequestBytes)) {\n $throw('FluxionOptions.maxRequestBytes must be a positive integer');\n }\n\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n\n return {\n dir,\n host,\n port,\n handlerTimeoutMs,\n middlewareTimeoutMs,\n staticResourceTimeoutMs,\n reloadDelay,\n metaPort,\n moduleDir,\n workerOptions: resolveWorkerOptions(workerOptions),\n maxRequestBytes,\n logger,\n include,\n apiInclude,\n exclude,\n nativeWatcher,\n https: normalizeHttpsOptions(https, moduleDir),\n };\n}\n","import type { PrimaryMessage, WorkerMessage } from './types.js';\n\nexport const enum PrimaryAction {\n /**\n * Health check message, the worker should respond with Pong and the latency information\n */\n Ping = 100,\n}\n\nexport const enum WorkerAction {\n /**\n * Just created\n */\n Created = 200,\n\n /**\n * Ready for tasks\n * - fluxion options are injected\n */\n Ready,\n\n /**\n * Response to Ping, used for health check and latency measurement\n */\n Pong,\n\n /**\n * Runtime telemetry snapshot from worker process.\n */\n Stats,\n}\n\nexport const isPrimaryMessage = (v: PrimaryMessage): v is PrimaryMessage => [PrimaryAction.Ping].includes(v?.type);\n\nexport const isWorkerMessage = (v: WorkerMessage): v is WorkerMessage =>\n [WorkerAction.Pong, WorkerAction.Created, WorkerAction.Ready, WorkerAction.Stats].includes(v?.type);\n","import type cluster from 'node:cluster';\nimport type { PrimaryMessage, WorkerMessage } from './types.js';\n\nexport const sendToPrimary = (message: WorkerMessage) => process.send?.(message);\n\nexport const sendToWorker = (worker: cluster.Worker, message: PrimaryMessage) => worker.send(message);\n","export const DUMMY_BASE_URL = 'http://fluxion.local';\nexport const META_PREFIX = '/_fluxion';\n\nexport const STATIC_HANDLED_FLAG = Symbol.for('fluxion.router.StaticHandled');\nexport const HANDLER_TIMEOUT_FLAG = Symbol.for('fluxion.handlerTimeout');\nexport const MIDDLEWARE_TIMEOUT_FLAG = Symbol.for('fluxion.middlewareTimeout');\n\nexport const STATIC_CONTENT_TYPES: Record<string, string> = {\n '.css': 'text/css; charset=utf-8',\n '.html': 'text/html; charset=utf-8',\n '.ico': 'image/x-icon',\n '.js': 'text/javascript; charset=utf-8',\n '.json': 'application/json; charset=utf-8',\n '.map': 'application/json; charset=utf-8',\n '.png': 'image/png',\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.svg': 'image/svg+xml',\n '.txt': 'text/plain; charset=utf-8',\n '.webp': 'image/webp',\n};\n\nexport const enum HttpCode {\n Ok = 200,\n MethodNotAllowed = 405,\n BadRequest = 400,\n PayloadTooLarge = 413,\n NotFound = 404,\n InternalServerError = 500,\n}\n\nexport const enum HandlerResult {\n NotFound,\n Handled,\n}\n\nexport const enum FluxionModuleType {\n Api,\n StaticResource,\n}\n\nexport const noop = () => {};\n","import type { ServerResponse } from 'node:http';\nimport { HttpCode } from '@/common/consts.js';\n\nexport function sendJson(res: ServerResponse, payload: unknown, statusCode: HttpCode = HttpCode.Ok): void {\n res.statusCode = statusCode;\n res.setHeader('Content-Type', 'application/json; charset=utf-8');\n res.end(JSON.stringify(payload));\n}\n\nexport function safeSendJson(res: ServerResponse, payload: unknown, statusCode: HttpCode = HttpCode.Ok): void {\n if (res.writableEnded) {\n return;\n }\n\n if (res.headersSent) {\n res.end();\n return;\n }\n\n sendJson(res, payload, statusCode);\n}\n","import type { FluxionContext } from '../types.js';\nimport http from 'node:http';\n\nimport { getErrorMessage } from '@/common/logger.js';\nimport { HttpCode, META_PREFIX } from '@/common/consts.js';\nimport { sendJson } from '../http/respond.js';\n\nexport function createPrimaryMetaApiServer(\n cx: Pick<FluxionContext, 'logger' | 'options' | 'router'>,\n getWorkersSnapshot: () => unknown,\n): http.Server {\n const server = http.createServer((req, res) => {\n const method = req.method ?? 'GET';\n\n let pathname = '/';\n try {\n pathname = new URL(req.url ?? '/', 'http://fluxion.local').pathname;\n } catch {\n sendJson(res, { message: 'Bad Request: invalid url' }, HttpCode.BadRequest);\n return;\n }\n\n if (method === 'GET' && pathname === META_PREFIX + '/healthz') {\n sendJson(res, {\n ok: true,\n role: 'primary',\n pid: process.pid,\n now: Date.now(),\n uptimeSeconds: Number(process.uptime().toFixed(3)),\n });\n return;\n }\n\n if (method === 'GET' && pathname === META_PREFIX + '/workers') {\n sendJson(res, {\n ok: true,\n now: Date.now(),\n workers: getWorkersSnapshot(),\n });\n return;\n }\n\n sendJson(res, { message: 'Not Found' }, HttpCode.NotFound);\n });\n\n server.on('listening', () => {\n cx.logger.info('MetaApiStarted', {\n pid: process.pid,\n host: cx.options.host,\n port: cx.options.metaPort,\n prefix: META_PREFIX,\n });\n });\n\n server.on('error', (error) => {\n cx.logger.error('MetaApiError', {\n host: cx.options.host,\n port: cx.options.metaPort,\n error: getErrorMessage(error),\n });\n process.exit(1);\n });\n\n server.listen(cx.options.metaPort, cx.options.host);\n return server;\n}\n","import type { WorkerMessage, WorkerState, WorkerRuntimeStats } from './types.js';\nimport type { FluxionContext } from '../types.js';\nimport os from 'node:os';\nimport cluster from 'node:cluster';\n\nimport { isWorkerMessage, WorkerAction, PrimaryAction } from './consts.js';\nimport { sendToWorker } from './communicate.js';\nimport { createPrimaryMetaApiServer } from './meta-api.js';\n\nconst bytesToMb = (bytes: number) => Number((bytes / 1024 / 1024).toFixed(2));\n\n/**\n * Anti-storm guard shared by proactive recycle (restartWhen) and reactive\n * respawn (crash). A slot may be restarted at most MAX_RESTARTS_PER_WINDOW\n * times within RESTART_WINDOW_MS; further attempts are suppressed and alerted\n * rather than fork-bombing. The window is rolling, so a quiet minute restores\n * capacity — it throttles, never kills a slot permanently.\n */\nconst RESTART_WINDOW_MS = 60_000;\nconst MAX_RESTARTS_PER_WINDOW = 3;\n\nexport function initPrimary(cx: Pick<FluxionContext, 'logger' | 'options' | 'router'>) {\n if (!cluster.isPrimary) {\n $throw('createPrimary should only be called in primary process');\n }\n\n const { workerOptions } = cx.options;\n const restartWhen = workerOptions.restartWhen;\n const cpuCount = Math.max(1, os.cpus().length);\n const workerCount = Math.max(1, Math.min(workerOptions.maxWorkerCount ?? Math.min(2, cpuCount), cpuCount));\n\n cx.logger.info('PrimaryStarted', {\n pid: process.pid,\n workers: workerCount,\n host: cx.options.host,\n port: cx.options.port,\n metaPort: cx.options.metaPort,\n });\n\n const workers = new Map<number, WorkerState>();\n\n // slot -> recent restart timestamps (pruned to RESTART_WINDOW_MS on access).\n // Keyed by the stable 1-based slot, not cluster's worker.id (which changes\n // on every fork), so history survives respawn cycles.\n const restartLog = new Map<number, number[]>();\n\n const restartCountInWindow = (slot: number) => {\n const now = Date.now();\n const log = (restartLog.get(slot) ?? []).filter((t) => now - t < RESTART_WINDOW_MS);\n restartLog.set(slot, log);\n return log.length;\n };\n\n const recordRestart = (slot: number) => {\n const now = Date.now();\n const log = (restartLog.get(slot) ?? []).filter((t) => now - t < RESTART_WINDOW_MS);\n log.push(now);\n restartLog.set(slot, log);\n };\n\n const isStorming = (slot: number) => restartCountInWindow(slot) >= MAX_RESTARTS_PER_WINDOW;\n\n const getWorkersSnapshot = () => {\n return {\n primaryPid: process.pid,\n host: cx.options.host,\n port: cx.options.port,\n metaPort: cx.options.metaPort,\n uptimeSeconds: Number(process.uptime().toFixed(3)),\n workers: Array.from(workers.entries()).map(([workerId, info]) => {\n const { instance } = info;\n const stats = info.lastStats;\n return {\n workerId,\n slot: info.slot,\n pid: info.pid ?? instance.process.pid ?? null,\n state: info.state,\n restartReason: info.restartReason ?? null,\n createdAt: info.createdAt,\n readyAt: info.readyAt ?? null,\n connected: instance.isConnected(),\n dead: instance.isDead(),\n exitedAfterDisconnect: instance.exitedAfterDisconnect,\n lastPongAt: info.lastPongAt ?? null,\n lastRttMs: info.lastRttMs ?? null,\n stats:\n stats === undefined\n ? null\n : {\n at: stats.at,\n uptimeSeconds: stats.uptimeSeconds,\n cpu: stats.cpu,\n memory: {\n ...stats.memory,\n rssMb: bytesToMb(stats.memory.rss),\n heapTotalMb: bytesToMb(stats.memory.heapTotal),\n heapUsedMb: bytesToMb(stats.memory.heapUsed),\n externalMb: bytesToMb(stats.memory.external),\n arrayBuffersMb: bytesToMb(stats.memory.arrayBuffers),\n },\n },\n };\n }),\n };\n };\n\n createPrimaryMetaApiServer(cx, getWorkersSnapshot);\n\n // Recycle a worker via hard kill (SIGTERM). Guards enforce one-at-a-time\n // (so a workload-wide condition rolls restarts instead of nuking the pool)\n // and the anti-storm window. Note: relies on the worker's default SIGTERM\n // disposition to exit; fluxion workers never trap SIGTERM.\n const initiateRecycle = (info: WorkerState, reason: string) => {\n for (const w of workers.values()) {\n if (w.state === 'restarting') return; // another recycle in flight; retried next tick\n }\n if (isStorming(info.slot)) {\n cx.logger.warn('WorkerRecycleSuppressed', {\n slot: info.slot,\n pid: info.pid,\n reason,\n windowMs: RESTART_WINDOW_MS,\n max: MAX_RESTARTS_PER_WINDOW,\n });\n return;\n }\n recordRestart(info.slot);\n info.state = 'restarting';\n info.restartReason = reason;\n cx.logger.warn('WorkerRecycling', { slot: info.slot, pid: info.pid, reason });\n info.instance.kill();\n };\n\n // Evaluate memory + uptime against a fresh stats report. Runs on every\n // Stats message (~every 2s), so reaction latency is bounded by the stats\n // interval, not the ping interval. Infinity thresholds short-circuit here.\n const evaluateResourceConditions = (info: WorkerState, stats: WorkerRuntimeStats) => {\n const rssMb = bytesToMb(stats.memory.rss);\n if (rssMb > restartWhen.memoryUsageGreaterThan) {\n initiateRecycle(info, `memoryUsageGreaterThan: rss ${rssMb}MB > ${restartWhen.memoryUsageGreaterThan}MB`);\n return;\n }\n const uptimeMs = stats.uptimeSeconds * 1000;\n if (uptimeMs > restartWhen.uptimeGreaterThan) {\n initiateRecycle(\n info,\n `uptimeGreaterThan: ${Math.round(uptimeMs / 1000)}s > ${Math.round(restartWhen.uptimeGreaterThan / 1000)}s`,\n );\n }\n };\n\n // Evaluate liveness against the last pong. Runs on the ping tick (5s); a\n // wedged worker stops replying, lastPongAt goes stale past the threshold.\n const evaluateLiveness = (now: number) => {\n for (const info of workers.values()) {\n if (info.state !== 'ready' || info.lastPongAt === undefined) continue;\n const staleMs = now - info.lastPongAt;\n if (staleMs > restartWhen.healthzTimeout) {\n initiateRecycle(\n info,\n `healthzTimeout: no pong for ${Math.round(staleMs / 1000)}s > ${Math.round(restartWhen.healthzTimeout / 1000)}s`,\n );\n }\n }\n };\n\n const spawnSlot = (slot: number) => {\n attachWorker(cluster.fork({ WORKER_ID: String(slot) }), slot);\n };\n\n const attachWorker = (worker: cluster.Worker, slot: number): void => {\n const workerInfo: WorkerState = {\n state: 'creating',\n pid: worker.process.pid,\n slot,\n createdAt: Date.now(),\n instance: worker,\n };\n workers.set(worker.id, workerInfo);\n\n worker.on('message', (raw: WorkerMessage) => {\n if (!isWorkerMessage(raw)) {\n return;\n }\n\n if (raw.type === WorkerAction.Pong) {\n const rtt = Date.now() - raw.sentAt;\n workerInfo.pid = raw.pid;\n workerInfo.lastPongAt = Date.now();\n workerInfo.lastRttMs = rtt;\n return;\n }\n\n if (raw.type === WorkerAction.Ready) {\n workerInfo.state = 'ready';\n workerInfo.pid = raw.pid;\n workerInfo.readyAt = Date.now();\n cx.logger.info('WorkerReady', { workerId: worker.id, slot, pid: raw.pid });\n return;\n }\n\n if (raw.type === WorkerAction.Created) {\n workerInfo.state = 'created';\n workerInfo.pid = raw.pid;\n cx.logger.info('WorkerCreated', { workerId: worker.id, slot, pid: raw.pid });\n return;\n }\n\n if (raw.type === WorkerAction.Stats) {\n workerInfo.pid = raw.pid;\n workerInfo.lastStats = raw.stats;\n if (workerInfo.state === 'ready') {\n evaluateResourceConditions(workerInfo, raw.stats);\n }\n }\n });\n\n worker.on('exit', (code, signal) => {\n const info = workers.get(worker.id);\n workers.delete(worker.id);\n const exitedSlot = info?.slot;\n const expected = info?.state === 'restarting';\n const reason = info?.restartReason ?? null;\n\n cx.logger.warn('WorkerExited', {\n workerId: worker.id,\n slot: exitedSlot ?? null,\n pid: worker.process.pid ?? 'unknown',\n code,\n signal: signal ?? 'none',\n expected,\n reason,\n });\n\n if (exitedSlot === undefined) return;\n\n if (expected) {\n // Proactive recycle: the restart was already counted when initiated.\n spawnSlot(exitedSlot);\n return;\n }\n\n // Unexpected crash: count it, then respawn unless anti-storm trips.\n recordRestart(exitedSlot);\n if (isStorming(exitedSlot)) {\n cx.logger.error('WorkerRespawnSuppressed', {\n slot: exitedSlot,\n windowMs: RESTART_WINDOW_MS,\n max: MAX_RESTARTS_PER_WINDOW,\n });\n return;\n }\n spawnSlot(exitedSlot);\n });\n };\n\n for (let i = 0; i < workerCount; i++) {\n spawnSlot(i + 1);\n }\n\n const pingTimer = setInterval(() => {\n const sentAt = Date.now();\n for (const info of workers.values()) {\n if (!info.instance.isConnected()) {\n continue;\n }\n try {\n sendToWorker(info.instance, { type: PrimaryAction.Ping, sentAt });\n } catch {\n // Ignore transient IPC errors; worker lifecycle events will reconcile state.\n }\n }\n evaluateLiveness(Date.now());\n }, 5000);\n pingTimer.unref();\n}\n","/**\n * For low version of Node.js that does not support `Promise.try`, we can implement it ourselves.\n *\n * Only for async functions.\n */\nexport function PromiseTry<T extends (...args: any[]) => any>(fn: T, ...args: Parameters<T>) {\n return new Promise<ReturnType<T>>((resolve, reject) => {\n // in case `fn` throws synchronously, we catch it and reject the promise\n try {\n const r = fn(...args);\n if (r instanceof Promise) {\n r.then(resolve).catch(reject);\n } else {\n resolve(r);\n }\n } catch (error) {\n reject(error);\n }\n });\n}\n","import type { IncomingMessage } from 'node:http';\n\nexport function getRealIp(req: IncomingMessage): string {\n const forwardedFor = req.headersDistinct['x-forwarded-for'];\n if (forwardedFor) {\n const firstForwarded = forwardedFor[0]?.split(',')[0]?.trim();\n if (firstForwarded && firstForwarded.length > 0) {\n return firstForwarded;\n }\n }\n\n const realIp = req.headersDistinct['x-real-ip']?.[0].trim();\n if (realIp !== undefined) {\n return realIp;\n }\n\n return req.socket.remoteAddress ?? 'unknown';\n}\n\nexport function isTextualContentType(contentType: string | undefined): boolean {\n if (contentType === undefined) {\n return false;\n }\n\n const normalized = contentType.toLowerCase();\n\n return (\n normalized.startsWith('text/') ||\n normalized.includes('json') ||\n normalized.includes('xml') ||\n normalized.includes('x-www-form-urlencoded') ||\n normalized.includes('javascript')\n );\n}\n","import { DUMMY_BASE_URL } from '@/common/consts.js';\n\nexport function toURL(rawUrl: string | undefined): URL | undefined {\n if (rawUrl === undefined) {\n return undefined;\n }\n\n try {\n return new URL(rawUrl, DUMMY_BASE_URL);\n } catch {\n return undefined;\n }\n}\n","export function parseQuery(searchParams: URLSearchParams): Record<string, string | string[]> {\n const query: Record<string, string | string[]> = {};\n\n for (const [key, value] of searchParams.entries()) {\n const existing = query[key];\n\n if (existing === undefined) {\n query[key] = value;\n continue;\n }\n\n if (Array.isArray(existing)) {\n existing.push(value);\n continue;\n }\n\n query[key] = [existing, value];\n }\n\n return query;\n}\n","import type http from 'node:http';\n\nimport { isTextualContentType } from './headers.js';\nimport { parseQuery } from './query.js';\n\nexport interface BodyPreview {\n exists: boolean;\n value?: string;\n bytes: number;\n truncated: boolean;\n}\n\nfunction createRequestBodyTooLargeError(receivedBytes: number, maxBytes: number): NodeJS.ErrnoException {\n const sizeError = new Error(\n `request body too large: ${receivedBytes.toString()} bytes exceeds ${maxBytes.toString()} bytes`,\n ) as NodeJS.ErrnoException;\n\n sizeError.code = 'REQUEST_BODY_TOO_LARGE';\n\n return sizeError;\n}\n\nfunction getHeaderValue(headerValue: string | string[] | undefined): string | undefined {\n return Array.isArray(headerValue) ? headerValue[0] : headerValue;\n}\n\nfunction createEmptyPreview(): BodyPreview {\n return {\n exists: false,\n bytes: 0,\n truncated: false,\n };\n}\n\nfunction createPreview(\n previewBuffer: Buffer,\n totalBytes: number,\n contentType: string | undefined,\n truncated: boolean,\n): BodyPreview {\n if (totalBytes === 0) {\n return createEmptyPreview();\n }\n\n if (isTextualContentType(contentType)) {\n return {\n exists: true,\n value: previewBuffer.toString('utf8'),\n bytes: totalBytes,\n truncated,\n };\n }\n\n return {\n exists: true,\n value: `<binary body: ${totalBytes} bytes>`,\n bytes: totalBytes,\n truncated,\n };\n}\n\nasync function readRequestBodyWithPreview(\n req: http.IncomingMessage,\n method: string,\n maxBytes: number,\n previewMaxBytes = 8192,\n): Promise<{ rawBody: Buffer | undefined; preview: BodyPreview }> {\n if (method === 'GET' || method === 'HEAD') {\n return {\n rawBody: undefined,\n preview: createEmptyPreview(),\n };\n }\n\n if (req.readableEnded) {\n return {\n rawBody: undefined,\n preview: createEmptyPreview(),\n };\n }\n\n const contentLengthRaw = getHeaderValue(req.headers['content-length']);\n const declaredBytes = contentLengthRaw !== undefined ? Number.parseInt(contentLengthRaw, 10) : NaN;\n\n if (Number.isFinite(declaredBytes) && declaredBytes > maxBytes) {\n throw createRequestBodyTooLargeError(declaredBytes, maxBytes);\n }\n\n return new Promise((resolve, reject) => {\n const rawBodyChunks: Buffer[] = [];\n const previewChunks: Buffer[] = [];\n let totalBytes = 0;\n let previewBytes = 0;\n let previewTruncated = false;\n let settled = false;\n\n const cleanup = (): void => {\n req.off('data', onData);\n req.off('end', onEnd);\n req.off('error', onError);\n req.off('aborted', onAborted);\n };\n\n const settle = (action: () => void): void => {\n if (settled) {\n return;\n }\n\n settled = true;\n action();\n };\n\n const onData = (chunk: Buffer | string | Uint8Array): void => {\n const bufferChunk = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);\n totalBytes += bufferChunk.byteLength;\n\n if (totalBytes > maxBytes) {\n cleanup();\n req.resume();\n settle(() => {\n reject(createRequestBodyTooLargeError(totalBytes, maxBytes));\n });\n return;\n }\n\n rawBodyChunks.push(bufferChunk);\n\n if (previewBytes < previewMaxBytes) {\n const remaining = previewMaxBytes - previewBytes;\n const nextSlice = bufferChunk.subarray(0, remaining);\n previewChunks.push(nextSlice);\n previewBytes += nextSlice.length;\n\n if (nextSlice.length < bufferChunk.length) {\n previewTruncated = true;\n }\n } else {\n previewTruncated = true;\n }\n };\n\n const onEnd = (): void => {\n cleanup();\n settle(() => {\n const rawBody = rawBodyChunks.length > 0 ? Buffer.concat(rawBodyChunks) : undefined;\n const previewBuffer = previewChunks.length > 0 ? Buffer.concat(previewChunks) : Buffer.alloc(0);\n\n resolve({\n rawBody,\n preview: createPreview(\n previewBuffer,\n rawBody?.byteLength ?? 0,\n getHeaderValue(req.headers['content-type']),\n previewTruncated,\n ),\n });\n });\n };\n\n const onError = (error: Error): void => {\n cleanup();\n settle(() => {\n reject(error);\n });\n };\n\n const onAborted = (): void => {\n cleanup();\n settle(() => {\n reject(new Error('request aborted while reading body'));\n });\n };\n\n req.on('data', onData);\n req.once('end', onEnd);\n req.once('error', onError);\n req.once('aborted', onAborted);\n });\n}\n\nexport async function parseBody(\n req: http.IncomingMessage,\n method: string,\n maxBytes: number,\n): Promise<{ body: Record<string, any>; preview: BodyPreview }> {\n const { rawBody, preview } = await readRequestBodyWithPreview(req, method, maxBytes);\n\n if (rawBody === undefined || rawBody.byteLength === 0) {\n return {\n body: {},\n preview,\n };\n }\n\n const contentType = getHeaderValue(req.headers['content-type'])?.toLowerCase() ?? '';\n\n if (contentType.includes('json')) {\n const textBody = rawBody.toString('utf8').trim();\n\n if (textBody.length === 0) {\n return {\n body: {},\n preview,\n };\n }\n\n try {\n const parsed = JSON.parse(textBody) as unknown;\n\n if (parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed)) {\n return {\n body: parsed as Record<string, any>,\n preview,\n };\n }\n\n return {\n body: { value: parsed },\n preview,\n };\n } catch {\n return {\n body: { raw: textBody },\n preview,\n };\n }\n }\n\n if (contentType.includes('x-www-form-urlencoded')) {\n return {\n body: parseQuery(new URLSearchParams(rawBody.toString('utf8'))),\n preview,\n };\n }\n\n if (isTextualContentType(contentType)) {\n return {\n body: { raw: rawBody.toString('utf8') },\n preview,\n };\n }\n\n return {\n body: {},\n preview,\n };\n}\n","/**\n * Parse Cookie header string into an object\n */\nexport function parseCookie(cookieHeader: string | undefined): Record<string, string> {\n if (!cookieHeader) {\n return {};\n }\n\n const cookies: Record<string, string> = {};\n const pairs = cookieHeader.split(';');\n\n for (const pair of pairs) {\n const [key, ...valueParts] = pair.split('=');\n if (!key) continue;\n\n const trimmedKey = key.trim();\n const value = valueParts.join('=').trim();\n cookies[trimmedKey] = decodeURIComponent(value);\n }\n\n return cookies;\n}\n\n/**\n * Serialize an object into a Cookie header string\n */\nexport function serializeCookie(name: string, value: string, options?: {\n maxAge?: number;\n expires?: Date;\n domain?: string;\n path?: string;\n secure?: boolean;\n httpOnly?: boolean;\n sameSite?: 'strict' | 'lax' | 'none';\n}): string {\n let cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}`;\n\n if (options) {\n if (options.maxAge !== undefined) {\n cookie += `; Max-Age=${options.maxAge}`;\n }\n if (options.expires) {\n cookie += `; Expires=${options.expires.toUTCString()}`;\n }\n if (options.domain) {\n cookie += `; Domain=${options.domain}`;\n }\n if (options.path) {\n cookie += `; Path=${options.path}`;\n }\n if (options.secure) {\n cookie += '; Secure';\n }\n if (options.httpOnly) {\n cookie += '; HttpOnly';\n }\n if (options.sameSite) {\n cookie += `; SameSite=${options.sameSite}`;\n }\n }\n\n return cookie;\n}\n","import http from 'node:http';\nimport https from 'node:https';\n\nimport type { FluxionContext, NormalizedRequest } from '../types.js';\nimport { $keys } from '@/common/native.js';\nimport {\n HttpCode,\n HANDLER_TIMEOUT_FLAG,\n META_PREFIX,\n STATIC_HANDLED_FLAG,\n FluxionModuleType,\n MIDDLEWARE_TIMEOUT_FLAG,\n} from '@/common/consts.js';\nimport { PromiseTry } from '@/common/promise-try.js';\nimport { getErrorMessage } from '@/common/logger.js';\n\nimport { getRealIp } from '../http/headers.js';\nimport { toURL } from '../http/request.js';\nimport { safeSendJson } from '../http/respond.js';\nimport { parseBody, type BodyPreview } from '../http/body.js';\nimport { parseQuery } from '../http/query.js';\nimport { parseCookie } from '../http/cookie.js';\n\nconst waiter = (mainPromise: Promise<any>, timeoutMs: number, flag: symbol) =>\n Promise.race([mainPromise, new Promise((r) => setTimeout(() => r(flag), timeoutMs))]);\n\nexport function createWorkerServer(cx: FluxionContext): http.Server | https.Server {\n const requestHandler = async (req: http.IncomingMessage, res: http.ServerResponse) => {\n const method = req.method ?? 'GET';\n const ip = getRealIp(req);\n const url = toURL(req.url);\n if (url === undefined) {\n safeSendJson(res, { message: 'Bad Request: req.url is undefined' }, HttpCode.BadRequest);\n return;\n }\n\n const normalized: NormalizedRequest = {\n method,\n ip,\n url,\n query: parseQuery(url.searchParams),\n body: {},\n headers: req.headers,\n cookie: parseCookie(req.headers.cookie as string | undefined),\n };\n\n let bodyPreview: BodyPreview = {\n exists: false,\n bytes: 0,\n truncated: false,\n };\n\n cx.logger.info('Req', { method, ip, path: url.pathname });\n\n const start = performance.now();\n res.once('finish', () => {\n const fields: Record<string, unknown> = {\n workerId: process.env.WORKER_ID ?? '[primary]',\n method,\n ip,\n path: url.pathname,\n status: res.statusCode,\n duration: (performance.now() - start).toFixed(4),\n };\n\n if ($keys(normalized.query).length > 0) {\n fields.query = normalized.query;\n }\n\n if (bodyPreview.exists) {\n fields.body = bodyPreview.value;\n fields.bodyBytes = bodyPreview.bytes;\n fields.bodyTruncated = bodyPreview.truncated;\n }\n\n cx.logger.info('Res', fields);\n });\n\n // * Start request handling\n try {\n if (normalized.url.pathname.startsWith(META_PREFIX + '/')) {\n safeSendJson(res, { message: `Not Found` }, HttpCode.NotFound);\n return;\n }\n\n const parsed = await parseBody(req, normalized.method, cx.options.maxRequestBytes);\n normalized.body = parsed.body;\n bodyPreview = parsed.preview;\n\n const m = await cx.router.getModule(url);\n if (!m) {\n safeSendJson(res, { message: 'Not Found' }, HttpCode.NotFound);\n return;\n }\n\n if (req.method && m.methods && !m.methods.includes(req.method)) {\n safeSendJson(res, { message: 'Method Not Allowed' }, HttpCode.MethodNotAllowed);\n return;\n }\n\n const timeoutMs =\n m.type === FluxionModuleType.Api\n ? (m.handlerTimeoutMs ?? cx.options.handlerTimeoutMs)\n : cx.options.staticResourceTimeoutMs;\n\n // Middleware execution\n if (m.middlewares) {\n for (let i = 0; i < m.middlewares.length; i++) {\n const result = await waiter(\n PromiseTry(m.middlewares[i], normalized, req, res),\n cx.options.middlewareTimeoutMs,\n MIDDLEWARE_TIMEOUT_FLAG,\n );\n\n if (result === MIDDLEWARE_TIMEOUT_FLAG) {\n cx.logger.warn('MiddlewareTimeout', {\n method: normalized.method,\n ip: normalized.ip,\n });\n safeSendJson(res, { message: 'Internal Server Error' }, HttpCode.InternalServerError);\n return;\n }\n if (res.writableEnded) {\n return;\n }\n if (res.headersSent) {\n res.end();\n return;\n }\n }\n }\n\n const result = await waiter(PromiseTry(m.handler, normalized, req, res), timeoutMs, HANDLER_TIMEOUT_FLAG);\n\n if (result === HANDLER_TIMEOUT_FLAG) {\n cx.logger.warn('HandlerTimeout', {\n method: normalized.method,\n ip: normalized.ip,\n });\n safeSendJson(res, { message: 'Handler timed out' }, HttpCode.InternalServerError);\n return;\n }\n\n if (result !== STATIC_HANDLED_FLAG) {\n safeSendJson(res, result);\n }\n } catch (error) {\n cx.logger.error('RequestFailed', {\n method: normalized.method,\n ip: normalized.ip,\n path: normalized.url.pathname,\n error: getErrorMessage(error),\n });\n\n if ((error as NodeJS.ErrnoException).code === 'REQUEST_BODY_TOO_LARGE') {\n safeSendJson(res, { message: getErrorMessage(error) }, HttpCode.PayloadTooLarge);\n } else {\n safeSendJson(res, { message: getErrorMessage(error) }, HttpCode.InternalServerError);\n }\n }\n };\n\n const server = cx.options.https\n ? https.createServer(\n {\n key: cx.options.https.key,\n cert: cx.options.https.cert,\n ca: cx.options.https.ca,\n },\n requestHandler,\n )\n : http.createServer(requestHandler);\n\n server.on('close', () => {\n cx.logger.info('ServerClosed', {\n host: cx.options.host,\n port: cx.options.port,\n });\n });\n\n server.listen(cx.options.port, cx.options.host, () => {\n cx.logger.info('ServerStarted', {\n pid: process.pid,\n protocol: cx.options.https ? 'https' : 'http',\n host: cx.options.host,\n port: cx.options.port,\n });\n cx.logger.info('DynamicDirectory', { directory: cx.options.dir });\n });\n\n server.on('error', (error) => {\n cx.logger.error('ServerError', {\n error: getErrorMessage(error),\n });\n });\n\n return server;\n}\n","import type { PrimaryMessage } from './types.js';\nimport type { FluxionContext } from '../types.js';\nimport cluster from 'node:cluster';\n\nimport { getErrorMessage } from '@/common/logger.js';\nimport { WorkerAction, PrimaryAction, isPrimaryMessage } from './consts.js';\nimport { sendToPrimary } from './communicate.js';\nimport { createWorkerServer } from './server.js';\n\nconst startStatsReporter = () => {\n let previousCpuUsage = process.cpuUsage();\n let previousAt = Date.now();\n\n const interval = setInterval(() => {\n const now = Date.now();\n const elapsedMicros = Math.max(1, (now - previousAt) * 1000);\n const cpuDelta = process.cpuUsage(previousCpuUsage);\n const cpuPercent = Number((((cpuDelta.user + cpuDelta.system) / elapsedMicros) * 100).toFixed(2));\n\n previousCpuUsage = process.cpuUsage();\n previousAt = now;\n\n const memoryUsage = process.memoryUsage();\n sendToPrimary({\n type: WorkerAction.Stats,\n pid: process.pid,\n stats: {\n at: now,\n pid: process.pid,\n uptimeSeconds: Number(process.uptime().toFixed(3)),\n cpu: {\n userMicros: cpuDelta.user,\n systemMicros: cpuDelta.system,\n percent: cpuPercent,\n },\n memory: {\n rss: memoryUsage.rss,\n heapTotal: memoryUsage.heapTotal,\n heapUsed: memoryUsage.heapUsed,\n external: memoryUsage.external,\n arrayBuffers: memoryUsage.arrayBuffers,\n },\n },\n });\n }, 2000);\n\n interval.unref();\n};\n\nexport function initWorker(cx: FluxionContext) {\n if (cluster.isPrimary) {\n $throw('createWorker should only be called in worker process');\n }\n\n process.on('message', (raw: PrimaryMessage) => {\n if (!isPrimaryMessage(raw)) {\n return;\n }\n\n if (raw.type === PrimaryAction.Ping) {\n sendToPrimary({ type: WorkerAction.Pong, pid: process.pid, sentAt: raw.sentAt, receivedAt: Date.now() });\n return;\n }\n });\n\n sendToPrimary({ type: WorkerAction.Created, pid: process.pid });\n startStatsReporter();\n\n try {\n createWorkerServer(cx);\n sendToPrimary({ type: WorkerAction.Ready, pid: process.pid });\n } catch (e) {\n cx.logger.error('WorkerBootstrapFailed', {\n pid: process.pid,\n error: getErrorMessage(e),\n });\n process.exit(1);\n }\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport type { FluxionContext } from '../types.js';\n\nexport type WatcherContext = Pick<FluxionContext, 'options' | 'logger' | 'router'>;\n\nexport abstract class FluxionWatcherBase {\n protected readonly cx: WatcherContext;\n\n private timer: NodeJS.Timeout | null = null;\n private readonly filesChanged = new Map<string, string>();\n\n constructor(cx: WatcherContext) {\n this.cx = cx;\n }\n\n /**\n * Recursively register all files in the options directory.\n */\n protected async init(): Promise<this> {\n const dir = this.cx.options.dir;\n if (!fs.existsSync(dir)) {\n this.cx.logger.warn(`Directory does not exist: ${dir}`);\n return this;\n }\n\n const registerList: Array<Promise<void>> = [];\n\n const registerRecursive = (absoluteDir: string, relativeDir: string) => {\n const entries = fs.readdirSync(absoluteDir, { withFileTypes: true });\n\n for (let i = 0; i < entries.length; i++) {\n const entry = entries[i];\n const absolutePath = path.join(absoluteDir, entry.name);\n const relativePath = path.join(relativeDir, entry.name);\n\n if (entry.isDirectory()) {\n registerRecursive(absolutePath, relativePath);\n } else if (entry.isFile()) {\n const p = this.cx.router.register(absolutePath, relativePath).catch((e) => {\n this.cx.logger.error(`Error registering file ${relativePath}: ${(e as Error).message}`);\n });\n registerList.push(p);\n }\n }\n };\n\n registerRecursive(dir, '');\n await Promise.all(registerList);\n\n this.cx.logger.info(`Initial registration complete for directory: ${dir}`);\n return this;\n }\n\n protected queueUp(absolutePath: string, relativePath: string): void {\n this.filesChanged.set(absolutePath, relativePath);\n if (this.timer) {\n return;\n }\n\n this.timer = setTimeout(async () => {\n const promises = [...this.filesChanged].map(([absolutePath, relativePath]) =>\n this.cx.router\n .register(absolutePath, relativePath)\n .catch((err) => this.cx.logger.error(`Error refreshing handlers: ${(err as Error).message}`))\n .finally(() => this.filesChanged.delete(absolutePath)),\n );\n await Promise.all(promises);\n this.timer = null;\n }, this.cx.options.reloadDelay);\n }\n\n protected stopCore(): void {\n if (this.timer) {\n clearTimeout(this.timer);\n this.timer = null;\n }\n\n this.filesChanged.clear();\n }\n\n abstract start(): Promise<this>;\n abstract stop(): this;\n}\n","import path from 'node:path';\nimport type { FSWatcher } from 'chokidar';\nimport chokidar from 'chokidar';\n\nimport { FluxionWatcherBase, type WatcherContext } from './base.js';\n\nexport class FluxionChokidarWatcher extends FluxionWatcherBase {\n private watcher: FSWatcher | null = null;\n\n constructor(cx: WatcherContext) {\n super(cx);\n }\n\n /**\n * Start watching files with chokidar.\n *\n * Using chokidar provides:\n * - Cross-platform recursive watch support (including Linux/CentOS)\n * - Better event handling and stability\n * - Automatic resource management\n */\n async start(): Promise<this> {\n this.stop();\n await this.init();\n\n const dir = this.cx.options.dir;\n this.watcher = chokidar\n .watch(dir, {\n persistent: true, // Keep the process running\n ignoreInitial: true, // Don't emit 'add' events for initial scan\n usePolling: false, // Use polling as fallback (helps with some network drives)\n awaitWriteFinish: {\n stabilityThreshold: 100,\n pollInterval: 50,\n }, // Atomic writes handling\n })\n .on('all', (_event, absolutePath) => {\n if (!absolutePath) {\n return;\n }\n\n // & `filename` is absolute(Maybe because of watching an absolute path `dir`)\n this.queueUp(absolutePath, path.relative(dir, absolutePath));\n })\n .on('error', (err: unknown) => {\n const error = err instanceof Error ? err : new Error(String(err));\n this.cx.logger.error(`Watcher error: ${error.message}`);\n this.cx.logger.error(`Restarting watcher...`);\n this.start();\n })\n .on('ready', () => {\n this.cx.logger.info(`Watcher ready and watching directory: ${dir}`);\n });\n\n this.cx.logger.info(`Watcher started on directory: ${dir}`);\n return this;\n }\n\n stop(): this {\n if (this.watcher) {\n void this.watcher.close();\n this.watcher = null;\n }\n\n this.stopCore();\n return this;\n }\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\n\nimport { FluxionWatcherBase, type WatcherContext } from './base.js';\n\nexport class FluxionNativeWatcher extends FluxionWatcherBase {\n private watcher: fs.FSWatcher | null = null;\n\n constructor(cx: WatcherContext) {\n super(cx);\n }\n\n /**\n * Since all actions are mapped to `rename` and `change` (WatchEventType).\n *\n * We could only record every file and reload them all.\n */\n async start(): Promise<this> {\n this.stop();\n await this.init();\n\n const dir = this.cx.options.dir;\n this.watcher = fs\n .watch(dir, { recursive: true }, (_eventType, relativePath) => {\n if (!relativePath) {\n return;\n }\n\n // & Unlike chokidar, `filename` here is relativePath\n this.queueUp(path.join(dir, relativePath), relativePath);\n })\n .on('error', (err) => {\n this.cx.logger.error(`Watcher error: ${err.message}`);\n this.cx.logger.error(`Restarting watcher...`);\n this.start();\n });\n\n this.cx.logger.info(`Watcher started on directory: ${dir}`);\n return this;\n }\n\n stop(): this {\n if (this.watcher) {\n this.watcher.close();\n this.watcher = null;\n }\n\n this.stopCore();\n return this;\n }\n}\n","import type { FluxionContext, FluxionModuleWithType } from '@/types.js';\nimport { static_cast } from 'type-narrow';\nimport { FluxionModuleType } from './consts';\n\nfunction isFluxionModule(cx: Pick<FluxionContext, 'options' | 'logger'>, o: unknown): o is FluxionModuleWithType {\n if (typeof o !== 'object' || o === null) {\n return false;\n }\n\n static_cast<FluxionModuleWithType>(o);\n\n if (typeof o.handler !== 'function') {\n cx.logger.error(`handler must be a function`);\n return false;\n }\n\n if (o.disposer !== undefined && typeof o.disposer !== 'function') {\n cx.logger.error(`disposer must be a function if provided`);\n return false;\n }\n\n const ms = o.handlerTimeoutMs;\n if (ms !== undefined && (!Number.isSafeInteger(ms) || ms < 100)) {\n cx.logger.error(`handlerTimeoutMs must be an integer >= 100 if provided`);\n return false;\n }\n\n if (o.type !== FluxionModuleType.Api) {\n cx.logger.error(`You must use defineFluxionModule to create module`);\n return false;\n }\n\n return true;\n}\n\nexport function loadFluxionModule(\n cx: Pick<FluxionContext, 'options' | 'logger'>,\n fullpath: string,\n): FluxionModuleWithType {\n delete require.cache[fullpath];\n let m = require(fullpath);\n if (isFluxionModule(cx, m)) {\n } else if (isFluxionModule(cx, m.default)) {\n m = m.default;\n } else {\n $throw(`Invalid handler module '${fullpath}', make sure it satisfies defineFluxionModule(...) helper`);\n }\n\n return m;\n}\n","import type { FluxionContext, FluxionModuleWithType } from '../types.js';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { minimatch } from 'minimatch';\nimport { FluxionModuleType, STATIC_CONTENT_TYPES, STATIC_HANDLED_FLAG } from '@/common/consts.js';\nimport { loadFluxionModule } from '@/common/injector.js';\nimport { cctl } from '@/common/color.js';\nimport { PromiseTry } from '@/common/promise-try.js';\n\nexport class FluxionRouter {\n private readonly cx: Pick<FluxionContext, 'options' | 'logger'>;\n private readonly handlers: Map<string, FluxionModuleWithType> = new Map();\n\n constructor(cx: Pick<FluxionContext, 'options' | 'logger'>) {\n this.cx = cx;\n }\n\n makeStaticResource(filepath: string): FluxionModuleWithType {\n return {\n type: FluxionModuleType.StaticResource,\n handler: async (normalized, _req, res) => {\n if (normalized.method !== 'GET' && normalized.method !== 'HEAD') {\n res.statusCode = 405;\n res.setHeader('Allow', 'GET, HEAD');\n res.end();\n return;\n }\n\n if (!fs.existsSync(filepath)) {\n res.statusCode = 404;\n res.end('Not Found');\n return;\n }\n\n const stat = fs.statSync(filepath);\n if (!stat.isFile()) {\n res.statusCode = 404;\n res.end('Not Found');\n return;\n }\n\n const extension = path.extname(filepath).toLowerCase();\n const contentType = STATIC_CONTENT_TYPES[extension] ?? 'application/octet-stream';\n\n res.statusCode = 200;\n res.setHeader('Content-Type', contentType);\n res.setHeader('Content-Length', String(stat.size));\n\n if (normalized.method === 'HEAD') {\n res.end();\n return;\n }\n\n return new Promise<symbol>((resolve, reject) => {\n const stream = fs.createReadStream(filepath);\n stream.on('error', reject);\n stream.on('end', () => resolve(STATIC_HANDLED_FLAG));\n stream.pipe(res);\n });\n },\n };\n }\n\n /**\n * File registration logic with fast-glob pattern matching:\n * 1. Check if the path exists, if not, delete the handler;\n * 2. If file doesn't match include patterns, skip registration;\n * 3. If file matches exclude patterns, skip registration;\n * 4. If file matches apiInclude patterns, register as API handler;\n * 5. Otherwise, register as static resource.\n */\n async register(absolutePath: string, relativePath: string) {\n // Get the disposer and delete\n const disposer = this.handlers.get(relativePath)?.disposer;\n if (disposer) {\n await PromiseTry(disposer);\n }\n\n // # Delete\n if (!fs.existsSync(absolutePath)) {\n this.handlers.delete(relativePath);\n // & Watcher will emit recursively, so there is no need to use this.remove(rp);\n this.cx.logger.info(`${cctl.red}Deleted ${cctl.reset} - ${relativePath}`);\n return;\n }\n\n // Step 2: Check if file matches include patterns (default: all files)\n // If not matching, skip registration\n const matchesInclude = this.cx.options.include.some((pattern) => minimatch(relativePath, pattern));\n if (!matchesInclude) {\n this.handlers.delete(relativePath);\n this.cx.logger.info(`${cctl.yellow}Skipped ${cctl.reset} - ${relativePath}`);\n return;\n }\n\n // Step 3: Check if file matches exclude patterns\n // If matching, skip registration\n const matchesExclude = this.cx.options.exclude.some((pattern) => minimatch(relativePath, pattern));\n if (matchesExclude) {\n this.handlers.delete(relativePath);\n this.cx.logger.info(`${cctl.orange}Excluded${cctl.reset} - ${relativePath}`);\n return;\n }\n\n // Step 4 & 5: Check if file matches apiInclude patterns\n // If matching, register as API handler; otherwise as static resource\n const matchesApiInclude = this.cx.options.apiInclude.some((pattern) => minimatch(relativePath, pattern));\n if (matchesApiInclude) {\n const m = loadFluxionModule(this.cx, absolutePath);\n this.handlers.set(relativePath, m);\n this.cx.logger.info(`${cctl.green}Api ${cctl.reset} - ${relativePath}`);\n return;\n }\n\n // register as static resource\n this.handlers.set(relativePath, this.makeStaticResource(relativePath));\n this.cx.logger.info(`${cctl.brightBlue}Static ${cctl.reset} - ${relativePath}`);\n }\n\n getModule(url: URL): FluxionModuleWithType | undefined {\n const relativePath = url.pathname.replace(/^[/]+/, '').replace(/[/]+$/, '');\n return this.handlers.get(relativePath);\n }\n\n /**\n * If the path points to a file, it would be simple.\n * But if it's a directory, we need to find all registered handlers under this directory and remove them.\n *\n * @param somepath\n * @deprecated\n */\n remove(somepath: string): void {\n if (this.handlers.has(somepath)) {\n this.handlers.delete(somepath);\n this.cx.logger.info(`${cctl.red}Deleted ${cctl.reset} - ${somepath}`);\n }\n // & Not in handler map -> It is a directory\n const prefix = somepath.endsWith('/') ? somepath : somepath + '/';\n for (const key of this.handlers.keys()) {\n if (key.startsWith(prefix)) {\n this.handlers.delete(key);\n this.cx.logger.info(`${cctl.red}Deleted ${cctl.reset} - ${key}`);\n }\n }\n }\n}\n","import type { FluxionContext, FluxionOptions } from './types.js';\n\nimport { createLogger, createWorkerLogger } from '@/common/logger.js';\nimport { normalizeOptions } from './http/options.js';\nimport cluster from 'node:cluster';\nimport { initPrimary } from './cluster/primary.js';\nimport { initWorker } from './cluster/worker.js';\nimport { FluxionChokidarWatcher } from './watcher/chokidar.js';\nimport { FluxionNativeWatcher } from './watcher/native.js';\nimport { FluxionRouter } from './router/index.js';\n\nexport async function fluxion(options: FluxionOptions) {\n const context = { options: normalizeOptions(options) } as FluxionContext;\n\n context.logger = createLogger(context as Pick<FluxionContext, 'options'>);\n context.router = new FluxionRouter(context as Pick<FluxionContext, 'options' | 'logger'>);\n\n if (cluster.isPrimary) {\n initPrimary(context);\n } else {\n // Replace logger with worker logger that prefixes PID\n context.logger = createWorkerLogger(context.logger, process.pid);\n // Only worker creates the watcher\n const Watcher = context.options.nativeWatcher ? FluxionNativeWatcher : FluxionChokidarWatcher;\n context.watcher = await new Watcher(context as Pick<FluxionContext, 'options' | 'logger' | 'router'>).start();\n initWorker(context);\n }\n}\n","import type { FluxionDispose, FluxionHandler, FluxionModule, FluxionModuleWithType } from './types.js';\nimport { fluxion } from './fluxion.js';\nimport { FluxionModuleType, noop } from './common/consts.js';\n\nexport { fluxion };\nexport type { FluxionDispose, FluxionHandler, FluxionModule as FluxionHandlerModule, FluxionOptions } from './types.js';\n\n/**\n * Use handler function and optional disposer function to define a Fluxion module.\n * @param handler Main function that handles request and response instances\n * @param disposer Deal with resource cleanup when the server is about to close\n */\nexport function defineFluxionModule(handler: FluxionHandler, disposer?: FluxionDispose): FluxionModuleWithType;\n/**\n * Provides type safety for defining Fluxion modules.\n */\nexport function defineFluxionModule(fluxionModule: FluxionModule): FluxionModuleWithType;\nexport function defineFluxionModule(\n a: FluxionModule | FluxionHandler,\n disposer: FluxionDispose = noop,\n): FluxionModuleWithType {\n if (typeof a === 'function') {\n if (typeof disposer !== 'function') {\n $throw(`Invalid disposer, expected a function but got ${typeof disposer}`);\n }\n return { handler: a, disposer, type: FluxionModuleType.Api };\n }\n\n if (typeof a !== 'object' || a === null) {\n $throw(`Invalid argument, expected a FluxionModule object or a handler function, but got ${typeof a}`);\n }\n\n if (typeof a.handler !== 'function') {\n $throw(`Invalid FluxionModule, \"handler\" must be a function`);\n }\n\n if (a.disposer !== undefined && typeof a.disposer !== 'function') {\n $throw(`Invalid FluxionModule, \"disposer\" must be a function if provided`);\n }\n\n if (a.methods !== undefined && (!Array.isArray(a.methods) || a.methods.some((v) => typeof v !== 'string'))) {\n $throw(`Invalid FluxionModule, \"methods\" must be an array of strings if provided`);\n }\n\n if (\n a.middlewares !== undefined &&\n (!Array.isArray(a.middlewares) || a.middlewares.some((v) => typeof v !== 'function'))\n ) {\n $throw(`Invalid FluxionModule, \"middlewares\" must be an array of functions if provided`);\n }\n\n return { ...a, type: FluxionModuleType.Api };\n}\n\nif (process.env.NODE_ENV !== 'production') {\n globalThis.$throw = (message: string) => {\n throw new Error('[fluxion error]' + message);\n };\n\n const int = (n: string | undefined, defaultValue: number): number => {\n if (n === undefined) {\n return defaultValue;\n }\n const parsed = Number.parseInt(n, 10);\n if (Number.isNaN(parsed)) {\n return defaultValue;\n }\n return parsed;\n };\n\n fluxion({\n dir: process.env.DYNAMIC_DIRECTORY ?? 'dynamicDirectory',\n host: process.env.HOST ?? 'localhost',\n port: int(process.env.PORT, 9000),\n metaPort: int(process.env.META_PORT, 9001),\n reloadDelay: process.env.RELOAD_DELAY ? Number.parseInt(process.env.RELOAD_DELAY, 10) : undefined,\n workerOptions: {\n maxWorkerCount: 4,\n },\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["http","https","http","fs","path","path","fs","path","fs","path","cluster"],"sources":["../src/common/dtm.ts","../src/common/native.ts","../src/common/color.ts","../src/common/logger.ts","../src/http/options.ts","../src/cluster/consts.ts","../src/cluster/communicate.ts","../src/common/consts.ts","../src/http/respond.ts","../src/cluster/meta-api.ts","../src/cluster/primary.ts","../src/common/promise-try.ts","../src/http/headers.ts","../src/http/request.ts","../src/http/query.ts","../src/http/body.ts","../src/http/cookie.ts","../src/cluster/server.ts","../src/cluster/worker.ts","../src/watcher/base.ts","../src/watcher/chokidar.ts","../src/watcher/native.ts","../src/common/injector.ts","../src/router/index.ts","../src/fluxion.ts","../src/index.ts"],"sourcesContent":["export function dtm(dt = new Date()) {\n const y = dt.getFullYear();\n const m = String(dt.getMonth() + 1).padStart(2, '0');\n const d = String(dt.getDate()).padStart(2, '0');\n const hh = String(dt.getHours()).padStart(2, '0');\n const mm = String(dt.getMinutes()).padStart(2, '0');\n const ss = String(dt.getSeconds()).padStart(2, '0');\n const ms = String(dt.getMilliseconds()).padStart(3, '0');\n return `${y}.${m}.${d} ${hh}:${mm}:${ss}.${ms}`;\n}\n","export const $stringify = JSON.stringify;\n\nexport const $keys = Object.keys;\n","const useColor = process.env.FLUXION_COLORS !== '0';\n\n/**\n * Color Control Characters for Terminal (cctl)\n */\nexport namespace cctl {\n export const reset = useColor ? '\\x1b[0m' : '';\n export const bold = useColor ? '\\x1b[1m' : '';\n export const dim = useColor ? '\\x1b[2m' : '';\n export const italic = useColor ? '\\x1b[3m' : '';\n export const underline = useColor ? '\\x1b[4m' : '';\n export const blink = useColor ? '\\x1b[5m' : '';\n export const inverse = useColor ? '\\x1b[7m' : '';\n\n export const black = useColor ? '\\x1b[30m' : '';\n export const red = useColor ? '\\x1b[31m' : '';\n export const green = useColor ? '\\x1b[32m' : '';\n export const yellow = useColor ? '\\x1b[33m' : '';\n export const blue = useColor ? '\\x1b[34m' : '';\n export const magenta = useColor ? '\\x1b[35m' : '';\n export const cyan = useColor ? '\\x1b[36m' : '';\n export const white = useColor ? '\\x1b[37m' : '';\n\n export const brightBlack = useColor ? '\\x1b[90m' : '';\n export const brightRed = useColor ? '\\x1b[91m' : '';\n export const brightGreen = useColor ? '\\x1b[92m' : '';\n export const brightYellow = useColor ? '\\x1b[93m' : '';\n export const brightBlue = useColor ? '\\x1b[94m' : '';\n export const brightMagenta = useColor ? '\\x1b[95m' : '';\n export const brightCyan = useColor ? '\\x1b[96m' : '';\n export const brightWhite = useColor ? '\\x1b[97m' : '';\n\n export const bgBlack = useColor ? '\\x1b[40m' : '';\n export const bgRed = useColor ? '\\x1b[41m' : '';\n export const bgGreen = useColor ? '\\x1b[42m' : '';\n export const bgYellow = useColor ? '\\x1b[43m' : '';\n export const bgBlue = useColor ? '\\x1b[44m' : '';\n export const bgMagenta = useColor ? '\\x1b[45m' : '';\n export const bgCyan = useColor ? '\\x1b[46m' : '';\n export const bgWhite = useColor ? '\\x1b[47m' : '';\n\n export const bgBrightBlack = useColor ? '\\x1b[100m' : '';\n export const bgBrightRed = useColor ? '\\x1b[101m' : '';\n export const bgBrightGreen = useColor ? '\\x1b[102m' : '';\n export const bgBrightYellow = useColor ? '\\x1b[103m' : '';\n export const bgBrightBlue = useColor ? '\\x1b[104m' : '';\n export const bgBrightMagenta = useColor ? '\\x1b[105m' : '';\n export const bgBrightCyan = useColor ? '\\x1b[106m' : '';\n export const bgBrightWhite = useColor ? '\\x1b[107m' : '';\n\n // 'rgb(225, 16, 248)';\n export const purple = useColor ? '\\x1b[38;2;225;16;248m' : '';\n // 'rgb(248, 147, 16)';\n export const orange = useColor ? '\\x1b[38;2;248;147;16m' : '';\n export const darkGreen = useColor ? '\\x1b[38;2;22;101;52m' : '';\n export const claude = useColor ? '\\x1b[38;2;217;119;87m' : '';\n export const deepseek = useColor ? '\\x1b[38;2;57;100;254m' : '';\n export const gpt = useColor ? '\\x1b[38;2;41;60;77m' : '';\n}\n","import type { FluxionContext } from '@/types.js';\nimport type { otherstring } from '@/global.js';\n\nimport { dtm } from './dtm.js';\nimport { $keys, $stringify } from './native.js';\nimport { cctl } from './color.js';\n\ntype LogLevel = 'INFO' | 'WARN' | 'ERROR' | 'SUCC' | 'DEBUG' | 'VERBOSE' | otherstring;\n\ninterface LogEntry {\n timestamp: string;\n level: LogLevel;\n event: string;\n message?: string;\n [key: string]: unknown;\n}\n\nexport type LoggerOption = 'one-line' | 'json-line' | FluxionLoggerFn;\n\nexport type FluxionLoggerFn = (entry: LogEntry) => void;\n\nexport interface FluxionLogger {\n /**\n * [WARN] We assert that `fields` is an object or undefined.\n */\n write(level: LogLevel, event: string, fields?: object): void;\n info(event: string, fields?: object): void;\n warn(event: string, fields?: object): void;\n error(event: string, fields?: object): void;\n succ(event: string, fields?: object): void;\n debug(event: string, fields?: object): void;\n verbose(event: string, fields?: object): void;\n}\n\nconst safeStringify = (value: unknown): string => {\n try {\n return $stringify(value);\n } catch {\n return '[unserializable]';\n }\n};\n\nconst ColoredLevels: Record<LogLevel, string> = {\n INFO: `${cctl.cyan}INFO${cctl.reset}`,\n WARN: `${cctl.orange}WARN${cctl.reset}`,\n ERROR: `${cctl.red}ERROR${cctl.reset}`,\n SUCC: `${cctl.green}SUCC${cctl.reset}`,\n DEBUG: `${cctl.blue}DEBUG${cctl.reset}`,\n VERBOSE: `${cctl.purple}VERBOSE${cctl.reset}`,\n};\n\nexport const oneLineLogger: FluxionLoggerFn = (entry: LogEntry) => {\n const { level: rawLevel, timestamp: rawTimestamp, event: rawEvent, message: rawMessage, ...fields } = entry;\n\n const timestamp = `${cctl.darkGreen}[${rawTimestamp}]${cctl.reset}`;\n const level = ColoredLevels[rawLevel] ?? rawLevel;\n const body = rawMessage ?? rawEvent;\n const fieldsText = $keys(fields).length > 0 ? `${cctl.dim}${safeStringify(fields)}${cctl.reset}` : '';\n\n // eslint-disable-next-line @typescript-eslint/no-console\n console.log(`${timestamp} ${level} ${body}${fieldsText}`);\n};\n\n/**\n * & Logger Options here is checked by normalizeOptions function.\n */\nfunction resolveLoggerSink(cx: Pick<FluxionContext, 'options'>): FluxionLoggerFn {\n const loggerOption = cx.options.logger;\n if (loggerOption === undefined || loggerOption === 'one-line') {\n return oneLineLogger;\n }\n\n if (loggerOption === 'json-line') {\n // eslint-disable-next-line @typescript-eslint/no-console\n return (entry: LogEntry) => console.log(safeStringify(entry));\n }\n\n return loggerOption;\n}\n\nexport function createLogger(cx: Pick<FluxionContext, 'options'>): FluxionLogger {\n const sink = resolveLoggerSink(cx);\n\n const logger: FluxionLogger = {\n write(level: LogLevel, event: string, fields: Record<string, unknown> = {}): void {\n const entry: LogEntry = {\n ...fields,\n timestamp: dtm(),\n level,\n event,\n };\n\n try {\n sink(entry);\n } catch {\n // Ignore logger sink failures to avoid breaking request handling.\n }\n },\n info(event: string, fields?: Record<string, unknown>): void {\n this.write('INFO', event, fields);\n },\n warn(event: string, fields?: Record<string, unknown>): void {\n this.write('WARN', event, fields);\n },\n error(event: string, fields?: Record<string, unknown>): void {\n this.write('ERROR', event, fields);\n },\n succ(event: string, fields?: Record<string, unknown>): void {\n this.write('SUCC', event, fields);\n },\n debug(event: string, fields?: Record<string, unknown>): void {\n this.write('DEBUG', event, fields);\n },\n verbose(event: string, fields?: Record<string, unknown>): void {\n this.write('VERBOSE', event, fields);\n },\n };\n\n return logger;\n}\n\n/**\n * Create a worker logger that prefixes all log messages with the worker PID.\n */\nexport function createWorkerLogger(baseLogger: FluxionLogger, pid: number): FluxionLogger {\n const pidPrefix = `[${pid}]`;\n\n return {\n write(level: LogLevel, event: string, fields?: object): void {\n baseLogger.write(level, `${pidPrefix} ${event}`, fields);\n },\n info(event: string, fields?: object): void {\n baseLogger.info(`${pidPrefix} ${event}`, fields);\n },\n warn(event: string, fields?: object): void {\n baseLogger.warn(`${pidPrefix} ${event}`, fields);\n },\n error(event: string, fields?: object): void {\n baseLogger.error(`${pidPrefix} ${event}`, fields);\n },\n succ(event: string, fields?: object): void {\n baseLogger.succ(`${pidPrefix} ${event}`, fields);\n },\n debug(event: string, fields?: object): void {\n baseLogger.debug(`${pidPrefix} ${event}`, fields);\n },\n verbose(event: string, fields?: object): void {\n baseLogger.verbose(`${pidPrefix} ${event}`, fields);\n },\n };\n}\n\n/**\n * ! Error.isError needs Node.js 24\n */\nexport const getErrorMessage =\n typeof Error.isError === 'function'\n ? (e: unknown): string => (Error.isError(e) ? e.message : String(e))\n : (e: unknown): string => (e as any)?.message || String(e);\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport type { WorkerOptions, NormalizedWorkerOptions, FluxionOptions, NormalizedFluxionOptions } from '../types.js';\n\n/**\n * Resolves worker options with framework defaults. All thresholds become\n * concrete numbers (`Infinity` disables a check) so the primary can evaluate\n * them without null-handling.\n */\nfunction resolveWorkerOptions(options: WorkerOptions = {}): NormalizedWorkerOptions {\n const rw = options.restartWhen ?? {};\n const healthzTimeout = rw.healthzTimeout ?? 30_000;\n // Ping runs every 5s; a threshold below 2x that would recycle healthy workers\n // (a ready worker's lastPongAt is normally ~5s old). Infinity disables.\n if (healthzTimeout !== Infinity && (!Number.isFinite(healthzTimeout) || healthzTimeout < 10_000)) {\n $throw('workerOptions.restartWhen.healthzTimeout must be a finite number >= 10000 (ms) or Infinity');\n }\n return {\n maxWorkerCount: options.maxWorkerCount ?? 4,\n restartWhen: {\n memoryUsageGreaterThan: rw.memoryUsageGreaterThan ?? Infinity,\n healthzTimeout,\n uptimeGreaterThan: rw.uptimeGreaterThan ?? Infinity,\n },\n };\n}\n\n/**\n * Read certificate content from a file path or return the content directly.\n */\nfunction readCertificateContent(content: string | Buffer, moduleDir: string): Buffer {\n if (Buffer.isBuffer(content)) {\n return content;\n }\n if (typeof content === 'string') {\n // Check if it looks like a file path (not a PEM certificate)\n // PEM certificates start with \"-----BEGIN\"\n if (!content.startsWith('-----BEGIN')) {\n const filePath = path.isAbsolute(content) ? content : path.join(moduleDir, content);\n if (fs.existsSync(filePath)) {\n return fs.readFileSync(filePath);\n }\n }\n return Buffer.from(content);\n }\n $throw('Certificate content must be a string or Buffer');\n}\n\n/**\n * Normalize HTTPS options.\n */\nfunction normalizeHttpsOptions(\n https: FluxionOptions['https'],\n moduleDir: string,\n): NormalizedFluxionOptions['https'] | undefined {\n if (!https) {\n return undefined;\n }\n\n if (typeof https !== 'object' || https === null || Array.isArray(https)) {\n $throw('FluxionOptions.https must be an object');\n }\n if (typeof https.key !== 'string') {\n $throw('FluxionOptions.https.key must be a string');\n }\n if (typeof https.cert !== 'string') {\n $throw('FluxionOptions.https.cert must be a string');\n }\n\n const result: NormalizedFluxionOptions['https'] = {\n key: readCertificateContent(https.key, moduleDir),\n cert: readCertificateContent(https.cert, moduleDir),\n };\n\n if (https.ca !== undefined) {\n if (Array.isArray(https.ca)) {\n result.ca = https.ca.map((item) => readCertificateContent(item, moduleDir));\n } else {\n result.ca = readCertificateContent(https.ca, moduleDir);\n }\n }\n\n return result;\n}\n\n/**\n * Normalize options and create necessary resources like the dynamic directory and logger.\n */\nexport function normalizeOptions(o: FluxionOptions): NormalizedFluxionOptions {\n if (typeof o !== 'object' || o === null || Array.isArray(o)) {\n $throw('FluxionOptions must be an object');\n }\n\n const {\n dir = path.isAbsolute(o.dir) ? o.dir : path.join(process.cwd(), o.dir),\n host,\n port,\n handlerTimeoutMs = 5000,\n middlewareTimeoutMs = 3000,\n staticResourceTimeoutMs = 10 * 600000,\n metaPort = port + 1,\n moduleDir = process.cwd(),\n workerOptions = {},\n maxRequestBytes = 8_000_000,\n reloadDelay = 500,\n include = ['**/*'],\n apiInclude = ['**/*.ts'],\n exclude = [\n '**/node_modules/**',\n '**/.git/**',\n '**/dist/**',\n '**/build/**',\n '**/.vscode/**',\n '**/.idea/**',\n '**/*.log',\n '**/.DS_Store',\n '**/coverage/**',\n '**/.nyc_output/**',\n '**/*.tmp',\n '**/*.temp',\n ],\n https,\n nativeWatcher = false,\n } = o as FluxionOptions;\n\n const logger = o.logger ?? 'one-line';\n if (logger !== 'one-line' && logger !== 'json-line' && typeof logger !== 'function') {\n $throw(`Invalid logger option, Must be 'one-line', 'json-line' or a custom logger function`);\n }\n\n if (typeof dir !== 'string') {\n $throw('FluxionOptions.dir must be a string');\n }\n\n if (typeof moduleDir !== 'string') {\n $throw('FluxionOptions.moduleDir must be a string');\n }\n\n if (typeof host !== 'string') {\n $throw('FluxionOptions.host must be a string');\n }\n\n if (!Number.isSafeInteger(handlerTimeoutMs) || handlerTimeoutMs <= 100) {\n $throw('FluxionOptions.handlerTimeoutMs must be an integer greater than 100');\n }\n\n if (!Number.isSafeInteger(middlewareTimeoutMs) || middlewareTimeoutMs <= 100) {\n $throw('FluxionOptions.middlewareTimeoutMs must be an integer greater than 100');\n }\n\n if (typeof reloadDelay !== 'number' || reloadDelay <= 0 || !Number.isSafeInteger(reloadDelay)) {\n $throw('FluxionOptions.reloadDelay must be a positive integer');\n }\n\n if (reloadDelay < 50) {\n $throw('FluxionOptions.reloadDelay must be greater than or equal to 50');\n }\n\n if (typeof port !== 'number' || !Number.isSafeInteger(port)) {\n $throw('FluxionOptions.port must be a positive integer');\n }\n\n if (port <= 1 || port > 65535) {\n $throw('FluxionOptions.port must be 1 ~ 65535');\n }\n\n if (typeof metaPort !== 'number' || !Number.isSafeInteger(metaPort)) {\n $throw('FluxionOptions.metaPort must be a positive integer');\n }\n\n if (metaPort <= 1 || metaPort > 65535) {\n $throw('FluxionOptions.metaPort must be 1 ~ 65535');\n }\n\n if (metaPort === port) {\n $throw('FluxionOptions.metaPort must be different from FluxionOptions.port');\n }\n\n if (typeof workerOptions !== 'object' || workerOptions === null || Array.isArray(workerOptions)) {\n $throw('FluxionOptions.workerOptions must be an object');\n }\n\n if (typeof maxRequestBytes !== 'number' || maxRequestBytes <= 0 || !Number.isSafeInteger(maxRequestBytes)) {\n $throw('FluxionOptions.maxRequestBytes must be a positive integer');\n }\n\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n\n return {\n dir,\n host,\n port,\n handlerTimeoutMs,\n middlewareTimeoutMs,\n staticResourceTimeoutMs,\n reloadDelay,\n metaPort,\n moduleDir,\n workerOptions: resolveWorkerOptions(workerOptions),\n maxRequestBytes,\n logger,\n include,\n apiInclude,\n exclude,\n nativeWatcher,\n https: normalizeHttpsOptions(https, moduleDir),\n };\n}\n","import type { PrimaryMessage, WorkerMessage } from './types.js';\n\nexport const enum PrimaryAction {\n /**\n * Health check message, the worker should respond with Pong and the latency information\n */\n Ping = 100,\n}\n\nexport const enum WorkerAction {\n /**\n * Just created\n */\n Created = 200,\n\n /**\n * Ready for tasks\n * - fluxion options are injected\n */\n Ready,\n\n /**\n * Response to Ping, used for health check and latency measurement\n */\n Pong,\n\n /**\n * Runtime telemetry snapshot from worker process.\n */\n Stats,\n}\n\nexport const isPrimaryMessage = (v: PrimaryMessage): v is PrimaryMessage => [PrimaryAction.Ping].includes(v?.type);\n\nexport const isWorkerMessage = (v: WorkerMessage): v is WorkerMessage =>\n [WorkerAction.Pong, WorkerAction.Created, WorkerAction.Ready, WorkerAction.Stats].includes(v?.type);\n","import type cluster from 'node:cluster';\nimport type { PrimaryMessage, WorkerMessage } from './types.js';\n\nexport const sendToPrimary = (message: WorkerMessage) => process.send?.(message);\n\nexport const sendToWorker = (worker: cluster.Worker, message: PrimaryMessage) => worker.send(message);\n","export const DUMMY_BASE_URL = 'http://fluxion.local';\nexport const META_PREFIX = '/_fluxion';\n\nexport const STATIC_HANDLED_FLAG = Symbol.for('fluxion.router.StaticHandled');\nexport const HANDLER_TIMEOUT_FLAG = Symbol.for('fluxion.handlerTimeout');\nexport const MIDDLEWARE_TIMEOUT_FLAG = Symbol.for('fluxion.middlewareTimeout');\n\nexport const STATIC_CONTENT_TYPES: Record<string, string> = {\n '.css': 'text/css; charset=utf-8',\n '.html': 'text/html; charset=utf-8',\n '.ico': 'image/x-icon',\n '.js': 'text/javascript; charset=utf-8',\n '.json': 'application/json; charset=utf-8',\n '.map': 'application/json; charset=utf-8',\n '.png': 'image/png',\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.svg': 'image/svg+xml',\n '.txt': 'text/plain; charset=utf-8',\n '.webp': 'image/webp',\n};\n\nexport const enum HttpCode {\n Ok = 200,\n MethodNotAllowed = 405,\n BadRequest = 400,\n PayloadTooLarge = 413,\n NotFound = 404,\n InternalServerError = 500,\n}\n\nexport const enum HandlerResult {\n NotFound,\n Handled,\n}\n\nexport const enum FluxionModuleType {\n Api,\n StaticResource,\n}\n\nexport const noop = () => {};\n","import type { ServerResponse } from 'node:http';\nimport { HttpCode } from '@/common/consts.js';\n\nexport function sendJson(res: ServerResponse, payload: unknown, statusCode: HttpCode = HttpCode.Ok): void {\n res.statusCode = statusCode;\n res.setHeader('Content-Type', 'application/json; charset=utf-8');\n res.end(JSON.stringify(payload));\n}\n\nexport function safeSendJson(res: ServerResponse, payload: unknown, statusCode: HttpCode = HttpCode.Ok): void {\n if (res.writableEnded) {\n return;\n }\n\n if (res.headersSent) {\n res.end();\n return;\n }\n\n sendJson(res, payload, statusCode);\n}\n","import type { FluxionContext } from '../types.js';\nimport http from 'node:http';\n\nimport { getErrorMessage } from '@/common/logger.js';\nimport { HttpCode, META_PREFIX } from '@/common/consts.js';\nimport { sendJson } from '../http/respond.js';\n\nexport function createPrimaryMetaApiServer(\n cx: Pick<FluxionContext, 'logger' | 'options' | 'router'>,\n getWorkersSnapshot: () => unknown,\n): http.Server {\n const server = http.createServer((req, res) => {\n const method = req.method ?? 'GET';\n\n let pathname = '/';\n try {\n pathname = new URL(req.url ?? '/', 'http://fluxion.local').pathname;\n } catch {\n sendJson(res, { message: 'Bad Request: invalid url' }, HttpCode.BadRequest);\n return;\n }\n\n if (method === 'GET' && pathname === META_PREFIX + '/healthz') {\n sendJson(res, {\n ok: true,\n role: 'primary',\n pid: process.pid,\n now: Date.now(),\n uptimeSeconds: Number(process.uptime().toFixed(3)),\n });\n return;\n }\n\n if (method === 'GET' && pathname === META_PREFIX + '/workers') {\n sendJson(res, {\n ok: true,\n now: Date.now(),\n workers: getWorkersSnapshot(),\n });\n return;\n }\n\n sendJson(res, { message: 'Not Found' }, HttpCode.NotFound);\n });\n\n server.on('listening', () => {\n cx.logger.info('MetaApiStarted', {\n pid: process.pid,\n host: cx.options.host,\n port: cx.options.metaPort,\n prefix: META_PREFIX,\n });\n });\n\n server.on('error', (error) => {\n cx.logger.error('MetaApiError', {\n host: cx.options.host,\n port: cx.options.metaPort,\n error: getErrorMessage(error),\n });\n process.exit(1);\n });\n\n server.listen(cx.options.metaPort, cx.options.host);\n return server;\n}\n","import type { WorkerMessage, WorkerState, WorkerRuntimeStats } from './types.js';\nimport type { FluxionContext } from '../types.js';\nimport os from 'node:os';\nimport cluster from 'node:cluster';\n\nimport { isWorkerMessage, WorkerAction, PrimaryAction } from './consts.js';\nimport { sendToWorker } from './communicate.js';\nimport { createPrimaryMetaApiServer } from './meta-api.js';\n\nconst bytesToMb = (bytes: number) => Number((bytes / 1024 / 1024).toFixed(2));\n\n/**\n * Anti-storm guard shared by proactive recycle (restartWhen) and reactive\n * respawn (crash). A slot may be restarted at most MAX_RESTARTS_PER_WINDOW\n * times within RESTART_WINDOW_MS; further attempts are suppressed and alerted\n * rather than fork-bombing. The window is rolling, so a quiet minute restores\n * capacity — it throttles, never kills a slot permanently.\n */\nconst RESTART_WINDOW_MS = 60_000;\nconst MAX_RESTARTS_PER_WINDOW = 3;\n\nexport function initPrimary(cx: Pick<FluxionContext, 'logger' | 'options' | 'router'>) {\n if (!cluster.isPrimary) {\n $throw('createPrimary should only be called in primary process');\n }\n\n const { workerOptions } = cx.options;\n const restartWhen = workerOptions.restartWhen;\n const cpuCount = Math.max(1, os.cpus().length);\n const workerCount = Math.max(1, Math.min(workerOptions.maxWorkerCount ?? Math.min(2, cpuCount), cpuCount));\n\n cx.logger.info('PrimaryStarted', {\n pid: process.pid,\n workers: workerCount,\n host: cx.options.host,\n port: cx.options.port,\n metaPort: cx.options.metaPort,\n });\n\n const workers = new Map<number, WorkerState>();\n\n // slot -> recent restart timestamps (pruned to RESTART_WINDOW_MS on access).\n // Keyed by the stable 1-based slot, not cluster's worker.id (which changes\n // on every fork), so history survives respawn cycles.\n const restartLog = new Map<number, number[]>();\n\n const restartCountInWindow = (slot: number) => {\n const now = Date.now();\n const log = (restartLog.get(slot) ?? []).filter((t) => now - t < RESTART_WINDOW_MS);\n restartLog.set(slot, log);\n return log.length;\n };\n\n const recordRestart = (slot: number) => {\n const now = Date.now();\n const log = (restartLog.get(slot) ?? []).filter((t) => now - t < RESTART_WINDOW_MS);\n log.push(now);\n restartLog.set(slot, log);\n };\n\n const isStorming = (slot: number) => restartCountInWindow(slot) >= MAX_RESTARTS_PER_WINDOW;\n\n const getWorkersSnapshot = () => {\n return {\n primaryPid: process.pid,\n host: cx.options.host,\n port: cx.options.port,\n metaPort: cx.options.metaPort,\n uptimeSeconds: Number(process.uptime().toFixed(3)),\n workers: Array.from(workers.entries()).map(([workerId, info]) => {\n const { instance } = info;\n const stats = info.lastStats;\n return {\n workerId,\n slot: info.slot,\n pid: info.pid ?? instance.process.pid ?? null,\n state: info.state,\n restartReason: info.restartReason ?? null,\n createdAt: info.createdAt,\n readyAt: info.readyAt ?? null,\n connected: instance.isConnected(),\n dead: instance.isDead(),\n exitedAfterDisconnect: instance.exitedAfterDisconnect,\n lastPongAt: info.lastPongAt ?? null,\n lastRttMs: info.lastRttMs ?? null,\n stats:\n stats === undefined\n ? null\n : {\n at: stats.at,\n uptimeSeconds: stats.uptimeSeconds,\n cpu: stats.cpu,\n memory: {\n ...stats.memory,\n rssMb: bytesToMb(stats.memory.rss),\n heapTotalMb: bytesToMb(stats.memory.heapTotal),\n heapUsedMb: bytesToMb(stats.memory.heapUsed),\n externalMb: bytesToMb(stats.memory.external),\n arrayBuffersMb: bytesToMb(stats.memory.arrayBuffers),\n },\n },\n };\n }),\n };\n };\n\n createPrimaryMetaApiServer(cx, getWorkersSnapshot);\n\n // Recycle a worker via hard kill (SIGTERM). Guards enforce one-at-a-time\n // (so a workload-wide condition rolls restarts instead of nuking the pool)\n // and the anti-storm window. Note: relies on the worker's default SIGTERM\n // disposition to exit; fluxion workers never trap SIGTERM.\n const initiateRecycle = (info: WorkerState, reason: string) => {\n for (const w of workers.values()) {\n if (w.state === 'restarting') return; // another recycle in flight; retried next tick\n }\n if (isStorming(info.slot)) {\n cx.logger.warn('WorkerRecycleSuppressed', {\n slot: info.slot,\n pid: info.pid,\n reason,\n windowMs: RESTART_WINDOW_MS,\n max: MAX_RESTARTS_PER_WINDOW,\n });\n return;\n }\n recordRestart(info.slot);\n info.state = 'restarting';\n info.restartReason = reason;\n cx.logger.warn('WorkerRecycling', { slot: info.slot, pid: info.pid, reason });\n info.instance.kill();\n };\n\n // Evaluate memory + uptime against a fresh stats report. Runs on every\n // Stats message (~every 2s), so reaction latency is bounded by the stats\n // interval, not the ping interval. Infinity thresholds short-circuit here.\n const evaluateResourceConditions = (info: WorkerState, stats: WorkerRuntimeStats) => {\n const rssMb = bytesToMb(stats.memory.rss);\n if (rssMb > restartWhen.memoryUsageGreaterThan) {\n initiateRecycle(info, `memoryUsageGreaterThan: rss ${rssMb}MB > ${restartWhen.memoryUsageGreaterThan}MB`);\n return;\n }\n const uptimeMs = stats.uptimeSeconds * 1000;\n if (uptimeMs > restartWhen.uptimeGreaterThan) {\n initiateRecycle(\n info,\n `uptimeGreaterThan: ${Math.round(uptimeMs / 1000)}s > ${Math.round(restartWhen.uptimeGreaterThan / 1000)}s`,\n );\n }\n };\n\n // Evaluate liveness against the last pong. Runs on the ping tick (5s); a\n // wedged worker stops replying, lastPongAt goes stale past the threshold.\n const evaluateLiveness = (now: number) => {\n for (const info of workers.values()) {\n if (info.state !== 'ready' || info.lastPongAt === undefined) continue;\n const staleMs = now - info.lastPongAt;\n if (staleMs > restartWhen.healthzTimeout) {\n initiateRecycle(\n info,\n `healthzTimeout: no pong for ${Math.round(staleMs / 1000)}s > ${Math.round(restartWhen.healthzTimeout / 1000)}s`,\n );\n }\n }\n };\n\n const spawnSlot = (slot: number) => {\n attachWorker(cluster.fork({ WORKER_ID: String(slot) }), slot);\n };\n\n const attachWorker = (worker: cluster.Worker, slot: number): void => {\n const workerInfo: WorkerState = {\n state: 'creating',\n pid: worker.process.pid,\n slot,\n createdAt: Date.now(),\n instance: worker,\n };\n workers.set(worker.id, workerInfo);\n\n worker.on('message', (raw: WorkerMessage) => {\n if (!isWorkerMessage(raw)) {\n return;\n }\n\n if (raw.type === WorkerAction.Pong) {\n const rtt = Date.now() - raw.sentAt;\n workerInfo.pid = raw.pid;\n workerInfo.lastPongAt = Date.now();\n workerInfo.lastRttMs = rtt;\n return;\n }\n\n if (raw.type === WorkerAction.Ready) {\n workerInfo.state = 'ready';\n workerInfo.pid = raw.pid;\n workerInfo.readyAt = Date.now();\n cx.logger.info('WorkerReady', { workerId: worker.id, slot, pid: raw.pid });\n return;\n }\n\n if (raw.type === WorkerAction.Created) {\n workerInfo.state = 'created';\n workerInfo.pid = raw.pid;\n cx.logger.info('WorkerCreated', { workerId: worker.id, slot, pid: raw.pid });\n return;\n }\n\n if (raw.type === WorkerAction.Stats) {\n workerInfo.pid = raw.pid;\n workerInfo.lastStats = raw.stats;\n if (workerInfo.state === 'ready') {\n evaluateResourceConditions(workerInfo, raw.stats);\n }\n }\n });\n\n worker.on('exit', (code, signal) => {\n const info = workers.get(worker.id);\n workers.delete(worker.id);\n const exitedSlot = info?.slot;\n const expected = info?.state === 'restarting';\n const reason = info?.restartReason ?? null;\n\n cx.logger.warn('WorkerExited', {\n workerId: worker.id,\n slot: exitedSlot ?? null,\n pid: worker.process.pid ?? 'unknown',\n code,\n signal: signal ?? 'none',\n expected,\n reason,\n });\n\n if (exitedSlot === undefined) return;\n\n if (expected) {\n // Proactive recycle: the restart was already counted when initiated.\n spawnSlot(exitedSlot);\n return;\n }\n\n // Unexpected crash: count it, then respawn unless anti-storm trips.\n recordRestart(exitedSlot);\n if (isStorming(exitedSlot)) {\n cx.logger.error('WorkerRespawnSuppressed', {\n slot: exitedSlot,\n windowMs: RESTART_WINDOW_MS,\n max: MAX_RESTARTS_PER_WINDOW,\n });\n return;\n }\n spawnSlot(exitedSlot);\n });\n };\n\n for (let i = 0; i < workerCount; i++) {\n spawnSlot(i + 1);\n }\n\n const pingTimer = setInterval(() => {\n const sentAt = Date.now();\n for (const info of workers.values()) {\n if (!info.instance.isConnected()) {\n continue;\n }\n try {\n sendToWorker(info.instance, { type: PrimaryAction.Ping, sentAt });\n } catch {\n // Ignore transient IPC errors; worker lifecycle events will reconcile state.\n }\n }\n evaluateLiveness(Date.now());\n }, 5000);\n pingTimer.unref();\n}\n","/**\n * For low version of Node.js that does not support `Promise.try`, we can implement it ourselves.\n *\n * Only for async functions.\n */\nexport function PromiseTry<T extends (...args: any[]) => any>(fn: T, ...args: Parameters<T>) {\n return new Promise<ReturnType<T>>((resolve, reject) => {\n // in case `fn` throws synchronously, we catch it and reject the promise\n try {\n const r = fn(...args);\n if (r instanceof Promise) {\n r.then(resolve).catch(reject);\n } else {\n resolve(r);\n }\n } catch (error) {\n reject(error);\n }\n });\n}\n","import type { IncomingMessage } from 'node:http';\n\nexport function getRealIp(req: IncomingMessage): string {\n const forwardedFor = req.headersDistinct['x-forwarded-for'];\n if (forwardedFor) {\n const firstForwarded = forwardedFor[0]?.split(',')[0]?.trim();\n if (firstForwarded && firstForwarded.length > 0) {\n return firstForwarded;\n }\n }\n\n const realIp = req.headersDistinct['x-real-ip']?.[0].trim();\n if (realIp !== undefined) {\n return realIp;\n }\n\n return req.socket.remoteAddress ?? 'unknown';\n}\n\nexport function isTextualContentType(contentType: string | undefined): boolean {\n if (contentType === undefined) {\n return false;\n }\n\n const normalized = contentType.toLowerCase();\n\n return (\n normalized.startsWith('text/') ||\n normalized.includes('json') ||\n normalized.includes('xml') ||\n normalized.includes('x-www-form-urlencoded') ||\n normalized.includes('javascript')\n );\n}\n","import { DUMMY_BASE_URL } from '@/common/consts.js';\n\nexport function toURL(rawUrl: string | undefined): URL | undefined {\n if (rawUrl === undefined) {\n return undefined;\n }\n\n try {\n return new URL(rawUrl, DUMMY_BASE_URL);\n } catch {\n return undefined;\n }\n}\n","export function parseQuery(searchParams: URLSearchParams): Record<string, string | string[]> {\n const query: Record<string, string | string[]> = {};\n\n for (const [key, value] of searchParams.entries()) {\n const existing = query[key];\n\n if (existing === undefined) {\n query[key] = value;\n continue;\n }\n\n if (Array.isArray(existing)) {\n existing.push(value);\n continue;\n }\n\n query[key] = [existing, value];\n }\n\n return query;\n}\n","import type http from 'node:http';\n\nimport { isTextualContentType } from './headers.js';\nimport { parseQuery } from './query.js';\n\nexport interface BodyPreview {\n exists: boolean;\n value?: string;\n bytes: number;\n truncated: boolean;\n}\n\nfunction createRequestBodyTooLargeError(receivedBytes: number, maxBytes: number): NodeJS.ErrnoException {\n const sizeError = new Error(\n `request body too large: ${receivedBytes.toString()} bytes exceeds ${maxBytes.toString()} bytes`,\n ) as NodeJS.ErrnoException;\n\n sizeError.code = 'REQUEST_BODY_TOO_LARGE';\n\n return sizeError;\n}\n\nfunction getHeaderValue(headerValue: string | string[] | undefined): string | undefined {\n return Array.isArray(headerValue) ? headerValue[0] : headerValue;\n}\n\nfunction createEmptyPreview(): BodyPreview {\n return {\n exists: false,\n bytes: 0,\n truncated: false,\n };\n}\n\nfunction createPreview(\n previewBuffer: Buffer,\n totalBytes: number,\n contentType: string | undefined,\n truncated: boolean,\n): BodyPreview {\n if (totalBytes === 0) {\n return createEmptyPreview();\n }\n\n if (isTextualContentType(contentType)) {\n return {\n exists: true,\n value: previewBuffer.toString('utf8'),\n bytes: totalBytes,\n truncated,\n };\n }\n\n return {\n exists: true,\n value: `<binary body: ${totalBytes} bytes>`,\n bytes: totalBytes,\n truncated,\n };\n}\n\nasync function readRequestBodyWithPreview(\n req: http.IncomingMessage,\n method: string,\n maxBytes: number,\n previewMaxBytes = 8192,\n): Promise<{ rawBody: Buffer | undefined; preview: BodyPreview }> {\n if (method === 'GET' || method === 'HEAD') {\n return {\n rawBody: undefined,\n preview: createEmptyPreview(),\n };\n }\n\n if (req.readableEnded) {\n return {\n rawBody: undefined,\n preview: createEmptyPreview(),\n };\n }\n\n const contentLengthRaw = getHeaderValue(req.headers['content-length']);\n const declaredBytes = contentLengthRaw !== undefined ? Number.parseInt(contentLengthRaw, 10) : NaN;\n\n if (Number.isFinite(declaredBytes) && declaredBytes > maxBytes) {\n throw createRequestBodyTooLargeError(declaredBytes, maxBytes);\n }\n\n return new Promise((resolve, reject) => {\n const rawBodyChunks: Buffer[] = [];\n const previewChunks: Buffer[] = [];\n let totalBytes = 0;\n let previewBytes = 0;\n let previewTruncated = false;\n let settled = false;\n\n const cleanup = (): void => {\n req.off('data', onData);\n req.off('end', onEnd);\n req.off('error', onError);\n req.off('aborted', onAborted);\n };\n\n const settle = (action: () => void): void => {\n if (settled) {\n return;\n }\n\n settled = true;\n action();\n };\n\n const onData = (chunk: Buffer | string | Uint8Array): void => {\n const bufferChunk = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);\n totalBytes += bufferChunk.byteLength;\n\n if (totalBytes > maxBytes) {\n cleanup();\n req.resume();\n settle(() => {\n reject(createRequestBodyTooLargeError(totalBytes, maxBytes));\n });\n return;\n }\n\n rawBodyChunks.push(bufferChunk);\n\n if (previewBytes < previewMaxBytes) {\n const remaining = previewMaxBytes - previewBytes;\n const nextSlice = bufferChunk.subarray(0, remaining);\n previewChunks.push(nextSlice);\n previewBytes += nextSlice.length;\n\n if (nextSlice.length < bufferChunk.length) {\n previewTruncated = true;\n }\n } else {\n previewTruncated = true;\n }\n };\n\n const onEnd = (): void => {\n cleanup();\n settle(() => {\n const rawBody = rawBodyChunks.length > 0 ? Buffer.concat(rawBodyChunks) : undefined;\n const previewBuffer = previewChunks.length > 0 ? Buffer.concat(previewChunks) : Buffer.alloc(0);\n\n resolve({\n rawBody,\n preview: createPreview(\n previewBuffer,\n rawBody?.byteLength ?? 0,\n getHeaderValue(req.headers['content-type']),\n previewTruncated,\n ),\n });\n });\n };\n\n const onError = (error: Error): void => {\n cleanup();\n settle(() => {\n reject(error);\n });\n };\n\n const onAborted = (): void => {\n cleanup();\n settle(() => {\n reject(new Error('request aborted while reading body'));\n });\n };\n\n req.on('data', onData);\n req.once('end', onEnd);\n req.once('error', onError);\n req.once('aborted', onAborted);\n });\n}\n\nexport async function parseBody(\n req: http.IncomingMessage,\n method: string,\n maxBytes: number,\n): Promise<{ body: Record<string, any>; preview: BodyPreview }> {\n const { rawBody, preview } = await readRequestBodyWithPreview(req, method, maxBytes);\n\n if (rawBody === undefined || rawBody.byteLength === 0) {\n return {\n body: {},\n preview,\n };\n }\n\n const contentType = getHeaderValue(req.headers['content-type'])?.toLowerCase() ?? '';\n\n if (contentType.includes('json')) {\n const textBody = rawBody.toString('utf8').trim();\n\n if (textBody.length === 0) {\n return {\n body: {},\n preview,\n };\n }\n\n try {\n const parsed = JSON.parse(textBody) as unknown;\n\n if (parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed)) {\n return {\n body: parsed as Record<string, any>,\n preview,\n };\n }\n\n return {\n body: { value: parsed },\n preview,\n };\n } catch {\n return {\n body: { raw: textBody },\n preview,\n };\n }\n }\n\n if (contentType.includes('x-www-form-urlencoded')) {\n return {\n body: parseQuery(new URLSearchParams(rawBody.toString('utf8'))),\n preview,\n };\n }\n\n if (isTextualContentType(contentType)) {\n return {\n body: { raw: rawBody.toString('utf8') },\n preview,\n };\n }\n\n return {\n body: {},\n preview,\n };\n}\n","/**\n * Parse Cookie header string into an object\n */\nexport function parseCookie(cookieHeader: string | undefined): Record<string, string> {\n if (!cookieHeader) {\n return {};\n }\n\n const cookies: Record<string, string> = {};\n const pairs = cookieHeader.split(';');\n\n for (const pair of pairs) {\n const [key, ...valueParts] = pair.split('=');\n if (!key) continue;\n\n const trimmedKey = key.trim();\n const value = valueParts.join('=').trim();\n cookies[trimmedKey] = decodeURIComponent(value);\n }\n\n return cookies;\n}\n\n/**\n * Serialize an object into a Cookie header string\n */\nexport function serializeCookie(name: string, value: string, options?: {\n maxAge?: number;\n expires?: Date;\n domain?: string;\n path?: string;\n secure?: boolean;\n httpOnly?: boolean;\n sameSite?: 'strict' | 'lax' | 'none';\n}): string {\n let cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}`;\n\n if (options) {\n if (options.maxAge !== undefined) {\n cookie += `; Max-Age=${options.maxAge}`;\n }\n if (options.expires) {\n cookie += `; Expires=${options.expires.toUTCString()}`;\n }\n if (options.domain) {\n cookie += `; Domain=${options.domain}`;\n }\n if (options.path) {\n cookie += `; Path=${options.path}`;\n }\n if (options.secure) {\n cookie += '; Secure';\n }\n if (options.httpOnly) {\n cookie += '; HttpOnly';\n }\n if (options.sameSite) {\n cookie += `; SameSite=${options.sameSite}`;\n }\n }\n\n return cookie;\n}\n","import http from 'node:http';\nimport https from 'node:https';\n\nimport type { FluxionContext, NormalizedRequest } from '../types.js';\nimport { $keys } from '@/common/native.js';\nimport {\n HttpCode,\n HANDLER_TIMEOUT_FLAG,\n META_PREFIX,\n STATIC_HANDLED_FLAG,\n FluxionModuleType,\n MIDDLEWARE_TIMEOUT_FLAG,\n} from '@/common/consts.js';\nimport { PromiseTry } from '@/common/promise-try.js';\nimport { getErrorMessage } from '@/common/logger.js';\n\nimport { getRealIp } from '../http/headers.js';\nimport { toURL } from '../http/request.js';\nimport { safeSendJson } from '../http/respond.js';\nimport { parseBody, type BodyPreview } from '../http/body.js';\nimport { parseQuery } from '../http/query.js';\nimport { parseCookie } from '../http/cookie.js';\n\nconst waiter = (mainPromise: Promise<any>, timeoutMs: number, flag: symbol) =>\n Promise.race([mainPromise, new Promise((r) => setTimeout(() => r(flag), timeoutMs))]);\n\nexport function createWorkerServer(cx: FluxionContext): http.Server | https.Server {\n const requestHandler = async (req: http.IncomingMessage, res: http.ServerResponse) => {\n const method = req.method ?? 'GET';\n const ip = getRealIp(req);\n const url = toURL(req.url);\n if (url === undefined) {\n safeSendJson(res, { message: 'Bad Request: req.url is undefined' }, HttpCode.BadRequest);\n return;\n }\n\n const normalized: NormalizedRequest = {\n method,\n ip,\n url,\n query: parseQuery(url.searchParams),\n body: {},\n headers: req.headers,\n cookie: parseCookie(req.headers.cookie as string | undefined),\n };\n\n let bodyPreview: BodyPreview = {\n exists: false,\n bytes: 0,\n truncated: false,\n };\n\n cx.logger.info('Req', { method, ip, path: url.pathname });\n\n const start = performance.now();\n res.once('finish', () => {\n const fields: Record<string, unknown> = {\n workerId: process.env.WORKER_ID ?? '[primary]',\n method,\n ip,\n path: url.pathname,\n status: res.statusCode,\n duration: (performance.now() - start).toFixed(4),\n };\n\n if ($keys(normalized.query).length > 0) {\n fields.query = normalized.query;\n }\n\n if (bodyPreview.exists) {\n fields.body = bodyPreview.value;\n fields.bodyBytes = bodyPreview.bytes;\n fields.bodyTruncated = bodyPreview.truncated;\n }\n\n cx.logger.info('Res', fields);\n });\n\n // * Start request handling\n try {\n if (normalized.url.pathname.startsWith(META_PREFIX + '/')) {\n safeSendJson(res, { message: `Not Found` }, HttpCode.NotFound);\n return;\n }\n\n const parsed = await parseBody(req, normalized.method, cx.options.maxRequestBytes);\n normalized.body = parsed.body;\n bodyPreview = parsed.preview;\n\n const m = await cx.router.getModule(url);\n if (!m) {\n safeSendJson(res, { message: 'Not Found' }, HttpCode.NotFound);\n return;\n }\n\n if (req.method && m.methods && !m.methods.includes(req.method)) {\n safeSendJson(res, { message: 'Method Not Allowed' }, HttpCode.MethodNotAllowed);\n return;\n }\n\n const timeoutMs =\n m.type === FluxionModuleType.Api\n ? (m.handlerTimeoutMs ?? cx.options.handlerTimeoutMs)\n : cx.options.staticResourceTimeoutMs;\n\n // Middleware execution\n if (m.middlewares) {\n for (let i = 0; i < m.middlewares.length; i++) {\n const result = await waiter(\n PromiseTry(m.middlewares[i], normalized, req, res),\n cx.options.middlewareTimeoutMs,\n MIDDLEWARE_TIMEOUT_FLAG,\n );\n\n if (result === MIDDLEWARE_TIMEOUT_FLAG) {\n cx.logger.warn('MiddlewareTimeout', {\n method: normalized.method,\n ip: normalized.ip,\n });\n safeSendJson(res, { message: 'Internal Server Error' }, HttpCode.InternalServerError);\n return;\n }\n if (res.writableEnded) {\n return;\n }\n if (res.headersSent) {\n res.end();\n return;\n }\n }\n }\n\n const result = await waiter(PromiseTry(m.handler, normalized, req, res), timeoutMs, HANDLER_TIMEOUT_FLAG);\n\n if (result === HANDLER_TIMEOUT_FLAG) {\n cx.logger.warn('HandlerTimeout', {\n method: normalized.method,\n ip: normalized.ip,\n });\n safeSendJson(res, { message: 'Handler timed out' }, HttpCode.InternalServerError);\n return;\n }\n\n if (result !== STATIC_HANDLED_FLAG) {\n safeSendJson(res, result);\n }\n } catch (error) {\n cx.logger.error('RequestFailed', {\n method: normalized.method,\n ip: normalized.ip,\n path: normalized.url.pathname,\n error: getErrorMessage(error),\n });\n\n if ((error as NodeJS.ErrnoException).code === 'REQUEST_BODY_TOO_LARGE') {\n safeSendJson(res, { message: getErrorMessage(error) }, HttpCode.PayloadTooLarge);\n } else {\n safeSendJson(res, { message: getErrorMessage(error) }, HttpCode.InternalServerError);\n }\n }\n };\n\n const server = cx.options.https\n ? https.createServer(\n {\n key: cx.options.https.key,\n cert: cx.options.https.cert,\n ca: cx.options.https.ca,\n },\n requestHandler,\n )\n : http.createServer(requestHandler);\n\n server.on('close', () => {\n cx.logger.info('ServerClosed', {\n host: cx.options.host,\n port: cx.options.port,\n });\n });\n\n server.listen(cx.options.port, cx.options.host, () => {\n cx.logger.info('ServerStarted', {\n pid: process.pid,\n protocol: cx.options.https ? 'https' : 'http',\n host: cx.options.host,\n port: cx.options.port,\n });\n cx.logger.info('DynamicDirectory', { directory: cx.options.dir });\n });\n\n server.on('error', (error) => {\n cx.logger.error('ServerError', {\n error: getErrorMessage(error),\n });\n });\n\n return server;\n}\n","import type { PrimaryMessage } from './types.js';\nimport type { FluxionContext } from '../types.js';\nimport cluster from 'node:cluster';\n\nimport { getErrorMessage } from '@/common/logger.js';\nimport { WorkerAction, PrimaryAction, isPrimaryMessage } from './consts.js';\nimport { sendToPrimary } from './communicate.js';\nimport { createWorkerServer } from './server.js';\n\nconst startStatsReporter = () => {\n let previousCpuUsage = process.cpuUsage();\n let previousAt = Date.now();\n\n const interval = setInterval(() => {\n const now = Date.now();\n const elapsedMicros = Math.max(1, (now - previousAt) * 1000);\n const cpuDelta = process.cpuUsage(previousCpuUsage);\n const cpuPercent = Number((((cpuDelta.user + cpuDelta.system) / elapsedMicros) * 100).toFixed(2));\n\n previousCpuUsage = process.cpuUsage();\n previousAt = now;\n\n const memoryUsage = process.memoryUsage();\n sendToPrimary({\n type: WorkerAction.Stats,\n pid: process.pid,\n stats: {\n at: now,\n pid: process.pid,\n uptimeSeconds: Number(process.uptime().toFixed(3)),\n cpu: {\n userMicros: cpuDelta.user,\n systemMicros: cpuDelta.system,\n percent: cpuPercent,\n },\n memory: {\n rss: memoryUsage.rss,\n heapTotal: memoryUsage.heapTotal,\n heapUsed: memoryUsage.heapUsed,\n external: memoryUsage.external,\n arrayBuffers: memoryUsage.arrayBuffers,\n },\n },\n });\n }, 2000);\n\n interval.unref();\n};\n\nexport function initWorker(cx: FluxionContext) {\n if (cluster.isPrimary) {\n $throw('createWorker should only be called in worker process');\n }\n\n process.on('message', (raw: PrimaryMessage) => {\n if (!isPrimaryMessage(raw)) {\n return;\n }\n\n if (raw.type === PrimaryAction.Ping) {\n sendToPrimary({ type: WorkerAction.Pong, pid: process.pid, sentAt: raw.sentAt, receivedAt: Date.now() });\n return;\n }\n });\n\n sendToPrimary({ type: WorkerAction.Created, pid: process.pid });\n startStatsReporter();\n\n try {\n createWorkerServer(cx);\n sendToPrimary({ type: WorkerAction.Ready, pid: process.pid });\n } catch (e) {\n cx.logger.error('WorkerBootstrapFailed', {\n pid: process.pid,\n error: getErrorMessage(e),\n });\n process.exit(1);\n }\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport type { FluxionContext } from '../types.js';\n\nexport type WatcherContext = Pick<FluxionContext, 'options' | 'logger' | 'router'>;\n\nexport abstract class FluxionWatcherBase {\n protected readonly cx: WatcherContext;\n\n private timer: NodeJS.Timeout | null = null;\n private readonly filesChanged = new Map<string, string>();\n\n constructor(cx: WatcherContext) {\n this.cx = cx;\n }\n\n /**\n * Recursively register all files in the options directory.\n */\n protected async init(): Promise<this> {\n const dir = this.cx.options.dir;\n if (!fs.existsSync(dir)) {\n this.cx.logger.warn(`Directory does not exist: ${dir}`);\n return this;\n }\n\n const registerList: Array<Promise<void>> = [];\n\n const registerRecursive = (absoluteDir: string, relativeDir: string) => {\n const entries = fs.readdirSync(absoluteDir, { withFileTypes: true });\n\n for (let i = 0; i < entries.length; i++) {\n const entry = entries[i];\n const absolutePath = path.join(absoluteDir, entry.name);\n const relativePath = path.join(relativeDir, entry.name);\n\n if (entry.isDirectory()) {\n registerRecursive(absolutePath, relativePath);\n } else if (entry.isFile()) {\n const p = this.cx.router.register(absolutePath, relativePath).catch((e) => {\n this.cx.logger.error(`Error registering file ${relativePath}: ${(e as Error).message}`);\n });\n registerList.push(p);\n }\n }\n };\n\n registerRecursive(dir, '');\n await Promise.all(registerList);\n\n this.cx.logger.info(`Initial registration complete for directory: ${dir}`);\n return this;\n }\n\n protected queueUp(absolutePath: string, relativePath: string): void {\n this.filesChanged.set(absolutePath, relativePath);\n if (this.timer) {\n return;\n }\n\n this.timer = setTimeout(async () => {\n const promises = [...this.filesChanged].map(([absolutePath, relativePath]) =>\n this.cx.router\n .register(absolutePath, relativePath)\n .catch((err) => this.cx.logger.error(`Error refreshing handlers: ${(err as Error).message}`))\n .finally(() => this.filesChanged.delete(absolutePath)),\n );\n await Promise.all(promises);\n this.timer = null;\n }, this.cx.options.reloadDelay);\n }\n\n protected stopCore(): void {\n if (this.timer) {\n clearTimeout(this.timer);\n this.timer = null;\n }\n\n this.filesChanged.clear();\n }\n\n abstract start(): Promise<this>;\n abstract stop(): this;\n}\n","import path from 'node:path';\nimport type { FSWatcher } from 'chokidar';\nimport chokidar from 'chokidar';\n\nimport { FluxionWatcherBase, type WatcherContext } from './base.js';\n\nexport class FluxionChokidarWatcher extends FluxionWatcherBase {\n private watcher: FSWatcher | null = null;\n\n constructor(cx: WatcherContext) {\n super(cx);\n }\n\n /**\n * Start watching files with chokidar.\n *\n * Using chokidar provides:\n * - Cross-platform recursive watch support (including Linux/CentOS)\n * - Better event handling and stability\n * - Automatic resource management\n */\n async start(): Promise<this> {\n this.stop();\n await this.init();\n\n const dir = this.cx.options.dir;\n this.watcher = chokidar\n .watch(dir, {\n persistent: true, // Keep the process running\n ignoreInitial: true, // Don't emit 'add' events for initial scan\n usePolling: false, // Use polling as fallback (helps with some network drives)\n awaitWriteFinish: {\n stabilityThreshold: 100,\n pollInterval: 50,\n }, // Atomic writes handling\n })\n .on('all', (_event, absolutePath) => {\n if (!absolutePath) {\n return;\n }\n\n // & `filename` is absolute(Maybe because of watching an absolute path `dir`)\n this.queueUp(absolutePath, path.relative(dir, absolutePath));\n })\n .on('error', (err: unknown) => {\n const error = err instanceof Error ? err : new Error(String(err));\n this.cx.logger.error(`Watcher error: ${error.message}`);\n this.cx.logger.error(`Restarting watcher...`);\n this.start();\n })\n .on('ready', () => {\n this.cx.logger.info(`Watcher ready and watching directory: ${dir}`);\n });\n\n this.cx.logger.info(`Watcher started on directory: ${dir}`);\n return this;\n }\n\n stop(): this {\n if (this.watcher) {\n void this.watcher.close();\n this.watcher = null;\n }\n\n this.stopCore();\n return this;\n }\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\n\nimport { FluxionWatcherBase, type WatcherContext } from './base.js';\n\nexport class FluxionNativeWatcher extends FluxionWatcherBase {\n private watcher: fs.FSWatcher | null = null;\n\n constructor(cx: WatcherContext) {\n super(cx);\n }\n\n /**\n * Since all actions are mapped to `rename` and `change` (WatchEventType).\n *\n * We could only record every file and reload them all.\n */\n async start(): Promise<this> {\n this.stop();\n await this.init();\n\n const dir = this.cx.options.dir;\n this.watcher = fs\n .watch(dir, { recursive: true }, (_eventType, relativePath) => {\n if (!relativePath) {\n return;\n }\n\n // & Unlike chokidar, `filename` here is relativePath\n this.queueUp(path.join(dir, relativePath), relativePath);\n })\n .on('error', (err) => {\n this.cx.logger.error(`Watcher error: ${err.message}`);\n this.cx.logger.error(`Restarting watcher...`);\n this.start();\n });\n\n this.cx.logger.info(`Watcher started on directory: ${dir}`);\n return this;\n }\n\n stop(): this {\n if (this.watcher) {\n this.watcher.close();\n this.watcher = null;\n }\n\n this.stopCore();\n return this;\n }\n}\n","import type { FluxionContext, FluxionModuleWithType } from '@/types.js';\nimport { static_cast } from 'type-narrow';\nimport { FluxionModuleType } from './consts';\n\nfunction isFluxionModule(cx: Pick<FluxionContext, 'options' | 'logger'>, o: unknown): o is FluxionModuleWithType {\n if (typeof o !== 'object' || o === null) {\n return false;\n }\n\n static_cast<FluxionModuleWithType>(o);\n\n if (typeof o.handler !== 'function') {\n cx.logger.error(`handler must be a function`);\n return false;\n }\n\n if (o.disposer !== undefined && typeof o.disposer !== 'function') {\n cx.logger.error(`disposer must be a function if provided`);\n return false;\n }\n\n const ms = o.handlerTimeoutMs;\n if (ms !== undefined && (!Number.isSafeInteger(ms) || ms < 100)) {\n cx.logger.error(`handlerTimeoutMs must be an integer >= 100 if provided`);\n return false;\n }\n\n if (o.type !== FluxionModuleType.Api) {\n cx.logger.error(`You must use defineFluxionModule to create module`);\n return false;\n }\n\n return true;\n}\n\nexport function loadFluxionModule(\n cx: Pick<FluxionContext, 'options' | 'logger'>,\n fullpath: string,\n): FluxionModuleWithType {\n delete require.cache[fullpath];\n let m = require(fullpath);\n if (isFluxionModule(cx, m)) {\n } else if (isFluxionModule(cx, m.default)) {\n m = m.default;\n } else {\n $throw(`Invalid handler module '${fullpath}', make sure it satisfies defineFluxionModule(...) helper`);\n }\n\n return m;\n}\n","import type { FluxionContext, FluxionModuleWithType } from '../types.js';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { minimatch } from 'minimatch';\nimport { FluxionModuleType, STATIC_CONTENT_TYPES, STATIC_HANDLED_FLAG } from '@/common/consts.js';\nimport { loadFluxionModule } from '@/common/injector.js';\nimport { cctl } from '@/common/color.js';\nimport { PromiseTry } from '@/common/promise-try.js';\n\nexport class FluxionRouter {\n private readonly cx: Pick<FluxionContext, 'options' | 'logger'>;\n private readonly handlers: Map<string, FluxionModuleWithType> = new Map();\n\n constructor(cx: Pick<FluxionContext, 'options' | 'logger'>) {\n this.cx = cx;\n }\n\n makeStaticResource(filepath: string): FluxionModuleWithType {\n return {\n type: FluxionModuleType.StaticResource,\n handler: async (normalized, _req, res) => {\n if (normalized.method !== 'GET' && normalized.method !== 'HEAD') {\n res.statusCode = 405;\n res.setHeader('Allow', 'GET, HEAD');\n res.end();\n return;\n }\n\n if (!fs.existsSync(filepath)) {\n res.statusCode = 404;\n res.end('Not Found');\n return;\n }\n\n const stat = fs.statSync(filepath);\n if (!stat.isFile()) {\n res.statusCode = 404;\n res.end('Not Found');\n return;\n }\n\n const extension = path.extname(filepath).toLowerCase();\n const contentType = STATIC_CONTENT_TYPES[extension] ?? 'application/octet-stream';\n\n res.statusCode = 200;\n res.setHeader('Content-Type', contentType);\n res.setHeader('Content-Length', String(stat.size));\n\n if (normalized.method === 'HEAD') {\n res.end();\n return;\n }\n\n return new Promise<symbol>((resolve, reject) => {\n const stream = fs.createReadStream(filepath);\n stream.on('error', reject);\n stream.on('end', () => resolve(STATIC_HANDLED_FLAG));\n stream.pipe(res);\n });\n },\n };\n }\n\n /**\n * File registration logic with fast-glob pattern matching:\n * 1. Check if the path exists, if not, delete the handler;\n * 2. If file doesn't match include patterns, skip registration;\n * 3. If file matches exclude patterns, skip registration;\n * 4. If file matches apiInclude patterns, register as API handler;\n * 5. Otherwise, register as static resource.\n */\n async register(absolutePath: string, relativePath: string) {\n // Get the disposer and delete\n const disposer = this.handlers.get(relativePath)?.disposer;\n if (disposer) {\n await PromiseTry(disposer);\n }\n\n // # Delete\n if (!fs.existsSync(absolutePath)) {\n this.handlers.delete(relativePath);\n // & Watcher will emit recursively, so there is no need to use this.remove(rp);\n this.cx.logger.info(`${cctl.red}Deleted ${cctl.reset} - ${relativePath}`);\n return;\n }\n\n // Step 2: Check if file matches include patterns (default: all files)\n // If not matching, skip registration\n const matchesInclude = this.cx.options.include.some((pattern) => minimatch(relativePath, pattern));\n if (!matchesInclude) {\n this.handlers.delete(relativePath);\n this.cx.logger.info(`${cctl.yellow}Skipped ${cctl.reset} - ${relativePath}`);\n return;\n }\n\n // Step 3: Check if file matches exclude patterns\n // If matching, skip registration\n const matchesExclude = this.cx.options.exclude.some((pattern) => minimatch(relativePath, pattern));\n if (matchesExclude) {\n this.handlers.delete(relativePath);\n this.cx.logger.info(`${cctl.orange}Excluded${cctl.reset} - ${relativePath}`);\n return;\n }\n\n // Step 4 & 5: Check if file matches apiInclude patterns\n // If matching, register as API handler; otherwise as static resource\n const matchesApiInclude = this.cx.options.apiInclude.some((pattern) => minimatch(relativePath, pattern));\n if (matchesApiInclude) {\n const m = loadFluxionModule(this.cx, absolutePath);\n this.handlers.set(relativePath, m);\n this.cx.logger.info(`${cctl.green}Api ${cctl.reset} - ${relativePath}`);\n return;\n }\n\n // register as static resource\n this.handlers.set(relativePath, this.makeStaticResource(relativePath));\n this.cx.logger.info(`${cctl.brightBlue}Static ${cctl.reset} - ${relativePath}`);\n }\n\n getModule(url: URL): FluxionModuleWithType | undefined {\n const relativePath = url.pathname.replace(/^[/]+/, '').replace(/[/]+$/, '');\n return this.handlers.get(relativePath);\n }\n\n /**\n * If the path points to a file, it would be simple.\n * But if it's a directory, we need to find all registered handlers under this directory and remove them.\n *\n * @param somepath\n * @deprecated\n */\n remove(somepath: string): void {\n if (this.handlers.has(somepath)) {\n this.handlers.delete(somepath);\n this.cx.logger.info(`${cctl.red}Deleted ${cctl.reset} - ${somepath}`);\n }\n // & Not in handler map -> It is a directory\n const prefix = somepath.endsWith('/') ? somepath : somepath + '/';\n for (const key of this.handlers.keys()) {\n if (key.startsWith(prefix)) {\n this.handlers.delete(key);\n this.cx.logger.info(`${cctl.red}Deleted ${cctl.reset} - ${key}`);\n }\n }\n }\n}\n","import type { FluxionContext, FluxionOptions } from './types.js';\n\nimport { createLogger, createWorkerLogger } from '@/common/logger.js';\nimport { normalizeOptions } from './http/options.js';\nimport cluster from 'node:cluster';\nimport { initPrimary } from './cluster/primary.js';\nimport { initWorker } from './cluster/worker.js';\nimport { FluxionChokidarWatcher } from './watcher/chokidar.js';\nimport { FluxionNativeWatcher } from './watcher/native.js';\nimport { FluxionRouter } from './router/index.js';\n\nexport async function fluxion(options: FluxionOptions) {\n const context = { options: normalizeOptions(options) } as FluxionContext;\n\n context.logger = createLogger(context as Pick<FluxionContext, 'options'>);\n context.router = new FluxionRouter(context as Pick<FluxionContext, 'options' | 'logger'>);\n\n if (cluster.isPrimary) {\n initPrimary(context);\n } else {\n // Replace logger with worker logger that prefixes PID\n context.logger = createWorkerLogger(context.logger, process.pid);\n // Only worker creates the watcher\n const Watcher = context.options.nativeWatcher ? FluxionNativeWatcher : FluxionChokidarWatcher;\n context.watcher = await new Watcher(context as Pick<FluxionContext, 'options' | 'logger' | 'router'>).start();\n initWorker(context);\n }\n}\n","import type {\n FluxionDispose,\n FluxionHandler,\n FluxionMiddleware,\n FluxionModule,\n FluxionModuleWithType,\n} from './types.js';\nimport { fluxion } from './fluxion.js';\nimport { FluxionModuleType, noop } from './common/consts.js';\n\nexport { fluxion };\nexport type { FluxionDispose, FluxionHandler, FluxionModule as FluxionHandlerModule, FluxionOptions } from './types.js';\n\n/**\n * Use handler function and optional disposer function to define a Fluxion module.\n * @param handler Main function that handles request and response instances\n * @param disposer Deal with resource cleanup when the server is about to close\n */\nexport function defineFluxionModule(handler: FluxionHandler, disposer?: FluxionDispose): FluxionModuleWithType;\n/**\n * Provides type safety for defining Fluxion modules.\n */\nexport function defineFluxionModule(fluxionModule: FluxionModule): FluxionModuleWithType;\nexport function defineFluxionModule(\n a: FluxionModule | FluxionHandler,\n disposer: FluxionDispose = noop,\n): FluxionModuleWithType {\n if (typeof a === 'function') {\n if (typeof disposer !== 'function') {\n $throw(`Invalid disposer, expected a function but got ${typeof disposer}`);\n }\n return { handler: a, disposer, type: FluxionModuleType.Api };\n }\n\n if (typeof a !== 'object' || a === null) {\n $throw(`Invalid argument, expected a FluxionModule object or a handler function, but got ${typeof a}`);\n }\n\n if (typeof a.handler !== 'function') {\n $throw(`Invalid FluxionModule, \"handler\" must be a function`);\n }\n\n if (a.disposer !== undefined && typeof a.disposer !== 'function') {\n $throw(`Invalid FluxionModule, \"disposer\" must be a function if provided`);\n }\n\n if (a.methods !== undefined && (!Array.isArray(a.methods) || a.methods.some((v) => typeof v !== 'string'))) {\n $throw(`Invalid FluxionModule, \"methods\" must be an array of strings if provided`);\n }\n\n if (\n a.middlewares !== undefined &&\n (!Array.isArray(a.middlewares) || a.middlewares.some((v) => typeof v !== 'function'))\n ) {\n $throw(`Invalid FluxionModule, \"middlewares\" must be an array of functions if provided`);\n }\n\n return { ...a, type: FluxionModuleType.Api };\n}\n\nexport function defineFluxionMiddleware(middleware: FluxionMiddleware): FluxionMiddleware {\n if (typeof middleware !== 'function') {\n $throw(`Invalid FluxionMiddleware, expected a function but got ${typeof middleware}`);\n }\n return middleware;\n}\n\nif (process.env.NODE_ENV !== 'production') {\n globalThis.$throw = (message: string) => {\n throw new Error('[fluxion error]' + message);\n };\n\n const int = (n: string | undefined, defaultValue: number): number => {\n if (n === undefined) {\n return defaultValue;\n }\n const parsed = Number.parseInt(n, 10);\n if (Number.isNaN(parsed)) {\n return defaultValue;\n }\n return parsed;\n };\n\n fluxion({\n dir: process.env.DYNAMIC_DIRECTORY ?? 'dynamicDirectory',\n host: process.env.HOST ?? 'localhost',\n port: int(process.env.PORT, 9000),\n metaPort: int(process.env.META_PORT, 9001),\n reloadDelay: process.env.RELOAD_DELAY ? Number.parseInt(process.env.RELOAD_DELAY, 10) : undefined,\n workerOptions: {\n maxWorkerCount: 4,\n },\n });\n}\n"],"mappings":"k0BAAA,SAAgB,EAAI,EAAK,IAAI,KAAQ,CAQnC,MAAO,GAPG,EAAG,YAOH,EAAE,GANF,OAAO,EAAG,SAAS,EAAI,CAAC,CAAC,CAAC,SAAS,EAAG,GAMjC,EAAE,GALP,OAAO,EAAG,QAAQ,CAAC,CAAC,CAAC,SAAS,EAAG,GAKvB,EAAE,GAJX,OAAO,EAAG,SAAS,CAAC,CAAC,CAAC,SAAS,EAAG,GAInB,EAAE,GAHjB,OAAO,EAAG,WAAW,CAAC,CAAC,CAAC,SAAS,EAAG,GAGf,EAAE,GAFvB,OAAO,EAAG,WAAW,CAAC,CAAC,CAAC,SAAS,EAAG,GAET,EAAE,GAD7B,OAAO,EAAG,gBAAgB,CAAC,CAAC,CAAC,SAAS,EAAG,GACR,GAC9C,CCTA,MAAa,EAAa,KAAK,UAElB,EAAQ,OAAO,KCFtB,EAAW,QAAQ,IAAI,iBAAmB,IAKzC,IAAA,uBACgB,EAAW,UAAY,UACxB,EAAW,UAAY,SACxB,EAAW,UAAY,YACpB,EAAW,UAAY,eACpB,EAAW,UAAY,WAC3B,EAAW,UAAY,aACrB,EAAW,UAAY,WAEzB,EAAW,WAAa,SAC1B,EAAW,WAAa,WACtB,EAAW,WAAa,YACvB,EAAW,WAAa,UAC1B,EAAW,WAAa,aACrB,EAAW,WAAa,UAC3B,EAAW,WAAa,WACvB,EAAW,WAAa,iBAElB,EAAW,WAAa,eAC1B,EAAW,WAAa,iBACtB,EAAW,WAAa,kBACvB,EAAW,WAAa,gBAC1B,EAAW,WAAa,mBACrB,EAAW,WAAa,gBAC3B,EAAW,WAAa,iBACvB,EAAW,WAAa,aAE5B,EAAW,WAAa,WAC1B,EAAW,WAAa,aACtB,EAAW,WAAa,cACvB,EAAW,WAAa,YAC1B,EAAW,WAAa,eACrB,EAAW,WAAa,YAC3B,EAAW,WAAa,aACvB,EAAW,WAAa,mBAElB,EAAW,YAAc,iBAC3B,EAAW,YAAc,mBACvB,EAAW,YAAc,oBACxB,EAAW,YAAc,kBAC3B,EAAW,YAAc,qBACtB,EAAW,YAAc,kBAC5B,EAAW,YAAc,mBACxB,EAAW,YAAc,YAGhC,EAAW,wBAA0B,YAErC,EAAW,wBAA0B,eAClC,EAAW,uBAAyB,YACvC,EAAW,wBAA0B,cACnC,EAAW,wBAA0B,SAC1C,EAAW,sBAAwB,KACvD,AAAA,IAAA,CAAA,CAAD,ECxBA,MAAM,EAAiB,GAA2B,CAChD,GAAI,CACF,OAAO,EAAW,CAAK,CACzB,MAAQ,CACN,MAAO,kBACT,CACF,EAEM,GAA0C,CAC9C,KAAM,GAAG,EAAK,KAAK,MAAM,EAAK,QAC9B,KAAM,GAAG,EAAK,OAAO,MAAM,EAAK,QAChC,MAAO,GAAG,EAAK,IAAI,OAAO,EAAK,QAC/B,KAAM,GAAG,EAAK,MAAM,MAAM,EAAK,QAC/B,MAAO,GAAG,EAAK,KAAK,OAAO,EAAK,QAChC,QAAS,GAAG,EAAK,OAAO,SAAS,EAAK,OACxC,EAEa,EAAkC,GAAoB,CACjE,GAAM,CAAE,MAAO,EAAU,UAAW,EAAc,MAAO,EAAU,QAAS,EAAY,GAAG,GAAW,EAEhG,EAAY,GAAG,EAAK,UAAU,GAAG,EAAa,GAAG,EAAK,QACtD,EAAQ,GAAc,IAAa,EACnC,EAAO,GAAc,EACrB,EAAa,EAAM,CAAM,CAAC,CAAC,OAAS,EAAI,GAAG,EAAK,MAAM,EAAc,CAAM,IAAI,EAAK,QAAU,GAGnG,QAAQ,IAAI,GAAG,EAAU,GAAG,EAAM,GAAG,IAAO,GAAY,CAC1D,EAKA,SAAS,EAAkB,EAAsD,CAC/E,IAAM,EAAe,EAAG,QAAQ,OAUhC,OATI,IAAiB,IAAA,IAAa,IAAiB,WAC1C,EAGL,IAAiB,YAEX,GAAoB,QAAQ,IAAI,EAAc,CAAK,CAAC,EAGvD,CACT,CAEA,SAAgB,GAAa,EAAoD,CAC/E,IAAM,EAAO,EAAkB,CAAE,EAqCjC,MAAO,CAlCL,MAAM,EAAiB,EAAe,EAAkC,CAAC,EAAS,CAChF,IAAM,EAAkB,CACtB,GAAG,EACH,UAAW,EAAI,EACf,QACA,OACF,EAEA,GAAI,CACF,EAAK,CAAK,CACZ,MAAQ,CAER,CACF,EACA,KAAK,EAAe,EAAwC,CAC1D,KAAK,MAAM,OAAQ,EAAO,CAAM,CAClC,EACA,KAAK,EAAe,EAAwC,CAC1D,KAAK,MAAM,OAAQ,EAAO,CAAM,CAClC,EACA,MAAM,EAAe,EAAwC,CAC3D,KAAK,MAAM,QAAS,EAAO,CAAM,CACnC,EACA,KAAK,EAAe,EAAwC,CAC1D,KAAK,MAAM,OAAQ,EAAO,CAAM,CAClC,EACA,MAAM,EAAe,EAAwC,CAC3D,KAAK,MAAM,QAAS,EAAO,CAAM,CACnC,EACA,QAAQ,EAAe,EAAwC,CAC7D,KAAK,MAAM,UAAW,EAAO,CAAM,CACrC,CAGU,CACd,CAKA,SAAgB,GAAmB,EAA2B,EAA4B,CACxF,IAAM,EAAY,IAAI,EAAI,GAE1B,MAAO,CACL,MAAM,EAAiB,EAAe,EAAuB,CAC3D,EAAW,MAAM,EAAO,GAAG,EAAU,GAAG,IAAS,CAAM,CACzD,EACA,KAAK,EAAe,EAAuB,CACzC,EAAW,KAAK,GAAG,EAAU,GAAG,IAAS,CAAM,CACjD,EACA,KAAK,EAAe,EAAuB,CACzC,EAAW,KAAK,GAAG,EAAU,GAAG,IAAS,CAAM,CACjD,EACA,MAAM,EAAe,EAAuB,CAC1C,EAAW,MAAM,GAAG,EAAU,GAAG,IAAS,CAAM,CAClD,EACA,KAAK,EAAe,EAAuB,CACzC,EAAW,KAAK,GAAG,EAAU,GAAG,IAAS,CAAM,CACjD,EACA,MAAM,EAAe,EAAuB,CAC1C,EAAW,MAAM,GAAG,EAAU,GAAG,IAAS,CAAM,CAClD,EACA,QAAQ,EAAe,EAAuB,CAC5C,EAAW,QAAQ,GAAG,EAAU,GAAG,IAAS,CAAM,CACpD,CACF,CACF,CAKA,MAAa,EACX,OAAO,MAAM,SAAY,WACpB,GAAwB,MAAM,QAAQ,CAAC,EAAI,EAAE,QAAU,OAAO,CAAC,EAC/D,GAAwB,GAAW,SAAW,OAAO,CAAC,ECrJ7D,SAAS,GAAqB,EAAyB,CAAC,EAA4B,CAClF,IAAM,EAAK,EAAQ,aAAe,CAAC,EAC7B,EAAiB,EAAG,gBAAkB,IAG5C,GAAI,IAAmB,MAAa,CAAC,OAAO,SAAS,CAAc,GAAK,EAAiB,KACvF,MAAA,MAAA,4GAAmG,EAErG,MAAO,CACL,eAAgB,EAAQ,gBAAkB,EAC1C,YAAa,CACX,uBAAwB,EAAG,wBAA0B,IACrD,iBACA,kBAAmB,EAAG,mBAAqB,GAC7C,CACF,CACF,CAKA,SAAS,EAAuB,EAA0B,EAA2B,CACnF,GAAI,OAAO,SAAS,CAAO,EACzB,OAAO,EAET,GAAI,OAAO,GAAY,SAAU,CAG/B,GAAI,CAAC,EAAQ,WAAW,YAAY,EAAG,CACrC,IAAM,EAAW,EAAA,QAAK,WAAW,CAAO,EAAI,EAAU,EAAA,QAAK,KAAK,EAAW,CAAO,EAClF,GAAI,EAAA,QAAG,WAAW,CAAQ,EACxB,OAAO,EAAA,QAAG,aAAa,CAAQ,CAEnC,CACA,OAAO,OAAO,KAAK,CAAO,CAC5B,CACA,MAAA,MAAA,gEAAuD,CACzD,CAKA,SAAS,EACP,EACA,EAC+C,CAC/C,GAAI,CAAC,EACH,OAGF,GAAI,OAAO,GAAU,WAAY,GAAkB,MAAM,QAAQ,CAAK,EACpE,MAAA,MAAA,wDAA+C,EAEjD,GAAI,OAAO,EAAM,KAAQ,SACvB,MAAA,MAAA,2DAAkD,EAEpD,GAAI,OAAO,EAAM,MAAS,SACxB,MAAA,MAAA,4DAAmD,EAGrD,IAAM,EAA4C,CAChD,IAAK,EAAuB,EAAM,IAAK,CAAS,EAChD,KAAM,EAAuB,EAAM,KAAM,CAAS,CACpD,EAUA,OARI,EAAM,KAAO,IAAA,KACX,MAAM,QAAQ,EAAM,EAAE,EACxB,EAAO,GAAK,EAAM,GAAG,IAAK,GAAS,EAAuB,EAAM,CAAS,CAAC,EAE1E,EAAO,GAAK,EAAuB,EAAM,GAAI,CAAS,GAInD,CACT,CAKA,SAAgB,EAAiB,EAA6C,CAC5E,GAAI,OAAO,GAAM,WAAY,GAAc,MAAM,QAAQ,CAAC,EACxD,MAAA,MAAA,kDAAyC,EAG3C,GAAM,CACJ,MAAM,EAAA,QAAK,WAAW,EAAE,GAAG,EAAI,EAAE,IAAM,EAAA,QAAK,KAAK,QAAQ,IAAI,EAAG,EAAE,GAAG,EACrE,OACA,OACA,mBAAmB,IACnB,sBAAsB,IACtB,0BAA0B,GAAK,IAC/B,WAAW,EAAO,EAClB,YAAY,QAAQ,IAAI,EACxB,gBAAgB,CAAC,EACjB,kBAAkB,IAClB,cAAc,IACd,UAAU,CAAC,MAAM,EACjB,aAAa,CAAC,SAAS,EACvB,UAAU,CACR,qBACA,aACA,aACA,cACA,gBACA,cACA,WACA,eACA,iBACA,oBACA,WACA,WACF,EACA,QACA,gBAAgB,IACd,EAEE,EAAS,EAAE,QAAU,WAC3B,GAAI,IAAW,YAAc,IAAW,aAAe,OAAO,GAAW,WACvE,MAAA,MAAA,oGAA2F,EAG7F,GAAI,OAAO,GAAQ,SACjB,MAAA,MAAA,qDAA4C,EAG9C,GAAI,OAAO,GAAc,SACvB,MAAA,MAAA,2DAAkD,EAGpD,GAAI,OAAO,GAAS,SAClB,MAAA,MAAA,sDAA6C,EAG/C,GAAI,CAAC,OAAO,cAAc,CAAgB,GAAK,GAAoB,IACjE,MAAA,MAAA,qFAA4E,EAG9E,GAAI,CAAC,OAAO,cAAc,CAAmB,GAAK,GAAuB,IACvE,MAAA,MAAA,wFAA+E,EAGjF,GAAI,OAAO,GAAgB,UAAY,GAAe,GAAK,CAAC,OAAO,cAAc,CAAW,EAC1F,MAAA,MAAA,uEAA8D,EAGhE,GAAI,EAAc,GAChB,MAAA,MAAA,gFAAuE,EAGzE,GAAI,OAAO,GAAS,UAAY,CAAC,OAAO,cAAc,CAAI,EACxD,MAAA,MAAA,gEAAuD,EAGzD,GAAI,GAAQ,GAAK,EAAO,MACtB,MAAA,MAAA,uDAA8C,EAGhD,GAAI,OAAO,GAAa,UAAY,CAAC,OAAO,cAAc,CAAQ,EAChE,MAAA,MAAA,oEAA2D,EAG7D,GAAI,GAAY,GAAK,EAAW,MAC9B,MAAA,MAAA,2DAAkD,EAGpD,GAAI,IAAa,EACf,MAAA,MAAA,oFAA2E,EAG7E,GAAI,OAAO,GAAkB,WAAY,GAA0B,MAAM,QAAQ,CAAa,EAC5F,MAAA,MAAA,gEAAuD,EAGzD,GAAI,OAAO,GAAoB,UAAY,GAAmB,GAAK,CAAC,OAAO,cAAc,CAAe,EACtG,MAAA,MAAA,2EAAkE,EAOpE,OAJK,EAAA,QAAG,WAAW,CAAG,GACpB,EAAA,QAAG,UAAU,EAAK,CAAE,UAAW,EAAK,CAAC,EAGhC,CACL,MACA,OACA,OACA,mBACA,sBACA,0BACA,cACA,WACA,YACA,cAAe,GAAqB,CAAa,EACjD,kBACA,SACA,UACA,aACA,UACA,gBACA,MAAO,EAAsB,EAAO,CAAS,CAC/C,CACF,CCjLA,MAAa,EAAoB,GAA2C,CAAA,GAAmB,CAAC,CAAC,SAAS,GAAG,IAAI,EAEpG,EAAmB,GAC9B,gBAAgF,CAAC,CAAC,SAAS,GAAG,IAAI,EChCvF,EAAiB,GAA2B,QAAQ,OAAO,CAAO,EAElE,GAAgB,EAAwB,IAA4B,EAAO,KAAK,CAAO,ECFvF,EAAsB,OAAO,IAAI,8BAA8B,EAC/D,EAAuB,OAAO,IAAI,wBAAwB,EAC1D,EAA0B,OAAO,IAAI,2BAA2B,EAEhE,GAA+C,CAC1D,OAAQ,0BACR,QAAS,2BACT,OAAQ,eACR,MAAO,iCACP,QAAS,kCACT,OAAQ,kCACR,OAAQ,YACR,OAAQ,aACR,QAAS,aACT,OAAQ,gBACR,OAAQ,4BACR,QAAS,YACX,EAqBa,OAAa,CAAC,ECtC3B,SAAgB,EAAS,EAAqB,EAAkB,EAAA,IAA0C,CACxG,EAAI,WAAa,EACjB,EAAI,UAAU,eAAgB,iCAAiC,EAC/D,EAAI,IAAI,KAAK,UAAU,CAAO,CAAC,CACjC,CAEA,SAAgB,EAAa,EAAqB,EAAkB,EAAA,IAA0C,CACxG,MAAI,cAIR,IAAI,EAAI,YAAa,CACnB,EAAI,IAAI,EACR,MACF,CAEA,EAAS,EAAK,EAAS,CAAU,CAFjC,CAGF,CCbA,SAAgB,EACd,EACA,EACa,CACb,IAAM,EAASA,EAAAA,QAAK,cAAc,EAAK,IAAQ,CAC7C,IAAM,EAAS,EAAI,QAAU,MAEzB,EAAW,IACf,GAAI,CACF,EAAW,IAAI,IAAI,EAAI,KAAO,IAAK,sBAAsB,CAAC,CAAC,QAC7D,MAAQ,CACN,EAAS,EAAK,CAAE,QAAS,0BAA2B,EAAA,GAAsB,EAC1E,MACF,CAEA,GAAI,IAAW,OAAS,IAAA,oBAAuC,CAC7D,EAAS,EAAK,CACZ,GAAI,GACJ,KAAM,UACN,IAAK,QAAQ,IACb,IAAK,KAAK,IAAI,EACd,cAAe,OAAO,QAAQ,OAAO,CAAC,CAAC,QAAQ,CAAC,CAAC,CACnD,CAAC,EACD,MACF,CAEA,GAAI,IAAW,OAAS,IAAA,oBAAuC,CAC7D,EAAS,EAAK,CACZ,GAAI,GACJ,IAAK,KAAK,IAAI,EACd,QAAS,EAAmB,CAC9B,CAAC,EACD,MACF,CAEA,EAAS,EAAK,CAAE,QAAS,WAAY,EAAA,GAAoB,CAC3D,CAAC,EAqBD,OAnBA,EAAO,GAAG,gBAAmB,CAC3B,EAAG,OAAO,KAAK,iBAAkB,CAC/B,IAAK,QAAQ,IACb,KAAM,EAAG,QAAQ,KACjB,KAAM,EAAG,QAAQ,SACjB,OAAQ,WACV,CAAC,CACH,CAAC,EAED,EAAO,GAAG,QAAU,GAAU,CAC5B,EAAG,OAAO,MAAM,eAAgB,CAC9B,KAAM,EAAG,QAAQ,KACjB,KAAM,EAAG,QAAQ,SACjB,MAAO,EAAgB,CAAK,CAC9B,CAAC,EACD,QAAQ,KAAK,CAAC,CAChB,CAAC,EAED,EAAO,OAAO,EAAG,QAAQ,SAAU,EAAG,QAAQ,IAAI,EAC3C,CACT,CCxDA,MAAM,EAAa,GAAkB,QAAQ,EAAQ,KAAO,KAAA,CAAM,QAAQ,CAAC,CAAC,EAStE,EAAoB,IAG1B,SAAgB,EAAY,EAA2D,CACrF,GAAI,CAAC,EAAA,QAAQ,UACX,MAAA,MAAA,wEAA+D,EAGjE,GAAM,CAAE,iBAAkB,EAAG,QACvB,EAAc,EAAc,YAC5B,EAAW,KAAK,IAAI,EAAG,EAAA,QAAG,KAAK,CAAC,CAAC,MAAM,EACvC,EAAc,KAAK,IAAI,EAAG,KAAK,IAAI,EAAc,gBAAkB,KAAK,IAAI,EAAG,CAAQ,EAAG,CAAQ,CAAC,EAEzG,EAAG,OAAO,KAAK,iBAAkB,CAC/B,IAAK,QAAQ,IACb,QAAS,EACT,KAAM,EAAG,QAAQ,KACjB,KAAM,EAAG,QAAQ,KACjB,SAAU,EAAG,QAAQ,QACvB,CAAC,EAED,IAAM,EAAU,IAAI,IAKd,EAAa,IAAI,IAEjB,EAAwB,GAAiB,CAC7C,IAAM,EAAM,KAAK,IAAI,EACf,GAAO,EAAW,IAAI,CAAI,GAAK,CAAC,EAAA,CAAG,OAAQ,GAAM,EAAM,EAAI,CAAiB,EAElF,OADA,EAAW,IAAI,EAAM,CAAG,EACjB,EAAI,MACb,EAEM,EAAiB,GAAiB,CACtC,IAAM,EAAM,KAAK,IAAI,EACf,GAAO,EAAW,IAAI,CAAI,GAAK,CAAC,EAAA,CAAG,OAAQ,GAAM,EAAM,EAAI,CAAiB,EAClF,EAAI,KAAK,CAAG,EACZ,EAAW,IAAI,EAAM,CAAG,CAC1B,EAEM,EAAc,GAAiB,EAAqB,CAAI,GAAK,EA8CnE,EAA2B,OA3ClB,CACL,WAAY,QAAQ,IACpB,KAAM,EAAG,QAAQ,KACjB,KAAM,EAAG,QAAQ,KACjB,SAAU,EAAG,QAAQ,SACrB,cAAe,OAAO,QAAQ,OAAO,CAAC,CAAC,QAAQ,CAAC,CAAC,EACjD,QAAS,MAAM,KAAK,EAAQ,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,EAAU,KAAU,CAC/D,GAAM,CAAE,YAAa,EACf,EAAQ,EAAK,UACnB,MAAO,CACL,WACA,KAAM,EAAK,KACX,IAAK,EAAK,KAAO,EAAS,QAAQ,KAAO,KACzC,MAAO,EAAK,MACZ,cAAe,EAAK,eAAiB,KACrC,UAAW,EAAK,UAChB,QAAS,EAAK,SAAW,KACzB,UAAW,EAAS,YAAY,EAChC,KAAM,EAAS,OAAO,EACtB,sBAAuB,EAAS,sBAChC,WAAY,EAAK,YAAc,KAC/B,UAAW,EAAK,WAAa,KAC7B,MACE,IAAU,IAAA,GACN,KACA,CACE,GAAI,EAAM,GACV,cAAe,EAAM,cACrB,IAAK,EAAM,IACX,OAAQ,CACN,GAAG,EAAM,OACT,MAAO,EAAU,EAAM,OAAO,GAAG,EACjC,YAAa,EAAU,EAAM,OAAO,SAAS,EAC7C,WAAY,EAAU,EAAM,OAAO,QAAQ,EAC3C,WAAY,EAAU,EAAM,OAAO,QAAQ,EAC3C,eAAgB,EAAU,EAAM,OAAO,YAAY,CACrD,CACF,CACR,CACF,CAAC,CACH,EAG+C,EAMjD,IAAM,GAAmB,EAAmB,IAAmB,CAC7D,IAAK,IAAM,KAAK,EAAQ,OAAO,EAC7B,GAAI,EAAE,QAAU,aAAc,OAEhC,GAAI,EAAW,EAAK,IAAI,EAAG,CACzB,EAAG,OAAO,KAAK,0BAA2B,CACxC,KAAM,EAAK,KACX,IAAK,EAAK,IACV,SACA,SAAU,EACV,IAAK,CACP,CAAC,EACD,MACF,CACA,EAAc,EAAK,IAAI,EACvB,EAAK,MAAQ,aACb,EAAK,cAAgB,EACrB,EAAG,OAAO,KAAK,kBAAmB,CAAE,KAAM,EAAK,KAAM,IAAK,EAAK,IAAK,QAAO,CAAC,EAC5E,EAAK,SAAS,KAAK,CACrB,EAKM,GAA8B,EAAmB,IAA8B,CACnF,IAAM,EAAQ,EAAU,EAAM,OAAO,GAAG,EACxC,GAAI,EAAQ,EAAY,uBAAwB,CAC9C,EAAgB,EAAM,+BAA+B,EAAM,OAAO,EAAY,uBAAuB,GAAG,EACxG,MACF,CACA,IAAM,EAAW,EAAM,cAAgB,IACnC,EAAW,EAAY,mBACzB,EACE,EACA,sBAAsB,KAAK,MAAM,EAAW,GAAI,EAAE,MAAM,KAAK,MAAM,EAAY,kBAAoB,GAAI,EAAE,EAC3G,CAEJ,EAIM,EAAoB,GAAgB,CACxC,IAAK,IAAM,KAAQ,EAAQ,OAAO,EAAG,CACnC,GAAI,EAAK,QAAU,SAAW,EAAK,aAAe,IAAA,GAAW,SAC7D,IAAM,EAAU,EAAM,EAAK,WACvB,EAAU,EAAY,gBACxB,EACE,EACA,+BAA+B,KAAK,MAAM,EAAU,GAAI,EAAE,MAAM,KAAK,MAAM,EAAY,eAAiB,GAAI,EAAE,EAChH,CAEJ,CACF,EAEM,EAAa,GAAiB,CAClC,EAAa,EAAA,QAAQ,KAAK,CAAE,UAAW,OAAO,CAAI,CAAE,CAAC,EAAG,CAAI,CAC9D,EAEM,GAAgB,EAAwB,IAAuB,CACnE,IAAM,EAA0B,CAC9B,MAAO,WACP,IAAK,EAAO,QAAQ,IACpB,OACA,UAAW,KAAK,IAAI,EACpB,SAAU,CACZ,EACA,EAAQ,IAAI,EAAO,GAAI,CAAU,EAEjC,EAAO,GAAG,UAAY,GAAuB,CACtC,KAAgB,CAAG,EAIxB,IAAI,EAAI,OAAA,IAA4B,CAClC,IAAM,EAAM,KAAK,IAAI,EAAI,EAAI,OAC7B,EAAW,IAAM,EAAI,IACrB,EAAW,WAAa,KAAK,IAAI,EACjC,EAAW,UAAY,EACvB,MACF,CAEA,GAAI,EAAI,OAAA,IAA6B,CACnC,EAAW,MAAQ,QACnB,EAAW,IAAM,EAAI,IACrB,EAAW,QAAU,KAAK,IAAI,EAC9B,EAAG,OAAO,KAAK,cAAe,CAAE,SAAU,EAAO,GAAI,OAAM,IAAK,EAAI,GAAI,CAAC,EACzE,MACF,CAEA,GAAI,EAAI,OAAA,IAA+B,CACrC,EAAW,MAAQ,UACnB,EAAW,IAAM,EAAI,IACrB,EAAG,OAAO,KAAK,gBAAiB,CAAE,SAAU,EAAO,GAAI,OAAM,IAAK,EAAI,GAAI,CAAC,EAC3E,MACF,CAEI,EAAI,OAAA,MACN,EAAW,IAAM,EAAI,IACrB,EAAW,UAAY,EAAI,MACvB,EAAW,QAAU,SACvB,EAA2B,EAAY,EAAI,KAAK,EArBpD,CAwBF,CAAC,EAED,EAAO,GAAG,QAAS,EAAM,IAAW,CAClC,IAAM,EAAO,EAAQ,IAAI,EAAO,EAAE,EAClC,EAAQ,OAAO,EAAO,EAAE,EACxB,IAAM,EAAa,GAAM,KACnB,EAAW,GAAM,QAAU,aAC3B,EAAS,GAAM,eAAiB,KAEtC,KAAG,OAAO,KAAK,eAAgB,CAC7B,SAAU,EAAO,GACjB,KAAM,GAAc,KACpB,IAAK,EAAO,QAAQ,KAAO,UAC3B,OACA,OAAQ,GAAU,OAClB,WACA,QACF,CAAC,EAEG,IAAe,IAAA,GAEnB,IAAI,EAAU,CAEZ,EAAU,CAAU,EACpB,MACF,CAIA,GADA,EAAc,CAAU,EACpB,EAAW,CAAU,EAAG,CAC1B,EAAG,OAAO,MAAM,0BAA2B,CACzC,KAAM,EACN,SAAU,EACV,IAAK,CACP,CAAC,EACD,MACF,CACA,EAAU,CAAU,CAZpB,CAaF,CAAC,CACH,EAEA,IAAK,IAAI,EAAI,EAAG,EAAI,EAAa,IAC/B,EAAU,EAAI,CAAC,EAiBjB,gBAdoC,CAClC,IAAM,EAAS,KAAK,IAAI,EACxB,IAAK,IAAM,KAAQ,EAAQ,OAAO,EAC3B,KAAK,SAAS,YAAY,EAG/B,GAAI,CACF,EAAa,EAAK,SAAU,CAAE,KAAA,IAA0B,QAAO,CAAC,CAClE,MAAQ,CAER,CAEF,EAAiB,KAAK,IAAI,CAAC,CAC7B,EAAG,GACK,CAAC,CAAC,MAAM,CAClB,CC9QA,SAAgB,EAA8C,EAAO,GAAG,EAAqB,CAC3F,OAAO,IAAI,SAAwB,EAAS,IAAW,CAErD,GAAI,CACF,IAAM,EAAI,EAAG,GAAG,CAAI,EAChB,aAAa,QACf,EAAE,KAAK,CAAO,CAAC,CAAC,MAAM,CAAM,EAE5B,EAAQ,CAAC,CAEb,OAAS,EAAO,CACd,EAAO,CAAK,CACd,CACF,CAAC,CACH,CCjBA,SAAgB,EAAU,EAA8B,CACtD,IAAM,EAAe,EAAI,gBAAgB,mBACzC,GAAI,EAAc,CAChB,IAAM,EAAiB,EAAa,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC,EAAE,EAAE,KAAK,EAC5D,GAAI,GAAkB,EAAe,OAAS,EAC5C,OAAO,CAEX,CAEA,IAAM,EAAS,EAAI,gBAAgB,YAAY,GAAG,EAAE,CAAC,KAAK,EAK1D,OAJI,IAAW,IAAA,GAIR,EAAI,OAAO,eAAiB,UAH1B,CAIX,CAEA,SAAgB,EAAqB,EAA0C,CAC7E,GAAI,IAAgB,IAAA,GAClB,MAAO,GAGT,IAAM,EAAa,EAAY,YAAY,EAE3C,OACE,EAAW,WAAW,OAAO,GAC7B,EAAW,SAAS,MAAM,GAC1B,EAAW,SAAS,KAAK,GACzB,EAAW,SAAS,uBAAuB,GAC3C,EAAW,SAAS,YAAY,CAEpC,CC/BA,SAAgB,EAAM,EAA6C,CAC7D,OAAW,IAAA,GAIf,GAAI,CACF,OAAO,IAAI,IAAI,EAAQ,sBAAc,CACvC,MAAQ,CACN,MACF,CACF,CCZA,SAAgB,EAAW,EAAkE,CAC3F,IAAM,EAA2C,CAAC,EAElD,IAAK,GAAM,CAAC,EAAK,KAAU,EAAa,QAAQ,EAAG,CACjD,IAAM,EAAW,EAAM,GAEvB,GAAI,IAAa,IAAA,GAAW,CAC1B,EAAM,GAAO,EACb,QACF,CAEA,GAAI,MAAM,QAAQ,CAAQ,EAAG,CAC3B,EAAS,KAAK,CAAK,EACnB,QACF,CAEA,EAAM,GAAO,CAAC,EAAU,CAAK,CAC/B,CAEA,OAAO,CACT,CCRA,SAAS,EAA+B,EAAuB,EAAyC,CACtG,IAAM,EAAgB,MACpB,2BAA2B,EAAc,SAAS,EAAE,iBAAiB,EAAS,SAAS,EAAE,OAC3F,EAIA,MAFA,GAAU,KAAO,yBAEV,CACT,CAEA,SAAS,EAAe,EAAgE,CACtF,OAAO,MAAM,QAAQ,CAAW,EAAI,EAAY,GAAK,CACvD,CAEA,SAAS,GAAkC,CACzC,MAAO,CACL,OAAQ,GACR,MAAO,EACP,UAAW,EACb,CACF,CAEA,SAAS,GACP,EACA,EACA,EACA,EACa,CAcb,OAbI,IAAe,EACV,EAAmB,EAGxB,EAAqB,CAAW,EAC3B,CACL,OAAQ,GACR,MAAO,EAAc,SAAS,MAAM,EACpC,MAAO,EACP,WACF,EAGK,CACL,OAAQ,GACR,MAAO,iBAAiB,EAAW,SACnC,MAAO,EACP,WACF,CACF,CAEA,eAAe,GACb,EACA,EACA,EACA,EAAkB,KAC8C,CAQhE,GAPI,IAAW,OAAS,IAAW,QAO/B,EAAI,cACN,MAAO,CACL,QAAS,IAAA,GACT,QAAS,EAAmB,CAC9B,EAGF,IAAM,EAAmB,EAAe,EAAI,QAAQ,iBAAiB,EAC/D,EAAgB,IAAqB,IAAA,GAAoD,IAAxC,OAAO,SAAS,EAAkB,EAAE,EAE3F,GAAI,OAAO,SAAS,CAAa,GAAK,EAAgB,EACpD,MAAM,EAA+B,EAAe,CAAQ,EAG9D,OAAO,IAAI,SAAS,EAAS,IAAW,CACtC,IAAM,EAA0B,CAAC,EAC3B,EAA0B,CAAC,EAC7B,EAAa,EACb,EAAe,EACf,EAAmB,GACnB,EAAU,GAER,MAAsB,CAC1B,EAAI,IAAI,OAAQ,CAAM,EACtB,EAAI,IAAI,MAAO,CAAK,EACpB,EAAI,IAAI,QAAS,CAAO,EACxB,EAAI,IAAI,UAAW,CAAS,CAC9B,EAEM,EAAU,GAA6B,CACvC,IAIJ,EAAU,GACV,EAAO,EACT,EAEM,EAAU,GAA8C,CAC5D,IAAM,EAAc,OAAO,SAAS,CAAK,EAAI,EAAQ,OAAO,KAAK,CAAK,EAGtE,GAFA,GAAc,EAAY,WAEtB,EAAa,EAAU,CACzB,EAAQ,EACR,EAAI,OAAO,EACX,MAAa,CACX,EAAO,EAA+B,EAAY,CAAQ,CAAC,CAC7D,CAAC,EACD,MACF,CAIA,GAFA,EAAc,KAAK,CAAW,EAE1B,EAAe,EAAiB,CAClC,IAAM,EAAY,EAAkB,EAC9B,EAAY,EAAY,SAAS,EAAG,CAAS,EACnD,EAAc,KAAK,CAAS,EAC5B,GAAgB,EAAU,OAEtB,EAAU,OAAS,EAAY,SACjC,EAAmB,GAEvB,KACE,GAAmB,EAEvB,EAEM,MAAoB,CACxB,EAAQ,EACR,MAAa,CACX,IAAM,EAAU,EAAc,OAAS,EAAI,OAAO,OAAO,CAAa,EAAI,IAAA,GAG1E,EAAQ,CACN,UACA,QAAS,GAJW,EAAc,OAAS,EAAI,OAAO,OAAO,CAAa,EAAI,OAAO,MAAM,CAAC,EAM1F,GAAS,YAAc,EACvB,EAAe,EAAI,QAAQ,eAAe,EAC1C,CACF,CACF,CAAC,CACH,CAAC,CACH,EAEM,EAAW,GAAuB,CACtC,EAAQ,EACR,MAAa,CACX,EAAO,CAAK,CACd,CAAC,CACH,EAEM,MAAwB,CAC5B,EAAQ,EACR,MAAa,CACX,EAAW,MAAM,oCAAoC,CAAC,CACxD,CAAC,CACH,EAEA,EAAI,GAAG,OAAQ,CAAM,EACrB,EAAI,KAAK,MAAO,CAAK,EACrB,EAAI,KAAK,QAAS,CAAO,EACzB,EAAI,KAAK,UAAW,CAAS,CAC/B,CAAC,CACH,CAEA,eAAsB,GACpB,EACA,EACA,EAC8D,CAC9D,GAAM,CAAE,UAAS,WAAY,MAAM,GAA2B,EAAK,EAAQ,CAAQ,EAEnF,GAAI,IAAY,IAAA,IAAa,EAAQ,aAAe,EAClD,MAAO,CACL,KAAM,CAAC,EACP,SACF,EAGF,IAAM,EAAc,EAAe,EAAI,QAAQ,eAAe,CAAC,EAAE,YAAY,GAAK,GAElF,GAAI,EAAY,SAAS,MAAM,EAAG,CAChC,IAAM,EAAW,EAAQ,SAAS,MAAM,CAAC,CAAC,KAAK,EAE/C,GAAI,EAAS,SAAW,EACtB,MAAO,CACL,KAAM,CAAC,EACP,SACF,EAGF,GAAI,CACF,IAAM,EAAS,KAAK,MAAM,CAAQ,EASlC,OAPuB,OAAO,GAAW,UAArC,GAAiD,CAAC,MAAM,QAAQ,CAAM,EACjE,CACL,KAAM,EACN,SACF,EAGK,CACL,KAAM,CAAE,MAAO,CAAO,EACtB,SACF,CACF,MAAQ,CACN,MAAO,CACL,KAAM,CAAE,IAAK,CAAS,EACtB,SACF,CACF,CACF,CAgBA,OAdI,EAAY,SAAS,uBAAuB,EACvC,CACL,KAAM,EAAW,IAAI,gBAAgB,EAAQ,SAAS,MAAM,CAAC,CAAC,EAC9D,SACF,EAGE,EAAqB,CAAW,EAC3B,CACL,KAAM,CAAE,IAAK,EAAQ,SAAS,MAAM,CAAE,EACtC,SACF,EAGK,CACL,KAAM,CAAC,EACP,SACF,CACF,CCnPA,SAAgB,GAAY,EAA0D,CACpF,GAAI,CAAC,EACH,MAAO,CAAC,EAGV,IAAM,EAAkC,CAAC,EACnC,EAAQ,EAAa,MAAM,GAAG,EAEpC,IAAK,IAAM,KAAQ,EAAO,CACxB,GAAM,CAAC,EAAK,GAAG,GAAc,EAAK,MAAM,GAAG,EAC3C,GAAI,CAAC,EAAK,SAEV,IAAM,EAAa,EAAI,KAAK,EACtB,EAAQ,EAAW,KAAK,GAAG,CAAC,CAAC,KAAK,EACxC,EAAQ,GAAc,mBAAmB,CAAK,CAChD,CAEA,OAAO,CACT,CCEA,MAAM,GAAU,EAA2B,EAAmB,IAC5D,QAAQ,KAAK,CAAC,EAAa,IAAI,QAAS,GAAM,eAAiB,EAAE,CAAI,EAAG,CAAS,CAAC,CAAC,CAAC,EAEtF,SAAgB,GAAmB,EAAgD,CACjF,IAAM,EAAiB,MAAO,EAA2B,IAA6B,CACpF,IAAM,EAAS,EAAI,QAAU,MACvB,EAAK,EAAU,CAAG,EAClB,EAAM,EAAM,EAAI,GAAG,EACzB,GAAI,IAAQ,IAAA,GAAW,CACrB,EAAa,EAAK,CAAE,QAAS,mCAAoC,EAAA,GAAsB,EACvF,MACF,CAEA,IAAM,EAAgC,CACpC,SACA,KACA,MACA,MAAO,EAAW,EAAI,YAAY,EAClC,KAAM,CAAC,EACP,QAAS,EAAI,QACb,OAAQ,GAAY,EAAI,QAAQ,MAA4B,CAC9D,EAEI,EAA2B,CAC7B,OAAQ,GACR,MAAO,EACP,UAAW,EACb,EAEA,EAAG,OAAO,KAAK,MAAO,CAAE,SAAQ,KAAI,KAAM,EAAI,QAAS,CAAC,EAExD,IAAM,EAAQ,YAAY,IAAI,EAC9B,EAAI,KAAK,aAAgB,CACvB,IAAM,EAAkC,CACtC,SAAU,QAAQ,IAAI,WAAa,YACnC,SACA,KACA,KAAM,EAAI,SACV,OAAQ,EAAI,WACZ,UAAW,YAAY,IAAI,EAAI,EAAA,CAAO,QAAQ,CAAC,CACjD,EAEI,EAAM,EAAW,KAAK,CAAC,CAAC,OAAS,IACnC,EAAO,MAAQ,EAAW,OAGxB,EAAY,SACd,EAAO,KAAO,EAAY,MAC1B,EAAO,UAAY,EAAY,MAC/B,EAAO,cAAgB,EAAY,WAGrC,EAAG,OAAO,KAAK,MAAO,CAAM,CAC9B,CAAC,EAGD,GAAI,CACF,GAAI,EAAW,IAAI,SAAS,WAAA,YAA4B,EAAG,CACzD,EAAa,EAAK,CAAE,QAAS,WAAY,EAAA,GAAoB,EAC7D,MACF,CAEA,IAAM,EAAS,MAAM,GAAU,EAAK,EAAW,OAAQ,EAAG,QAAQ,eAAe,EACjF,EAAW,KAAO,EAAO,KACzB,EAAc,EAAO,QAErB,IAAM,EAAI,MAAM,EAAG,OAAO,UAAU,CAAG,EACvC,GAAI,CAAC,EAAG,CACN,EAAa,EAAK,CAAE,QAAS,WAAY,EAAA,GAAoB,EAC7D,MACF,CAEA,GAAI,EAAI,QAAU,EAAE,SAAW,CAAC,EAAE,QAAQ,SAAS,EAAI,MAAM,EAAG,CAC9D,EAAa,EAAK,CAAE,QAAS,oBAAqB,EAAA,GAA4B,EAC9E,MACF,CAEA,IAAM,EACJ,EAAE,OAAA,EACG,EAAE,kBAAoB,EAAG,QAAQ,iBAClC,EAAG,QAAQ,wBAGjB,GAAI,EAAE,YACJ,IAAK,IAAI,EAAI,EAAG,EAAI,EAAE,YAAY,OAAQ,IAAK,CAO7C,GAAI,MANiB,EACnB,EAAW,EAAE,YAAY,GAAI,EAAY,EAAK,CAAG,EACjD,EAAG,QAAQ,oBACX,CACF,IAEe,EAAyB,CACtC,EAAG,OAAO,KAAK,oBAAqB,CAClC,OAAQ,EAAW,OACnB,GAAI,EAAW,EACjB,CAAC,EACD,EAAa,EAAK,CAAE,QAAS,uBAAwB,EAAA,GAA+B,EACpF,MACF,CACA,GAAI,EAAI,cACN,OAEF,GAAI,EAAI,YAAa,CACnB,EAAI,IAAI,EACR,MACF,CACF,CAGF,IAAM,EAAS,MAAM,EAAO,EAAW,EAAE,QAAS,EAAY,EAAK,CAAG,EAAG,EAAW,CAAoB,EAExG,GAAI,IAAW,EAAsB,CACnC,EAAG,OAAO,KAAK,iBAAkB,CAC/B,OAAQ,EAAW,OACnB,GAAI,EAAW,EACjB,CAAC,EACD,EAAa,EAAK,CAAE,QAAS,mBAAoB,EAAA,GAA+B,EAChF,MACF,CAEI,IAAW,GACb,EAAa,EAAK,CAAM,CAE5B,OAAS,EAAO,CACd,EAAG,OAAO,MAAM,gBAAiB,CAC/B,OAAQ,EAAW,OACnB,GAAI,EAAW,GACf,KAAM,EAAW,IAAI,SACrB,MAAO,EAAgB,CAAK,CAC9B,CAAC,EAEI,EAAgC,OAAS,yBAC5C,EAAa,EAAK,CAAE,QAAS,EAAgB,CAAK,CAAE,EAAA,GAA2B,EAE/E,EAAa,EAAK,CAAE,QAAS,EAAgB,CAAK,CAAE,EAAA,GAA+B,CAEvF,CACF,EAEM,EAAS,EAAG,QAAQ,MACtBC,EAAAA,QAAM,aACJ,CACE,IAAK,EAAG,QAAQ,MAAM,IACtB,KAAM,EAAG,QAAQ,MAAM,KACvB,GAAI,EAAG,QAAQ,MAAM,EACvB,EACA,CACF,EACAC,EAAAA,QAAK,aAAa,CAAc,EAyBpC,OAvBA,EAAO,GAAG,YAAe,CACvB,EAAG,OAAO,KAAK,eAAgB,CAC7B,KAAM,EAAG,QAAQ,KACjB,KAAM,EAAG,QAAQ,IACnB,CAAC,CACH,CAAC,EAED,EAAO,OAAO,EAAG,QAAQ,KAAM,EAAG,QAAQ,SAAY,CACpD,EAAG,OAAO,KAAK,gBAAiB,CAC9B,IAAK,QAAQ,IACb,SAAU,EAAG,QAAQ,MAAQ,QAAU,OACvC,KAAM,EAAG,QAAQ,KACjB,KAAM,EAAG,QAAQ,IACnB,CAAC,EACD,EAAG,OAAO,KAAK,mBAAoB,CAAE,UAAW,EAAG,QAAQ,GAAI,CAAC,CAClE,CAAC,EAED,EAAO,GAAG,QAAU,GAAU,CAC5B,EAAG,OAAO,MAAM,cAAe,CAC7B,MAAO,EAAgB,CAAK,CAC9B,CAAC,CACH,CAAC,EAEM,CACT,CC5LA,MAAM,OAA2B,CAC/B,IAAI,EAAmB,QAAQ,SAAS,EACpC,EAAa,KAAK,IAAI,EAmC1B,gBAjCmC,CACjC,IAAM,EAAM,KAAK,IAAI,EACf,EAAgB,KAAK,IAAI,GAAI,EAAM,GAAc,GAAI,EACrD,EAAW,QAAQ,SAAS,CAAgB,EAC5C,EAAa,SAAU,EAAS,KAAO,EAAS,QAAU,EAAiB,IAAA,CAAK,QAAQ,CAAC,CAAC,EAEhG,EAAmB,QAAQ,SAAS,EACpC,EAAa,EAEb,IAAM,EAAc,QAAQ,YAAY,EACxC,EAAc,CACZ,KAAA,IACA,IAAK,QAAQ,IACb,MAAO,CACL,GAAI,EACJ,IAAK,QAAQ,IACb,cAAe,OAAO,QAAQ,OAAO,CAAC,CAAC,QAAQ,CAAC,CAAC,EACjD,IAAK,CACH,WAAY,EAAS,KACrB,aAAc,EAAS,OACvB,QAAS,CACX,EACA,OAAQ,CACN,IAAK,EAAY,IACjB,UAAW,EAAY,UACvB,SAAU,EAAY,SACtB,SAAU,EAAY,SACtB,aAAc,EAAY,YAC5B,CACF,CACF,CAAC,CACH,EAAG,GAEI,CAAC,CAAC,MAAM,CACjB,EAEA,SAAgB,GAAW,EAAoB,CAC7C,GAAI,EAAA,QAAQ,UACV,MAAA,MAAA,sEAA6D,EAG/D,QAAQ,GAAG,UAAY,GAAwB,CACxC,KAAiB,CAAG,GAIrB,EAAI,OAAA,IAA6B,CACnC,EAAc,CAAE,KAAA,IAAyB,IAAK,QAAQ,IAAK,OAAQ,EAAI,OAAQ,WAAY,KAAK,IAAI,CAAE,CAAC,EACvG,MACF,CACF,CAAC,EAED,EAAc,CAAE,KAAA,IAA4B,IAAK,QAAQ,GAAI,CAAC,EAC9D,GAAmB,EAEnB,GAAI,CACF,GAAmB,CAAE,EACrB,EAAc,CAAE,KAAA,IAA0B,IAAK,QAAQ,GAAI,CAAC,CAC9D,OAAS,EAAG,CACV,EAAG,OAAO,MAAM,wBAAyB,CACvC,IAAK,QAAQ,IACb,MAAO,EAAgB,CAAC,CAC1B,CAAC,EACD,QAAQ,KAAK,CAAC,CAChB,CACF,CCxEA,IAAsB,EAAtB,KAAyC,CACvC,GAEA,MAAuC,KACvC,aAAgC,IAAI,IAEpC,YAAY,EAAoB,CAC9B,KAAK,GAAK,CACZ,CAKA,MAAgB,MAAsB,CACpC,IAAM,EAAM,KAAK,GAAG,QAAQ,IAC5B,GAAI,CAACC,EAAAA,QAAG,WAAW,CAAG,EAEpB,OADA,KAAK,GAAG,OAAO,KAAK,6BAA6B,GAAK,EAC/C,KAGT,IAAM,EAAqC,CAAC,EAEtC,GAAqB,EAAqB,IAAwB,CACtE,IAAM,EAAUA,EAAAA,QAAG,YAAY,EAAa,CAAE,cAAe,EAAK,CAAC,EAEnE,IAAK,IAAI,EAAI,EAAG,EAAI,EAAQ,OAAQ,IAAK,CACvC,IAAM,EAAQ,EAAQ,GAChB,EAAeC,EAAAA,QAAK,KAAK,EAAa,EAAM,IAAI,EAChD,EAAeA,EAAAA,QAAK,KAAK,EAAa,EAAM,IAAI,EAEtD,GAAI,EAAM,YAAY,EACpB,EAAkB,EAAc,CAAY,OACvC,GAAI,EAAM,OAAO,EAAG,CACzB,IAAM,EAAI,KAAK,GAAG,OAAO,SAAS,EAAc,CAAY,CAAC,CAAC,MAAO,GAAM,CACzE,KAAK,GAAG,OAAO,MAAM,0BAA0B,EAAa,IAAK,EAAY,SAAS,CACxF,CAAC,EACD,EAAa,KAAK,CAAC,CACrB,CACF,CACF,EAMA,OAJA,EAAkB,EAAK,EAAE,EACzB,MAAM,QAAQ,IAAI,CAAY,EAE9B,KAAK,GAAG,OAAO,KAAK,gDAAgD,GAAK,EAClE,IACT,CAEA,QAAkB,EAAsB,EAA4B,CAClE,KAAK,aAAa,IAAI,EAAc,CAAY,EAC5C,MAAK,QAIT,KAAK,MAAQ,WAAW,SAAY,CAClC,IAAM,EAAW,CAAC,GAAG,KAAK,YAAY,CAAC,CAAC,KAAK,CAAC,EAAc,KAC1D,KAAK,GAAG,OACL,SAAS,EAAc,CAAY,CAAC,CACpC,MAAO,GAAQ,KAAK,GAAG,OAAO,MAAM,8BAA+B,EAAc,SAAS,CAAC,CAAC,CAC5F,YAAc,KAAK,aAAa,OAAO,CAAY,CAAC,CACzD,EACA,MAAM,QAAQ,IAAI,CAAQ,EAC1B,KAAK,MAAQ,IACf,EAAG,KAAK,GAAG,QAAQ,WAAW,EAChC,CAEA,UAA2B,CACzB,AAEE,KAAK,SADL,aAAa,KAAK,KAAK,EACV,MAGf,KAAK,aAAa,MAAM,CAC1B,CAIF,EC7Ea,GAAb,cAA4C,CAAmB,CAC7D,QAAoC,KAEpC,YAAY,EAAoB,CAC9B,MAAM,CAAE,CACV,CAUA,MAAM,OAAuB,CAC3B,KAAK,KAAK,EACV,MAAM,KAAK,KAAK,EAEhB,IAAM,EAAM,KAAK,GAAG,QAAQ,IA8B5B,MA7BA,MAAK,QAAU,EAAA,QACZ,MAAM,EAAK,CACV,WAAY,GACZ,cAAe,GACf,WAAY,GACZ,iBAAkB,CAChB,mBAAoB,IACpB,aAAc,EAChB,CACF,CAAC,CAAC,CACD,GAAG,OAAQ,EAAQ,IAAiB,CAC9B,GAKL,KAAK,QAAQ,EAAcC,EAAAA,QAAK,SAAS,EAAK,CAAY,CAAC,CAC7D,CAAC,CAAC,CACD,GAAG,QAAU,GAAiB,CAC7B,IAAM,EAAQ,aAAe,MAAQ,EAAU,MAAM,OAAO,CAAG,CAAC,EAChE,KAAK,GAAG,OAAO,MAAM,kBAAkB,EAAM,SAAS,EACtD,KAAK,GAAG,OAAO,MAAM,uBAAuB,EAC5C,KAAK,MAAM,CACb,CAAC,CAAC,CACD,GAAG,YAAe,CACjB,KAAK,GAAG,OAAO,KAAK,yCAAyC,GAAK,CACpE,CAAC,EAEH,KAAK,GAAG,OAAO,KAAK,iCAAiC,GAAK,EACnD,IACT,CAEA,MAAa,CAOX,MANA,CAEE,KAAK,WADL,KAAU,QAAQ,MAAM,EACT,MAGjB,KAAK,SAAS,EACP,IACT,CACF,EC9Da,GAAb,cAA0C,CAAmB,CAC3D,QAAuC,KAEvC,YAAY,EAAoB,CAC9B,MAAM,CAAE,CACV,CAOA,MAAM,OAAuB,CAC3B,KAAK,KAAK,EACV,MAAM,KAAK,KAAK,EAEhB,IAAM,EAAM,KAAK,GAAG,QAAQ,IAiB5B,MAhBA,MAAK,QAAUC,EAAAA,QACZ,MAAM,EAAK,CAAE,UAAW,EAAK,GAAI,EAAY,IAAiB,CACxD,GAKL,KAAK,QAAQC,EAAAA,QAAK,KAAK,EAAK,CAAY,EAAG,CAAY,CACzD,CAAC,CAAC,CACD,GAAG,QAAU,GAAQ,CACpB,KAAK,GAAG,OAAO,MAAM,kBAAkB,EAAI,SAAS,EACpD,KAAK,GAAG,OAAO,MAAM,uBAAuB,EAC5C,KAAK,MAAM,CACb,CAAC,EAEH,KAAK,GAAG,OAAO,KAAK,iCAAiC,GAAK,EACnD,IACT,CAEA,MAAa,CAOX,MANA,CAEE,KAAK,WADL,KAAK,QAAQ,MAAM,EACJ,MAGjB,KAAK,SAAS,EACP,IACT,CACF,EC9CA,SAAS,EAAgB,EAAgD,EAAwC,CAC/G,GAAI,OAAO,GAAM,WAAY,EAC3B,MAAO,GAKT,IAFA,EAAA,EAAA,YAAA,CAAmC,CAAC,EAEhC,OAAO,EAAE,SAAY,WAEvB,OADA,EAAG,OAAO,MAAM,4BAA4B,EACrC,GAGT,GAAI,EAAE,WAAa,IAAA,IAAa,OAAO,EAAE,UAAa,WAEpD,OADA,EAAG,OAAO,MAAM,yCAAyC,EAClD,GAGT,IAAM,EAAK,EAAE,iBAWb,OAVI,IAAO,IAAA,KAAc,CAAC,OAAO,cAAc,CAAE,GAAK,EAAK,MACzD,EAAG,OAAO,MAAM,wDAAwD,EACjE,IAGL,EAAE,OAAA,EAKC,IAJL,EAAG,OAAO,MAAM,mDAAmD,EAC5D,GAIX,CAEA,SAAgB,GACd,EACA,EACuB,CACvB,OAAO,QAAQ,MAAM,GACrB,IAAI,EAAI,QAAQ,CAAQ,EACxB,GAAI,GAAgB,EAAI,CAAC,EAClB,GAAI,EAAgB,EAAI,EAAE,OAAO,EACtC,EAAI,EAAE,aAEN,MAAA,MAAA,2CAAkC,EAAS,0DAA0D,EAGvG,OAAO,CACT,CCxCA,IAAa,GAAb,KAA2B,CACzB,GACA,SAAgE,IAAI,IAEpE,YAAY,EAAgD,CAC1D,KAAK,GAAK,CACZ,CAEA,mBAAmB,EAAyC,CAC1D,MAAO,CACL,KAAA,EACA,QAAS,MAAO,EAAY,EAAM,IAAQ,CACxC,GAAI,EAAW,SAAW,OAAS,EAAW,SAAW,OAAQ,CAC/D,EAAI,WAAa,IACjB,EAAI,UAAU,QAAS,WAAW,EAClC,EAAI,IAAI,EACR,MACF,CAEA,GAAI,CAACC,EAAAA,QAAG,WAAW,CAAQ,EAAG,CAC5B,EAAI,WAAa,IACjB,EAAI,IAAI,WAAW,EACnB,MACF,CAEA,IAAM,EAAOA,EAAAA,QAAG,SAAS,CAAQ,EACjC,GAAI,CAAC,EAAK,OAAO,EAAG,CAClB,EAAI,WAAa,IACjB,EAAI,IAAI,WAAW,EACnB,MACF,CAGA,IAAM,EAAc,GADFC,EAAAA,QAAK,QAAQ,CAAQ,CAAC,CAAC,YACQ,IAAM,2BAMvD,GAJA,EAAI,WAAa,IACjB,EAAI,UAAU,eAAgB,CAAW,EACzC,EAAI,UAAU,iBAAkB,OAAO,EAAK,IAAI,CAAC,EAE7C,EAAW,SAAW,OAAQ,CAChC,EAAI,IAAI,EACR,MACF,CAEA,OAAO,IAAI,SAAiB,EAAS,IAAW,CAC9C,IAAM,EAASD,EAAAA,QAAG,iBAAiB,CAAQ,EAC3C,EAAO,GAAG,QAAS,CAAM,EACzB,EAAO,GAAG,UAAa,EAAQ,CAAmB,CAAC,EACnD,EAAO,KAAK,CAAG,CACjB,CAAC,CACH,CACF,CACF,CAUA,MAAM,SAAS,EAAsB,EAAsB,CAEzD,IAAM,EAAW,KAAK,SAAS,IAAI,CAAY,CAAC,EAAE,SAMlD,GALI,GACF,MAAM,EAAW,CAAQ,EAIvB,CAACA,EAAAA,QAAG,WAAW,CAAY,EAAG,CAChC,KAAK,SAAS,OAAO,CAAY,EAEjC,KAAK,GAAG,OAAO,KAAK,GAAG,EAAK,IAAI,UAAU,EAAK,MAAM,KAAK,GAAc,EACxE,MACF,CAKA,GAAI,CADmB,KAAK,GAAG,QAAQ,QAAQ,KAAM,IAAA,EAAA,EAAA,UAAA,CAAsB,EAAc,CAAO,CAC9E,EAAG,CACnB,KAAK,SAAS,OAAO,CAAY,EACjC,KAAK,GAAG,OAAO,KAAK,GAAG,EAAK,OAAO,UAAU,EAAK,MAAM,KAAK,GAAc,EAC3E,MACF,CAKA,GADuB,KAAK,GAAG,QAAQ,QAAQ,KAAM,IAAA,EAAA,EAAA,UAAA,CAAsB,EAAc,CAAO,CAC/E,EAAG,CAClB,KAAK,SAAS,OAAO,CAAY,EACjC,KAAK,GAAG,OAAO,KAAK,GAAG,EAAK,OAAO,UAAU,EAAK,MAAM,KAAK,GAAc,EAC3E,MACF,CAKA,GAD0B,KAAK,GAAG,QAAQ,WAAW,KAAM,IAAA,EAAA,EAAA,UAAA,CAAsB,EAAc,CAAO,CAClF,EAAG,CACrB,IAAM,EAAI,GAAkB,KAAK,GAAI,CAAY,EACjD,KAAK,SAAS,IAAI,EAAc,CAAC,EACjC,KAAK,GAAG,OAAO,KAAK,GAAG,EAAK,MAAM,UAAU,EAAK,MAAM,KAAK,GAAc,EAC1E,MACF,CAGA,KAAK,SAAS,IAAI,EAAc,KAAK,mBAAmB,CAAY,CAAC,EACrE,KAAK,GAAG,OAAO,KAAK,GAAG,EAAK,WAAW,UAAU,EAAK,MAAM,KAAK,GAAc,CACjF,CAEA,UAAU,EAA6C,CACrD,IAAM,EAAe,EAAI,SAAS,QAAQ,QAAS,EAAE,CAAC,CAAC,QAAQ,QAAS,EAAE,EAC1E,OAAO,KAAK,SAAS,IAAI,CAAY,CACvC,CASA,OAAO,EAAwB,CACzB,KAAK,SAAS,IAAI,CAAQ,IAC5B,KAAK,SAAS,OAAO,CAAQ,EAC7B,KAAK,GAAG,OAAO,KAAK,GAAG,EAAK,IAAI,UAAU,EAAK,MAAM,KAAK,GAAU,GAGtE,IAAM,EAAS,EAAS,SAAS,GAAG,EAAI,EAAW,EAAW,IAC9D,IAAK,IAAM,KAAO,KAAK,SAAS,KAAK,EAC/B,EAAI,WAAW,CAAM,IACvB,KAAK,SAAS,OAAO,CAAG,EACxB,KAAK,GAAG,OAAO,KAAK,GAAG,EAAK,IAAI,UAAU,EAAK,MAAM,KAAK,GAAK,EAGrE,CACF,ECtIA,eAAsB,EAAQ,EAAyB,CACrD,IAAM,EAAU,CAAE,QAAS,EAAiB,CAAO,CAAE,EAErD,EAAQ,OAAS,GAAa,CAA0C,EACxE,EAAQ,OAAS,IAAI,GAAc,CAAqD,EAEpFE,EAAAA,QAAQ,UACV,EAAY,CAAO,GAGnB,EAAQ,OAAS,GAAmB,EAAQ,OAAQ,QAAQ,GAAG,EAG/D,EAAQ,QAAU,MAAM,IADR,EAAQ,QAAQ,cAAgB,GAAuB,IACnC,CAAgE,CAAC,CAAC,MAAM,EAC5G,GAAW,CAAO,EAEtB,CCJA,SAAgB,GACd,EACA,EAA2B,GACJ,CACvB,GAAI,OAAO,GAAM,WAAY,CAC3B,GAAI,OAAO,GAAa,WACtB,MAAA,MAAA,iEAAwD,OAAO,GAAU,EAE3E,MAAO,CAAE,QAAS,EAAG,WAAU,KAAA,CAA4B,CAC7D,CAEA,GAAI,OAAO,GAAM,WAAY,EAC3B,MAAA,MAAA,oGAA2F,OAAO,GAAG,EAGvG,GAAI,OAAO,EAAE,SAAY,WACvB,MAAA,MAAA,qEAA4D,EAG9D,GAAI,EAAE,WAAa,IAAA,IAAa,OAAO,EAAE,UAAa,WACpD,MAAA,MAAA,kFAAyE,EAG3E,GAAI,EAAE,UAAY,IAAA,KAAc,CAAC,MAAM,QAAQ,EAAE,OAAO,GAAK,EAAE,QAAQ,KAAM,GAAM,OAAO,GAAM,QAAQ,GACtG,MAAA,MAAA,0FAAiF,EAGnF,GACE,EAAE,cAAgB,IAAA,KACjB,CAAC,MAAM,QAAQ,EAAE,WAAW,GAAK,EAAE,YAAY,KAAM,GAAM,OAAO,GAAM,UAAU,GAEnF,MAAA,MAAA,gGAAuF,EAGzF,MAAO,CAAE,GAAG,EAAG,KAAA,CAA4B,CAC7C,CAEA,SAAgB,GAAwB,EAAkD,CACxF,GAAI,OAAO,GAAe,WACxB,MAAA,MAAA,0EAAiE,OAAO,GAAY,EAEtF,OAAO,CACT"}
|