fluxion-ts 0.13.2 → 0.13.4
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/cli.cjs +2 -1
- package/dist/cli.cjs.map +1 -0
- package/dist/cli.d.cts +1 -0
- package/dist/cli.d.mts +1 -0
- package/dist/cli.mjs +2 -1
- package/dist/cli.mjs.map +1 -0
- package/dist/fluxion--NlXBaPd.mjs +2 -0
- package/dist/fluxion--NlXBaPd.mjs.map +1 -0
- package/dist/fluxion-B4A8HXmW.cjs +2 -0
- package/dist/fluxion-B4A8HXmW.cjs.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +7 -6
package/dist/cli.cjs
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
#!/usr/bin/env tsx
|
|
2
|
-
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:path");c=s(c);let l=require("node:cluster");l=s(l);let u=require("fast-json-stable-stringify");u=s(u);let d=require("node:fs");d=s(d);let f=require("node:os");f=s(f);let p=require("node:http");p=s(p);let m=require("node:crypto");m=s(m);let h=require("node:https");h=s(h);let g=require("chokidar");g=s(g);let _=require("minimatch");function v(e){console.log(e),process.exit(0)}function y(e,t){let n=[`config`],r=[null,`status`,`stop`,`logs`];if(r.includes(e)||v(`Unknown command [${e}], please use one of [${r.join(`, `)}]`),t.forEach(e=>{n.includes(e.option)||v(`Unknown option [${e.option}], please use one of [${n.join(`, `)}]`)}),e===null){let e=t.find(e=>e.option===`config`)??{option:`config`,value:`fluxion.config.ts`};return(e.value===null||e.value.trim()===``)&&v(`Option [--config] requires a non-empty value`),{name:null,options:[{option:`config`,value:e.value}]}}if(e===`status`)return t.length!==0&&v(`Command [${e}] does not accept any options`),{name:`status`,options:[]};if(e===`stop`)return t.length!==0&&v(`Command [${e}] does not accept any options`),{name:`stop`,options:[]};if(e===`stop`)return t.length!==0&&v(`Command [${e}] does not accept any options`),{name:`logs`,options:[]};v(`Unknown command`)}function b(){let e=process.argv.slice(2),t=[],n=null;for(let r=0;r<e.length;r++){let i=e[r];if(i.startsWith(`--`)){let n=``,a=null;i.includes(`=`)?[n,a]=i.slice(2).split(`=`,2):(n=i.slice(2),a=e[r+1]??null,r++),t.some(e=>e.option===n)&&v(`Duplicate option [${n}]`),t.push({option:n,value:a});continue}n!==null&&v(`Already detect a command [${n}], but now got [${i}]`),n=i}return y(n,t)}function x(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 S=process.env.FLUXION_COLORS!==`0`;let C;(function(e){e.reset=S?`\x1B[0m`:``,e.bold=S?`\x1B[1m`:``,e.dim=S?`\x1B[2m`:``,e.italic=S?`\x1B[3m`:``,e.underline=S?`\x1B[4m`:``,e.blink=S?`\x1B[5m`:``,e.inverse=S?`\x1B[7m`:``,e.black=S?`\x1B[30m`:``,e.red=S?`\x1B[31m`:``,e.green=S?`\x1B[32m`:``,e.yellow=S?`\x1B[33m`:``,e.blue=S?`\x1B[34m`:``,e.magenta=S?`\x1B[35m`:``,e.cyan=S?`\x1B[36m`:``,e.white=S?`\x1B[37m`:``,e.brightBlack=S?`\x1B[90m`:``,e.brightRed=S?`\x1B[91m`:``,e.brightGreen=S?`\x1B[92m`:``,e.brightYellow=S?`\x1B[93m`:``,e.brightBlue=S?`\x1B[94m`:``,e.brightMagenta=S?`\x1B[95m`:``,e.brightCyan=S?`\x1B[96m`:``,e.brightWhite=S?`\x1B[97m`:``,e.bgBlack=S?`\x1B[40m`:``,e.bgRed=S?`\x1B[41m`:``,e.bgGreen=S?`\x1B[42m`:``,e.bgYellow=S?`\x1B[43m`:``,e.bgBlue=S?`\x1B[44m`:``,e.bgMagenta=S?`\x1B[45m`:``,e.bgCyan=S?`\x1B[46m`:``,e.bgWhite=S?`\x1B[47m`:``,e.bgBrightBlack=S?`\x1B[100m`:``,e.bgBrightRed=S?`\x1B[101m`:``,e.bgBrightGreen=S?`\x1B[102m`:``,e.bgBrightYellow=S?`\x1B[103m`:``,e.bgBrightBlue=S?`\x1B[104m`:``,e.bgBrightMagenta=S?`\x1B[105m`:``,e.bgBrightCyan=S?`\x1B[106m`:``,e.bgBrightWhite=S?`\x1B[107m`:``,e.purple=S?`\x1B[38;2;225;16;248m`:``,e.orange=S?`\x1B[38;2;248;147;16m`:``,e.darkGreen=S?`\x1B[38;2;22;101;52m`:``,e.claude=S?`\x1B[38;2;217;119;87m`:``,e.deepseek=S?`\x1B[38;2;57;100;254m`:``,e.gpt=S?`\x1B[38;2;41;60;77m`:``})(C||={});const w=e=>{try{return(0,u.default)(e)}catch{return`[unserializable]`}},ee={INFO:`${C.cyan}INFO${C.reset}`,WARN:`${C.orange}WARN${C.reset}`,ERROR:`${C.red}ERROR${C.reset}`,SUCC:`${C.green}SUCC${C.reset}`,DEBUG:`${C.blue}DEBUG${C.reset}`,VERBOSE:`${C.purple}VERBOSE${C.reset}`},te=e=>{let{level:t,timestamp:n,message:r,pid:i,...a}=e,o=`${C.darkGreen}[${n}]${C.reset}`,s=ee[t]??t,c=i===void 0?``:` [${i}]`,l=Object.keys(a).length>0?` ${C.dim}${w(a)}${C.reset}`:``;console.log(`${o} ${s}${c} ${r}${l}`)};function ne(e){let t=e.options.logger;return t===void 0||t===`one-line`?te:t===`json-line`?e=>console.log(w(e)):t}function re(e){let t=ne(e);return{write(e,n){let r=typeof n==`string`?{message:n,timestamp:x(),level:e}:{...n,timestamp:x(),level:e};try{t(r)}catch{}},info(e){this.write(`INFO`,e)},warn(e){this.write(`WARN`,e)},error(e){this.write(`ERROR`,e)},succ(e){this.write(`SUCC`,e)},debug(e){this.write(`DEBUG`,e)},verbose(e){this.write(`VERBOSE`,e)}}}function ie(e,t){return{write(n,r){e.write(n,typeof r==`string`?{message:r,pid:t}:{...r,pid:t})},info(e){this.write(`INFO`,e)},warn(e){this.write(`WARN`,e)},error(e){this.write(`ERROR`,e)},succ(e){this.write(`SUCC`,e)},debug(e){this.write(`DEBUG`,e)},verbose(e){this.write(`VERBOSE`,e)}}}const T=typeof Error.isError==`function`?e=>Error.isError(e)?e.message:String(e):e=>e?.message||String(e),E=Symbol(`fluxion.router.StaticHandled`),D=Symbol(`fluxion.router.StaticHandled`),O=Symbol(`fluxion.handlerTimeout`),k=Symbol(`fluxion.middlewareTimeout`),ae={".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`};function oe(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 A(e,t){if(Buffer.isBuffer(e))return e;if(typeof e==`string`){if(!e.startsWith(`-----BEGIN`)){let n=c.default.isAbsolute(e)?e:c.default.join(t,e);if(d.default.existsSync(n))return d.default.readFileSync(n)}return Buffer.from(e)}throw Error(`[fluxion error] Certificate content must be a string or Buffer`)}function j(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:A(e.key,t),cert:A(e.cert,t)};return e.ca!==void 0&&(Array.isArray(e.ca)?n.ca=e.ca.map(e=>A(e,t)):n.ca=A(e.ca,t)),n}function M(e){if(typeof e!=`object`||!e||Array.isArray(e))throw Error(`[fluxion error] FluxionOptions must be an object`);let{dir:t,host:n,port:r,handlerTimeoutMs:i=5e3,middlewareTimeoutMs:a=3e3,staticResourceTimeoutMs:o=10*6e5,metaPort:s=r+1,moduleDir:l=process.cwd(),workerOptions:u={},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,metaSecret:y}=e,b=e.logger??`one-line`;if(b!==`one-line`&&b!==`json-line`&&typeof b!=`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`);let x=c.default.resolve(t);if(typeof l!=`string`)throw Error(`[fluxion error] FluxionOptions.moduleDir must be a string`);let S=c.default.resolve(l);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 u!=`object`||!u||Array.isArray(u))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`);if(y!==void 0&&(typeof y!=`string`||y.length<20||/\s/.test(y)||!/[A-Za-z]/.test(y)||!/\d/.test(y)))throw Error(`[fluxion error] FluxionOptions.metaSecret must be a string with at least 20 characters, include both letters and digits, and contain no whitespace`);return d.default.existsSync(x)||d.default.mkdirSync(x,{recursive:!0}),{dir:x,host:n,port:r,handlerTimeoutMs:i,middlewareTimeoutMs:a,staticResourceTimeoutMs:o,reloadDelay:p,metaPort:s,moduleDir:S,workerOptions:oe(u),maxRequestBytes:f,logger:b,include:m,apiInclude:h,exclude:g,nativeWatcher:v,metaSecret:y,https:j(_,S),normalizedFlag:E}}const se=e=>[100,101].includes(e?.type),ce=e=>[202,200,201,203,204].includes(e?.type),N=e=>process.send?.(e),P=(e,t)=>e.send(t);function F(e,t,n=200){e.statusCode=n,e.setHeader(`Content-Type`,`application/json; charset=utf-8`),e.end(JSON.stringify(t))}function I(e,t,n=200){if(!e.writableEnded){if(e.headersSent){e.end();return}F(e,t,n)}}function le(e,t,n){let r=p.default.createServer(async(r,i)=>{let a=r.method??`GET`,o;try{o=new URL(r.url??`/`,`http://fluxion.local`)}catch{F(i,{message:`Bad Request: invalid url`},400);return}if(a===`GET`&&o.pathname===`/_fluxion/healthz`){F(i,{ok:!0,role:`primary`,pid:process.pid,now:Date.now(),uptimeSeconds:Number(process.uptime().toFixed(3))});return}if(a===`GET`&&o.pathname===`/_fluxion/workers`){F(i,{ok:!0,now:Date.now(),workers:t()});return}if(a===`GET`&&o.pathname===`/_fluxion/routes`){if(!e.options.metaSecret){F(i,{message:`Not Found`},404);return}if(o.searchParams.get(`secret`)!==e.options.metaSecret){F(i,{message:`Forbidden`},403);return}let t=await n();F(i,{ok:!0,now:Date.now(),routes:t});return}F(i,{message:`Not Found`},404)});return r.on(`listening`,()=>{e.logger.info({message:`MetaApiStarted`,pid:process.pid,host:e.options.host,port:e.options.metaPort,prefix:`/_fluxion`})}),r.on(`error`,t=>{e.logger.error({message:`MetaApiError`,host:e.options.host,port:e.options.metaPort,code:t.code,error:T(t)}),process.exit(1)}),r.listen(e.options.metaPort,e.options.host),r}var ue=class{instanceFilePath;homeDir;fluxionDir;constructor(){this.homeDir=f.default.homedir(),this.fluxionDir=c.default.join(this.homeDir,`.fluxion`),this.instanceFilePath=c.default.join(this.fluxionDir,`instance.json`),this.ensureFluxionDir()}ensureFluxionDir(){d.default.existsSync(this.fluxionDir)||d.default.mkdirSync(this.fluxionDir,{recursive:!0})}calculateConfigHash(e){try{if(!d.default.existsSync(e))return``;let t=d.default.readFileSync(e,`utf-8`);return m.default.createHash(`sha256`).update(t).digest(`hex`)}catch{return``}}readInstances(){try{if(!d.default.existsSync(this.instanceFilePath))return[];let e=d.default.readFileSync(this.instanceFilePath,`utf-8`);return JSON.parse(e).instances||[]}catch{return[]}}writeInstances(e){try{let t={instances:e};d.default.writeFileSync(this.instanceFilePath,JSON.stringify(t,null,2),`utf-8`)}catch(e){console.error(`Failed to write instance.json:`,e)}}cleanupDeadInstances(e){return e.filter(e=>{try{return process.kill(e.pid,0),!0}catch{return!1}})}findDuplicateInstance(e,t){let n=this.readInstances();return this.cleanupDeadInstances(n).find(n=>n.configHash===e||n.port===t)||null}killProcess(e){try{return process.kill(e,`SIGTERM`),!0}catch(t){return console.error(`Failed to kill process ${e}:`,t),!1}}registerInstance(e,t,n,r){let i=this.calculateConfigHash(e),a=process.pid,o=process.cwd(),s=this.findDuplicateInstance(i,n);if(s){console.warn(`[FluxionInstanceManager] Found existing instance with same config or port: PID=${s.pid}, PORT=${s.port}`),this.killProcess(s.pid)&&console.warn(`[FluxionInstanceManager] Killed old process ${s.pid}`);let e=this.readInstances().filter(e=>e.pid!==s.pid);this.writeInstances(e)}let c=this.readInstances();c=this.cleanupDeadInstances(c),c=c.filter(e=>e.pid!==a);let l={startTime:Date.now(),pid:a,configHash:i,host:t,port:n,metaPort:r,cwd:o,configPath:e};c.push(l),this.writeInstances(c),console.info(`[FluxionInstanceManager] Registered instance: PID=${a}, PORT=${n}, HASH=${i.slice(0,8)}...`)}unregisterInstance(){let e=process.pid,t=this.readInstances(),n=t.filter(t=>t.pid!==e);n.length!==t.length&&(this.writeInstances(n),console.info(`[FluxionInstanceManager] Unregistered instance: PID=${e}`))}getRunningInstances(){let e=this.readInstances();return this.cleanupDeadInstances(e)}printInstances(){let e=this.getRunningInstances();console.info(`[FluxionInstanceManager] Current instances:`);for(let t of e)console.info(` - PID: ${t.pid}, PORT: ${t.port}, HASH: ${t.configHash.slice(0,8)}..., START: ${new Date(t.startTime).toISOString()}`)}};let L=null;function R(){return L||=new ue,L}function de(e,t,n,r){R().registerInstance(e,t,n,r)}function z(){R().unregisterInstance()}const B=e=>Number((e/1024/1024).toFixed(2)),V=6e4;function H(e){if(!l.default.isPrimary)throw Error(`[fluxion error] createPrimary should only be called in primary process`);de(c.default.join(e.options.moduleDir||process.cwd(),`fluxion.config.ts`),e.options.host,e.options.port,e.options.metaPort),process.on(`exit`,z),process.on(`SIGINT`,()=>{z(),process.exit(0)}),process.on(`SIGTERM`,()=>{z(),process.exit(0)});let{workerOptions:t}=e.options,n=t.restartWhen,r=Math.max(1,f.default.cpus().length),i=Math.max(1,Math.min(t.maxWorkerCount??Math.min(2,r),r));e.logger.info({message:`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=0,u=new Map,d=e=>{let t=Date.now(),n=(u.get(e)??[]).filter(e=>t-e<V);return u.set(e,n),n.length},p=e=>{let t=Date.now(),n=(u.get(e)??[]).filter(e=>t-e<V);n.push(t),u.set(e,n)},m=e=>d(e)>=3;le(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:B(r.memory.rss),heapTotalMb:B(r.memory.heapTotal),heapUsedMb:B(r.memory.heapUsed),externalMb:B(r.memory.external),arrayBuffersMb:B(r.memory.arrayBuffers)}}}})}),()=>{let e=Array.from(a.values()).find(e=>e.state===`ready`&&e.instance.isConnected());return e?new Promise(t=>{let n=++s,r=setTimeout(()=>{o.delete(n),t([])},1e3);r.unref(),o.set(n,{resolve:t,timer:r});try{P(e.instance,{type:101,requestId:n})}catch{clearTimeout(r),o.delete(n),t([])}}):Promise.resolve([])});let h=(t,n)=>{for(let e of a.values())if(e.state===`restarting`)return;if(m(t.slot)){e.logger.warn({message:`WorkerRecycleSuppressed`,slot:t.slot,pid:t.pid,reason:n,windowMs:V,max:3});return}p(t.slot),t.state=`restarting`,t.restartReason=n,e.logger.warn({message:`WorkerRecycling`,slot:t.slot,pid:t.pid,reason:n}),t.instance.kill()},g=(e,t)=>{let r=B(t.memory.rss);if(r>n.memoryUsageGreaterThan){h(e,`memoryUsageGreaterThan: rss ${r}MB > ${n.memoryUsageGreaterThan}MB`);return}let i=t.uptimeSeconds*1e3;i>n.uptimeGreaterThan&&h(e,`uptimeGreaterThan: ${Math.round(i/1e3)}s > ${Math.round(n.uptimeGreaterThan/1e3)}s`)},_=e=>{for(let t of a.values()){if(t.state!==`ready`||t.lastPongAt===void 0)continue;let r=e-t.lastPongAt;r>n.healthzTimeout&&h(t,`healthzTimeout: no pong for ${Math.round(r/1e3)}s > ${Math.round(n.healthzTimeout/1e3)}s`)}},v=e=>{y(l.default.fork({WORKER_ID:String(e)}),e)},y=(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(ce(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({message:`WorkerReady`,workerId:t.id,slot:n,pid:i.pid});return}if(i.type===200){r.state=`created`,r.pid=i.pid,e.logger.info({message:`WorkerCreated`,workerId:t.id,slot:n,pid:i.pid});return}if(i.type===203){r.pid=i.pid,r.lastStats=i.stats,r.state===`ready`&&g(r,i.stats);return}if(i.type===204){let e=o.get(i.requestId);e&&(clearTimeout(e.timer),o.delete(i.requestId),e.resolve(i.routes))}}}),t.on(`exit`,(n,r)=>{let i=a.get(t.id);a.delete(t.id);let o=i?.slot,s=i?.state===`restarting`,c=i?.restartReason??null;if(e.logger.warn({message:`WorkerExited`,workerId:t.id,slot:o??null,pid:t.process.pid??`unknown`,code:n,signal:r??`none`,expected:s,reason:c}),o!==void 0){if(s){v(o);return}if(p(o),m(o)){e.logger.error({message:`WorkerRespawnSuppressed`,slot:o,windowMs:V,max:3});return}v(o)}})};for(let e=0;e<i;e++)v(e+1);setInterval(()=>{let e=Date.now();for(let t of a.values())if(t.instance.isConnected())try{P(t.instance,{type:100,sentAt:e})}catch{}_(Date.now())},5e3).unref()}function U(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 fe(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 pe(e){if(e!==void 0)try{return new URL(e,`http://fluxion.local`)}catch{return}}function G(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}var K=class extends Error{errno;code;constructor(e,t,n){super(e),this.name=`HttpException`,this.errno=t,this.code=n}},me=class extends K{constructor(e=`Payload Too Large`){super(e,413,`PAYLOAD_TOO_LARGE`)}};function q(e,t){return new me(`request body too large: ${e.toString()} bytes exceeds ${t.toString()} bytes`)}function J(e){return Array.isArray(e)?e[0]:e}function Y(){return{exists:!1,bytes:0,truncated:!1}}function he(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 ge(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);return new Promise((t,i)=>{let o=[],s=[],c=0,l=0,u=!1,d=!1,f=()=>{e.off(`data`,m),e.off(`end`,h),e.off(`error`,g),e.off(`aborted`,_)},p=e=>{d||(d=!0,e())};if(Number.isFinite(a)&&a>n){f(),e.resume(),p(()=>i(q(a,n)));return}let m=t=>{let a=Buffer.isBuffer(t)?t:Buffer.from(t);if(c+=a.byteLength,c>n){f(),e.resume(),p(()=>i(q(c,n)));return}if(o.push(a),l<r){let e=r-l,t=a.subarray(0,e);s.push(t),l+=t.length,t.length<a.length&&(u=!0)}else u=!0},h=()=>{f(),p(()=>{let n=o.length>0?Buffer.concat(o):void 0;t({rawBody:n,preview:he(s.length>0?Buffer.concat(s):Buffer.alloc(0),n?.byteLength??0,J(e.headers[`content-type`]),u)})})},g=e=>{f(),p(()=>i(e))},_=()=>{f(),p(()=>i(Error(`request aborted while reading body`)))};e.on(`data`,m),e.once(`end`,h),e.once(`error`,g),e.once(`aborted`,_)})}async function _e(e,t,n){let{rawBody:r,preview:i}=await ge(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:G(new URLSearchParams(r.toString(`utf8`))),preview:i}:W(a)?{body:{raw:r.toString(`utf8`)},preview:i}:{body:{},preview:i}}function ve(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 ye(e){let t=Object.freeze({logger:e.logger}),n=async(n,r)=>{let i=n.method??`GET`,a=fe(n),o=pe(n.url);if(o===void 0){I(r,{message:`Bad Request: req.url is undefined`},400);return}let s={method:i,ip:a,url:o,query:G(o.searchParams),body:{},headers:n.headers,cookie:ve(n.headers.cookie),meta:{}},c={exists:!1,bytes:0,truncated:!1};e.logger.info({message:`request`,method:i,ip:a,path:o.pathname});let l=performance.now();r.once(`finish`,()=>{let t={workerId:process.env.WORKER_ID??`[primary]`,message:`response`,method:i,ip:a,path:o.pathname,status:r.statusCode,duration:(performance.now()-l).toFixed(4)};Object.keys(s.query).length>0&&(t.query=s.query),c.exists&&(t.body=c.value,t.bodyBytes=c.bytes,t.bodyTruncated=c.truncated),e.logger.info(t)});try{if(s.url.pathname.startsWith(`/_fluxion/`)){I(r,{message:`Not Found`},404);return}let i=await _e(n,s.method,e.options.maxRequestBytes);s.body=i.body,c=i.preview;let a=await e.router.getModule(o);if(!a){I(r,{message:`Not Found`},404);return}if(n.method&&a.methods&&!a.methods.includes(n.method)){I(r,{message:`Method Not Allowed`},405);return}let l=a.type===0?a.handlerTimeoutMs??e.options.handlerTimeoutMs:e.options.staticResourceTimeoutMs;if(a.middlewares)for(let i=0;i<a.middlewares.length;i++){if(await X(U(a.middlewares[i],s,t,n,r),e.options.middlewareTimeoutMs,k)===k){e.logger.warn({message:`MiddlewareTimeout`,method:s.method,ip:s.ip}),I(r,{message:`Internal Server Error`},500);return}if(r.writableEnded)return;if(r.headersSent){r.end();return}}let u=await X(U(a.handler,s,t,n,r),l,O);if(u===O){e.logger.warn({message:`HandlerTimeout`,method:s.method,ip:s.ip}),I(r,{message:`Handler timed out`},500);return}u!==D&&I(r,u)}catch(t){t instanceof K?(e.logger.error({message:`RequestFailed`,method:s.method,ip:s.ip,path:s.url.pathname,error:t.message}),I(r,{message:t.message},t.errno)):(e.logger.error({message:`RequestFailed`,method:s.method,ip:s.ip,path:s.url.pathname,error:T(t)}),I(r,{message:T(t)},t.errno??500))}},r=e.options.https?h.default.createServer({key:e.options.https.key,cert:e.options.https.cert,ca:e.options.https.ca},n):p.default.createServer(n);return new Promise((t,n)=>{let i=!1;r.on(`close`,()=>{e.logger.info({message:`ServerClosed`,host:e.options.host,port:e.options.port})}),r.once(`listening`,()=>{i=!0,e.logger.info({message:`ServerStarted`,pid:process.pid,protocol:e.options.https?`https`:`http`,host:e.options.host,port:e.options.port}),e.logger.info({message:`DynamicDirectory`,directory:e.options.dir}),t(r)}),r.on(`error`,t=>{e.logger.error({message:`ServerError`,error:T(t)}),i&&process.exit(1),n(t)}),r.listen(e.options.port,e.options.host)})}const be=()=>{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();N({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 xe(e){if(l.default.isPrimary)throw Error(`[fluxion error] createWorker should only be called in worker process`);process.on(`message`,t=>{if(se(t)){if(t.type===100){N({type:202,pid:process.pid,sentAt:t.sentAt,receivedAt:Date.now()});return}t.type===101&&N({type:204,pid:process.pid,requestId:t.requestId,routes:e.router.getRoutes()})}}),N({type:200,pid:process.pid}),be();let t,n=!1,r=r=>{n||(n=!0,e.logger.warn({message:`WorkerShuttingDown`,pid:process.pid,signal:r}),e.watcher.stop(),t||process.exit(0),setTimeout(()=>process.exit(1),1e4).unref(),t.close(t=>{t&&(e.logger.error({message:`WorkerShutdownFailed`,pid:process.pid,error:T(t)}),process.exit(1)),process.exit(0)}))};process.once(`SIGINT`,r),process.once(`SIGTERM`,r),ye(e).then(e=>{t=e,N({type:201,pid:process.pid})}).catch(t=>{e.logger.error({message:`WorkerBootstrapFailed`,pid:process.pid,error:T(t)}),e.watcher.stop(),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(!d.default.existsSync(e))return this.cx.logger.warn(`Directory does not exist: ${e}`),this;let t=[],n=(e,r)=>{let i=d.default.readdirSync(e,{withFileTypes:!0});for(let a=0;a<i.length;a++){let o=i[a],s=c.default.join(e,o.name),l=c.default.join(r,o.name);if(o.isDirectory())n(s,l);else if(o.isFile()){let e=this.cx.router.register(s,l).catch(e=>{this.cx.logger.error(`Error registering file ${l}: ${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()}},Q=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=g.default.watch(e,{persistent:!0,ignoreInitial:!0,usePolling:!1,awaitWriteFinish:{stabilityThreshold:100,pollInterval:50}}).on(`all`,(t,n)=>{n&&this.queueUp(n,c.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}},Se=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=d.default.watch(e,{recursive:!0},(t,n)=>{n&&this.queueUp(c.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 Ce(e){}const we=Ce;function $(e,t){if(typeof t!=`object`||!t)return!1;if(we(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 Te(e,t){delete require.cache[t];let n=require(t);if($(e,n.default))n=n.default;else if(!$(e,n))throw Error(`[fluxion error] Invalid handler module '${t}', make sure it satisfies defineFluxionModule(...) helper`);return n}var Ee=class{cx;handlers=new Map;constructor(e){this.cx=e}makeStaticResource(e){return{type:1,handler:async(t,n,r,i)=>{if(t.method!==`GET`&&t.method!==`HEAD`){i.statusCode=405,i.setHeader(`Allow`,`GET, HEAD`),i.end();return}if(!d.default.existsSync(e)){i.statusCode=404,i.end(`Not Found`);return}let a=d.default.statSync(e);if(!a.isFile()){i.statusCode=404,i.end(`Not Found`);return}let o=ae[c.default.extname(e).toLowerCase()]??`application/octet-stream`;if(i.statusCode=200,i.setHeader(`Content-Type`,o),i.setHeader(`Content-Length`,String(a.size)),t.method===`HEAD`){i.end();return}return new Promise((t,n)=>{let a=d.default.createReadStream(e),o=()=>{a.off(`error`,s),a.off(`end`,c),i.off(`close`,l),r.off(`aborted`,l)},s=e=>{o(),n(e)},c=()=>{o(),t(D)},l=()=>{o(),a.destroy(),t(D)};a.on(`error`,s),a.on(`end`,c),i.on(`close`,l),r.on(`aborted`,l),a.pipe(i)})}}}async register(e,t){let n=this.handlers.get(t)?.disposer;if(n&&await U(n),!d.default.existsSync(e)){this.handlers.delete(t),this.cx.logger.info({action:`Delete`,url:t});return}if(!this.cx.options.include.some(e=>(0,_.minimatch)(t,e))){this.handlers.delete(t),this.cx.logger.info({action:`Skip`,url:t});return}if(this.cx.options.exclude.some(e=>(0,_.minimatch)(t,e))){this.handlers.delete(t),this.cx.logger.info({action:`Exclude`,url:t});return}if(this.cx.options.apiInclude.some(e=>(0,_.minimatch)(t,e))){let n=Te(this.cx,e);this.handlers.set(t,n),this.cx.logger.info({action:`RegisterApi`,url:t});return}this.handlers.set(t,this.makeStaticResource(e)),this.cx.logger.info({action:`RegisterStatic`,url:t})}getModule(e){let t=e.pathname.replace(/^[/]+/,``).replace(/[/]+$/,``);return this.handlers.get(t)}getRoutes(){return[...this.handlers.entries()].map(([e,t])=>({path:`/`+e,type:t.type===0?`api`:`static`,methods:t.methods?[...t.methods]:null})).sort((e,t)=>e.path.localeCompare(t.path))}};async function De(e){let t={options:e.normalizedFlag===E?e:M(e)};t.logger=re(t),t.router=new Ee(t),l.default.isPrimary?H(t):(t.logger=ie(t.logger,process.pid),t.watcher=await new(t.options.nativeWatcher?Se:Q)(t).start(),xe(t))}function Oe(e){if(e.name===null){let t=e.options.find(e=>e.option===`config`)?.value??`fluxion.config.ts`;t=c.default.isAbsolute(t)?t:c.default.join(process.cwd(),t),De(require(t).default)}else if(e.name===`status`){let e=R().getRunningInstances();if(console.log(`Running Fluxion instances:`),e.length===0)console.log(` No instances running.`);else for(let t of e)console.log(` - PID: ${t.pid}`),console.log(` Port: ${t.port}`),console.log(` Meta Port: ${t.metaPort}`),console.log(` Host: ${t.host}`),console.log(` Config Hash: ${t.configHash.slice(0,16)}...`),console.log(` Started: ${new Date(t.startTime).toISOString()}`),console.log(` Working Dir: ${t.cwd}`),console.log(` Config Path: ${t.configPath}`),console.log(``)}else if(e.name===`stop`){let e=R(),t=e.getRunningInstances();if(t.length===0){console.log(`No running instances to stop.`);return}console.log(`Stopping ${t.length} instance(s)...`);for(let e of t)try{process.kill(e.pid,`SIGTERM`),console.log(` - Stopped instance PID: ${e.pid}`)}catch(t){console.error(` - Failed to stop instance PID: ${e.pid}`,t)}e.unregisterInstance(),console.log(`All instances stopped.`)}else e.name===`logs`&&console.log(`Logs command is not yet implemented.`)}function ke(){Oe(b())}ke();
|
|
2
|
+
const e=require("./fluxion-B4A8HXmW.cjs");let t=require("node:path");t=e.w(t);function n(e){console.log(e),process.exit(0)}function r(e,t){let r=[`config`],i=[null,`status`,`stop`,`logs`];if(i.includes(e)||n(`Unknown command [${e}], please use one of [${i.join(`, `)}]`),t.forEach(e=>{r.includes(e.option)||n(`Unknown option [${e.option}], please use one of [${r.join(`, `)}]`)}),e===null){let e=t.find(e=>e.option===`config`)??{option:`config`,value:`fluxion.config.ts`};return(e.value===null||e.value.trim()===``)&&n(`Option [--config] requires a non-empty value`),{name:null,options:[{option:`config`,value:e.value}]}}if(e===`status`)return t.length!==0&&n(`Command [${e}] does not accept any options`),{name:`status`,options:[]};if(e===`stop`)return t.length!==0&&n(`Command [${e}] does not accept any options`),{name:`stop`,options:[]};if(e===`stop`)return t.length!==0&&n(`Command [${e}] does not accept any options`),{name:`logs`,options:[]};n(`Unknown command`)}function i(){let e=process.argv.slice(2),t=[],i=null;for(let r=0;r<e.length;r++){let a=e[r];if(a.startsWith(`--`)){let i=``,o=null;a.includes(`=`)?[i,o]=a.slice(2).split(`=`,2):(i=a.slice(2),o=e[r+1]??null,r++),t.some(e=>e.option===i)&&n(`Duplicate option [${i}]`),t.push({option:i,value:o});continue}i!==null&&n(`Already detect a command [${i}], but now got [${a}]`),i=a}return r(i,t)}function a(n){if(n.name===null){let r=n.options.find(e=>e.option===`config`)?.value??`fluxion.config.ts`;r=t.default.isAbsolute(r)?r:t.default.join(process.cwd(),r),e.t(require(r).default)}else if(n.name===`status`){let t=e.x().getRunningInstances();if(console.log(`Running Fluxion instances:`),t.length===0)console.log(` No instances running.`);else for(let e of t)console.log(` - PID: ${e.pid}`),console.log(` Port: ${e.port}`),console.log(` Meta Port: ${e.metaPort}`),console.log(` Host: ${e.host}`),console.log(` Config Hash: ${e.configHash.slice(0,16)}...`),console.log(` Started: ${new Date(e.startTime).toISOString()}`),console.log(` Working Dir: ${e.cwd}`),console.log(` Config Path: ${e.configPath}`),console.log(``)}else if(n.name===`stop`){let t=e.x(),n=t.getRunningInstances();if(n.length===0){console.log(`No running instances to stop.`);return}console.log(`Stopping ${n.length} instance(s)...`);for(let e of n)try{process.kill(e.pid,`SIGTERM`),console.log(` - Stopped instance PID: ${e.pid}`)}catch(t){console.error(` - Failed to stop instance PID: ${e.pid}`,t)}t.unregisterInstance(),console.log(`All instances stopped.`)}else n.name===`logs`&&console.log(`Logs command is not yet implemented.`)}function o(){a(i())}o();
|
|
3
|
+
//# sourceMappingURL=cli.cjs.map
|
package/dist/cli.cjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.cjs","names":["path","getInstanceManager"],"sources":["../src/cli/utils.ts","../src/cli/parser.ts","../src/cli/executor.ts","../src/cli/index.ts"],"sourcesContent":["export function quit(message: string): never {\n console.log(message);\n process.exit(0);\n}\n","import type { FluxionCommand } from './types.js';\nimport { quit } from './utils.js';\n\nfunction validate(command: string | null, options: Array<{ option: string; value: string | null }>): FluxionCommand {\n const OPTS = ['config'];\n const CMDS = [null, 'status', 'stop', 'logs'];\n\n // # Validating\n\n if (!CMDS.includes(command)) {\n quit(`Unknown command [${command}], please use one of [${CMDS.join(', ')}]`);\n }\n\n options.forEach((v) => {\n if (!OPTS.includes(v.option)) {\n quit(`Unknown option [${v.option}], please use one of [${OPTS.join(', ')}]`);\n }\n });\n\n if (command === null) {\n const config = options.find((v) => v.option === 'config') ?? { option: 'config', value: 'fluxion.config.ts' };\n if (config.value === null || config.value.trim() === '') {\n quit('Option [--config] requires a non-empty value');\n }\n\n return {\n name: null,\n options: [{ option: 'config', value: config.value }],\n };\n }\n\n if (command === 'status') {\n if (options.length !== 0) {\n quit(`Command [${command}] does not accept any options`);\n }\n\n return { name: 'status', options: [] };\n }\n\n if (command === 'stop') {\n if (options.length !== 0) {\n quit(`Command [${command}] does not accept any options`);\n }\n\n return { name: 'stop', options: [] };\n }\n\n if (command === 'stop') {\n if (options.length !== 0) {\n quit(`Command [${command}] does not accept any options`);\n }\n return { name: 'logs', options: [] };\n }\n\n // ! Normally nothing needed\n quit('Unknown command');\n}\n\nexport function parseCommand(): FluxionCommand {\n const args = process.argv.slice(2);\n\n const options: Array<{ option: string; value: string | null }> = [];\n let command: string | null = null;\n\n for (let i = 0; i < args.length; i++) {\n const a = args[i];\n // deal with an option\n if (a.startsWith('--')) {\n let key = '';\n let value = null;\n if (a.includes('=')) {\n [key, value] = a.slice(2).split('=', 2);\n } else {\n key = a.slice(2);\n value = args[i + 1] ?? null;\n i++;\n }\n if (options.some((v) => v.option === key)) {\n quit(`Duplicate option [${key}]`);\n }\n options.push({ option: key, value });\n continue;\n }\n\n if (command !== null) {\n quit(`Already detect a command [${command}], but now got [${a}]`);\n }\n command = a;\n }\n\n return validate(command, options);\n}\n","import path from 'node:path';\nimport { FluxionCommand } from './types.js';\nimport { fluxion } from '@/fluxion.js';\nimport { getInstanceManager } from '@/cluster/launcher.js';\n\nexport function executor(command: FluxionCommand) {\n if (command.name === null) {\n let configPath = command.options.find((v) => v.option === 'config')?.value ?? 'fluxion.config.ts';\n configPath = path.isAbsolute(configPath) ? configPath : path.join(process.cwd(), configPath);\n const config = require(configPath);\n fluxion(config.default);\n } else if (command.name === 'status') {\n // 显示当前运行的 fluxion 实例\n const manager = getInstanceManager();\n const instances = manager.getRunningInstances();\n console.log('Running Fluxion instances:');\n if (instances.length === 0) {\n console.log(' No instances running.');\n } else {\n for (const instance of instances) {\n console.log(` - PID: ${instance.pid}`);\n console.log(` Port: ${instance.port}`);\n console.log(` Meta Port: ${instance.metaPort}`);\n console.log(` Host: ${instance.host}`);\n console.log(` Config Hash: ${instance.configHash.slice(0, 16)}...`);\n console.log(` Started: ${new Date(instance.startTime).toISOString()}`);\n console.log(` Working Dir: ${instance.cwd}`);\n console.log(` Config Path: ${instance.configPath}`);\n console.log('');\n }\n }\n } else if (command.name === 'stop') {\n // 停止所有运行的 fluxion 实例\n const manager = getInstanceManager();\n const instances = manager.getRunningInstances();\n if (instances.length === 0) {\n console.log('No running instances to stop.');\n return;\n }\n console.log(`Stopping ${instances.length} instance(s)...`);\n for (const instance of instances) {\n try {\n process.kill(instance.pid, 'SIGTERM');\n console.log(` - Stopped instance PID: ${instance.pid}`);\n } catch (error) {\n console.error(` - Failed to stop instance PID: ${instance.pid}`, error);\n }\n }\n // 清理实例记录\n manager.unregisterInstance();\n console.log('All instances stopped.');\n } else if (command.name === 'logs') {\n // 显示日志(此功能暂未实现)\n console.log('Logs command is not yet implemented.');\n }\n}\n","#!/usr/bin/env tsx\nimport { parseCommand } from './parser.js';\nimport { executor } from './executor.js';\n\nfunction main() {\n const command = parseCommand();\n executor(command);\n}\n\nmain();\n"],"mappings":";8EAAA,SAAgB,EAAK,EAAwB,CAC3C,QAAQ,IAAI,CAAO,EACnB,QAAQ,KAAK,CAAC,CAChB,CCAA,SAAS,EAAS,EAAwB,EAA0E,CAClH,IAAM,EAAO,CAAC,QAAQ,EAChB,EAAO,CAAC,KAAM,SAAU,OAAQ,MAAM,EAc5C,GAVK,EAAK,SAAS,CAAO,GACxB,EAAK,oBAAoB,EAAQ,wBAAwB,EAAK,KAAK,IAAI,EAAE,EAAE,EAG7E,EAAQ,QAAS,GAAM,CAChB,EAAK,SAAS,EAAE,MAAM,GACzB,EAAK,mBAAmB,EAAE,OAAO,wBAAwB,EAAK,KAAK,IAAI,EAAE,EAAE,CAE/E,CAAC,EAEG,IAAY,KAAM,CACpB,IAAM,EAAS,EAAQ,KAAM,GAAM,EAAE,SAAW,QAAQ,GAAK,CAAE,OAAQ,SAAU,MAAO,mBAAoB,EAK5G,OAJI,EAAO,QAAU,MAAQ,EAAO,MAAM,KAAK,IAAM,KACnD,EAAK,8CAA8C,EAG9C,CACL,KAAM,KACN,QAAS,CAAC,CAAE,OAAQ,SAAU,MAAO,EAAO,KAAM,CAAC,CACrD,CACF,CAEA,GAAI,IAAY,SAKd,OAJI,EAAQ,SAAW,GACrB,EAAK,YAAY,EAAQ,8BAA8B,EAGlD,CAAE,KAAM,SAAU,QAAS,CAAC,CAAE,EAGvC,GAAI,IAAY,OAKd,OAJI,EAAQ,SAAW,GACrB,EAAK,YAAY,EAAQ,8BAA8B,EAGlD,CAAE,KAAM,OAAQ,QAAS,CAAC,CAAE,EAGrC,GAAI,IAAY,OAId,OAHI,EAAQ,SAAW,GACrB,EAAK,YAAY,EAAQ,8BAA8B,EAElD,CAAE,KAAM,OAAQ,QAAS,CAAC,CAAE,EAIrC,EAAK,iBAAiB,CACxB,CAEA,SAAgB,GAA+B,CAC7C,IAAM,EAAO,QAAQ,KAAK,MAAM,CAAC,EAE3B,EAA2D,CAAC,EAC9D,EAAyB,KAE7B,IAAK,IAAI,EAAI,EAAG,EAAI,EAAK,OAAQ,IAAK,CACpC,IAAM,EAAI,EAAK,GAEf,GAAI,EAAE,WAAW,IAAI,EAAG,CACtB,IAAI,EAAM,GACN,EAAQ,KACR,EAAE,SAAS,GAAG,EAChB,CAAC,EAAK,GAAS,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,IAAK,CAAC,GAEtC,EAAM,EAAE,MAAM,CAAC,EACf,EAAQ,EAAK,EAAI,IAAM,KACvB,KAEE,EAAQ,KAAM,GAAM,EAAE,SAAW,CAAG,GACtC,EAAK,qBAAqB,EAAI,EAAE,EAElC,EAAQ,KAAK,CAAE,OAAQ,EAAK,OAAM,CAAC,EACnC,QACF,CAEI,IAAY,MACd,EAAK,6BAA6B,EAAQ,kBAAkB,EAAE,EAAE,EAElE,EAAU,CACZ,CAEA,OAAO,EAAS,EAAS,CAAO,CAClC,CCtFA,SAAgB,EAAS,EAAyB,CAChD,GAAI,EAAQ,OAAS,KAAM,CACzB,IAAI,EAAa,EAAQ,QAAQ,KAAM,GAAM,EAAE,SAAW,QAAQ,CAAC,EAAE,OAAS,oBAC9E,EAAaA,EAAAA,QAAK,WAAW,CAAU,EAAI,EAAaA,EAAAA,QAAK,KAAK,QAAQ,IAAI,EAAG,CAAU,EAE3F,EAAA,EADe,QAAQ,CACV,CAAC,CAAC,OAAO,CACxB,MAAO,GAAI,EAAQ,OAAS,SAAU,CAGpC,IAAM,EADUC,EAAAA,EACQ,CAAC,CAAC,oBAAoB,EAE9C,GADA,QAAQ,IAAI,4BAA4B,EACpC,EAAU,SAAW,EACvB,QAAQ,IAAI,yBAAyB,OAErC,IAAK,IAAM,KAAY,EACrB,QAAQ,IAAI,YAAY,EAAS,KAAK,EACtC,QAAQ,IAAI,aAAa,EAAS,MAAM,EACxC,QAAQ,IAAI,kBAAkB,EAAS,UAAU,EACjD,QAAQ,IAAI,aAAa,EAAS,MAAM,EACxC,QAAQ,IAAI,oBAAoB,EAAS,WAAW,MAAM,EAAG,EAAE,EAAE,IAAI,EACrE,QAAQ,IAAI,gBAAgB,IAAI,KAAK,EAAS,SAAS,CAAC,CAAC,YAAY,GAAG,EACxE,QAAQ,IAAI,oBAAoB,EAAS,KAAK,EAC9C,QAAQ,IAAI,oBAAoB,EAAS,YAAY,EACrD,QAAQ,IAAI,EAAE,CAGpB,MAAO,GAAI,EAAQ,OAAS,OAAQ,CAElC,IAAM,EAAUA,EAAAA,EAAmB,EAC7B,EAAY,EAAQ,oBAAoB,EAC9C,GAAI,EAAU,SAAW,EAAG,CAC1B,QAAQ,IAAI,+BAA+B,EAC3C,MACF,CACA,QAAQ,IAAI,YAAY,EAAU,OAAO,gBAAgB,EACzD,IAAK,IAAM,KAAY,EACrB,GAAI,CACF,QAAQ,KAAK,EAAS,IAAK,SAAS,EACpC,QAAQ,IAAI,6BAA6B,EAAS,KAAK,CACzD,OAAS,EAAO,CACd,QAAQ,MAAM,oCAAoC,EAAS,MAAO,CAAK,CACzE,CAGF,EAAQ,mBAAmB,EAC3B,QAAQ,IAAI,wBAAwB,CACtC,MAAW,EAAQ,OAAS,QAE1B,QAAQ,IAAI,sCAAsC,CAEtD,CCnDA,SAAS,GAAO,CAEd,EADgB,EACD,CAAC,CAClB,CAEA,EAAK"}
|
package/dist/cli.d.cts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
package/dist/cli.d.mts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
package/dist/cli.mjs
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
#!/usr/bin/env tsx
|
|
2
|
-
import{createRequire as e}from"node:module";import t from"node:path";import n from"node:cluster";import r from"fast-json-stable-stringify";import i from"node:fs";import a from"node:os";import o from"node:http";import s from"node:crypto";import c from"node:https";import l from"chokidar";import{minimatch as u}from"minimatch";var d=e(import.meta.url);function f(e){console.log(e),process.exit(0)}function p(e,t){let n=[`config`],r=[null,`status`,`stop`,`logs`];if(r.includes(e)||f(`Unknown command [${e}], please use one of [${r.join(`, `)}]`),t.forEach(e=>{n.includes(e.option)||f(`Unknown option [${e.option}], please use one of [${n.join(`, `)}]`)}),e===null){let e=t.find(e=>e.option===`config`)??{option:`config`,value:`fluxion.config.ts`};return(e.value===null||e.value.trim()===``)&&f(`Option [--config] requires a non-empty value`),{name:null,options:[{option:`config`,value:e.value}]}}if(e===`status`)return t.length!==0&&f(`Command [${e}] does not accept any options`),{name:`status`,options:[]};if(e===`stop`)return t.length!==0&&f(`Command [${e}] does not accept any options`),{name:`stop`,options:[]};if(e===`stop`)return t.length!==0&&f(`Command [${e}] does not accept any options`),{name:`logs`,options:[]};f(`Unknown command`)}function m(){let e=process.argv.slice(2),t=[],n=null;for(let r=0;r<e.length;r++){let i=e[r];if(i.startsWith(`--`)){let n=``,a=null;i.includes(`=`)?[n,a]=i.slice(2).split(`=`,2):(n=i.slice(2),a=e[r+1]??null,r++),t.some(e=>e.option===n)&&f(`Duplicate option [${n}]`),t.push({option:n,value:a});continue}n!==null&&f(`Already detect a command [${n}], but now got [${i}]`),n=i}return p(n,t)}function h(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 g=process.env.FLUXION_COLORS!==`0`;let _;(function(e){e.reset=g?`\x1B[0m`:``,e.bold=g?`\x1B[1m`:``,e.dim=g?`\x1B[2m`:``,e.italic=g?`\x1B[3m`:``,e.underline=g?`\x1B[4m`:``,e.blink=g?`\x1B[5m`:``,e.inverse=g?`\x1B[7m`:``,e.black=g?`\x1B[30m`:``,e.red=g?`\x1B[31m`:``,e.green=g?`\x1B[32m`:``,e.yellow=g?`\x1B[33m`:``,e.blue=g?`\x1B[34m`:``,e.magenta=g?`\x1B[35m`:``,e.cyan=g?`\x1B[36m`:``,e.white=g?`\x1B[37m`:``,e.brightBlack=g?`\x1B[90m`:``,e.brightRed=g?`\x1B[91m`:``,e.brightGreen=g?`\x1B[92m`:``,e.brightYellow=g?`\x1B[93m`:``,e.brightBlue=g?`\x1B[94m`:``,e.brightMagenta=g?`\x1B[95m`:``,e.brightCyan=g?`\x1B[96m`:``,e.brightWhite=g?`\x1B[97m`:``,e.bgBlack=g?`\x1B[40m`:``,e.bgRed=g?`\x1B[41m`:``,e.bgGreen=g?`\x1B[42m`:``,e.bgYellow=g?`\x1B[43m`:``,e.bgBlue=g?`\x1B[44m`:``,e.bgMagenta=g?`\x1B[45m`:``,e.bgCyan=g?`\x1B[46m`:``,e.bgWhite=g?`\x1B[47m`:``,e.bgBrightBlack=g?`\x1B[100m`:``,e.bgBrightRed=g?`\x1B[101m`:``,e.bgBrightGreen=g?`\x1B[102m`:``,e.bgBrightYellow=g?`\x1B[103m`:``,e.bgBrightBlue=g?`\x1B[104m`:``,e.bgBrightMagenta=g?`\x1B[105m`:``,e.bgBrightCyan=g?`\x1B[106m`:``,e.bgBrightWhite=g?`\x1B[107m`:``,e.purple=g?`\x1B[38;2;225;16;248m`:``,e.orange=g?`\x1B[38;2;248;147;16m`:``,e.darkGreen=g?`\x1B[38;2;22;101;52m`:``,e.claude=g?`\x1B[38;2;217;119;87m`:``,e.deepseek=g?`\x1B[38;2;57;100;254m`:``,e.gpt=g?`\x1B[38;2;41;60;77m`:``})(_||={});const v=e=>{try{return r(e)}catch{return`[unserializable]`}},y={INFO:`${_.cyan}INFO${_.reset}`,WARN:`${_.orange}WARN${_.reset}`,ERROR:`${_.red}ERROR${_.reset}`,SUCC:`${_.green}SUCC${_.reset}`,DEBUG:`${_.blue}DEBUG${_.reset}`,VERBOSE:`${_.purple}VERBOSE${_.reset}`},b=e=>{let{level:t,timestamp:n,message:r,pid:i,...a}=e,o=`${_.darkGreen}[${n}]${_.reset}`,s=y[t]??t,c=i===void 0?``:` [${i}]`,l=Object.keys(a).length>0?` ${_.dim}${v(a)}${_.reset}`:``;console.log(`${o} ${s}${c} ${r}${l}`)};function x(e){let t=e.options.logger;return t===void 0||t===`one-line`?b:t===`json-line`?e=>console.log(v(e)):t}function S(e){let t=x(e);return{write(e,n){let r=typeof n==`string`?{message:n,timestamp:h(),level:e}:{...n,timestamp:h(),level:e};try{t(r)}catch{}},info(e){this.write(`INFO`,e)},warn(e){this.write(`WARN`,e)},error(e){this.write(`ERROR`,e)},succ(e){this.write(`SUCC`,e)},debug(e){this.write(`DEBUG`,e)},verbose(e){this.write(`VERBOSE`,e)}}}function ee(e,t){return{write(n,r){e.write(n,typeof r==`string`?{message:r,pid:t}:{...r,pid:t})},info(e){this.write(`INFO`,e)},warn(e){this.write(`WARN`,e)},error(e){this.write(`ERROR`,e)},succ(e){this.write(`SUCC`,e)},debug(e){this.write(`DEBUG`,e)},verbose(e){this.write(`VERBOSE`,e)}}}const C=typeof Error.isError==`function`?e=>Error.isError(e)?e.message:String(e):e=>e?.message||String(e),w=Symbol(`fluxion.router.StaticHandled`),T=Symbol(`fluxion.router.StaticHandled`),E=Symbol(`fluxion.handlerTimeout`),D=Symbol(`fluxion.middlewareTimeout`),te={".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`};function ne(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 O(e,n){if(Buffer.isBuffer(e))return e;if(typeof e==`string`){if(!e.startsWith(`-----BEGIN`)){let r=t.isAbsolute(e)?e:t.join(n,e);if(i.existsSync(r))return i.readFileSync(r)}return Buffer.from(e)}throw Error(`[fluxion error] Certificate content must be a string or Buffer`)}function re(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:O(e.key,t),cert:O(e.cert,t)};return e.ca!==void 0&&(Array.isArray(e.ca)?n.ca=e.ca.map(e=>O(e,t)):n.ca=O(e.ca,t)),n}function ie(e){if(typeof e!=`object`||!e||Array.isArray(e))throw Error(`[fluxion error] FluxionOptions must be an object`);let{dir:n,host:r,port:a,handlerTimeoutMs:o=5e3,middlewareTimeoutMs:s=3e3,staticResourceTimeoutMs:c=10*6e5,metaPort:l=a+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,metaSecret:y}=e,b=e.logger??`one-line`;if(b!==`one-line`&&b!==`json-line`&&typeof b!=`function`)throw Error(`[fluxion error] Invalid logger option, Must be 'one-line', 'json-line' or a custom logger function`);if(typeof n!=`string`)throw Error(`[fluxion error] FluxionOptions.dir must be a string`);let x=t.resolve(n);if(typeof u!=`string`)throw Error(`[fluxion error] FluxionOptions.moduleDir must be a string`);let S=t.resolve(u);if(typeof r!=`string`)throw Error(`[fluxion error] FluxionOptions.host must be a string`);if(!Number.isSafeInteger(o)||o<=100)throw Error(`[fluxion error] FluxionOptions.handlerTimeoutMs must be an integer greater than 100`);if(!Number.isSafeInteger(s)||s<=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 a!=`number`||!Number.isSafeInteger(a))throw Error(`[fluxion error] FluxionOptions.port must be a positive integer`);if(a<=1||a>65535)throw Error(`[fluxion error] FluxionOptions.port must be 1 ~ 65535`);if(typeof l!=`number`||!Number.isSafeInteger(l))throw Error(`[fluxion error] FluxionOptions.metaPort must be a positive integer`);if(l<=1||l>65535)throw Error(`[fluxion error] FluxionOptions.metaPort must be 1 ~ 65535`);if(l===a)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`);if(y!==void 0&&(typeof y!=`string`||y.length<20||/\s/.test(y)||!/[A-Za-z]/.test(y)||!/\d/.test(y)))throw Error(`[fluxion error] FluxionOptions.metaSecret must be a string with at least 20 characters, include both letters and digits, and contain no whitespace`);return i.existsSync(x)||i.mkdirSync(x,{recursive:!0}),{dir:x,host:r,port:a,handlerTimeoutMs:o,middlewareTimeoutMs:s,staticResourceTimeoutMs:c,reloadDelay:p,metaPort:l,moduleDir:S,workerOptions:ne(d),maxRequestBytes:f,logger:b,include:m,apiInclude:h,exclude:g,nativeWatcher:v,metaSecret:y,https:re(_,S),normalizedFlag:w}}const ae=e=>[100,101].includes(e?.type),k=e=>[202,200,201,203,204].includes(e?.type),A=e=>process.send?.(e),j=(e,t)=>e.send(t);function M(e,t,n=200){e.statusCode=n,e.setHeader(`Content-Type`,`application/json; charset=utf-8`),e.end(JSON.stringify(t))}function N(e,t,n=200){if(!e.writableEnded){if(e.headersSent){e.end();return}M(e,t,n)}}function P(e,t,n){let r=o.createServer(async(r,i)=>{let a=r.method??`GET`,o;try{o=new URL(r.url??`/`,`http://fluxion.local`)}catch{M(i,{message:`Bad Request: invalid url`},400);return}if(a===`GET`&&o.pathname===`/_fluxion/healthz`){M(i,{ok:!0,role:`primary`,pid:process.pid,now:Date.now(),uptimeSeconds:Number(process.uptime().toFixed(3))});return}if(a===`GET`&&o.pathname===`/_fluxion/workers`){M(i,{ok:!0,now:Date.now(),workers:t()});return}if(a===`GET`&&o.pathname===`/_fluxion/routes`){if(!e.options.metaSecret){M(i,{message:`Not Found`},404);return}if(o.searchParams.get(`secret`)!==e.options.metaSecret){M(i,{message:`Forbidden`},403);return}let t=await n();M(i,{ok:!0,now:Date.now(),routes:t});return}M(i,{message:`Not Found`},404)});return r.on(`listening`,()=>{e.logger.info({message:`MetaApiStarted`,pid:process.pid,host:e.options.host,port:e.options.metaPort,prefix:`/_fluxion`})}),r.on(`error`,t=>{e.logger.error({message:`MetaApiError`,host:e.options.host,port:e.options.metaPort,code:t.code,error:C(t)}),process.exit(1)}),r.listen(e.options.metaPort,e.options.host),r}var F=class{instanceFilePath;homeDir;fluxionDir;constructor(){this.homeDir=a.homedir(),this.fluxionDir=t.join(this.homeDir,`.fluxion`),this.instanceFilePath=t.join(this.fluxionDir,`instance.json`),this.ensureFluxionDir()}ensureFluxionDir(){i.existsSync(this.fluxionDir)||i.mkdirSync(this.fluxionDir,{recursive:!0})}calculateConfigHash(e){try{if(!i.existsSync(e))return``;let t=i.readFileSync(e,`utf-8`);return s.createHash(`sha256`).update(t).digest(`hex`)}catch{return``}}readInstances(){try{if(!i.existsSync(this.instanceFilePath))return[];let e=i.readFileSync(this.instanceFilePath,`utf-8`);return JSON.parse(e).instances||[]}catch{return[]}}writeInstances(e){try{let t={instances:e};i.writeFileSync(this.instanceFilePath,JSON.stringify(t,null,2),`utf-8`)}catch(e){console.error(`Failed to write instance.json:`,e)}}cleanupDeadInstances(e){return e.filter(e=>{try{return process.kill(e.pid,0),!0}catch{return!1}})}findDuplicateInstance(e,t){let n=this.readInstances();return this.cleanupDeadInstances(n).find(n=>n.configHash===e||n.port===t)||null}killProcess(e){try{return process.kill(e,`SIGTERM`),!0}catch(t){return console.error(`Failed to kill process ${e}:`,t),!1}}registerInstance(e,t,n,r){let i=this.calculateConfigHash(e),a=process.pid,o=process.cwd(),s=this.findDuplicateInstance(i,n);if(s){console.warn(`[FluxionInstanceManager] Found existing instance with same config or port: PID=${s.pid}, PORT=${s.port}`),this.killProcess(s.pid)&&console.warn(`[FluxionInstanceManager] Killed old process ${s.pid}`);let e=this.readInstances().filter(e=>e.pid!==s.pid);this.writeInstances(e)}let c=this.readInstances();c=this.cleanupDeadInstances(c),c=c.filter(e=>e.pid!==a);let l={startTime:Date.now(),pid:a,configHash:i,host:t,port:n,metaPort:r,cwd:o,configPath:e};c.push(l),this.writeInstances(c),console.info(`[FluxionInstanceManager] Registered instance: PID=${a}, PORT=${n}, HASH=${i.slice(0,8)}...`)}unregisterInstance(){let e=process.pid,t=this.readInstances(),n=t.filter(t=>t.pid!==e);n.length!==t.length&&(this.writeInstances(n),console.info(`[FluxionInstanceManager] Unregistered instance: PID=${e}`))}getRunningInstances(){let e=this.readInstances();return this.cleanupDeadInstances(e)}printInstances(){let e=this.getRunningInstances();console.info(`[FluxionInstanceManager] Current instances:`);for(let t of e)console.info(` - PID: ${t.pid}, PORT: ${t.port}, HASH: ${t.configHash.slice(0,8)}..., START: ${new Date(t.startTime).toISOString()}`)}};let I=null;function L(){return I||=new F,I}function oe(e,t,n,r){L().registerInstance(e,t,n,r)}function R(){L().unregisterInstance()}const z=e=>Number((e/1024/1024).toFixed(2)),B=6e4;function V(e){if(!n.isPrimary)throw Error(`[fluxion error] createPrimary should only be called in primary process`);oe(t.join(e.options.moduleDir||process.cwd(),`fluxion.config.ts`),e.options.host,e.options.port,e.options.metaPort),process.on(`exit`,R),process.on(`SIGINT`,()=>{R(),process.exit(0)}),process.on(`SIGTERM`,()=>{R(),process.exit(0)});let{workerOptions:r}=e.options,i=r.restartWhen,o=Math.max(1,a.cpus().length),s=Math.max(1,Math.min(r.maxWorkerCount??Math.min(2,o),o));e.logger.info({message:`PrimaryStarted`,pid:process.pid,workers:s,host:e.options.host,port:e.options.port,metaPort:e.options.metaPort});let c=new Map,l=new Map,u=0,d=new Map,f=e=>{let t=Date.now(),n=(d.get(e)??[]).filter(e=>t-e<B);return d.set(e,n),n.length},p=e=>{let t=Date.now(),n=(d.get(e)??[]).filter(e=>t-e<B);n.push(t),d.set(e,n)},m=e=>f(e)>=3;P(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(c.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 e=Array.from(c.values()).find(e=>e.state===`ready`&&e.instance.isConnected());return e?new Promise(t=>{let n=++u,r=setTimeout(()=>{l.delete(n),t([])},1e3);r.unref(),l.set(n,{resolve:t,timer:r});try{j(e.instance,{type:101,requestId:n})}catch{clearTimeout(r),l.delete(n),t([])}}):Promise.resolve([])});let h=(t,n)=>{for(let e of c.values())if(e.state===`restarting`)return;if(m(t.slot)){e.logger.warn({message:`WorkerRecycleSuppressed`,slot:t.slot,pid:t.pid,reason:n,windowMs:B,max:3});return}p(t.slot),t.state=`restarting`,t.restartReason=n,e.logger.warn({message:`WorkerRecycling`,slot:t.slot,pid:t.pid,reason:n}),t.instance.kill()},g=(e,t)=>{let n=z(t.memory.rss);if(n>i.memoryUsageGreaterThan){h(e,`memoryUsageGreaterThan: rss ${n}MB > ${i.memoryUsageGreaterThan}MB`);return}let r=t.uptimeSeconds*1e3;r>i.uptimeGreaterThan&&h(e,`uptimeGreaterThan: ${Math.round(r/1e3)}s > ${Math.round(i.uptimeGreaterThan/1e3)}s`)},_=e=>{for(let t of c.values()){if(t.state!==`ready`||t.lastPongAt===void 0)continue;let n=e-t.lastPongAt;n>i.healthzTimeout&&h(t,`healthzTimeout: no pong for ${Math.round(n/1e3)}s > ${Math.round(i.healthzTimeout/1e3)}s`)}},v=e=>{y(n.fork({WORKER_ID:String(e)}),e)},y=(t,n)=>{let r={state:`creating`,pid:t.process.pid,slot:n,createdAt:Date.now(),instance:t};c.set(t.id,r),t.on(`message`,i=>{if(k(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({message:`WorkerReady`,workerId:t.id,slot:n,pid:i.pid});return}if(i.type===200){r.state=`created`,r.pid=i.pid,e.logger.info({message:`WorkerCreated`,workerId:t.id,slot:n,pid:i.pid});return}if(i.type===203){r.pid=i.pid,r.lastStats=i.stats,r.state===`ready`&&g(r,i.stats);return}if(i.type===204){let e=l.get(i.requestId);e&&(clearTimeout(e.timer),l.delete(i.requestId),e.resolve(i.routes))}}}),t.on(`exit`,(n,r)=>{let i=c.get(t.id);c.delete(t.id);let a=i?.slot,o=i?.state===`restarting`,s=i?.restartReason??null;if(e.logger.warn({message:`WorkerExited`,workerId:t.id,slot:a??null,pid:t.process.pid??`unknown`,code:n,signal:r??`none`,expected:o,reason:s}),a!==void 0){if(o){v(a);return}if(p(a),m(a)){e.logger.error({message:`WorkerRespawnSuppressed`,slot:a,windowMs:B,max:3});return}v(a)}})};for(let e=0;e<s;e++)v(e+1);setInterval(()=>{let e=Date.now();for(let t of c.values())if(t.instance.isConnected())try{j(t.instance,{type:100,sentAt:e})}catch{}_(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}var q=class extends Error{errno;code;constructor(e,t,n){super(e),this.name=`HttpException`,this.errno=t,this.code=n}},se=class extends q{constructor(e=`Payload Too Large`){super(e,413,`PAYLOAD_TOO_LARGE`)}};function J(e,t){return new se(`request body too large: ${e.toString()} bytes exceeds ${t.toString()} bytes`)}function Y(e){return Array.isArray(e)?e[0]:e}function X(){return{exists:!1,bytes:0,truncated:!1}}function ce(e,t,n,r){return t===0?X():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 le(e,t,n,r=8192){if(t===`GET`||t===`HEAD`||e.readableEnded)return{rawBody:void 0,preview:X()};let i=Y(e.headers[`content-length`]),a=i===void 0?NaN:Number.parseInt(i,10);return new Promise((t,i)=>{let o=[],s=[],c=0,l=0,u=!1,d=!1,f=()=>{e.off(`data`,m),e.off(`end`,h),e.off(`error`,g),e.off(`aborted`,_)},p=e=>{d||(d=!0,e())};if(Number.isFinite(a)&&a>n){f(),e.resume(),p(()=>i(J(a,n)));return}let m=t=>{let a=Buffer.isBuffer(t)?t:Buffer.from(t);if(c+=a.byteLength,c>n){f(),e.resume(),p(()=>i(J(c,n)));return}if(o.push(a),l<r){let e=r-l,t=a.subarray(0,e);s.push(t),l+=t.length,t.length<a.length&&(u=!0)}else u=!0},h=()=>{f(),p(()=>{let n=o.length>0?Buffer.concat(o):void 0;t({rawBody:n,preview:ce(s.length>0?Buffer.concat(s):Buffer.alloc(0),n?.byteLength??0,Y(e.headers[`content-type`]),u)})})},g=e=>{f(),p(()=>i(e))},_=()=>{f(),p(()=>i(Error(`request aborted while reading body`)))};e.on(`data`,m),e.once(`end`,h),e.once(`error`,g),e.once(`aborted`,_)})}async function ue(e,t,n){let{rawBody:r,preview:i}=await le(e,t,n);if(r===void 0||r.byteLength===0)return{body:{},preview:i};let a=Y(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 de(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 Z=(e,t,n)=>Promise.race([e,new Promise(e=>setTimeout(()=>e(n),t))]);function fe(e){let t=Object.freeze({logger:e.logger}),n=async(n,r)=>{let i=n.method??`GET`,a=U(n),o=G(n.url);if(o===void 0){N(r,{message:`Bad Request: req.url is undefined`},400);return}let s={method:i,ip:a,url:o,query:K(o.searchParams),body:{},headers:n.headers,cookie:de(n.headers.cookie),meta:{}},c={exists:!1,bytes:0,truncated:!1};e.logger.info({message:`request`,method:i,ip:a,path:o.pathname});let l=performance.now();r.once(`finish`,()=>{let t={workerId:process.env.WORKER_ID??`[primary]`,message:`response`,method:i,ip:a,path:o.pathname,status:r.statusCode,duration:(performance.now()-l).toFixed(4)};Object.keys(s.query).length>0&&(t.query=s.query),c.exists&&(t.body=c.value,t.bodyBytes=c.bytes,t.bodyTruncated=c.truncated),e.logger.info(t)});try{if(s.url.pathname.startsWith(`/_fluxion/`)){N(r,{message:`Not Found`},404);return}let i=await ue(n,s.method,e.options.maxRequestBytes);s.body=i.body,c=i.preview;let a=await e.router.getModule(o);if(!a){N(r,{message:`Not Found`},404);return}if(n.method&&a.methods&&!a.methods.includes(n.method)){N(r,{message:`Method Not Allowed`},405);return}let l=a.type===0?a.handlerTimeoutMs??e.options.handlerTimeoutMs:e.options.staticResourceTimeoutMs;if(a.middlewares)for(let i=0;i<a.middlewares.length;i++){if(await Z(H(a.middlewares[i],s,t,n,r),e.options.middlewareTimeoutMs,D)===D){e.logger.warn({message:`MiddlewareTimeout`,method:s.method,ip:s.ip}),N(r,{message:`Internal Server Error`},500);return}if(r.writableEnded)return;if(r.headersSent){r.end();return}}let u=await Z(H(a.handler,s,t,n,r),l,E);if(u===E){e.logger.warn({message:`HandlerTimeout`,method:s.method,ip:s.ip}),N(r,{message:`Handler timed out`},500);return}u!==T&&N(r,u)}catch(t){t instanceof q?(e.logger.error({message:`RequestFailed`,method:s.method,ip:s.ip,path:s.url.pathname,error:t.message}),N(r,{message:t.message},t.errno)):(e.logger.error({message:`RequestFailed`,method:s.method,ip:s.ip,path:s.url.pathname,error:C(t)}),N(r,{message:C(t)},t.errno??500))}},r=e.options.https?c.createServer({key:e.options.https.key,cert:e.options.https.cert,ca:e.options.https.ca},n):o.createServer(n);return new Promise((t,n)=>{let i=!1;r.on(`close`,()=>{e.logger.info({message:`ServerClosed`,host:e.options.host,port:e.options.port})}),r.once(`listening`,()=>{i=!0,e.logger.info({message:`ServerStarted`,pid:process.pid,protocol:e.options.https?`https`:`http`,host:e.options.host,port:e.options.port}),e.logger.info({message:`DynamicDirectory`,directory:e.options.dir}),t(r)}),r.on(`error`,t=>{e.logger.error({message:`ServerError`,error:C(t)}),i&&process.exit(1),n(t)}),r.listen(e.options.port,e.options.host)})}const pe=()=>{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();A({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 me(e){if(n.isPrimary)throw Error(`[fluxion error] createWorker should only be called in worker process`);process.on(`message`,t=>{if(ae(t)){if(t.type===100){A({type:202,pid:process.pid,sentAt:t.sentAt,receivedAt:Date.now()});return}t.type===101&&A({type:204,pid:process.pid,requestId:t.requestId,routes:e.router.getRoutes()})}}),A({type:200,pid:process.pid}),pe();let t,r=!1,i=n=>{r||(r=!0,e.logger.warn({message:`WorkerShuttingDown`,pid:process.pid,signal:n}),e.watcher.stop(),t||process.exit(0),setTimeout(()=>process.exit(1),1e4).unref(),t.close(t=>{t&&(e.logger.error({message:`WorkerShutdownFailed`,pid:process.pid,error:C(t)}),process.exit(1)),process.exit(0)}))};process.once(`SIGINT`,i),process.once(`SIGTERM`,i),fe(e).then(e=>{t=e,A({type:201,pid:process.pid})}).catch(t=>{e.logger.error({message:`WorkerBootstrapFailed`,pid:process.pid,error:C(t)}),e.watcher.stop(),process.exit(1)})}var Q=class{cx;timer=null;filesChanged=new Map;constructor(e){this.cx=e}async init(){let e=this.cx.options.dir;if(!i.existsSync(e))return this.cx.logger.warn(`Directory does not exist: ${e}`),this;let n=[],r=(e,a)=>{let o=i.readdirSync(e,{withFileTypes:!0});for(let i=0;i<o.length;i++){let s=o[i],c=t.join(e,s.name),l=t.join(a,s.name);if(s.isDirectory())r(c,l);else if(s.isFile()){let e=this.cx.router.register(c,l).catch(e=>{this.cx.logger.error(`Error registering file ${l}: ${e.message}`)});n.push(e)}}};return r(e,``),await Promise.all(n),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()}},he=class extends Q{watcher=null;constructor(e){super(e)}async start(){this.stop(),await this.init();let e=this.cx.options.dir;return this.watcher=l.watch(e,{persistent:!0,ignoreInitial:!0,usePolling:!1,awaitWriteFinish:{stabilityThreshold:100,pollInterval:50}}).on(`all`,(n,r)=>{r&&this.queueUp(r,t.relative(e,r))}).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}},ge=class extends Q{watcher=null;constructor(e){super(e)}async start(){this.stop(),await this.init();let e=this.cx.options.dir;return this.watcher=i.watch(e,{recursive:!0},(n,r)=>{r&&this.queueUp(t.join(e,r),r)}).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 _e(e){}const ve=_e;function $(e,t){if(typeof t!=`object`||!t)return!1;if(ve(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 ye(e,t){delete d.cache[t];let n=d(t);if($(e,n.default))n=n.default;else if(!$(e,n))throw Error(`[fluxion error] Invalid handler module '${t}', make sure it satisfies defineFluxionModule(...) helper`);return n}var be=class{cx;handlers=new Map;constructor(e){this.cx=e}makeStaticResource(e){return{type:1,handler:async(n,r,a,o)=>{if(n.method!==`GET`&&n.method!==`HEAD`){o.statusCode=405,o.setHeader(`Allow`,`GET, HEAD`),o.end();return}if(!i.existsSync(e)){o.statusCode=404,o.end(`Not Found`);return}let s=i.statSync(e);if(!s.isFile()){o.statusCode=404,o.end(`Not Found`);return}let c=te[t.extname(e).toLowerCase()]??`application/octet-stream`;if(o.statusCode=200,o.setHeader(`Content-Type`,c),o.setHeader(`Content-Length`,String(s.size)),n.method===`HEAD`){o.end();return}return new Promise((t,n)=>{let r=i.createReadStream(e),s=()=>{r.off(`error`,c),r.off(`end`,l),o.off(`close`,u),a.off(`aborted`,u)},c=e=>{s(),n(e)},l=()=>{s(),t(T)},u=()=>{s(),r.destroy(),t(T)};r.on(`error`,c),r.on(`end`,l),o.on(`close`,u),a.on(`aborted`,u),r.pipe(o)})}}}async register(e,t){let n=this.handlers.get(t)?.disposer;if(n&&await H(n),!i.existsSync(e)){this.handlers.delete(t),this.cx.logger.info({action:`Delete`,url:t});return}if(!this.cx.options.include.some(e=>u(t,e))){this.handlers.delete(t),this.cx.logger.info({action:`Skip`,url:t});return}if(this.cx.options.exclude.some(e=>u(t,e))){this.handlers.delete(t),this.cx.logger.info({action:`Exclude`,url:t});return}if(this.cx.options.apiInclude.some(e=>u(t,e))){let n=ye(this.cx,e);this.handlers.set(t,n),this.cx.logger.info({action:`RegisterApi`,url:t});return}this.handlers.set(t,this.makeStaticResource(e)),this.cx.logger.info({action:`RegisterStatic`,url:t})}getModule(e){let t=e.pathname.replace(/^[/]+/,``).replace(/[/]+$/,``);return this.handlers.get(t)}getRoutes(){return[...this.handlers.entries()].map(([e,t])=>({path:`/`+e,type:t.type===0?`api`:`static`,methods:t.methods?[...t.methods]:null})).sort((e,t)=>e.path.localeCompare(t.path))}};async function xe(e){let t={options:e.normalizedFlag===w?e:ie(e)};t.logger=S(t),t.router=new be(t),n.isPrimary?V(t):(t.logger=ee(t.logger,process.pid),t.watcher=await new(t.options.nativeWatcher?ge:he)(t).start(),me(t))}function Se(e){if(e.name===null){let n=e.options.find(e=>e.option===`config`)?.value??`fluxion.config.ts`;n=t.isAbsolute(n)?n:t.join(process.cwd(),n),xe(d(n).default)}else if(e.name===`status`){let e=L().getRunningInstances();if(console.log(`Running Fluxion instances:`),e.length===0)console.log(` No instances running.`);else for(let t of e)console.log(` - PID: ${t.pid}`),console.log(` Port: ${t.port}`),console.log(` Meta Port: ${t.metaPort}`),console.log(` Host: ${t.host}`),console.log(` Config Hash: ${t.configHash.slice(0,16)}...`),console.log(` Started: ${new Date(t.startTime).toISOString()}`),console.log(` Working Dir: ${t.cwd}`),console.log(` Config Path: ${t.configPath}`),console.log(``)}else if(e.name===`stop`){let e=L(),t=e.getRunningInstances();if(t.length===0){console.log(`No running instances to stop.`);return}console.log(`Stopping ${t.length} instance(s)...`);for(let e of t)try{process.kill(e.pid,`SIGTERM`),console.log(` - Stopped instance PID: ${e.pid}`)}catch(t){console.error(` - Failed to stop instance PID: ${e.pid}`,t)}e.unregisterInstance(),console.log(`All instances stopped.`)}else e.name===`logs`&&console.log(`Logs command is not yet implemented.`)}function Ce(){Se(m())}Ce();export{};
|
|
2
|
+
import{t as e,w as t,x as n}from"./fluxion--NlXBaPd.mjs";import r from"node:path";function i(e){console.log(e),process.exit(0)}function a(e,t){let n=[`config`],r=[null,`status`,`stop`,`logs`];if(r.includes(e)||i(`Unknown command [${e}], please use one of [${r.join(`, `)}]`),t.forEach(e=>{n.includes(e.option)||i(`Unknown option [${e.option}], please use one of [${n.join(`, `)}]`)}),e===null){let e=t.find(e=>e.option===`config`)??{option:`config`,value:`fluxion.config.ts`};return(e.value===null||e.value.trim()===``)&&i(`Option [--config] requires a non-empty value`),{name:null,options:[{option:`config`,value:e.value}]}}if(e===`status`)return t.length!==0&&i(`Command [${e}] does not accept any options`),{name:`status`,options:[]};if(e===`stop`)return t.length!==0&&i(`Command [${e}] does not accept any options`),{name:`stop`,options:[]};if(e===`stop`)return t.length!==0&&i(`Command [${e}] does not accept any options`),{name:`logs`,options:[]};i(`Unknown command`)}function o(){let e=process.argv.slice(2),t=[],n=null;for(let r=0;r<e.length;r++){let a=e[r];if(a.startsWith(`--`)){let n=``,o=null;a.includes(`=`)?[n,o]=a.slice(2).split(`=`,2):(n=a.slice(2),o=e[r+1]??null,r++),t.some(e=>e.option===n)&&i(`Duplicate option [${n}]`),t.push({option:n,value:o});continue}n!==null&&i(`Already detect a command [${n}], but now got [${a}]`),n=a}return a(n,t)}function s(i){if(i.name===null){let n=i.options.find(e=>e.option===`config`)?.value??`fluxion.config.ts`;n=r.isAbsolute(n)?n:r.join(process.cwd(),n),e(t(n).default)}else if(i.name===`status`){let e=n().getRunningInstances();if(console.log(`Running Fluxion instances:`),e.length===0)console.log(` No instances running.`);else for(let t of e)console.log(` - PID: ${t.pid}`),console.log(` Port: ${t.port}`),console.log(` Meta Port: ${t.metaPort}`),console.log(` Host: ${t.host}`),console.log(` Config Hash: ${t.configHash.slice(0,16)}...`),console.log(` Started: ${new Date(t.startTime).toISOString()}`),console.log(` Working Dir: ${t.cwd}`),console.log(` Config Path: ${t.configPath}`),console.log(``)}else if(i.name===`stop`){let e=n(),t=e.getRunningInstances();if(t.length===0){console.log(`No running instances to stop.`);return}console.log(`Stopping ${t.length} instance(s)...`);for(let e of t)try{process.kill(e.pid,`SIGTERM`),console.log(` - Stopped instance PID: ${e.pid}`)}catch(t){console.error(` - Failed to stop instance PID: ${e.pid}`,t)}e.unregisterInstance(),console.log(`All instances stopped.`)}else i.name===`logs`&&console.log(`Logs command is not yet implemented.`)}function c(){s(o())}c();export{};
|
|
3
|
+
//# sourceMappingURL=cli.mjs.map
|
package/dist/cli.mjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.mjs","names":[],"sources":["../src/cli/utils.ts","../src/cli/parser.ts","../src/cli/executor.ts","../src/cli/index.ts"],"sourcesContent":["export function quit(message: string): never {\n console.log(message);\n process.exit(0);\n}\n","import type { FluxionCommand } from './types.js';\nimport { quit } from './utils.js';\n\nfunction validate(command: string | null, options: Array<{ option: string; value: string | null }>): FluxionCommand {\n const OPTS = ['config'];\n const CMDS = [null, 'status', 'stop', 'logs'];\n\n // # Validating\n\n if (!CMDS.includes(command)) {\n quit(`Unknown command [${command}], please use one of [${CMDS.join(', ')}]`);\n }\n\n options.forEach((v) => {\n if (!OPTS.includes(v.option)) {\n quit(`Unknown option [${v.option}], please use one of [${OPTS.join(', ')}]`);\n }\n });\n\n if (command === null) {\n const config = options.find((v) => v.option === 'config') ?? { option: 'config', value: 'fluxion.config.ts' };\n if (config.value === null || config.value.trim() === '') {\n quit('Option [--config] requires a non-empty value');\n }\n\n return {\n name: null,\n options: [{ option: 'config', value: config.value }],\n };\n }\n\n if (command === 'status') {\n if (options.length !== 0) {\n quit(`Command [${command}] does not accept any options`);\n }\n\n return { name: 'status', options: [] };\n }\n\n if (command === 'stop') {\n if (options.length !== 0) {\n quit(`Command [${command}] does not accept any options`);\n }\n\n return { name: 'stop', options: [] };\n }\n\n if (command === 'stop') {\n if (options.length !== 0) {\n quit(`Command [${command}] does not accept any options`);\n }\n return { name: 'logs', options: [] };\n }\n\n // ! Normally nothing needed\n quit('Unknown command');\n}\n\nexport function parseCommand(): FluxionCommand {\n const args = process.argv.slice(2);\n\n const options: Array<{ option: string; value: string | null }> = [];\n let command: string | null = null;\n\n for (let i = 0; i < args.length; i++) {\n const a = args[i];\n // deal with an option\n if (a.startsWith('--')) {\n let key = '';\n let value = null;\n if (a.includes('=')) {\n [key, value] = a.slice(2).split('=', 2);\n } else {\n key = a.slice(2);\n value = args[i + 1] ?? null;\n i++;\n }\n if (options.some((v) => v.option === key)) {\n quit(`Duplicate option [${key}]`);\n }\n options.push({ option: key, value });\n continue;\n }\n\n if (command !== null) {\n quit(`Already detect a command [${command}], but now got [${a}]`);\n }\n command = a;\n }\n\n return validate(command, options);\n}\n","import path from 'node:path';\nimport { FluxionCommand } from './types.js';\nimport { fluxion } from '@/fluxion.js';\nimport { getInstanceManager } from '@/cluster/launcher.js';\n\nexport function executor(command: FluxionCommand) {\n if (command.name === null) {\n let configPath = command.options.find((v) => v.option === 'config')?.value ?? 'fluxion.config.ts';\n configPath = path.isAbsolute(configPath) ? configPath : path.join(process.cwd(), configPath);\n const config = require(configPath);\n fluxion(config.default);\n } else if (command.name === 'status') {\n // 显示当前运行的 fluxion 实例\n const manager = getInstanceManager();\n const instances = manager.getRunningInstances();\n console.log('Running Fluxion instances:');\n if (instances.length === 0) {\n console.log(' No instances running.');\n } else {\n for (const instance of instances) {\n console.log(` - PID: ${instance.pid}`);\n console.log(` Port: ${instance.port}`);\n console.log(` Meta Port: ${instance.metaPort}`);\n console.log(` Host: ${instance.host}`);\n console.log(` Config Hash: ${instance.configHash.slice(0, 16)}...`);\n console.log(` Started: ${new Date(instance.startTime).toISOString()}`);\n console.log(` Working Dir: ${instance.cwd}`);\n console.log(` Config Path: ${instance.configPath}`);\n console.log('');\n }\n }\n } else if (command.name === 'stop') {\n // 停止所有运行的 fluxion 实例\n const manager = getInstanceManager();\n const instances = manager.getRunningInstances();\n if (instances.length === 0) {\n console.log('No running instances to stop.');\n return;\n }\n console.log(`Stopping ${instances.length} instance(s)...`);\n for (const instance of instances) {\n try {\n process.kill(instance.pid, 'SIGTERM');\n console.log(` - Stopped instance PID: ${instance.pid}`);\n } catch (error) {\n console.error(` - Failed to stop instance PID: ${instance.pid}`, error);\n }\n }\n // 清理实例记录\n manager.unregisterInstance();\n console.log('All instances stopped.');\n } else if (command.name === 'logs') {\n // 显示日志(此功能暂未实现)\n console.log('Logs command is not yet implemented.');\n }\n}\n","#!/usr/bin/env tsx\nimport { parseCommand } from './parser.js';\nimport { executor } from './executor.js';\n\nfunction main() {\n const command = parseCommand();\n executor(command);\n}\n\nmain();\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{createRequire as e}from"node:module";import t from"node:cluster";import n from"fast-json-stable-stringify";import r from"node:fs";import i from"node:path";import a from"node:os";import o from"node:http";import s from"node:crypto";import c from"node:https";import l from"chokidar";import{minimatch as u}from"minimatch";var d=e(import.meta.url);function f(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 p=process.env.FLUXION_COLORS!==`0`;let m;(function(e){e.reset=p?`\x1B[0m`:``,e.bold=p?`\x1B[1m`:``,e.dim=p?`\x1B[2m`:``,e.italic=p?`\x1B[3m`:``,e.underline=p?`\x1B[4m`:``,e.blink=p?`\x1B[5m`:``,e.inverse=p?`\x1B[7m`:``,e.black=p?`\x1B[30m`:``,e.red=p?`\x1B[31m`:``,e.green=p?`\x1B[32m`:``,e.yellow=p?`\x1B[33m`:``,e.blue=p?`\x1B[34m`:``,e.magenta=p?`\x1B[35m`:``,e.cyan=p?`\x1B[36m`:``,e.white=p?`\x1B[37m`:``,e.brightBlack=p?`\x1B[90m`:``,e.brightRed=p?`\x1B[91m`:``,e.brightGreen=p?`\x1B[92m`:``,e.brightYellow=p?`\x1B[93m`:``,e.brightBlue=p?`\x1B[94m`:``,e.brightMagenta=p?`\x1B[95m`:``,e.brightCyan=p?`\x1B[96m`:``,e.brightWhite=p?`\x1B[97m`:``,e.bgBlack=p?`\x1B[40m`:``,e.bgRed=p?`\x1B[41m`:``,e.bgGreen=p?`\x1B[42m`:``,e.bgYellow=p?`\x1B[43m`:``,e.bgBlue=p?`\x1B[44m`:``,e.bgMagenta=p?`\x1B[45m`:``,e.bgCyan=p?`\x1B[46m`:``,e.bgWhite=p?`\x1B[47m`:``,e.bgBrightBlack=p?`\x1B[100m`:``,e.bgBrightRed=p?`\x1B[101m`:``,e.bgBrightGreen=p?`\x1B[102m`:``,e.bgBrightYellow=p?`\x1B[103m`:``,e.bgBrightBlue=p?`\x1B[104m`:``,e.bgBrightMagenta=p?`\x1B[105m`:``,e.bgBrightCyan=p?`\x1B[106m`:``,e.bgBrightWhite=p?`\x1B[107m`:``,e.purple=p?`\x1B[38;2;225;16;248m`:``,e.orange=p?`\x1B[38;2;248;147;16m`:``,e.darkGreen=p?`\x1B[38;2;22;101;52m`:``,e.claude=p?`\x1B[38;2;217;119;87m`:``,e.deepseek=p?`\x1B[38;2;57;100;254m`:``,e.gpt=p?`\x1B[38;2;41;60;77m`:``})(m||={});const h=e=>{try{return n(e)}catch{return`[unserializable]`}},g={INFO:`${m.cyan}INFO${m.reset}`,WARN:`${m.orange}WARN${m.reset}`,ERROR:`${m.red}ERROR${m.reset}`,SUCC:`${m.green}SUCC${m.reset}`,DEBUG:`${m.blue}DEBUG${m.reset}`,VERBOSE:`${m.purple}VERBOSE${m.reset}`},_=e=>{let{level:t,timestamp:n,message:r,pid:i,...a}=e,o=`${m.darkGreen}[${n}]${m.reset}`,s=g[t]??t,c=i===void 0?``:` [${i}]`,l=Object.keys(a).length>0?` ${m.dim}${h(a)}${m.reset}`:``;console.log(`${o} ${s}${c} ${r}${l}`)};function v(e){let t=e.options.logger;return t===void 0||t===`one-line`?_:t===`json-line`?e=>console.log(h(e)):t}function y(e){let t=v(e);return{write(e,n){let r=typeof n==`string`?{message:n,timestamp:f(),level:e}:{...n,timestamp:f(),level:e};try{t(r)}catch{}},info(e){this.write(`INFO`,e)},warn(e){this.write(`WARN`,e)},error(e){this.write(`ERROR`,e)},succ(e){this.write(`SUCC`,e)},debug(e){this.write(`DEBUG`,e)},verbose(e){this.write(`VERBOSE`,e)}}}function b(e,t){return{write(n,r){e.write(n,typeof r==`string`?{message:r,pid:t}:{...r,pid:t})},info(e){this.write(`INFO`,e)},warn(e){this.write(`WARN`,e)},error(e){this.write(`ERROR`,e)},succ(e){this.write(`SUCC`,e)},debug(e){this.write(`DEBUG`,e)},verbose(e){this.write(`VERBOSE`,e)}}}const x=typeof Error.isError==`function`?e=>Error.isError(e)?e.message:String(e):e=>e?.message||String(e),S=Symbol(`fluxion.router.StaticHandled`),C=Symbol(`fluxion.router.StaticHandled`),w=Symbol(`fluxion.handlerTimeout`),T=Symbol(`fluxion.middlewareTimeout`),ee={".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`};let te=function(e){return e[e.Ok=200]=`Ok`,e[e.Created=201]=`Created`,e[e.Accepted=202]=`Accepted`,e[e.NoContent=204]=`NoContent`,e[e.PartialContent=206]=`PartialContent`,e[e.MovedPermanently=301]=`MovedPermanently`,e[e.Found=302]=`Found`,e[e.NotModified=304]=`NotModified`,e[e.TemporaryRedirect=307]=`TemporaryRedirect`,e[e.PermanentRedirect=308]=`PermanentRedirect`,e[e.BadRequest=400]=`BadRequest`,e[e.Unauthorized=401]=`Unauthorized`,e[e.Forbidden=403]=`Forbidden`,e[e.NotFound=404]=`NotFound`,e[e.MethodNotAllowed=405]=`MethodNotAllowed`,e[e.NotAcceptable=406]=`NotAcceptable`,e[e.RequestTimeout=408]=`RequestTimeout`,e[e.Conflict=409]=`Conflict`,e[e.Gone=410]=`Gone`,e[e.PayloadTooLarge=413]=`PayloadTooLarge`,e[e.UnsupportedMediaType=415]=`UnsupportedMediaType`,e[e.UnprocessableEntity=422]=`UnprocessableEntity`,e[e.TooManyRequests=429]=`TooManyRequests`,e[e.InternalServerError=500]=`InternalServerError`,e[e.NotImplemented=501]=`NotImplemented`,e[e.BadGateway=502]=`BadGateway`,e[e.ServiceUnavailable=503]=`ServiceUnavailable`,e[e.GatewayTimeout=504]=`GatewayTimeout`,e}({});function ne(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=i.isAbsolute(e)?e:i.join(t,e);if(r.existsSync(n))return r.readFileSync(n)}return Buffer.from(e)}throw Error(`[fluxion error] Certificate content must be a string or Buffer`)}function re(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 D(e){if(typeof e!=`object`||!e||Array.isArray(e))throw Error(`[fluxion error] FluxionOptions must be an object`);let{dir:t,host:n,port:a,handlerTimeoutMs:o=5e3,middlewareTimeoutMs:s=3e3,staticResourceTimeoutMs:c=10*6e5,metaPort:l=a+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,metaSecret:y}=e,b=e.logger??`one-line`;if(b!==`one-line`&&b!==`json-line`&&typeof b!=`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`);let x=i.resolve(t);if(typeof u!=`string`)throw Error(`[fluxion error] FluxionOptions.moduleDir must be a string`);let C=i.resolve(u);if(typeof n!=`string`)throw Error(`[fluxion error] FluxionOptions.host must be a string`);if(!Number.isSafeInteger(o)||o<=100)throw Error(`[fluxion error] FluxionOptions.handlerTimeoutMs must be an integer greater than 100`);if(!Number.isSafeInteger(s)||s<=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 a!=`number`||!Number.isSafeInteger(a))throw Error(`[fluxion error] FluxionOptions.port must be a positive integer`);if(a<=1||a>65535)throw Error(`[fluxion error] FluxionOptions.port must be 1 ~ 65535`);if(typeof l!=`number`||!Number.isSafeInteger(l))throw Error(`[fluxion error] FluxionOptions.metaPort must be a positive integer`);if(l<=1||l>65535)throw Error(`[fluxion error] FluxionOptions.metaPort must be 1 ~ 65535`);if(l===a)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`);if(y!==void 0&&(typeof y!=`string`||y.length<20||/\s/.test(y)||!/[A-Za-z]/.test(y)||!/\d/.test(y)))throw Error(`[fluxion error] FluxionOptions.metaSecret must be a string with at least 20 characters, include both letters and digits, and contain no whitespace`);return r.existsSync(x)||r.mkdirSync(x,{recursive:!0}),{dir:x,host:n,port:a,handlerTimeoutMs:o,middlewareTimeoutMs:s,staticResourceTimeoutMs:c,reloadDelay:p,metaPort:l,moduleDir:C,workerOptions:ne(d),maxRequestBytes:f,logger:b,include:m,apiInclude:h,exclude:g,nativeWatcher:v,metaSecret:y,https:re(_,C),normalizedFlag:S}}const O=e=>[100,101].includes(e?.type),k=e=>[202,200,201,203,204].includes(e?.type),A=e=>process.send?.(e),j=(e,t)=>e.send(t);function M(e,t,n=200){e.statusCode=n,e.setHeader(`Content-Type`,`application/json; charset=utf-8`),e.end(JSON.stringify(t))}function N(e,t,n=200){if(!e.writableEnded){if(e.headersSent){e.end();return}M(e,t,n)}}function ie(e,t,n){let r=o.createServer(async(r,i)=>{let a=r.method??`GET`,o;try{o=new URL(r.url??`/`,`http://fluxion.local`)}catch{M(i,{message:`Bad Request: invalid url`},400);return}if(a===`GET`&&o.pathname===`/_fluxion/healthz`){M(i,{ok:!0,role:`primary`,pid:process.pid,now:Date.now(),uptimeSeconds:Number(process.uptime().toFixed(3))});return}if(a===`GET`&&o.pathname===`/_fluxion/workers`){M(i,{ok:!0,now:Date.now(),workers:t()});return}if(a===`GET`&&o.pathname===`/_fluxion/routes`){if(!e.options.metaSecret){M(i,{message:`Not Found`},404);return}if(o.searchParams.get(`secret`)!==e.options.metaSecret){M(i,{message:`Forbidden`},403);return}let t=await n();M(i,{ok:!0,now:Date.now(),routes:t});return}M(i,{message:`Not Found`},404)});return r.on(`listening`,()=>{e.logger.info({message:`MetaApiStarted`,pid:process.pid,host:e.options.host,port:e.options.metaPort,prefix:`/_fluxion`})}),r.on(`error`,t=>{e.logger.error({message:`MetaApiError`,host:e.options.host,port:e.options.metaPort,code:t.code,error:x(t)}),process.exit(1)}),r.listen(e.options.metaPort,e.options.host),r}var ae=class{instanceFilePath;homeDir;fluxionDir;constructor(){this.homeDir=a.homedir(),this.fluxionDir=i.join(this.homeDir,`.fluxion`),this.instanceFilePath=i.join(this.fluxionDir,`instance.json`),this.ensureFluxionDir()}ensureFluxionDir(){r.existsSync(this.fluxionDir)||r.mkdirSync(this.fluxionDir,{recursive:!0})}calculateConfigHash(e){try{if(!r.existsSync(e))return``;let t=r.readFileSync(e,`utf-8`);return s.createHash(`sha256`).update(t).digest(`hex`)}catch{return``}}readInstances(){try{if(!r.existsSync(this.instanceFilePath))return[];let e=r.readFileSync(this.instanceFilePath,`utf-8`);return JSON.parse(e).instances||[]}catch{return[]}}writeInstances(e){try{let t={instances:e};r.writeFileSync(this.instanceFilePath,JSON.stringify(t,null,2),`utf-8`)}catch(e){console.error(`Failed to write instance.json:`,e)}}cleanupDeadInstances(e){return e.filter(e=>{try{return process.kill(e.pid,0),!0}catch{return!1}})}findDuplicateInstance(e,t){let n=this.readInstances();return this.cleanupDeadInstances(n).find(n=>n.configHash===e||n.port===t)||null}killProcess(e){try{return process.kill(e,`SIGTERM`),!0}catch(t){return console.error(`Failed to kill process ${e}:`,t),!1}}registerInstance(e,t,n,r){let i=this.calculateConfigHash(e),a=process.pid,o=process.cwd(),s=this.findDuplicateInstance(i,n);if(s){console.warn(`[FluxionInstanceManager] Found existing instance with same config or port: PID=${s.pid}, PORT=${s.port}`),this.killProcess(s.pid)&&console.warn(`[FluxionInstanceManager] Killed old process ${s.pid}`);let e=this.readInstances().filter(e=>e.pid!==s.pid);this.writeInstances(e)}let c=this.readInstances();c=this.cleanupDeadInstances(c),c=c.filter(e=>e.pid!==a);let l={startTime:Date.now(),pid:a,configHash:i,host:t,port:n,metaPort:r,cwd:o,configPath:e};c.push(l),this.writeInstances(c),console.info(`[FluxionInstanceManager] Registered instance: PID=${a}, PORT=${n}, HASH=${i.slice(0,8)}...`)}unregisterInstance(){let e=process.pid,t=this.readInstances(),n=t.filter(t=>t.pid!==e);n.length!==t.length&&(this.writeInstances(n),console.info(`[FluxionInstanceManager] Unregistered instance: PID=${e}`))}getRunningInstances(){let e=this.readInstances();return this.cleanupDeadInstances(e)}printInstances(){let e=this.getRunningInstances();console.info(`[FluxionInstanceManager] Current instances:`);for(let t of e)console.info(` - PID: ${t.pid}, PORT: ${t.port}, HASH: ${t.configHash.slice(0,8)}..., START: ${new Date(t.startTime).toISOString()}`)}};let P=null;function F(){return P||=new ae,P}function oe(e,t,n,r){F().registerInstance(e,t,n,r)}function I(){F().unregisterInstance()}const L=e=>Number((e/1024/1024).toFixed(2)),R=6e4;function se(e){if(!t.isPrimary)throw Error(`[fluxion error] createPrimary should only be called in primary process`);oe(i.join(e.options.moduleDir||process.cwd(),`fluxion.config.ts`),e.options.host,e.options.port,e.options.metaPort),process.on(`exit`,I),process.on(`SIGINT`,()=>{I(),process.exit(0)}),process.on(`SIGTERM`,()=>{I(),process.exit(0)});let{workerOptions:n}=e.options,r=n.restartWhen,o=Math.max(1,a.cpus().length),s=Math.max(1,Math.min(n.maxWorkerCount??Math.min(2,o),o));e.logger.info({message:`PrimaryStarted`,pid:process.pid,workers:s,host:e.options.host,port:e.options.port,metaPort:e.options.metaPort});let c=new Map,l=new Map,u=0,d=new Map,f=e=>{let t=Date.now(),n=(d.get(e)??[]).filter(e=>t-e<R);return d.set(e,n),n.length},p=e=>{let t=Date.now(),n=(d.get(e)??[]).filter(e=>t-e<R);n.push(t),d.set(e,n)},m=e=>f(e)>=3;ie(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(c.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:L(r.memory.rss),heapTotalMb:L(r.memory.heapTotal),heapUsedMb:L(r.memory.heapUsed),externalMb:L(r.memory.external),arrayBuffersMb:L(r.memory.arrayBuffers)}}}})}),()=>{let e=Array.from(c.values()).find(e=>e.state===`ready`&&e.instance.isConnected());return e?new Promise(t=>{let n=++u,r=setTimeout(()=>{l.delete(n),t([])},1e3);r.unref(),l.set(n,{resolve:t,timer:r});try{j(e.instance,{type:101,requestId:n})}catch{clearTimeout(r),l.delete(n),t([])}}):Promise.resolve([])});let h=(t,n)=>{for(let e of c.values())if(e.state===`restarting`)return;if(m(t.slot)){e.logger.warn({message:`WorkerRecycleSuppressed`,slot:t.slot,pid:t.pid,reason:n,windowMs:R,max:3});return}p(t.slot),t.state=`restarting`,t.restartReason=n,e.logger.warn({message:`WorkerRecycling`,slot:t.slot,pid:t.pid,reason:n}),t.instance.kill()},g=(e,t)=>{let n=L(t.memory.rss);if(n>r.memoryUsageGreaterThan){h(e,`memoryUsageGreaterThan: rss ${n}MB > ${r.memoryUsageGreaterThan}MB`);return}let i=t.uptimeSeconds*1e3;i>r.uptimeGreaterThan&&h(e,`uptimeGreaterThan: ${Math.round(i/1e3)}s > ${Math.round(r.uptimeGreaterThan/1e3)}s`)},_=e=>{for(let t of c.values()){if(t.state!==`ready`||t.lastPongAt===void 0)continue;let n=e-t.lastPongAt;n>r.healthzTimeout&&h(t,`healthzTimeout: no pong for ${Math.round(n/1e3)}s > ${Math.round(r.healthzTimeout/1e3)}s`)}},v=e=>{y(t.fork({WORKER_ID:String(e)}),e)},y=(t,n)=>{let r={state:`creating`,pid:t.process.pid,slot:n,createdAt:Date.now(),instance:t};c.set(t.id,r),t.on(`message`,i=>{if(k(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({message:`WorkerReady`,workerId:t.id,slot:n,pid:i.pid});return}if(i.type===200){r.state=`created`,r.pid=i.pid,e.logger.info({message:`WorkerCreated`,workerId:t.id,slot:n,pid:i.pid});return}if(i.type===203){r.pid=i.pid,r.lastStats=i.stats,r.state===`ready`&&g(r,i.stats);return}if(i.type===204){let e=l.get(i.requestId);e&&(clearTimeout(e.timer),l.delete(i.requestId),e.resolve(i.routes))}}}),t.on(`exit`,(n,r)=>{let i=c.get(t.id);c.delete(t.id);let a=i?.slot,o=i?.state===`restarting`,s=i?.restartReason??null;if(e.logger.warn({message:`WorkerExited`,workerId:t.id,slot:a??null,pid:t.process.pid??`unknown`,code:n,signal:r??`none`,expected:o,reason:s}),a!==void 0){if(o){v(a);return}if(p(a),m(a)){e.logger.error({message:`WorkerRespawnSuppressed`,slot:a,windowMs:R,max:3});return}v(a)}})};for(let e=0;e<s;e++)v(e+1);setInterval(()=>{let e=Date.now();for(let t of c.values())if(t.instance.isConnected())try{j(t.instance,{type:100,sentAt:e})}catch{}_(Date.now())},5e3).unref()}function z(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 ce(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 B(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 V(e){if(e!==void 0)try{return new URL(e,`http://fluxion.local`)}catch{return}}function H(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}var U=class extends Error{errno;code;constructor(e,t,n){super(e),this.name=`HttpException`,this.errno=t,this.code=n}},W=class extends U{constructor(e=`Bad Request`){super(e,400,`BAD_REQUEST`)}},G=class extends U{constructor(e=`Unauthorized`){super(e,401,`UNAUTHORIZED`)}},le=class extends U{constructor(e=`Forbidden`){super(e,403,`FORBIDDEN`)}},ue=class extends U{constructor(e=`Not Found`){super(e,404,`NOT_FOUND`)}},de=class extends U{constructor(e=`Method Not Allowed`){super(e,405,`METHOD_NOT_ALLOWED`)}},fe=class extends U{constructor(e=`Not Acceptable`){super(e,406,`NOT_ACCEPTABLE`)}},pe=class extends U{constructor(e=`Request Timeout`){super(e,408,`REQUEST_TIMEOUT`)}},me=class extends U{constructor(e=`Conflict`){super(e,409,`CONFLICT`)}},he=class extends U{constructor(e=`Gone`){super(e,410,`GONE`)}},K=class extends U{constructor(e=`Payload Too Large`){super(e,413,`PAYLOAD_TOO_LARGE`)}},ge=class extends U{constructor(e=`Unsupported Media Type`){super(e,415,`UNSUPPORTED_MEDIA_TYPE`)}},_e=class extends U{constructor(e=`Unprocessable Entity`){super(e,422,`UNPROCESSABLE_ENTITY`)}},ve=class extends U{constructor(e=`Too Many Requests`){super(e,429,`TOO_MANY_REQUESTS`)}},ye=class extends U{constructor(e=`Internal Server Error`){super(e,500,`INTERNAL_SERVER_ERROR`)}},be=class extends U{constructor(e=`Not Implemented`){super(e,501,`NOT_IMPLEMENTED`)}},xe=class extends U{constructor(e=`Bad Gateway`){super(e,502,`BAD_GATEWAY`)}},Se=class extends U{constructor(e=`Service Unavailable`){super(e,503,`SERVICE_UNAVAILABLE`)}},Ce=class extends U{constructor(e=`Gateway Timeout`){super(e,504,`GATEWAY_TIMEOUT`)}};function q(e,t){return new K(`request body too large: ${e.toString()} bytes exceeds ${t.toString()} bytes`)}function J(e){return Array.isArray(e)?e[0]:e}function Y(){return{exists:!1,bytes:0,truncated:!1}}function we(e,t,n,r){return t===0?Y():B(n)?{exists:!0,value:e.toString(`utf8`),bytes:t,truncated:r}:{exists:!0,value:`<binary body: ${t} bytes>`,bytes:t,truncated:r}}async function Te(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);return new Promise((t,i)=>{let o=[],s=[],c=0,l=0,u=!1,d=!1,f=()=>{e.off(`data`,m),e.off(`end`,h),e.off(`error`,g),e.off(`aborted`,_)},p=e=>{d||(d=!0,e())};if(Number.isFinite(a)&&a>n){f(),e.resume(),p(()=>i(q(a,n)));return}let m=t=>{let a=Buffer.isBuffer(t)?t:Buffer.from(t);if(c+=a.byteLength,c>n){f(),e.resume(),p(()=>i(q(c,n)));return}if(o.push(a),l<r){let e=r-l,t=a.subarray(0,e);s.push(t),l+=t.length,t.length<a.length&&(u=!0)}else u=!0},h=()=>{f(),p(()=>{let n=o.length>0?Buffer.concat(o):void 0;t({rawBody:n,preview:we(s.length>0?Buffer.concat(s):Buffer.alloc(0),n?.byteLength??0,J(e.headers[`content-type`]),u)})})},g=e=>{f(),p(()=>i(e))},_=()=>{f(),p(()=>i(Error(`request aborted while reading body`)))};e.on(`data`,m),e.once(`end`,h),e.once(`error`,g),e.once(`aborted`,_)})}async function Ee(e,t,n){let{rawBody:r,preview:i}=await Te(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:H(new URLSearchParams(r.toString(`utf8`))),preview:i}:B(a)?{body:{raw:r.toString(`utf8`)},preview:i}:{body:{},preview:i}}function De(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 Oe(e){let t=Object.freeze({logger:e.logger}),n=async(n,r)=>{let i=n.method??`GET`,a=ce(n),o=V(n.url);if(o===void 0){N(r,{message:`Bad Request: req.url is undefined`},400);return}let s={method:i,ip:a,url:o,query:H(o.searchParams),body:{},headers:n.headers,cookie:De(n.headers.cookie),meta:{}},c={exists:!1,bytes:0,truncated:!1};e.logger.info({message:`request`,method:i,ip:a,path:o.pathname});let l=performance.now();r.once(`finish`,()=>{let t={workerId:process.env.WORKER_ID??`[primary]`,message:`response`,method:i,ip:a,path:o.pathname,status:r.statusCode,duration:(performance.now()-l).toFixed(4)};Object.keys(s.query).length>0&&(t.query=s.query),c.exists&&(t.body=c.value,t.bodyBytes=c.bytes,t.bodyTruncated=c.truncated),e.logger.info(t)});try{if(s.url.pathname.startsWith(`/_fluxion/`)){N(r,{message:`Not Found`},404);return}let i=await Ee(n,s.method,e.options.maxRequestBytes);s.body=i.body,c=i.preview;let a=await e.router.getModule(o);if(!a){N(r,{message:`Not Found`},404);return}if(n.method&&a.methods&&!a.methods.includes(n.method)){N(r,{message:`Method Not Allowed`},405);return}let l=a.type===0?a.handlerTimeoutMs??e.options.handlerTimeoutMs:e.options.staticResourceTimeoutMs;if(a.middlewares)for(let i=0;i<a.middlewares.length;i++){if(await X(z(a.middlewares[i],s,t,n,r),e.options.middlewareTimeoutMs,T)===T){e.logger.warn({message:`MiddlewareTimeout`,method:s.method,ip:s.ip}),N(r,{message:`Internal Server Error`},500);return}if(r.writableEnded)return;if(r.headersSent){r.end();return}}let u=await X(z(a.handler,s,t,n,r),l,w);if(u===w){e.logger.warn({message:`HandlerTimeout`,method:s.method,ip:s.ip}),N(r,{message:`Handler timed out`},500);return}u!==C&&N(r,u)}catch(t){t instanceof U?(e.logger.error({message:`RequestFailed`,method:s.method,ip:s.ip,path:s.url.pathname,error:t.message}),N(r,{message:t.message},t.errno)):(e.logger.error({message:`RequestFailed`,method:s.method,ip:s.ip,path:s.url.pathname,error:x(t)}),N(r,{message:x(t)},t.errno??500))}},r=e.options.https?c.createServer({key:e.options.https.key,cert:e.options.https.cert,ca:e.options.https.ca},n):o.createServer(n);return new Promise((t,n)=>{let i=!1;r.on(`close`,()=>{e.logger.info({message:`ServerClosed`,host:e.options.host,port:e.options.port})}),r.once(`listening`,()=>{i=!0,e.logger.info({message:`ServerStarted`,pid:process.pid,protocol:e.options.https?`https`:`http`,host:e.options.host,port:e.options.port}),e.logger.info({message:`DynamicDirectory`,directory:e.options.dir}),t(r)}),r.on(`error`,t=>{e.logger.error({message:`ServerError`,error:x(t)}),i&&process.exit(1),n(t)}),r.listen(e.options.port,e.options.host)})}const ke=()=>{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();A({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 Ae(e){if(t.isPrimary)throw Error(`[fluxion error] createWorker should only be called in worker process`);process.on(`message`,t=>{if(O(t)){if(t.type===100){A({type:202,pid:process.pid,sentAt:t.sentAt,receivedAt:Date.now()});return}t.type===101&&A({type:204,pid:process.pid,requestId:t.requestId,routes:e.router.getRoutes()})}}),A({type:200,pid:process.pid}),ke();let n,r=!1,i=t=>{r||(r=!0,e.logger.warn({message:`WorkerShuttingDown`,pid:process.pid,signal:t}),e.watcher.stop(),n||process.exit(0),setTimeout(()=>process.exit(1),1e4).unref(),n.close(t=>{t&&(e.logger.error({message:`WorkerShutdownFailed`,pid:process.pid,error:x(t)}),process.exit(1)),process.exit(0)}))};process.once(`SIGINT`,i),process.once(`SIGTERM`,i),Oe(e).then(e=>{n=e,A({type:201,pid:process.pid})}).catch(t=>{e.logger.error({message:`WorkerBootstrapFailed`,pid:process.pid,error:x(t)}),e.watcher.stop(),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(!r.existsSync(e))return this.cx.logger.warn(`Directory does not exist: ${e}`),this;let t=[],n=(e,a)=>{let o=r.readdirSync(e,{withFileTypes:!0});for(let r=0;r<o.length;r++){let s=o[r],c=i.join(e,s.name),l=i.join(a,s.name);if(s.isDirectory())n(c,l);else if(s.isFile()){let e=this.cx.router.register(c,l).catch(e=>{this.cx.logger.error(`Error registering file ${l}: ${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()}},je=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=l.watch(e,{persistent:!0,ignoreInitial:!0,usePolling:!1,awaitWriteFinish:{stabilityThreshold:100,pollInterval:50}}).on(`all`,(t,n)=>{n&&this.queueUp(n,i.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}},Q=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=r.watch(e,{recursive:!0},(t,n)=>{n&&this.queueUp(i.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 Me(e){}const Ne=Me;function $(e,t){if(typeof t!=`object`||!t)return!1;if(Ne(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 Pe(e,t){delete d.cache[t];let n=d(t);if($(e,n.default))n=n.default;else if(!$(e,n))throw Error(`[fluxion error] Invalid handler module '${t}', make sure it satisfies defineFluxionModule(...) helper`);return n}var Fe=class{cx;handlers=new Map;constructor(e){this.cx=e}makeStaticResource(e){return{type:1,handler:async(t,n,a,o)=>{if(t.method!==`GET`&&t.method!==`HEAD`){o.statusCode=405,o.setHeader(`Allow`,`GET, HEAD`),o.end();return}if(!r.existsSync(e)){o.statusCode=404,o.end(`Not Found`);return}let s=r.statSync(e);if(!s.isFile()){o.statusCode=404,o.end(`Not Found`);return}let c=ee[i.extname(e).toLowerCase()]??`application/octet-stream`;if(o.statusCode=200,o.setHeader(`Content-Type`,c),o.setHeader(`Content-Length`,String(s.size)),t.method===`HEAD`){o.end();return}return new Promise((t,n)=>{let i=r.createReadStream(e),s=()=>{i.off(`error`,c),i.off(`end`,l),o.off(`close`,u),a.off(`aborted`,u)},c=e=>{s(),n(e)},l=()=>{s(),t(C)},u=()=>{s(),i.destroy(),t(C)};i.on(`error`,c),i.on(`end`,l),o.on(`close`,u),a.on(`aborted`,u),i.pipe(o)})}}}async register(e,t){let n=this.handlers.get(t)?.disposer;if(n&&await z(n),!r.existsSync(e)){this.handlers.delete(t),this.cx.logger.info({action:`Delete`,url:t});return}if(!this.cx.options.include.some(e=>u(t,e))){this.handlers.delete(t),this.cx.logger.info({action:`Skip`,url:t});return}if(this.cx.options.exclude.some(e=>u(t,e))){this.handlers.delete(t),this.cx.logger.info({action:`Exclude`,url:t});return}if(this.cx.options.apiInclude.some(e=>u(t,e))){let n=Pe(this.cx,e);this.handlers.set(t,n),this.cx.logger.info({action:`RegisterApi`,url:t});return}this.handlers.set(t,this.makeStaticResource(e)),this.cx.logger.info({action:`RegisterStatic`,url:t})}getModule(e){let t=e.pathname.replace(/^[/]+/,``).replace(/[/]+$/,``);return this.handlers.get(t)}getRoutes(){return[...this.handlers.entries()].map(([e,t])=>({path:`/`+e,type:t.type===0?`api`:`static`,methods:t.methods?[...t.methods]:null})).sort((e,t)=>e.path.localeCompare(t.path))}};async function Ie(e){let n={options:e.normalizedFlag===S?e:D(e)};n.logger=y(n),n.router=new Fe(n),t.isPrimary?se(n):(n.logger=b(n.logger,process.pid),n.watcher=await new(n.options.nativeWatcher?Q:je)(n).start(),Ae(n))}export{te as C,D as S,ve as _,le as a,ge as b,U as c,fe as d,ue as f,Se as g,pe as h,me as i,ye as l,K as m,xe as n,Ce as o,be as p,W as r,he as s,Ie as t,de as u,G as v,d as w,F as x,_e as y};
|
|
2
|
+
//# sourceMappingURL=fluxion--NlXBaPd.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fluxion--NlXBaPd.mjs","names":[],"sources":["../src/common/dtm.ts","../src/common/color.ts","../src/common/logger.ts","../src/common/consts.ts","../src/defines/options.ts","../src/cluster/consts.ts","../src/cluster/communicate.ts","../src/http/respond.ts","../src/cluster/meta-api.ts","../src/cluster/launcher.ts","../src/cluster/primary.ts","../src/common/promise-try.ts","../src/http/headers.ts","../src/http/request.ts","../src/http/query.ts","../src/http/exceptions.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","../node_modules/.pnpm/type-narrow@0.2.2/node_modules/type-narrow/dist/index.mjs","../src/common/injector.ts","../src/router/index.ts","../src/fluxion.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","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';\nimport stringify from 'fast-json-stable-stringify';\n\nimport { dtm } from './dtm.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 [key: string]: unknown;\n}\n\nexport type LoggerOption = 'one-line' | 'json-line' | FluxionLoggerFn;\n\nexport type FluxionLoggerFn = (entry: LogEntry) => void;\n\nexport interface MessageObject {\n [key: string]: unknown;\n message?: string;\n}\n\nexport interface FluxionLogger {\n /**\n * [WARN] We assert that `fields` is an object or undefined.\n */\n write(level: LogLevel, messageOrObject: string | MessageObject): void;\n info(messageOrObject: string | MessageObject): void;\n warn(messageOrObject: string | MessageObject): void;\n error(messageOrObject: string | MessageObject): void;\n succ(messageOrObject: string | MessageObject): void;\n debug(messageOrObject: string | MessageObject): void;\n verbose(messageOrObject: string | MessageObject): 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, message: rawMessage, pid, ...fields } = entry;\n\n const timestamp = `${cctl.darkGreen}[${rawTimestamp}]${cctl.reset}`;\n const level = ColoredLevels[rawLevel] ?? rawLevel;\n const pidText = pid === undefined ? '' : ` [${pid}]`;\n const fieldsText = Object.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}${pidText} ${rawMessage}${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, o: string | object): void {\n const entry: LogEntry =\n typeof o === 'string'\n ? {\n message: o,\n timestamp: dtm(),\n level,\n }\n : {\n ...o,\n timestamp: dtm(),\n level,\n };\n\n try {\n sink(entry);\n } catch {\n // Ignore logger sink failures to avoid breaking request handling.\n }\n },\n info(messageOrObject: string | MessageObject): void {\n this.write('INFO', messageOrObject);\n },\n warn(messageOrObject: string | MessageObject): void {\n this.write('WARN', messageOrObject);\n },\n error(messageOrObject: string | MessageObject): void {\n this.write('ERROR', messageOrObject);\n },\n succ(messageOrObject: string | MessageObject): void {\n this.write('SUCC', messageOrObject);\n },\n debug(messageOrObject: string | MessageObject): void {\n this.write('DEBUG', messageOrObject);\n },\n verbose(messageOrObject: string | MessageObject): void {\n this.write('VERBOSE', messageOrObject);\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 return {\n write(level: LogLevel, messageOrObject: string | MessageObject): void {\n baseLogger.write(\n level,\n typeof messageOrObject === 'string' ? { message: messageOrObject, pid } : { ...messageOrObject, pid },\n );\n },\n info(messageOrObject: string | MessageObject): void {\n this.write('INFO', messageOrObject);\n },\n warn(messageOrObject: string | MessageObject): void {\n this.write('WARN', messageOrObject);\n },\n error(messageOrObject: string | MessageObject): void {\n this.write('ERROR', messageOrObject);\n },\n succ(messageOrObject: string | MessageObject): void {\n this.write('SUCC', messageOrObject);\n },\n debug(messageOrObject: string | MessageObject): void {\n this.write('DEBUG', messageOrObject);\n },\n verbose(messageOrObject: string | MessageObject): void {\n this.write('VERBOSE', messageOrObject);\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","export const DUMMY_BASE_URL = 'http://fluxion.local';\nexport const META_PREFIX = '/_fluxion';\n\nexport const OPTIONS_NORMALIZED_FLAG = Symbol('fluxion.router.StaticHandled');\nexport const STATIC_HANDLED_FLAG = Symbol('fluxion.router.StaticHandled');\nexport const HANDLER_TIMEOUT_FLAG = Symbol('fluxion.handlerTimeout');\nexport const MIDDLEWARE_TIMEOUT_FLAG = Symbol('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 enum HttpCode {\n // 2xx Success\n Ok = 200,\n Created = 201,\n Accepted = 202,\n NoContent = 204,\n PartialContent = 206,\n\n // 3xx Redirection\n MovedPermanently = 301,\n Found = 302,\n NotModified = 304,\n TemporaryRedirect = 307,\n PermanentRedirect = 308,\n\n // 4xx Client Error\n BadRequest = 400,\n Unauthorized = 401,\n Forbidden = 403,\n NotFound = 404,\n MethodNotAllowed = 405,\n NotAcceptable = 406,\n RequestTimeout = 408,\n Conflict = 409,\n Gone = 410,\n PayloadTooLarge = 413,\n UnsupportedMediaType = 415,\n UnprocessableEntity = 422,\n TooManyRequests = 429,\n\n // 5xx Server Error\n InternalServerError = 500,\n NotImplemented = 501,\n BadGateway = 502,\n ServiceUnavailable = 503,\n GatewayTimeout = 504,\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 fs from 'node:fs';\nimport path from 'node:path';\nimport type { WorkerOptions, NormalizedWorkerOptions, FluxionOptions, NormalizedFluxionOptions } from '../types.js';\nimport { OPTIONS_NORMALIZED_FLAG } from '@/common/consts.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 defineFluxionOptions(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: rawDir,\n host,\n port,\n handlerTimeoutMs = 5000,\n middlewareTimeoutMs = 3000,\n staticResourceTimeoutMs = 10 * 600000,\n metaPort = port + 1,\n moduleDir: rawModuleDir = 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 metaSecret,\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 rawDir !== 'string') {\n $throw('FluxionOptions.dir must be a string');\n }\n const dir = path.resolve(rawDir);\n\n if (typeof rawModuleDir !== 'string') {\n $throw('FluxionOptions.moduleDir must be a string');\n }\n const moduleDir = path.resolve(rawModuleDir);\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 (\n metaSecret !== undefined &&\n (typeof metaSecret !== 'string' ||\n metaSecret.length < 20 ||\n /\\s/.test(metaSecret) ||\n !/[A-Za-z]/.test(metaSecret) ||\n !/\\d/.test(metaSecret))\n ) {\n $throw(\n 'FluxionOptions.metaSecret must be a string with at least 20 characters, include both letters and digits, and contain no whitespace',\n );\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 metaSecret,\n https: normalizeHttpsOptions(https, moduleDir),\n // !\n normalizedFlag: OPTIONS_NORMALIZED_FLAG,\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 /**\n * Ask a worker for its current router snapshot.\n */\n Routes,\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 /**\n * Current router snapshot from worker process.\n */\n Routes,\n}\n\nexport const isPrimaryMessage = (v: PrimaryMessage): v is PrimaryMessage =>\n [PrimaryAction.Ping, PrimaryAction.Routes].includes(v?.type);\n\nexport const isWorkerMessage = (v: WorkerMessage): v is WorkerMessage =>\n [WorkerAction.Pong, WorkerAction.Created, WorkerAction.Ready, WorkerAction.Stats, WorkerAction.Routes].includes(\n v?.type,\n );\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","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, FluxionRouteMeta } 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 getRoutesSnapshot: () => Promise<FluxionRouteMeta[]>,\n): http.Server {\n const server = http.createServer(async (req, res) => {\n const method = req.method ?? 'GET';\n\n let url: URL;\n try {\n url = new URL(req.url ?? '/', 'http://fluxion.local');\n } catch {\n sendJson(res, { message: 'Bad Request: invalid url' }, HttpCode.BadRequest);\n return;\n }\n\n if (method === 'GET' && url.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' && url.pathname === META_PREFIX + '/workers') {\n sendJson(res, {\n ok: true,\n now: Date.now(),\n workers: getWorkersSnapshot(),\n });\n return;\n }\n\n if (method === 'GET' && url.pathname === META_PREFIX + '/routes') {\n if (!cx.options.metaSecret) {\n sendJson(res, { message: 'Not Found' }, HttpCode.NotFound);\n return;\n }\n\n if (url.searchParams.get('secret') !== cx.options.metaSecret) {\n sendJson(res, { message: 'Forbidden' }, HttpCode.Forbidden);\n return;\n }\n\n const routes = await getRoutesSnapshot();\n sendJson(res, { ok: true, now: Date.now(), routes });\n return;\n }\n\n sendJson(res, { message: 'Not Found' }, HttpCode.NotFound);\n });\n\n server.on('listening', () => {\n cx.logger.info({\n message: '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: NodeJS.ErrnoException) => {\n cx.logger.error({\n message: 'MetaApiError',\n host: cx.options.host,\n port: cx.options.metaPort,\n code: error.code,\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 crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\n\n/**\n * 实例记录结构\n */\ninterface FluxionInstanceRecord {\n /** 启动时间戳 */\n startTime: number;\n /** 主进程 PID */\n pid: number;\n /** 配置文件 Hash 值 */\n configHash: string;\n /** 主机地址 */\n host: string;\n /** 端口号 */\n port: number;\n /** Meta API 端口号 */\n metaPort: number;\n /** 工作目录 */\n cwd: string;\n /** 配置文件路径 */\n configPath: string;\n}\n\n/**\n * 实例 JSON 文件结构\n */\ninterface InstanceJson {\n instances: FluxionInstanceRecord[];\n}\n\n/**\n * FLUXION 实例管理器\n * 用于防止重复启动相同配置的 fluxion 实例\n */\nexport class FluxionInstanceManager {\n private readonly instanceFilePath: string;\n private readonly homeDir: string;\n private readonly fluxionDir: string;\n\n constructor() {\n this.homeDir = os.homedir();\n this.fluxionDir = path.join(this.homeDir, '.fluxion');\n this.instanceFilePath = path.join(this.fluxionDir, 'instance.json');\n\n // 确保 .fluxion 目录存在\n this.ensureFluxionDir();\n }\n\n /**\n * 确保 .fluxion 目录存在\n */\n private ensureFluxionDir(): void {\n if (!fs.existsSync(this.fluxionDir)) {\n fs.mkdirSync(this.fluxionDir, { recursive: true });\n }\n }\n\n /**\n * 计算配置文件的 Hash 值\n * @param configPath 配置文件路径\n * @returns Hash 字符串\n */\n private calculateConfigHash(configPath: string): string {\n try {\n if (!fs.existsSync(configPath)) {\n return '';\n }\n const configContent = fs.readFileSync(configPath, 'utf-8');\n return crypto.createHash('sha256').update(configContent).digest('hex');\n } catch {\n return '';\n }\n }\n\n /**\n * 读取实例记录\n * @returns 实例记录数组\n */\n private readInstances(): FluxionInstanceRecord[] {\n try {\n if (!fs.existsSync(this.instanceFilePath)) {\n return [];\n }\n const content = fs.readFileSync(this.instanceFilePath, 'utf-8');\n const data: InstanceJson = JSON.parse(content);\n return data.instances || [];\n } catch {\n return [];\n }\n }\n\n /**\n * 写入实例记录\n * @param instances 实例记录数组\n */\n private writeInstances(instances: FluxionInstanceRecord[]): void {\n try {\n const data: InstanceJson = { instances };\n fs.writeFileSync(this.instanceFilePath, JSON.stringify(data, null, 2), 'utf-8');\n } catch (error) {\n console.error('Failed to write instance.json:', error);\n }\n }\n\n /**\n * 清理已死亡的实例记录\n * @param instances 实例记录数组\n * @returns 清理后的实例记录数组\n */\n private cleanupDeadInstances(instances: FluxionInstanceRecord[]): FluxionInstanceRecord[] {\n return instances.filter((instance) => {\n try {\n // 检查进程是否存在\n process.kill(instance.pid, 0);\n return true;\n } catch {\n // 进程不存在,移除记录\n return false;\n }\n });\n }\n\n /**\n * 查找相同配置的运行实例\n * @param configHash 配置文件 Hash 值\n * @param port 端口号\n * @returns 找到的实例记录,如果没有则返回 null\n */\n private findDuplicateInstance(configHash: string, port: number): FluxionInstanceRecord | null {\n const instances = this.readInstances();\n const cleanedInstances = this.cleanupDeadInstances(instances);\n\n // 查找相同配置或相同端口的实例\n const duplicate = cleanedInstances.find(\n (instance) => instance.configHash === configHash || instance.port === port,\n );\n\n return duplicate || null;\n }\n\n /**\n * 终止进程\n * @param pid 进程 ID\n * @returns 是否成功终止\n */\n private killProcess(pid: number): boolean {\n try {\n process.kill(pid, 'SIGTERM');\n return true;\n } catch (error) {\n console.error(`Failed to kill process ${pid}:`, error);\n return false;\n }\n }\n\n /**\n * 注册当前实例\n * @param configPath 配置文件路径\n * @param host 主机地址\n * @param port 端口号\n * @param metaPort Meta API 端口号\n */\n registerInstance(configPath: string, host: string, port: number, metaPort: number): void {\n const configHash = this.calculateConfigHash(configPath);\n const currentPid = process.pid;\n const cwd = process.cwd();\n\n // 检查是否有重复配置的实例在运行\n const duplicate = this.findDuplicateInstance(configHash, port);\n if (duplicate) {\n console.warn(\n `[FluxionInstanceManager] Found existing instance with same config or port: PID=${duplicate.pid}, PORT=${duplicate.port}`,\n );\n\n // 尝试终止旧进程\n if (this.killProcess(duplicate.pid)) {\n console.warn(`[FluxionInstanceManager] Killed old process ${duplicate.pid}`);\n }\n\n // 移除旧实例记录\n const instances = this.readInstances();\n const filtered = instances.filter((instance) => instance.pid !== duplicate.pid);\n this.writeInstances(filtered);\n }\n\n // 读取现有实例记录,清理死亡实例\n let instances = this.readInstances();\n instances = this.cleanupDeadInstances(instances);\n\n // 移除当前 PID 的旧记录(如果存在)\n instances = instances.filter((instance) => instance.pid !== currentPid);\n\n // 添加新实例记录\n const newRecord: FluxionInstanceRecord = {\n startTime: Date.now(),\n pid: currentPid,\n configHash,\n host,\n port,\n metaPort,\n cwd,\n configPath,\n };\n instances.push(newRecord);\n\n // 写入文件\n this.writeInstances(instances);\n\n console.info(\n `[FluxionInstanceManager] Registered instance: PID=${currentPid}, PORT=${port}, HASH=${configHash.slice(0, 8)}...`,\n );\n }\n\n /**\n * 注销当前实例\n */\n unregisterInstance(): void {\n const currentPid = process.pid;\n const instances = this.readInstances();\n const filtered = instances.filter((instance) => instance.pid !== currentPid);\n\n if (filtered.length !== instances.length) {\n this.writeInstances(filtered);\n console.info(`[FluxionInstanceManager] Unregistered instance: PID=${currentPid}`);\n }\n }\n\n /**\n * 获取所有运行的实例\n * @returns 实例记录数组\n */\n getRunningInstances(): FluxionInstanceRecord[] {\n const instances = this.readInstances();\n return this.cleanupDeadInstances(instances);\n }\n\n /**\n * 打印实例信息(用于调试)\n */\n printInstances(): void {\n const instances = this.getRunningInstances();\n console.info('[FluxionInstanceManager] Current instances:');\n for (const instance of instances) {\n console.info(\n ` - PID: ${instance.pid}, PORT: ${instance.port}, HASH: ${instance.configHash.slice(0, 8)}..., START: ${new Date(instance.startTime).toISOString()}`,\n );\n }\n }\n}\n\n// 全局实例管理器\nlet instanceManager: FluxionInstanceManager | null = null;\n\n/**\n * 获取全局实例管理器\n * @returns FluxionInstanceManager 实例\n */\nexport function getInstanceManager(): FluxionInstanceManager {\n if (!instanceManager) {\n instanceManager = new FluxionInstanceManager();\n }\n return instanceManager;\n}\n\n/**\n * 启动 fluxion 实例前的检查和注册\n * @param configPath 配置文件路径\n * @param host 主机地址\n * @param port 端口号\n * @param metaPort Meta API 端口号\n */\nexport function launchFluxionInstance(configPath: string, host: string, port: number, metaPort: number): void {\n const manager = getInstanceManager();\n manager.registerInstance(configPath, host, port, metaPort);\n}\n\n/**\n * 清理 fluxion 实例(在进程退出时调用)\n */\nexport function cleanupFluxionInstance(): void {\n const manager = getInstanceManager();\n manager.unregisterInstance();\n}\n","import type { WorkerMessage, WorkerState, WorkerRuntimeStats } from './types.js';\nimport type { FluxionContext, FluxionRouteMeta } from '../types.js';\nimport os from 'node:os';\nimport cluster from 'node:cluster';\nimport path from 'node:path';\n\nimport { isWorkerMessage, WorkerAction, PrimaryAction } from './consts.js';\nimport { sendToWorker } from './communicate.js';\nimport { createPrimaryMetaApiServer } from './meta-api.js';\nimport { launchFluxionInstance, cleanupFluxionInstance } from './launcher.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 // 注册当前 fluxion 实例\n const configPath = path.join(cx.options.moduleDir || process.cwd(), 'fluxion.config.ts');\n launchFluxionInstance(configPath, cx.options.host, cx.options.port, cx.options.metaPort);\n\n // 注册清理函数(在进程退出时调用)\n process.on('exit', cleanupFluxionInstance);\n process.on('SIGINT', () => {\n cleanupFluxionInstance();\n process.exit(0);\n });\n process.on('SIGTERM', () => {\n cleanupFluxionInstance();\n process.exit(0);\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({\n message: '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 const routeRequests = new Map<number, { resolve: (routes: FluxionRouteMeta[]) => void; timer: NodeJS.Timeout }>();\n let routeRequestId = 0;\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 const worker = Array.from(workers.values()).find((info) => info.state === 'ready' && info.instance.isConnected());\n if (!worker) {\n return Promise.resolve([]);\n }\n\n return new Promise((resolve) => {\n const requestId = ++routeRequestId;\n const timer = setTimeout(() => {\n routeRequests.delete(requestId);\n resolve([]);\n }, 1000);\n timer.unref();\n routeRequests.set(requestId, { resolve, timer });\n try {\n sendToWorker(worker.instance, { type: PrimaryAction.Routes, requestId });\n } catch {\n clearTimeout(timer);\n routeRequests.delete(requestId);\n resolve([]);\n }\n });\n });\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({\n message: '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({\n message: 'WorkerRecycling',\n slot: info.slot,\n pid: info.pid,\n reason,\n });\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({\n message: 'WorkerReady',\n workerId: worker.id,\n slot,\n pid: raw.pid,\n });\n return;\n }\n\n if (raw.type === WorkerAction.Created) {\n workerInfo.state = 'created';\n workerInfo.pid = raw.pid;\n cx.logger.info({\n message: 'WorkerCreated',\n workerId: worker.id,\n slot,\n pid: raw.pid,\n });\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 return;\n }\n\n if (raw.type === WorkerAction.Routes) {\n const request = routeRequests.get(raw.requestId);\n if (request) {\n clearTimeout(request.timer);\n routeRequests.delete(raw.requestId);\n request.resolve(raw.routes);\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({\n message: '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({\n message: '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 { HttpCode } from '@/common/consts.js';\n\n/**\n * Base class for all HTTP exceptions\n */\nexport abstract class HttpException extends Error implements NodeJS.ErrnoException {\n errno?: number | undefined;\n code?: string | undefined;\n\n constructor(message: string, statusCode: HttpCode, code: string) {\n super(message);\n this.name = 'HttpException';\n this.errno = statusCode;\n this.code = code;\n }\n}\n\n// 4xx Client Error Exceptions\n\n/**\n * 400 Bad Request - Malformed or invalid request\n */\nexport class BadRequestException extends HttpException {\n constructor(message: string = 'Bad Request') {\n super(message, HttpCode.BadRequest, 'BAD_REQUEST');\n }\n}\n\n/**\n * 401 Unauthorized - Authentication required or failed\n */\nexport class UnauthorizedException extends HttpException {\n constructor(message: string = 'Unauthorized') {\n super(message, HttpCode.Unauthorized, 'UNAUTHORIZED');\n }\n}\n\n/**\n * 403 Forbidden - Valid request but refused authorization\n */\nexport class ForbiddenException extends HttpException {\n constructor(message: string = 'Forbidden') {\n super(message, HttpCode.Forbidden, 'FORBIDDEN');\n }\n}\n\n/**\n * 404 Not Found - Resource does not exist\n */\nexport class NotFoundException extends HttpException {\n constructor(message: string = 'Not Found') {\n super(message, HttpCode.NotFound, 'NOT_FOUND');\n }\n}\n\n/**\n * 405 Method Not Allowed - HTTP method not supported for resource\n */\nexport class MethodNotAllowedException extends HttpException {\n constructor(message: string = 'Method Not Allowed') {\n super(message, HttpCode.MethodNotAllowed, 'METHOD_NOT_ALLOWED');\n }\n}\n\n/**\n * 406 Not Acceptable - Cannot generate acceptable response\n */\nexport class NotAcceptableException extends HttpException {\n constructor(message: string = 'Not Acceptable') {\n super(message, HttpCode.NotAcceptable, 'NOT_ACCEPTABLE');\n }\n}\n\n/**\n * 408 Request Timeout - Client did not produce request within time\n */\nexport class RequestTimeoutException extends HttpException {\n constructor(message: string = 'Request Timeout') {\n super(message, HttpCode.RequestTimeout, 'REQUEST_TIMEOUT');\n }\n}\n\n/**\n * 409 Conflict - Request conflicts with current state\n */\nexport class ConflictException extends HttpException {\n constructor(message: string = 'Conflict') {\n super(message, HttpCode.Conflict, 'CONFLICT');\n }\n}\n\n/**\n * 410 Gone - Resource no longer available\n */\nexport class GoneException extends HttpException {\n constructor(message: string = 'Gone') {\n super(message, HttpCode.Gone, 'GONE');\n }\n}\n\n/**\n * 413 Payload Too Large - Request entity larger than limits\n */\nexport class PayloadTooLargeException extends HttpException {\n constructor(message: string = 'Payload Too Large') {\n super(message, HttpCode.PayloadTooLarge, 'PAYLOAD_TOO_LARGE');\n }\n}\n\n/**\n * 415 Unsupported Media Type - Requested format not supported\n */\nexport class UnsupportedMediaTypeException extends HttpException {\n constructor(message: string = 'Unsupported Media Type') {\n super(message, HttpCode.UnsupportedMediaType, 'UNSUPPORTED_MEDIA_TYPE');\n }\n}\n\n/**\n * 422 Unprocessable Entity - Syntactically correct but semantically erroneous\n */\nexport class UnprocessableEntityException extends HttpException {\n constructor(message: string = 'Unprocessable Entity') {\n super(message, HttpCode.UnprocessableEntity, 'UNPROCESSABLE_ENTITY');\n }\n}\n\n/**\n * 429 Too Many Requests - Rate limit exceeded\n */\nexport class TooManyRequestsException extends HttpException {\n constructor(message: string = 'Too Many Requests') {\n super(message, HttpCode.TooManyRequests, 'TOO_MANY_REQUESTS');\n }\n}\n\n// 5xx Server Error Exceptions\n\n/**\n * 500 Internal Server Error - Unexpected server condition\n */\nexport class InternalServerErrorException extends HttpException {\n constructor(message: string = 'Internal Server Error') {\n super(message, HttpCode.InternalServerError, 'INTERNAL_SERVER_ERROR');\n }\n}\n\n/**\n * 501 Not Implemented - Server does not support functionality\n */\nexport class NotImplementedException extends HttpException {\n constructor(message: string = 'Not Implemented') {\n super(message, HttpCode.NotImplemented, 'NOT_IMPLEMENTED');\n }\n}\n\n/**\n * 502 Bad Gateway - Invalid response from upstream server\n */\nexport class BadGatewayException extends HttpException {\n constructor(message: string = 'Bad Gateway') {\n super(message, HttpCode.BadGateway, 'BAD_GATEWAY');\n }\n}\n\n/**\n * 503 Service Unavailable - Server temporarily unavailable\n */\nexport class ServiceUnavailableException extends HttpException {\n constructor(message: string = 'Service Unavailable') {\n super(message, HttpCode.ServiceUnavailable, 'SERVICE_UNAVAILABLE');\n }\n}\n\n/**\n * 504 Gateway Timeout - Upstream server timeout\n */\nexport class GatewayTimeoutException extends HttpException {\n constructor(message: string = 'Gateway Timeout') {\n super(message, HttpCode.GatewayTimeout, 'GATEWAY_TIMEOUT');\n }\n}\n","import type http from 'node:http';\n\nimport { isTextualContentType } from './headers.js';\nimport { parseQuery } from './query.js';\nimport { PayloadTooLargeException } from './exceptions.js';\n\nexport interface BodyPreview {\n exists: boolean;\n value?: string;\n bytes: number;\n truncated: boolean;\n}\n\nfunction createPayloadTooLargeException(receivedBytes: number, maxBytes: number): PayloadTooLargeException {\n return new PayloadTooLargeException(\n `request body too large: ${receivedBytes.toString()} bytes exceeds ${maxBytes.toString()} bytes`,\n );\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 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 // Check content-length upfront, but still drain the request\n if (Number.isFinite(declaredBytes) && declaredBytes > maxBytes) {\n cleanup();\n req.resume();\n settle(() => reject(createPayloadTooLargeException(declaredBytes, maxBytes)));\n return;\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(() => reject(createPayloadTooLargeException(totalBytes, maxBytes)));\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(() => reject(error));\n };\n\n const onAborted = (): void => {\n cleanup();\n settle(() => reject(new Error('request aborted while reading body')));\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, FluxionModuleContext, NormalizedRequest } from '../types.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';\nimport { HttpException } from '@/http/exceptions.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): Promise<http.Server | https.Server> {\n const moduleCx: FluxionModuleContext = Object.freeze({ logger: cx.logger });\n\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 meta: {},\n };\n\n let bodyPreview: BodyPreview = {\n exists: false,\n bytes: 0,\n truncated: false,\n };\n\n cx.logger.info({ message: 'request', method, ip, path: url.pathname });\n\n const start = performance.now();\n res.once('finish', () => {\n const o: Record<string, unknown> = {\n workerId: process.env.WORKER_ID ?? '[primary]',\n message: 'response',\n method,\n ip,\n path: url.pathname,\n status: res.statusCode,\n duration: (performance.now() - start).toFixed(4),\n };\n\n if (Object.keys(normalized.query).length > 0) {\n o.query = normalized.query;\n }\n\n if (bodyPreview.exists) {\n o.body = bodyPreview.value;\n o.bodyBytes = bodyPreview.bytes;\n o.bodyTruncated = bodyPreview.truncated;\n }\n\n cx.logger.info(o);\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, moduleCx, req, res),\n cx.options.middlewareTimeoutMs,\n MIDDLEWARE_TIMEOUT_FLAG,\n );\n\n if (result === MIDDLEWARE_TIMEOUT_FLAG) {\n cx.logger.warn({\n message: '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(\n PromiseTry(m.handler, normalized, moduleCx, req, res),\n timeoutMs,\n HANDLER_TIMEOUT_FLAG,\n );\n\n if (result === HANDLER_TIMEOUT_FLAG) {\n cx.logger.warn({ message: 'HandlerTimeout', method: normalized.method, ip: normalized.ip });\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 (e) {\n if (e instanceof HttpException) {\n cx.logger.error({\n message: 'RequestFailed',\n method: normalized.method,\n ip: normalized.ip,\n path: normalized.url.pathname,\n error: e.message,\n });\n safeSendJson(res, { message: e.message }, e.errno);\n } else {\n cx.logger.error({\n message: 'RequestFailed',\n method: normalized.method,\n ip: normalized.ip,\n path: normalized.url.pathname,\n error: getErrorMessage(e),\n });\n safeSendJson(\n res,\n { message: getErrorMessage(e) },\n (e as NodeJS.ErrnoException).errno ?? HttpCode.InternalServerError,\n );\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 return new Promise((resolve, reject) => {\n let listening = false;\n\n server.on('close', () => {\n cx.logger.info({\n message: 'ServerClosed',\n host: cx.options.host,\n port: cx.options.port,\n });\n });\n\n server.once('listening', () => {\n listening = true;\n cx.logger.info({\n message: '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({\n message: 'DynamicDirectory',\n directory: cx.options.dir,\n });\n resolve(server);\n });\n\n server.on('error', (error) => {\n cx.logger.error({\n message: 'ServerError',\n error: getErrorMessage(error),\n });\n if (listening) {\n process.exit(1);\n }\n reject(error);\n });\n\n server.listen(cx.options.port, cx.options.host);\n });\n}\n","import type { PrimaryMessage } from './types.js';\nimport type { FluxionContext } from '../types.js';\nimport type http from 'node:http';\nimport type https from 'node:https';\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 if (raw.type === PrimaryAction.Routes) {\n sendToPrimary({\n type: WorkerAction.Routes,\n pid: process.pid,\n requestId: raw.requestId,\n routes: cx.router.getRoutes(),\n });\n }\n });\n\n sendToPrimary({ type: WorkerAction.Created, pid: process.pid });\n startStatsReporter();\n\n // # Start creation\n let server: http.Server | https.Server | undefined;\n let exiting = false;\n\n const shutdown = (signal: NodeJS.Signals) => {\n if (exiting) {\n return;\n }\n exiting = true;\n cx.logger.warn({ message: 'WorkerShuttingDown', pid: process.pid, signal });\n cx.watcher.stop();\n\n if (!server) {\n process.exit(0);\n }\n\n const timer = setTimeout(() => process.exit(1), 10_000);\n timer.unref();\n server.close((error) => {\n if (error) {\n cx.logger.error({ message: 'WorkerShutdownFailed', pid: process.pid, error: getErrorMessage(error) });\n process.exit(1);\n }\n process.exit(0);\n });\n };\n\n process.once('SIGINT', shutdown);\n process.once('SIGTERM', shutdown);\n\n createWorkerServer(cx)\n .then((s) => {\n server = s;\n sendToPrimary({ type: WorkerAction.Ready, pid: process.pid });\n })\n .catch((e) => {\n cx.logger.error({\n message: 'WorkerBootstrapFailed',\n pid: process.pid,\n error: getErrorMessage(e),\n });\n cx.watcher.stop();\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","function n(n) {}\n\nconst t = n;\n\nfunction o() {\n return n => {};\n}\n\nexport { o as createNarrower, n as narrow, t as static_cast };\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.default)) {\n m = m.default;\n } else if (isFluxionModule(cx, m)) {\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, FluxionRouteMeta } 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 { 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, _cx, 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\n const cleanup = () => {\n stream.off('error', onError);\n stream.off('end', onEnd);\n res.off('close', onClientClose);\n req.off('aborted', onClientClose);\n };\n\n const onError = (error: Error) => {\n cleanup();\n reject(error);\n };\n\n const onEnd = () => {\n cleanup();\n resolve(STATIC_HANDLED_FLAG);\n };\n\n const onClientClose = () => {\n cleanup();\n stream.destroy();\n resolve(STATIC_HANDLED_FLAG);\n };\n\n stream.on('error', onError);\n stream.on('end', onEnd);\n res.on('close', onClientClose);\n req.on('aborted', onClientClose);\n\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 this.cx.logger.info({ action: 'Delete', url: 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({ action: 'Skip', url: 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({ action: 'Exclude', url: 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({ action: 'RegisterApi', url: relativePath });\n return;\n }\n\n // register as static resource\n this.handlers.set(relativePath, this.makeStaticResource(absolutePath));\n this.cx.logger.info({ action: 'RegisterStatic', url: 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 getRoutes(): FluxionRouteMeta[] {\n return [...this.handlers.entries()]\n .map(\n ([relativePath, m]): FluxionRouteMeta => ({\n path: '/' + relativePath,\n type: m.type === FluxionModuleType.Api ? 'api' : 'static',\n methods: m.methods ? [...m.methods] : null,\n }),\n )\n .sort((a, b) => a.path.localeCompare(b.path));\n }\n}\n","import type { FluxionContext, FluxionOptions, NormalizedFluxionOptions } from './types.js';\nimport cluster from 'node:cluster';\n\nimport { createLogger, createWorkerLogger } from './common/logger.js';\nimport { OPTIONS_NORMALIZED_FLAG } from './common/consts.js';\nimport { defineFluxionOptions } from './defines/options.js';\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 | NormalizedFluxionOptions) {\n const alreadyNormalized = (options as NormalizedFluxionOptions).normalizedFlag === OPTIONS_NORMALIZED_FLAG;\n const context = { options: alreadyNormalized ? options : defineFluxionOptions(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"],"x_google_ignoreList":[23],"mappings":"8VAAA,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,MAAM,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,ECrBA,MAAM,EAAiB,GAA2B,CAChD,GAAI,CACF,OAAO,EAAU,CAAK,CACxB,MAAQ,CACN,MAAO,kBACT,CACF,EAEM,EAA0C,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,QAAS,EAAY,MAAK,GAAG,GAAW,EAEpF,EAAY,GAAG,EAAK,UAAU,GAAG,EAAa,GAAG,EAAK,QACtD,EAAQ,EAAc,IAAa,EACnC,EAAU,IAAQ,IAAA,GAAY,GAAK,KAAK,EAAI,GAC5C,EAAa,OAAO,KAAK,CAAM,CAAC,CAAC,OAAS,EAAI,IAAI,EAAK,MAAM,EAAc,CAAM,IAAI,EAAK,QAAU,GAG1G,QAAQ,IAAI,GAAG,EAAU,GAAG,IAAQ,EAAQ,GAAG,IAAa,GAAY,CAC1E,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,EAAa,EAAoD,CAC/E,IAAM,EAAO,EAAkB,CAAE,EA2CjC,MAAO,CAxCL,MAAM,EAAiB,EAA0B,CAC/C,IAAM,EACJ,OAAO,GAAM,SACT,CACE,QAAS,EACT,UAAW,EAAI,EACf,OACF,EACA,CACE,GAAG,EACH,UAAW,EAAI,EACf,OACF,EAEN,GAAI,CACF,EAAK,CAAK,CACZ,MAAQ,CAER,CACF,EACA,KAAK,EAA+C,CAClD,KAAK,MAAM,OAAQ,CAAe,CACpC,EACA,KAAK,EAA+C,CAClD,KAAK,MAAM,OAAQ,CAAe,CACpC,EACA,MAAM,EAA+C,CACnD,KAAK,MAAM,QAAS,CAAe,CACrC,EACA,KAAK,EAA+C,CAClD,KAAK,MAAM,OAAQ,CAAe,CACpC,EACA,MAAM,EAA+C,CACnD,KAAK,MAAM,QAAS,CAAe,CACrC,EACA,QAAQ,EAA+C,CACrD,KAAK,MAAM,UAAW,CAAe,CACvC,CAGU,CACd,CAKA,SAAgB,EAAmB,EAA2B,EAA4B,CACxF,MAAO,CACL,MAAM,EAAiB,EAA+C,CACpE,EAAW,MACT,EACA,OAAO,GAAoB,SAAW,CAAE,QAAS,EAAiB,KAAI,EAAI,CAAE,GAAG,EAAiB,KAAI,CACtG,CACF,EACA,KAAK,EAA+C,CAClD,KAAK,MAAM,OAAQ,CAAe,CACpC,EACA,KAAK,EAA+C,CAClD,KAAK,MAAM,OAAQ,CAAe,CACpC,EACA,MAAM,EAA+C,CACnD,KAAK,MAAM,QAAS,CAAe,CACrC,EACA,KAAK,EAA+C,CAClD,KAAK,MAAM,OAAQ,CAAe,CACpC,EACA,MAAM,EAA+C,CACnD,KAAK,MAAM,QAAS,CAAe,CACrC,EACA,QAAQ,EAA+C,CACrD,KAAK,MAAM,UAAW,CAAe,CACvC,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,ECrKhD,EAA0B,OAAO,8BAA8B,EAC/D,EAAsB,OAAO,8BAA8B,EAC3D,EAAuB,OAAO,wBAAwB,EACtD,EAA0B,OAAO,2BAA2B,EAE5D,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,EAEA,IAAY,GAAL,SAAA,EAAA,OAEL,GAAA,EAAA,GAAA,KAAA,KACA,EAAA,EAAA,QAAA,KAAA,UACA,EAAA,EAAA,SAAA,KAAA,WACA,EAAA,EAAA,UAAA,KAAA,YACA,EAAA,EAAA,eAAA,KAAA,iBAGA,EAAA,EAAA,iBAAA,KAAA,mBACA,EAAA,EAAA,MAAA,KAAA,QACA,EAAA,EAAA,YAAA,KAAA,cACA,EAAA,EAAA,kBAAA,KAAA,oBACA,EAAA,EAAA,kBAAA,KAAA,oBAGA,EAAA,EAAA,WAAA,KAAA,aACA,EAAA,EAAA,aAAA,KAAA,eACA,EAAA,EAAA,UAAA,KAAA,YACA,EAAA,EAAA,SAAA,KAAA,WACA,EAAA,EAAA,iBAAA,KAAA,mBACA,EAAA,EAAA,cAAA,KAAA,gBACA,EAAA,EAAA,eAAA,KAAA,iBACA,EAAA,EAAA,SAAA,KAAA,WACA,EAAA,EAAA,KAAA,KAAA,OACA,EAAA,EAAA,gBAAA,KAAA,kBACA,EAAA,EAAA,qBAAA,KAAA,uBACA,EAAA,EAAA,oBAAA,KAAA,sBACA,EAAA,EAAA,gBAAA,KAAA,kBAGA,EAAA,EAAA,oBAAA,KAAA,sBACA,EAAA,EAAA,eAAA,KAAA,iBACA,EAAA,EAAA,WAAA,KAAA,aACA,EAAA,EAAA,mBAAA,KAAA,qBACA,EAAA,EAAA,eAAA,KAAA,kBACF,EAAA,CAAA,CAAA,ECjDA,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,EAAK,WAAW,CAAO,EAAI,EAAU,EAAK,KAAK,EAAW,CAAO,EAClF,GAAI,EAAG,WAAW,CAAQ,EACxB,OAAO,EAAG,aAAa,CAAQ,CAEnC,CACA,OAAO,OAAO,KAAK,CAAO,CAC5B,CACA,MAAA,MAAA,gEAAuD,CACzD,CAKA,SAAS,GACP,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,EAAqB,EAA6C,CAChF,GAAI,OAAO,GAAM,WAAY,GAAc,MAAM,QAAQ,CAAC,EACxD,MAAA,MAAA,kDAAyC,EAG3C,GAAM,CACJ,IAAK,EACL,OACA,OACA,mBAAmB,IACnB,sBAAsB,IACtB,0BAA0B,GAAK,IAC/B,WAAW,EAAO,EAClB,UAAW,EAAe,QAAQ,IAAI,EACtC,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,GAChB,cACE,EAEE,EAAS,EAAE,QAAU,WAC3B,GAAI,IAAW,YAAc,IAAW,aAAe,OAAO,GAAW,WACvE,MAAA,MAAA,oGAA2F,EAG7F,GAAI,OAAO,GAAW,SACpB,MAAA,MAAA,qDAA4C,EAE9C,IAAM,EAAM,EAAK,QAAQ,CAAM,EAE/B,GAAI,OAAO,GAAiB,SAC1B,MAAA,MAAA,2DAAkD,EAEpD,IAAM,EAAY,EAAK,QAAQ,CAAY,EAE3C,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,EAGpE,GACE,IAAe,IAAA,KACd,OAAO,GAAe,UACrB,EAAW,OAAS,IACpB,KAAK,KAAK,CAAU,GACpB,CAAC,WAAW,KAAK,CAAU,GAC3B,CAAC,KAAK,KAAK,CAAU,GAEvB,MAAA,MAAA,oJAEA,EAOF,OAJK,EAAG,WAAW,CAAG,GACpB,EAAG,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,aACA,MAAO,GAAsB,EAAO,CAAS,EAE7C,eAAgB,CAClB,CACF,CC3LA,MAAa,EAAoB,GAC/B,CAAA,IAAA,GAAyC,CAAC,CAAC,SAAS,GAAG,IAAI,EAEhD,EAAmB,GAC9B,oBAAqG,CAAC,CAAC,SACrG,GAAG,IACL,EC7CW,EAAiB,GAA2B,QAAQ,OAAO,CAAO,EAElE,GAAgB,EAAwB,IAA4B,EAAO,KAAK,CAAO,ECFpG,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,GACd,EACA,EACA,EACa,CACb,IAAM,EAAS,EAAK,aAAa,MAAO,EAAK,IAAQ,CACnD,IAAM,EAAS,EAAI,QAAU,MAEzB,EACJ,GAAI,CACF,EAAM,IAAI,IAAI,EAAI,KAAO,IAAK,sBAAsB,CACtD,MAAQ,CACN,EAAS,EAAK,CAAE,QAAS,0BAA2B,EAAA,GAAsB,EAC1E,MACF,CAEA,GAAI,IAAW,OAAS,EAAI,WAAA,oBAAuC,CACjE,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,EAAI,WAAA,oBAAuC,CACjE,EAAS,EAAK,CACZ,GAAI,GACJ,IAAK,KAAK,IAAI,EACd,QAAS,EAAmB,CAC9B,CAAC,EACD,MACF,CAEA,GAAI,IAAW,OAAS,EAAI,WAAA,mBAAsC,CAChE,GAAI,CAAC,EAAG,QAAQ,WAAY,CAC1B,EAAS,EAAK,CAAE,QAAS,WAAY,EAAA,GAAoB,EACzD,MACF,CAEA,GAAI,EAAI,aAAa,IAAI,QAAQ,IAAM,EAAG,QAAQ,WAAY,CAC5D,EAAS,EAAK,CAAE,QAAS,WAAY,EAAA,GAAqB,EAC1D,MACF,CAEA,IAAM,EAAS,MAAM,EAAkB,EACvC,EAAS,EAAK,CAAE,GAAI,GAAM,IAAK,KAAK,IAAI,EAAG,QAAO,CAAC,EACnD,MACF,CAEA,EAAS,EAAK,CAAE,QAAS,WAAY,EAAA,GAAoB,CAC3D,CAAC,EAwBD,OAtBA,EAAO,GAAG,gBAAmB,CAC3B,EAAG,OAAO,KAAK,CACb,QAAS,iBACT,IAAK,QAAQ,IACb,KAAM,EAAG,QAAQ,KACjB,KAAM,EAAG,QAAQ,SACjB,OAAQ,WACV,CAAC,CACH,CAAC,EAED,EAAO,GAAG,QAAU,GAAiC,CACnD,EAAG,OAAO,MAAM,CACd,QAAS,eACT,KAAM,EAAG,QAAQ,KACjB,KAAM,EAAG,QAAQ,SACjB,KAAM,EAAM,KACZ,MAAO,EAAgB,CAAK,CAC9B,CAAC,EACD,QAAQ,KAAK,CAAC,CAChB,CAAC,EAED,EAAO,OAAO,EAAG,QAAQ,SAAU,EAAG,QAAQ,IAAI,EAC3C,CACT,CC/CA,IAAa,GAAb,KAAoC,CAClC,iBACA,QACA,WAEA,aAAc,CACZ,KAAK,QAAU,EAAG,QAAQ,EAC1B,KAAK,WAAa,EAAK,KAAK,KAAK,QAAS,UAAU,EACpD,KAAK,iBAAmB,EAAK,KAAK,KAAK,WAAY,eAAe,EAGlE,KAAK,iBAAiB,CACxB,CAKA,kBAAiC,CAC1B,EAAG,WAAW,KAAK,UAAU,GAChC,EAAG,UAAU,KAAK,WAAY,CAAE,UAAW,EAAK,CAAC,CAErD,CAOA,oBAA4B,EAA4B,CACtD,GAAI,CACF,GAAI,CAAC,EAAG,WAAW,CAAU,EAC3B,MAAO,GAET,IAAM,EAAgB,EAAG,aAAa,EAAY,OAAO,EACzD,OAAO,EAAO,WAAW,QAAQ,CAAC,CAAC,OAAO,CAAa,CAAC,CAAC,OAAO,KAAK,CACvE,MAAQ,CACN,MAAO,EACT,CACF,CAMA,eAAiD,CAC/C,GAAI,CACF,GAAI,CAAC,EAAG,WAAW,KAAK,gBAAgB,EACtC,MAAO,CAAC,EAEV,IAAM,EAAU,EAAG,aAAa,KAAK,iBAAkB,OAAO,EAE9D,OAD2B,KAAK,MAAM,CAC5B,CAAC,CAAC,WAAa,CAAC,CAC5B,MAAQ,CACN,MAAO,CAAC,CACV,CACF,CAMA,eAAuB,EAA0C,CAC/D,GAAI,CACF,IAAM,EAAqB,CAAE,WAAU,EACvC,EAAG,cAAc,KAAK,iBAAkB,KAAK,UAAU,EAAM,KAAM,CAAC,EAAG,OAAO,CAChF,OAAS,EAAO,CACd,QAAQ,MAAM,iCAAkC,CAAK,CACvD,CACF,CAOA,qBAA6B,EAA6D,CACxF,OAAO,EAAU,OAAQ,GAAa,CACpC,GAAI,CAGF,OADA,QAAQ,KAAK,EAAS,IAAK,CAAC,EACrB,EACT,MAAQ,CAEN,MAAO,EACT,CACF,CAAC,CACH,CAQA,sBAA8B,EAAoB,EAA4C,CAC5F,IAAM,EAAY,KAAK,cAAc,EAQrC,OAPyB,KAAK,qBAAqB,CAGlB,CAAC,CAAC,KAChC,GAAa,EAAS,aAAe,GAAc,EAAS,OAAS,CAGzD,GAAK,IACtB,CAOA,YAAoB,EAAsB,CACxC,GAAI,CAEF,OADA,QAAQ,KAAK,EAAK,SAAS,EACpB,EACT,OAAS,EAAO,CAEd,OADA,QAAQ,MAAM,0BAA0B,EAAI,GAAI,CAAK,EAC9C,EACT,CACF,CASA,iBAAiB,EAAoB,EAAc,EAAc,EAAwB,CACvF,IAAM,EAAa,KAAK,oBAAoB,CAAU,EAChD,EAAa,QAAQ,IACrB,EAAM,QAAQ,IAAI,EAGlB,EAAY,KAAK,sBAAsB,EAAY,CAAI,EAC7D,GAAI,EAAW,CACb,QAAQ,KACN,kFAAkF,EAAU,IAAI,SAAS,EAAU,MACrH,EAGI,KAAK,YAAY,EAAU,GAAG,GAChC,QAAQ,KAAK,+CAA+C,EAAU,KAAK,EAK7E,IAAM,EADY,KAAK,cACE,CAAC,CAAC,OAAQ,GAAa,EAAS,MAAQ,EAAU,GAAG,EAC9E,KAAK,eAAe,CAAQ,CAC9B,CAGA,IAAI,EAAY,KAAK,cAAc,EACnC,EAAY,KAAK,qBAAqB,CAAS,EAG/C,EAAY,EAAU,OAAQ,GAAa,EAAS,MAAQ,CAAU,EAGtE,IAAM,EAAmC,CACvC,UAAW,KAAK,IAAI,EACpB,IAAK,EACL,aACA,OACA,OACA,WACA,MACA,YACF,EACA,EAAU,KAAK,CAAS,EAGxB,KAAK,eAAe,CAAS,EAE7B,QAAQ,KACN,qDAAqD,EAAW,SAAS,EAAK,SAAS,EAAW,MAAM,EAAG,CAAC,EAAE,IAChH,CACF,CAKA,oBAA2B,CACzB,IAAM,EAAa,QAAQ,IACrB,EAAY,KAAK,cAAc,EAC/B,EAAW,EAAU,OAAQ,GAAa,EAAS,MAAQ,CAAU,EAEvE,EAAS,SAAW,EAAU,SAChC,KAAK,eAAe,CAAQ,EAC5B,QAAQ,KAAK,uDAAuD,GAAY,EAEpF,CAMA,qBAA+C,CAC7C,IAAM,EAAY,KAAK,cAAc,EACrC,OAAO,KAAK,qBAAqB,CAAS,CAC5C,CAKA,gBAAuB,CACrB,IAAM,EAAY,KAAK,oBAAoB,EAC3C,QAAQ,KAAK,6CAA6C,EAC1D,IAAK,IAAM,KAAY,EACrB,QAAQ,KACN,YAAY,EAAS,IAAI,UAAU,EAAS,KAAK,UAAU,EAAS,WAAW,MAAM,EAAG,CAAC,EAAE,cAAc,IAAI,KAAK,EAAS,SAAS,CAAC,CAAC,YAAY,GACpJ,CAEJ,CACF,EAGA,IAAI,EAAiD,KAMrD,SAAgB,GAA6C,CAI3D,MAHA,CACE,IAAkB,IAAI,GAEjB,CACT,CASA,SAAgB,GAAsB,EAAoB,EAAc,EAAc,EAAwB,CAE5G,EAAM,CAAC,CAAC,iBAAiB,EAAY,EAAM,EAAM,CAAQ,CAC3D,CAKA,SAAgB,GAA+B,CAE7C,EAAM,CAAC,CAAC,mBAAmB,CAC7B,CCnRA,MAAM,EAAa,GAAkB,QAAQ,EAAQ,KAAO,KAAA,CAAM,QAAQ,CAAC,CAAC,EAStE,EAAoB,IAG1B,SAAgB,GAAY,EAA2D,CACrF,GAAI,CAAC,EAAQ,UACX,MAAA,MAAA,wEAA+D,EAKjE,GADmB,EAAK,KAAK,EAAG,QAAQ,WAAa,QAAQ,IAAI,EAAG,mBACrC,EAAG,EAAG,QAAQ,KAAM,EAAG,QAAQ,KAAM,EAAG,QAAQ,QAAQ,EAGvF,QAAQ,GAAG,OAAQ,CAAsB,EACzC,QAAQ,GAAG,aAAgB,CACzB,EAAuB,EACvB,QAAQ,KAAK,CAAC,CAChB,CAAC,EACD,QAAQ,GAAG,cAAiB,CAC1B,EAAuB,EACvB,QAAQ,KAAK,CAAC,CAChB,CAAC,EAED,GAAM,CAAE,iBAAkB,EAAG,QACvB,EAAc,EAAc,YAC5B,EAAW,KAAK,IAAI,EAAG,EAAG,KAAK,CAAC,CAAC,MAAM,EACvC,EAAc,KAAK,IAAI,EAAG,KAAK,IAAI,EAAc,gBAAkB,KAAK,IAAI,EAAG,CAAQ,EAAG,CAAQ,CAAC,EAEzG,EAAG,OAAO,KAAK,CACb,QAAS,iBACT,IAAK,QAAQ,IACb,QAAS,EACT,KAAM,EAAG,QAAQ,KACjB,KAAM,EAAG,QAAQ,KACjB,SAAU,EAAG,QAAQ,QACvB,CAAC,EAED,IAAM,EAAU,IAAI,IACd,EAAgB,IAAI,IACtB,EAAiB,EAKf,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,GAA2B,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,OAGuD,CACvD,IAAM,EAAS,MAAM,KAAK,EAAQ,OAAO,CAAC,CAAC,CAAC,KAAM,GAAS,EAAK,QAAU,SAAW,EAAK,SAAS,YAAY,CAAC,EAKhH,OAJK,EAIE,IAAI,QAAS,GAAY,CAC9B,IAAM,EAAY,EAAE,EACd,EAAQ,eAAiB,CAC7B,EAAc,OAAO,CAAS,EAC9B,EAAQ,CAAC,CAAC,CACZ,EAAG,GAAI,EACP,EAAM,MAAM,EACZ,EAAc,IAAI,EAAW,CAAE,UAAS,OAAM,CAAC,EAC/C,GAAI,CACF,EAAa,EAAO,SAAU,CAAE,KAAA,IAA4B,WAAU,CAAC,CACzE,MAAQ,CACN,aAAa,CAAK,EAClB,EAAc,OAAO,CAAS,EAC9B,EAAQ,CAAC,CAAC,CACZ,CACF,CAAC,EAlBQ,QAAQ,QAAQ,CAAC,CAAC,CAmB7B,CAAC,EAMD,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,CACb,QAAS,0BACT,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,CACb,QAAS,kBACT,KAAM,EAAK,KACX,IAAK,EAAK,IACV,QACF,CAAC,EACD,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,EAAQ,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,CACb,QAAS,cACT,SAAU,EAAO,GACjB,OACA,IAAK,EAAI,GACX,CAAC,EACD,MACF,CAEA,GAAI,EAAI,OAAA,IAA+B,CACrC,EAAW,MAAQ,UACnB,EAAW,IAAM,EAAI,IACrB,EAAG,OAAO,KAAK,CACb,QAAS,gBACT,SAAU,EAAO,GACjB,OACA,IAAK,EAAI,GACX,CAAC,EACD,MACF,CAEA,GAAI,EAAI,OAAA,IAA6B,CACnC,EAAW,IAAM,EAAI,IACrB,EAAW,UAAY,EAAI,MACvB,EAAW,QAAU,SACvB,EAA2B,EAAY,EAAI,KAAK,EAElD,MACF,CAEA,GAAI,EAAI,OAAA,IAA8B,CACpC,IAAM,EAAU,EAAc,IAAI,EAAI,SAAS,EAC3C,IACF,aAAa,EAAQ,KAAK,EAC1B,EAAc,OAAO,EAAI,SAAS,EAClC,EAAQ,QAAQ,EAAI,MAAM,EAE9B,CA3CA,CA4CF,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,CACb,QAAS,eACT,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,CACd,QAAS,0BACT,KAAM,EACN,SAAU,EACV,IAAK,CACP,CAAC,EACD,MACF,CACA,EAAU,CAAU,CAbpB,CAcF,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,CCpVA,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,GAAU,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,CCfA,IAAsB,EAAtB,cAA4C,KAAuC,CACjF,MACA,KAEA,YAAY,EAAiB,EAAsB,EAAc,CAC/D,MAAM,CAAO,EACb,KAAK,KAAO,gBACZ,KAAK,MAAQ,EACb,KAAK,KAAO,CACd,CACF,EAOa,EAAb,cAAyC,CAAc,CACrD,YAAY,EAAkB,cAAe,CAC3C,MAAM,EAAA,IAA8B,aAAa,CACnD,CACF,EAKa,EAAb,cAA2C,CAAc,CACvD,YAAY,EAAkB,eAAgB,CAC5C,MAAM,EAAA,IAAgC,cAAc,CACtD,CACF,EAKa,GAAb,cAAwC,CAAc,CACpD,YAAY,EAAkB,YAAa,CACzC,MAAM,EAAA,IAA6B,WAAW,CAChD,CACF,EAKa,GAAb,cAAuC,CAAc,CACnD,YAAY,EAAkB,YAAa,CACzC,MAAM,EAAA,IAA4B,WAAW,CAC/C,CACF,EAKa,GAAb,cAA+C,CAAc,CAC3D,YAAY,EAAkB,qBAAsB,CAClD,MAAM,EAAA,IAAoC,oBAAoB,CAChE,CACF,EAKa,GAAb,cAA4C,CAAc,CACxD,YAAY,EAAkB,iBAAkB,CAC9C,MAAM,EAAA,IAAiC,gBAAgB,CACzD,CACF,EAKa,GAAb,cAA6C,CAAc,CACzD,YAAY,EAAkB,kBAAmB,CAC/C,MAAM,EAAA,IAAkC,iBAAiB,CAC3D,CACF,EAKa,GAAb,cAAuC,CAAc,CACnD,YAAY,EAAkB,WAAY,CACxC,MAAM,EAAA,IAA4B,UAAU,CAC9C,CACF,EAKa,GAAb,cAAmC,CAAc,CAC/C,YAAY,EAAkB,OAAQ,CACpC,MAAM,EAAA,IAAwB,MAAM,CACtC,CACF,EAKa,EAAb,cAA8C,CAAc,CAC1D,YAAY,EAAkB,oBAAqB,CACjD,MAAM,EAAA,IAAmC,mBAAmB,CAC9D,CACF,EAKa,GAAb,cAAmD,CAAc,CAC/D,YAAY,EAAkB,yBAA0B,CACtD,MAAM,EAAA,IAAwC,wBAAwB,CACxE,CACF,EAKa,GAAb,cAAkD,CAAc,CAC9D,YAAY,EAAkB,uBAAwB,CACpD,MAAM,EAAA,IAAuC,sBAAsB,CACrE,CACF,EAKa,GAAb,cAA8C,CAAc,CAC1D,YAAY,EAAkB,oBAAqB,CACjD,MAAM,EAAA,IAAmC,mBAAmB,CAC9D,CACF,EAOa,GAAb,cAAkD,CAAc,CAC9D,YAAY,EAAkB,wBAAyB,CACrD,MAAM,EAAA,IAAuC,uBAAuB,CACtE,CACF,EAKa,GAAb,cAA6C,CAAc,CACzD,YAAY,EAAkB,kBAAmB,CAC/C,MAAM,EAAA,IAAkC,iBAAiB,CAC3D,CACF,EAKa,GAAb,cAAyC,CAAc,CACrD,YAAY,EAAkB,cAAe,CAC3C,MAAM,EAAA,IAA8B,aAAa,CACnD,CACF,EAKa,GAAb,cAAiD,CAAc,CAC7D,YAAY,EAAkB,sBAAuB,CACnD,MAAM,EAAA,IAAsC,qBAAqB,CACnE,CACF,EAKa,GAAb,cAA6C,CAAc,CACzD,YAAY,EAAkB,kBAAmB,CAC/C,MAAM,EAAA,IAAkC,iBAAiB,CAC3D,CACF,ECxKA,SAAS,EAA+B,EAAuB,EAA4C,CACzG,OAAO,IAAI,EACT,2BAA2B,EAAc,SAAS,EAAE,iBAAiB,EAAS,SAAS,EAAE,OAC3F,CACF,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,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,EAGA,GAAI,OAAO,SAAS,CAAa,GAAK,EAAgB,EAAU,CAC9D,EAAQ,EACR,EAAI,OAAO,EACX,MAAa,EAAO,EAA+B,EAAe,CAAQ,CAAC,CAAC,EAC5E,MACF,CAEA,IAAM,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,EAAO,EAA+B,EAAY,CAAQ,CAAC,CAAC,EACzE,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,EAAO,CAAK,CAAC,CAC5B,EAEM,MAAwB,CAC5B,EAAQ,EACR,MAAa,EAAW,MAAM,oCAAoC,CAAC,CAAC,CACtE,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,CC9OA,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,EAAyD,CAC1F,IAAM,EAAiC,OAAO,OAAO,CAAE,OAAQ,EAAG,MAAO,CAAC,EAEpE,EAAiB,MAAO,EAA2B,IAA6B,CACpF,IAAM,EAAS,EAAI,QAAU,MACvB,EAAK,GAAU,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,EAC5D,KAAM,CAAC,CACT,EAEI,EAA2B,CAC7B,OAAQ,GACR,MAAO,EACP,UAAW,EACb,EAEA,EAAG,OAAO,KAAK,CAAE,QAAS,UAAW,SAAQ,KAAI,KAAM,EAAI,QAAS,CAAC,EAErE,IAAM,EAAQ,YAAY,IAAI,EAC9B,EAAI,KAAK,aAAgB,CACvB,IAAM,EAA6B,CACjC,SAAU,QAAQ,IAAI,WAAa,YACnC,QAAS,WACT,SACA,KACA,KAAM,EAAI,SACV,OAAQ,EAAI,WACZ,UAAW,YAAY,IAAI,EAAI,EAAA,CAAO,QAAQ,CAAC,CACjD,EAEI,OAAO,KAAK,EAAW,KAAK,CAAC,CAAC,OAAS,IACzC,EAAE,MAAQ,EAAW,OAGnB,EAAY,SACd,EAAE,KAAO,EAAY,MACrB,EAAE,UAAY,EAAY,MAC1B,EAAE,cAAgB,EAAY,WAGhC,EAAG,OAAO,KAAK,CAAC,CAClB,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,EAAU,EAAK,CAAG,EAC3D,EAAG,QAAQ,oBACX,CACF,IAEe,EAAyB,CACtC,EAAG,OAAO,KAAK,CACb,QAAS,oBACT,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,EACnB,EAAW,EAAE,QAAS,EAAY,EAAU,EAAK,CAAG,EACpD,EACA,CACF,EAEA,GAAI,IAAW,EAAsB,CACnC,EAAG,OAAO,KAAK,CAAE,QAAS,iBAAkB,OAAQ,EAAW,OAAQ,GAAI,EAAW,EAAG,CAAC,EAC1F,EAAa,EAAK,CAAE,QAAS,mBAAoB,EAAA,GAA+B,EAChF,MACF,CAEI,IAAW,GACb,EAAa,EAAK,CAAM,CAE5B,OAAS,EAAG,CACN,aAAa,GACf,EAAG,OAAO,MAAM,CACd,QAAS,gBACT,OAAQ,EAAW,OACnB,GAAI,EAAW,GACf,KAAM,EAAW,IAAI,SACrB,MAAO,EAAE,OACX,CAAC,EACD,EAAa,EAAK,CAAE,QAAS,EAAE,OAAQ,EAAG,EAAE,KAAK,IAEjD,EAAG,OAAO,MAAM,CACd,QAAS,gBACT,OAAQ,EAAW,OACnB,GAAI,EAAW,GACf,KAAM,EAAW,IAAI,SACrB,MAAO,EAAgB,CAAC,CAC1B,CAAC,EACD,EACE,EACA,CAAE,QAAS,EAAgB,CAAC,CAAE,EAC7B,EAA4B,OAAA,GAC/B,EAEJ,CACF,EAEM,EAAS,EAAG,QAAQ,MACtB,EAAM,aACJ,CACE,IAAK,EAAG,QAAQ,MAAM,IACtB,KAAM,EAAG,QAAQ,MAAM,KACvB,GAAI,EAAG,QAAQ,MAAM,EACvB,EACA,CACF,EACA,EAAK,aAAa,CAAc,EAEpC,OAAO,IAAI,SAAS,EAAS,IAAW,CACtC,IAAI,EAAY,GAEhB,EAAO,GAAG,YAAe,CACvB,EAAG,OAAO,KAAK,CACb,QAAS,eACT,KAAM,EAAG,QAAQ,KACjB,KAAM,EAAG,QAAQ,IACnB,CAAC,CACH,CAAC,EAED,EAAO,KAAK,gBAAmB,CAC7B,EAAY,GACZ,EAAG,OAAO,KAAK,CACb,QAAS,gBACT,IAAK,QAAQ,IACb,SAAU,EAAG,QAAQ,MAAQ,QAAU,OACvC,KAAM,EAAG,QAAQ,KACjB,KAAM,EAAG,QAAQ,IACnB,CAAC,EACD,EAAG,OAAO,KAAK,CACb,QAAS,mBACT,UAAW,EAAG,QAAQ,GACxB,CAAC,EACD,EAAQ,CAAM,CAChB,CAAC,EAED,EAAO,GAAG,QAAU,GAAU,CAC5B,EAAG,OAAO,MAAM,CACd,QAAS,cACT,MAAO,EAAgB,CAAK,CAC9B,CAAC,EACG,GACF,QAAQ,KAAK,CAAC,EAEhB,EAAO,CAAK,CACd,CAAC,EAED,EAAO,OAAO,EAAG,QAAQ,KAAM,EAAG,QAAQ,IAAI,CAChD,CAAC,CACH,CC3NA,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,EAAQ,UACV,MAAA,MAAA,sEAA6D,EAG/D,QAAQ,GAAG,UAAY,GAAwB,CACxC,KAAiB,CAAG,EAIzB,IAAI,EAAI,OAAA,IAA6B,CACnC,EAAc,CAAE,KAAA,IAAyB,IAAK,QAAQ,IAAK,OAAQ,EAAI,OAAQ,WAAY,KAAK,IAAI,CAAE,CAAC,EACvG,MACF,CAEI,EAAI,OAAA,KACN,EAAc,CACZ,KAAA,IACA,IAAK,QAAQ,IACb,UAAW,EAAI,UACf,OAAQ,EAAG,OAAO,UAAU,CAC9B,CAAC,CARH,CAUF,CAAC,EAED,EAAc,CAAE,KAAA,IAA4B,IAAK,QAAQ,GAAI,CAAC,EAC9D,GAAmB,EAGnB,IAAI,EACA,EAAU,GAER,EAAY,GAA2B,CACvC,IAGJ,EAAU,GACV,EAAG,OAAO,KAAK,CAAE,QAAS,qBAAsB,IAAK,QAAQ,IAAK,QAAO,CAAC,EAC1E,EAAG,QAAQ,KAAK,EAEX,GACH,QAAQ,KAAK,CAAC,EAIhB,eAD+B,QAAQ,KAAK,CAAC,EAAG,GAC5C,CAAC,CAAC,MAAM,EACZ,EAAO,MAAO,GAAU,CAClB,IACF,EAAG,OAAO,MAAM,CAAE,QAAS,uBAAwB,IAAK,QAAQ,IAAK,MAAO,EAAgB,CAAK,CAAE,CAAC,EACpG,QAAQ,KAAK,CAAC,GAEhB,QAAQ,KAAK,CAAC,CAChB,CAAC,EACH,EAEA,QAAQ,KAAK,SAAU,CAAQ,EAC/B,QAAQ,KAAK,UAAW,CAAQ,EAEhC,GAAmB,CAAE,CAAA,CAClB,KAAM,GAAM,CACX,EAAS,EACT,EAAc,CAAE,KAAA,IAA0B,IAAK,QAAQ,GAAI,CAAC,CAC9D,CAAC,CAAA,CACA,MAAO,GAAM,CACZ,EAAG,OAAO,MAAM,CACd,QAAS,wBACT,IAAK,QAAQ,IACb,MAAO,EAAgB,CAAC,CAC1B,CAAC,EACD,EAAG,QAAQ,KAAK,EAChB,QAAQ,KAAK,CAAC,CAChB,CAAC,CACL,CCrHA,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,CAAC,EAAG,WAAW,CAAG,EAEpB,OADA,KAAK,GAAG,OAAO,KAAK,6BAA6B,GAAK,EAC/C,KAGT,IAAM,EAAqC,CAAC,EAEtC,GAAqB,EAAqB,IAAwB,CACtE,IAAM,EAAU,EAAG,YAAY,EAAa,CAAE,cAAe,EAAK,CAAC,EAEnE,IAAK,IAAI,EAAI,EAAG,EAAI,EAAQ,OAAQ,IAAK,CACvC,IAAM,EAAQ,EAAQ,GAChB,EAAe,EAAK,KAAK,EAAa,EAAM,IAAI,EAChD,EAAe,EAAK,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,EACZ,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,EAAc,EAAK,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,EAAb,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,QAAU,EACZ,MAAM,EAAK,CAAE,UAAW,EAAK,GAAI,EAAY,IAAiB,CACxD,GAKL,KAAK,QAAQ,EAAK,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,EClDA,SAAS,GAAE,EAAG,CAAC,CAEf,MAAM,GAAI,GCEV,SAAS,EAAgB,EAAgD,EAAwC,CAC/G,GAAI,OAAO,GAAM,WAAY,EAC3B,MAAO,GAKT,GAFA,GAAmC,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,OAAA,EAAe,MAAM,GACrB,IAAI,EAAA,EAAY,CAAQ,EACxB,GAAI,EAAgB,EAAI,EAAE,OAAO,EAC/B,EAAI,EAAE,aACD,GAAI,GAAgB,EAAI,CAAC,EAE9B,MAAA,MAAA,2CAAkC,EAAS,0DAA0D,EAGvG,OAAO,CACT,CCzCA,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,EAAK,EAAK,IAAQ,CAC5C,GAAI,EAAW,SAAW,OAAS,EAAW,SAAW,OAAQ,CAC/D,EAAI,WAAa,IACjB,EAAI,UAAU,QAAS,WAAW,EAClC,EAAI,IAAI,EACR,MACF,CAEA,GAAI,CAAC,EAAG,WAAW,CAAQ,EAAG,CAC5B,EAAI,WAAa,IACjB,EAAI,IAAI,WAAW,EACnB,MACF,CAEA,IAAM,EAAO,EAAG,SAAS,CAAQ,EACjC,GAAI,CAAC,EAAK,OAAO,EAAG,CAClB,EAAI,WAAa,IACjB,EAAI,IAAI,WAAW,EACnB,MACF,CAGA,IAAM,EAAc,GADF,EAAK,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,EAAS,EAAG,iBAAiB,CAAQ,EAErC,MAAgB,CACpB,EAAO,IAAI,QAAS,CAAO,EAC3B,EAAO,IAAI,MAAO,CAAK,EACvB,EAAI,IAAI,QAAS,CAAa,EAC9B,EAAI,IAAI,UAAW,CAAa,CAClC,EAEM,EAAW,GAAiB,CAChC,EAAQ,EACR,EAAO,CAAK,CACd,EAEM,MAAc,CAClB,EAAQ,EACR,EAAQ,CAAmB,CAC7B,EAEM,MAAsB,CAC1B,EAAQ,EACR,EAAO,QAAQ,EACf,EAAQ,CAAmB,CAC7B,EAEA,EAAO,GAAG,QAAS,CAAO,EAC1B,EAAO,GAAG,MAAO,CAAK,EACtB,EAAI,GAAG,QAAS,CAAa,EAC7B,EAAI,GAAG,UAAW,CAAa,EAE/B,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,CAAC,EAAG,WAAW,CAAY,EAAG,CAChC,KAAK,SAAS,OAAO,CAAY,EACjC,KAAK,GAAG,OAAO,KAAK,CAAE,OAAQ,SAAU,IAAK,CAAa,CAAC,EAC3D,MACF,CAKA,GAAI,CADmB,KAAK,GAAG,QAAQ,QAAQ,KAAM,GAAY,EAAU,EAAc,CAAO,CAC9E,EAAG,CACnB,KAAK,SAAS,OAAO,CAAY,EACjC,KAAK,GAAG,OAAO,KAAK,CAAE,OAAQ,OAAQ,IAAK,CAAa,CAAC,EACzD,MACF,CAKA,GADuB,KAAK,GAAG,QAAQ,QAAQ,KAAM,GAAY,EAAU,EAAc,CAAO,CAC/E,EAAG,CAClB,KAAK,SAAS,OAAO,CAAY,EACjC,KAAK,GAAG,OAAO,KAAK,CAAE,OAAQ,UAAW,IAAK,CAAa,CAAC,EAC5D,MACF,CAKA,GAD0B,KAAK,GAAG,QAAQ,WAAW,KAAM,GAAY,EAAU,EAAc,CAAO,CAClF,EAAG,CACrB,IAAM,EAAI,GAAkB,KAAK,GAAI,CAAY,EACjD,KAAK,SAAS,IAAI,EAAc,CAAC,EACjC,KAAK,GAAG,OAAO,KAAK,CAAE,OAAQ,cAAe,IAAK,CAAa,CAAC,EAChE,MACF,CAGA,KAAK,SAAS,IAAI,EAAc,KAAK,mBAAmB,CAAY,CAAC,EACrE,KAAK,GAAG,OAAO,KAAK,CAAE,OAAQ,iBAAkB,IAAK,CAAa,CAAC,CACrE,CAEA,UAAU,EAA6C,CACrD,IAAM,EAAe,EAAI,SAAS,QAAQ,QAAS,EAAE,CAAC,CAAC,QAAQ,QAAS,EAAE,EAC1E,OAAO,KAAK,SAAS,IAAI,CAAY,CACvC,CAEA,WAAgC,CAC9B,MAAO,CAAC,GAAG,KAAK,SAAS,QAAQ,CAAC,CAAC,CAChC,KACE,CAAC,EAAc,MAA0B,CACxC,KAAM,IAAM,EACZ,KAAM,EAAE,OAAA,EAAiC,MAAQ,SACjD,QAAS,EAAE,QAAU,CAAC,GAAG,EAAE,OAAO,EAAI,IACxC,EACF,CAAC,CACA,MAAM,EAAG,IAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC,CAChD,CACF,ECpJA,eAAsB,GAAQ,EAAoD,CAEhF,IAAM,EAAU,CAAE,QADS,EAAqC,iBAAmB,EACpC,EAAU,EAAqB,CAAO,CAAE,EAEvF,EAAQ,OAAS,EAAa,CAA0C,EACxE,EAAQ,OAAS,IAAI,GAAc,CAAqD,EAEpF,EAAQ,UACV,GAAY,CAAO,GAGnB,EAAQ,OAAS,EAAmB,EAAQ,OAAQ,QAAQ,GAAG,EAG/D,EAAQ,QAAU,MAAM,IADR,EAAQ,QAAQ,cAAgB,EAAuB,IACnC,CAAgE,CAAC,CAAC,MAAM,EAC5G,GAAW,CAAO,EAEtB"}
|