fluxion-ts 0.8.2 → 0.8.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.cts +15 -2
- package/dist/index.d.mts +15 -2
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -18,6 +18,12 @@ interface LogEntry {
|
|
|
18
18
|
type LoggerOption = 'one-line' | 'json-line' | FluxionLoggerFn;
|
|
19
19
|
type FluxionLoggerFn = (entry: LogEntry) => void;
|
|
20
20
|
//#endregion
|
|
21
|
+
//#region src/common/consts.d.ts
|
|
22
|
+
declare const enum FluxionModuleType {
|
|
23
|
+
Api = 0,
|
|
24
|
+
StaticResource = 1
|
|
25
|
+
}
|
|
26
|
+
//#endregion
|
|
21
27
|
//#region src/types.d.ts
|
|
22
28
|
interface NormalizedRequest {
|
|
23
29
|
/**
|
|
@@ -137,6 +143,10 @@ interface FluxionOptions {
|
|
|
137
143
|
/**
|
|
138
144
|
* Logger output mode or custom logger sink.
|
|
139
145
|
* Defaults to `one-line`.
|
|
146
|
+
*
|
|
147
|
+
* Also accepts a logger function that will replace the default one.
|
|
148
|
+
*
|
|
149
|
+
* ! **ATTENTION** Fluxion calls the logging function synchronously and fails silently; make sure to handle exceptions yourself.
|
|
140
150
|
*/
|
|
141
151
|
logger?: LoggerOption;
|
|
142
152
|
/**
|
|
@@ -213,6 +223,9 @@ interface FluxionModule {
|
|
|
213
223
|
*/
|
|
214
224
|
methods?: HTTPMethod[];
|
|
215
225
|
}
|
|
226
|
+
type FluxionModuleWithType = FluxionModule & {
|
|
227
|
+
type: FluxionModuleType;
|
|
228
|
+
};
|
|
216
229
|
//#endregion
|
|
217
230
|
//#region src/fluxion.d.ts
|
|
218
231
|
declare function fluxion(options: FluxionOptions): Promise<void>;
|
|
@@ -223,11 +236,11 @@ declare function fluxion(options: FluxionOptions): Promise<void>;
|
|
|
223
236
|
* @param handler Main function that handles request and response instances
|
|
224
237
|
* @param disposer Deal with resource cleanup when the server is about to close
|
|
225
238
|
*/
|
|
226
|
-
declare function defineFluxionModule(handler: FluxionHandler, disposer?: FluxionDispose):
|
|
239
|
+
declare function defineFluxionModule(handler: FluxionHandler, disposer?: FluxionDispose): FluxionModuleWithType;
|
|
227
240
|
/**
|
|
228
241
|
* Provides type safety for defining Fluxion modules.
|
|
229
242
|
*/
|
|
230
|
-
declare function defineFluxionModule(fluxionModule: FluxionModule):
|
|
243
|
+
declare function defineFluxionModule(fluxionModule: FluxionModule): FluxionModuleWithType;
|
|
231
244
|
//#endregion
|
|
232
245
|
export { type FluxionDispose, type FluxionHandler, type FluxionModule as FluxionHandlerModule, type FluxionOptions, defineFluxionModule, fluxion };
|
|
233
246
|
//# sourceMappingURL=index.d.cts.map
|
package/dist/index.d.mts
CHANGED
|
@@ -18,6 +18,12 @@ interface LogEntry {
|
|
|
18
18
|
type LoggerOption = 'one-line' | 'json-line' | FluxionLoggerFn;
|
|
19
19
|
type FluxionLoggerFn = (entry: LogEntry) => void;
|
|
20
20
|
//#endregion
|
|
21
|
+
//#region src/common/consts.d.ts
|
|
22
|
+
declare const enum FluxionModuleType {
|
|
23
|
+
Api = 0,
|
|
24
|
+
StaticResource = 1
|
|
25
|
+
}
|
|
26
|
+
//#endregion
|
|
21
27
|
//#region src/types.d.ts
|
|
22
28
|
interface NormalizedRequest {
|
|
23
29
|
/**
|
|
@@ -137,6 +143,10 @@ interface FluxionOptions {
|
|
|
137
143
|
/**
|
|
138
144
|
* Logger output mode or custom logger sink.
|
|
139
145
|
* Defaults to `one-line`.
|
|
146
|
+
*
|
|
147
|
+
* Also accepts a logger function that will replace the default one.
|
|
148
|
+
*
|
|
149
|
+
* ! **ATTENTION** Fluxion calls the logging function synchronously and fails silently; make sure to handle exceptions yourself.
|
|
140
150
|
*/
|
|
141
151
|
logger?: LoggerOption;
|
|
142
152
|
/**
|
|
@@ -213,6 +223,9 @@ interface FluxionModule {
|
|
|
213
223
|
*/
|
|
214
224
|
methods?: HTTPMethod[];
|
|
215
225
|
}
|
|
226
|
+
type FluxionModuleWithType = FluxionModule & {
|
|
227
|
+
type: FluxionModuleType;
|
|
228
|
+
};
|
|
216
229
|
//#endregion
|
|
217
230
|
//#region src/fluxion.d.ts
|
|
218
231
|
declare function fluxion(options: FluxionOptions): Promise<void>;
|
|
@@ -223,11 +236,11 @@ declare function fluxion(options: FluxionOptions): Promise<void>;
|
|
|
223
236
|
* @param handler Main function that handles request and response instances
|
|
224
237
|
* @param disposer Deal with resource cleanup when the server is about to close
|
|
225
238
|
*/
|
|
226
|
-
declare function defineFluxionModule(handler: FluxionHandler, disposer?: FluxionDispose):
|
|
239
|
+
declare function defineFluxionModule(handler: FluxionHandler, disposer?: FluxionDispose): FluxionModuleWithType;
|
|
227
240
|
/**
|
|
228
241
|
* Provides type safety for defining Fluxion modules.
|
|
229
242
|
*/
|
|
230
|
-
declare function defineFluxionModule(fluxionModule: FluxionModule):
|
|
243
|
+
declare function defineFluxionModule(fluxionModule: FluxionModule): FluxionModuleWithType;
|
|
231
244
|
//#endregion
|
|
232
245
|
export { type FluxionDispose, type FluxionHandler, type FluxionModule as FluxionHandlerModule, type FluxionOptions, defineFluxionModule, fluxion };
|
|
233
246
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{createRequire as e}from"node:module";import t from"node:fs";import n from"node:path";import r from"node:cluster";import i from"node:os";import a from"node:http";import o from"node:https";import s from"chokidar";import{minimatch as c}from"minimatch";var l=e(import.meta.url);function u(e=new Date){return`${e.getFullYear()}.${String(e.getMonth()+1).padStart(2,`0`)}.${String(e.getDate()).padStart(2,`0`)} ${String(e.getHours()).padStart(2,`0`)}:${String(e.getMinutes()).padStart(2,`0`)}:${String(e.getSeconds()).padStart(2,`0`)}.${String(e.getMilliseconds()).padStart(3,`0`)}`}const d=JSON.stringify,f=Object.keys,p=process.env.FLUXION_COLORS!==`0`;let m;(function(e){e.reset=p?`\x1B[0m`:``,e.bold=p?`\x1B[1m`:``,e.dim=p?`\x1B[2m`:``,e.italic=p?`\x1B[3m`:``,e.underline=p?`\x1B[4m`:``,e.blink=p?`\x1B[5m`:``,e.inverse=p?`\x1B[7m`:``,e.black=p?`\x1B[30m`:``,e.red=p?`\x1B[31m`:``,e.green=p?`\x1B[32m`:``,e.yellow=p?`\x1B[33m`:``,e.blue=p?`\x1B[34m`:``,e.magenta=p?`\x1B[35m`:``,e.cyan=p?`\x1B[36m`:``,e.white=p?`\x1B[37m`:``,e.brightBlack=p?`\x1B[90m`:``,e.brightRed=p?`\x1B[91m`:``,e.brightGreen=p?`\x1B[92m`:``,e.brightYellow=p?`\x1B[93m`:``,e.brightBlue=p?`\x1B[94m`:``,e.brightMagenta=p?`\x1B[95m`:``,e.brightCyan=p?`\x1B[96m`:``,e.brightWhite=p?`\x1B[97m`:``,e.bgBlack=p?`\x1B[40m`:``,e.bgRed=p?`\x1B[41m`:``,e.bgGreen=p?`\x1B[42m`:``,e.bgYellow=p?`\x1B[43m`:``,e.bgBlue=p?`\x1B[44m`:``,e.bgMagenta=p?`\x1B[45m`:``,e.bgCyan=p?`\x1B[46m`:``,e.bgWhite=p?`\x1B[47m`:``,e.bgBrightBlack=p?`\x1B[100m`:``,e.bgBrightRed=p?`\x1B[101m`:``,e.bgBrightGreen=p?`\x1B[102m`:``,e.bgBrightYellow=p?`\x1B[103m`:``,e.bgBrightBlue=p?`\x1B[104m`:``,e.bgBrightMagenta=p?`\x1B[105m`:``,e.bgBrightCyan=p?`\x1B[106m`:``,e.bgBrightWhite=p?`\x1B[107m`:``,e.purple=p?`\x1B[38;2;225;16;248m`:``,e.orange=p?`\x1B[38;2;248;147;16m`:``,e.darkGreen=p?`\x1B[38;2;22;101;52m`:``,e.claude=p?`\x1B[38;2;217;119;87m`:``,e.deepseek=p?`\x1B[38;2;57;100;254m`:``,e.gpt=p?`\x1B[38;2;41;60;77m`:``})(m||={});const h=e=>{try{return d(e)}catch{return`[unserializable]`}},g={INFO:`${m.cyan}INFO${m.reset}`,WARN:`${m.orange}WARN${m.reset}`,ERROR:`${m.red}ERROR${m.reset}`,SUCC:`${m.green}SUCC${m.reset}`,DEBUG:`${m.blue}DEBUG${m.reset}`,VERBOSE:`${m.purple}VERBOSE${m.reset}`},_=e=>{let{level:t,timestamp:n,event:r,message:i,...a}=e,o=`${m.darkGreen}[${n}]${m.reset}`,s=g[t]??t,c=i??r,l=f(a).length>0?`${m.dim}${h(a)}${m.reset}`:``;console.log(`${o} ${s} ${c}${l}`)};function v(e){let t=e.options.logger;return t===void 0||t===`one-line`?_:t===`json-line`?e=>console.log(h(e)):t}function ee(e){let t=v(e);return{write(e,n,r={}){let i={...r,timestamp:u(),level:e,event:n};try{t(i)}catch{}},info(e,t){this.write(`INFO`,e,t)},warn(e,t){this.write(`WARN`,e,t)},error(e,t){this.write(`ERROR`,e,t)},succ(e,t){this.write(`SUCC`,e,t)},debug(e,t){this.write(`DEBUG`,e,t)},verbose(e,t){this.write(`VERBOSE`,e,t)}}}function y(e,t){let n=`[${t}]`;return{write(t,r,i){e.write(t,`${n} ${r}`,i)},info(t,r){e.info(`${n} ${t}`,r)},warn(t,r){e.warn(`${n} ${t}`,r)},error(t,r){e.error(`${n} ${t}`,r)},succ(t,r){e.succ(`${n} ${t}`,r)},debug(t,r){e.debug(`${n} ${t}`,r)},verbose(t,r){e.verbose(`${n} ${t}`,r)}}}const b=typeof Error.isError==`function`?e=>Error.isError(e)?e.message:String(e):e=>e?.message||String(e);function x(e={}){let t=e.restartWhen??{},n=t.healthzTimeout??3e4;return n!==1/0&&(!Number.isFinite(n)||n<1e4)&&$throw(`workerOptions.restartWhen.healthzTimeout must be a finite number >= 10000 (ms) or Infinity`),{maxWorkerCount:e.maxWorkerCount??4,restartWhen:{memoryUsageGreaterThan:t.memoryUsageGreaterThan??1/0,healthzTimeout:n,uptimeGreaterThan:t.uptimeGreaterThan??1/0}}}function S(e,r){if(Buffer.isBuffer(e))return e;if(typeof e==`string`){if(!e.startsWith(`-----BEGIN`)){let i=n.isAbsolute(e)?e:n.join(r,e);if(t.existsSync(i))return t.readFileSync(i)}return Buffer.from(e)}$throw(`Certificate content must be a string or Buffer`)}function te(e,t){if(!e)return;(typeof e!=`object`||!e||Array.isArray(e))&&$throw(`FluxionOptions.https must be an object`),typeof e.key!=`string`&&$throw(`FluxionOptions.https.key must be a string`),typeof e.cert!=`string`&&$throw(`FluxionOptions.https.cert must be a string`);let n={key:S(e.key,t),cert:S(e.cert,t)};return e.ca!==void 0&&(Array.isArray(e.ca)?n.ca=e.ca.map(e=>S(e,t)):n.ca=S(e.ca,t)),n}function C(e){(typeof e!=`object`||!e||Array.isArray(e))&&$throw(`FluxionOptions must be an object`);let{dir:r,host:i,port:a,handlerTimeoutMs:o=5e3,staticResourceTimeoutMs:s=10*6e5,metaPort:c,moduleDir:l=process.cwd(),workerOptions:u={},maxRequestBytes:d=8e6,reloadDelay:f=500,include:p=[`**/*`],apiInclude:m=[`**/*.ts`],exclude:h=[`**/node_modules/**`,`**/.git/**`,`**/dist/**`,`**/build/**`,`**/.vscode/**`,`**/.idea/**`,`**/*.log`,`**/.DS_Store`,`**/coverage/**`,`**/.nyc_output/**`,`**/*.tmp`,`**/*.temp`],https:g,nativeWatcher:_=!1}=e,v=e.logger??`one-line`;return v!==`one-line`&&v!==`json-line`&&typeof v!=`function`&&$throw(`Invalid logger option, Must be 'one-line', 'json-line' or a custom logger function`),typeof r!=`string`&&$throw(`FluxionOptions.dir must be a string`),typeof l!=`string`&&$throw(`FluxionOptions.moduleDir must be a string`),typeof i!=`string`&&$throw(`FluxionOptions.host must be a string`),(!Number.isSafeInteger(o)||o<=100)&&$throw(`FluxionOptions.handlerTimeoutMs must be an integer greater than 100`),(typeof f!=`number`||f<=0||!Number.isSafeInteger(f))&&$throw(`FluxionOptions.reloadDelay must be a positive integer`),f<50&&$throw(`FluxionOptions.reloadDelay must be greater than or equal to 50`),(typeof a!=`number`||!Number.isSafeInteger(a))&&$throw(`FluxionOptions.port must be a positive integer`),(a<=1||a>65535)&&$throw(`FluxionOptions.port must be 1 ~ 65535`),c??=a+1,(typeof c!=`number`||!Number.isSafeInteger(c))&&$throw(`FluxionOptions.metaPort must be a positive integer`),(c<=1||c>65535)&&$throw(`FluxionOptions.metaPort must be 1 ~ 65535`),c===a&&$throw(`FluxionOptions.metaPort must be different from FluxionOptions.port`),(typeof u!=`object`||!u||Array.isArray(u))&&$throw(`FluxionOptions.workerOptions must be an object`),(typeof d!=`number`||d<=0||!Number.isSafeInteger(d))&&$throw(`FluxionOptions.maxRequestBytes must be a positive integer`),r=n.isAbsolute(r)?r:n.join(process.cwd(),r),t.existsSync(r)||t.mkdirSync(r,{recursive:!0}),{dir:r,host:i,port:a,handlerTimeoutMs:o,staticResourceTimeoutMs:s,reloadDelay:f,metaPort:c,moduleDir:l,workerOptions:x(u),maxRequestBytes:d,logger:v,include:p,apiInclude:m,exclude:h,nativeWatcher:_,https:te(g,l)}}const ne=e=>[100].includes(e?.type),w=e=>[202,200,201,203].includes(e?.type),T=e=>process.send?.(e),E=(e,t)=>e.send(t),D=Symbol.for(`fluxion.router.StaticHandled`),O=Symbol.for(`fluxion.handlerTimeout`),k={".css":`text/css; charset=utf-8`,".html":`text/html; charset=utf-8`,".ico":`image/x-icon`,".js":`text/javascript; charset=utf-8`,".json":`application/json; charset=utf-8`,".map":`application/json; charset=utf-8`,".png":`image/png`,".jpg":`image/jpeg`,".jpeg":`image/jpeg`,".svg":`image/svg+xml`,".txt":`text/plain; charset=utf-8`,".webp":`image/webp`},A=()=>{};function j(e,t,n=200){e.statusCode=n,e.setHeader(`Content-Type`,`application/json; charset=utf-8`),e.end(JSON.stringify(t))}function M(e,t,n=200){if(!e.writableEnded){if(e.headersSent){e.end();return}j(e,t,n)}}function N(e,t){let n=a.createServer((e,n)=>{let r=e.method??`GET`,i=`/`;try{i=new URL(e.url??`/`,`http://fluxion.local`).pathname}catch{j(n,{message:`Bad Request: invalid url`},400);return}if(r===`GET`&&i===`/_fluxion/healthz`){j(n,{ok:!0,role:`primary`,pid:process.pid,now:Date.now(),uptimeSeconds:Number(process.uptime().toFixed(3))});return}if(r===`GET`&&i===`/_fluxion/workers`){j(n,{ok:!0,now:Date.now(),workers:t()});return}j(n,{message:`Not Found`},404)});return n.on(`listening`,()=>{e.logger.info(`MetaApiStarted`,{pid:process.pid,host:e.options.host,port:e.options.metaPort,prefix:`/_fluxion`})}),n.on(`error`,t=>{e.logger.error(`MetaApiError`,{host:e.options.host,port:e.options.metaPort,error:b(t)}),process.exit(1)}),n.listen(e.options.metaPort,e.options.host),n}const P=e=>Number((e/1024/1024).toFixed(2)),F=6e4;function I(e){r.isPrimary||$throw(`createPrimary should only be called in primary process`);let{workerOptions:t}=e.options,n=t.restartWhen,a=Math.max(1,i.cpus().length),o=Math.max(1,Math.min(t.maxWorkerCount??Math.min(2,a),a));e.logger.info(`PrimaryStarted`,{pid:process.pid,workers:o,host:e.options.host,port:e.options.port,metaPort:e.options.metaPort});let s=new Map,c=new Map,l=e=>{let t=Date.now(),n=(c.get(e)??[]).filter(e=>t-e<F);return c.set(e,n),n.length},u=e=>{let t=Date.now(),n=(c.get(e)??[]).filter(e=>t-e<F);n.push(t),c.set(e,n)},d=e=>l(e)>=3;N(e,()=>({primaryPid:process.pid,host:e.options.host,port:e.options.port,metaPort:e.options.metaPort,uptimeSeconds:Number(process.uptime().toFixed(3)),workers:Array.from(s.entries()).map(([e,t])=>{let{instance:n}=t,r=t.lastStats;return{workerId:e,slot:t.slot,pid:t.pid??n.process.pid??null,state:t.state,restartReason:t.restartReason??null,createdAt:t.createdAt,readyAt:t.readyAt??null,connected:n.isConnected(),dead:n.isDead(),exitedAfterDisconnect:n.exitedAfterDisconnect,lastPongAt:t.lastPongAt??null,lastRttMs:t.lastRttMs??null,stats:r===void 0?null:{at:r.at,uptimeSeconds:r.uptimeSeconds,cpu:r.cpu,memory:{...r.memory,rssMb:P(r.memory.rss),heapTotalMb:P(r.memory.heapTotal),heapUsedMb:P(r.memory.heapUsed),externalMb:P(r.memory.external),arrayBuffersMb:P(r.memory.arrayBuffers)}}}})}));let f=(t,n)=>{for(let e of s.values())if(e.state===`restarting`)return;if(d(t.slot)){e.logger.warn(`WorkerRecycleSuppressed`,{slot:t.slot,pid:t.pid,reason:n,windowMs:F,max:3});return}u(t.slot),t.state=`restarting`,t.restartReason=n,e.logger.warn(`WorkerRecycling`,{slot:t.slot,pid:t.pid,reason:n}),t.instance.kill()},p=(e,t)=>{let r=P(t.memory.rss);if(r>n.memoryUsageGreaterThan){f(e,`memoryUsageGreaterThan: rss ${r}MB > ${n.memoryUsageGreaterThan}MB`);return}let i=t.uptimeSeconds*1e3;i>n.uptimeGreaterThan&&f(e,`uptimeGreaterThan: ${Math.round(i/1e3)}s > ${Math.round(n.uptimeGreaterThan/1e3)}s`)},m=e=>{for(let t of s.values()){if(t.state!==`ready`||t.lastPongAt===void 0)continue;let r=e-t.lastPongAt;r>n.healthzTimeout&&f(t,`healthzTimeout: no pong for ${Math.round(r/1e3)}s > ${Math.round(n.healthzTimeout/1e3)}s`)}},h=e=>{g(r.fork({WORKER_ID:String(e)}),e)},g=(t,n)=>{let r={state:`creating`,pid:t.process.pid,slot:n,createdAt:Date.now(),instance:t};s.set(t.id,r),t.on(`message`,i=>{if(w(i)){if(i.type===202){let e=Date.now()-i.sentAt;r.pid=i.pid,r.lastPongAt=Date.now(),r.lastRttMs=e;return}if(i.type===201){r.state=`ready`,r.pid=i.pid,r.readyAt=Date.now(),e.logger.info(`WorkerReady`,{workerId:t.id,slot:n,pid:i.pid});return}if(i.type===200){r.state=`created`,r.pid=i.pid,e.logger.info(`WorkerCreated`,{workerId:t.id,slot:n,pid:i.pid});return}i.type===203&&(r.pid=i.pid,r.lastStats=i.stats,r.state===`ready`&&p(r,i.stats))}}),t.on(`exit`,(n,r)=>{let i=s.get(t.id);s.delete(t.id);let a=i?.slot,o=i?.state===`restarting`,c=i?.restartReason??null;if(e.logger.warn(`WorkerExited`,{workerId:t.id,slot:a??null,pid:t.process.pid??`unknown`,code:n,signal:r??`none`,expected:o,reason:c}),a!==void 0){if(o){h(a);return}if(u(a),d(a)){e.logger.error(`WorkerRespawnSuppressed`,{slot:a,windowMs:F,max:3});return}h(a)}})};for(let e=0;e<o;e++)h(e+1);setInterval(()=>{let e=Date.now();for(let t of s.values())if(t.instance.isConnected())try{E(t.instance,{type:100,sentAt:e})}catch{}m(Date.now())},5e3).unref()}function L(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 R(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 z(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 B(e){if(e!==void 0)try{return new URL(e,`http://fluxion.local`)}catch{return}}function V(e){let t={};for(let[n,r]of e.entries()){let e=t[n];if(e===void 0){t[n]=r;continue}if(Array.isArray(e)){e.push(r);continue}t[n]=[e,r]}return t}function H(e,t){let n=Error(`request body too large: ${e.toString()} bytes exceeds ${t.toString()} bytes`);return n.code=`REQUEST_BODY_TOO_LARGE`,n}function U(e){return Array.isArray(e)?e[0]:e}function W(){return{exists:!1,bytes:0,truncated:!1}}function G(e,t,n,r){return t===0?W():z(n)?{exists:!0,value:e.toString(`utf8`),bytes:t,truncated:r}:{exists:!0,value:`<binary body: ${t} bytes>`,bytes:t,truncated:r}}async function K(e,t,n,r=8192){if(t===`GET`||t===`HEAD`||e.readableEnded)return{rawBody:void 0,preview:W()};let i=U(e.headers[`content-length`]),a=i===void 0?NaN:Number.parseInt(i,10);if(Number.isFinite(a)&&a>n)throw H(a,n);return new Promise((t,i)=>{let a=[],o=[],s=0,c=0,l=!1,u=!1,d=()=>{e.off(`data`,p),e.off(`end`,m),e.off(`error`,h),e.off(`aborted`,g)},f=e=>{u||(u=!0,e())},p=t=>{let u=Buffer.isBuffer(t)?t:Buffer.from(t);if(s+=u.byteLength,s>n){d(),e.resume(),f(()=>{i(H(s,n))});return}if(a.push(u),c<r){let e=r-c,t=u.subarray(0,e);o.push(t),c+=t.length,t.length<u.length&&(l=!0)}else l=!0},m=()=>{d(),f(()=>{let n=a.length>0?Buffer.concat(a):void 0;t({rawBody:n,preview:G(o.length>0?Buffer.concat(o):Buffer.alloc(0),n?.byteLength??0,U(e.headers[`content-type`]),l)})})},h=e=>{d(),f(()=>{i(e)})},g=()=>{d(),f(()=>{i(Error(`request aborted while reading body`))})};e.on(`data`,p),e.once(`end`,m),e.once(`error`,h),e.once(`aborted`,g)})}async function q(e,t,n){let{rawBody:r,preview:i}=await K(e,t,n);if(r===void 0||r.byteLength===0)return{body:{},preview:i};let a=U(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:V(new URLSearchParams(r.toString(`utf8`))),preview:i}:z(a)?{body:{raw:r.toString(`utf8`)},preview:i}:{body:{},preview:i}}function J(e){if(!e)return{};let t={},n=e.split(`;`);for(let e of n){let[n,...r]=e.split(`=`);if(!n)continue;let i=n.trim(),a=r.join(`=`).trim();t[i]=decodeURIComponent(a)}return t}function Y(e){let t=async(t,n)=>{let r=t.method??`GET`,i=R(t),a=B(t.url);if(a===void 0){M(n,{message:`Bad Request: req.url is undefined`},400);return}let o={method:r,ip:i,url:a,query:V(a.searchParams),body:{},headers:t.headers,cookie:J(t.headers.cookie)},s={exists:!1,bytes:0,truncated:!1};e.logger.info(`Req`,{method:r,ip:i,path:a.pathname});let c=performance.now();n.once(`finish`,()=>{let t={workerId:process.env.WORKER_ID??`[primary]`,method:r,ip:i,path:a.pathname,status:n.statusCode,duration:(performance.now()-c).toFixed(4)};f(o.query).length>0&&(t.query=o.query),s.exists&&(t.body=s.value,t.bodyBytes=s.bytes,t.bodyTruncated=s.truncated),e.logger.info(`Res`,t)});try{if(o.url.pathname.startsWith(`/_fluxion/`)){M(n,{message:`Meta APIs are available on port ${e.options.metaPort}`},404);return}let r=await q(t,o.method,e.options.maxRequestBytes);o.body=r.body,s=r.preview;let i=await e.router.getModule(a);if(!i){M(n,{message:`Not Found`},404);return}if(t.method&&i.methods&&!i.methods.includes(t.method)){M(n,{message:`Method Not Allowed`},405);return}let c=i.type===0?i.handlerTimeoutMs??e.options.handlerTimeoutMs:e.options.staticResourceTimeoutMs,l=await Promise.race([L(i.handler,o,t,n),new Promise(e=>setTimeout(()=>e(O),c))]);if(l===O){e.logger.warn(`HandlerTimeout`,{method:o.method,ip:o.ip}),M(n,{message:`Handler timed out`},500);return}l!==D&&M(n,l)}catch(t){e.logger.error(`RequestFailed`,{method:o.method,ip:o.ip,path:o.url.pathname,error:b(t)}),t.code===`REQUEST_BODY_TOO_LARGE`?M(n,{message:b(t)},413):M(n,{message:b(t)},500)}},n=e.options.https?o.createServer({key:e.options.https.key,cert:e.options.https.cert,ca:e.options.https.ca},t):a.createServer(t);return n.on(`close`,()=>{e.logger.info(`ServerClosed`,{host:e.options.host,port:e.options.port})}),n.listen(e.options.port,e.options.host,()=>{e.logger.info(`ServerStarted`,{pid:process.pid,protocol:e.options.https?`https`:`http`,host:e.options.host,port:e.options.port}),e.logger.info(`DynamicDirectory`,{directory:e.options.dir})}),n.on(`error`,t=>{e.logger.error(`ServerError`,{error:b(t)})}),n}const X=()=>{let e=process.cpuUsage(),t=Date.now();setInterval(()=>{let n=Date.now(),r=Math.max(1,(n-t)*1e3),i=process.cpuUsage(e),a=Number(((i.user+i.system)/r*100).toFixed(2));e=process.cpuUsage(),t=n;let o=process.memoryUsage();T({type:203,pid:process.pid,stats:{at:n,pid:process.pid,uptimeSeconds:Number(process.uptime().toFixed(3)),cpu:{userMicros:i.user,systemMicros:i.system,percent:a},memory:{rss:o.rss,heapTotal:o.heapTotal,heapUsed:o.heapUsed,external:o.external,arrayBuffers:o.arrayBuffers}}})},2e3).unref()};function re(e){r.isPrimary&&$throw(`createWorker should only be called in worker process`),process.on(`message`,e=>{if(ne(e)&&e.type===100){T({type:202,pid:process.pid,sentAt:e.sentAt,receivedAt:Date.now()});return}}),T({type:200,pid:process.pid}),X();try{Y(e),T({type:201,pid:process.pid})}catch(t){e.logger.error(`WorkerBootstrapFailed`,{pid:process.pid,error:b(t)}),process.exit(1)}}var Z=class{cx;timer=null;filesChanged=new Map;constructor(e){this.cx=e}async init(){let e=this.cx.options.dir;if(!t.existsSync(e))return this.cx.logger.warn(`Directory does not exist: ${e}`),this;let r=[],i=(e,a)=>{let o=t.readdirSync(e,{withFileTypes:!0});for(let t=0;t<o.length;t++){let s=o[t],c=n.join(e,s.name),l=n.join(a,s.name);if(s.isDirectory())i(c,l);else if(s.isFile()){let e=this.cx.router.register(c,l).catch(e=>{this.cx.logger.error(`Error registering file ${l}: ${e.message}`)});r.push(e)}}};return i(e,``),await Promise.all(r),this.cx.logger.info(`Initial registration complete for directory: ${e}`),this}queueUp(e,t){this.filesChanged.set(e,t),!this.timer&&(this.timer=setTimeout(async()=>{let e=[...this.filesChanged].map(([e,t])=>this.cx.router.register(e,t).catch(e=>this.cx.logger.error(`Error refreshing handlers: ${e.message}`)).finally(()=>this.filesChanged.delete(e)));await Promise.all(e),this.timer=null},this.cx.options.reloadDelay))}stopCore(){this.timer&&=(clearTimeout(this.timer),null),this.filesChanged.clear()}},ie=class extends Z{watcher=null;constructor(e){super(e)}async start(){this.stop(),await this.init();let e=this.cx.options.dir;return this.watcher=s.watch(e,{persistent:!0,ignoreInitial:!0,usePolling:!1,awaitWriteFinish:{stabilityThreshold:100,pollInterval:50}}).on(`all`,(t,r)=>{r&&this.queueUp(r,n.relative(e,r))}).on(`error`,e=>{let t=e instanceof Error?e:Error(String(e));this.cx.logger.error(`Watcher error: ${t.message}`),this.cx.logger.error(`Restarting watcher...`),this.start()}).on(`ready`,()=>{this.cx.logger.info(`Watcher ready and watching directory: ${e}`)}),this.cx.logger.info(`Watcher started on directory: ${e}`),this}stop(){return this.watcher&&=(this.watcher.close(),null),this.stopCore(),this}},ae=class extends Z{watcher=null;constructor(e){super(e)}async start(){this.stop(),await this.init();let e=this.cx.options.dir;return this.watcher=t.watch(e,{recursive:!0},(t,r)=>{r&&this.queueUp(n.join(e,r),r)}).on(`error`,e=>{this.cx.logger.error(`Watcher error: ${e.message}`),this.cx.logger.error(`Restarting watcher...`),this.start()}),this.cx.logger.info(`Watcher started on directory: ${e}`),this}stop(){return this.watcher&&=(this.watcher.close(),null),this.stopCore(),this}};function oe(e){}const se=oe;function Q(e){return!(typeof e!=`object`||!e||(se(e),typeof e.handler!=`function`)||e.disposer!==void 0&&typeof e.disposer!=`function`||e.handlerTimeoutMs!==void 0&&typeof e.handlerTimeoutMs!=`function`)}function ce(e,t){delete l.cache[t];let n=l(t);return Q(n)||(Q(n.default)?n=n.default:$throw(`Invalid handler module '${t}', make sure it satisfies defineFluxionModule(...) helper`)),{...n,type:0}}var le=class{cx;handlers=new Map;constructor(e){this.cx=e}makeStaticResource(e){return{type:1,handler:async(r,i,a)=>{if(r.method!==`GET`&&r.method!==`HEAD`){a.statusCode=405,a.setHeader(`Allow`,`GET, HEAD`),a.end();return}if(!t.existsSync(e)){a.statusCode=404,a.end(`Not Found`);return}let o=t.statSync(e);if(!o.isFile()){a.statusCode=404,a.end(`Not Found`);return}let s=k[n.extname(e).toLowerCase()]??`application/octet-stream`;if(a.statusCode=200,a.setHeader(`Content-Type`,s),a.setHeader(`Content-Length`,String(o.size)),r.method===`HEAD`){a.end();return}return new Promise((n,r)=>{let i=t.createReadStream(e);i.on(`error`,r),i.on(`end`,()=>n(D)),i.pipe(a)})}}}async register(e,n){if(!t.existsSync(e)){let e=this.handlers.get(n)?.disposer;e&&await L(e),this.handlers.delete(n),this.cx.logger.info(`${m.red}Deleted ${m.reset} - ${n}`);return}if(!this.cx.options.include.some(e=>c(n,e))){this.handlers.delete(n),this.cx.logger.info(`${m.yellow}Skipped ${m.reset} - ${n}`);return}if(this.cx.options.exclude.some(e=>c(n,e))){this.handlers.delete(n),this.cx.logger.info(`${m.orange}Excluded${m.reset} - ${n}`);return}if(this.cx.options.apiInclude.some(e=>c(n,e))){let t=ce(this.cx,e);this.handlers.set(n,t),this.cx.logger.info(`${m.green}Api ${m.reset} - ${n}`);return}this.handlers.set(n,this.makeStaticResource(n)),this.cx.logger.info(`${m.brightBlue}Static ${m.reset} - ${n}`)}getModule(e){let t=e.pathname.replace(/^[\/]+/,``).replace(/[\/]+$/,``);return this.handlers.get(t)}remove(e){this.handlers.has(e)&&(this.handlers.delete(e),this.cx.logger.info(`${m.red}Deleted ${m.reset} - ${e}`));let t=e.endsWith(`/`)?e:e+`/`;for(let e of this.handlers.keys())e.startsWith(t)&&(this.handlers.delete(e),this.cx.logger.info(`${m.red}Deleted ${m.reset} - ${e}`))}};async function $(e){let t={options:C(e)};t.logger=ee(t),t.router=new le(t),r.isPrimary?I(t):(t.logger=y(t.logger,process.pid),t.watcher=await new(t.options.nativeWatcher?ae:ie)(t).start(),re(t))}function ue(e,t=A){return typeof e==`function`?(typeof t!=`function`&&$throw(`Invalid disposer, expected a function but got ${typeof t}`),{handler:e,disposer:t}):((typeof e!=`object`||!e)&&$throw(`Invalid argument, expected a FluxionModule object or a handler function, but got ${typeof e}`),typeof e.handler!=`function`&&$throw(`Invalid FluxionModule, "handler" must be a function`),e.disposer!==void 0&&typeof e.disposer!=`function`&&$throw(`Invalid FluxionModule, "disposer" must be a function if provided`),e.methods!==void 0&&!Array.isArray(e.methods)&&$throw(`Invalid FluxionModule, "methods" must be an array of strings if provided`),e)}if(process.env.NODE_ENV!==`production`){globalThis.$throw=e=>{throw Error(`[fluxion error]`+e)};let e=(e,t)=>{if(e===void 0)return t;let n=Number.parseInt(e,10);return Number.isNaN(n)?t:n};$({dir:process.env.DYNAMIC_DIRECTORY??`dynamicDirectory`,host:process.env.HOST??`localhost`,port:e(process.env.PORT,9e3),metaPort:e(process.env.META_PORT,9001),reloadDelay:process.env.RELOAD_DELAY?Number.parseInt(process.env.RELOAD_DELAY,10):void 0,workerOptions:{maxWorkerCount:4}})}export{ue as defineFluxionModule,$ as fluxion};
|
|
1
|
+
import{createRequire as e}from"node:module";import t from"node:fs";import n from"node:path";import r from"node:cluster";import i from"node:os";import a from"node:http";import o from"node:https";import s from"chokidar";import{minimatch as c}from"minimatch";var l=e(import.meta.url);function u(e=new Date){return`${e.getFullYear()}.${String(e.getMonth()+1).padStart(2,`0`)}.${String(e.getDate()).padStart(2,`0`)} ${String(e.getHours()).padStart(2,`0`)}:${String(e.getMinutes()).padStart(2,`0`)}:${String(e.getSeconds()).padStart(2,`0`)}.${String(e.getMilliseconds()).padStart(3,`0`)}`}const d=JSON.stringify,f=Object.keys,p=process.env.FLUXION_COLORS!==`0`;let m;(function(e){e.reset=p?`\x1B[0m`:``,e.bold=p?`\x1B[1m`:``,e.dim=p?`\x1B[2m`:``,e.italic=p?`\x1B[3m`:``,e.underline=p?`\x1B[4m`:``,e.blink=p?`\x1B[5m`:``,e.inverse=p?`\x1B[7m`:``,e.black=p?`\x1B[30m`:``,e.red=p?`\x1B[31m`:``,e.green=p?`\x1B[32m`:``,e.yellow=p?`\x1B[33m`:``,e.blue=p?`\x1B[34m`:``,e.magenta=p?`\x1B[35m`:``,e.cyan=p?`\x1B[36m`:``,e.white=p?`\x1B[37m`:``,e.brightBlack=p?`\x1B[90m`:``,e.brightRed=p?`\x1B[91m`:``,e.brightGreen=p?`\x1B[92m`:``,e.brightYellow=p?`\x1B[93m`:``,e.brightBlue=p?`\x1B[94m`:``,e.brightMagenta=p?`\x1B[95m`:``,e.brightCyan=p?`\x1B[96m`:``,e.brightWhite=p?`\x1B[97m`:``,e.bgBlack=p?`\x1B[40m`:``,e.bgRed=p?`\x1B[41m`:``,e.bgGreen=p?`\x1B[42m`:``,e.bgYellow=p?`\x1B[43m`:``,e.bgBlue=p?`\x1B[44m`:``,e.bgMagenta=p?`\x1B[45m`:``,e.bgCyan=p?`\x1B[46m`:``,e.bgWhite=p?`\x1B[47m`:``,e.bgBrightBlack=p?`\x1B[100m`:``,e.bgBrightRed=p?`\x1B[101m`:``,e.bgBrightGreen=p?`\x1B[102m`:``,e.bgBrightYellow=p?`\x1B[103m`:``,e.bgBrightBlue=p?`\x1B[104m`:``,e.bgBrightMagenta=p?`\x1B[105m`:``,e.bgBrightCyan=p?`\x1B[106m`:``,e.bgBrightWhite=p?`\x1B[107m`:``,e.purple=p?`\x1B[38;2;225;16;248m`:``,e.orange=p?`\x1B[38;2;248;147;16m`:``,e.darkGreen=p?`\x1B[38;2;22;101;52m`:``,e.claude=p?`\x1B[38;2;217;119;87m`:``,e.deepseek=p?`\x1B[38;2;57;100;254m`:``,e.gpt=p?`\x1B[38;2;41;60;77m`:``})(m||={});const h=e=>{try{return d(e)}catch{return`[unserializable]`}},g={INFO:`${m.cyan}INFO${m.reset}`,WARN:`${m.orange}WARN${m.reset}`,ERROR:`${m.red}ERROR${m.reset}`,SUCC:`${m.green}SUCC${m.reset}`,DEBUG:`${m.blue}DEBUG${m.reset}`,VERBOSE:`${m.purple}VERBOSE${m.reset}`},_=e=>{let{level:t,timestamp:n,event:r,message:i,...a}=e,o=`${m.darkGreen}[${n}]${m.reset}`,s=g[t]??t,c=i??r,l=f(a).length>0?`${m.dim}${h(a)}${m.reset}`:``;console.log(`${o} ${s} ${c}${l}`)};function v(e){let t=e.options.logger;return t===void 0||t===`one-line`?_:t===`json-line`?e=>console.log(h(e)):t}function ee(e){let t=v(e);return{write(e,n,r={}){let i={...r,timestamp:u(),level:e,event:n};try{t(i)}catch{}},info(e,t){this.write(`INFO`,e,t)},warn(e,t){this.write(`WARN`,e,t)},error(e,t){this.write(`ERROR`,e,t)},succ(e,t){this.write(`SUCC`,e,t)},debug(e,t){this.write(`DEBUG`,e,t)},verbose(e,t){this.write(`VERBOSE`,e,t)}}}function y(e,t){let n=`[${t}]`;return{write(t,r,i){e.write(t,`${n} ${r}`,i)},info(t,r){e.info(`${n} ${t}`,r)},warn(t,r){e.warn(`${n} ${t}`,r)},error(t,r){e.error(`${n} ${t}`,r)},succ(t,r){e.succ(`${n} ${t}`,r)},debug(t,r){e.debug(`${n} ${t}`,r)},verbose(t,r){e.verbose(`${n} ${t}`,r)}}}const b=typeof Error.isError==`function`?e=>Error.isError(e)?e.message:String(e):e=>e?.message||String(e);function x(e={}){let t=e.restartWhen??{},n=t.healthzTimeout??3e4;return n!==1/0&&(!Number.isFinite(n)||n<1e4)&&$throw(`workerOptions.restartWhen.healthzTimeout must be a finite number >= 10000 (ms) or Infinity`),{maxWorkerCount:e.maxWorkerCount??4,restartWhen:{memoryUsageGreaterThan:t.memoryUsageGreaterThan??1/0,healthzTimeout:n,uptimeGreaterThan:t.uptimeGreaterThan??1/0}}}function S(e,r){if(Buffer.isBuffer(e))return e;if(typeof e==`string`){if(!e.startsWith(`-----BEGIN`)){let i=n.isAbsolute(e)?e:n.join(r,e);if(t.existsSync(i))return t.readFileSync(i)}return Buffer.from(e)}$throw(`Certificate content must be a string or Buffer`)}function te(e,t){if(!e)return;(typeof e!=`object`||!e||Array.isArray(e))&&$throw(`FluxionOptions.https must be an object`),typeof e.key!=`string`&&$throw(`FluxionOptions.https.key must be a string`),typeof e.cert!=`string`&&$throw(`FluxionOptions.https.cert must be a string`);let n={key:S(e.key,t),cert:S(e.cert,t)};return e.ca!==void 0&&(Array.isArray(e.ca)?n.ca=e.ca.map(e=>S(e,t)):n.ca=S(e.ca,t)),n}function C(e){(typeof e!=`object`||!e||Array.isArray(e))&&$throw(`FluxionOptions must be an object`);let{dir:r=n.isAbsolute(e.dir)?e.dir:n.join(process.cwd(),e.dir),host:i,port:a,handlerTimeoutMs:o=5e3,staticResourceTimeoutMs:s=10*6e5,metaPort:c=a+1,moduleDir:l=process.cwd(),workerOptions:u={},maxRequestBytes:d=8e6,reloadDelay:f=500,include:p=[`**/*`],apiInclude:m=[`**/*.ts`],exclude:h=[`**/node_modules/**`,`**/.git/**`,`**/dist/**`,`**/build/**`,`**/.vscode/**`,`**/.idea/**`,`**/*.log`,`**/.DS_Store`,`**/coverage/**`,`**/.nyc_output/**`,`**/*.tmp`,`**/*.temp`],https:g,nativeWatcher:_=!1}=e,v=e.logger??`one-line`;return v!==`one-line`&&v!==`json-line`&&typeof v!=`function`&&$throw(`Invalid logger option, Must be 'one-line', 'json-line' or a custom logger function`),typeof r!=`string`&&$throw(`FluxionOptions.dir must be a string`),typeof l!=`string`&&$throw(`FluxionOptions.moduleDir must be a string`),typeof i!=`string`&&$throw(`FluxionOptions.host must be a string`),(!Number.isSafeInteger(o)||o<=100)&&$throw(`FluxionOptions.handlerTimeoutMs must be an integer greater than 100`),(typeof f!=`number`||f<=0||!Number.isSafeInteger(f))&&$throw(`FluxionOptions.reloadDelay must be a positive integer`),f<50&&$throw(`FluxionOptions.reloadDelay must be greater than or equal to 50`),(typeof a!=`number`||!Number.isSafeInteger(a))&&$throw(`FluxionOptions.port must be a positive integer`),(a<=1||a>65535)&&$throw(`FluxionOptions.port must be 1 ~ 65535`),(typeof c!=`number`||!Number.isSafeInteger(c))&&$throw(`FluxionOptions.metaPort must be a positive integer`),(c<=1||c>65535)&&$throw(`FluxionOptions.metaPort must be 1 ~ 65535`),c===a&&$throw(`FluxionOptions.metaPort must be different from FluxionOptions.port`),(typeof u!=`object`||!u||Array.isArray(u))&&$throw(`FluxionOptions.workerOptions must be an object`),(typeof d!=`number`||d<=0||!Number.isSafeInteger(d))&&$throw(`FluxionOptions.maxRequestBytes must be a positive integer`),t.existsSync(r)||t.mkdirSync(r,{recursive:!0}),{dir:r,host:i,port:a,handlerTimeoutMs:o,staticResourceTimeoutMs:s,reloadDelay:f,metaPort:c,moduleDir:l,workerOptions:x(u),maxRequestBytes:d,logger:v,include:p,apiInclude:m,exclude:h,nativeWatcher:_,https:te(g,l)}}const ne=e=>[100].includes(e?.type),w=e=>[202,200,201,203].includes(e?.type),T=e=>process.send?.(e),E=(e,t)=>e.send(t),D=Symbol.for(`fluxion.router.StaticHandled`),O=Symbol.for(`fluxion.handlerTimeout`),k={".css":`text/css; charset=utf-8`,".html":`text/html; charset=utf-8`,".ico":`image/x-icon`,".js":`text/javascript; charset=utf-8`,".json":`application/json; charset=utf-8`,".map":`application/json; charset=utf-8`,".png":`image/png`,".jpg":`image/jpeg`,".jpeg":`image/jpeg`,".svg":`image/svg+xml`,".txt":`text/plain; charset=utf-8`,".webp":`image/webp`},A=()=>{};function j(e,t,n=200){e.statusCode=n,e.setHeader(`Content-Type`,`application/json; charset=utf-8`),e.end(JSON.stringify(t))}function M(e,t,n=200){if(!e.writableEnded){if(e.headersSent){e.end();return}j(e,t,n)}}function N(e,t){let n=a.createServer((e,n)=>{let r=e.method??`GET`,i=`/`;try{i=new URL(e.url??`/`,`http://fluxion.local`).pathname}catch{j(n,{message:`Bad Request: invalid url`},400);return}if(r===`GET`&&i===`/_fluxion/healthz`){j(n,{ok:!0,role:`primary`,pid:process.pid,now:Date.now(),uptimeSeconds:Number(process.uptime().toFixed(3))});return}if(r===`GET`&&i===`/_fluxion/workers`){j(n,{ok:!0,now:Date.now(),workers:t()});return}j(n,{message:`Not Found`},404)});return n.on(`listening`,()=>{e.logger.info(`MetaApiStarted`,{pid:process.pid,host:e.options.host,port:e.options.metaPort,prefix:`/_fluxion`})}),n.on(`error`,t=>{e.logger.error(`MetaApiError`,{host:e.options.host,port:e.options.metaPort,error:b(t)}),process.exit(1)}),n.listen(e.options.metaPort,e.options.host),n}const P=e=>Number((e/1024/1024).toFixed(2)),F=6e4;function I(e){r.isPrimary||$throw(`createPrimary should only be called in primary process`);let{workerOptions:t}=e.options,n=t.restartWhen,a=Math.max(1,i.cpus().length),o=Math.max(1,Math.min(t.maxWorkerCount??Math.min(2,a),a));e.logger.info(`PrimaryStarted`,{pid:process.pid,workers:o,host:e.options.host,port:e.options.port,metaPort:e.options.metaPort});let s=new Map,c=new Map,l=e=>{let t=Date.now(),n=(c.get(e)??[]).filter(e=>t-e<F);return c.set(e,n),n.length},u=e=>{let t=Date.now(),n=(c.get(e)??[]).filter(e=>t-e<F);n.push(t),c.set(e,n)},d=e=>l(e)>=3;N(e,()=>({primaryPid:process.pid,host:e.options.host,port:e.options.port,metaPort:e.options.metaPort,uptimeSeconds:Number(process.uptime().toFixed(3)),workers:Array.from(s.entries()).map(([e,t])=>{let{instance:n}=t,r=t.lastStats;return{workerId:e,slot:t.slot,pid:t.pid??n.process.pid??null,state:t.state,restartReason:t.restartReason??null,createdAt:t.createdAt,readyAt:t.readyAt??null,connected:n.isConnected(),dead:n.isDead(),exitedAfterDisconnect:n.exitedAfterDisconnect,lastPongAt:t.lastPongAt??null,lastRttMs:t.lastRttMs??null,stats:r===void 0?null:{at:r.at,uptimeSeconds:r.uptimeSeconds,cpu:r.cpu,memory:{...r.memory,rssMb:P(r.memory.rss),heapTotalMb:P(r.memory.heapTotal),heapUsedMb:P(r.memory.heapUsed),externalMb:P(r.memory.external),arrayBuffersMb:P(r.memory.arrayBuffers)}}}})}));let f=(t,n)=>{for(let e of s.values())if(e.state===`restarting`)return;if(d(t.slot)){e.logger.warn(`WorkerRecycleSuppressed`,{slot:t.slot,pid:t.pid,reason:n,windowMs:F,max:3});return}u(t.slot),t.state=`restarting`,t.restartReason=n,e.logger.warn(`WorkerRecycling`,{slot:t.slot,pid:t.pid,reason:n}),t.instance.kill()},p=(e,t)=>{let r=P(t.memory.rss);if(r>n.memoryUsageGreaterThan){f(e,`memoryUsageGreaterThan: rss ${r}MB > ${n.memoryUsageGreaterThan}MB`);return}let i=t.uptimeSeconds*1e3;i>n.uptimeGreaterThan&&f(e,`uptimeGreaterThan: ${Math.round(i/1e3)}s > ${Math.round(n.uptimeGreaterThan/1e3)}s`)},m=e=>{for(let t of s.values()){if(t.state!==`ready`||t.lastPongAt===void 0)continue;let r=e-t.lastPongAt;r>n.healthzTimeout&&f(t,`healthzTimeout: no pong for ${Math.round(r/1e3)}s > ${Math.round(n.healthzTimeout/1e3)}s`)}},h=e=>{g(r.fork({WORKER_ID:String(e)}),e)},g=(t,n)=>{let r={state:`creating`,pid:t.process.pid,slot:n,createdAt:Date.now(),instance:t};s.set(t.id,r),t.on(`message`,i=>{if(w(i)){if(i.type===202){let e=Date.now()-i.sentAt;r.pid=i.pid,r.lastPongAt=Date.now(),r.lastRttMs=e;return}if(i.type===201){r.state=`ready`,r.pid=i.pid,r.readyAt=Date.now(),e.logger.info(`WorkerReady`,{workerId:t.id,slot:n,pid:i.pid});return}if(i.type===200){r.state=`created`,r.pid=i.pid,e.logger.info(`WorkerCreated`,{workerId:t.id,slot:n,pid:i.pid});return}i.type===203&&(r.pid=i.pid,r.lastStats=i.stats,r.state===`ready`&&p(r,i.stats))}}),t.on(`exit`,(n,r)=>{let i=s.get(t.id);s.delete(t.id);let a=i?.slot,o=i?.state===`restarting`,c=i?.restartReason??null;if(e.logger.warn(`WorkerExited`,{workerId:t.id,slot:a??null,pid:t.process.pid??`unknown`,code:n,signal:r??`none`,expected:o,reason:c}),a!==void 0){if(o){h(a);return}if(u(a),d(a)){e.logger.error(`WorkerRespawnSuppressed`,{slot:a,windowMs:F,max:3});return}h(a)}})};for(let e=0;e<o;e++)h(e+1);setInterval(()=>{let e=Date.now();for(let t of s.values())if(t.instance.isConnected())try{E(t.instance,{type:100,sentAt:e})}catch{}m(Date.now())},5e3).unref()}function L(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 R(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 z(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 B(e){if(e!==void 0)try{return new URL(e,`http://fluxion.local`)}catch{return}}function V(e){let t={};for(let[n,r]of e.entries()){let e=t[n];if(e===void 0){t[n]=r;continue}if(Array.isArray(e)){e.push(r);continue}t[n]=[e,r]}return t}function H(e,t){let n=Error(`request body too large: ${e.toString()} bytes exceeds ${t.toString()} bytes`);return n.code=`REQUEST_BODY_TOO_LARGE`,n}function U(e){return Array.isArray(e)?e[0]:e}function W(){return{exists:!1,bytes:0,truncated:!1}}function G(e,t,n,r){return t===0?W():z(n)?{exists:!0,value:e.toString(`utf8`),bytes:t,truncated:r}:{exists:!0,value:`<binary body: ${t} bytes>`,bytes:t,truncated:r}}async function K(e,t,n,r=8192){if(t===`GET`||t===`HEAD`||e.readableEnded)return{rawBody:void 0,preview:W()};let i=U(e.headers[`content-length`]),a=i===void 0?NaN:Number.parseInt(i,10);if(Number.isFinite(a)&&a>n)throw H(a,n);return new Promise((t,i)=>{let a=[],o=[],s=0,c=0,l=!1,u=!1,d=()=>{e.off(`data`,p),e.off(`end`,m),e.off(`error`,h),e.off(`aborted`,g)},f=e=>{u||(u=!0,e())},p=t=>{let u=Buffer.isBuffer(t)?t:Buffer.from(t);if(s+=u.byteLength,s>n){d(),e.resume(),f(()=>{i(H(s,n))});return}if(a.push(u),c<r){let e=r-c,t=u.subarray(0,e);o.push(t),c+=t.length,t.length<u.length&&(l=!0)}else l=!0},m=()=>{d(),f(()=>{let n=a.length>0?Buffer.concat(a):void 0;t({rawBody:n,preview:G(o.length>0?Buffer.concat(o):Buffer.alloc(0),n?.byteLength??0,U(e.headers[`content-type`]),l)})})},h=e=>{d(),f(()=>{i(e)})},g=()=>{d(),f(()=>{i(Error(`request aborted while reading body`))})};e.on(`data`,p),e.once(`end`,m),e.once(`error`,h),e.once(`aborted`,g)})}async function q(e,t,n){let{rawBody:r,preview:i}=await K(e,t,n);if(r===void 0||r.byteLength===0)return{body:{},preview:i};let a=U(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:V(new URLSearchParams(r.toString(`utf8`))),preview:i}:z(a)?{body:{raw:r.toString(`utf8`)},preview:i}:{body:{},preview:i}}function J(e){if(!e)return{};let t={},n=e.split(`;`);for(let e of n){let[n,...r]=e.split(`=`);if(!n)continue;let i=n.trim(),a=r.join(`=`).trim();t[i]=decodeURIComponent(a)}return t}function Y(e){let t=async(t,n)=>{let r=t.method??`GET`,i=R(t),a=B(t.url);if(a===void 0){M(n,{message:`Bad Request: req.url is undefined`},400);return}let o={method:r,ip:i,url:a,query:V(a.searchParams),body:{},headers:t.headers,cookie:J(t.headers.cookie)},s={exists:!1,bytes:0,truncated:!1};e.logger.info(`Req`,{method:r,ip:i,path:a.pathname});let c=performance.now();n.once(`finish`,()=>{let t={workerId:process.env.WORKER_ID??`[primary]`,method:r,ip:i,path:a.pathname,status:n.statusCode,duration:(performance.now()-c).toFixed(4)};f(o.query).length>0&&(t.query=o.query),s.exists&&(t.body=s.value,t.bodyBytes=s.bytes,t.bodyTruncated=s.truncated),e.logger.info(`Res`,t)});try{if(o.url.pathname.startsWith(`/_fluxion/`)){M(n,{message:`Meta APIs are available on port ${e.options.metaPort}`},404);return}let r=await q(t,o.method,e.options.maxRequestBytes);o.body=r.body,s=r.preview;let i=await e.router.getModule(a);if(!i){M(n,{message:`Not Found`},404);return}if(t.method&&i.methods&&!i.methods.includes(t.method)){M(n,{message:`Method Not Allowed`},405);return}let c=i.type===0?i.handlerTimeoutMs??e.options.handlerTimeoutMs:e.options.staticResourceTimeoutMs,l=await Promise.race([L(i.handler,o,t,n),new Promise(e=>setTimeout(()=>e(O),c))]);if(l===O){e.logger.warn(`HandlerTimeout`,{method:o.method,ip:o.ip}),M(n,{message:`Handler timed out`},500);return}l!==D&&M(n,l)}catch(t){e.logger.error(`RequestFailed`,{method:o.method,ip:o.ip,path:o.url.pathname,error:b(t)}),t.code===`REQUEST_BODY_TOO_LARGE`?M(n,{message:b(t)},413):M(n,{message:b(t)},500)}},n=e.options.https?o.createServer({key:e.options.https.key,cert:e.options.https.cert,ca:e.options.https.ca},t):a.createServer(t);return n.on(`close`,()=>{e.logger.info(`ServerClosed`,{host:e.options.host,port:e.options.port})}),n.listen(e.options.port,e.options.host,()=>{e.logger.info(`ServerStarted`,{pid:process.pid,protocol:e.options.https?`https`:`http`,host:e.options.host,port:e.options.port}),e.logger.info(`DynamicDirectory`,{directory:e.options.dir})}),n.on(`error`,t=>{e.logger.error(`ServerError`,{error:b(t)})}),n}const X=()=>{let e=process.cpuUsage(),t=Date.now();setInterval(()=>{let n=Date.now(),r=Math.max(1,(n-t)*1e3),i=process.cpuUsage(e),a=Number(((i.user+i.system)/r*100).toFixed(2));e=process.cpuUsage(),t=n;let o=process.memoryUsage();T({type:203,pid:process.pid,stats:{at:n,pid:process.pid,uptimeSeconds:Number(process.uptime().toFixed(3)),cpu:{userMicros:i.user,systemMicros:i.system,percent:a},memory:{rss:o.rss,heapTotal:o.heapTotal,heapUsed:o.heapUsed,external:o.external,arrayBuffers:o.arrayBuffers}}})},2e3).unref()};function re(e){r.isPrimary&&$throw(`createWorker should only be called in worker process`),process.on(`message`,e=>{if(ne(e)&&e.type===100){T({type:202,pid:process.pid,sentAt:e.sentAt,receivedAt:Date.now()});return}}),T({type:200,pid:process.pid}),X();try{Y(e),T({type:201,pid:process.pid})}catch(t){e.logger.error(`WorkerBootstrapFailed`,{pid:process.pid,error:b(t)}),process.exit(1)}}var Z=class{cx;timer=null;filesChanged=new Map;constructor(e){this.cx=e}async init(){let e=this.cx.options.dir;if(!t.existsSync(e))return this.cx.logger.warn(`Directory does not exist: ${e}`),this;let r=[],i=(e,a)=>{let o=t.readdirSync(e,{withFileTypes:!0});for(let t=0;t<o.length;t++){let s=o[t],c=n.join(e,s.name),l=n.join(a,s.name);if(s.isDirectory())i(c,l);else if(s.isFile()){let e=this.cx.router.register(c,l).catch(e=>{this.cx.logger.error(`Error registering file ${l}: ${e.message}`)});r.push(e)}}};return i(e,``),await Promise.all(r),this.cx.logger.info(`Initial registration complete for directory: ${e}`),this}queueUp(e,t){this.filesChanged.set(e,t),!this.timer&&(this.timer=setTimeout(async()=>{let e=[...this.filesChanged].map(([e,t])=>this.cx.router.register(e,t).catch(e=>this.cx.logger.error(`Error refreshing handlers: ${e.message}`)).finally(()=>this.filesChanged.delete(e)));await Promise.all(e),this.timer=null},this.cx.options.reloadDelay))}stopCore(){this.timer&&=(clearTimeout(this.timer),null),this.filesChanged.clear()}},ie=class extends Z{watcher=null;constructor(e){super(e)}async start(){this.stop(),await this.init();let e=this.cx.options.dir;return this.watcher=s.watch(e,{persistent:!0,ignoreInitial:!0,usePolling:!1,awaitWriteFinish:{stabilityThreshold:100,pollInterval:50}}).on(`all`,(t,r)=>{r&&this.queueUp(r,n.relative(e,r))}).on(`error`,e=>{let t=e instanceof Error?e:Error(String(e));this.cx.logger.error(`Watcher error: ${t.message}`),this.cx.logger.error(`Restarting watcher...`),this.start()}).on(`ready`,()=>{this.cx.logger.info(`Watcher ready and watching directory: ${e}`)}),this.cx.logger.info(`Watcher started on directory: ${e}`),this}stop(){return this.watcher&&=(this.watcher.close(),null),this.stopCore(),this}},ae=class extends Z{watcher=null;constructor(e){super(e)}async start(){this.stop(),await this.init();let e=this.cx.options.dir;return this.watcher=t.watch(e,{recursive:!0},(t,r)=>{r&&this.queueUp(n.join(e,r),r)}).on(`error`,e=>{this.cx.logger.error(`Watcher error: ${e.message}`),this.cx.logger.error(`Restarting watcher...`),this.start()}),this.cx.logger.info(`Watcher started on directory: ${e}`),this}stop(){return this.watcher&&=(this.watcher.close(),null),this.stopCore(),this}};function oe(e){}const se=oe;function Q(e,t){if(typeof t!=`object`||!t)return!1;if(se(t),typeof t.handler!=`function`)return e.logger.error(`handler must be a function`),!1;if(t.disposer!==void 0&&typeof t.disposer!=`function`)return e.logger.error(`disposer must be a function if provided`),!1;let n=t.handlerTimeoutMs;return n!==void 0&&(!Number.isSafeInteger(n)||n<100)?(e.logger.error(`handlerTimeoutMs must be an integer >= 100 if provided`),!1):t.type===0?!0:(e.logger.error(`You must use defineFluxionModule to create module`),!1)}function ce(e,t){delete l.cache[t];let n=l(t);return Q(e,n)||(Q(e,n.default)?n=n.default:$throw(`Invalid handler module '${t}', make sure it satisfies defineFluxionModule(...) helper`)),n}var le=class{cx;handlers=new Map;constructor(e){this.cx=e}makeStaticResource(e){return{type:1,handler:async(r,i,a)=>{if(r.method!==`GET`&&r.method!==`HEAD`){a.statusCode=405,a.setHeader(`Allow`,`GET, HEAD`),a.end();return}if(!t.existsSync(e)){a.statusCode=404,a.end(`Not Found`);return}let o=t.statSync(e);if(!o.isFile()){a.statusCode=404,a.end(`Not Found`);return}let s=k[n.extname(e).toLowerCase()]??`application/octet-stream`;if(a.statusCode=200,a.setHeader(`Content-Type`,s),a.setHeader(`Content-Length`,String(o.size)),r.method===`HEAD`){a.end();return}return new Promise((n,r)=>{let i=t.createReadStream(e);i.on(`error`,r),i.on(`end`,()=>n(D)),i.pipe(a)})}}}async register(e,n){let r=this.handlers.get(n)?.disposer;if(r&&await L(r),!t.existsSync(e)){this.handlers.delete(n),this.cx.logger.info(`${m.red}Deleted ${m.reset} - ${n}`);return}if(!this.cx.options.include.some(e=>c(n,e))){this.handlers.delete(n),this.cx.logger.info(`${m.yellow}Skipped ${m.reset} - ${n}`);return}if(this.cx.options.exclude.some(e=>c(n,e))){this.handlers.delete(n),this.cx.logger.info(`${m.orange}Excluded${m.reset} - ${n}`);return}if(this.cx.options.apiInclude.some(e=>c(n,e))){let t=ce(this.cx,e);this.handlers.set(n,t),this.cx.logger.info(`${m.green}Api ${m.reset} - ${n}`);return}this.handlers.set(n,this.makeStaticResource(n)),this.cx.logger.info(`${m.brightBlue}Static ${m.reset} - ${n}`)}getModule(e){let t=e.pathname.replace(/^[/]+/,``).replace(/[/]+$/,``);return this.handlers.get(t)}remove(e){this.handlers.has(e)&&(this.handlers.delete(e),this.cx.logger.info(`${m.red}Deleted ${m.reset} - ${e}`));let t=e.endsWith(`/`)?e:e+`/`;for(let e of this.handlers.keys())e.startsWith(t)&&(this.handlers.delete(e),this.cx.logger.info(`${m.red}Deleted ${m.reset} - ${e}`))}};async function $(e){let t={options:C(e)};t.logger=ee(t),t.router=new le(t),r.isPrimary?I(t):(t.logger=y(t.logger,process.pid),t.watcher=await new(t.options.nativeWatcher?ae:ie)(t).start(),re(t))}function ue(e,t=A){return typeof e==`function`?(typeof t!=`function`&&$throw(`Invalid disposer, expected a function but got ${typeof t}`),{handler:e,disposer:t,type:0}):((typeof e!=`object`||!e)&&$throw(`Invalid argument, expected a FluxionModule object or a handler function, but got ${typeof e}`),typeof e.handler!=`function`&&$throw(`Invalid FluxionModule, "handler" must be a function`),e.disposer!==void 0&&typeof e.disposer!=`function`&&$throw(`Invalid FluxionModule, "disposer" must be a function if provided`),e.methods!==void 0&&!Array.isArray(e.methods)&&$throw(`Invalid FluxionModule, "methods" must be an array of strings if provided`),{...e,type:0})}if(process.env.NODE_ENV!==`production`){globalThis.$throw=e=>{throw Error(`[fluxion error]`+e)};let e=(e,t)=>{if(e===void 0)return t;let n=Number.parseInt(e,10);return Number.isNaN(n)?t:n};$({dir:process.env.DYNAMIC_DIRECTORY??`dynamicDirectory`,host:process.env.HOST??`localhost`,port:e(process.env.PORT,9e3),metaPort:e(process.env.META_PORT,9001),reloadDelay:process.env.RELOAD_DELAY?Number.parseInt(process.env.RELOAD_DELAY,10):void 0,workerOptions:{maxWorkerCount:4}})}export{ue as defineFluxionModule,$ as fluxion};
|
|
2
2
|
//# sourceMappingURL=index.mjs.map
|