fluxion-vio 0.1.1 → 0.1.3

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/README.md CHANGED
@@ -111,6 +111,47 @@ Parsed before the handler runs, except for `GET`/`HEAD`:
111
111
 
112
112
  Run sequentially before the handler, each with `middlewareTimeoutMs` (default 3 s). Side effects on `req` are visible to the next middleware and the handler. Write to `rawRes` and return to short-circuit.
113
113
 
114
+ ## Logging
115
+
116
+ Logs are JSON Lines by default: one JSON object per line on stdout, with `timestamp`, `level` and `event` fields.
117
+
118
+ ```ts
119
+ cx.logger.info('request'); // {"timestamp":"…","level":"INFO","event":"request"}
120
+ cx.logger.warn({ event: 'RateLimited', ip: req.ip }); // {"timestamp":"…","level":"WARN","event":"RateLimited","ip":"…"}
121
+ ```
122
+
123
+ - A primitive payload (`string`, number, boolean, `null`) becomes `event`.
124
+ - An object payload is merged after the defaults, so your own fields can override `timestamp`, `level` or `event`.
125
+ - An `Error` value anywhere in the payload is serialized as `message` + newline + `stack` (plain `JSON.stringify` would give `{}`). Use the exported `formatError(e)` to get the same string yourself.
126
+
127
+ There is no built-in one-line mode. To get a human-readable format, pass your own logger function:
128
+
129
+ ```ts
130
+ import { fluxion, defineFluxionLogger } from 'fluxion-vio';
131
+
132
+ const oneLine = defineFluxionLogger(({ timestamp, level, event, ...fields }) => {
133
+ const rest = Object.keys(fields).length ? ' ' + JSON.stringify(fields) : '';
134
+ console.log(`[${timestamp}] ${level} ${String(event ?? '')}${rest}`);
135
+ });
136
+
137
+ await fluxion({ dir: './dynamic', host: '127.0.0.1', port: 3000, logger: oneLine });
138
+ ```
139
+
140
+ Setting `FLUXION_INSTANCE_LOG=/path/to/file.log` additionally writes the same JSON lines to that file.
141
+
142
+ ### Redirecting all output to a file
143
+
144
+ Set `logOutputFile` to send everything the process prints — the framework's JSON lines and your own `console.log` / `console.error` — to a file instead of the terminal:
145
+
146
+ ```ts
147
+ await fluxion({ dir: './dynamic', host: '127.0.0.1', port: 3000, logOutputFile: './logs/server.log' });
148
+ ```
149
+
150
+ - Relative paths are resolved against `process.cwd()`; missing parent directories are created. The file is appended to, never truncated.
151
+ - The redirect starts when `fluxion()` is called, before it opens the port, so a startup failure (a busy port, for instance) lands in the file too. Output printed *before* that call has already gone to the terminal — call `fluxion()` as early as possible.
152
+ - On an in-place restart the same file is reopened in append mode, so the log continues across restarts.
153
+ - `SIGINT` / `SIGTERM` flush the file before exiting.
154
+
114
155
  ## HTTP exceptions
115
156
 
116
157
  ```ts
@@ -137,6 +178,21 @@ Every request resolves its file from disk, so there is no route table to refresh
137
178
 
138
179
  Module-level state survives between requests until a reload resets it — keep handlers stateless.
139
180
 
181
+ ## Memory-triggered restart
182
+
183
+ Long-running processes that reload edited files keep growing: V8's compilation cache and old module instances are never handed back. Set `restartMemoryMB` to restart the process in place once its RSS crosses that many megabytes:
184
+
185
+ ```ts
186
+ await fluxion({ dir: './dynamic', host: '127.0.0.1', port: 3000, restartMemoryMB: 512 });
187
+ ```
188
+
189
+ - RSS is first sampled 5 seconds after `fluxion()` returns — long enough for your own startup work (database connections, cache warm-up) to settle — and then every 10 seconds. The first sample above the threshold wins.
190
+ - The server stops accepting connections and closes idle ones, then the file log is flushed. In-flight requests get up to 5 seconds to finish.
191
+ - The entry point is re-executed with the same argv and environment via `process.execve`, **keeping the PID** — pm2, systemd or Docker see no restart. Expect a gap of a few dozen milliseconds.
192
+ - If `execve` fails, the process logs `RestartFailed` and exits with code 1 so a supervisor can pick it up.
193
+
194
+ Requires Node.js ≥ 22.15 on Linux or macOS. Elsewhere the option is ignored — the server starts normally and logs a `RestartDisabled` warning once. Leave it unset (`false`) unless you need it.
195
+
140
196
  ## Options
141
197
 
142
198
  ```ts
@@ -154,10 +210,12 @@ interface FluxionOptions {
154
210
  exclude?: string[]; // node_modules, .git, dist, ...
155
211
 
156
212
  maxRequestBytes?: number; // 8_000_000
157
- logger?: 'one-line' | 'json-line' | ((entry) => void); // 'one-line'
213
+ logger?: 'json-line' | ((entry) => void); // 'json-line'
214
+ logOutputFile?: string; // redirect stdout/stderr here (relative to cwd)
158
215
  target?: string | string[]; // esbuild target, default 'node<major>'
159
216
  https?: { key: string | Buffer; cert: string | Buffer; ca?: string | Buffer | Array<string | Buffer> };
160
217
  csp?: string | false; // "default-src 'self'"; false omits the header
218
+ restartMemoryMB?: number | false; // restart in place above this RSS; false (default) disables
161
219
  }
162
220
  ```
163
221
 
@@ -175,7 +233,7 @@ Single process, no supervisor. Use pm2, systemd, Docker, or a similar manager:
175
233
  pm2 start "node server.js" --name fluxion-vio-app
176
234
  ```
177
235
 
178
- Need clustering? Run several instances behind a proxy, or use upstream Fluxion.
236
+ Need clustering? Run several instances behind a proxy, or use upstream Fluxion. To cap memory growth, see [Memory-triggered restart](#memory-triggered-restart) — it restarts in place, so the manager never notices.
179
237
 
180
238
  ## Migrating from Fluxion
181
239
 
package/dist/index.cjs CHANGED
@@ -1,7 +1,9 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require("fast-json-stable-stringify");c=s(c,1);let l=require("node:fs");l=s(l,1);let u=require("node:path");u=s(u,1);let d=require("node:http");d=s(d,1);let f=require("node:https");f=s(f,1);let p=require("node:fs/promises");p=s(p,1);let m=require("minimatch"),h=require("esbuild"),g=require("node:vm"),_=require("node:module"),v=require("node:url");const y=e=>{try{return(0,c.default)(e)}catch{return`[unserializable]`}},b={CORE:`CORE`,INFO:`INFO`,WARN:`WARN`,ERROR:`ERROR`,SUCC:`SUCC`,DEBUG:`DEBUG`,VERBOSE:`VERBOSE`},x=e=>{let{level:t,timestamp:n,message:r,pid:i,...a}=e,o=`[${n}]`,s=b[t]??t,c=i===void 0?``:` [${i}]`,l=Object.keys(a).length>0?` ${y(a)}`:``,u=r?r+l:l.trim();console.log(`${o} ${s}${c} ${u}`)};function ee(e){if(!(0,l.existsSync)(e)){let t=(0,u.dirname)(e);(0,l.existsSync)(t)||(0,l.mkdirSync)(t,{recursive:!0})}let t=(0,l.createWriteStream)(e,{flags:`a`});return e=>{try{let n=e.timestamp||new Date().toISOString(),r=e.level||`INFO`,i=e.pid===void 0?``:` [${e.pid}]`,a=e.message?e.message:y(e);t.write(`[${n}] ${r}${i} ${a}\n`)}catch{}}}function S(e){let t=process.env.FLUXION_INSTANCE_LOG,n=t?ee(t):null,r=e.options.logger;if(r===void 0||r===`one-line`)return n?e=>{x(e),n(e)}:x;if(r===`json-line`){let e=e=>console.log(y(e));return n?t=>{e(t),n(t)}:e}return n?e=>{r(e),n(e)}:r}function te(e){let t=S(e);return{write(e,n){let r=typeof n==`string`?{message:n,timestamp:new Date().toISOString(),level:e}:{...n,timestamp:new Date().toISOString(),level:e};try{t(r)}catch{}},info(e){this.write(`INFO`,e)},warn(e){this.write(`WARN`,e)},error(e){this.write(`ERROR`,e)},succ(e){this.write(`SUCC`,e)},debug(e){this.write(`DEBUG`,e)},verbose(e){this.write(`VERBOSE`,e)},core(e){this.write(`CORE`,e)}}}const C=typeof Error.isError==`function`?e=>Error.isError(e)?e.message:String(e):e=>e?.message||String(e);let w;(function(e){e.OptionsNormalized=Symbol(`fluxion.options.normalized`),e.StaticHandled=Symbol(`fluxion.router.StaticHandled`),e.HandlerTimeout=Symbol(`fluxion.handlerTimeout`),e.MiddlewareTimeout=Symbol(`fluxion.middlewareTimeout`)})(w||={});const ne={".css":`text/css; charset=utf-8`,".html":`text/html; charset=utf-8`,".htm":`text/html; charset=utf-8`,".js":`text/javascript; charset=utf-8`,".mjs":`text/javascript; charset=utf-8`,".cjs":`text/javascript; charset=utf-8`,".json":`application/json; charset=utf-8`,".jsonld":`application/ld+json; charset=utf-8`,".map":`application/json; charset=utf-8`,".txt":`text/plain; charset=utf-8`,".md":`text/markdown; charset=utf-8`,".xml":`application/xml; charset=utf-8`,".csv":`text/csv; charset=utf-8`,".yaml":`application/yaml; charset=utf-8`,".yml":`application/yaml; charset=utf-8`,".toml":`application/toml; charset=utf-8`,".ico":`image/x-icon`,".png":`image/png`,".jpg":`image/jpeg`,".jpeg":`image/jpeg`,".gif":`image/gif`,".webp":`image/webp`,".avif":`image/avif`,".svg":`image/svg+xml`,".bmp":`image/bmp`,".tif":`image/tiff`,".tiff":`image/tiff`,".woff":`font/woff`,".woff2":`font/woff2`,".ttf":`font/ttf`,".otf":`font/otf`,".eot":`application/vnd.ms-fontobject`,".mp3":`audio/mpeg`,".wav":`audio/wav`,".ogg":`audio/ogg`,".oga":`audio/ogg`,".m4a":`audio/mp4`,".aac":`audio/aac`,".flac":`audio/flac`,".mp4":`video/mp4`,".webm":`video/webm`,".ogv":`video/ogg`,".mov":`video/quicktime`,".m4v":`video/mp4`,".pdf":`application/pdf`,".wasm":`application/wasm`,".zip":`application/zip`,".gz":`application/gzip`,".tar":`application/x-tar`,".rar":`application/vnd.rar`,".7z":`application/x-7z-compressed`,".webmanifest":`application/manifest+json; charset=utf-8`,".exe":`application/octet-stream`};let re=function(e){return e[e.Ok=200]=`Ok`,e[e.Created=201]=`Created`,e[e.Accepted=202]=`Accepted`,e[e.NoContent=204]=`NoContent`,e[e.PartialContent=206]=`PartialContent`,e[e.MovedPermanently=301]=`MovedPermanently`,e[e.Found=302]=`Found`,e[e.NotModified=304]=`NotModified`,e[e.TemporaryRedirect=307]=`TemporaryRedirect`,e[e.PermanentRedirect=308]=`PermanentRedirect`,e[e.BadRequest=400]=`BadRequest`,e[e.Unauthorized=401]=`Unauthorized`,e[e.Forbidden=403]=`Forbidden`,e[e.NotFound=404]=`NotFound`,e[e.MethodNotAllowed=405]=`MethodNotAllowed`,e[e.NotAcceptable=406]=`NotAcceptable`,e[e.RequestTimeout=408]=`RequestTimeout`,e[e.Conflict=409]=`Conflict`,e[e.Gone=410]=`Gone`,e[e.PayloadTooLarge=413]=`PayloadTooLarge`,e[e.UnsupportedMediaType=415]=`UnsupportedMediaType`,e[e.UnprocessableEntity=422]=`UnprocessableEntity`,e[e.TooManyRequests=429]=`TooManyRequests`,e[e.InternalServerError=500]=`InternalServerError`,e[e.NotImplemented=501]=`NotImplemented`,e[e.BadGateway=502]=`BadGateway`,e[e.ServiceUnavailable=503]=`ServiceUnavailable`,e[e.GatewayTimeout=504]=`GatewayTimeout`,e}({});var T=class extends Error{constructor(e){super(e),this.name=`FluxionVioError`}};function E(e){if(Buffer.isBuffer(e))return e;if(typeof e==`string`){if(!e.startsWith(`-----BEGIN`)){let t=u.default.resolve(e);if(l.default.existsSync(t))return l.default.readFileSync(t)}return Buffer.from(e)}throw new T(`Certificate content must be a string or Buffer`)}function ie(e){if(!e)return;if(typeof e!=`object`||!e||Array.isArray(e))throw new T(`FluxionOptions.https must be an object`);if(typeof e.key!=`string`&&!Buffer.isBuffer(e.key))throw new T(`FluxionOptions.https.key must be a string or Buffer`);if(typeof e.cert!=`string`&&!Buffer.isBuffer(e.cert))throw new T(`FluxionOptions.https.cert must be a string or Buffer`);let t={key:E(e.key),cert:E(e.cert)};return e.ca!==void 0&&(Array.isArray(e.ca)?t.ca=e.ca.map(e=>E(e)):t.ca=E(e.ca)),t}function D(e){if(typeof e!=`object`||!e||Array.isArray(e))throw new T(`FluxionOptions must be an object`);if(`include`in e)throw new T(`The "include" option has been removed. Please use:
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require("node:fs");c=s(c,1);let l=require("node:path");l=s(l,1);let u=require("node:fs/promises");u=s(u,1);let d=require("node:http");d=s(d,1);let f=require("node:https");f=s(f,1);let p=require("minimatch"),m=require("esbuild"),h=require("node:vm"),g=require("node:module"),_=require("node:url");const v=e=>typeof e==`object`&&!!e&&!Array.isArray(e),y=e=>typeof Error.isError==`function`?Error.isError(e):e instanceof Error;function b(e){if(typeof e==`object`&&e){let{message:t,stack:n}=e,r=``;if(typeof t==`string`&&t.length>0&&(r=t),typeof n==`string`&&n.length>0&&(r+=(r.length>0?`
2
+ `:``)+n),r.length>0)return r}return String(e)}const x=(e,t)=>typeof t==`bigint`?t.toString():y(t)?b(t):t,S=e=>JSON.stringify({timestamp:typeof e.timestamp==`string`?e.timestamp:String(e.timestamp),level:typeof e.level==`string`?e.level:`INFO`,event:`[unserializable]`}),C=e=>{try{return JSON.stringify(e,x)??S(e)}catch{return S(e)}},ee=e=>{console.log(C(e))};function w(e){return new Promise(t=>{if(e.destroyed||e.writableEnded){t();return}let n=()=>{clearTimeout(r),e.off(`error`,n),t()},r=setTimeout(n,2e3);r.unref(),e.once(`error`,n);try{e.write(``,n)}catch{n()}})}function te(e,t){if(!(0,c.existsSync)(e)){let t=(0,l.dirname)(e);(0,c.existsSync)(t)||(0,c.mkdirSync)(t,{recursive:!0})}let n=(0,c.createWriteStream)(e,{flags:`a`,autoClose:!t});return{sink:e=>{try{n.write(C(e)+`
3
+ `)}catch{}},flush:async()=>{await w(n),t&&await new Promise(e=>n.close(()=>e()))}}}function ne(e,t){let n=process.env.FLUXION_INSTANCE_LOG,r=n?te(n,t):null,i=e.options.logger,a=typeof i==`function`?i:ee;if(!r)return a;let o=e=>{a(e),r.sink(e)};return o.flush=r.flush,o}function T(e,t={}){let n=ne(e,t.deferClose===!0);return{write(e,t){let r=new Date().toISOString(),i=v(t)?{timestamp:r,level:e,event:t.event,...t}:{timestamp:r,level:e,event:t};try{n(i)}catch{}},info(e){this.write(`INFO`,e)},warn(e){this.write(`WARN`,e)},error(e){this.write(`ERROR`,e)},succ(e){this.write(`SUCC`,e)},debug(e){this.write(`DEBUG`,e)},verbose(e){this.write(`VERBOSE`,e)},core(e){this.write(`CORE`,e)},flush(){return n.flush?.()??Promise.resolve()}}}async function E(e){await(0,u.mkdir)((0,l.dirname)(e),{recursive:!0});let t=await(0,u.open)(e,`a`),n=(e,n,r)=>{(0,c.writeSync)(t.fd,Buffer.from(e));let i=typeof n==`function`?n:r;return typeof i==`function`&&i(),!0};process.stdout.write=n,process.stderr.write=n}let D;(function(e){e.OptionsNormalized=Symbol(`fluxion.options.normalized`),e.StaticHandled=Symbol(`fluxion.router.StaticHandled`),e.HandlerTimeout=Symbol(`fluxion.handlerTimeout`),e.MiddlewareTimeout=Symbol(`fluxion.middlewareTimeout`)})(D||={});const re={".css":`text/css; charset=utf-8`,".html":`text/html; charset=utf-8`,".htm":`text/html; charset=utf-8`,".js":`text/javascript; charset=utf-8`,".mjs":`text/javascript; charset=utf-8`,".cjs":`text/javascript; charset=utf-8`,".json":`application/json; charset=utf-8`,".jsonld":`application/ld+json; charset=utf-8`,".map":`application/json; charset=utf-8`,".txt":`text/plain; charset=utf-8`,".md":`text/markdown; charset=utf-8`,".xml":`application/xml; charset=utf-8`,".csv":`text/csv; charset=utf-8`,".yaml":`application/yaml; charset=utf-8`,".yml":`application/yaml; charset=utf-8`,".toml":`application/toml; charset=utf-8`,".ico":`image/x-icon`,".png":`image/png`,".jpg":`image/jpeg`,".jpeg":`image/jpeg`,".gif":`image/gif`,".webp":`image/webp`,".avif":`image/avif`,".svg":`image/svg+xml`,".bmp":`image/bmp`,".tif":`image/tiff`,".tiff":`image/tiff`,".woff":`font/woff`,".woff2":`font/woff2`,".ttf":`font/ttf`,".otf":`font/otf`,".eot":`application/vnd.ms-fontobject`,".mp3":`audio/mpeg`,".wav":`audio/wav`,".ogg":`audio/ogg`,".oga":`audio/ogg`,".m4a":`audio/mp4`,".aac":`audio/aac`,".flac":`audio/flac`,".mp4":`video/mp4`,".webm":`video/webm`,".ogv":`video/ogg`,".mov":`video/quicktime`,".m4v":`video/mp4`,".pdf":`application/pdf`,".wasm":`application/wasm`,".zip":`application/zip`,".gz":`application/gzip`,".tar":`application/x-tar`,".rar":`application/vnd.rar`,".7z":`application/x-7z-compressed`,".webmanifest":`application/manifest+json; charset=utf-8`,".exe":`application/octet-stream`};let ie=function(e){return e[e.Ok=200]=`Ok`,e[e.Created=201]=`Created`,e[e.Accepted=202]=`Accepted`,e[e.NoContent=204]=`NoContent`,e[e.PartialContent=206]=`PartialContent`,e[e.MovedPermanently=301]=`MovedPermanently`,e[e.Found=302]=`Found`,e[e.NotModified=304]=`NotModified`,e[e.TemporaryRedirect=307]=`TemporaryRedirect`,e[e.PermanentRedirect=308]=`PermanentRedirect`,e[e.BadRequest=400]=`BadRequest`,e[e.Unauthorized=401]=`Unauthorized`,e[e.Forbidden=403]=`Forbidden`,e[e.NotFound=404]=`NotFound`,e[e.MethodNotAllowed=405]=`MethodNotAllowed`,e[e.NotAcceptable=406]=`NotAcceptable`,e[e.RequestTimeout=408]=`RequestTimeout`,e[e.Conflict=409]=`Conflict`,e[e.Gone=410]=`Gone`,e[e.PayloadTooLarge=413]=`PayloadTooLarge`,e[e.UnsupportedMediaType=415]=`UnsupportedMediaType`,e[e.UnprocessableEntity=422]=`UnprocessableEntity`,e[e.TooManyRequests=429]=`TooManyRequests`,e[e.InternalServerError=500]=`InternalServerError`,e[e.NotImplemented=501]=`NotImplemented`,e[e.BadGateway=502]=`BadGateway`,e[e.ServiceUnavailable=503]=`ServiceUnavailable`,e[e.GatewayTimeout=504]=`GatewayTimeout`,e}({});const ae=e=>e/(1024*1024),oe=()=>typeof process.execve==`function`;function se(e,t=5e3){return new Promise(n=>{let r=!1,i=()=>{r||(r=!0,clearTimeout(a),n())},a=setTimeout(()=>{e.closeAllConnections(),i()},t);a.unref(),e.close(i),e.closeIdleConnections()})}const ce=()=>[process.execPath,...process.execArgv,...process.argv.slice(1)];async function le(e,t,n={}){typeof process.execve!=`function`&&(t.error({event:`RestartFailed`,pid:process.pid,error:`process.execve is not available`}),process.exit(1)),t.warn({event:`Restarting`,pid:process.pid,...n}),await se(e),await t.flush();try{process.execve(process.execPath,ce(),process.env)}catch(e){t.error({event:`RestartFailed`,pid:process.pid,error:b(e)})}process.exit(1)}function O(e){let{restartMemoryMB:t,server:n,logger:r,startupGraceMs:i=5e3,checkIntervalMs:a=1e4}=e;if(t===!1)return{start:()=>{},stop:()=>{}};let o=!1,s,c=()=>{if(o)return;let e=ae(process.memoryUsage.rss());e<=t||(o=!0,s!==void 0&&(clearInterval(s),s=void 0),le(n,r,{reason:`memory`,rssMB:Math.round(e)}))};return{start(){s!==void 0||o||(s=setTimeout(()=>{s=setInterval(c,a),s.unref(),c()},i),s.unref())},stop(){s!==void 0&&(clearInterval(s),s=void 0)}}}var k=class extends Error{constructor(e){super(e),this.name=`FluxionVioError`}};function A(e){if(Buffer.isBuffer(e))return e;if(typeof e==`string`){if(!e.startsWith(`-----BEGIN`)){let t=l.default.resolve(e);if(c.default.existsSync(t))return c.default.readFileSync(t)}return Buffer.from(e)}throw new k(`Certificate content must be a string or Buffer`)}function ue(e){if(!e)return;if(typeof e!=`object`||!e||Array.isArray(e))throw new k(`FluxionOptions.https must be an object`);if(typeof e.key!=`string`&&!Buffer.isBuffer(e.key))throw new k(`FluxionOptions.https.key must be a string or Buffer`);if(typeof e.cert!=`string`&&!Buffer.isBuffer(e.cert))throw new k(`FluxionOptions.https.cert must be a string or Buffer`);let t={key:A(e.key),cert:A(e.cert)};return e.ca!==void 0&&(Array.isArray(e.ca)?t.ca=e.ca.map(e=>A(e)):t.ca=A(e.ca)),t}function j(e){if(typeof e!=`object`||!e||Array.isArray(e))throw new k(`FluxionOptions must be an object`);if(`include`in e)throw new k(`The "include" option has been removed. Please use:
2
4
  - "apiInclude" for API handler patterns (default: ["**/*.ts"])
3
5
  - "staticInclude" for static resource patterns (default: ["**/*"])
4
6
  Example migration:
5
7
  OLD: { include: ["**/*.ts", "**/*.js"], apiInclude: ["**/*.ts"] }
6
- 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:c=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'`}=e,g=r,_=e.logger??`one-line`;if(_!==`one-line`&&_!==`json-line`&&typeof _!=`function`)throw new T(`Invalid logger option, Must be 'one-line', 'json-line' or a custom logger function`);if(typeof t!=`string`)throw new T(`FluxionOptions.dir must be a string`);let v=u.default.resolve(process.cwd(),t),y=v.endsWith(u.default.sep)?v:v+u.default.sep;if(typeof s!=`string`&&!(Array.isArray(s)&&s.every(e=>typeof e==`string`)))throw new T(`FluxionOptions.target must be a string or an array of strings`);if(typeof n!=`string`)throw new T(`FluxionOptions.host must be a string`);if(!Number.isSafeInteger(i)||i<=100)throw new T(`FluxionOptions.handlerTimeoutMs must be an integer greater than 100`);if(!Number.isSafeInteger(a)||a<=100)throw new T(`FluxionOptions.middlewareTimeoutMs must be an integer greater than 100`);if(typeof g!=`number`||!Number.isSafeInteger(g))throw new T(`FluxionOptions.port must be a positive integer`);if(g<=1||g>65535)throw new T(`FluxionOptions.port must be 1 ~ 65535`);if(typeof c!=`number`||c<=0||!Number.isSafeInteger(c))throw new T(`FluxionOptions.maxRequestBytes must be a positive integer`);if(h!==!1&&typeof h!=`string`)throw new T(`FluxionOptions.csp must be a string or false`);l.default.existsSync(y)||l.default.mkdirSync(y,{recursive:!0});let b={dir:y,host:n,port:g,handlerTimeoutMs:i,middlewareTimeoutMs:a,staticResourceTimeoutMs:o,target:s,maxRequestBytes:c,logger:_,apiInclude:d,staticInclude:f,exclude:p,https:ie(m),csp:h,normalizedFlag:w.OptionsNormalized};return process.argv.includes(`--verbose`)&&console.log(`normalized options`,{...b,https:b.https?`set`:`unset`}),b}function O(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 ae(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 k(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 oe(e){if(e!==void 0)try{return new URL(e,`http://fluxion.local`)}catch{return}}function se(e,t,n=200){e.statusCode=n,e.setHeader(`Content-Type`,`application/json; charset=utf-8`),e.end(JSON.stringify(t))}function A(e,t,n=200){if(!e.writableEnded){if(e.headersSent){e.end();return}se(e,t,n)}}function j(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 M=class extends Error{errno;code;constructor(e,t,n){super(e),this.name=`HttpException`,this.errno=t,this.code=n}},ce=class extends M{constructor(e=`Bad Request`){super(e,400,`BAD_REQUEST`)}},le=class extends M{constructor(e=`Unauthorized`){super(e,401,`UNAUTHORIZED`)}},ue=class extends M{constructor(e=`Forbidden`){super(e,403,`FORBIDDEN`)}},N=class extends M{constructor(e=`Not Found`){super(e,404,`NOT_FOUND`)}},P=class extends M{constructor(e=`Method Not Allowed`){super(e,405,`METHOD_NOT_ALLOWED`)}},F=class extends M{constructor(e=`Not Acceptable`){super(e,406,`NOT_ACCEPTABLE`)}},I=class extends M{constructor(e=`Request Timeout`){super(e,408,`REQUEST_TIMEOUT`)}},L=class extends M{constructor(e=`Conflict`){super(e,409,`CONFLICT`)}},R=class extends M{constructor(e=`Gone`){super(e,410,`GONE`)}},z=class extends M{constructor(e=`Payload Too Large`){super(e,413,`PAYLOAD_TOO_LARGE`)}},B=class extends M{constructor(e=`Unsupported Media Type`){super(e,415,`UNSUPPORTED_MEDIA_TYPE`)}},V=class extends M{constructor(e=`Unprocessable Entity`){super(e,422,`UNPROCESSABLE_ENTITY`)}},H=class extends M{constructor(e=`Too Many Requests`){super(e,429,`TOO_MANY_REQUESTS`)}},U=class extends M{constructor(e=`Internal Server Error`){super(e,500,`INTERNAL_SERVER_ERROR`)}},de=class extends M{constructor(e=`Not Implemented`){super(e,501,`NOT_IMPLEMENTED`)}},fe=class extends M{constructor(e=`Bad Gateway`){super(e,502,`BAD_GATEWAY`)}},pe=class extends M{constructor(e=`Service Unavailable`){super(e,503,`SERVICE_UNAVAILABLE`)}},me=class extends M{constructor(e=`Gateway Timeout`){super(e,504,`GATEWAY_TIMEOUT`)}};function W(e,t){return new z(`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 he(e,t,n,r){return t===0?K():k(n)?{exists:!0,value:e.toString(`utf8`),bytes:t,truncated:r}:{exists:!0,value:`<binary body: ${t} bytes>`,bytes:t,truncated:r}}async function ge(e,t,n,r=8192){if(t===`GET`||t===`HEAD`||e.readableEnded)return{rawBody:void 0,preview: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:he(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 _e(e,t,n){let{rawBody:r,preview:i}=await ge(e,t,n);if(r===void 0||r.byteLength===0)return{body:{},preview:i};let a=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:j(new URLSearchParams(r.toString(`utf8`))),preview:i}:k(a)?{body:{raw:r.toString(`utf8`)},preview:i}:{body:{},preview:i}}function ve(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 q=(e,t,n)=>Promise.race([e,new Promise(e=>setTimeout(()=>e(n),t))]);function ye(e,t,n){if(typeof n.disposer!=`function`)return;let r=n.disposer;t.once(`close`,()=>{O(r).catch(t=>{e.logger.error({message:`DisposerFailed`,url:n.absolutePath,error:C(t)})})})}function be(e){let t=Object.freeze({logger:e.logger}),n=async(n,r)=>{let i=n.method??`GET`,a=ae(n),o=oe(n.url);if(o===void 0){A(r,{message:`Bad Request: req.url is undefined`},400);return}let s={method:i,ip:a,url:o,query:j(o.searchParams),body:{},headers:n.headers,cookie:ve(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({message:`request`,method:i,ip:a,path:o.pathname});let l=performance.now();r.once(`finish`,()=>{let t={message:`response`,method:i,ip:a,path:o.pathname,status:r.statusCode,duration:(performance.now()-l).toFixed(4)};Object.keys(s.query).length>0&&(t.query=s.query),c.exists&&(t.body=c.value,t.bodyBytes=c.bytes,t.bodyTruncated=c.truncated),e.logger.core(t)});try{let i=await _e(n,s.method,e.options.maxRequestBytes);s.body=i.body,c=i.preview;let a=await e.router(o);if(!a){A(r,{message:`Not Found`},404);return}if(n.method&&a.methods&&!a.methods.includes(n.method)){A(r,{message:`Method Not Allowed`},405);return}ye(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 q(O(a.middlewares[i],s,t,n,r),e.options.middlewareTimeoutMs,w.MiddlewareTimeout)===w.MiddlewareTimeout){e.logger.warn({message:`MiddlewareTimeout`,method:s.method,ip:s.ip}),A(r,{message:`Internal Server Error`},500);return}if(r.writableEnded)return;if(r.headersSent){r.end();return}}let u=await q(O(a.handler,s,t,n,r),l,w.HandlerTimeout);if(u===w.HandlerTimeout){e.logger.warn({message:`HandlerTimeout`,method:s.method,ip:s.ip}),A(r,{message:`Handler timed out`},500);return}u!==w.StaticHandled&&A(r,u)}catch(t){t instanceof M?(e.logger.error({...s,message:`RequestFailed`,error:t.message}),A(r,{message:t.message},t.errno)):(e.logger.error({...s,message:`RequestFailed`,error:C(t)}),A(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({message:`ServerClosed`,host:e.options.host,port:e.options.port})}),r.once(`listening`,()=>{i=!0,e.logger.core({message:`FluxionStarted`,version:`0.1.0`,pid:process.pid,protocol:e.options.https?`https`:`http`,host:e.options.host,port:e.options.port}),e.logger.core({message:`DynamicDirectory`,directory:e.options.dir}),t(r)}),r.on(`error`,t=>{e.logger.error({message:`ServerError`,error:C(t)}),!i&&n(t)}),r.listen(e.options.port,e.options.host)})}function xe(e){}const Se=xe,J=new Map,Ce=`${u.sep}node_modules${u.sep}`,Y=new Set([`.ts`,`.tsx`,`.mts`,`.cts`,`.js`,`.jsx`,`.mjs`,`.cjs`]),X=[...Y,`.json`],we={".js":[`.ts`,`.tsx`],".jsx":[`.tsx`],".mjs":[`.mts`],".cjs":[`.cts`]},Te=e=>!e.startsWith(`.`)&&!(0,u.isAbsolute)(e),Ee=e=>{try{let t=(0,l.statSync)(e.filename);return t.mtimeMs!==e.mtimeMs||t.size!==e.size}catch{return!0}},Z=e=>{try{return(0,l.statSync)(e).isFile()}catch{return!1}},De=e=>{if(Z(e))return e;let t=(0,u.extname)(e),n=e.slice(0,e.length-t.length);for(let e of we[t]??[])if(Z(n+e))return n+e;for(let t of X)if(Z(e+t))return e+t;for(let t of X)if(Z((0,u.join)(e,`index`+t)))return(0,u.join)(e,`index`+t)};var Oe=class{opts;constructor(e){this.opts=e}compile(e){let{outputFiles:t}=(0,h.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,v.pathToFileURL)(e).href),"import.meta.filename":JSON.stringify(e),"import.meta.dirname":JSON.stringify((0,u.dirname)(e))}});return t[0].text}require(e){return this.load((0,u.resolve)(e)).exports}isHot(e){let t=(0,u.extname)(e);return e.startsWith(this.opts.dir)&&!e.includes(Ce)&&(Y.has(t)||t===`.json`)}load(e){let t=J.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)J.get(e.filename)===e&&J.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(J.get(t.filename)!==t||Ee(t))return!0;return!1}loadFresh(e){let{mtimeMs:t,size:n}=(0,l.statSync)(e),r={id:e,filename:e,exports:{},mtimeMs:t,size:n,deps:new Map};J.set(e,r);try{this.evaluate(e,r)}catch(t){throw J.delete(e),t}return r}evaluate(e,t){let n=(0,u.extname)(e);if(n===`.json`){t.exports=JSON.parse((0,l.readFileSync)(e,`utf8`));return}if(!Y.has(n))throw Error(`FluxionLoader: unsupported file type "${n}" (${e})`);(0,g.compileFunction)(this.compile(e),[`exports`,`require`,`module`,`__filename`,`__dirname`],{filename:e}).call(t.exports,t.exports,this.createModuleRequire(t),t,e,(0,u.dirname)(e))}createModuleRequire(e){let t=(0,_.createRequire)(e.filename),n=t=>Te(t)?void 0:De((0,u.resolve)((0,u.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 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 ke(e,t,n){let r=new Oe(e.options).require(t);if(Q(e,r.default))r=r.default;else if(!Q(e,r))throw new T(`Invalid handler module '${t}', make sure it satisfies defineFluxionModule(...) helper`);return r.absolutePath=t,r.mtimeMs=n.mtimeMs,r}function Ae(e){return async t=>{let n=t.pathname.replace(/^[/]+/,``).replace(/[/]+$/,``),r=u.default.join(e.options.dir,n),i=u.default.relative(e.options.dir,r);if(i.startsWith(`..`)||u.default.isAbsolute(i)||e.options.exclude.some(e=>(0,m.minimatch)(n,e)))return;let a=await p.default.stat(r).catch(()=>void 0);if(!(!a||!a.isFile())){if(e.options.apiInclude.some(e=>(0,m.minimatch)(n,e)))return ke(e,r,a);if(e.options.staticInclude.some(e=>(0,m.minimatch)(n,e)))return $(r)}}}function $(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 p.default.stat(e).catch(e=>e);if(a instanceof Error)return i.statusCode=404,i.end(`Not Accessible`),n.logger.error({action:`StaticResourceError`,url:t.url,error:a.message}),null;if(!a.isFile()){i.statusCode=404,i.end(`Not Found`);return}let o=ne[u.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,l.createReadStream)(e),o=()=>{a.off(`error`,s),a.off(`end`,c),i.off(`close`,u),r.off(`aborted`,u)},s=e=>{o(),n(e)},c=()=>{o(),t(w.StaticHandled)},u=()=>{o(),a.destroy(),t(w.StaticHandled)};a.on(`error`,s),a.on(`end`,c),i.on(`close`,u),r.on(`aborted`,u),a.pipe(i)})}}}async function je(e){let t={options:e.normalizedFlag===w.OptionsNormalized?e:D(e)};t.logger=te(t),t.router=Ae(t);let n=await be(t),r=e=>{t.logger.warn({message:`ShuttingDown`,pid:process.pid,signal:e}),n.close(()=>process.exit(0))};process.once(`SIGINT`,()=>r(`SIGINT`)),process.once(`SIGTERM`,()=>r(`SIGTERM`))}function Me(e,t){if(typeof e==`function`){if(t!==void 0&&typeof t!=`function`)throw new T(`Invalid disposer, expected a function but got ${typeof t}`);return{handler:e,disposer:t,type:0}}if(typeof e!=`object`||!e)throw new T(`Invalid argument, expected a FluxionModule object or a handler function, but got ${typeof e}`);if(typeof e.handler!=`function`)throw new T(`Invalid FluxionModule, "handler" must be a function`);if(e.disposer!==void 0&&typeof e.disposer!=`function`)throw new T(`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 T(`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 T(`Invalid FluxionModule, "middlewares" must be an array of functions if provided`);return{...e,type:0}}function Ne(e){if(typeof e!=`function`)throw new T(`Invalid FluxionMiddleware, expected a function but got ${typeof e}`);return e}function Pe(e){if(typeof e!=`function`)throw new T(`Invalid FluxionLoggerFn, expected a function but got ${typeof e}`);return e}exports.BadGatewayException=fe,exports.BadRequestException=ce,exports.ConflictException=L,exports.ForbiddenException=ue,exports.GatewayTimeoutException=me,exports.GoneException=R,exports.HttpCode=re,exports.HttpException=M,exports.InternalServerErrorException=U,exports.MethodNotAllowedException=P,exports.NotAcceptableException=F,exports.NotFoundException=N,exports.NotImplementedException=de,exports.PayloadTooLargeException=z,exports.RequestTimeoutException=I,exports.ServiceUnavailableException=pe,exports.TooManyRequestsException=H,exports.UnauthorizedException=le,exports.UnprocessableEntityException=V,exports.UnsupportedMediaTypeException=B,exports.VERSION=`0.1.0`,exports.defineFluxionLogger=Pe,exports.defineFluxionMiddleware=Ne,exports.defineFluxionModule=Me,exports.defineFluxionOptions=D,exports.fluxion=je;
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;
7
9
  //# sourceMappingURL=index.cjs.map