fluxion-vio 0.1.3 → 0.1.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/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +3 -3
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -5,5 +5,5 @@ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=Object.
|
|
|
5
5
|
- "staticInclude" for static resource patterns (default: ["**/*"])
|
|
6
6
|
Example migration:
|
|
7
7
|
OLD: { include: ["**/*.ts", "**/*.js"], apiInclude: ["**/*.ts"] }
|
|
8
|
-
NEW: { apiInclude: ["**/*.ts"], staticInclude: ["**/*.js"] }`);let{dir:t,host:n,port:r,handlerTimeoutMs:i=5e3,middlewareTimeoutMs:a=3e3,staticResourceTimeoutMs:o=180*1e3,target:s=`node${process.versions.node.split(`.`)[0]}`,maxRequestBytes:u=8e6,apiInclude:d=[`**/*.ts`],staticInclude:f=[`**/*`],exclude:p=[`**/node_modules/**`,`**/.git/**`,`**/dist/**`,`**/build/**`,`**/.vscode/**`,`**/.idea/**`,`**/*.log`,`**/.DS_Store`,`**/coverage/**`,`**/.nyc_output/**`,`**/*.tmp`,`**/*.temp`],https:m,csp:h=`default-src 'self'`,restartMemoryMB:g=!1,logOutputFile:_}=e,v=r,y=e.logger??`json-line`;if(y!==`json-line`&&typeof y!=`function`)throw new k(`Invalid logger option, Must be 'json-line' or a custom logger function (write a one-line logger yourself)`);if(typeof t!=`string`)throw new k(`FluxionOptions.dir must be a string`);let b=l.default.resolve(process.cwd(),t),x=b.endsWith(l.default.sep)?b:b+l.default.sep;if(typeof s!=`string`&&!(Array.isArray(s)&&s.every(e=>typeof e==`string`)))throw new k(`FluxionOptions.target must be a string or an array of strings`);if(typeof n!=`string`)throw new k(`FluxionOptions.host must be a string`);if(!Number.isSafeInteger(i)||i<=100)throw new k(`FluxionOptions.handlerTimeoutMs must be an integer greater than 100`);if(!Number.isSafeInteger(a)||a<=100)throw new k(`FluxionOptions.middlewareTimeoutMs must be an integer greater than 100`);if(typeof v!=`number`||!Number.isSafeInteger(v))throw new k(`FluxionOptions.port must be a positive integer`);if(v<=1||v>65535)throw new k(`FluxionOptions.port must be 1 ~ 65535`);if(typeof u!=`number`||u<=0||!Number.isSafeInteger(u))throw new k(`FluxionOptions.maxRequestBytes must be a positive integer`);if(h!==!1&&typeof h!=`string`)throw new k(`FluxionOptions.csp must be a string or false`);if(g!==!1&&(typeof g!=`number`||!Number.isFinite(g)||g<=0))throw new k(`FluxionOptions.restartMemoryMB must be a positive number of MB, or false`);if(_!==void 0&&(typeof _!=`string`||_.length===0))throw new k(`FluxionOptions.logOutputFile must be a non-empty string path`);let S=_===void 0?void 0:l.default.resolve(process.cwd(),_);c.default.existsSync(x)||c.default.mkdirSync(x,{recursive:!0});let C={dir:x,host:n,port:v,handlerTimeoutMs:i,middlewareTimeoutMs:a,staticResourceTimeoutMs:o,target:s,maxRequestBytes:u,logger:y,logOutputFile:S,apiInclude:d,staticInclude:f,exclude:p,https:ue(m),csp:h,restartMemoryMB:g,normalizedFlag:D.OptionsNormalized};return process.argv.includes(`--verbose`)&&console.log(`normalized options`,{...C,https:C.https?`set`:`unset`}),C}function M(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 de(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 N(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 P(e){if(e!==void 0)try{return new URL(e,`http://fluxion.local`)}catch{return}}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 L(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 R=class extends Error{errno;code;constructor(e,t,n){super(e),this.name=`HttpException`,this.errno=t,this.code=n}},z=class extends R{constructor(e=`Bad Request`){super(e,400,`BAD_REQUEST`)}},B=class extends R{constructor(e=`Unauthorized`){super(e,401,`UNAUTHORIZED`)}},V=class extends R{constructor(e=`Forbidden`){super(e,403,`FORBIDDEN`)}},H=class extends R{constructor(e=`Not Found`){super(e,404,`NOT_FOUND`)}},fe=class extends R{constructor(e=`Method Not Allowed`){super(e,405,`METHOD_NOT_ALLOWED`)}},pe=class extends R{constructor(e=`Not Acceptable`){super(e,406,`NOT_ACCEPTABLE`)}},me=class extends R{constructor(e=`Request Timeout`){super(e,408,`REQUEST_TIMEOUT`)}},he=class extends R{constructor(e=`Conflict`){super(e,409,`CONFLICT`)}},ge=class extends R{constructor(e=`Gone`){super(e,410,`GONE`)}},U=class extends R{constructor(e=`Payload Too Large`){super(e,413,`PAYLOAD_TOO_LARGE`)}},_e=class extends R{constructor(e=`Unsupported Media Type`){super(e,415,`UNSUPPORTED_MEDIA_TYPE`)}},ve=class extends R{constructor(e=`Unprocessable Entity`){super(e,422,`UNPROCESSABLE_ENTITY`)}},ye=class extends R{constructor(e=`Too Many Requests`){super(e,429,`TOO_MANY_REQUESTS`)}},be=class extends R{constructor(e=`Internal Server Error`){super(e,500,`INTERNAL_SERVER_ERROR`)}},xe=class extends R{constructor(e=`Not Implemented`){super(e,501,`NOT_IMPLEMENTED`)}},Se=class extends R{constructor(e=`Bad Gateway`){super(e,502,`BAD_GATEWAY`)}},Ce=class extends R{constructor(e=`Service Unavailable`){super(e,503,`SERVICE_UNAVAILABLE`)}},we=class extends R{constructor(e=`Gateway Timeout`){super(e,504,`GATEWAY_TIMEOUT`)}};function W(e,t){return new U(`request body too large: ${e.toString()} bytes exceeds ${t.toString()} bytes`)}function G(e){return Array.isArray(e)?e[0]:e}function K(){return{exists:!1,bytes:0,truncated:!1}}function Te(e,t,n,r){return t===0?K():N(n)?{exists:!0,value:e.toString(`utf8`),bytes:t,truncated:r}:{exists:!0,value:`<binary body: ${t} bytes>`,bytes:t,truncated:r}}async function Ee(e,t,n,r=8192){if(t===`GET`||t===`HEAD`||e.readableEnded)return{rawBody:void 0,preview:K()};let i=G(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(W(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(W(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:Te(s.length>0?Buffer.concat(s):Buffer.alloc(0),n?.byteLength??0,G(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 q(e,t,n){let{rawBody:r,preview:i}=await Ee(e,t,n);if(r===void 0||r.byteLength===0)return{body:{},preview:i};let a=G(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:L(new URLSearchParams(r.toString(`utf8`))),preview:i}:N(a)?{body:{raw:r.toString(`utf8`)},preview:i}:{body:{},preview:i}}function De(e){if(!e)return{};let t={},n=e.split(`;`),r=0;for(let e of n){if(r>=100)break;let[n,...i]=e.split(`=`);if(!n)continue;let a=n.trim(),o=i.join(`=`).trim();t[a]=decodeURIComponent(o),r++}return t}const J=(e,t,n)=>Promise.race([e,new Promise(e=>setTimeout(()=>e(n),t))]);function Oe(e,t,n){if(typeof n.disposer!=`function`)return;let r=n.disposer;t.once(`close`,()=>{M(r).catch(t=>{e.logger.error({event:`DisposerFailed`,url:n.absolutePath,error:b(t)})})})}function ke(e){let t=Object.freeze({logger:e.logger}),n=async(n,r)=>{let i=n.method??`GET`,a=de(n),o=P(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:L(o.searchParams),body:{},headers:n.headers,cookie:De(n.headers.cookie),meta:{}};r.setHeader(`X-Content-Type-Options`,`nosniff`),r.setHeader(`X-Frame-Options`,`DENY`),r.setHeader(`X-XSS-Protection`,`1; mode=block`),e.options.csp!==!1&&r.setHeader(`Content-Security-Policy`,e.options.csp);let c={exists:!1,bytes:0,truncated:!1};e.logger.core({event:`request`,method:i,ip:a,path:o.pathname});let l=performance.now();r.once(`finish`,()=>{let t={event:`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.core(t)});try{let i=await q(n,s.method,e.options.maxRequestBytes);s.body=i.body,c=i.preview;let a=await e.router(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}Oe(e,r,a);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 J(M(a.middlewares[i],s,t,n,r),e.options.middlewareTimeoutMs,D.MiddlewareTimeout)===D.MiddlewareTimeout){e.logger.warn({event:`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 J(M(a.handler,s,t,n,r),l,D.HandlerTimeout);if(u===D.HandlerTimeout){e.logger.warn({event:`HandlerTimeout`,method:s.method,ip:s.ip}),I(r,{message:`Handler timed out`},500);return}u!==D.StaticHandled&&I(r,u)}catch(t){t instanceof R?(e.logger.error({...s,event:`RequestFailed`,error:t.message}),I(r,{message:t.message},t.errno)):(e.logger.error({...s,event:`RequestFailed`,error:b(t)}),I(r,{message:`Internal Server Error`},500))}},r=e.options.https?f.default.createServer({key:e.options.https.key,cert:e.options.https.cert,ca:e.options.https.ca},n):d.default.createServer(n);return new Promise((t,n)=>{let i=!1;r.on(`close`,()=>{e.logger.core({event:`ServerClosed`,host:e.options.host,port:e.options.port})}),r.once(`listening`,()=>{i=!0,e.logger.core({event:`FluxionStarted`,version:`0.1.2`,pid:process.pid,protocol:e.options.https?`https`:`http`,host:e.options.host,port:e.options.port}),e.logger.core({event:`DynamicDirectory`,directory:e.options.dir}),t(r)}),r.on(`error`,t=>{e.logger.error({event:`ServerError`,error:b(t)}),!i&&n(t)}),r.listen(e.options.port,e.options.host)})}function Ae(e){}const je=Ae,Y=new Map,Me=`${l.sep}node_modules${l.sep}`,X=new Set([`.ts`,`.tsx`,`.mts`,`.cts`,`.js`,`.jsx`,`.mjs`,`.cjs`]),Z=[...X,`.json`],Ne={".js":[`.ts`,`.tsx`],".jsx":[`.tsx`],".mjs":[`.mts`],".cjs":[`.cts`]},Pe=e=>!e.startsWith(`.`)&&!(0,l.isAbsolute)(e),Fe=e=>{try{let t=(0,c.statSync)(e.filename);return t.mtimeMs!==e.mtimeMs||t.size!==e.size}catch{return!0}},Q=e=>{try{return(0,c.statSync)(e).isFile()}catch{return!1}},Ie=e=>{if(Q(e))return e;let t=(0,l.extname)(e),n=e.slice(0,e.length-t.length);for(let e of Ne[t]??[])if(Q(n+e))return n+e;for(let t of Z)if(Q(e+t))return e+t;for(let t of Z)if(Q((0,l.join)(e,`index`+t)))return(0,l.join)(e,`index`+t)};var Le=class{opts;constructor(e){this.opts=e}compile(e){let{outputFiles:t}=(0,m.buildSync)({entryPoints:[e],bundle:!1,write:!1,platform:`node`,format:`cjs`,target:this.opts.target,sourcemap:`inline`,sourcesContent:!1,logLevel:`silent`,supported:{"dynamic-import":!1},define:{"import.meta.url":JSON.stringify((0,_.pathToFileURL)(e).href),"import.meta.filename":JSON.stringify(e),"import.meta.dirname":JSON.stringify((0,l.dirname)(e))}});return t[0].text}require(e){return this.load((0,l.resolve)(e)).exports}isHot(e){let t=(0,l.extname)(e);return e.startsWith(this.opts.dir)&&!e.includes(Me)&&(X.has(t)||t===`.json`)}load(e){let t=Y.get(e);if(t===void 0)return this.loadFresh(e);let n=this.collectChain(t);if(!this.anyStale(n))return t;for(let e of n)Y.get(e.filename)===e&&Y.delete(e.filename);return this.loadFresh(e)}collectChain(e){let t=new Set([e]);for(let e of t)for(let n of e.deps.values())t.add(n);return t}anyStale(e){for(let t of e)if(Y.get(t.filename)!==t||Fe(t))return!0;return!1}loadFresh(e){let{mtimeMs:t,size:n}=(0,c.statSync)(e),r={id:e,filename:e,exports:{},mtimeMs:t,size:n,deps:new Map};Y.set(e,r);try{this.evaluate(e,r)}catch(t){throw Y.delete(e),t}return r}evaluate(e,t){let n=(0,l.extname)(e);if(n===`.json`){t.exports=JSON.parse((0,c.readFileSync)(e,`utf8`));return}if(!X.has(n))throw Error(`FluxionLoader: unsupported file type "${n}" (${e})`);(0,h.compileFunction)(this.compile(e),[`exports`,`require`,`module`,`__filename`,`__dirname`],{filename:e}).call(t.exports,t.exports,this.createModuleRequire(t),t,e,(0,l.dirname)(e))}createModuleRequire(e){let t=(0,g.createRequire)(e.filename),n=t=>Pe(t)?void 0:Ie((0,l.resolve)((0,l.dirname)(e.filename),t)),r=r=>{let i=n(r);if(i===void 0)return t(r);if(!this.isHot(i))return t(i);let a=this.load(i);return e.deps.set(i,a),a.exports};return r.resolve=e=>n(e)??t.resolve(e),r.cache=t.cache,r}};function $(e,t){if(typeof t!=`object`||!t)return!1;if(je(t),typeof t.handler!=`function`)return e.logger.error({event:`InvalidModule`,error:`handler must be a function`}),!1;if(t.disposer!==void 0&&typeof t.disposer!=`function`)return e.logger.error({event:`InvalidModule`,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({event:`InvalidModule`,error:`handlerTimeoutMs must be an integer >= 100 if provided`}),!1):t.type===0?!0:(e.logger.error({event:`InvalidModule`,error:`You must use defineFluxionModule to create module`}),!1)}function Re(e,t,n){let r=new Le(e.options).require(t);if($(e,r.default))r=r.default;else if(!$(e,r))throw new k(`Invalid handler module '${t}', make sure it satisfies defineFluxionModule(...) helper`);return r.absolutePath=t,r.mtimeMs=n.mtimeMs,r}function ze(e){return async t=>{let n=t.pathname.replace(/^[/]+/,``).replace(/[/]+$/,``),r=l.default.join(e.options.dir,n),i=l.default.relative(e.options.dir,r);if(i.startsWith(`..`)||l.default.isAbsolute(i)||e.options.exclude.some(e=>(0,p.minimatch)(n,e)))return;let a=await u.default.stat(r).catch(()=>void 0);if(!(!a||!a.isFile())){if(e.options.apiInclude.some(e=>(0,p.minimatch)(n,e)))return Re(e,r,a);if(e.options.staticInclude.some(e=>(0,p.minimatch)(n,e)))return Be(r)}}}function Be(e){return{type:1,mtimeMs:NaN,absolutePath:e,handler:async(t,n,r,i)=>{if(t.method!==`GET`&&t.method!==`HEAD`){i.statusCode=405,i.setHeader(`Allow`,`GET, HEAD`),i.end();return}let a=await u.default.stat(e).catch(e=>e);if(a instanceof Error)return i.statusCode=404,i.end(`Not Accessible`),n.logger.error({event:`StaticResourceError`,url:t.url.pathname,error:a.message}),null;if(!a.isFile()){i.statusCode=404,i.end(`Not Found`);return}let o=re[l.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=(0,c.createReadStream)(e),o=()=>{a.off(`error`,s),a.off(`end`,l),i.off(`close`,u),r.off(`aborted`,u)},s=e=>{o(),n(e)},l=()=>{o(),t(D.StaticHandled)},u=()=>{o(),a.destroy(),t(D.StaticHandled)};a.on(`error`,s),a.on(`end`,l),i.on(`close`,u),r.on(`aborted`,u),a.pipe(i)})}}}async function Ve(e){let t={options:e.normalizedFlag===D.OptionsNormalized?e:j(e)};t.options.logOutputFile&&await E(t.options.logOutputFile);let n=t.options.restartMemoryMB!==!1&&oe();t.logger=T(t,{deferClose:n}),t.router=ze(t);let r=await ke(t);t.options.restartMemoryMB!==!1&&!n&&t.logger.warn({event:`RestartDisabled`,reason:`process.execve is not available on this Node.js version`,requiredVersion:`22.15`,nodeVersion:process.version,restartMemoryMB:t.options.restartMemoryMB});let i=O({restartMemoryMB:n?t.options.restartMemoryMB:!1,server:r,logger:t.logger}),a=e=>{i.stop(),t.logger.warn({event:`ShuttingDown`,pid:process.pid,signal:e}),r.close(async()=>{await t.logger.flush(),process.exit(0)})};process.once(`SIGINT`,()=>a(`SIGINT`)),process.once(`SIGTERM`,()=>a(`SIGTERM`)),i.start()}function He(e,t){if(typeof e==`function`){if(t!==void 0&&typeof t!=`function`)throw new k(`Invalid disposer, expected a function but got ${typeof t}`);return{handler:e,disposer:t,type:0}}if(typeof e!=`object`||!e)throw new k(`Invalid argument, expected a FluxionModule object or a handler function, but got ${typeof e}`);if(typeof e.handler!=`function`)throw new k(`Invalid FluxionModule, "handler" must be a function`);if(e.disposer!==void 0&&typeof e.disposer!=`function`)throw new k(`Invalid FluxionModule, "disposer" must be a function if provided`);if(e.methods!==void 0&&(!Array.isArray(e.methods)||e.methods.some(e=>typeof e!=`string`)))throw new k(`Invalid FluxionModule, "methods" must be an array of strings if provided`);if(e.middlewares!==void 0&&(!Array.isArray(e.middlewares)||e.middlewares.some(e=>typeof e!=`function`)))throw new k(`Invalid FluxionModule, "middlewares" must be an array of functions if provided`);return{...e,type:0}}function Ue(e){if(typeof e!=`function`)throw new k(`Invalid FluxionMiddleware, expected a function but got ${typeof e}`);return e}function We(e){if(typeof e!=`function`)throw new k(`Invalid FluxionLoggerFn, expected a function but got ${typeof e}`);return e}exports.BadGatewayException=Se,exports.BadRequestException=z,exports.ConflictException=he,exports.ForbiddenException=V,exports.GatewayTimeoutException=we,exports.GoneException=ge,exports.HttpCode=ie,exports.HttpException=R,exports.InternalServerErrorException=be,exports.MethodNotAllowedException=fe,exports.NotAcceptableException=pe,exports.NotFoundException=H,exports.NotImplementedException=xe,exports.PayloadTooLargeException=U,exports.RequestTimeoutException=me,exports.ServiceUnavailableException=Ce,exports.TooManyRequestsException=ye,exports.UnauthorizedException=B,exports.UnprocessableEntityException=ve,exports.UnsupportedMediaTypeException=_e,exports.VERSION=`0.1.2`,exports.defineFluxionLogger=We,exports.defineFluxionMiddleware=Ue,exports.defineFluxionModule=He,exports.defineFluxionOptions=j,exports.fluxion=Ve;
|
|
8
|
+
NEW: { apiInclude: ["**/*.ts"], staticInclude: ["**/*.js"] }`);let{dir:t,host:n,port:r,handlerTimeoutMs:i=5e3,middlewareTimeoutMs:a=3e3,staticResourceTimeoutMs:o=180*1e3,target:s=`node${process.versions.node.split(`.`)[0]}`,maxRequestBytes:u=8e6,apiInclude:d=[`**/*.ts`],staticInclude:f=[`**/*`],exclude:p=[`**/node_modules/**`,`**/.git/**`,`**/dist/**`,`**/build/**`,`**/.vscode/**`,`**/.idea/**`,`**/*.log`,`**/.DS_Store`,`**/coverage/**`,`**/.nyc_output/**`,`**/*.tmp`,`**/*.temp`],https:m,csp:h=`default-src 'self'`,restartMemoryMB:g=!1,logOutputFile:_}=e,v=r,y=e.logger??`json-line`;if(y!==`json-line`&&typeof y!=`function`)throw new k(`Invalid logger option, Must be 'json-line' or a custom logger function (write a one-line logger yourself)`);if(typeof t!=`string`)throw new k(`FluxionOptions.dir must be a string`);let b=l.default.resolve(process.cwd(),t),x=b.endsWith(l.default.sep)?b:b+l.default.sep;if(typeof s!=`string`&&!(Array.isArray(s)&&s.every(e=>typeof e==`string`)))throw new k(`FluxionOptions.target must be a string or an array of strings`);if(typeof n!=`string`)throw new k(`FluxionOptions.host must be a string`);if(!Number.isSafeInteger(i)||i<=100)throw new k(`FluxionOptions.handlerTimeoutMs must be an integer greater than 100`);if(!Number.isSafeInteger(a)||a<=100)throw new k(`FluxionOptions.middlewareTimeoutMs must be an integer greater than 100`);if(typeof v!=`number`||!Number.isSafeInteger(v))throw new k(`FluxionOptions.port must be a positive integer`);if(v<=1||v>65535)throw new k(`FluxionOptions.port must be 1 ~ 65535`);if(typeof u!=`number`||u<=0||!Number.isSafeInteger(u))throw new k(`FluxionOptions.maxRequestBytes must be a positive integer`);if(h!==!1&&typeof h!=`string`)throw new k(`FluxionOptions.csp must be a string or false`);if(g!==!1&&(typeof g!=`number`||!Number.isFinite(g)||g<=0))throw new k(`FluxionOptions.restartMemoryMB must be a positive number of MB, or false`);if(_!==void 0&&(typeof _!=`string`||_.length===0))throw new k(`FluxionOptions.logOutputFile must be a non-empty string path`);let S=_===void 0?void 0:l.default.resolve(process.cwd(),_);c.default.existsSync(x)||c.default.mkdirSync(x,{recursive:!0});let C={dir:x,host:n,port:v,handlerTimeoutMs:i,middlewareTimeoutMs:a,staticResourceTimeoutMs:o,target:s,maxRequestBytes:u,logger:y,logOutputFile:S,apiInclude:d,staticInclude:f,exclude:p,https:ue(m),csp:h,restartMemoryMB:g,normalizedFlag:D.OptionsNormalized};return process.argv.includes(`--verbose`)&&console.log(`normalized options`,{...C,https:C.https?`set`:`unset`}),C}function M(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 de(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 N(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 P(e){if(e!==void 0)try{return new URL(e,`http://fluxion.local`)}catch{return}}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 L(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 R=class extends Error{errno;code;constructor(e,t,n){super(e),this.name=`HttpException`,this.errno=t,this.code=n}},z=class extends R{constructor(e=`Bad Request`){super(e,400,`BAD_REQUEST`)}},B=class extends R{constructor(e=`Unauthorized`){super(e,401,`UNAUTHORIZED`)}},V=class extends R{constructor(e=`Forbidden`){super(e,403,`FORBIDDEN`)}},H=class extends R{constructor(e=`Not Found`){super(e,404,`NOT_FOUND`)}},fe=class extends R{constructor(e=`Method Not Allowed`){super(e,405,`METHOD_NOT_ALLOWED`)}},pe=class extends R{constructor(e=`Not Acceptable`){super(e,406,`NOT_ACCEPTABLE`)}},me=class extends R{constructor(e=`Request Timeout`){super(e,408,`REQUEST_TIMEOUT`)}},he=class extends R{constructor(e=`Conflict`){super(e,409,`CONFLICT`)}},ge=class extends R{constructor(e=`Gone`){super(e,410,`GONE`)}},U=class extends R{constructor(e=`Payload Too Large`){super(e,413,`PAYLOAD_TOO_LARGE`)}},_e=class extends R{constructor(e=`Unsupported Media Type`){super(e,415,`UNSUPPORTED_MEDIA_TYPE`)}},ve=class extends R{constructor(e=`Unprocessable Entity`){super(e,422,`UNPROCESSABLE_ENTITY`)}},ye=class extends R{constructor(e=`Too Many Requests`){super(e,429,`TOO_MANY_REQUESTS`)}},be=class extends R{constructor(e=`Internal Server Error`){super(e,500,`INTERNAL_SERVER_ERROR`)}},xe=class extends R{constructor(e=`Not Implemented`){super(e,501,`NOT_IMPLEMENTED`)}},Se=class extends R{constructor(e=`Bad Gateway`){super(e,502,`BAD_GATEWAY`)}},Ce=class extends R{constructor(e=`Service Unavailable`){super(e,503,`SERVICE_UNAVAILABLE`)}},we=class extends R{constructor(e=`Gateway Timeout`){super(e,504,`GATEWAY_TIMEOUT`)}};function W(e,t){return new U(`request body too large: ${e.toString()} bytes exceeds ${t.toString()} bytes`)}function G(e){return Array.isArray(e)?e[0]:e}function K(){return{exists:!1,bytes:0,truncated:!1}}function Te(e,t,n,r){return t===0?K():N(n)?{exists:!0,value:e.toString(`utf8`),bytes:t,truncated:r}:{exists:!0,value:`<binary body: ${t} bytes>`,bytes:t,truncated:r}}async function Ee(e,t,n,r=8192){if(t===`GET`||t===`HEAD`||e.readableEnded)return{rawBody:void 0,preview:K()};let i=G(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(W(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(W(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:Te(s.length>0?Buffer.concat(s):Buffer.alloc(0),n?.byteLength??0,G(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 q(e,t,n){let{rawBody:r,preview:i}=await Ee(e,t,n);if(r===void 0||r.byteLength===0)return{body:{},preview:i};let a=G(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:L(new URLSearchParams(r.toString(`utf8`))),preview:i}:N(a)?{body:{raw:r.toString(`utf8`)},preview:i}:{body:{},preview:i}}function De(e){if(!e)return{};let t={},n=e.split(`;`),r=0;for(let e of n){if(r>=100)break;let[n,...i]=e.split(`=`);if(!n)continue;let a=n.trim(),o=i.join(`=`).trim();t[a]=decodeURIComponent(o),r++}return t}const J=(e,t,n)=>Promise.race([e,new Promise(e=>setTimeout(()=>e(n),t))]);function Oe(e,t,n){if(typeof n.disposer!=`function`)return;let r=n.disposer;t.once(`close`,()=>{M(r).catch(t=>{e.logger.error({event:`DisposerFailed`,url:n.absolutePath,error:b(t)})})})}function ke(e){let t=Object.freeze({logger:e.logger}),n=async(n,r)=>{let i=n.method??`GET`,a=de(n),o=P(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:L(o.searchParams),body:{},headers:n.headers,cookie:De(n.headers.cookie),meta:{}};r.setHeader(`X-Content-Type-Options`,`nosniff`),r.setHeader(`X-Frame-Options`,`DENY`),r.setHeader(`X-XSS-Protection`,`1; mode=block`),e.options.csp!==!1&&r.setHeader(`Content-Security-Policy`,e.options.csp);let c={exists:!1,bytes:0,truncated:!1};e.logger.core({event:`request`,method:i,ip:a,path:o.pathname});let l=performance.now();r.once(`finish`,()=>{let t={event:`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.core(t)});try{let i=await q(n,s.method,e.options.maxRequestBytes);s.body=i.body,c=i.preview;let a=await e.router(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}Oe(e,r,a);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 J(M(a.middlewares[i],s,t,n,r),e.options.middlewareTimeoutMs,D.MiddlewareTimeout)===D.MiddlewareTimeout){e.logger.warn({event:`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 J(M(a.handler,s,t,n,r),l,D.HandlerTimeout);if(u===D.HandlerTimeout){e.logger.warn({event:`HandlerTimeout`,method:s.method,ip:s.ip}),I(r,{message:`Handler timed out`},500);return}u!==D.StaticHandled&&I(r,u)}catch(t){t instanceof R?(e.logger.error({...s,event:`RequestFailed`,error:t.message}),I(r,{message:t.message},t.errno)):(e.logger.error({...s,event:`RequestFailed`,error:b(t)}),I(r,{message:`Internal Server Error`},500))}},r=e.options.https?f.default.createServer({key:e.options.https.key,cert:e.options.https.cert,ca:e.options.https.ca},n):d.default.createServer(n);return new Promise((t,n)=>{let i=!1;r.on(`close`,()=>{e.logger.core({event:`ServerClosed`,host:e.options.host,port:e.options.port})}),r.once(`listening`,()=>{i=!0,e.logger.core({event:`FluxionStarted`,version:`0.1.3`,pid:process.pid,protocol:e.options.https?`https`:`http`,host:e.options.host,port:e.options.port}),e.logger.core({event:`DynamicDirectory`,directory:e.options.dir}),t(r)}),r.on(`error`,t=>{e.logger.error({event:`ServerError`,error:b(t)}),!i&&n(t)}),r.listen(e.options.port,e.options.host)})}function Ae(e){}const je=Ae,Y=new Map,Me=`${l.sep}node_modules${l.sep}`,X=new Set([`.ts`,`.tsx`,`.mts`,`.cts`,`.js`,`.jsx`,`.mjs`,`.cjs`]),Z=[...X,`.json`],Ne={".js":[`.ts`,`.tsx`],".jsx":[`.tsx`],".mjs":[`.mts`],".cjs":[`.cts`]},Pe=e=>!e.startsWith(`.`)&&!(0,l.isAbsolute)(e),Fe=e=>{try{let t=(0,c.statSync)(e.filename);return t.mtimeMs!==e.mtimeMs||t.size!==e.size}catch{return!0}},Q=e=>{try{return(0,c.statSync)(e).isFile()}catch{return!1}},Ie=e=>{if(Q(e))return e;let t=(0,l.extname)(e),n=e.slice(0,e.length-t.length);for(let e of Ne[t]??[])if(Q(n+e))return n+e;for(let t of Z)if(Q(e+t))return e+t;for(let t of Z)if(Q((0,l.join)(e,`index`+t)))return(0,l.join)(e,`index`+t)};var Le=class{opts;constructor(e){this.opts=e}compile(e,t){let n={"import.meta.url":JSON.stringify((0,_.pathToFileURL)(e).href),"import.meta.filename":JSON.stringify(e),"import.meta.dirname":JSON.stringify((0,l.dirname)(e))};t&&(n.__dirname=JSON.stringify((0,l.dirname)(e)),n.__filename=JSON.stringify(e));let{outputFiles:r}=(0,m.buildSync)({entryPoints:[e],bundle:!1,write:!1,platform:`node`,format:`cjs`,target:this.opts.target,sourcemap:`inline`,sourcesContent:!1,logLevel:`silent`,supported:{"dynamic-import":!1},define:n});return r[0].text}require(e){return this.load((0,l.resolve)(e)).exports}isHot(e){let t=(0,l.extname)(e);return e.startsWith(this.opts.dir)&&!e.includes(Me)&&(X.has(t)||t===`.json`)}load(e){let t=Y.get(e);if(t===void 0)return this.loadFresh(e);let n=this.collectChain(t);if(!this.anyStale(n))return t;for(let e of n)Y.get(e.filename)===e&&Y.delete(e.filename);return this.loadFresh(e)}collectChain(e){let t=new Set([e]);for(let e of t)for(let n of e.deps.values())t.add(n);return t}anyStale(e){for(let t of e)if(Y.get(t.filename)!==t||Fe(t))return!0;return!1}loadFresh(e){let{mtimeMs:t,size:n}=(0,c.statSync)(e),r={id:e,filename:e,exports:{},mtimeMs:t,size:n,deps:new Map};Y.set(e,r);try{this.evaluate(e,r)}catch(t){throw Y.delete(e),t}return r}evaluate(e,t){let n=(0,l.extname)(e);if(n===`.json`){t.exports=JSON.parse((0,c.readFileSync)(e,`utf8`));return}if(!X.has(n))throw Error(`FluxionLoader: unsupported file type "${n}" (${e})`);this.compileFunction(e).call(t.exports,t.exports,this.createModuleRequire(t),t,e,(0,l.dirname)(e))}compileFunction(e){let t=[`exports`,`require`,`module`,`__filename`,`__dirname`];try{return(0,h.compileFunction)(this.compile(e,!1),t,{filename:e})}catch(n){if(!(n instanceof SyntaxError&&/__dirname|__filename/.test(n.message)))throw n;return(0,h.compileFunction)(this.compile(e,!0),t.slice(0,3),{filename:e})}}createModuleRequire(e){let t=(0,g.createRequire)(e.filename),n=t=>Pe(t)?void 0:Ie((0,l.resolve)((0,l.dirname)(e.filename),t)),r=r=>{let i=n(r);if(i===void 0)return t(r);if(!this.isHot(i))return t(i);let a=this.load(i);return e.deps.set(i,a),a.exports};return r.resolve=e=>n(e)??t.resolve(e),r.cache=t.cache,r}};function $(e,t){if(typeof t!=`object`||!t)return!1;if(je(t),typeof t.handler!=`function`)return e.logger.error({event:`InvalidModule`,error:`handler must be a function`}),!1;if(t.disposer!==void 0&&typeof t.disposer!=`function`)return e.logger.error({event:`InvalidModule`,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({event:`InvalidModule`,error:`handlerTimeoutMs must be an integer >= 100 if provided`}),!1):t.type===0?!0:(e.logger.error({event:`InvalidModule`,error:`You must use defineFluxionModule to create module`}),!1)}function Re(e,t,n){let r=new Le(e.options).require(t);if($(e,r.default))r=r.default;else if(!$(e,r))throw new k(`Invalid handler module '${t}', make sure it satisfies defineFluxionModule(...) helper`);return r.absolutePath=t,r.mtimeMs=n.mtimeMs,r}function ze(e){return async t=>{let n=t.pathname.replace(/^[/]+/,``).replace(/[/]+$/,``),r=l.default.join(e.options.dir,n),i=l.default.relative(e.options.dir,r);if(i.startsWith(`..`)||l.default.isAbsolute(i)||e.options.exclude.some(e=>(0,p.minimatch)(n,e)))return;let a=await u.default.stat(r).catch(()=>void 0);if(!(!a||!a.isFile())){if(e.options.apiInclude.some(e=>(0,p.minimatch)(n,e)))return Re(e,r,a);if(e.options.staticInclude.some(e=>(0,p.minimatch)(n,e)))return Be(r)}}}function Be(e){return{type:1,mtimeMs:NaN,absolutePath:e,handler:async(t,n,r,i)=>{if(t.method!==`GET`&&t.method!==`HEAD`){i.statusCode=405,i.setHeader(`Allow`,`GET, HEAD`),i.end();return}let a=await u.default.stat(e).catch(e=>e);if(a instanceof Error)return i.statusCode=404,i.end(`Not Accessible`),n.logger.error({event:`StaticResourceError`,url:t.url.pathname,error:a.message}),null;if(!a.isFile()){i.statusCode=404,i.end(`Not Found`);return}let o=re[l.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=(0,c.createReadStream)(e),o=()=>{a.off(`error`,s),a.off(`end`,l),i.off(`close`,u),r.off(`aborted`,u)},s=e=>{o(),n(e)},l=()=>{o(),t(D.StaticHandled)},u=()=>{o(),a.destroy(),t(D.StaticHandled)};a.on(`error`,s),a.on(`end`,l),i.on(`close`,u),r.on(`aborted`,u),a.pipe(i)})}}}async function Ve(e){let t={options:e.normalizedFlag===D.OptionsNormalized?e:j(e)};t.options.logOutputFile&&await E(t.options.logOutputFile);let n=t.options.restartMemoryMB!==!1&&oe();t.logger=T(t,{deferClose:n}),t.router=ze(t);let r=await ke(t);t.options.restartMemoryMB!==!1&&!n&&t.logger.warn({event:`RestartDisabled`,reason:`process.execve is not available on this Node.js version`,requiredVersion:`22.15`,nodeVersion:process.version,restartMemoryMB:t.options.restartMemoryMB});let i=O({restartMemoryMB:n?t.options.restartMemoryMB:!1,server:r,logger:t.logger}),a=e=>{i.stop(),t.logger.warn({event:`ShuttingDown`,pid:process.pid,signal:e}),r.close(async()=>{await t.logger.flush(),process.exit(0)})};process.once(`SIGINT`,()=>a(`SIGINT`)),process.once(`SIGTERM`,()=>a(`SIGTERM`)),i.start()}function He(e,t){if(typeof e==`function`){if(t!==void 0&&typeof t!=`function`)throw new k(`Invalid disposer, expected a function but got ${typeof t}`);return{handler:e,disposer:t,type:0}}if(typeof e!=`object`||!e)throw new k(`Invalid argument, expected a FluxionModule object or a handler function, but got ${typeof e}`);if(typeof e.handler!=`function`)throw new k(`Invalid FluxionModule, "handler" must be a function`);if(e.disposer!==void 0&&typeof e.disposer!=`function`)throw new k(`Invalid FluxionModule, "disposer" must be a function if provided`);if(e.methods!==void 0&&(!Array.isArray(e.methods)||e.methods.some(e=>typeof e!=`string`)))throw new k(`Invalid FluxionModule, "methods" must be an array of strings if provided`);if(e.middlewares!==void 0&&(!Array.isArray(e.middlewares)||e.middlewares.some(e=>typeof e!=`function`)))throw new k(`Invalid FluxionModule, "middlewares" must be an array of functions if provided`);return{...e,type:0}}function Ue(e){if(typeof e!=`function`)throw new k(`Invalid FluxionMiddleware, expected a function but got ${typeof e}`);return e}function We(e){if(typeof e!=`function`)throw new k(`Invalid FluxionLoggerFn, expected a function but got ${typeof e}`);return e}exports.BadGatewayException=Se,exports.BadRequestException=z,exports.ConflictException=he,exports.ForbiddenException=V,exports.GatewayTimeoutException=we,exports.GoneException=ge,exports.HttpCode=ie,exports.HttpException=R,exports.InternalServerErrorException=be,exports.MethodNotAllowedException=fe,exports.NotAcceptableException=pe,exports.NotFoundException=H,exports.NotImplementedException=xe,exports.PayloadTooLargeException=U,exports.RequestTimeoutException=me,exports.ServiceUnavailableException=Ce,exports.TooManyRequestsException=ye,exports.UnauthorizedException=B,exports.UnprocessableEntityException=ve,exports.UnsupportedMediaTypeException=_e,exports.VERSION=`0.1.3`,exports.defineFluxionLogger=We,exports.defineFluxionMiddleware=Ue,exports.defineFluxionModule=He,exports.defineFluxionOptions=j,exports.fluxion=Ve;
|
|
9
9
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["path","fs","sep","path","fs"],"sources":["../src/common/logger.ts","../src/common/output.ts","../src/common/consts.ts","../src/common/restart.ts","../src/common/error.ts","../src/defines/options.ts","../src/common/promise-try.ts","../src/http/headers.ts","../src/http/request.ts","../src/http/respond.ts","../src/http/query.ts","../src/http/exceptions.ts","../src/http/body.ts","../src/http/cookie.ts","../src/http/server.ts","../node_modules/.pnpm/type-narrow@0.2.2/node_modules/type-narrow/dist/index.mjs","../src/common/loader.ts","../src/common/injector.ts","../src/router/index.ts","../src/fluxion.ts","../src/defines/index.ts","../src/index.ts"],"sourcesContent":["import type { FluxionContext } from '@/types.js';\nimport type { otherstring } from '@/types.js';\nimport type { WriteStream } from 'node:fs';\nimport { createWriteStream, existsSync } from 'node:fs';\nimport { dirname } from 'node:path';\nimport { mkdirSync } from 'node:fs';\n\ntype LogLevel = 'CORE' | 'INFO' | 'WARN' | 'ERROR' | 'SUCC' | 'DEBUG' | 'VERBOSE' | otherstring;\n\nexport interface LogEntry {\n timestamp: string;\n level: LogLevel;\n event?: unknown;\n [key: string]: unknown;\n}\n\n/**\n * `'json-line'` writes one JSON object per line. `one-line` is intentionally\n * not built in: write your own function with {@link defineFluxionLogger}.\n */\nexport type LoggerOption = 'json-line' | FluxionLoggerFn;\n\nexport type FluxionLoggerFn = (entry: LogEntry) => void;\n\n/**\n * A log payload is either a plain object, whose fields are merged into the\n * entry, or a primitive, which becomes the `event` field.\n */\nexport type LogPayload = string | number | boolean | bigint | null | undefined | MessageObject;\n\nexport interface MessageObject {\n [key: string]: unknown;\n event?: unknown;\n}\n\nexport interface FluxionLogger {\n /**\n * Primitive payloads become `event`; object payloads are spread over the\n * default `timestamp`, `level` and `event` fields.\n */\n write(level: LogLevel, payload: LogPayload): void;\n info(payload: LogPayload): void;\n warn(payload: LogPayload): void;\n error(payload: LogPayload): void;\n succ(payload: LogPayload): void;\n debug(payload: LogPayload): void;\n verbose(payload: LogPayload): void;\n}\n\n/**\n * Internal-only logger used by fluxion's own subsystems (router, injector,\n * ...). It extends the public {@link FluxionLogger}\n * with a `core` level that records framework-originated logs — e.g. route\n * registration, module lifecycle — so they are visually distinct\n * from logs emitted by user handlers.\n *\n * ! This type is NOT exported to application code: {@link FluxionModuleContext}\n * exposes only {@link FluxionLogger}, keeping\n * `core` off-limits to user handlers.\n */\nexport interface InternalFluxionLogger extends FluxionLogger {\n core(payload: LogPayload): void;\n /**\n * Resolve once the buffered file sink has been drained. Must be awaited\n * before the process image is replaced (`process.execve`), or the tail of the\n * log is lost.\n */\n flush(): Promise<void>;\n}\n\nconst isMessageObject = (payload: LogPayload): payload is MessageObject =>\n typeof payload === 'object' && payload !== null && !Array.isArray(payload);\n\n/**\n * ! Error.isError needs Node.js 24\n */\nconst isError = (value: unknown): value is Error =>\n typeof Error.isError === 'function' ? Error.isError(value) : value instanceof Error;\n\n/**\n * Turn anything thrown or passed as an error into a printable string:\n * the `message`, followed by the `stack` on the next line when present.\n */\nexport function formatError(e: unknown): string {\n if (typeof e === 'object' && e !== null) {\n const { message, stack } = e as { message?: unknown; stack?: unknown };\n let text = '';\n if (typeof message === 'string' && message.length > 0) {\n text = message;\n }\n if (typeof stack === 'string' && stack.length > 0) {\n text += (text.length > 0 ? '\\n' : '') + stack;\n }\n if (text.length > 0) {\n return text;\n }\n }\n return String(e);\n}\n\nconst jsonReplacer = (_key: string, value: unknown): unknown => {\n if (typeof value === 'bigint') {\n return value.toString();\n }\n // Errors serialize to `{}` on their own, so expand them to message + stack.\n if (isError(value)) {\n return formatError(value);\n }\n return value;\n};\n\nconst fallbackLine = (entry: LogEntry): string =>\n JSON.stringify({\n timestamp: typeof entry.timestamp === 'string' ? entry.timestamp : String(entry.timestamp),\n level: typeof entry.level === 'string' ? entry.level : 'INFO',\n event: '[unserializable]',\n });\n\n/**\n * Serialize one entry as a single JSON line.\n */\nconst safeStringify = (entry: LogEntry): string => {\n try {\n return JSON.stringify(entry, jsonReplacer) ?? fallbackLine(entry);\n } catch {\n return fallbackLine(entry);\n }\n};\n\n/**\n * Default sink: one JSON object per line on stdout.\n */\nexport const jsonLineLogger: FluxionLoggerFn = (entry: LogEntry) => {\n // eslint-disable-next-line no-console\n console.log(safeStringify(entry));\n};\n\n/**\n * Resolve once everything queued on the stream has been handed to the OS. An\n * empty write gets a callback behind the queued chunks without ending the\n * stream itself. Resolves on error too, and after a short timeout, so a stuck\n * disk cannot hold a restart hostage.\n */\nfunction flushStream(stream: WriteStream): Promise<void> {\n return new Promise((resolve) => {\n if (stream.destroyed || stream.writableEnded) {\n resolve();\n return;\n }\n const done = () => {\n clearTimeout(timer);\n stream.off('error', done);\n resolve();\n };\n const timer = setTimeout(done, 2000);\n timer.unref();\n stream.once('error', done);\n try {\n stream.write('', done);\n } catch {\n done();\n }\n });\n}\n\n/**\n * 创建文件日志写入器\n *\n * With `deferClose`, the stream does not close itself on error and is closed by\n * `flush()` instead: the logger stays usable until an explicit flush.\n */\nfunction createFileSink(\n logFilePath: string,\n deferClose: boolean,\n): { sink: FluxionLoggerFn; flush: () => Promise<void> } {\n // 确保日志目录存在\n if (!existsSync(logFilePath)) {\n const dir = dirname(logFilePath);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n }\n\n const fileStream = createWriteStream(logFilePath, { flags: 'a', autoClose: !deferClose });\n\n const sink: FluxionLoggerFn = (entry: LogEntry) => {\n try {\n fileStream.write(safeStringify(entry) + '\\n');\n } catch {\n // 忽略文件写入错误\n }\n };\n\n const flush = async () => {\n await flushStream(fileStream);\n if (deferClose) {\n await new Promise<void>((resolve) => fileStream.close(() => resolve()));\n }\n };\n\n return { sink, flush };\n}\n\ntype LoggerSink = FluxionLoggerFn & { flush?: () => Promise<void> };\n\n/**\n * & Logger Options here is checked by normalizeOptions function.\n *\n * When `deferClose` is set, the returned sink carries a `flush` that eventually\n * closes the file stream; it is meant for the in-place restart path, which must\n * drain the log before `process.execve` replaces the image.\n */\nfunction resolveLoggerSink(cx: Pick<FluxionContext, 'options'>, deferClose: boolean): LoggerSink {\n const instanceLogPath = process.env.FLUXION_INSTANCE_LOG;\n const fileSink = instanceLogPath ? createFileSink(instanceLogPath, deferClose) : null;\n\n const loggerOption = cx.options.logger;\n const consoleSink = typeof loggerOption === 'function' ? loggerOption : jsonLineLogger;\n\n if (!fileSink) {\n return consoleSink;\n }\n\n const combined: LoggerSink = (entry: LogEntry) => {\n consoleSink(entry);\n fileSink.sink(entry);\n };\n combined.flush = fileSink.flush;\n return combined;\n}\n\nexport function createLogger(\n cx: Pick<FluxionContext, 'options'>,\n options: { deferClose?: boolean } = {},\n): InternalFluxionLogger {\n const sink = resolveLoggerSink(cx, options.deferClose === true);\n\n const logger: InternalFluxionLogger = {\n write(level: LogLevel, payload: LogPayload): void {\n // Default fields come first in insertion order; the spread lets a user\n // field override `timestamp`, `level` or `event`.\n const timestamp = new Date().toISOString();\n const entry: LogEntry = isMessageObject(payload)\n ? { timestamp, level, event: payload.event, ...payload }\n : { timestamp, level, event: payload };\n\n try {\n sink(entry);\n } catch {\n // Ignore logger sink failures to avoid breaking request handling.\n }\n },\n info(payload: LogPayload): void {\n this.write('INFO', payload);\n },\n warn(payload: LogPayload): void {\n this.write('WARN', payload);\n },\n error(payload: LogPayload): void {\n this.write('ERROR', payload);\n },\n succ(payload: LogPayload): void {\n this.write('SUCC', payload);\n },\n debug(payload: LogPayload): void {\n this.write('DEBUG', payload);\n },\n verbose(payload: LogPayload): void {\n this.write('VERBOSE', payload);\n },\n core(payload: LogPayload): void {\n this.write('CORE', payload);\n },\n flush(): Promise<void> {\n return sink.flush?.() ?? Promise.resolve();\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): InternalFluxionLogger {\n return {\n write(level: LogLevel, payload: LogPayload): void {\n baseLogger.write(level, isMessageObject(payload) ? { ...payload, pid } : { event: payload, pid });\n },\n info(payload: LogPayload): void {\n this.write('INFO', payload);\n },\n warn(payload: LogPayload): void {\n this.write('WARN', payload);\n },\n error(payload: LogPayload): void {\n this.write('ERROR', payload);\n },\n succ(payload: LogPayload): void {\n this.write('SUCC', payload);\n },\n debug(payload: LogPayload): void {\n this.write('DEBUG', payload);\n },\n verbose(payload: LogPayload): void {\n this.write('VERBOSE', payload);\n },\n core(payload: LogPayload): void {\n this.write('CORE', payload);\n },\n flush(): Promise<void> {\n // The base logger has no buffered stream of its own.\n return Promise.resolve();\n },\n };\n}\n","import { writeSync } from 'node:fs';\nimport { mkdir, open } from 'node:fs/promises';\nimport { dirname } from 'node:path';\n\n/**\n * Send everything written to `process.stdout` / `process.stderr` (so `console.*` too) to `filePath`, appending.\n *\n * Writes are synchronous: nothing is buffered, so there is nothing to flush before `process.exit` or `process.execve`.\n */\nexport async function redirectOutput(filePath: string): Promise<void> {\n await mkdir(dirname(filePath), { recursive: true });\n // The handle stays referenced by the closure below; a collected FileHandle would close the fd under us.\n const handle = await open(filePath, 'a');\n\n const write = (chunk: string | Uint8Array, encoding?: unknown, cb?: unknown) => {\n writeSync(handle.fd, Buffer.from(chunk));\n const done = typeof encoding === 'function' ? encoding : cb;\n if (typeof done === 'function') {\n done();\n }\n return true;\n };\n\n process.stdout.write = write as typeof process.stdout.write;\n process.stderr.write = write as typeof process.stderr.write;\n}\n","export const DUMMY_BASE_URL = 'http://fluxion.local';\n\nexport namespace flags {\n export const OptionsNormalized = Symbol('fluxion.options.normalized');\n export const StaticHandled = Symbol('fluxion.router.StaticHandled');\n export const HandlerTimeout = Symbol('fluxion.handlerTimeout');\n export const MiddlewareTimeout = Symbol('fluxion.middlewareTimeout');\n}\n\nexport const STATIC_CONTENT_TYPES: Record<string, string> = {\n '.css': 'text/css; charset=utf-8',\n '.html': 'text/html; charset=utf-8',\n '.htm': 'text/html; charset=utf-8',\n '.js': 'text/javascript; charset=utf-8',\n '.mjs': 'text/javascript; charset=utf-8',\n '.cjs': 'text/javascript; charset=utf-8',\n '.json': 'application/json; charset=utf-8',\n '.jsonld': 'application/ld+json; charset=utf-8',\n '.map': 'application/json; charset=utf-8',\n '.txt': 'text/plain; charset=utf-8',\n '.md': 'text/markdown; charset=utf-8',\n '.xml': 'application/xml; charset=utf-8',\n '.csv': 'text/csv; charset=utf-8',\n '.yaml': 'application/yaml; charset=utf-8',\n '.yml': 'application/yaml; charset=utf-8',\n '.toml': 'application/toml; charset=utf-8',\n\n '.ico': 'image/x-icon',\n '.png': 'image/png',\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.gif': 'image/gif',\n '.webp': 'image/webp',\n '.avif': 'image/avif',\n '.svg': 'image/svg+xml',\n '.bmp': 'image/bmp',\n '.tif': 'image/tiff',\n '.tiff': 'image/tiff',\n\n '.woff': 'font/woff',\n '.woff2': 'font/woff2',\n '.ttf': 'font/ttf',\n '.otf': 'font/otf',\n '.eot': 'application/vnd.ms-fontobject',\n\n '.mp3': 'audio/mpeg',\n '.wav': 'audio/wav',\n '.ogg': 'audio/ogg',\n '.oga': 'audio/ogg',\n '.m4a': 'audio/mp4',\n '.aac': 'audio/aac',\n '.flac': 'audio/flac',\n '.mp4': 'video/mp4',\n '.webm': 'video/webm',\n '.ogv': 'video/ogg',\n '.mov': 'video/quicktime',\n '.m4v': 'video/mp4',\n\n '.pdf': 'application/pdf',\n '.wasm': 'application/wasm',\n '.zip': 'application/zip',\n '.gz': 'application/gzip',\n '.tar': 'application/x-tar',\n '.rar': 'application/vnd.rar',\n '.7z': 'application/x-7z-compressed',\n '.webmanifest': 'application/manifest+json; charset=utf-8',\n '.exe': 'application/octet-stream',\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 ModuleType {\n Api,\n StaticResource,\n}\n","import type { InternalFluxionLogger } from './logger.js';\nimport { formatError } from './logger.js';\n\n/**\n * How long after `fluxion()` finishes before memory is first sampled, in ms.\n * Application code that runs right after `await fluxion()` — opening database\n * connections, warming caches — allocates memory that is not bloat, so the\n * first check waits for it to settle.\n */\nconst STARTUP_GRACE_MS = 5000;\n\n/** How often resident memory is sampled after the grace period, in ms. */\nconst MEMORY_CHECK_INTERVAL_MS = 10_000;\n\n/** How long to wait for in-flight connections to finish before cutting them. */\nconst CLOSE_TIMEOUT_MS = 5000;\n\n/**\n * The parts of `http.Server` / `https.Server` the restart path needs. Both\n * classes provide them, but `https.Server` does not extend `http.Server`.\n */\ninterface Listener {\n close(cb?: (err?: Error) => void): unknown;\n closeIdleConnections(): void;\n closeAllConnections(): void;\n}\n\nexport interface RestarterOptions {\n /**\n * Resident-memory threshold in MB. `false` disables the monitor.\n */\n restartMemoryMB: number | false;\n /** The listener to close before the image is replaced. */\n server: Listener;\n logger: InternalFluxionLogger;\n /**\n * Swap in another grace period, e.g. for tests.\n */\n startupGraceMs?: number;\n /**\n * Swap in another check interval, e.g. for tests.\n */\n checkIntervalMs?: number;\n}\n\nexport interface Restarter {\n /**\n * Start the grace period and, after it, the periodic sampling. Call as the\n * last step of startup so the grace period covers the caller's own init.\n */\n start(): void;\n /**\n * Stop sampling memory. Safe to call before {@link start}.\n */\n stop(): void;\n}\n\nconst megabytes = (bytes: number) => bytes / (1024 * 1024);\n\n/**\n * `process.execve` exists from Node.js 22.15 on Linux and macOS.\n */\nexport const supportsInPlaceRestart = (): boolean => typeof process.execve === 'function';\n\n/**\n * Resolve once the server has closed, or after `timeoutMs` — a keep-alive\n * connection that never goes away must not block the restart forever.\n */\nfunction closeServer(server: Listener, timeoutMs = CLOSE_TIMEOUT_MS): Promise<void> {\n return new Promise((resolve) => {\n let settled = false;\n const done = () => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n resolve();\n };\n const timer = setTimeout(() => {\n server.closeAllConnections();\n done();\n }, timeoutMs);\n timer.unref();\n\n server.close(done);\n server.closeIdleConnections();\n });\n}\n\n/**\n * Rebuild the command line the process was started with. `tsx` and other\n * loaders live in `execArgv`, so dropping it would start the entry point\n * unparsed.\n */\nconst commandLine = () => [process.execPath, ...process.execArgv, ...process.argv.slice(1)];\n\n/**\n * Replace the running process image with a fresh copy of itself, keeping the\n * PID. Listeners must be closed and the log flushed before the call: execve\n * only preserves stdio.\n *\n * Never returns on success — the entry point is re-executed in the same\n * process. On failure the process exits with code 1 so a supervisor restarts\n * it.\n */\nexport async function restartInPlace(\n server: Listener,\n logger: InternalFluxionLogger,\n extra: Record<string, unknown> = {},\n): Promise<never> {\n if (typeof process.execve !== 'function') {\n // Callers check `supportsInPlaceRestart` first; keep this for direct use.\n logger.error({ event: 'RestartFailed', pid: process.pid, error: 'process.execve is not available' });\n process.exit(1);\n }\n\n logger.warn({ event: 'Restarting', pid: process.pid, ...extra });\n\n await closeServer(server);\n await logger.flush();\n\n try {\n process.execve(process.execPath, commandLine(), process.env);\n } catch (e) {\n logger.error({ event: 'RestartFailed', pid: process.pid, error: formatError(e) });\n }\n process.exit(1);\n}\n\n/**\n * Watch RSS and run {@link restartInPlace} once it crosses the threshold.\n * Returns a no-op restarter when the threshold is `false`.\n *\n * Call this as the last step of `fluxion()`: the grace period is measured from\n * that moment. The timers are unref'd, so they never keep the process alive on\n * their own.\n */\nexport function watchMemory(options: RestarterOptions): Restarter {\n const {\n restartMemoryMB,\n server,\n logger,\n startupGraceMs = STARTUP_GRACE_MS,\n checkIntervalMs = MEMORY_CHECK_INTERVAL_MS,\n } = options;\n\n if (restartMemoryMB === false) {\n return { start: () => {}, stop: () => {} };\n }\n\n let restarting = false;\n let timer: NodeJS.Timeout | undefined;\n\n const check = () => {\n if (restarting) return;\n const rssMB = megabytes(process.memoryUsage.rss());\n if (rssMB <= restartMemoryMB) return;\n\n restarting = true;\n if (timer !== undefined) {\n clearInterval(timer);\n timer = undefined;\n }\n // Not awaited: the call ends the process, and the interval callback must\n // not keep the event loop busy while listeners drain.\n void restartInPlace(server, logger, { reason: 'memory', rssMB: Math.round(rssMB) });\n };\n\n return {\n start() {\n if (timer !== undefined || restarting) return;\n // Skip the startup phase entirely; the first sample lands when user code\n // has had time to finish its own initialization.\n timer = setTimeout(() => {\n timer = setInterval(check, checkIntervalMs);\n timer.unref();\n check();\n }, startupGraceMs);\n timer.unref();\n },\n stop() {\n if (timer !== undefined) {\n clearInterval(timer);\n timer = undefined;\n }\n },\n };\n}\n","export class FluxionError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'FluxionVioError';\n }\n}\n","import { FluxionError } from '@/common/error.js';\nimport type { FluxionOptions, NormalizedFluxionOptions } from '@/types.js';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nimport { flags } from '@/common/consts.js';\n\n/**\n * Read certificate content from a file path or return the content directly.\n * Relative paths are resolved against the current working directory.\n */\nfunction readCertificateContent(content: string | Buffer): 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.resolve(content);\n if (fs.existsSync(filePath)) {\n return fs.readFileSync(filePath);\n }\n }\n return Buffer.from(content);\n }\n throw new FluxionError('Certificate content must be a string or Buffer');\n}\n\n/**\n * Normalize HTTPS options.\n */\nfunction normalizeHttpsOptions(https: FluxionOptions['https']): NormalizedFluxionOptions['https'] | undefined {\n if (!https) {\n return undefined;\n }\n\n if (typeof https !== 'object' || https === null || Array.isArray(https)) {\n throw new FluxionError('FluxionOptions.https must be an object');\n }\n if (typeof https.key !== 'string' && !Buffer.isBuffer(https.key)) {\n throw new FluxionError('FluxionOptions.https.key must be a string or Buffer');\n }\n if (typeof https.cert !== 'string' && !Buffer.isBuffer(https.cert)) {\n throw new FluxionError('FluxionOptions.https.cert must be a string or Buffer');\n }\n\n const result: NormalizedFluxionOptions['https'] = {\n key: readCertificateContent(https.key),\n cert: readCertificateContent(https.cert),\n };\n\n if (https.ca !== undefined) {\n if (Array.isArray(https.ca)) {\n result.ca = https.ca.map((item) => readCertificateContent(item));\n } else {\n result.ca = readCertificateContent(https.ca);\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 new FluxionError('FluxionOptions must be an object');\n }\n\n // Check for deprecated 'include' option\n if ('include' in o) {\n throw new FluxionError(\n 'The \"include\" option has been removed. Please use:\\n' +\n ' - \"apiInclude\" for API handler patterns (default: [\"**/*.ts\"])\\n' +\n ' - \"staticInclude\" for static resource patterns (default: [\"**/*\"])\\n' +\n 'Example migration:\\n' +\n ' OLD: { include: [\"**/*.ts\", \"**/*.js\"], apiInclude: [\"**/*.ts\"] }\\n' +\n ' NEW: { apiInclude: [\"**/*.ts\"], staticInclude: [\"**/*.js\"] }',\n );\n }\n\n const {\n dir: rawDir,\n host,\n port: userPort,\n handlerTimeoutMs = 5000,\n middlewareTimeoutMs = 3000,\n staticResourceTimeoutMs = 3 * 60 * 1000,\n target = `node${process.versions.node.split('.')[0]}`,\n maxRequestBytes = 8_000_000,\n apiInclude = ['**/*.ts'],\n staticInclude = ['**/*'],\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 csp = \"default-src 'self'\",\n restartMemoryMB = false,\n logOutputFile: rawLogOutputFile,\n } = o as FluxionOptions;\n\n const port = userPort;\n\n const logger = o.logger ?? 'json-line';\n if (logger !== 'json-line' && typeof logger !== 'function') {\n throw new FluxionError(\n `Invalid logger option, Must be 'json-line' or a custom logger function (write a one-line logger yourself)`,\n );\n }\n\n if (typeof rawDir !== 'string') {\n throw new FluxionError('FluxionOptions.dir must be a string');\n }\n // Ends with a separator, so \"is this file under dir\" is a plain `startsWith`\n const resolvedDir = path.resolve(process.cwd(), rawDir);\n const dir = resolvedDir.endsWith(path.sep) ? resolvedDir : resolvedDir + path.sep;\n\n if (typeof target !== 'string' && !(Array.isArray(target) && target.every((t) => typeof t === 'string'))) {\n throw new FluxionError('FluxionOptions.target must be a string or an array of strings');\n }\n\n if (typeof host !== 'string') {\n throw new FluxionError('FluxionOptions.host must be a string');\n }\n\n if (!Number.isSafeInteger(handlerTimeoutMs) || handlerTimeoutMs <= 100) {\n throw new FluxionError('FluxionOptions.handlerTimeoutMs must be an integer greater than 100');\n }\n\n if (!Number.isSafeInteger(middlewareTimeoutMs) || middlewareTimeoutMs <= 100) {\n throw new FluxionError('FluxionOptions.middlewareTimeoutMs must be an integer greater than 100');\n }\n\n if (typeof port !== 'number' || !Number.isSafeInteger(port)) {\n throw new FluxionError('FluxionOptions.port must be a positive integer');\n }\n\n if (port <= 1 || port > 65535) {\n throw new FluxionError('FluxionOptions.port must be 1 ~ 65535');\n }\n\n if (typeof maxRequestBytes !== 'number' || maxRequestBytes <= 0 || !Number.isSafeInteger(maxRequestBytes)) {\n throw new FluxionError('FluxionOptions.maxRequestBytes must be a positive integer');\n }\n\n if (csp !== false && typeof csp !== 'string') {\n throw new FluxionError('FluxionOptions.csp must be a string or false');\n }\n\n if (restartMemoryMB !== false) {\n if (typeof restartMemoryMB !== 'number' || !Number.isFinite(restartMemoryMB) || restartMemoryMB <= 0) {\n throw new FluxionError('FluxionOptions.restartMemoryMB must be a positive number of MB, or false');\n }\n }\n\n if (rawLogOutputFile !== undefined && (typeof rawLogOutputFile !== 'string' || rawLogOutputFile.length === 0)) {\n throw new FluxionError('FluxionOptions.logOutputFile must be a non-empty string path');\n }\n // Relative paths follow `dir` and `https`: resolved against the cwd.\n const logOutputFile = rawLogOutputFile === undefined ? undefined : path.resolve(process.cwd(), rawLogOutputFile);\n\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n\n const result: NormalizedFluxionOptions = {\n dir,\n host,\n port,\n handlerTimeoutMs,\n middlewareTimeoutMs,\n staticResourceTimeoutMs,\n target,\n maxRequestBytes,\n logger,\n logOutputFile,\n apiInclude,\n staticInclude,\n exclude,\n https: normalizeHttpsOptions(https),\n csp,\n restartMemoryMB,\n normalizedFlag: flags.OptionsNormalized,\n };\n\n if (process.argv.includes('--verbose')) {\n console.log('normalized options', { ...result, https: result.https ? 'set' : 'unset' });\n }\n\n return result;\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 (e) {\n reject(e);\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","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","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 let count = 0;\n const MAX_COOKIE_KEYS = 100;\n\n for (const pair of pairs) {\n if (count >= MAX_COOKIE_KEYS) {\n break;\n }\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 count++;\n }\n\n return cookies;\n}\n\n/**\n * Serialize an object into a Cookie header string\n */\nexport function serializeCookie(\n name: string,\n value: string,\n 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 },\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, FluxionRequest, NormalizedModule } from '../types.js';\nimport { HttpCode, flags, ModuleType } from '@/common/consts.js';\nimport { PromiseTry } from '@/common/promise-try.js';\nimport { formatError } from '@/common/logger.js';\n\nimport { getRealIp } from './headers.js';\nimport { toURL } from './request.js';\nimport { safeSendJson } from './respond.js';\nimport { parseBody, type BodyPreview } from './body.js';\nimport { parseQuery } from './query.js';\nimport { parseCookie } from './cookie.js';\nimport { HttpException } from './exceptions.js';\n\nconst waiter = (mainPromise: Promise<any>, timeoutMs: number, flag: symbol) =>\n Promise.race([mainPromise, new Promise((r) => setTimeout(() => r(flag), timeoutMs))]);\n\n/**\n * Run a module's disposer once the response has been sent.\n * Modules without a disposer are left untouched.\n */\nfunction disposeAfterResponse(cx: FluxionContext, res: http.ServerResponse, m: NormalizedModule) {\n // The module instance is shared by requests while its files are unchanged: the disposer runs once per response.\n if (typeof m.disposer !== 'function') {\n return;\n }\n const disposer = m.disposer;\n res.once('close', () => {\n PromiseTry(disposer).catch((e: unknown) => {\n cx.logger.error({ event: 'DisposerFailed', url: m.absolutePath, error: formatError(e) });\n });\n });\n}\n\nexport function createServer(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: FluxionRequest = {\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 // Security headers for all responses\n res.setHeader('X-Content-Type-Options', 'nosniff');\n res.setHeader('X-Frame-Options', 'DENY');\n res.setHeader('X-XSS-Protection', '1; mode=block');\n // Content-Security-Policy (user-configurable, header omitted when set to false)\n if (cx.options.csp !== false) {\n res.setHeader('Content-Security-Policy', cx.options.csp);\n }\n\n let bodyPreview: BodyPreview = {\n exists: false,\n bytes: 0,\n truncated: false,\n };\n\n cx.logger.core({ event: 'request', method, ip, path: url.pathname });\n\n const start = performance.now();\n res.once('finish', () => {\n const o: Record<string, unknown> = {\n event: '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.core(o);\n });\n\n // * Start request handling\n try {\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(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 // Clean up this request's module instance after the response is sent.\n disposeAfterResponse(cx, res, m);\n\n const timeoutMs =\n m.type === ModuleType.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 flags.MiddlewareTimeout,\n );\n\n if (result === flags.MiddlewareTimeout) {\n cx.logger.warn({\n event: '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 flags.HandlerTimeout,\n );\n\n if (result === flags.HandlerTimeout) {\n cx.logger.warn({ event: 'HandlerTimeout', method: normalized.method, ip: normalized.ip });\n safeSendJson(res, { message: 'Handler timed out' }, HttpCode.InternalServerError);\n return;\n }\n\n if (result !== flags.StaticHandled) {\n safeSendJson(res, result);\n }\n } catch (e) {\n if (e instanceof HttpException) {\n cx.logger.error({\n ...normalized,\n event: 'RequestFailed',\n error: e.message,\n });\n safeSendJson(res, { message: e.message }, e.errno);\n } else {\n cx.logger.error({\n ...normalized,\n event: 'RequestFailed',\n error: formatError(e),\n });\n safeSendJson(res, { message: 'Internal Server Error' }, HttpCode.InternalServerError);\n }\n }\n };\n\n const server = cx.options.https\n ? https.createServer(\n {\n key: cx.options.https.key,\n cert: cx.options.https.cert,\n ca: cx.options.https.ca,\n },\n requestHandler,\n )\n : http.createServer(requestHandler);\n\n return new Promise((resolve, reject) => {\n let listening = false;\n\n server.on('close', () => {\n cx.logger.core({\n event: 'ServerClosed',\n host: cx.options.host,\n port: cx.options.port,\n });\n });\n\n server.once('listening', () => {\n listening = true;\n cx.logger.core({\n event: 'FluxionStarted',\n version: '__VERSION__',\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.core({\n event: 'DynamicDirectory',\n directory: cx.options.dir,\n });\n resolve(server);\n });\n\n server.on('error', (e) => {\n cx.logger.error({\n event: 'ServerError',\n error: formatError(e),\n });\n if (listening) {\n // Server encountered an error after binding — log and let the\n // caller (PM2 / user code) decide how to recover instead of\n // forcing process.exit(1) here.\n return;\n }\n reject(e);\n });\n\n server.listen(cx.options.port, cx.options.host);\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 { buildSync } from 'esbuild';\nimport { compileFunction } from 'node:vm';\nimport { createRequire } from 'node:module';\nimport { readFileSync, statSync } from 'node:fs';\nimport { dirname, extname, isAbsolute, join, resolve, sep } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport type { NormalizedFluxionOptions } from '@/types.js';\n\n/**\n * A module loaded by `FluxionLoader`. `deps` holds the instances it required, so a stale dependency shows up\n * even when the cache has already been refilled.\n */\nexport interface LoadedModule {\n id: string;\n filename: string;\n exports: any;\n mtimeMs: number;\n size: number;\n deps: Map<string, LoadedModule>;\n}\n\n/**\n * Every loaded module, keyed by absolute path, shared by all fluxion instances. Delete an entry to force a reload.\n */\nexport const moduleCache = new Map<string, LoadedModule>();\n\nconst NODE_MODULES = `${sep}node_modules${sep}`;\n\nconst COMPILED = new Set(['.ts', '.tsx', '.mts', '.cts', '.js', '.jsx', '.mjs', '.cjs']);\nconst EXTENSIONS = [...COMPILED, '.json'];\n// By TypeScript convention `import './a.js'` points at `a.ts`\nconst JS_TO_TS: Record<string, string[]> = {\n '.js': ['.ts', '.tsx'],\n '.jsx': ['.tsx'],\n '.mjs': ['.mts'],\n '.cjs': ['.cts'],\n};\n\nconst isBare = (specifier: string) => !specifier.startsWith('.') && !isAbsolute(specifier);\n\nconst isChanged = (m: LoadedModule) => {\n try {\n const s = statSync(m.filename);\n return s.mtimeMs !== m.mtimeMs || s.size !== m.size;\n } catch {\n // gone counts as changed\n return true;\n }\n};\n\nconst isFile = (path: string) => {\n try {\n return statSync(path).isFile();\n } catch {\n return false;\n }\n};\n\n/**\n * Find the real file behind an extension-less or `.js`-style path, the way esbuild would.\n */\nconst findFile = (base: string) => {\n if (isFile(base)) {\n return base;\n }\n\n const ext = extname(base);\n const stem = base.slice(0, base.length - ext.length);\n for (const e of JS_TO_TS[ext] ?? []) {\n if (isFile(stem + e)) {\n return stem + e;\n }\n }\n for (const e of EXTENSIONS) {\n if (isFile(base + e)) {\n return base + e;\n }\n }\n for (const e of EXTENSIONS) {\n if (isFile(join(base, 'index' + e))) {\n return join(base, 'index' + e);\n }\n }\n};\n\n/**\n * Load a ts/js file under `options.dir`. Local files it imports are loaded the same way, each compiled by esbuild on\n * its own; node_modules, builtins and files outside `dir` go through the native `require`.\n *\n * A file is loaded fresh the first time and its mtime and size are recorded together with those of every file it\n * required. Asking again compares them: if any one differs, the whole chain is dropped and loaded fresh, otherwise\n * the cached exports are returned. Content is never compared.\n *\n * Synchronous, because `require` is. `require()` returns `exports`, so `export default x` is `result.default`.\n */\nexport class FluxionLoader {\n private readonly opts: Pick<NormalizedFluxionOptions, 'dir' | 'target'>;\n\n constructor(options: Pick<NormalizedFluxionOptions, 'dir' | 'target'>) {\n this.opts = options;\n }\n\n private compile(file: string) {\n const { outputFiles } = buildSync({\n entryPoints: [file],\n bundle: false,\n write: false,\n platform: 'node',\n format: 'cjs',\n target: this.opts.target,\n sourcemap: 'inline',\n sourcesContent: false,\n logLevel: 'silent',\n // `import(x)` becomes `require(x)`, because a vm function has no way to run a native dynamic import\n supported: { 'dynamic-import': false },\n define: {\n 'import.meta.url': JSON.stringify(pathToFileURL(file).href),\n 'import.meta.filename': JSON.stringify(file),\n 'import.meta.dirname': JSON.stringify(dirname(file)),\n },\n });\n return outputFiles[0].text;\n }\n\n require(absolutePath: string): any {\n return this.load(resolve(absolutePath)).exports;\n }\n\n private isHot(file: string) {\n const ext = extname(file);\n return file.startsWith(this.opts.dir) && !file.includes(NODE_MODULES) && (COMPILED.has(ext) || ext === '.json');\n }\n\n /**\n * Return the cached module if nothing in its chain changed, otherwise drop the chain and load it fresh.\n */\n private load(file: string): LoadedModule {\n const cached = moduleCache.get(file);\n if (cached === undefined) {\n return this.loadFresh(file);\n }\n\n const chain = this.collectChain(cached);\n if (!this.anyStale(chain)) {\n return cached;\n }\n for (const m of chain) {\n // A newer instance of the same path may have been loaded by someone else meanwhile: that one is fine, keep it\n if (moduleCache.get(m.filename) === m) {\n moduleCache.delete(m.filename);\n }\n }\n return this.loadFresh(file);\n }\n\n private collectChain(root: LoadedModule) {\n const chain = new Set([root]);\n // a Set visits what is added while iterating, and the `has` check built into it stops cycles\n for (const m of chain) {\n for (const dep of m.deps.values()) {\n chain.add(dep);\n }\n }\n return chain;\n }\n\n private anyStale(chain: Set<LoadedModule>) {\n for (const m of chain) {\n if (moduleCache.get(m.filename) !== m || isChanged(m)) {\n return true;\n }\n }\n return false;\n }\n\n private loadFresh(file: string): LoadedModule {\n // Read before the file is, so an edit in between is seen as a change next time instead of being missed\n const { mtimeMs, size } = statSync(file);\n const mod: LoadedModule = { id: file, filename: file, exports: {}, mtimeMs, size, deps: new Map() };\n // Registered before it runs, so a circular import sees the partial exports instead of looping\n moduleCache.set(file, mod);\n try {\n this.evaluate(file, mod);\n } catch (e) {\n moduleCache.delete(file);\n throw e;\n }\n return mod;\n }\n\n private evaluate(file: string, mod: LoadedModule) {\n const ext = extname(file);\n if (ext === '.json') {\n mod.exports = JSON.parse(readFileSync(file, 'utf8'));\n return;\n }\n if (!COMPILED.has(ext)) {\n throw new Error(`FluxionLoader: unsupported file type \"${ext}\" (${file})`);\n }\n\n const fn = compileFunction(this.compile(file), ['exports', 'require', 'module', '__filename', '__dirname'], {\n filename: file,\n });\n fn.call(mod.exports, mod.exports, this.createModuleRequire(mod), mod, file, dirname(file));\n }\n\n /**\n * The only closure left per module, and it is made once per fresh load: it has to remember which module is asking.\n */\n private createModuleRequire(from: LoadedModule) {\n const native = createRequire(from.filename);\n const locate = (specifier: string) =>\n isBare(specifier) ? undefined : findFile(resolve(dirname(from.filename), specifier));\n\n const req = (specifier: string) => {\n const file = locate(specifier);\n if (file === undefined) {\n // builtins, packages and anything that cannot be found; the native require reports the latter\n return native(specifier);\n }\n if (!this.isHot(file)) {\n return native(file);\n }\n const dep = this.load(file);\n // Also recorded for requires made long after loading (`import()` inside a function)\n from.deps.set(file, dep);\n return dep.exports;\n };\n req.resolve = (specifier: string) => locate(specifier) ?? native.resolve(specifier);\n req.cache = native.cache;\n return req;\n }\n}\n","import { FluxionError } from './error.js';\nimport type { FluxionContext, NormalizedModule } from '@/types.js';\nimport type { Stats } from 'node:fs';\nimport { static_cast } from 'type-narrow';\nimport { ModuleType } from './consts.js';\nimport { FluxionLoader } from './loader.js';\n\nfunction isFluxionModule(cx: Pick<FluxionContext, 'options' | 'logger'>, o: unknown): o is NormalizedModule {\n if (typeof o !== 'object' || o === null) {\n return false;\n }\n\n static_cast<NormalizedModule>(o);\n\n if (typeof o.handler !== 'function') {\n cx.logger.error({ event: 'InvalidModule', error: 'handler must be a function' });\n return false;\n }\n\n if (o.disposer !== undefined && typeof o.disposer !== 'function') {\n cx.logger.error({ event: 'InvalidModule', 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({ event: 'InvalidModule', error: 'handlerTimeoutMs must be an integer >= 100 if provided' });\n return false;\n }\n\n if (o.type !== ModuleType.Api) {\n cx.logger.error({ event: 'InvalidModule', 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 absolutePath: string,\n stat: Stats,\n): NormalizedModule {\n const loader = new FluxionLoader(cx.options);\n\n let m = loader.require(absolutePath);\n if (isFluxionModule(cx, m.default)) {\n m = m.default;\n } else if (isFluxionModule(cx, m)) {\n } else {\n throw new FluxionError(\n `Invalid handler module '${absolutePath}', make sure it satisfies defineFluxionModule(...) helper`,\n );\n }\n\n m.absolutePath = absolutePath;\n m.mtimeMs = stat.mtimeMs;\n\n return m;\n}\n","import type { FluxionContext, NormalizedModule } from '../types.js';\nimport { createReadStream } from 'node:fs';\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\nimport { minimatch } from 'minimatch';\nimport { ModuleType, STATIC_CONTENT_TYPES, flags } from '@/common/consts.js';\nimport { loadFluxionModule } from '@/common/injector.js';\n\n/**\n * Resolve a request URL to the module that should handle it.\n * Nothing is registered: the file is looked up on every call. API modules are reloaded only when the file or one\n * of the files it imports has changed (mtime or size).\n */\nexport type FluxionRouter = (url: URL) => Promise<NormalizedModule | undefined>;\n\nexport function createRouter(cx: Pick<FluxionContext, 'options' | 'logger'>): FluxionRouter {\n return async (url) => {\n const relativePath = url.pathname.replace(/^[/]+/, '').replace(/[/]+$/, '');\n const absolutePath = path.join(cx.options.dir, relativePath);\n\n // ! Fail if the resolved path escapes the configured directory\n const withinDir = path.relative(cx.options.dir, absolutePath);\n if (withinDir.startsWith('..') || path.isAbsolute(withinDir)) {\n return undefined;\n }\n\n // Excluded files are never served\n if (cx.options.exclude.some((p) => minimatch(relativePath, p))) {\n return undefined;\n }\n\n // The file must exist and be a regular file\n const stat = await fs.stat(absolutePath).catch(() => undefined);\n if (!stat || !stat.isFile()) {\n return undefined;\n }\n\n if (cx.options.apiInclude.some((p) => minimatch(relativePath, p))) {\n return loadFluxionModule(cx, absolutePath, stat);\n }\n\n if (cx.options.staticInclude.some((p) => minimatch(relativePath, p))) {\n return makeStaticResource(absolutePath);\n }\n\n return undefined;\n };\n}\n\nfunction makeStaticResource(absolutePath: string): NormalizedModule {\n return {\n type: ModuleType.StaticResource,\n mtimeMs: NaN,\n absolutePath,\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 const stat = await fs.stat(absolutePath).catch((e: Error) => e);\n if (stat instanceof Error) {\n res.statusCode = 404;\n res.end('Not Accessible');\n cx.logger.error({\n event: 'StaticResourceError',\n url: normalized.url.pathname,\n error: stat.message,\n });\n return null;\n }\n\n if (!stat.isFile()) {\n res.statusCode = 404;\n res.end('Not Found');\n return;\n }\n\n const extension = path.extname(absolutePath).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 = createReadStream(absolutePath);\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(flags.StaticHandled);\n };\n\n const onClientClose = () => {\n cleanup();\n stream.destroy();\n resolve(flags.StaticHandled);\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","import type { FluxionContext, FluxionOptions, NormalizedFluxionOptions } from './types.js';\n\nimport { createLogger } from './common/logger.js';\nimport { redirectOutput } from './common/output.js';\nimport { flags } from './common/consts.js';\nimport { supportsInPlaceRestart, watchMemory } from './common/restart.js';\nimport { defineFluxionOptions } from './defines/options.js';\nimport { createServer } from './http/server.js';\nimport { createRouter } from './router/index.js';\n\n/**\n * Creates a fluxion-vio server instance.\n * @package Fluxion Vio\n * @version __VERSION__\n */\nexport async function fluxion(options: FluxionOptions | NormalizedFluxionOptions) {\n const alreadyNormalized = (options as NormalizedFluxionOptions).normalizedFlag === flags.OptionsNormalized;\n const context = { options: alreadyNormalized ? options : defineFluxionOptions(options) } as FluxionContext;\n\n // Before anything is logged, so a startup failure (a busy port) lands in the file too.\n if (context.options.logOutputFile) {\n await redirectOutput(context.options.logOutputFile);\n }\n\n // A restart must drain the log before execve replaces the image, so the file\n // sink is flushed explicitly instead of closing itself. On runtimes without\n // `process.execve` the option is ignored, so there is nothing to flush for.\n const restartable = context.options.restartMemoryMB !== false && supportsInPlaceRestart();\n context.logger = createLogger(context as Pick<FluxionContext, 'options'>, { deferClose: restartable });\n context.router = createRouter(context as Pick<FluxionContext, 'options' | 'logger'>);\n\n // Start HTTP server\n const server = await createServer(context);\n\n if (context.options.restartMemoryMB !== false && !restartable) {\n context.logger.warn({\n event: 'RestartDisabled',\n reason: 'process.execve is not available on this Node.js version',\n requiredVersion: '22.15',\n nodeVersion: process.version,\n restartMemoryMB: context.options.restartMemoryMB,\n });\n }\n\n const restarter = watchMemory({\n restartMemoryMB: restartable ? context.options.restartMemoryMB : false,\n server,\n logger: context.logger,\n });\n\n // Register signal handlers for graceful shutdown\n const shutdown = (signal: NodeJS.Signals) => {\n restarter.stop();\n context.logger.warn({ event: 'ShuttingDown', pid: process.pid, signal });\n server.close(async () => {\n await context.logger.flush();\n process.exit(0);\n });\n };\n\n process.once('SIGINT', () => shutdown('SIGINT'));\n process.once('SIGTERM', () => shutdown('SIGTERM'));\n\n // ! This must stay the last step: the memory monitor's startup grace period\n // is measured from here, giving the caller's own initialization (database\n // connections, caches) time to settle before memory is judged.\n restarter.start();\n}\n","import { FluxionError } from '@/common/error.js';\nimport type {\n FluxionHandler,\n FluxionDisposer,\n FluxionModule,\n FluxionMiddleware,\n FluxionModuleObject,\n} from '@/types.js';\nimport type { FluxionLoggerFn } from '@/common/logger.js';\nimport { ModuleType } from '@/common/consts.js';\n\nexport { defineFluxionOptions } from './options.js';\n\n/**\n * Use handler function and optional disposer function to define a Fluxion module.\n * @param handler Main function that handles request and response instances\n * @param disposer Deal with resource cleanup once the response has been sent. Optional.\n */\nexport function defineFluxionModule(handler: FluxionHandler, disposer?: FluxionDisposer): FluxionModuleObject;\n/**\n * Provides type safety for defining Fluxion modules.\n */\nexport function defineFluxionModule(fluxionModule: FluxionModule): FluxionModuleObject;\nexport function defineFluxionModule(\n a: FluxionModule | FluxionHandler,\n disposer?: FluxionDisposer,\n): FluxionModuleObject {\n if (typeof a === 'function') {\n if (disposer !== undefined && typeof disposer !== 'function') {\n throw new FluxionError(`Invalid disposer, expected a function but got ${typeof disposer}`);\n }\n return { handler: a, disposer, type: ModuleType.Api };\n }\n\n if (typeof a !== 'object' || a === null) {\n throw new FluxionError(\n `Invalid argument, expected a FluxionModule object or a handler function, but got ${typeof a}`,\n );\n }\n\n if (typeof a.handler !== 'function') {\n throw new FluxionError(`Invalid FluxionModule, \"handler\" must be a function`);\n }\n\n if (a.disposer !== undefined && typeof a.disposer !== 'function') {\n throw new FluxionError(`Invalid FluxionModule, \"disposer\" must be a function if provided`);\n }\n\n if (a.methods !== undefined && (!Array.isArray(a.methods) || a.methods.some((v) => typeof v !== 'string'))) {\n throw new FluxionError(`Invalid FluxionModule, \"methods\" must be an array of strings if provided`);\n }\n\n if (\n a.middlewares !== undefined &&\n (!Array.isArray(a.middlewares) || a.middlewares.some((v) => typeof v !== 'function'))\n ) {\n throw new FluxionError(`Invalid FluxionModule, \"middlewares\" must be an array of functions if provided`);\n }\n\n return { ...a, type: ModuleType.Api };\n}\n\nexport function defineFluxionMiddleware(middleware: FluxionMiddleware): FluxionMiddleware {\n if (typeof middleware !== 'function') {\n throw new FluxionError(`Invalid FluxionMiddleware, expected a function but got ${typeof middleware}`);\n }\n return middleware;\n}\n\nexport function defineFluxionLogger(loggerFn: FluxionLoggerFn) {\n if (typeof loggerFn !== 'function') {\n throw new FluxionError(`Invalid FluxionLoggerFn, expected a function but got ${typeof loggerFn}`);\n }\n return loggerFn;\n}\n","import { fluxion } from './fluxion.js';\nimport { HttpCode } from './common/consts.js';\n\nexport { fluxion, HttpCode };\n\n/**\n * Export fluxion version for users' debugging.\n * @version __VERSION__\n */\nexport const VERSION = '__VERSION__';\n\n// Export all HTTP exceptions\nexport {\n HttpException,\n BadRequestException,\n UnauthorizedException,\n ForbiddenException,\n NotFoundException,\n MethodNotAllowedException,\n NotAcceptableException,\n RequestTimeoutException,\n ConflictException,\n GoneException,\n PayloadTooLargeException,\n UnsupportedMediaTypeException,\n UnprocessableEntityException,\n TooManyRequestsException,\n InternalServerErrorException,\n NotImplementedException,\n BadGatewayException,\n ServiceUnavailableException,\n GatewayTimeoutException,\n} from './http/exceptions.js';\n\nexport * from './defines/index.js';\n\nexport type {\n FluxionRequest,\n FluxionModuleContext,\n FluxionDisposer,\n FluxionHandler,\n FluxionModule,\n FluxionOptions,\n} from './types.js';\n\n// export { formatError } from './common/logger.js';\nexport type { FluxionLogger, LogEntry, LogPayload, FluxionLoggerFn, LoggerOption } from './common/logger.js';\n"],"x_google_ignoreList":[15],"mappings":"+0BAsEA,MAAM,EAAmB,GACvB,OAAO,GAAY,YAAY,GAAoB,CAAC,MAAM,QAAQ,CAAO,EAKrE,EAAW,GACf,OAAO,MAAM,SAAY,WAAa,MAAM,QAAQ,CAAK,EAAI,aAAiB,MAMhF,SAAgB,EAAY,EAAoB,CAC9C,GAAI,OAAO,GAAM,UAAY,EAAY,CACvC,GAAM,CAAE,UAAS,SAAU,EACvB,EAAO,GAOX,GANI,OAAO,GAAY,UAAY,EAAQ,OAAS,IAClD,EAAO,GAEL,OAAO,GAAU,UAAY,EAAM,OAAS,IAC9C,IAAS,EAAK,OAAS,EAAI;EAAO,IAAM,GAEtC,EAAK,OAAS,EAChB,OAAO,CAEX,CACA,OAAO,OAAO,CAAC,CACjB,CAEA,MAAM,GAAgB,EAAc,IAC9B,OAAO,GAAU,SACZ,EAAM,SAAS,EAGpB,EAAQ,CAAK,EACR,EAAY,CAAK,EAEnB,EAGH,EAAgB,GACpB,KAAK,UAAU,CACb,UAAW,OAAO,EAAM,WAAc,SAAW,EAAM,UAAY,OAAO,EAAM,SAAS,EACzF,MAAO,OAAO,EAAM,OAAU,SAAW,EAAM,MAAQ,OACvD,MAAO,kBACT,CAAC,EAKG,EAAiB,GAA4B,CACjD,GAAI,CACF,OAAO,KAAK,UAAU,EAAO,CAAY,GAAK,EAAa,CAAK,CAClE,MAAQ,CACN,OAAO,EAAa,CAAK,CAC3B,CACF,EAKa,GAAmC,GAAoB,CAElE,QAAQ,IAAI,EAAc,CAAK,CAAC,CAClC,EAQA,SAAS,EAAY,EAAoC,CACvD,OAAO,IAAI,QAAS,GAAY,CAC9B,GAAI,EAAO,WAAa,EAAO,cAAe,CAC5C,EAAQ,EACR,MACF,CACA,IAAM,MAAa,CACjB,aAAa,CAAK,EAClB,EAAO,IAAI,QAAS,CAAI,EACxB,EAAQ,CACV,EACM,EAAQ,WAAW,EAAM,GAAI,EACnC,EAAM,MAAM,EACZ,EAAO,KAAK,QAAS,CAAI,EACzB,GAAI,CACF,EAAO,MAAM,GAAI,CAAI,CACvB,MAAQ,CACN,EAAK,CACP,CACF,CAAC,CACH,CAQA,SAAS,GACP,EACA,EACuD,CAEvD,GAAI,EAAA,EAAA,EAAA,WAAA,CAAY,CAAW,EAAG,CAC5B,IAAM,GAAA,EAAA,EAAA,QAAA,CAAc,CAAW,GAC3B,EAAA,EAAA,WAAA,CAAY,CAAG,IACjB,EAAA,EAAA,UAAA,CAAU,EAAK,CAAE,UAAW,EAAK,CAAC,CAEtC,CAEA,IAAM,GAAA,EAAA,EAAA,kBAAA,CAA+B,EAAa,CAAE,MAAO,IAAK,UAAW,CAAC,CAAW,CAAC,EAiBxF,MAAO,CAAE,KAfsB,GAAoB,CACjD,GAAI,CACF,EAAW,MAAM,EAAc,CAAK,EAAI;CAAI,CAC9C,MAAQ,CAER,CACF,EASe,eAPW,CACxB,MAAM,EAAY,CAAU,EACxB,GACF,MAAM,IAAI,QAAe,GAAY,EAAW,UAAY,EAAQ,CAAC,CAAC,CAE1E,CAEqB,CACvB,CAWA,SAAS,GAAkB,EAAqC,EAAiC,CAC/F,IAAM,EAAkB,QAAQ,IAAI,qBAC9B,EAAW,EAAkB,GAAe,EAAiB,CAAU,EAAI,KAE3E,EAAe,EAAG,QAAQ,OAC1B,EAAc,OAAO,GAAiB,WAAa,EAAe,GAExE,GAAI,CAAC,EACH,OAAO,EAGT,IAAM,EAAwB,GAAoB,CAChD,EAAY,CAAK,EACjB,EAAS,KAAK,CAAK,CACrB,EAEA,MADA,GAAS,MAAQ,EAAS,MACnB,CACT,CAEA,SAAgB,EACd,EACA,EAAoC,CAAC,EACd,CACvB,IAAM,EAAO,GAAkB,EAAI,EAAQ,aAAe,EAAI,EA2C9D,MAAO,CAxCL,MAAM,EAAiB,EAA2B,CAGhD,IAAM,EAAY,IAAI,KAAK,CAAA,CAAE,YAAY,EACnC,EAAkB,EAAgB,CAAO,EAC3C,CAAE,YAAW,QAAO,MAAO,EAAQ,MAAO,GAAG,CAAQ,EACrD,CAAE,YAAW,QAAO,MAAO,CAAQ,EAEvC,GAAI,CACF,EAAK,CAAK,CACZ,MAAQ,CAER,CACF,EACA,KAAK,EAA2B,CAC9B,KAAK,MAAM,OAAQ,CAAO,CAC5B,EACA,KAAK,EAA2B,CAC9B,KAAK,MAAM,OAAQ,CAAO,CAC5B,EACA,MAAM,EAA2B,CAC/B,KAAK,MAAM,QAAS,CAAO,CAC7B,EACA,KAAK,EAA2B,CAC9B,KAAK,MAAM,OAAQ,CAAO,CAC5B,EACA,MAAM,EAA2B,CAC/B,KAAK,MAAM,QAAS,CAAO,CAC7B,EACA,QAAQ,EAA2B,CACjC,KAAK,MAAM,UAAW,CAAO,CAC/B,EACA,KAAK,EAA2B,CAC9B,KAAK,MAAM,OAAQ,CAAO,CAC5B,EACA,OAAuB,CACrB,OAAO,EAAK,QAAQ,GAAK,QAAQ,QAAQ,CAC3C,CAGU,CACd,CC9QA,eAAsB,EAAe,EAAiC,CACpE,MAAA,EAAA,EAAA,MAAA,EAAA,EAAA,EAAA,QAAA,CAAoB,CAAQ,EAAG,CAAE,UAAW,EAAK,CAAC,EAElD,IAAM,EAAS,MAAA,EAAA,EAAA,KAAA,CAAW,EAAU,GAAG,EAEjC,GAAS,EAA4B,EAAoB,IAAiB,EAC9E,EAAA,EAAA,UAAA,CAAU,EAAO,GAAI,OAAO,KAAK,CAAK,CAAC,EACvC,IAAM,EAAO,OAAO,GAAa,WAAa,EAAW,EAIzD,OAHI,OAAO,GAAS,YAClB,EAAK,EAEA,EACT,EAEA,QAAQ,OAAO,MAAQ,EACvB,QAAQ,OAAO,MAAQ,CACzB,CCvBO,IAAA,mCAC4B,OAAO,4BAA4B,kBACvC,OAAO,8BAA8B,mBACpC,OAAO,wBAAwB,sBAC5B,OAAO,2BAA2B,IACpE,AAAA,IAAA,CAAA,CAAD,EAEA,MAAa,GAA+C,CAC1D,OAAQ,0BACR,QAAS,2BACT,OAAQ,2BACR,MAAO,iCACP,OAAQ,iCACR,OAAQ,iCACR,QAAS,kCACT,UAAW,qCACX,OAAQ,kCACR,OAAQ,4BACR,MAAO,+BACP,OAAQ,iCACR,OAAQ,0BACR,QAAS,kCACT,OAAQ,kCACR,QAAS,kCAET,OAAQ,eACR,OAAQ,YACR,OAAQ,aACR,QAAS,aACT,OAAQ,YACR,QAAS,aACT,QAAS,aACT,OAAQ,gBACR,OAAQ,YACR,OAAQ,aACR,QAAS,aAET,QAAS,YACT,SAAU,aACV,OAAQ,WACR,OAAQ,WACR,OAAQ,gCAER,OAAQ,aACR,OAAQ,YACR,OAAQ,YACR,OAAQ,YACR,OAAQ,YACR,OAAQ,YACR,QAAS,aACT,OAAQ,YACR,QAAS,aACT,OAAQ,YACR,OAAQ,kBACR,OAAQ,YAER,OAAQ,kBACR,QAAS,mBACT,OAAQ,kBACR,MAAO,mBACP,OAAQ,oBACR,OAAQ,sBACR,MAAO,8BACP,eAAgB,2CAChB,OAAQ,0BACV,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,EChGA,MAgDM,GAAa,GAAkB,GAAS,KAAO,MAKxC,OAAwC,OAAO,QAAQ,QAAW,WAM/E,SAAS,GAAY,EAAkB,EAAY,IAAiC,CAClF,OAAO,IAAI,QAAS,GAAY,CAC9B,IAAI,EAAU,GACR,MAAa,CACb,IACJ,EAAU,GACV,aAAa,CAAK,EAClB,EAAQ,EACV,EACM,EAAQ,eAAiB,CAC7B,EAAO,oBAAoB,EAC3B,EAAK,CACP,EAAG,CAAS,EACZ,EAAM,MAAM,EAEZ,EAAO,MAAM,CAAI,EACjB,EAAO,qBAAqB,CAC9B,CAAC,CACH,CAOA,MAAM,OAAoB,CAAC,QAAQ,SAAU,GAAG,QAAQ,SAAU,GAAG,QAAQ,KAAK,MAAM,CAAC,CAAC,EAW1F,eAAsB,GACpB,EACA,EACA,EAAiC,CAAC,EAClB,CACZ,OAAO,QAAQ,QAAW,aAE5B,EAAO,MAAM,CAAE,MAAO,gBAAiB,IAAK,QAAQ,IAAK,MAAO,iCAAkC,CAAC,EACnG,QAAQ,KAAK,CAAC,GAGhB,EAAO,KAAK,CAAE,MAAO,aAAc,IAAK,QAAQ,IAAK,GAAG,CAAM,CAAC,EAE/D,MAAM,GAAY,CAAM,EACxB,MAAM,EAAO,MAAM,EAEnB,GAAI,CACF,QAAQ,OAAO,QAAQ,SAAU,GAAY,EAAG,QAAQ,GAAG,CAC7D,OAAS,EAAG,CACV,EAAO,MAAM,CAAE,MAAO,gBAAiB,IAAK,QAAQ,IAAK,MAAO,EAAY,CAAC,CAAE,CAAC,CAClF,CACA,QAAQ,KAAK,CAAC,CAChB,CAUA,SAAgB,EAAY,EAAsC,CAChE,GAAM,CACJ,kBACA,SACA,SACA,iBAAiB,IACjB,kBAAkB,KAChB,EAEJ,GAAI,IAAoB,GACtB,MAAO,CAAE,UAAa,CAAC,EAAG,SAAY,CAAC,CAAE,EAG3C,IAAI,EAAa,GACb,EAEE,MAAc,CAClB,GAAI,EAAY,OAChB,IAAM,EAAQ,GAAU,QAAQ,YAAY,IAAI,CAAC,EAC7C,GAAS,IAEb,EAAa,GACT,IAAU,IAAA,KACZ,cAAc,CAAK,EACnB,EAAQ,IAAA,IAIV,GAAoB,EAAQ,EAAQ,CAAE,OAAQ,SAAU,MAAO,KAAK,MAAM,CAAK,CAAE,CAAC,EACpF,EAEA,MAAO,CACL,OAAQ,CACF,IAAU,IAAA,IAAa,IAG3B,EAAQ,eAAiB,CACvB,EAAQ,YAAY,EAAO,CAAe,EAC1C,EAAM,MAAM,EACZ,EAAM,CACR,EAAG,CAAc,EACjB,EAAM,MAAM,EACd,EACA,MAAO,CACD,IAAU,IAAA,KACZ,cAAc,CAAK,EACnB,EAAQ,IAAA,GAEZ,CACF,CACF,CC1LA,IAAa,EAAb,cAAkC,KAAM,CACtC,YAAY,EAAiB,CAC3B,MAAM,CAAO,EACb,KAAK,KAAO,iBACd,CACF,ECMA,SAAS,EAAuB,EAAkC,CAChE,GAAI,OAAO,SAAS,CAAO,EACzB,OAAO,EAET,GAAI,OAAO,GAAY,SAAU,CAG/B,GAAI,CAAC,EAAQ,WAAW,YAAY,EAAG,CACrC,IAAM,EAAWA,EAAAA,QAAK,QAAQ,CAAO,EACrC,GAAIC,EAAAA,QAAG,WAAW,CAAQ,EACxB,OAAOA,EAAAA,QAAG,aAAa,CAAQ,CAEnC,CACA,OAAO,OAAO,KAAK,CAAO,CAC5B,CACA,MAAM,IAAI,EAAa,gDAAgD,CACzE,CAKA,SAAS,GAAsB,EAA+E,CAC5G,GAAI,CAAC,EACH,OAGF,GAAI,OAAO,GAAU,WAAY,GAAkB,MAAM,QAAQ,CAAK,EACpE,MAAM,IAAI,EAAa,wCAAwC,EAEjE,GAAI,OAAO,EAAM,KAAQ,UAAY,CAAC,OAAO,SAAS,EAAM,GAAG,EAC7D,MAAM,IAAI,EAAa,qDAAqD,EAE9E,GAAI,OAAO,EAAM,MAAS,UAAY,CAAC,OAAO,SAAS,EAAM,IAAI,EAC/D,MAAM,IAAI,EAAa,sDAAsD,EAG/E,IAAM,EAA4C,CAChD,IAAK,EAAuB,EAAM,GAAG,EACrC,KAAM,EAAuB,EAAM,IAAI,CACzC,EAUA,OARI,EAAM,KAAO,IAAA,KACX,MAAM,QAAQ,EAAM,EAAE,EACxB,EAAO,GAAK,EAAM,GAAG,IAAK,GAAS,EAAuB,CAAI,CAAC,EAE/D,EAAO,GAAK,EAAuB,EAAM,EAAE,GAIxC,CACT,CAKA,SAAgB,EAAqB,EAA6C,CAChF,GAAI,OAAO,GAAM,WAAY,GAAc,MAAM,QAAQ,CAAC,EACxD,MAAM,IAAI,EAAa,kCAAkC,EAI3D,GAAI,YAAa,EACf,MAAM,IAAI,EACR;;;;;+DAMF,EAGF,GAAM,CACJ,IAAK,EACL,OACA,KAAM,EACN,mBAAmB,IACnB,sBAAsB,IACtB,0BAA0B,IAAS,IACnC,SAAS,OAAO,QAAQ,SAAS,KAAK,MAAM,GAAG,CAAC,CAAC,KACjD,kBAAkB,IAClB,aAAa,CAAC,SAAS,EACvB,gBAAgB,CAAC,MAAM,EACvB,UAAU,CACR,qBACA,aACA,aACA,cACA,gBACA,cACA,WACA,eACA,iBACA,oBACA,WACA,WACF,EACA,QACA,MAAM,qBACN,kBAAkB,GAClB,cAAe,GACb,EAEE,EAAO,EAEP,EAAS,EAAE,QAAU,YAC3B,GAAI,IAAW,aAAe,OAAO,GAAW,WAC9C,MAAM,IAAI,EACR,2GACF,EAGF,GAAI,OAAO,GAAW,SACpB,MAAM,IAAI,EAAa,qCAAqC,EAG9D,IAAM,EAAcD,EAAAA,QAAK,QAAQ,QAAQ,IAAI,EAAG,CAAM,EAChD,EAAM,EAAY,SAASA,EAAAA,QAAK,GAAG,EAAI,EAAc,EAAcA,EAAAA,QAAK,IAE9E,GAAI,OAAO,GAAW,UAAY,EAAE,MAAM,QAAQ,CAAM,GAAK,EAAO,MAAO,GAAM,OAAO,GAAM,QAAQ,GACpG,MAAM,IAAI,EAAa,+DAA+D,EAGxF,GAAI,OAAO,GAAS,SAClB,MAAM,IAAI,EAAa,sCAAsC,EAG/D,GAAI,CAAC,OAAO,cAAc,CAAgB,GAAK,GAAoB,IACjE,MAAM,IAAI,EAAa,qEAAqE,EAG9F,GAAI,CAAC,OAAO,cAAc,CAAmB,GAAK,GAAuB,IACvE,MAAM,IAAI,EAAa,wEAAwE,EAGjG,GAAI,OAAO,GAAS,UAAY,CAAC,OAAO,cAAc,CAAI,EACxD,MAAM,IAAI,EAAa,gDAAgD,EAGzE,GAAI,GAAQ,GAAK,EAAO,MACtB,MAAM,IAAI,EAAa,uCAAuC,EAGhE,GAAI,OAAO,GAAoB,UAAY,GAAmB,GAAK,CAAC,OAAO,cAAc,CAAe,EACtG,MAAM,IAAI,EAAa,2DAA2D,EAGpF,GAAI,IAAQ,IAAS,OAAO,GAAQ,SAClC,MAAM,IAAI,EAAa,8CAA8C,EAGvE,GAAI,IAAoB,KAClB,OAAO,GAAoB,UAAY,CAAC,OAAO,SAAS,CAAe,GAAK,GAAmB,GACjG,MAAM,IAAI,EAAa,0EAA0E,EAIrG,GAAI,IAAqB,IAAA,KAAc,OAAO,GAAqB,UAAY,EAAiB,SAAW,GACzG,MAAM,IAAI,EAAa,8DAA8D,EAGvF,IAAM,EAAgB,IAAqB,IAAA,GAAY,IAAA,GAAYA,EAAAA,QAAK,QAAQ,QAAQ,IAAI,EAAG,CAAgB,EAE1GC,EAAAA,QAAG,WAAW,CAAG,GACpB,EAAA,QAAG,UAAU,EAAK,CAAE,UAAW,EAAK,CAAC,EAGvC,IAAM,EAAmC,CACvC,MACA,OACA,OACA,mBACA,sBACA,0BACA,SACA,kBACA,SACA,gBACA,aACA,gBACA,UACA,MAAO,GAAsB,CAAK,EAClC,MACA,kBACA,eAAgB,EAAM,iBACxB,EAMA,OAJI,QAAQ,KAAK,SAAS,WAAW,GACnC,QAAQ,IAAI,qBAAsB,CAAE,GAAG,EAAQ,MAAO,EAAO,MAAQ,MAAQ,OAAQ,CAAC,EAGjF,CACT,CCtMA,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,EAAG,CACV,EAAO,CAAC,CACV,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,CCTA,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,CCpBA,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,EAAb,cAAwC,CAAc,CACpD,YAAY,EAAkB,YAAa,CACzC,MAAM,EAAA,IAA6B,WAAW,CAChD,CACF,EAKa,EAAb,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,EACpB,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,EAChC,EAAQ,EAGZ,IAAK,IAAM,KAAQ,EAAO,CACxB,GAAI,GAAS,IACX,MAEF,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,EAC9C,GACF,CAEA,OAAO,CACT,CCXA,MAAM,GAAU,EAA2B,EAAmB,IAC5D,QAAQ,KAAK,CAAC,EAAa,IAAI,QAAS,GAAM,eAAiB,EAAE,CAAI,EAAG,CAAS,CAAC,CAAC,CAAC,EAMtF,SAAS,GAAqB,EAAoB,EAA0B,EAAqB,CAE/F,GAAI,OAAO,EAAE,UAAa,WACxB,OAEF,IAAM,EAAW,EAAE,SACnB,EAAI,KAAK,YAAe,CACtB,EAAW,CAAQ,CAAC,CAAC,MAAO,GAAe,CACzC,EAAG,OAAO,MAAM,CAAE,MAAO,iBAAkB,IAAK,EAAE,aAAc,MAAO,EAAY,CAAC,CAAE,CAAC,CACzF,CAAC,CACH,CAAC,CACH,CAEA,SAAgB,GAAa,EAAyD,CACpF,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,EAA6B,CACjC,SACA,KACA,MACA,MAAO,EAAW,EAAI,YAAY,EAClC,KAAM,CAAC,EACP,QAAS,EAAI,QACb,OAAQ,GAAY,EAAI,QAAQ,MAA4B,EAC5D,KAAM,CAAC,CACT,EAGA,EAAI,UAAU,yBAA0B,SAAS,EACjD,EAAI,UAAU,kBAAmB,MAAM,EACvC,EAAI,UAAU,mBAAoB,eAAe,EAE7C,EAAG,QAAQ,MAAQ,IACrB,EAAI,UAAU,0BAA2B,EAAG,QAAQ,GAAG,EAGzD,IAAI,EAA2B,CAC7B,OAAQ,GACR,MAAO,EACP,UAAW,EACb,EAEA,EAAG,OAAO,KAAK,CAAE,MAAO,UAAW,SAAQ,KAAI,KAAM,EAAI,QAAS,CAAC,EAEnE,IAAM,EAAQ,YAAY,IAAI,EAC9B,EAAI,KAAK,aAAgB,CACvB,IAAM,EAA6B,CACjC,MAAO,WACP,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,IAAM,EAAS,MAAM,EAAU,EAAK,EAAW,OAAQ,EAAG,QAAQ,eAAe,EACjF,EAAW,KAAO,EAAO,KACzB,EAAc,EAAO,QAErB,IAAM,EAAI,MAAM,EAAG,OAAO,CAAG,EAC7B,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,CAGA,GAAqB,EAAI,EAAK,CAAC,EAE/B,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,EAAM,iBACR,IAEe,EAAM,kBAAmB,CACtC,EAAG,OAAO,KAAK,CACb,MAAO,oBACP,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,EAAM,cACR,EAEA,GAAI,IAAW,EAAM,eAAgB,CACnC,EAAG,OAAO,KAAK,CAAE,MAAO,iBAAkB,OAAQ,EAAW,OAAQ,GAAI,EAAW,EAAG,CAAC,EACxF,EAAa,EAAK,CAAE,QAAS,mBAAoB,EAAA,GAA+B,EAChF,MACF,CAEI,IAAW,EAAM,eACnB,EAAa,EAAK,CAAM,CAE5B,OAAS,EAAG,CACN,aAAa,GACf,EAAG,OAAO,MAAM,CACd,GAAG,EACH,MAAO,gBACP,MAAO,EAAE,OACX,CAAC,EACD,EAAa,EAAK,CAAE,QAAS,EAAE,OAAQ,EAAG,EAAE,KAAK,IAEjD,EAAG,OAAO,MAAM,CACd,GAAG,EACH,MAAO,gBACP,MAAO,EAAY,CAAC,CACtB,CAAC,EACD,EAAa,EAAK,CAAE,QAAS,uBAAwB,EAAA,GAA+B,EAExF,CACF,EAEM,EAAS,EAAG,QAAQ,MACtB,EAAA,QAAM,aACJ,CACE,IAAK,EAAG,QAAQ,MAAM,IACtB,KAAM,EAAG,QAAQ,MAAM,KACvB,GAAI,EAAG,QAAQ,MAAM,EACvB,EACA,CACF,EACA,EAAA,QAAK,aAAa,CAAc,EAEpC,OAAO,IAAI,SAAS,EAAS,IAAW,CACtC,IAAI,EAAY,GAEhB,EAAO,GAAG,YAAe,CACvB,EAAG,OAAO,KAAK,CACb,MAAO,eACP,KAAM,EAAG,QAAQ,KACjB,KAAM,EAAG,QAAQ,IACnB,CAAC,CACH,CAAC,EAED,EAAO,KAAK,gBAAmB,CAC7B,EAAY,GACZ,EAAG,OAAO,KAAK,CACb,MAAO,iBACP,QAAS,QACT,IAAK,QAAQ,IACb,SAAU,EAAG,QAAQ,MAAQ,QAAU,OACvC,KAAM,EAAG,QAAQ,KACjB,KAAM,EAAG,QAAQ,IACnB,CAAC,EACD,EAAG,OAAO,KAAK,CACb,MAAO,mBACP,UAAW,EAAG,QAAQ,GACxB,CAAC,EACD,EAAQ,CAAM,CAChB,CAAC,EAED,EAAO,GAAG,QAAU,GAAM,CACxB,EAAG,OAAO,MAAM,CACd,MAAO,cACP,MAAO,EAAY,CAAC,CACtB,CAAC,EACG,IAMJ,EAAO,CAAC,CACV,CAAC,EAED,EAAO,OAAO,EAAG,QAAQ,KAAM,EAAG,QAAQ,IAAI,CAChD,CAAC,CACH,CClPA,SAAS,GAAE,EAAG,CAAC,CAEf,MAAM,GAAI,GCsBG,EAAc,IAAI,IAEzB,GAAe,GAAGC,EAAAA,IAAI,cAAcA,EAAAA,MAEpC,EAAW,IAAI,IAAI,CAAC,MAAO,OAAQ,OAAQ,OAAQ,MAAO,OAAQ,OAAQ,MAAM,CAAC,EACjF,EAAa,CAAC,GAAG,EAAU,OAAO,EAElC,GAAqC,CACzC,MAAO,CAAC,MAAO,MAAM,EACrB,OAAQ,CAAC,MAAM,EACf,OAAQ,CAAC,MAAM,EACf,OAAQ,CAAC,MAAM,CACjB,EAEM,GAAU,GAAsB,CAAC,EAAU,WAAW,GAAG,GAAK,EAAA,EAAA,EAAA,WAAA,CAAY,CAAS,EAEnF,GAAa,GAAoB,CACrC,GAAI,CACF,IAAM,GAAA,EAAA,EAAA,SAAA,CAAa,EAAE,QAAQ,EAC7B,OAAO,EAAE,UAAY,EAAE,SAAW,EAAE,OAAS,EAAE,IACjD,MAAQ,CAEN,MAAO,EACT,CACF,EAEM,EAAU,GAAiB,CAC/B,GAAI,CACF,OAAA,EAAA,EAAA,SAAA,CAAgB,CAAI,CAAC,CAAC,OAAO,CAC/B,MAAQ,CACN,MAAO,EACT,CACF,EAKM,GAAY,GAAiB,CACjC,GAAI,EAAO,CAAI,EACb,OAAO,EAGT,IAAM,GAAA,EAAA,EAAA,QAAA,CAAc,CAAI,EAClB,EAAO,EAAK,MAAM,EAAG,EAAK,OAAS,EAAI,MAAM,EACnD,IAAK,IAAM,KAAK,GAAS,IAAQ,CAAC,EAChC,GAAI,EAAO,EAAO,CAAC,EACjB,OAAO,EAAO,EAGlB,IAAK,IAAM,KAAK,EACd,GAAI,EAAO,EAAO,CAAC,EACjB,OAAO,EAAO,EAGlB,IAAK,IAAM,KAAK,EACd,GAAI,GAAA,EAAA,EAAA,KAAA,CAAY,EAAM,QAAU,CAAC,CAAC,EAChC,OAAA,EAAA,EAAA,KAAA,CAAY,EAAM,QAAU,CAAC,CAGnC,EAYA,IAAa,GAAb,KAA2B,CACzB,KAEA,YAAY,EAA2D,CACrE,KAAK,KAAO,CACd,CAEA,QAAgB,EAAc,CAC5B,GAAM,CAAE,gBAAA,EAAA,EAAA,UAAA,CAA0B,CAChC,YAAa,CAAC,CAAI,EAClB,OAAQ,GACR,MAAO,GACP,SAAU,OACV,OAAQ,MACR,OAAQ,KAAK,KAAK,OAClB,UAAW,SACX,eAAgB,GAChB,SAAU,SAEV,UAAW,CAAE,iBAAkB,EAAM,EACrC,OAAQ,CACN,kBAAmB,KAAK,WAAA,EAAA,EAAA,cAAA,CAAwB,CAAI,CAAC,CAAC,IAAI,EAC1D,uBAAwB,KAAK,UAAU,CAAI,EAC3C,sBAAuB,KAAK,WAAA,EAAA,EAAA,QAAA,CAAkB,CAAI,CAAC,CACrD,CACF,CAAC,EACD,OAAO,EAAY,EAAE,CAAC,IACxB,CAEA,QAAQ,EAA2B,CACjC,OAAO,KAAK,MAAA,EAAA,EAAA,QAAA,CAAa,CAAY,CAAC,CAAC,CAAC,OAC1C,CAEA,MAAc,EAAc,CAC1B,IAAM,GAAA,EAAA,EAAA,QAAA,CAAc,CAAI,EACxB,OAAO,EAAK,WAAW,KAAK,KAAK,GAAG,GAAK,CAAC,EAAK,SAAS,EAAY,IAAM,EAAS,IAAI,CAAG,GAAK,IAAQ,QACzG,CAKA,KAAa,EAA4B,CACvC,IAAM,EAAS,EAAY,IAAI,CAAI,EACnC,GAAI,IAAW,IAAA,GACb,OAAO,KAAK,UAAU,CAAI,EAG5B,IAAM,EAAQ,KAAK,aAAa,CAAM,EACtC,GAAI,CAAC,KAAK,SAAS,CAAK,EACtB,OAAO,EAET,IAAK,IAAM,KAAK,EAEV,EAAY,IAAI,EAAE,QAAQ,IAAM,GAClC,EAAY,OAAO,EAAE,QAAQ,EAGjC,OAAO,KAAK,UAAU,CAAI,CAC5B,CAEA,aAAqB,EAAoB,CACvC,IAAM,EAAQ,IAAI,IAAI,CAAC,CAAI,CAAC,EAE5B,IAAK,IAAM,KAAK,EACd,IAAK,IAAM,KAAO,EAAE,KAAK,OAAO,EAC9B,EAAM,IAAI,CAAG,EAGjB,OAAO,CACT,CAEA,SAAiB,EAA0B,CACzC,IAAK,IAAM,KAAK,EACd,GAAI,EAAY,IAAI,EAAE,QAAQ,IAAM,GAAK,GAAU,CAAC,EAClD,MAAO,GAGX,MAAO,EACT,CAEA,UAAkB,EAA4B,CAE5C,GAAM,CAAE,UAAS,SAAA,EAAA,EAAA,SAAA,CAAkB,CAAI,EACjC,EAAoB,CAAE,GAAI,EAAM,SAAU,EAAM,QAAS,CAAC,EAAG,UAAS,OAAM,KAAM,IAAI,GAAM,EAElG,EAAY,IAAI,EAAM,CAAG,EACzB,GAAI,CACF,KAAK,SAAS,EAAM,CAAG,CACzB,OAAS,EAAG,CAEV,MADA,EAAY,OAAO,CAAI,EACjB,CACR,CACA,OAAO,CACT,CAEA,SAAiB,EAAc,EAAmB,CAChD,IAAM,GAAA,EAAA,EAAA,QAAA,CAAc,CAAI,EACxB,GAAI,IAAQ,QAAS,CACnB,EAAI,QAAU,KAAK,OAAA,EAAA,EAAA,aAAA,CAAmB,EAAM,MAAM,CAAC,EACnD,MACF,CACA,GAAI,CAAC,EAAS,IAAI,CAAG,EACnB,MAAU,MAAM,yCAAyC,EAAI,KAAK,EAAK,EAAE,GAM3E,EAAA,EAAA,gBAAA,CAH2B,KAAK,QAAQ,CAAI,EAAG,CAAC,UAAW,UAAW,SAAU,aAAc,WAAW,EAAG,CAC1G,SAAU,CACZ,CACC,CAAC,CAAC,KAAK,EAAI,QAAS,EAAI,QAAS,KAAK,oBAAoB,CAAG,EAAG,EAAK,GAAA,EAAA,EAAA,QAAA,CAAc,CAAI,CAAC,CAC3F,CAKA,oBAA4B,EAAoB,CAC9C,IAAM,GAAA,EAAA,EAAA,cAAA,CAAuB,EAAK,QAAQ,EACpC,EAAU,GACd,GAAO,CAAS,EAAI,IAAA,GAAY,IAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,CAAyB,EAAK,QAAQ,EAAG,CAAS,CAAC,EAE/E,EAAO,GAAsB,CACjC,IAAM,EAAO,EAAO,CAAS,EAC7B,GAAI,IAAS,IAAA,GAEX,OAAO,EAAO,CAAS,EAEzB,GAAI,CAAC,KAAK,MAAM,CAAI,EAClB,OAAO,EAAO,CAAI,EAEpB,IAAM,EAAM,KAAK,KAAK,CAAI,EAG1B,OADA,EAAK,KAAK,IAAI,EAAM,CAAG,EAChB,EAAI,OACb,EAGA,MAFA,GAAI,QAAW,GAAsB,EAAO,CAAS,GAAK,EAAO,QAAQ,CAAS,EAClF,EAAI,MAAQ,EAAO,MACZ,CACT,CACF,ECjOA,SAAS,EAAgB,EAAgD,EAAmC,CAC1G,GAAI,OAAO,GAAM,WAAY,EAC3B,MAAO,GAKT,GAFA,GAA8B,CAAC,EAE3B,OAAO,EAAE,SAAY,WAEvB,OADA,EAAG,OAAO,MAAM,CAAE,MAAO,gBAAiB,MAAO,4BAA6B,CAAC,EACxE,GAGT,GAAI,EAAE,WAAa,IAAA,IAAa,OAAO,EAAE,UAAa,WAEpD,OADA,EAAG,OAAO,MAAM,CAAE,MAAO,gBAAiB,MAAO,yCAA0C,CAAC,EACrF,GAGT,IAAM,EAAK,EAAE,iBAWb,OAVI,IAAO,IAAA,KAAc,CAAC,OAAO,cAAc,CAAE,GAAK,EAAK,MACzD,EAAG,OAAO,MAAM,CAAE,MAAO,gBAAiB,MAAO,wDAAyD,CAAC,EACpG,IAGL,EAAE,OAAA,EAKC,IAJL,EAAG,OAAO,MAAM,CAAE,MAAO,gBAAiB,MAAO,mDAAoD,CAAC,EAC/F,GAIX,CAEA,SAAgB,GACd,EACA,EACA,EACkB,CAGlB,IAAI,EAAI,IAFW,GAAc,EAAG,OAEvB,CAAC,CAAC,QAAQ,CAAY,EACnC,GAAI,EAAgB,EAAI,EAAE,OAAO,EAC/B,EAAI,EAAE,aACD,GAAI,GAAgB,EAAI,CAAC,EAE9B,MAAM,IAAI,EACR,2BAA2B,EAAa,0DAC1C,EAMF,MAHA,GAAE,aAAe,EACjB,EAAE,QAAU,EAAK,QAEV,CACT,CC5CA,SAAgB,GAAa,EAA+D,CAC1F,OAAO,KAAO,IAAQ,CACpB,IAAM,EAAe,EAAI,SAAS,QAAQ,QAAS,EAAE,CAAC,CAAC,QAAQ,QAAS,EAAE,EACpE,EAAeC,EAAAA,QAAK,KAAK,EAAG,QAAQ,IAAK,CAAY,EAGrD,EAAYA,EAAAA,QAAK,SAAS,EAAG,QAAQ,IAAK,CAAY,EAM5D,GALI,EAAU,WAAW,IAAI,GAAKA,EAAAA,QAAK,WAAW,CAAS,GAKvD,EAAG,QAAQ,QAAQ,KAAM,IAAA,EAAA,EAAA,UAAA,CAAgB,EAAc,CAAC,CAAC,EAC3D,OAIF,IAAM,EAAO,MAAMC,EAAAA,QAAG,KAAK,CAAY,CAAC,CAAC,UAAY,IAAA,EAAS,EAC1D,MAAC,GAAQ,CAAC,EAAK,OAAO,GAI1B,IAAI,EAAG,QAAQ,WAAW,KAAM,IAAA,EAAA,EAAA,UAAA,CAAgB,EAAc,CAAC,CAAC,EAC9D,OAAO,GAAkB,EAAI,EAAc,CAAI,EAGjD,GAAI,EAAG,QAAQ,cAAc,KAAM,IAAA,EAAA,EAAA,UAAA,CAAgB,EAAc,CAAC,CAAC,EACjE,OAAO,GAAmB,CAAY,CAJS,CAQnD,CACF,CAEA,SAAS,GAAmB,EAAwC,CAClE,MAAO,CACL,KAAA,EACA,QAAS,IACT,eACA,QAAS,MAAO,EAAY,EAAI,EAAK,IAAQ,CAC3C,GAAI,EAAW,SAAW,OAAS,EAAW,SAAW,OAAQ,CAC/D,EAAI,WAAa,IACjB,EAAI,UAAU,QAAS,WAAW,EAClC,EAAI,IAAI,EACR,MACF,CAEA,IAAM,EAAO,MAAMA,EAAAA,QAAG,KAAK,CAAY,CAAC,CAAC,MAAO,GAAa,CAAC,EAC9D,GAAI,aAAgB,MAQlB,MAPA,GAAI,WAAa,IACjB,EAAI,IAAI,gBAAgB,EACxB,EAAG,OAAO,MAAM,CACd,MAAO,sBACP,IAAK,EAAW,IAAI,SACpB,MAAO,EAAK,OACd,CAAC,EACM,KAGT,GAAI,CAAC,EAAK,OAAO,EAAG,CAClB,EAAI,WAAa,IACjB,EAAI,IAAI,WAAW,EACnB,MACF,CAGA,IAAM,EAAc,GADFD,EAAAA,QAAK,QAAQ,CAAY,CAAC,CAAC,YACI,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,GAAA,EAAA,EAAA,iBAAA,CAA0B,CAAY,EAEtC,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,EAAM,aAAa,CAC7B,EAEM,MAAsB,CAC1B,EAAQ,EACR,EAAO,QAAQ,EACf,EAAQ,EAAM,aAAa,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,CChHA,eAAsB,GAAQ,EAAoD,CAEhF,IAAM,EAAU,CAAE,QADS,EAAqC,iBAAmB,EAAM,kBAC1C,EAAU,EAAqB,CAAO,CAAE,EAGnF,EAAQ,QAAQ,eAClB,MAAM,EAAe,EAAQ,QAAQ,aAAa,EAMpD,IAAM,EAAc,EAAQ,QAAQ,kBAAoB,IAAS,GAAuB,EACxF,EAAQ,OAAS,EAAa,EAA4C,CAAE,WAAY,CAAY,CAAC,EACrG,EAAQ,OAAS,GAAa,CAAqD,EAGnF,IAAM,EAAS,MAAM,GAAa,CAAO,EAErC,EAAQ,QAAQ,kBAAoB,IAAS,CAAC,GAChD,EAAQ,OAAO,KAAK,CAClB,MAAO,kBACP,OAAQ,0DACR,gBAAiB,QACjB,YAAa,QAAQ,QACrB,gBAAiB,EAAQ,QAAQ,eACnC,CAAC,EAGH,IAAM,EAAY,EAAY,CAC5B,gBAAiB,EAAc,EAAQ,QAAQ,gBAAkB,GACjE,SACA,OAAQ,EAAQ,MAClB,CAAC,EAGK,EAAY,GAA2B,CAC3C,EAAU,KAAK,EACf,EAAQ,OAAO,KAAK,CAAE,MAAO,eAAgB,IAAK,QAAQ,IAAK,QAAO,CAAC,EACvE,EAAO,MAAM,SAAY,CACvB,MAAM,EAAQ,OAAO,MAAM,EAC3B,QAAQ,KAAK,CAAC,CAChB,CAAC,CACH,EAEA,QAAQ,KAAK,aAAgB,EAAS,QAAQ,CAAC,EAC/C,QAAQ,KAAK,cAAiB,EAAS,SAAS,CAAC,EAKjD,EAAU,MAAM,CAClB,CC5CA,SAAgB,GACd,EACA,EACqB,CACrB,GAAI,OAAO,GAAM,WAAY,CAC3B,GAAI,IAAa,IAAA,IAAa,OAAO,GAAa,WAChD,MAAM,IAAI,EAAa,iDAAiD,OAAO,GAAU,EAE3F,MAAO,CAAE,QAAS,EAAG,WAAU,KAAA,CAAqB,CACtD,CAEA,GAAI,OAAO,GAAM,WAAY,EAC3B,MAAM,IAAI,EACR,oFAAoF,OAAO,GAC7F,EAGF,GAAI,OAAO,EAAE,SAAY,WACvB,MAAM,IAAI,EAAa,qDAAqD,EAG9E,GAAI,EAAE,WAAa,IAAA,IAAa,OAAO,EAAE,UAAa,WACpD,MAAM,IAAI,EAAa,kEAAkE,EAG3F,GAAI,EAAE,UAAY,IAAA,KAAc,CAAC,MAAM,QAAQ,EAAE,OAAO,GAAK,EAAE,QAAQ,KAAM,GAAM,OAAO,GAAM,QAAQ,GACtG,MAAM,IAAI,EAAa,0EAA0E,EAGnG,GACE,EAAE,cAAgB,IAAA,KACjB,CAAC,MAAM,QAAQ,EAAE,WAAW,GAAK,EAAE,YAAY,KAAM,GAAM,OAAO,GAAM,UAAU,GAEnF,MAAM,IAAI,EAAa,gFAAgF,EAGzG,MAAO,CAAE,GAAG,EAAG,KAAA,CAAqB,CACtC,CAEA,SAAgB,GAAwB,EAAkD,CACxF,GAAI,OAAO,GAAe,WACxB,MAAM,IAAI,EAAa,0DAA0D,OAAO,GAAY,EAEtG,OAAO,CACT,CAEA,SAAgB,GAAoB,EAA2B,CAC7D,GAAI,OAAO,GAAa,WACtB,MAAM,IAAI,EAAa,wDAAwD,OAAO,GAAU,EAElG,OAAO,CACT"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["path","fs","sep","path","fs"],"sources":["../src/common/logger.ts","../src/common/output.ts","../src/common/consts.ts","../src/common/restart.ts","../src/common/error.ts","../src/defines/options.ts","../src/common/promise-try.ts","../src/http/headers.ts","../src/http/request.ts","../src/http/respond.ts","../src/http/query.ts","../src/http/exceptions.ts","../src/http/body.ts","../src/http/cookie.ts","../src/http/server.ts","../node_modules/.pnpm/type-narrow@0.2.2/node_modules/type-narrow/dist/index.mjs","../src/common/loader.ts","../src/common/injector.ts","../src/router/index.ts","../src/fluxion.ts","../src/defines/index.ts","../src/index.ts"],"sourcesContent":["import type { FluxionContext } from '@/types.js';\nimport type { otherstring } from '@/types.js';\nimport type { WriteStream } from 'node:fs';\nimport { createWriteStream, existsSync } from 'node:fs';\nimport { dirname } from 'node:path';\nimport { mkdirSync } from 'node:fs';\n\ntype LogLevel = 'CORE' | 'INFO' | 'WARN' | 'ERROR' | 'SUCC' | 'DEBUG' | 'VERBOSE' | otherstring;\n\nexport interface LogEntry {\n timestamp: string;\n level: LogLevel;\n event?: unknown;\n [key: string]: unknown;\n}\n\n/**\n * `'json-line'` writes one JSON object per line. `one-line` is intentionally\n * not built in: write your own function with {@link defineFluxionLogger}.\n */\nexport type LoggerOption = 'json-line' | FluxionLoggerFn;\n\nexport type FluxionLoggerFn = (entry: LogEntry) => void;\n\n/**\n * A log payload is either a plain object, whose fields are merged into the\n * entry, or a primitive, which becomes the `event` field.\n */\nexport type LogPayload = string | number | boolean | bigint | null | undefined | MessageObject;\n\nexport interface MessageObject {\n [key: string]: unknown;\n event?: unknown;\n}\n\nexport interface FluxionLogger {\n /**\n * Primitive payloads become `event`; object payloads are spread over the\n * default `timestamp`, `level` and `event` fields.\n */\n write(level: LogLevel, payload: LogPayload): void;\n info(payload: LogPayload): void;\n warn(payload: LogPayload): void;\n error(payload: LogPayload): void;\n succ(payload: LogPayload): void;\n debug(payload: LogPayload): void;\n verbose(payload: LogPayload): void;\n}\n\n/**\n * Internal-only logger used by fluxion's own subsystems (router, injector,\n * ...). It extends the public {@link FluxionLogger}\n * with a `core` level that records framework-originated logs — e.g. route\n * registration, module lifecycle — so they are visually distinct\n * from logs emitted by user handlers.\n *\n * ! This type is NOT exported to application code: {@link FluxionModuleContext}\n * exposes only {@link FluxionLogger}, keeping\n * `core` off-limits to user handlers.\n */\nexport interface InternalFluxionLogger extends FluxionLogger {\n core(payload: LogPayload): void;\n /**\n * Resolve once the buffered file sink has been drained. Must be awaited\n * before the process image is replaced (`process.execve`), or the tail of the\n * log is lost.\n */\n flush(): Promise<void>;\n}\n\nconst isMessageObject = (payload: LogPayload): payload is MessageObject =>\n typeof payload === 'object' && payload !== null && !Array.isArray(payload);\n\n/**\n * ! Error.isError needs Node.js 24\n */\nconst isError = (value: unknown): value is Error =>\n typeof Error.isError === 'function' ? Error.isError(value) : value instanceof Error;\n\n/**\n * Turn anything thrown or passed as an error into a printable string:\n * the `message`, followed by the `stack` on the next line when present.\n */\nexport function formatError(e: unknown): string {\n if (typeof e === 'object' && e !== null) {\n const { message, stack } = e as { message?: unknown; stack?: unknown };\n let text = '';\n if (typeof message === 'string' && message.length > 0) {\n text = message;\n }\n if (typeof stack === 'string' && stack.length > 0) {\n text += (text.length > 0 ? '\\n' : '') + stack;\n }\n if (text.length > 0) {\n return text;\n }\n }\n return String(e);\n}\n\nconst jsonReplacer = (_key: string, value: unknown): unknown => {\n if (typeof value === 'bigint') {\n return value.toString();\n }\n // Errors serialize to `{}` on their own, so expand them to message + stack.\n if (isError(value)) {\n return formatError(value);\n }\n return value;\n};\n\nconst fallbackLine = (entry: LogEntry): string =>\n JSON.stringify({\n timestamp: typeof entry.timestamp === 'string' ? entry.timestamp : String(entry.timestamp),\n level: typeof entry.level === 'string' ? entry.level : 'INFO',\n event: '[unserializable]',\n });\n\n/**\n * Serialize one entry as a single JSON line.\n */\nconst safeStringify = (entry: LogEntry): string => {\n try {\n return JSON.stringify(entry, jsonReplacer) ?? fallbackLine(entry);\n } catch {\n return fallbackLine(entry);\n }\n};\n\n/**\n * Default sink: one JSON object per line on stdout.\n */\nexport const jsonLineLogger: FluxionLoggerFn = (entry: LogEntry) => {\n // eslint-disable-next-line no-console\n console.log(safeStringify(entry));\n};\n\n/**\n * Resolve once everything queued on the stream has been handed to the OS. An\n * empty write gets a callback behind the queued chunks without ending the\n * stream itself. Resolves on error too, and after a short timeout, so a stuck\n * disk cannot hold a restart hostage.\n */\nfunction flushStream(stream: WriteStream): Promise<void> {\n return new Promise((resolve) => {\n if (stream.destroyed || stream.writableEnded) {\n resolve();\n return;\n }\n const done = () => {\n clearTimeout(timer);\n stream.off('error', done);\n resolve();\n };\n const timer = setTimeout(done, 2000);\n timer.unref();\n stream.once('error', done);\n try {\n stream.write('', done);\n } catch {\n done();\n }\n });\n}\n\n/**\n * 创建文件日志写入器\n *\n * With `deferClose`, the stream does not close itself on error and is closed by\n * `flush()` instead: the logger stays usable until an explicit flush.\n */\nfunction createFileSink(\n logFilePath: string,\n deferClose: boolean,\n): { sink: FluxionLoggerFn; flush: () => Promise<void> } {\n // 确保日志目录存在\n if (!existsSync(logFilePath)) {\n const dir = dirname(logFilePath);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n }\n\n const fileStream = createWriteStream(logFilePath, { flags: 'a', autoClose: !deferClose });\n\n const sink: FluxionLoggerFn = (entry: LogEntry) => {\n try {\n fileStream.write(safeStringify(entry) + '\\n');\n } catch {\n // 忽略文件写入错误\n }\n };\n\n const flush = async () => {\n await flushStream(fileStream);\n if (deferClose) {\n await new Promise<void>((resolve) => fileStream.close(() => resolve()));\n }\n };\n\n return { sink, flush };\n}\n\ntype LoggerSink = FluxionLoggerFn & { flush?: () => Promise<void> };\n\n/**\n * & Logger Options here is checked by normalizeOptions function.\n *\n * When `deferClose` is set, the returned sink carries a `flush` that eventually\n * closes the file stream; it is meant for the in-place restart path, which must\n * drain the log before `process.execve` replaces the image.\n */\nfunction resolveLoggerSink(cx: Pick<FluxionContext, 'options'>, deferClose: boolean): LoggerSink {\n const instanceLogPath = process.env.FLUXION_INSTANCE_LOG;\n const fileSink = instanceLogPath ? createFileSink(instanceLogPath, deferClose) : null;\n\n const loggerOption = cx.options.logger;\n const consoleSink = typeof loggerOption === 'function' ? loggerOption : jsonLineLogger;\n\n if (!fileSink) {\n return consoleSink;\n }\n\n const combined: LoggerSink = (entry: LogEntry) => {\n consoleSink(entry);\n fileSink.sink(entry);\n };\n combined.flush = fileSink.flush;\n return combined;\n}\n\nexport function createLogger(\n cx: Pick<FluxionContext, 'options'>,\n options: { deferClose?: boolean } = {},\n): InternalFluxionLogger {\n const sink = resolveLoggerSink(cx, options.deferClose === true);\n\n const logger: InternalFluxionLogger = {\n write(level: LogLevel, payload: LogPayload): void {\n // Default fields come first in insertion order; the spread lets a user\n // field override `timestamp`, `level` or `event`.\n const timestamp = new Date().toISOString();\n const entry: LogEntry = isMessageObject(payload)\n ? { timestamp, level, event: payload.event, ...payload }\n : { timestamp, level, event: payload };\n\n try {\n sink(entry);\n } catch {\n // Ignore logger sink failures to avoid breaking request handling.\n }\n },\n info(payload: LogPayload): void {\n this.write('INFO', payload);\n },\n warn(payload: LogPayload): void {\n this.write('WARN', payload);\n },\n error(payload: LogPayload): void {\n this.write('ERROR', payload);\n },\n succ(payload: LogPayload): void {\n this.write('SUCC', payload);\n },\n debug(payload: LogPayload): void {\n this.write('DEBUG', payload);\n },\n verbose(payload: LogPayload): void {\n this.write('VERBOSE', payload);\n },\n core(payload: LogPayload): void {\n this.write('CORE', payload);\n },\n flush(): Promise<void> {\n return sink.flush?.() ?? Promise.resolve();\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): InternalFluxionLogger {\n return {\n write(level: LogLevel, payload: LogPayload): void {\n baseLogger.write(level, isMessageObject(payload) ? { ...payload, pid } : { event: payload, pid });\n },\n info(payload: LogPayload): void {\n this.write('INFO', payload);\n },\n warn(payload: LogPayload): void {\n this.write('WARN', payload);\n },\n error(payload: LogPayload): void {\n this.write('ERROR', payload);\n },\n succ(payload: LogPayload): void {\n this.write('SUCC', payload);\n },\n debug(payload: LogPayload): void {\n this.write('DEBUG', payload);\n },\n verbose(payload: LogPayload): void {\n this.write('VERBOSE', payload);\n },\n core(payload: LogPayload): void {\n this.write('CORE', payload);\n },\n flush(): Promise<void> {\n // The base logger has no buffered stream of its own.\n return Promise.resolve();\n },\n };\n}\n","import { writeSync } from 'node:fs';\nimport { mkdir, open } from 'node:fs/promises';\nimport { dirname } from 'node:path';\n\n/**\n * Send everything written to `process.stdout` / `process.stderr` (so `console.*` too) to `filePath`, appending.\n *\n * Writes are synchronous: nothing is buffered, so there is nothing to flush before `process.exit` or `process.execve`.\n */\nexport async function redirectOutput(filePath: string): Promise<void> {\n await mkdir(dirname(filePath), { recursive: true });\n // The handle stays referenced by the closure below; a collected FileHandle would close the fd under us.\n const handle = await open(filePath, 'a');\n\n const write = (chunk: string | Uint8Array, encoding?: unknown, cb?: unknown) => {\n writeSync(handle.fd, Buffer.from(chunk));\n const done = typeof encoding === 'function' ? encoding : cb;\n if (typeof done === 'function') {\n done();\n }\n return true;\n };\n\n process.stdout.write = write as typeof process.stdout.write;\n process.stderr.write = write as typeof process.stderr.write;\n}\n","export const DUMMY_BASE_URL = 'http://fluxion.local';\n\nexport namespace flags {\n export const OptionsNormalized = Symbol('fluxion.options.normalized');\n export const StaticHandled = Symbol('fluxion.router.StaticHandled');\n export const HandlerTimeout = Symbol('fluxion.handlerTimeout');\n export const MiddlewareTimeout = Symbol('fluxion.middlewareTimeout');\n}\n\nexport const STATIC_CONTENT_TYPES: Record<string, string> = {\n '.css': 'text/css; charset=utf-8',\n '.html': 'text/html; charset=utf-8',\n '.htm': 'text/html; charset=utf-8',\n '.js': 'text/javascript; charset=utf-8',\n '.mjs': 'text/javascript; charset=utf-8',\n '.cjs': 'text/javascript; charset=utf-8',\n '.json': 'application/json; charset=utf-8',\n '.jsonld': 'application/ld+json; charset=utf-8',\n '.map': 'application/json; charset=utf-8',\n '.txt': 'text/plain; charset=utf-8',\n '.md': 'text/markdown; charset=utf-8',\n '.xml': 'application/xml; charset=utf-8',\n '.csv': 'text/csv; charset=utf-8',\n '.yaml': 'application/yaml; charset=utf-8',\n '.yml': 'application/yaml; charset=utf-8',\n '.toml': 'application/toml; charset=utf-8',\n\n '.ico': 'image/x-icon',\n '.png': 'image/png',\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.gif': 'image/gif',\n '.webp': 'image/webp',\n '.avif': 'image/avif',\n '.svg': 'image/svg+xml',\n '.bmp': 'image/bmp',\n '.tif': 'image/tiff',\n '.tiff': 'image/tiff',\n\n '.woff': 'font/woff',\n '.woff2': 'font/woff2',\n '.ttf': 'font/ttf',\n '.otf': 'font/otf',\n '.eot': 'application/vnd.ms-fontobject',\n\n '.mp3': 'audio/mpeg',\n '.wav': 'audio/wav',\n '.ogg': 'audio/ogg',\n '.oga': 'audio/ogg',\n '.m4a': 'audio/mp4',\n '.aac': 'audio/aac',\n '.flac': 'audio/flac',\n '.mp4': 'video/mp4',\n '.webm': 'video/webm',\n '.ogv': 'video/ogg',\n '.mov': 'video/quicktime',\n '.m4v': 'video/mp4',\n\n '.pdf': 'application/pdf',\n '.wasm': 'application/wasm',\n '.zip': 'application/zip',\n '.gz': 'application/gzip',\n '.tar': 'application/x-tar',\n '.rar': 'application/vnd.rar',\n '.7z': 'application/x-7z-compressed',\n '.webmanifest': 'application/manifest+json; charset=utf-8',\n '.exe': 'application/octet-stream',\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 ModuleType {\n Api,\n StaticResource,\n}\n","import type { InternalFluxionLogger } from './logger.js';\nimport { formatError } from './logger.js';\n\n/**\n * How long after `fluxion()` finishes before memory is first sampled, in ms.\n * Application code that runs right after `await fluxion()` — opening database\n * connections, warming caches — allocates memory that is not bloat, so the\n * first check waits for it to settle.\n */\nconst STARTUP_GRACE_MS = 5000;\n\n/** How often resident memory is sampled after the grace period, in ms. */\nconst MEMORY_CHECK_INTERVAL_MS = 10_000;\n\n/** How long to wait for in-flight connections to finish before cutting them. */\nconst CLOSE_TIMEOUT_MS = 5000;\n\n/**\n * The parts of `http.Server` / `https.Server` the restart path needs. Both\n * classes provide them, but `https.Server` does not extend `http.Server`.\n */\ninterface Listener {\n close(cb?: (err?: Error) => void): unknown;\n closeIdleConnections(): void;\n closeAllConnections(): void;\n}\n\nexport interface RestarterOptions {\n /**\n * Resident-memory threshold in MB. `false` disables the monitor.\n */\n restartMemoryMB: number | false;\n /** The listener to close before the image is replaced. */\n server: Listener;\n logger: InternalFluxionLogger;\n /**\n * Swap in another grace period, e.g. for tests.\n */\n startupGraceMs?: number;\n /**\n * Swap in another check interval, e.g. for tests.\n */\n checkIntervalMs?: number;\n}\n\nexport interface Restarter {\n /**\n * Start the grace period and, after it, the periodic sampling. Call as the\n * last step of startup so the grace period covers the caller's own init.\n */\n start(): void;\n /**\n * Stop sampling memory. Safe to call before {@link start}.\n */\n stop(): void;\n}\n\nconst megabytes = (bytes: number) => bytes / (1024 * 1024);\n\n/**\n * `process.execve` exists from Node.js 22.15 on Linux and macOS.\n */\nexport const supportsInPlaceRestart = (): boolean => typeof process.execve === 'function';\n\n/**\n * Resolve once the server has closed, or after `timeoutMs` — a keep-alive\n * connection that never goes away must not block the restart forever.\n */\nfunction closeServer(server: Listener, timeoutMs = CLOSE_TIMEOUT_MS): Promise<void> {\n return new Promise((resolve) => {\n let settled = false;\n const done = () => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n resolve();\n };\n const timer = setTimeout(() => {\n server.closeAllConnections();\n done();\n }, timeoutMs);\n timer.unref();\n\n server.close(done);\n server.closeIdleConnections();\n });\n}\n\n/**\n * Rebuild the command line the process was started with. `tsx` and other\n * loaders live in `execArgv`, so dropping it would start the entry point\n * unparsed.\n */\nconst commandLine = () => [process.execPath, ...process.execArgv, ...process.argv.slice(1)];\n\n/**\n * Replace the running process image with a fresh copy of itself, keeping the\n * PID. Listeners must be closed and the log flushed before the call: execve\n * only preserves stdio.\n *\n * Never returns on success — the entry point is re-executed in the same\n * process. On failure the process exits with code 1 so a supervisor restarts\n * it.\n */\nexport async function restartInPlace(\n server: Listener,\n logger: InternalFluxionLogger,\n extra: Record<string, unknown> = {},\n): Promise<never> {\n if (typeof process.execve !== 'function') {\n // Callers check `supportsInPlaceRestart` first; keep this for direct use.\n logger.error({ event: 'RestartFailed', pid: process.pid, error: 'process.execve is not available' });\n process.exit(1);\n }\n\n logger.warn({ event: 'Restarting', pid: process.pid, ...extra });\n\n await closeServer(server);\n await logger.flush();\n\n try {\n process.execve(process.execPath, commandLine(), process.env);\n } catch (e) {\n logger.error({ event: 'RestartFailed', pid: process.pid, error: formatError(e) });\n }\n process.exit(1);\n}\n\n/**\n * Watch RSS and run {@link restartInPlace} once it crosses the threshold.\n * Returns a no-op restarter when the threshold is `false`.\n *\n * Call this as the last step of `fluxion()`: the grace period is measured from\n * that moment. The timers are unref'd, so they never keep the process alive on\n * their own.\n */\nexport function watchMemory(options: RestarterOptions): Restarter {\n const {\n restartMemoryMB,\n server,\n logger,\n startupGraceMs = STARTUP_GRACE_MS,\n checkIntervalMs = MEMORY_CHECK_INTERVAL_MS,\n } = options;\n\n if (restartMemoryMB === false) {\n return { start: () => {}, stop: () => {} };\n }\n\n let restarting = false;\n let timer: NodeJS.Timeout | undefined;\n\n const check = () => {\n if (restarting) return;\n const rssMB = megabytes(process.memoryUsage.rss());\n if (rssMB <= restartMemoryMB) return;\n\n restarting = true;\n if (timer !== undefined) {\n clearInterval(timer);\n timer = undefined;\n }\n // Not awaited: the call ends the process, and the interval callback must\n // not keep the event loop busy while listeners drain.\n void restartInPlace(server, logger, { reason: 'memory', rssMB: Math.round(rssMB) });\n };\n\n return {\n start() {\n if (timer !== undefined || restarting) return;\n // Skip the startup phase entirely; the first sample lands when user code\n // has had time to finish its own initialization.\n timer = setTimeout(() => {\n timer = setInterval(check, checkIntervalMs);\n timer.unref();\n check();\n }, startupGraceMs);\n timer.unref();\n },\n stop() {\n if (timer !== undefined) {\n clearInterval(timer);\n timer = undefined;\n }\n },\n };\n}\n","export class FluxionError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'FluxionVioError';\n }\n}\n","import { FluxionError } from '@/common/error.js';\nimport type { FluxionOptions, NormalizedFluxionOptions } from '@/types.js';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nimport { flags } from '@/common/consts.js';\n\n/**\n * Read certificate content from a file path or return the content directly.\n * Relative paths are resolved against the current working directory.\n */\nfunction readCertificateContent(content: string | Buffer): 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.resolve(content);\n if (fs.existsSync(filePath)) {\n return fs.readFileSync(filePath);\n }\n }\n return Buffer.from(content);\n }\n throw new FluxionError('Certificate content must be a string or Buffer');\n}\n\n/**\n * Normalize HTTPS options.\n */\nfunction normalizeHttpsOptions(https: FluxionOptions['https']): NormalizedFluxionOptions['https'] | undefined {\n if (!https) {\n return undefined;\n }\n\n if (typeof https !== 'object' || https === null || Array.isArray(https)) {\n throw new FluxionError('FluxionOptions.https must be an object');\n }\n if (typeof https.key !== 'string' && !Buffer.isBuffer(https.key)) {\n throw new FluxionError('FluxionOptions.https.key must be a string or Buffer');\n }\n if (typeof https.cert !== 'string' && !Buffer.isBuffer(https.cert)) {\n throw new FluxionError('FluxionOptions.https.cert must be a string or Buffer');\n }\n\n const result: NormalizedFluxionOptions['https'] = {\n key: readCertificateContent(https.key),\n cert: readCertificateContent(https.cert),\n };\n\n if (https.ca !== undefined) {\n if (Array.isArray(https.ca)) {\n result.ca = https.ca.map((item) => readCertificateContent(item));\n } else {\n result.ca = readCertificateContent(https.ca);\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 new FluxionError('FluxionOptions must be an object');\n }\n\n // Check for deprecated 'include' option\n if ('include' in o) {\n throw new FluxionError(\n 'The \"include\" option has been removed. Please use:\\n' +\n ' - \"apiInclude\" for API handler patterns (default: [\"**/*.ts\"])\\n' +\n ' - \"staticInclude\" for static resource patterns (default: [\"**/*\"])\\n' +\n 'Example migration:\\n' +\n ' OLD: { include: [\"**/*.ts\", \"**/*.js\"], apiInclude: [\"**/*.ts\"] }\\n' +\n ' NEW: { apiInclude: [\"**/*.ts\"], staticInclude: [\"**/*.js\"] }',\n );\n }\n\n const {\n dir: rawDir,\n host,\n port: userPort,\n handlerTimeoutMs = 5000,\n middlewareTimeoutMs = 3000,\n staticResourceTimeoutMs = 3 * 60 * 1000,\n target = `node${process.versions.node.split('.')[0]}`,\n maxRequestBytes = 8_000_000,\n apiInclude = ['**/*.ts'],\n staticInclude = ['**/*'],\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 csp = \"default-src 'self'\",\n restartMemoryMB = false,\n logOutputFile: rawLogOutputFile,\n } = o as FluxionOptions;\n\n const port = userPort;\n\n const logger = o.logger ?? 'json-line';\n if (logger !== 'json-line' && typeof logger !== 'function') {\n throw new FluxionError(\n `Invalid logger option, Must be 'json-line' or a custom logger function (write a one-line logger yourself)`,\n );\n }\n\n if (typeof rawDir !== 'string') {\n throw new FluxionError('FluxionOptions.dir must be a string');\n }\n // Ends with a separator, so \"is this file under dir\" is a plain `startsWith`\n const resolvedDir = path.resolve(process.cwd(), rawDir);\n const dir = resolvedDir.endsWith(path.sep) ? resolvedDir : resolvedDir + path.sep;\n\n if (typeof target !== 'string' && !(Array.isArray(target) && target.every((t) => typeof t === 'string'))) {\n throw new FluxionError('FluxionOptions.target must be a string or an array of strings');\n }\n\n if (typeof host !== 'string') {\n throw new FluxionError('FluxionOptions.host must be a string');\n }\n\n if (!Number.isSafeInteger(handlerTimeoutMs) || handlerTimeoutMs <= 100) {\n throw new FluxionError('FluxionOptions.handlerTimeoutMs must be an integer greater than 100');\n }\n\n if (!Number.isSafeInteger(middlewareTimeoutMs) || middlewareTimeoutMs <= 100) {\n throw new FluxionError('FluxionOptions.middlewareTimeoutMs must be an integer greater than 100');\n }\n\n if (typeof port !== 'number' || !Number.isSafeInteger(port)) {\n throw new FluxionError('FluxionOptions.port must be a positive integer');\n }\n\n if (port <= 1 || port > 65535) {\n throw new FluxionError('FluxionOptions.port must be 1 ~ 65535');\n }\n\n if (typeof maxRequestBytes !== 'number' || maxRequestBytes <= 0 || !Number.isSafeInteger(maxRequestBytes)) {\n throw new FluxionError('FluxionOptions.maxRequestBytes must be a positive integer');\n }\n\n if (csp !== false && typeof csp !== 'string') {\n throw new FluxionError('FluxionOptions.csp must be a string or false');\n }\n\n if (restartMemoryMB !== false) {\n if (typeof restartMemoryMB !== 'number' || !Number.isFinite(restartMemoryMB) || restartMemoryMB <= 0) {\n throw new FluxionError('FluxionOptions.restartMemoryMB must be a positive number of MB, or false');\n }\n }\n\n if (rawLogOutputFile !== undefined && (typeof rawLogOutputFile !== 'string' || rawLogOutputFile.length === 0)) {\n throw new FluxionError('FluxionOptions.logOutputFile must be a non-empty string path');\n }\n // Relative paths follow `dir` and `https`: resolved against the cwd.\n const logOutputFile = rawLogOutputFile === undefined ? undefined : path.resolve(process.cwd(), rawLogOutputFile);\n\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n\n const result: NormalizedFluxionOptions = {\n dir,\n host,\n port,\n handlerTimeoutMs,\n middlewareTimeoutMs,\n staticResourceTimeoutMs,\n target,\n maxRequestBytes,\n logger,\n logOutputFile,\n apiInclude,\n staticInclude,\n exclude,\n https: normalizeHttpsOptions(https),\n csp,\n restartMemoryMB,\n normalizedFlag: flags.OptionsNormalized,\n };\n\n if (process.argv.includes('--verbose')) {\n console.log('normalized options', { ...result, https: result.https ? 'set' : 'unset' });\n }\n\n return result;\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 (e) {\n reject(e);\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","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","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 let count = 0;\n const MAX_COOKIE_KEYS = 100;\n\n for (const pair of pairs) {\n if (count >= MAX_COOKIE_KEYS) {\n break;\n }\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 count++;\n }\n\n return cookies;\n}\n\n/**\n * Serialize an object into a Cookie header string\n */\nexport function serializeCookie(\n name: string,\n value: string,\n 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 },\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, FluxionRequest, NormalizedModule } from '../types.js';\nimport { HttpCode, flags, ModuleType } from '@/common/consts.js';\nimport { PromiseTry } from '@/common/promise-try.js';\nimport { formatError } from '@/common/logger.js';\n\nimport { getRealIp } from './headers.js';\nimport { toURL } from './request.js';\nimport { safeSendJson } from './respond.js';\nimport { parseBody, type BodyPreview } from './body.js';\nimport { parseQuery } from './query.js';\nimport { parseCookie } from './cookie.js';\nimport { HttpException } from './exceptions.js';\n\nconst waiter = (mainPromise: Promise<any>, timeoutMs: number, flag: symbol) =>\n Promise.race([mainPromise, new Promise((r) => setTimeout(() => r(flag), timeoutMs))]);\n\n/**\n * Run a module's disposer once the response has been sent.\n * Modules without a disposer are left untouched.\n */\nfunction disposeAfterResponse(cx: FluxionContext, res: http.ServerResponse, m: NormalizedModule) {\n // The module instance is shared by requests while its files are unchanged: the disposer runs once per response.\n if (typeof m.disposer !== 'function') {\n return;\n }\n const disposer = m.disposer;\n res.once('close', () => {\n PromiseTry(disposer).catch((e: unknown) => {\n cx.logger.error({ event: 'DisposerFailed', url: m.absolutePath, error: formatError(e) });\n });\n });\n}\n\nexport function createServer(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: FluxionRequest = {\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 // Security headers for all responses\n res.setHeader('X-Content-Type-Options', 'nosniff');\n res.setHeader('X-Frame-Options', 'DENY');\n res.setHeader('X-XSS-Protection', '1; mode=block');\n // Content-Security-Policy (user-configurable, header omitted when set to false)\n if (cx.options.csp !== false) {\n res.setHeader('Content-Security-Policy', cx.options.csp);\n }\n\n let bodyPreview: BodyPreview = {\n exists: false,\n bytes: 0,\n truncated: false,\n };\n\n cx.logger.core({ event: 'request', method, ip, path: url.pathname });\n\n const start = performance.now();\n res.once('finish', () => {\n const o: Record<string, unknown> = {\n event: '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.core(o);\n });\n\n // * Start request handling\n try {\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(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 // Clean up this request's module instance after the response is sent.\n disposeAfterResponse(cx, res, m);\n\n const timeoutMs =\n m.type === ModuleType.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 flags.MiddlewareTimeout,\n );\n\n if (result === flags.MiddlewareTimeout) {\n cx.logger.warn({\n event: '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 flags.HandlerTimeout,\n );\n\n if (result === flags.HandlerTimeout) {\n cx.logger.warn({ event: 'HandlerTimeout', method: normalized.method, ip: normalized.ip });\n safeSendJson(res, { message: 'Handler timed out' }, HttpCode.InternalServerError);\n return;\n }\n\n if (result !== flags.StaticHandled) {\n safeSendJson(res, result);\n }\n } catch (e) {\n if (e instanceof HttpException) {\n cx.logger.error({\n ...normalized,\n event: 'RequestFailed',\n error: e.message,\n });\n safeSendJson(res, { message: e.message }, e.errno);\n } else {\n cx.logger.error({\n ...normalized,\n event: 'RequestFailed',\n error: formatError(e),\n });\n safeSendJson(res, { message: 'Internal Server Error' }, HttpCode.InternalServerError);\n }\n }\n };\n\n const server = cx.options.https\n ? https.createServer(\n {\n key: cx.options.https.key,\n cert: cx.options.https.cert,\n ca: cx.options.https.ca,\n },\n requestHandler,\n )\n : http.createServer(requestHandler);\n\n return new Promise((resolve, reject) => {\n let listening = false;\n\n server.on('close', () => {\n cx.logger.core({\n event: 'ServerClosed',\n host: cx.options.host,\n port: cx.options.port,\n });\n });\n\n server.once('listening', () => {\n listening = true;\n cx.logger.core({\n event: 'FluxionStarted',\n version: '__VERSION__',\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.core({\n event: 'DynamicDirectory',\n directory: cx.options.dir,\n });\n resolve(server);\n });\n\n server.on('error', (e) => {\n cx.logger.error({\n event: 'ServerError',\n error: formatError(e),\n });\n if (listening) {\n // Server encountered an error after binding — log and let the\n // caller (PM2 / user code) decide how to recover instead of\n // forcing process.exit(1) here.\n return;\n }\n reject(e);\n });\n\n server.listen(cx.options.port, cx.options.host);\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 { buildSync } from 'esbuild';\nimport { compileFunction } from 'node:vm';\nimport { createRequire } from 'node:module';\nimport { readFileSync, statSync } from 'node:fs';\nimport { dirname, extname, isAbsolute, join, resolve, sep } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport type { NormalizedFluxionOptions } from '@/types.js';\n\n/**\n * A module loaded by `FluxionLoader`. `deps` holds the instances it required, so a stale dependency shows up\n * even when the cache has already been refilled.\n */\nexport interface LoadedModule {\n id: string;\n filename: string;\n exports: any;\n mtimeMs: number;\n size: number;\n deps: Map<string, LoadedModule>;\n}\n\n/**\n * Every loaded module, keyed by absolute path, shared by all fluxion instances. Delete an entry to force a reload.\n */\nexport const moduleCache = new Map<string, LoadedModule>();\n\nconst NODE_MODULES = `${sep}node_modules${sep}`;\n\nconst COMPILED = new Set(['.ts', '.tsx', '.mts', '.cts', '.js', '.jsx', '.mjs', '.cjs']);\nconst EXTENSIONS = [...COMPILED, '.json'];\n// By TypeScript convention `import './a.js'` points at `a.ts`\nconst JS_TO_TS: Record<string, string[]> = {\n '.js': ['.ts', '.tsx'],\n '.jsx': ['.tsx'],\n '.mjs': ['.mts'],\n '.cjs': ['.cts'],\n};\n\nconst isBare = (specifier: string) => !specifier.startsWith('.') && !isAbsolute(specifier);\n\nconst isChanged = (m: LoadedModule) => {\n try {\n const s = statSync(m.filename);\n return s.mtimeMs !== m.mtimeMs || s.size !== m.size;\n } catch {\n // gone counts as changed\n return true;\n }\n};\n\nconst isFile = (path: string) => {\n try {\n return statSync(path).isFile();\n } catch {\n return false;\n }\n};\n\n/**\n * Find the real file behind an extension-less or `.js`-style path, the way esbuild would.\n */\nconst findFile = (base: string) => {\n if (isFile(base)) {\n return base;\n }\n\n const ext = extname(base);\n const stem = base.slice(0, base.length - ext.length);\n for (const e of JS_TO_TS[ext] ?? []) {\n if (isFile(stem + e)) {\n return stem + e;\n }\n }\n for (const e of EXTENSIONS) {\n if (isFile(base + e)) {\n return base + e;\n }\n }\n for (const e of EXTENSIONS) {\n if (isFile(join(base, 'index' + e))) {\n return join(base, 'index' + e);\n }\n }\n};\n\n/**\n * Load a ts/js file under `options.dir`. Local files it imports are loaded the same way, each compiled by esbuild on\n * its own; node_modules, builtins and files outside `dir` go through the native `require`.\n *\n * A file is loaded fresh the first time and its mtime and size are recorded together with those of every file it\n * required. Asking again compares them: if any one differs, the whole chain is dropped and loaded fresh, otherwise\n * the cached exports are returned. Content is never compared.\n *\n * Synchronous, because `require` is. `require()` returns `exports`, so `export default x` is `result.default`.\n */\nexport class FluxionLoader {\n private readonly opts: Pick<NormalizedFluxionOptions, 'dir' | 'target'>;\n\n constructor(options: Pick<NormalizedFluxionOptions, 'dir' | 'target'>) {\n this.opts = options;\n }\n\n /**\n * `shadowed` switches `__dirname` / `__filename` from function parameters to `define` constants, for sources that\n * declare one at the top level and would otherwise collide with the parameter. Only then, because a `define`d name\n * is not a binding: `eval('__dirname')` would stop seeing it and assigning to it would leak to the global object.\n */\n private compile(file: string, shadowed: boolean) {\n const define: Record<string, string> = {\n 'import.meta.url': JSON.stringify(pathToFileURL(file).href),\n 'import.meta.filename': JSON.stringify(file),\n 'import.meta.dirname': JSON.stringify(dirname(file)),\n };\n if (shadowed) {\n define.__dirname = JSON.stringify(dirname(file));\n define.__filename = JSON.stringify(file);\n }\n\n const { outputFiles } = buildSync({\n entryPoints: [file],\n bundle: false,\n write: false,\n platform: 'node',\n format: 'cjs',\n target: this.opts.target,\n sourcemap: 'inline',\n sourcesContent: false,\n logLevel: 'silent',\n // `import(x)` becomes `require(x)`, because a vm function has no way to run a native dynamic import\n supported: { 'dynamic-import': false },\n define,\n });\n return outputFiles[0].text;\n }\n\n require(absolutePath: string): any {\n return this.load(resolve(absolutePath)).exports;\n }\n\n private isHot(file: string) {\n const ext = extname(file);\n return file.startsWith(this.opts.dir) && !file.includes(NODE_MODULES) && (COMPILED.has(ext) || ext === '.json');\n }\n\n /**\n * Return the cached module if nothing in its chain changed, otherwise drop the chain and load it fresh.\n */\n private load(file: string): LoadedModule {\n const cached = moduleCache.get(file);\n if (cached === undefined) {\n return this.loadFresh(file);\n }\n\n const chain = this.collectChain(cached);\n if (!this.anyStale(chain)) {\n return cached;\n }\n for (const m of chain) {\n // A newer instance of the same path may have been loaded by someone else meanwhile: that one is fine, keep it\n if (moduleCache.get(m.filename) === m) {\n moduleCache.delete(m.filename);\n }\n }\n return this.loadFresh(file);\n }\n\n private collectChain(root: LoadedModule) {\n const chain = new Set([root]);\n // a Set visits what is added while iterating, and the `has` check built into it stops cycles\n for (const m of chain) {\n for (const dep of m.deps.values()) {\n chain.add(dep);\n }\n }\n return chain;\n }\n\n private anyStale(chain: Set<LoadedModule>) {\n for (const m of chain) {\n if (moduleCache.get(m.filename) !== m || isChanged(m)) {\n return true;\n }\n }\n return false;\n }\n\n private loadFresh(file: string): LoadedModule {\n // Read before the file is, so an edit in between is seen as a change next time instead of being missed\n const { mtimeMs, size } = statSync(file);\n const mod: LoadedModule = { id: file, filename: file, exports: {}, mtimeMs, size, deps: new Map() };\n // Registered before it runs, so a circular import sees the partial exports instead of looping\n moduleCache.set(file, mod);\n try {\n this.evaluate(file, mod);\n } catch (e) {\n moduleCache.delete(file);\n throw e;\n }\n return mod;\n }\n\n private evaluate(file: string, mod: LoadedModule) {\n const ext = extname(file);\n if (ext === '.json') {\n mod.exports = JSON.parse(readFileSync(file, 'utf8'));\n return;\n }\n if (!COMPILED.has(ext)) {\n throw new Error(`FluxionLoader: unsupported file type \"${ext}\" (${file})`);\n }\n\n const fn = this.compileFunction(file);\n fn.call(mod.exports, mod.exports, this.createModuleRequire(mod), mod, file, dirname(file));\n }\n\n /**\n * Parameters first, so `eval('__dirname')` still sees it and assigning to it stays local. A source declaring its\n * own `__dirname` cannot be compiled with that parameter, so on that exact error the file is recompiled with\n * `define` instead. Compiling runs nothing, so a failed attempt has no side effects.\n */\n private compileFunction(file: string) {\n const params = ['exports', 'require', 'module', '__filename', '__dirname'];\n try {\n return compileFunction(this.compile(file, false), params, { filename: file });\n } catch (e) {\n const redeclared = e instanceof SyntaxError && /__dirname|__filename/.test(e.message);\n if (!redeclared) {\n throw e;\n }\n return compileFunction(this.compile(file, true), params.slice(0, 3), { filename: file });\n }\n }\n\n /**\n * The only closure left per module, and it is made once per fresh load: it has to remember which module is asking.\n */\n private createModuleRequire(from: LoadedModule) {\n const native = createRequire(from.filename);\n const locate = (specifier: string) =>\n isBare(specifier) ? undefined : findFile(resolve(dirname(from.filename), specifier));\n\n const req = (specifier: string) => {\n const file = locate(specifier);\n if (file === undefined) {\n // builtins, packages and anything that cannot be found; the native require reports the latter\n return native(specifier);\n }\n if (!this.isHot(file)) {\n return native(file);\n }\n const dep = this.load(file);\n // Also recorded for requires made long after loading (`import()` inside a function)\n from.deps.set(file, dep);\n return dep.exports;\n };\n req.resolve = (specifier: string) => locate(specifier) ?? native.resolve(specifier);\n req.cache = native.cache;\n return req;\n }\n}\n","import { FluxionError } from './error.js';\nimport type { FluxionContext, NormalizedModule } from '@/types.js';\nimport type { Stats } from 'node:fs';\nimport { static_cast } from 'type-narrow';\nimport { ModuleType } from './consts.js';\nimport { FluxionLoader } from './loader.js';\n\nfunction isFluxionModule(cx: Pick<FluxionContext, 'options' | 'logger'>, o: unknown): o is NormalizedModule {\n if (typeof o !== 'object' || o === null) {\n return false;\n }\n\n static_cast<NormalizedModule>(o);\n\n if (typeof o.handler !== 'function') {\n cx.logger.error({ event: 'InvalidModule', error: 'handler must be a function' });\n return false;\n }\n\n if (o.disposer !== undefined && typeof o.disposer !== 'function') {\n cx.logger.error({ event: 'InvalidModule', 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({ event: 'InvalidModule', error: 'handlerTimeoutMs must be an integer >= 100 if provided' });\n return false;\n }\n\n if (o.type !== ModuleType.Api) {\n cx.logger.error({ event: 'InvalidModule', 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 absolutePath: string,\n stat: Stats,\n): NormalizedModule {\n const loader = new FluxionLoader(cx.options);\n\n let m = loader.require(absolutePath);\n if (isFluxionModule(cx, m.default)) {\n m = m.default;\n } else if (isFluxionModule(cx, m)) {\n } else {\n throw new FluxionError(\n `Invalid handler module '${absolutePath}', make sure it satisfies defineFluxionModule(...) helper`,\n );\n }\n\n m.absolutePath = absolutePath;\n m.mtimeMs = stat.mtimeMs;\n\n return m;\n}\n","import type { FluxionContext, NormalizedModule } from '../types.js';\nimport { createReadStream } from 'node:fs';\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\nimport { minimatch } from 'minimatch';\nimport { ModuleType, STATIC_CONTENT_TYPES, flags } from '@/common/consts.js';\nimport { loadFluxionModule } from '@/common/injector.js';\n\n/**\n * Resolve a request URL to the module that should handle it.\n * Nothing is registered: the file is looked up on every call. API modules are reloaded only when the file or one\n * of the files it imports has changed (mtime or size).\n */\nexport type FluxionRouter = (url: URL) => Promise<NormalizedModule | undefined>;\n\nexport function createRouter(cx: Pick<FluxionContext, 'options' | 'logger'>): FluxionRouter {\n return async (url) => {\n const relativePath = url.pathname.replace(/^[/]+/, '').replace(/[/]+$/, '');\n const absolutePath = path.join(cx.options.dir, relativePath);\n\n // ! Fail if the resolved path escapes the configured directory\n const withinDir = path.relative(cx.options.dir, absolutePath);\n if (withinDir.startsWith('..') || path.isAbsolute(withinDir)) {\n return undefined;\n }\n\n // Excluded files are never served\n if (cx.options.exclude.some((p) => minimatch(relativePath, p))) {\n return undefined;\n }\n\n // The file must exist and be a regular file\n const stat = await fs.stat(absolutePath).catch(() => undefined);\n if (!stat || !stat.isFile()) {\n return undefined;\n }\n\n if (cx.options.apiInclude.some((p) => minimatch(relativePath, p))) {\n return loadFluxionModule(cx, absolutePath, stat);\n }\n\n if (cx.options.staticInclude.some((p) => minimatch(relativePath, p))) {\n return makeStaticResource(absolutePath);\n }\n\n return undefined;\n };\n}\n\nfunction makeStaticResource(absolutePath: string): NormalizedModule {\n return {\n type: ModuleType.StaticResource,\n mtimeMs: NaN,\n absolutePath,\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 const stat = await fs.stat(absolutePath).catch((e: Error) => e);\n if (stat instanceof Error) {\n res.statusCode = 404;\n res.end('Not Accessible');\n cx.logger.error({\n event: 'StaticResourceError',\n url: normalized.url.pathname,\n error: stat.message,\n });\n return null;\n }\n\n if (!stat.isFile()) {\n res.statusCode = 404;\n res.end('Not Found');\n return;\n }\n\n const extension = path.extname(absolutePath).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 = createReadStream(absolutePath);\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(flags.StaticHandled);\n };\n\n const onClientClose = () => {\n cleanup();\n stream.destroy();\n resolve(flags.StaticHandled);\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","import type { FluxionContext, FluxionOptions, NormalizedFluxionOptions } from './types.js';\n\nimport { createLogger } from './common/logger.js';\nimport { redirectOutput } from './common/output.js';\nimport { flags } from './common/consts.js';\nimport { supportsInPlaceRestart, watchMemory } from './common/restart.js';\nimport { defineFluxionOptions } from './defines/options.js';\nimport { createServer } from './http/server.js';\nimport { createRouter } from './router/index.js';\n\n/**\n * Creates a fluxion-vio server instance.\n * @package Fluxion Vio\n * @version __VERSION__\n */\nexport async function fluxion(options: FluxionOptions | NormalizedFluxionOptions) {\n const alreadyNormalized = (options as NormalizedFluxionOptions).normalizedFlag === flags.OptionsNormalized;\n const context = { options: alreadyNormalized ? options : defineFluxionOptions(options) } as FluxionContext;\n\n // Before anything is logged, so a startup failure (a busy port) lands in the file too.\n if (context.options.logOutputFile) {\n await redirectOutput(context.options.logOutputFile);\n }\n\n // A restart must drain the log before execve replaces the image, so the file\n // sink is flushed explicitly instead of closing itself. On runtimes without\n // `process.execve` the option is ignored, so there is nothing to flush for.\n const restartable = context.options.restartMemoryMB !== false && supportsInPlaceRestart();\n context.logger = createLogger(context as Pick<FluxionContext, 'options'>, { deferClose: restartable });\n context.router = createRouter(context as Pick<FluxionContext, 'options' | 'logger'>);\n\n // Start HTTP server\n const server = await createServer(context);\n\n if (context.options.restartMemoryMB !== false && !restartable) {\n context.logger.warn({\n event: 'RestartDisabled',\n reason: 'process.execve is not available on this Node.js version',\n requiredVersion: '22.15',\n nodeVersion: process.version,\n restartMemoryMB: context.options.restartMemoryMB,\n });\n }\n\n const restarter = watchMemory({\n restartMemoryMB: restartable ? context.options.restartMemoryMB : false,\n server,\n logger: context.logger,\n });\n\n // Register signal handlers for graceful shutdown\n const shutdown = (signal: NodeJS.Signals) => {\n restarter.stop();\n context.logger.warn({ event: 'ShuttingDown', pid: process.pid, signal });\n server.close(async () => {\n await context.logger.flush();\n process.exit(0);\n });\n };\n\n process.once('SIGINT', () => shutdown('SIGINT'));\n process.once('SIGTERM', () => shutdown('SIGTERM'));\n\n // ! This must stay the last step: the memory monitor's startup grace period\n // is measured from here, giving the caller's own initialization (database\n // connections, caches) time to settle before memory is judged.\n restarter.start();\n}\n","import { FluxionError } from '@/common/error.js';\nimport type {\n FluxionHandler,\n FluxionDisposer,\n FluxionModule,\n FluxionMiddleware,\n FluxionModuleObject,\n} from '@/types.js';\nimport type { FluxionLoggerFn } from '@/common/logger.js';\nimport { ModuleType } from '@/common/consts.js';\n\nexport { defineFluxionOptions } from './options.js';\n\n/**\n * Use handler function and optional disposer function to define a Fluxion module.\n * @param handler Main function that handles request and response instances\n * @param disposer Deal with resource cleanup once the response has been sent. Optional.\n */\nexport function defineFluxionModule(handler: FluxionHandler, disposer?: FluxionDisposer): FluxionModuleObject;\n/**\n * Provides type safety for defining Fluxion modules.\n */\nexport function defineFluxionModule(fluxionModule: FluxionModule): FluxionModuleObject;\nexport function defineFluxionModule(\n a: FluxionModule | FluxionHandler,\n disposer?: FluxionDisposer,\n): FluxionModuleObject {\n if (typeof a === 'function') {\n if (disposer !== undefined && typeof disposer !== 'function') {\n throw new FluxionError(`Invalid disposer, expected a function but got ${typeof disposer}`);\n }\n return { handler: a, disposer, type: ModuleType.Api };\n }\n\n if (typeof a !== 'object' || a === null) {\n throw new FluxionError(\n `Invalid argument, expected a FluxionModule object or a handler function, but got ${typeof a}`,\n );\n }\n\n if (typeof a.handler !== 'function') {\n throw new FluxionError(`Invalid FluxionModule, \"handler\" must be a function`);\n }\n\n if (a.disposer !== undefined && typeof a.disposer !== 'function') {\n throw new FluxionError(`Invalid FluxionModule, \"disposer\" must be a function if provided`);\n }\n\n if (a.methods !== undefined && (!Array.isArray(a.methods) || a.methods.some((v) => typeof v !== 'string'))) {\n throw new FluxionError(`Invalid FluxionModule, \"methods\" must be an array of strings if provided`);\n }\n\n if (\n a.middlewares !== undefined &&\n (!Array.isArray(a.middlewares) || a.middlewares.some((v) => typeof v !== 'function'))\n ) {\n throw new FluxionError(`Invalid FluxionModule, \"middlewares\" must be an array of functions if provided`);\n }\n\n return { ...a, type: ModuleType.Api };\n}\n\nexport function defineFluxionMiddleware(middleware: FluxionMiddleware): FluxionMiddleware {\n if (typeof middleware !== 'function') {\n throw new FluxionError(`Invalid FluxionMiddleware, expected a function but got ${typeof middleware}`);\n }\n return middleware;\n}\n\nexport function defineFluxionLogger(loggerFn: FluxionLoggerFn) {\n if (typeof loggerFn !== 'function') {\n throw new FluxionError(`Invalid FluxionLoggerFn, expected a function but got ${typeof loggerFn}`);\n }\n return loggerFn;\n}\n","import { fluxion } from './fluxion.js';\nimport { HttpCode } from './common/consts.js';\n\nexport { fluxion, HttpCode };\n\n/**\n * Export fluxion version for users' debugging.\n * @version __VERSION__\n */\nexport const VERSION = '__VERSION__';\n\n// Export all HTTP exceptions\nexport {\n HttpException,\n BadRequestException,\n UnauthorizedException,\n ForbiddenException,\n NotFoundException,\n MethodNotAllowedException,\n NotAcceptableException,\n RequestTimeoutException,\n ConflictException,\n GoneException,\n PayloadTooLargeException,\n UnsupportedMediaTypeException,\n UnprocessableEntityException,\n TooManyRequestsException,\n InternalServerErrorException,\n NotImplementedException,\n BadGatewayException,\n ServiceUnavailableException,\n GatewayTimeoutException,\n} from './http/exceptions.js';\n\nexport * from './defines/index.js';\n\nexport type {\n FluxionRequest,\n FluxionModuleContext,\n FluxionDisposer,\n FluxionHandler,\n FluxionModule,\n FluxionOptions,\n} from './types.js';\n\n// export { formatError } from './common/logger.js';\nexport type { FluxionLogger, LogEntry, LogPayload, FluxionLoggerFn, LoggerOption } from './common/logger.js';\n"],"x_google_ignoreList":[15],"mappings":"+0BAsEA,MAAM,EAAmB,GACvB,OAAO,GAAY,YAAY,GAAoB,CAAC,MAAM,QAAQ,CAAO,EAKrE,EAAW,GACf,OAAO,MAAM,SAAY,WAAa,MAAM,QAAQ,CAAK,EAAI,aAAiB,MAMhF,SAAgB,EAAY,EAAoB,CAC9C,GAAI,OAAO,GAAM,UAAY,EAAY,CACvC,GAAM,CAAE,UAAS,SAAU,EACvB,EAAO,GAOX,GANI,OAAO,GAAY,UAAY,EAAQ,OAAS,IAClD,EAAO,GAEL,OAAO,GAAU,UAAY,EAAM,OAAS,IAC9C,IAAS,EAAK,OAAS,EAAI;EAAO,IAAM,GAEtC,EAAK,OAAS,EAChB,OAAO,CAEX,CACA,OAAO,OAAO,CAAC,CACjB,CAEA,MAAM,GAAgB,EAAc,IAC9B,OAAO,GAAU,SACZ,EAAM,SAAS,EAGpB,EAAQ,CAAK,EACR,EAAY,CAAK,EAEnB,EAGH,EAAgB,GACpB,KAAK,UAAU,CACb,UAAW,OAAO,EAAM,WAAc,SAAW,EAAM,UAAY,OAAO,EAAM,SAAS,EACzF,MAAO,OAAO,EAAM,OAAU,SAAW,EAAM,MAAQ,OACvD,MAAO,kBACT,CAAC,EAKG,EAAiB,GAA4B,CACjD,GAAI,CACF,OAAO,KAAK,UAAU,EAAO,CAAY,GAAK,EAAa,CAAK,CAClE,MAAQ,CACN,OAAO,EAAa,CAAK,CAC3B,CACF,EAKa,GAAmC,GAAoB,CAElE,QAAQ,IAAI,EAAc,CAAK,CAAC,CAClC,EAQA,SAAS,EAAY,EAAoC,CACvD,OAAO,IAAI,QAAS,GAAY,CAC9B,GAAI,EAAO,WAAa,EAAO,cAAe,CAC5C,EAAQ,EACR,MACF,CACA,IAAM,MAAa,CACjB,aAAa,CAAK,EAClB,EAAO,IAAI,QAAS,CAAI,EACxB,EAAQ,CACV,EACM,EAAQ,WAAW,EAAM,GAAI,EACnC,EAAM,MAAM,EACZ,EAAO,KAAK,QAAS,CAAI,EACzB,GAAI,CACF,EAAO,MAAM,GAAI,CAAI,CACvB,MAAQ,CACN,EAAK,CACP,CACF,CAAC,CACH,CAQA,SAAS,GACP,EACA,EACuD,CAEvD,GAAI,EAAA,EAAA,EAAA,WAAA,CAAY,CAAW,EAAG,CAC5B,IAAM,GAAA,EAAA,EAAA,QAAA,CAAc,CAAW,GAC3B,EAAA,EAAA,WAAA,CAAY,CAAG,IACjB,EAAA,EAAA,UAAA,CAAU,EAAK,CAAE,UAAW,EAAK,CAAC,CAEtC,CAEA,IAAM,GAAA,EAAA,EAAA,kBAAA,CAA+B,EAAa,CAAE,MAAO,IAAK,UAAW,CAAC,CAAW,CAAC,EAiBxF,MAAO,CAAE,KAfsB,GAAoB,CACjD,GAAI,CACF,EAAW,MAAM,EAAc,CAAK,EAAI;CAAI,CAC9C,MAAQ,CAER,CACF,EASe,eAPW,CACxB,MAAM,EAAY,CAAU,EACxB,GACF,MAAM,IAAI,QAAe,GAAY,EAAW,UAAY,EAAQ,CAAC,CAAC,CAE1E,CAEqB,CACvB,CAWA,SAAS,GAAkB,EAAqC,EAAiC,CAC/F,IAAM,EAAkB,QAAQ,IAAI,qBAC9B,EAAW,EAAkB,GAAe,EAAiB,CAAU,EAAI,KAE3E,EAAe,EAAG,QAAQ,OAC1B,EAAc,OAAO,GAAiB,WAAa,EAAe,GAExE,GAAI,CAAC,EACH,OAAO,EAGT,IAAM,EAAwB,GAAoB,CAChD,EAAY,CAAK,EACjB,EAAS,KAAK,CAAK,CACrB,EAEA,MADA,GAAS,MAAQ,EAAS,MACnB,CACT,CAEA,SAAgB,EACd,EACA,EAAoC,CAAC,EACd,CACvB,IAAM,EAAO,GAAkB,EAAI,EAAQ,aAAe,EAAI,EA2C9D,MAAO,CAxCL,MAAM,EAAiB,EAA2B,CAGhD,IAAM,EAAY,IAAI,KAAK,CAAA,CAAE,YAAY,EACnC,EAAkB,EAAgB,CAAO,EAC3C,CAAE,YAAW,QAAO,MAAO,EAAQ,MAAO,GAAG,CAAQ,EACrD,CAAE,YAAW,QAAO,MAAO,CAAQ,EAEvC,GAAI,CACF,EAAK,CAAK,CACZ,MAAQ,CAER,CACF,EACA,KAAK,EAA2B,CAC9B,KAAK,MAAM,OAAQ,CAAO,CAC5B,EACA,KAAK,EAA2B,CAC9B,KAAK,MAAM,OAAQ,CAAO,CAC5B,EACA,MAAM,EAA2B,CAC/B,KAAK,MAAM,QAAS,CAAO,CAC7B,EACA,KAAK,EAA2B,CAC9B,KAAK,MAAM,OAAQ,CAAO,CAC5B,EACA,MAAM,EAA2B,CAC/B,KAAK,MAAM,QAAS,CAAO,CAC7B,EACA,QAAQ,EAA2B,CACjC,KAAK,MAAM,UAAW,CAAO,CAC/B,EACA,KAAK,EAA2B,CAC9B,KAAK,MAAM,OAAQ,CAAO,CAC5B,EACA,OAAuB,CACrB,OAAO,EAAK,QAAQ,GAAK,QAAQ,QAAQ,CAC3C,CAGU,CACd,CC9QA,eAAsB,EAAe,EAAiC,CACpE,MAAA,EAAA,EAAA,MAAA,EAAA,EAAA,EAAA,QAAA,CAAoB,CAAQ,EAAG,CAAE,UAAW,EAAK,CAAC,EAElD,IAAM,EAAS,MAAA,EAAA,EAAA,KAAA,CAAW,EAAU,GAAG,EAEjC,GAAS,EAA4B,EAAoB,IAAiB,EAC9E,EAAA,EAAA,UAAA,CAAU,EAAO,GAAI,OAAO,KAAK,CAAK,CAAC,EACvC,IAAM,EAAO,OAAO,GAAa,WAAa,EAAW,EAIzD,OAHI,OAAO,GAAS,YAClB,EAAK,EAEA,EACT,EAEA,QAAQ,OAAO,MAAQ,EACvB,QAAQ,OAAO,MAAQ,CACzB,CCvBO,IAAA,mCAC4B,OAAO,4BAA4B,kBACvC,OAAO,8BAA8B,mBACpC,OAAO,wBAAwB,sBAC5B,OAAO,2BAA2B,IACpE,AAAA,IAAA,CAAA,CAAD,EAEA,MAAa,GAA+C,CAC1D,OAAQ,0BACR,QAAS,2BACT,OAAQ,2BACR,MAAO,iCACP,OAAQ,iCACR,OAAQ,iCACR,QAAS,kCACT,UAAW,qCACX,OAAQ,kCACR,OAAQ,4BACR,MAAO,+BACP,OAAQ,iCACR,OAAQ,0BACR,QAAS,kCACT,OAAQ,kCACR,QAAS,kCAET,OAAQ,eACR,OAAQ,YACR,OAAQ,aACR,QAAS,aACT,OAAQ,YACR,QAAS,aACT,QAAS,aACT,OAAQ,gBACR,OAAQ,YACR,OAAQ,aACR,QAAS,aAET,QAAS,YACT,SAAU,aACV,OAAQ,WACR,OAAQ,WACR,OAAQ,gCAER,OAAQ,aACR,OAAQ,YACR,OAAQ,YACR,OAAQ,YACR,OAAQ,YACR,OAAQ,YACR,QAAS,aACT,OAAQ,YACR,QAAS,aACT,OAAQ,YACR,OAAQ,kBACR,OAAQ,YAER,OAAQ,kBACR,QAAS,mBACT,OAAQ,kBACR,MAAO,mBACP,OAAQ,oBACR,OAAQ,sBACR,MAAO,8BACP,eAAgB,2CAChB,OAAQ,0BACV,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,EChGA,MAgDM,GAAa,GAAkB,GAAS,KAAO,MAKxC,OAAwC,OAAO,QAAQ,QAAW,WAM/E,SAAS,GAAY,EAAkB,EAAY,IAAiC,CAClF,OAAO,IAAI,QAAS,GAAY,CAC9B,IAAI,EAAU,GACR,MAAa,CACb,IACJ,EAAU,GACV,aAAa,CAAK,EAClB,EAAQ,EACV,EACM,EAAQ,eAAiB,CAC7B,EAAO,oBAAoB,EAC3B,EAAK,CACP,EAAG,CAAS,EACZ,EAAM,MAAM,EAEZ,EAAO,MAAM,CAAI,EACjB,EAAO,qBAAqB,CAC9B,CAAC,CACH,CAOA,MAAM,OAAoB,CAAC,QAAQ,SAAU,GAAG,QAAQ,SAAU,GAAG,QAAQ,KAAK,MAAM,CAAC,CAAC,EAW1F,eAAsB,GACpB,EACA,EACA,EAAiC,CAAC,EAClB,CACZ,OAAO,QAAQ,QAAW,aAE5B,EAAO,MAAM,CAAE,MAAO,gBAAiB,IAAK,QAAQ,IAAK,MAAO,iCAAkC,CAAC,EACnG,QAAQ,KAAK,CAAC,GAGhB,EAAO,KAAK,CAAE,MAAO,aAAc,IAAK,QAAQ,IAAK,GAAG,CAAM,CAAC,EAE/D,MAAM,GAAY,CAAM,EACxB,MAAM,EAAO,MAAM,EAEnB,GAAI,CACF,QAAQ,OAAO,QAAQ,SAAU,GAAY,EAAG,QAAQ,GAAG,CAC7D,OAAS,EAAG,CACV,EAAO,MAAM,CAAE,MAAO,gBAAiB,IAAK,QAAQ,IAAK,MAAO,EAAY,CAAC,CAAE,CAAC,CAClF,CACA,QAAQ,KAAK,CAAC,CAChB,CAUA,SAAgB,EAAY,EAAsC,CAChE,GAAM,CACJ,kBACA,SACA,SACA,iBAAiB,IACjB,kBAAkB,KAChB,EAEJ,GAAI,IAAoB,GACtB,MAAO,CAAE,UAAa,CAAC,EAAG,SAAY,CAAC,CAAE,EAG3C,IAAI,EAAa,GACb,EAEE,MAAc,CAClB,GAAI,EAAY,OAChB,IAAM,EAAQ,GAAU,QAAQ,YAAY,IAAI,CAAC,EAC7C,GAAS,IAEb,EAAa,GACT,IAAU,IAAA,KACZ,cAAc,CAAK,EACnB,EAAQ,IAAA,IAIV,GAAoB,EAAQ,EAAQ,CAAE,OAAQ,SAAU,MAAO,KAAK,MAAM,CAAK,CAAE,CAAC,EACpF,EAEA,MAAO,CACL,OAAQ,CACF,IAAU,IAAA,IAAa,IAG3B,EAAQ,eAAiB,CACvB,EAAQ,YAAY,EAAO,CAAe,EAC1C,EAAM,MAAM,EACZ,EAAM,CACR,EAAG,CAAc,EACjB,EAAM,MAAM,EACd,EACA,MAAO,CACD,IAAU,IAAA,KACZ,cAAc,CAAK,EACnB,EAAQ,IAAA,GAEZ,CACF,CACF,CC1LA,IAAa,EAAb,cAAkC,KAAM,CACtC,YAAY,EAAiB,CAC3B,MAAM,CAAO,EACb,KAAK,KAAO,iBACd,CACF,ECMA,SAAS,EAAuB,EAAkC,CAChE,GAAI,OAAO,SAAS,CAAO,EACzB,OAAO,EAET,GAAI,OAAO,GAAY,SAAU,CAG/B,GAAI,CAAC,EAAQ,WAAW,YAAY,EAAG,CACrC,IAAM,EAAWA,EAAAA,QAAK,QAAQ,CAAO,EACrC,GAAIC,EAAAA,QAAG,WAAW,CAAQ,EACxB,OAAOA,EAAAA,QAAG,aAAa,CAAQ,CAEnC,CACA,OAAO,OAAO,KAAK,CAAO,CAC5B,CACA,MAAM,IAAI,EAAa,gDAAgD,CACzE,CAKA,SAAS,GAAsB,EAA+E,CAC5G,GAAI,CAAC,EACH,OAGF,GAAI,OAAO,GAAU,WAAY,GAAkB,MAAM,QAAQ,CAAK,EACpE,MAAM,IAAI,EAAa,wCAAwC,EAEjE,GAAI,OAAO,EAAM,KAAQ,UAAY,CAAC,OAAO,SAAS,EAAM,GAAG,EAC7D,MAAM,IAAI,EAAa,qDAAqD,EAE9E,GAAI,OAAO,EAAM,MAAS,UAAY,CAAC,OAAO,SAAS,EAAM,IAAI,EAC/D,MAAM,IAAI,EAAa,sDAAsD,EAG/E,IAAM,EAA4C,CAChD,IAAK,EAAuB,EAAM,GAAG,EACrC,KAAM,EAAuB,EAAM,IAAI,CACzC,EAUA,OARI,EAAM,KAAO,IAAA,KACX,MAAM,QAAQ,EAAM,EAAE,EACxB,EAAO,GAAK,EAAM,GAAG,IAAK,GAAS,EAAuB,CAAI,CAAC,EAE/D,EAAO,GAAK,EAAuB,EAAM,EAAE,GAIxC,CACT,CAKA,SAAgB,EAAqB,EAA6C,CAChF,GAAI,OAAO,GAAM,WAAY,GAAc,MAAM,QAAQ,CAAC,EACxD,MAAM,IAAI,EAAa,kCAAkC,EAI3D,GAAI,YAAa,EACf,MAAM,IAAI,EACR;;;;;+DAMF,EAGF,GAAM,CACJ,IAAK,EACL,OACA,KAAM,EACN,mBAAmB,IACnB,sBAAsB,IACtB,0BAA0B,IAAS,IACnC,SAAS,OAAO,QAAQ,SAAS,KAAK,MAAM,GAAG,CAAC,CAAC,KACjD,kBAAkB,IAClB,aAAa,CAAC,SAAS,EACvB,gBAAgB,CAAC,MAAM,EACvB,UAAU,CACR,qBACA,aACA,aACA,cACA,gBACA,cACA,WACA,eACA,iBACA,oBACA,WACA,WACF,EACA,QACA,MAAM,qBACN,kBAAkB,GAClB,cAAe,GACb,EAEE,EAAO,EAEP,EAAS,EAAE,QAAU,YAC3B,GAAI,IAAW,aAAe,OAAO,GAAW,WAC9C,MAAM,IAAI,EACR,2GACF,EAGF,GAAI,OAAO,GAAW,SACpB,MAAM,IAAI,EAAa,qCAAqC,EAG9D,IAAM,EAAcD,EAAAA,QAAK,QAAQ,QAAQ,IAAI,EAAG,CAAM,EAChD,EAAM,EAAY,SAASA,EAAAA,QAAK,GAAG,EAAI,EAAc,EAAcA,EAAAA,QAAK,IAE9E,GAAI,OAAO,GAAW,UAAY,EAAE,MAAM,QAAQ,CAAM,GAAK,EAAO,MAAO,GAAM,OAAO,GAAM,QAAQ,GACpG,MAAM,IAAI,EAAa,+DAA+D,EAGxF,GAAI,OAAO,GAAS,SAClB,MAAM,IAAI,EAAa,sCAAsC,EAG/D,GAAI,CAAC,OAAO,cAAc,CAAgB,GAAK,GAAoB,IACjE,MAAM,IAAI,EAAa,qEAAqE,EAG9F,GAAI,CAAC,OAAO,cAAc,CAAmB,GAAK,GAAuB,IACvE,MAAM,IAAI,EAAa,wEAAwE,EAGjG,GAAI,OAAO,GAAS,UAAY,CAAC,OAAO,cAAc,CAAI,EACxD,MAAM,IAAI,EAAa,gDAAgD,EAGzE,GAAI,GAAQ,GAAK,EAAO,MACtB,MAAM,IAAI,EAAa,uCAAuC,EAGhE,GAAI,OAAO,GAAoB,UAAY,GAAmB,GAAK,CAAC,OAAO,cAAc,CAAe,EACtG,MAAM,IAAI,EAAa,2DAA2D,EAGpF,GAAI,IAAQ,IAAS,OAAO,GAAQ,SAClC,MAAM,IAAI,EAAa,8CAA8C,EAGvE,GAAI,IAAoB,KAClB,OAAO,GAAoB,UAAY,CAAC,OAAO,SAAS,CAAe,GAAK,GAAmB,GACjG,MAAM,IAAI,EAAa,0EAA0E,EAIrG,GAAI,IAAqB,IAAA,KAAc,OAAO,GAAqB,UAAY,EAAiB,SAAW,GACzG,MAAM,IAAI,EAAa,8DAA8D,EAGvF,IAAM,EAAgB,IAAqB,IAAA,GAAY,IAAA,GAAYA,EAAAA,QAAK,QAAQ,QAAQ,IAAI,EAAG,CAAgB,EAE1GC,EAAAA,QAAG,WAAW,CAAG,GACpB,EAAA,QAAG,UAAU,EAAK,CAAE,UAAW,EAAK,CAAC,EAGvC,IAAM,EAAmC,CACvC,MACA,OACA,OACA,mBACA,sBACA,0BACA,SACA,kBACA,SACA,gBACA,aACA,gBACA,UACA,MAAO,GAAsB,CAAK,EAClC,MACA,kBACA,eAAgB,EAAM,iBACxB,EAMA,OAJI,QAAQ,KAAK,SAAS,WAAW,GACnC,QAAQ,IAAI,qBAAsB,CAAE,GAAG,EAAQ,MAAO,EAAO,MAAQ,MAAQ,OAAQ,CAAC,EAGjF,CACT,CCtMA,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,EAAG,CACV,EAAO,CAAC,CACV,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,CCTA,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,CCpBA,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,EAAb,cAAwC,CAAc,CACpD,YAAY,EAAkB,YAAa,CACzC,MAAM,EAAA,IAA6B,WAAW,CAChD,CACF,EAKa,EAAb,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,EACpB,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,EAChC,EAAQ,EAGZ,IAAK,IAAM,KAAQ,EAAO,CACxB,GAAI,GAAS,IACX,MAEF,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,EAC9C,GACF,CAEA,OAAO,CACT,CCXA,MAAM,GAAU,EAA2B,EAAmB,IAC5D,QAAQ,KAAK,CAAC,EAAa,IAAI,QAAS,GAAM,eAAiB,EAAE,CAAI,EAAG,CAAS,CAAC,CAAC,CAAC,EAMtF,SAAS,GAAqB,EAAoB,EAA0B,EAAqB,CAE/F,GAAI,OAAO,EAAE,UAAa,WACxB,OAEF,IAAM,EAAW,EAAE,SACnB,EAAI,KAAK,YAAe,CACtB,EAAW,CAAQ,CAAC,CAAC,MAAO,GAAe,CACzC,EAAG,OAAO,MAAM,CAAE,MAAO,iBAAkB,IAAK,EAAE,aAAc,MAAO,EAAY,CAAC,CAAE,CAAC,CACzF,CAAC,CACH,CAAC,CACH,CAEA,SAAgB,GAAa,EAAyD,CACpF,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,EAA6B,CACjC,SACA,KACA,MACA,MAAO,EAAW,EAAI,YAAY,EAClC,KAAM,CAAC,EACP,QAAS,EAAI,QACb,OAAQ,GAAY,EAAI,QAAQ,MAA4B,EAC5D,KAAM,CAAC,CACT,EAGA,EAAI,UAAU,yBAA0B,SAAS,EACjD,EAAI,UAAU,kBAAmB,MAAM,EACvC,EAAI,UAAU,mBAAoB,eAAe,EAE7C,EAAG,QAAQ,MAAQ,IACrB,EAAI,UAAU,0BAA2B,EAAG,QAAQ,GAAG,EAGzD,IAAI,EAA2B,CAC7B,OAAQ,GACR,MAAO,EACP,UAAW,EACb,EAEA,EAAG,OAAO,KAAK,CAAE,MAAO,UAAW,SAAQ,KAAI,KAAM,EAAI,QAAS,CAAC,EAEnE,IAAM,EAAQ,YAAY,IAAI,EAC9B,EAAI,KAAK,aAAgB,CACvB,IAAM,EAA6B,CACjC,MAAO,WACP,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,IAAM,EAAS,MAAM,EAAU,EAAK,EAAW,OAAQ,EAAG,QAAQ,eAAe,EACjF,EAAW,KAAO,EAAO,KACzB,EAAc,EAAO,QAErB,IAAM,EAAI,MAAM,EAAG,OAAO,CAAG,EAC7B,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,CAGA,GAAqB,EAAI,EAAK,CAAC,EAE/B,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,EAAM,iBACR,IAEe,EAAM,kBAAmB,CACtC,EAAG,OAAO,KAAK,CACb,MAAO,oBACP,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,EAAM,cACR,EAEA,GAAI,IAAW,EAAM,eAAgB,CACnC,EAAG,OAAO,KAAK,CAAE,MAAO,iBAAkB,OAAQ,EAAW,OAAQ,GAAI,EAAW,EAAG,CAAC,EACxF,EAAa,EAAK,CAAE,QAAS,mBAAoB,EAAA,GAA+B,EAChF,MACF,CAEI,IAAW,EAAM,eACnB,EAAa,EAAK,CAAM,CAE5B,OAAS,EAAG,CACN,aAAa,GACf,EAAG,OAAO,MAAM,CACd,GAAG,EACH,MAAO,gBACP,MAAO,EAAE,OACX,CAAC,EACD,EAAa,EAAK,CAAE,QAAS,EAAE,OAAQ,EAAG,EAAE,KAAK,IAEjD,EAAG,OAAO,MAAM,CACd,GAAG,EACH,MAAO,gBACP,MAAO,EAAY,CAAC,CACtB,CAAC,EACD,EAAa,EAAK,CAAE,QAAS,uBAAwB,EAAA,GAA+B,EAExF,CACF,EAEM,EAAS,EAAG,QAAQ,MACtB,EAAA,QAAM,aACJ,CACE,IAAK,EAAG,QAAQ,MAAM,IACtB,KAAM,EAAG,QAAQ,MAAM,KACvB,GAAI,EAAG,QAAQ,MAAM,EACvB,EACA,CACF,EACA,EAAA,QAAK,aAAa,CAAc,EAEpC,OAAO,IAAI,SAAS,EAAS,IAAW,CACtC,IAAI,EAAY,GAEhB,EAAO,GAAG,YAAe,CACvB,EAAG,OAAO,KAAK,CACb,MAAO,eACP,KAAM,EAAG,QAAQ,KACjB,KAAM,EAAG,QAAQ,IACnB,CAAC,CACH,CAAC,EAED,EAAO,KAAK,gBAAmB,CAC7B,EAAY,GACZ,EAAG,OAAO,KAAK,CACb,MAAO,iBACP,QAAS,QACT,IAAK,QAAQ,IACb,SAAU,EAAG,QAAQ,MAAQ,QAAU,OACvC,KAAM,EAAG,QAAQ,KACjB,KAAM,EAAG,QAAQ,IACnB,CAAC,EACD,EAAG,OAAO,KAAK,CACb,MAAO,mBACP,UAAW,EAAG,QAAQ,GACxB,CAAC,EACD,EAAQ,CAAM,CAChB,CAAC,EAED,EAAO,GAAG,QAAU,GAAM,CACxB,EAAG,OAAO,MAAM,CACd,MAAO,cACP,MAAO,EAAY,CAAC,CACtB,CAAC,EACG,IAMJ,EAAO,CAAC,CACV,CAAC,EAED,EAAO,OAAO,EAAG,QAAQ,KAAM,EAAG,QAAQ,IAAI,CAChD,CAAC,CACH,CClPA,SAAS,GAAE,EAAG,CAAC,CAEf,MAAM,GAAI,GCsBG,EAAc,IAAI,IAEzB,GAAe,GAAGC,EAAAA,IAAI,cAAcA,EAAAA,MAEpC,EAAW,IAAI,IAAI,CAAC,MAAO,OAAQ,OAAQ,OAAQ,MAAO,OAAQ,OAAQ,MAAM,CAAC,EACjF,EAAa,CAAC,GAAG,EAAU,OAAO,EAElC,GAAqC,CACzC,MAAO,CAAC,MAAO,MAAM,EACrB,OAAQ,CAAC,MAAM,EACf,OAAQ,CAAC,MAAM,EACf,OAAQ,CAAC,MAAM,CACjB,EAEM,GAAU,GAAsB,CAAC,EAAU,WAAW,GAAG,GAAK,EAAA,EAAA,EAAA,WAAA,CAAY,CAAS,EAEnF,GAAa,GAAoB,CACrC,GAAI,CACF,IAAM,GAAA,EAAA,EAAA,SAAA,CAAa,EAAE,QAAQ,EAC7B,OAAO,EAAE,UAAY,EAAE,SAAW,EAAE,OAAS,EAAE,IACjD,MAAQ,CAEN,MAAO,EACT,CACF,EAEM,EAAU,GAAiB,CAC/B,GAAI,CACF,OAAA,EAAA,EAAA,SAAA,CAAgB,CAAI,CAAC,CAAC,OAAO,CAC/B,MAAQ,CACN,MAAO,EACT,CACF,EAKM,GAAY,GAAiB,CACjC,GAAI,EAAO,CAAI,EACb,OAAO,EAGT,IAAM,GAAA,EAAA,EAAA,QAAA,CAAc,CAAI,EAClB,EAAO,EAAK,MAAM,EAAG,EAAK,OAAS,EAAI,MAAM,EACnD,IAAK,IAAM,KAAK,GAAS,IAAQ,CAAC,EAChC,GAAI,EAAO,EAAO,CAAC,EACjB,OAAO,EAAO,EAGlB,IAAK,IAAM,KAAK,EACd,GAAI,EAAO,EAAO,CAAC,EACjB,OAAO,EAAO,EAGlB,IAAK,IAAM,KAAK,EACd,GAAI,GAAA,EAAA,EAAA,KAAA,CAAY,EAAM,QAAU,CAAC,CAAC,EAChC,OAAA,EAAA,EAAA,KAAA,CAAY,EAAM,QAAU,CAAC,CAGnC,EAYA,IAAa,GAAb,KAA2B,CACzB,KAEA,YAAY,EAA2D,CACrE,KAAK,KAAO,CACd,CAOA,QAAgB,EAAc,EAAmB,CAC/C,IAAM,EAAiC,CACrC,kBAAmB,KAAK,WAAA,EAAA,EAAA,cAAA,CAAwB,CAAI,CAAC,CAAC,IAAI,EAC1D,uBAAwB,KAAK,UAAU,CAAI,EAC3C,sBAAuB,KAAK,WAAA,EAAA,EAAA,QAAA,CAAkB,CAAI,CAAC,CACrD,EACI,IACF,EAAO,UAAY,KAAK,WAAA,EAAA,EAAA,QAAA,CAAkB,CAAI,CAAC,EAC/C,EAAO,WAAa,KAAK,UAAU,CAAI,GAGzC,GAAM,CAAE,gBAAA,EAAA,EAAA,UAAA,CAA0B,CAChC,YAAa,CAAC,CAAI,EAClB,OAAQ,GACR,MAAO,GACP,SAAU,OACV,OAAQ,MACR,OAAQ,KAAK,KAAK,OAClB,UAAW,SACX,eAAgB,GAChB,SAAU,SAEV,UAAW,CAAE,iBAAkB,EAAM,EACrC,QACF,CAAC,EACD,OAAO,EAAY,EAAE,CAAC,IACxB,CAEA,QAAQ,EAA2B,CACjC,OAAO,KAAK,MAAA,EAAA,EAAA,QAAA,CAAa,CAAY,CAAC,CAAC,CAAC,OAC1C,CAEA,MAAc,EAAc,CAC1B,IAAM,GAAA,EAAA,EAAA,QAAA,CAAc,CAAI,EACxB,OAAO,EAAK,WAAW,KAAK,KAAK,GAAG,GAAK,CAAC,EAAK,SAAS,EAAY,IAAM,EAAS,IAAI,CAAG,GAAK,IAAQ,QACzG,CAKA,KAAa,EAA4B,CACvC,IAAM,EAAS,EAAY,IAAI,CAAI,EACnC,GAAI,IAAW,IAAA,GACb,OAAO,KAAK,UAAU,CAAI,EAG5B,IAAM,EAAQ,KAAK,aAAa,CAAM,EACtC,GAAI,CAAC,KAAK,SAAS,CAAK,EACtB,OAAO,EAET,IAAK,IAAM,KAAK,EAEV,EAAY,IAAI,EAAE,QAAQ,IAAM,GAClC,EAAY,OAAO,EAAE,QAAQ,EAGjC,OAAO,KAAK,UAAU,CAAI,CAC5B,CAEA,aAAqB,EAAoB,CACvC,IAAM,EAAQ,IAAI,IAAI,CAAC,CAAI,CAAC,EAE5B,IAAK,IAAM,KAAK,EACd,IAAK,IAAM,KAAO,EAAE,KAAK,OAAO,EAC9B,EAAM,IAAI,CAAG,EAGjB,OAAO,CACT,CAEA,SAAiB,EAA0B,CACzC,IAAK,IAAM,KAAK,EACd,GAAI,EAAY,IAAI,EAAE,QAAQ,IAAM,GAAK,GAAU,CAAC,EAClD,MAAO,GAGX,MAAO,EACT,CAEA,UAAkB,EAA4B,CAE5C,GAAM,CAAE,UAAS,SAAA,EAAA,EAAA,SAAA,CAAkB,CAAI,EACjC,EAAoB,CAAE,GAAI,EAAM,SAAU,EAAM,QAAS,CAAC,EAAG,UAAS,OAAM,KAAM,IAAI,GAAM,EAElG,EAAY,IAAI,EAAM,CAAG,EACzB,GAAI,CACF,KAAK,SAAS,EAAM,CAAG,CACzB,OAAS,EAAG,CAEV,MADA,EAAY,OAAO,CAAI,EACjB,CACR,CACA,OAAO,CACT,CAEA,SAAiB,EAAc,EAAmB,CAChD,IAAM,GAAA,EAAA,EAAA,QAAA,CAAc,CAAI,EACxB,GAAI,IAAQ,QAAS,CACnB,EAAI,QAAU,KAAK,OAAA,EAAA,EAAA,aAAA,CAAmB,EAAM,MAAM,CAAC,EACnD,MACF,CACA,GAAI,CAAC,EAAS,IAAI,CAAG,EACnB,MAAU,MAAM,yCAAyC,EAAI,KAAK,EAAK,EAAE,EAI3E,KADgB,gBAAgB,CAC/B,CAAC,CAAC,KAAK,EAAI,QAAS,EAAI,QAAS,KAAK,oBAAoB,CAAG,EAAG,EAAK,GAAA,EAAA,EAAA,QAAA,CAAc,CAAI,CAAC,CAC3F,CAOA,gBAAwB,EAAc,CACpC,IAAM,EAAS,CAAC,UAAW,UAAW,SAAU,aAAc,WAAW,EACzE,GAAI,CACF,OAAA,EAAA,EAAA,gBAAA,CAAuB,KAAK,QAAQ,EAAM,EAAK,EAAG,EAAQ,CAAE,SAAU,CAAK,CAAC,CAC9E,OAAS,EAAG,CAEV,GAAI,EADe,aAAa,aAAe,uBAAuB,KAAK,EAAE,OAAO,GAElF,MAAM,EAER,OAAA,EAAA,EAAA,gBAAA,CAAuB,KAAK,QAAQ,EAAM,EAAI,EAAG,EAAO,MAAM,EAAG,CAAC,EAAG,CAAE,SAAU,CAAK,CAAC,CACzF,CACF,CAKA,oBAA4B,EAAoB,CAC9C,IAAM,GAAA,EAAA,EAAA,cAAA,CAAuB,EAAK,QAAQ,EACpC,EAAU,GACd,GAAO,CAAS,EAAI,IAAA,GAAY,IAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,CAAyB,EAAK,QAAQ,EAAG,CAAS,CAAC,EAE/E,EAAO,GAAsB,CACjC,IAAM,EAAO,EAAO,CAAS,EAC7B,GAAI,IAAS,IAAA,GAEX,OAAO,EAAO,CAAS,EAEzB,GAAI,CAAC,KAAK,MAAM,CAAI,EAClB,OAAO,EAAO,CAAI,EAEpB,IAAM,EAAM,KAAK,KAAK,CAAI,EAG1B,OADA,EAAK,KAAK,IAAI,EAAM,CAAG,EAChB,EAAI,OACb,EAGA,MAFA,GAAI,QAAW,GAAsB,EAAO,CAAS,GAAK,EAAO,QAAQ,CAAS,EAClF,EAAI,MAAQ,EAAO,MACZ,CACT,CACF,EC5PA,SAAS,EAAgB,EAAgD,EAAmC,CAC1G,GAAI,OAAO,GAAM,WAAY,EAC3B,MAAO,GAKT,GAFA,GAA8B,CAAC,EAE3B,OAAO,EAAE,SAAY,WAEvB,OADA,EAAG,OAAO,MAAM,CAAE,MAAO,gBAAiB,MAAO,4BAA6B,CAAC,EACxE,GAGT,GAAI,EAAE,WAAa,IAAA,IAAa,OAAO,EAAE,UAAa,WAEpD,OADA,EAAG,OAAO,MAAM,CAAE,MAAO,gBAAiB,MAAO,yCAA0C,CAAC,EACrF,GAGT,IAAM,EAAK,EAAE,iBAWb,OAVI,IAAO,IAAA,KAAc,CAAC,OAAO,cAAc,CAAE,GAAK,EAAK,MACzD,EAAG,OAAO,MAAM,CAAE,MAAO,gBAAiB,MAAO,wDAAyD,CAAC,EACpG,IAGL,EAAE,OAAA,EAKC,IAJL,EAAG,OAAO,MAAM,CAAE,MAAO,gBAAiB,MAAO,mDAAoD,CAAC,EAC/F,GAIX,CAEA,SAAgB,GACd,EACA,EACA,EACkB,CAGlB,IAAI,EAAI,IAFW,GAAc,EAAG,OAEvB,CAAC,CAAC,QAAQ,CAAY,EACnC,GAAI,EAAgB,EAAI,EAAE,OAAO,EAC/B,EAAI,EAAE,aACD,GAAI,GAAgB,EAAI,CAAC,EAE9B,MAAM,IAAI,EACR,2BAA2B,EAAa,0DAC1C,EAMF,MAHA,GAAE,aAAe,EACjB,EAAE,QAAU,EAAK,QAEV,CACT,CC5CA,SAAgB,GAAa,EAA+D,CAC1F,OAAO,KAAO,IAAQ,CACpB,IAAM,EAAe,EAAI,SAAS,QAAQ,QAAS,EAAE,CAAC,CAAC,QAAQ,QAAS,EAAE,EACpE,EAAeC,EAAAA,QAAK,KAAK,EAAG,QAAQ,IAAK,CAAY,EAGrD,EAAYA,EAAAA,QAAK,SAAS,EAAG,QAAQ,IAAK,CAAY,EAM5D,GALI,EAAU,WAAW,IAAI,GAAKA,EAAAA,QAAK,WAAW,CAAS,GAKvD,EAAG,QAAQ,QAAQ,KAAM,IAAA,EAAA,EAAA,UAAA,CAAgB,EAAc,CAAC,CAAC,EAC3D,OAIF,IAAM,EAAO,MAAMC,EAAAA,QAAG,KAAK,CAAY,CAAC,CAAC,UAAY,IAAA,EAAS,EAC1D,MAAC,GAAQ,CAAC,EAAK,OAAO,GAI1B,IAAI,EAAG,QAAQ,WAAW,KAAM,IAAA,EAAA,EAAA,UAAA,CAAgB,EAAc,CAAC,CAAC,EAC9D,OAAO,GAAkB,EAAI,EAAc,CAAI,EAGjD,GAAI,EAAG,QAAQ,cAAc,KAAM,IAAA,EAAA,EAAA,UAAA,CAAgB,EAAc,CAAC,CAAC,EACjE,OAAO,GAAmB,CAAY,CAJS,CAQnD,CACF,CAEA,SAAS,GAAmB,EAAwC,CAClE,MAAO,CACL,KAAA,EACA,QAAS,IACT,eACA,QAAS,MAAO,EAAY,EAAI,EAAK,IAAQ,CAC3C,GAAI,EAAW,SAAW,OAAS,EAAW,SAAW,OAAQ,CAC/D,EAAI,WAAa,IACjB,EAAI,UAAU,QAAS,WAAW,EAClC,EAAI,IAAI,EACR,MACF,CAEA,IAAM,EAAO,MAAMA,EAAAA,QAAG,KAAK,CAAY,CAAC,CAAC,MAAO,GAAa,CAAC,EAC9D,GAAI,aAAgB,MAQlB,MAPA,GAAI,WAAa,IACjB,EAAI,IAAI,gBAAgB,EACxB,EAAG,OAAO,MAAM,CACd,MAAO,sBACP,IAAK,EAAW,IAAI,SACpB,MAAO,EAAK,OACd,CAAC,EACM,KAGT,GAAI,CAAC,EAAK,OAAO,EAAG,CAClB,EAAI,WAAa,IACjB,EAAI,IAAI,WAAW,EACnB,MACF,CAGA,IAAM,EAAc,GADFD,EAAAA,QAAK,QAAQ,CAAY,CAAC,CAAC,YACI,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,GAAA,EAAA,EAAA,iBAAA,CAA0B,CAAY,EAEtC,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,EAAM,aAAa,CAC7B,EAEM,MAAsB,CAC1B,EAAQ,EACR,EAAO,QAAQ,EACf,EAAQ,EAAM,aAAa,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,CChHA,eAAsB,GAAQ,EAAoD,CAEhF,IAAM,EAAU,CAAE,QADS,EAAqC,iBAAmB,EAAM,kBAC1C,EAAU,EAAqB,CAAO,CAAE,EAGnF,EAAQ,QAAQ,eAClB,MAAM,EAAe,EAAQ,QAAQ,aAAa,EAMpD,IAAM,EAAc,EAAQ,QAAQ,kBAAoB,IAAS,GAAuB,EACxF,EAAQ,OAAS,EAAa,EAA4C,CAAE,WAAY,CAAY,CAAC,EACrG,EAAQ,OAAS,GAAa,CAAqD,EAGnF,IAAM,EAAS,MAAM,GAAa,CAAO,EAErC,EAAQ,QAAQ,kBAAoB,IAAS,CAAC,GAChD,EAAQ,OAAO,KAAK,CAClB,MAAO,kBACP,OAAQ,0DACR,gBAAiB,QACjB,YAAa,QAAQ,QACrB,gBAAiB,EAAQ,QAAQ,eACnC,CAAC,EAGH,IAAM,EAAY,EAAY,CAC5B,gBAAiB,EAAc,EAAQ,QAAQ,gBAAkB,GACjE,SACA,OAAQ,EAAQ,MAClB,CAAC,EAGK,EAAY,GAA2B,CAC3C,EAAU,KAAK,EACf,EAAQ,OAAO,KAAK,CAAE,MAAO,eAAgB,IAAK,QAAQ,IAAK,QAAO,CAAC,EACvE,EAAO,MAAM,SAAY,CACvB,MAAM,EAAQ,OAAO,MAAM,EAC3B,QAAQ,KAAK,CAAC,CAChB,CAAC,CACH,EAEA,QAAQ,KAAK,aAAgB,EAAS,QAAQ,CAAC,EAC/C,QAAQ,KAAK,cAAiB,EAAS,SAAS,CAAC,EAKjD,EAAU,MAAM,CAClB,CC5CA,SAAgB,GACd,EACA,EACqB,CACrB,GAAI,OAAO,GAAM,WAAY,CAC3B,GAAI,IAAa,IAAA,IAAa,OAAO,GAAa,WAChD,MAAM,IAAI,EAAa,iDAAiD,OAAO,GAAU,EAE3F,MAAO,CAAE,QAAS,EAAG,WAAU,KAAA,CAAqB,CACtD,CAEA,GAAI,OAAO,GAAM,WAAY,EAC3B,MAAM,IAAI,EACR,oFAAoF,OAAO,GAC7F,EAGF,GAAI,OAAO,EAAE,SAAY,WACvB,MAAM,IAAI,EAAa,qDAAqD,EAG9E,GAAI,EAAE,WAAa,IAAA,IAAa,OAAO,EAAE,UAAa,WACpD,MAAM,IAAI,EAAa,kEAAkE,EAG3F,GAAI,EAAE,UAAY,IAAA,KAAc,CAAC,MAAM,QAAQ,EAAE,OAAO,GAAK,EAAE,QAAQ,KAAM,GAAM,OAAO,GAAM,QAAQ,GACtG,MAAM,IAAI,EAAa,0EAA0E,EAGnG,GACE,EAAE,cAAgB,IAAA,KACjB,CAAC,MAAM,QAAQ,EAAE,WAAW,GAAK,EAAE,YAAY,KAAM,GAAM,OAAO,GAAM,UAAU,GAEnF,MAAM,IAAI,EAAa,gFAAgF,EAGzG,MAAO,CAAE,GAAG,EAAG,KAAA,CAAqB,CACtC,CAEA,SAAgB,GAAwB,EAAkD,CACxF,GAAI,OAAO,GAAe,WACxB,MAAM,IAAI,EAAa,0DAA0D,OAAO,GAAY,EAEtG,OAAO,CACT,CAEA,SAAgB,GAAoB,EAA2B,CAC7D,GAAI,OAAO,GAAa,WACtB,MAAM,IAAI,EAAa,wDAAwD,OAAO,GAAU,EAElG,OAAO,CACT"}
|